build: support -race in binaries

Since a few releases, Go supports `go build -race` and then produces
binaries which do data race detection when invoked. Some changes are needed to
enable using this in a kind cluster:

- `-race` must be passed when building dynamically linked binaries.
  Only those support -race because CGO is required.
  To avoid adding yet another env variables, the existing KUBE_RACE
  gets used to convey the intent.
- KUBE_RACE must be passed into the dockerized build.
- Logging the base image of a release image makes it easier
  to figure out whether the binary has a chance to run.

The base image is important because dynamically linked binaries need a base
image with libc. By default, control plane components are linked statically,
so users need to explicitly override the defaults:

    KUBE_RACE=-race KUBE_CGO_OVERRIDES="kube-apiserver kube-controller-manager kube-scheduler" KUBE_GORUNNER_IMAGE=gcr.io/k8s-staging-test-infra/kubekins-e2e:v20250815-171060767f-master kind build node-image ...

KUBE_GORUNNER_IMAGE changes the base image for kube-apiserver,
kube-controller-manager and kube-scheduler. The kubekins image was picked for
this example because a Prow job definition already uses it. Reusing
it in a job avoids the need to maintain another image definition.

Running conformance tests against such a cluster with alpha+beta features
enabled revealed one new data race:

    $ kubectl logs -n kube-system kube-controller-manager-kind-control-plane
    ...
    WARNING: DATA RACE
    Write at 0x00c00019a730 by goroutine 216:
    k8s.io/client-go/tools/leaderelection.(*LeaderElector).setObservedRecord()
         k8s.io/client-go/tools/leaderelection/leaderelection.go:529 +0x179
    k8s.io/client-go/tools/leaderelection.(*LeaderElector).tryCoordinatedRenew()
         k8s.io/client-go/tools/leaderelection/leaderelection.go:367 +0x5ca
    ...
This commit is contained in:
Patrick Ohly 2025-09-01 19:12:15 +02:00
parent b94b6ece10
commit 23362e001c
3 changed files with 6 additions and 2 deletions

View file

@ -540,6 +540,7 @@ function kube::build::run_build_command_ex() {
--env "KUBE_BUILD_PLATFORMS=${KUBE_BUILD_PLATFORMS:-}"
--env "KUBE_CGO_OVERRIDES=' ${KUBE_CGO_OVERRIDES[*]:-} '"
--env "KUBE_STATIC_OVERRIDES=' ${KUBE_STATIC_OVERRIDES[*]:-} '"
--env "KUBE_RACE=${KUBE_RACE:-}"
--env "FORCE_HOST_GO=${FORCE_HOST_GO:-}"
--env "GO_VERSION=${GO_VERSION:-}"
--env "GOTOOLCHAIN=${GOTOOLCHAIN:-}"

View file

@ -332,7 +332,7 @@ function kube::release::create_docker_images_for_server() {
docker_file_path="${KUBE_ROOT}/build/server-image/${binary_name}/Dockerfile"
fi
kube::log::status "Starting docker build for image: ${binary_name}-${arch}"
kube::log::status "Starting docker build for image: ${binary_name}-${arch} with base ${base_image}"
(
rm -rf "${docker_build_path}"
mkdir -p "${docker_build_path}"

View file

@ -836,7 +836,7 @@ kube::golang::build_binaries_for_platform() {
kube::log::info " ${binary} (static)"
else
nonstatics+=("${binary}")
kube::log::info " ${binary} (non-static)"
kube::log::info " ${binary} (non-static${KUBE_RACE:+, race detection})"
fi
done
@ -862,6 +862,9 @@ kube::golang::build_binaries_for_platform() {
-ldflags="${goldflags}"
-tags="${gotags:-}"
)
if [[ -n "${KUBE_RACE:-}" ]]; then
build_args+=("${KUBE_RACE}")
fi
kube::golang::build_some_binaries "${nonstatics[@]}"
fi