From c1656d16041ab94ec05a84daee37bc5463a8d164 Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Thu, 18 Jun 2020 19:35:04 -0700 Subject: [PATCH] Adds Windows support for etcd image We can use docker buildx in order to build and push Windows images from the same Linux node, as long as the Dockerfile does not have any RUN commands in the Windows step. We also need to create a non-default builder instance in order to be able to build and push Windows images. The Windows images have to be built and pushed directly to the registry. For Windows containers without Hyper-V isolation, the host OS Version and the Container OS Version need to match, which is why we added multiple Windows OS Versions to the building process. For the manifest list, we need to also annotate the Windows OS Version, so the Windows nodes will be able to pull the proper image from the manifest list. Adds support for Windows OS Versions: 1809, 2004, 20H2, ltsc2022. Bumped etcd image revision. --- cluster/images/etcd/Makefile | 91 +++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/cluster/images/etcd/Makefile b/cluster/images/etcd/Makefile index 26cbfdab7d2..39877c49557 100644 --- a/cluster/images/etcd/Makefile +++ b/cluster/images/etcd/Makefile @@ -34,13 +34,34 @@ LATEST_ETCD_VERSION?=3.5.1 # REVISION provides a version number for this image and all it's bundled # artifacts. It should start at zero for each LATEST_ETCD_VERSION and increment # for each revision of this image at that etcd version. -REVISION?=0 +REVISION?=1 # IMAGE_TAG Uniquely identifies k8s.gcr.io/etcd docker image with a tag of the form "-". IMAGE_TAG=$(LATEST_ETCD_VERSION)-$(REVISION) ARCH?=amd64 -ALL_ARCH = amd64 arm arm64 ppc64le s390x + +# Operating systems supported: linux, windows +OS ?= linux +# OS Version for the Windows images: 1809, 20H2, ltsc2022 +OSVERSION ?= 1809 + +# The output type could either be docker (local), or registry. +# If it is registry, it will also allow us to push the Windows images. +OUTPUT_TYPE ?= docker + +ALL_OS = linux windows +ALL_ARCH.linux = amd64 arm arm64 ppc64le s390x +ALL_OS_ARCH.linux = $(foreach arch, ${ALL_ARCH.linux}, linux-$(arch)) +ALL_ARCH.windows = amd64 +ALL_OSVERSIONS.windows := 1809 20H2 ltsc2022 +ALL_OS_ARCH.windows = $(foreach arch, $(ALL_ARCH.windows), $(foreach osversion, ${ALL_OSVERSIONS.windows}, windows-$(arch)-${osversion})) +ALL_OS_ARCH = $(foreach os, $(ALL_OS), ${ALL_OS_ARCH.${os}}) + +IMAGE_SUFFIX.linux = $(OS)-$(ARCH) +IMAGE_SUFFIX.windows = $(OS)-$(ARCH)-$(OSVERSION) +IMAGE_SUFFIX := ${IMAGE_SUFFIX.${OS}} + # Image should be pulled from k8s.gcr.io, which will auto-detect # region (us, eu, asia, ...) and pull from the closest. REGISTRY?=k8s.gcr.io @@ -66,6 +87,10 @@ GOLANG_VERSION?=1.16.10 GOARM?=7 TEMP_DIR:=$(shell mktemp -d) +DOCKERFILE.linux = Dockerfile +DOCKERFILE.windows = Dockerfile.windows +DOCKERFILE := ${DOCKERFILE.${OS}} + ifeq ($(ARCH),amd64) BASEIMAGE?=k8s.gcr.io/build-image/debian-base:bullseye-v1.0.0 endif @@ -82,21 +107,26 @@ ifeq ($(ARCH),s390x) BASEIMAGE?=k8s.gcr.io/build-image/debian-base-s390x:bullseye-v1.0.0 endif -RUNNERIMAGE?=gcr.io/distroless/static:latest +BASE.windows = mcr.microsoft.com/windows/nanoserver + +RUNNERIMAGE.windows?=$(BASE.windows):$(OSVERSION) +RUNNERIMAGE.linux?=gcr.io/distroless/static:latest +RUNNERIMAGE := ${RUNNERIMAGE.${OS}} QEMUVERSION?=5.2.0-2 build: # Explicitly copy files to the temp directory $(BIN_INSTALL) migrate-if-needed.sh $(TEMP_DIR) - install Dockerfile $(TEMP_DIR) + $(BIN_INSTALL) migrate-if-needed.bat $(TEMP_DIR) + install $(DOCKERFILE) $(TEMP_DIR) # Compile migrate migrate_tmp_dir=$(shell mktemp -d); \ - docker run --rm --interactive -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes$(DOCKER_VOL_OPTS) -v $${migrate_tmp_dir}:/build$(DOCKER_VOL_OPTS) -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \ + docker run --rm --interactive -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes$(DOCKER_VOL_OPTS) -v $${migrate_tmp_dir}:/build$(DOCKER_VOL_OPTS) -e GOOS=$(OS) -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \ /bin/bash -c "CGO_ENABLED=0 GO111MODULE=off go build -o /build/migrate k8s.io/kubernetes/cluster/images/etcd/migrate"; \ $(BIN_INSTALL) $${migrate_tmp_dir}/migrate $(TEMP_DIR); \ - docker run --rm --interactive -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes$(DOCKER_VOL_OPTS) -v $${migrate_tmp_dir}:/build$(DOCKER_VOL_OPTS) -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \ + docker run --rm --interactive -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes$(DOCKER_VOL_OPTS) -v $${migrate_tmp_dir}:/build$(DOCKER_VOL_OPTS) -e GOOS=$(OS) -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \ /bin/bash -c "CGO_ENABLED=0 GO111MODULE=off go build -o /build/cp k8s.io/kubernetes/cluster/images/etcd/cp"; \ $(BIN_INSTALL) $${migrate_tmp_dir}/cp $(TEMP_DIR); @@ -104,6 +134,17 @@ ifeq ($(ARCH),amd64) # Do not compile if we should make an image for amd64, use the official etcd binaries instead # For each release create a tmp dir 'etcd_release_tmp_dir' and unpack the release tar there. +ifeq ($(OS),windows) + for version in $(BUNDLED_ETCD_VERSIONS); do \ + etcd_release_tmp_dir=$(shell mktemp -d); \ + curl -sSL --retry 5 https://github.com/coreos/etcd/releases/download/v$$version/etcd-v$$version-windows-amd64.zip -o etcd-v$$version-windows-amd64.zip; \ + unzip -q -d $$etcd_release_tmp_dir etcd-v$$version-windows-amd64.zip; \ + rm etcd-v$$version-windows-amd64.zip; \ + $(BIN_INSTALL) $$etcd_release_tmp_dir/etcd-v$$version-windows-amd64/etcd.exe $$etcd_release_tmp_dir/etcd-v$$version-windows-amd64/etcdctl.exe $(TEMP_DIR)/; \ + $(BIN_INSTALL) $(TEMP_DIR)/etcd.exe $(TEMP_DIR)/etcd-$$version.exe; \ + $(BIN_INSTALL) $(TEMP_DIR)/etcdctl.exe $(TEMP_DIR)/etcdctl-$$version.exe; \ + done +else for version in $(BUNDLED_ETCD_VERSIONS); do \ etcd_release_tmp_dir=$(shell mktemp -d); \ curl -sSL --retry 5 https://github.com/coreos/etcd/releases/download/v$$version/etcd-v$$version-linux-amd64.tar.gz | tar -xz -C $$etcd_release_tmp_dir --strip-components=1; \ @@ -111,6 +152,8 @@ ifeq ($(ARCH),amd64) $(BIN_INSTALL) $(TEMP_DIR)/etcd $(TEMP_DIR)/etcd-$$version; \ $(BIN_INSTALL) $(TEMP_DIR)/etcdctl $(TEMP_DIR)/etcdctl-$$version; \ done +endif + else # Download etcd in a golang container and cross-compile it statically @@ -142,7 +185,7 @@ else # The multiarch feature is in an limited and experimental state right now, and etcd should work fine on arm64 # On arm (which is 32-bit), it can't handle >1GB data in-memory, but it is very unlikely someone tinkering with their limited arm devices would reach such a high usage # ppc64le is still quite untested, but compiles and is probably in the process of being validated by IBM. - cd $(TEMP_DIR) && echo "ENV ETCD_UNSUPPORTED_ARCH=$(ARCH)" >> Dockerfile + cd $(TEMP_DIR) && echo "ENV ETCD_UNSUPPORTED_ARCH=$(ARCH)" >> $(DOCKERFILE) endif docker run --rm --privileged multiarch/qemu-user-static:$(QEMUVERSION) --reset -p yes @@ -152,33 +195,45 @@ endif # And build the image docker buildx build \ --pull \ - --load \ - --platform linux/$(ARCH) \ - -t $(REGISTRY)/etcd-$(ARCH):$(IMAGE_TAG) \ + --output=type=$(OUTPUT_TYPE) \ + --platform "$(OS)/$(ARCH)" \ + -t $(REGISTRY)/etcd:$(IMAGE_TAG)-$(IMAGE_SUFFIX) \ --build-arg BASEIMAGE=$(BASEIMAGE) \ --build-arg RUNNERIMAGE=$(RUNNERIMAGE) \ + -f $(TEMP_DIR)/$(DOCKERFILE) \ $(TEMP_DIR) docker buildx rm $$BUILDER push: build - docker tag $(REGISTRY)/etcd-$(ARCH):$(IMAGE_TAG) $(MANIFEST_IMAGE)-$(ARCH):$(IMAGE_TAG) - docker push $(MANIFEST_IMAGE)-$(ARCH):$(IMAGE_TAG) + +# split words on hyphen, access by 1-index +word-hyphen = $(word $2,$(subst -, ,$1)) sub-build-%: - $(MAKE) ARCH=$* build + $(MAKE) OUTPUT_TYPE=docker OS=$(call word-hyphen,$*,1) ARCH=$(call word-hyphen,$*,2) build -all-build: $(addprefix sub-build-,$(ALL_ARCH)) +all-build: $(addprefix sub-build-,$(ALL_OS_ARCH)) sub-push-image-%: - $(MAKE) ARCH=$* push + $(MAKE) OUTPUT_TYPE=registry OS=$(call word-hyphen,$*,1) ARCH=$(call word-hyphen,$*,2) OSVERSION=$(call word-hyphen,$*,3) REGISTRY=$(PUSH_REGISTRY) push -all-push-images: $(addprefix sub-push-image-,$(ALL_ARCH)) +all-push-images: $(addprefix sub-push-image-,$(ALL_OS_ARCH)) +# NOTE(claudiub): A non-default builder instance is needed in order to build Windows images. all-push: all-push-images push-manifest push-manifest: - docker manifest create --amend $(MANIFEST_IMAGE):$(IMAGE_TAG) $(shell echo $(ALL_ARCH) | sed -e "s~[^ ]*~$(MANIFEST_IMAGE)\-&:$(IMAGE_TAG)~g") - @for arch in $(ALL_ARCH); do docker manifest annotate --arch $${arch} ${MANIFEST_IMAGE}:${IMAGE_TAG} ${MANIFEST_IMAGE}-$${arch}:${IMAGE_TAG}; done + docker manifest create --amend $(MANIFEST_IMAGE):$(IMAGE_TAG) $(shell echo $(ALL_OS_ARCH) | sed -e "s~[^ ]*~$(MANIFEST_IMAGE):$(IMAGE_TAG)\-&~g") + set -x; for arch in $(ALL_ARCH.linux); do docker manifest annotate --os linux --arch $${arch} ${MANIFEST_IMAGE}:${IMAGE_TAG} ${MANIFEST_IMAGE}:${IMAGE_TAG}-linux-$${arch}; done + # For Windows images, we also need to include the "os.version" in the manifest list, so the Windows node can pull the proper image it needs. + # we use awk to also trim the quotes around the OS version string. + set -x; \ + for arch in $(ALL_ARCH.windows); do \ + for osversion in ${ALL_OSVERSIONS.windows}; do \ + full_version=`docker manifest inspect ${BASE.windows}:$${osversion} | grep "os.version" | head -n 1 | awk -F\" '{print $$4}'` || true; \ + docker manifest annotate --os windows --arch $${arch} --os-version $${full_version} ${MANIFEST_IMAGE}:${IMAGE_TAG} ${MANIFEST_IMAGE}:${IMAGE_TAG}-windows-$${arch}-$${osversion}; \ + done; \ + done docker manifest push --purge ${MANIFEST_IMAGE}:${IMAGE_TAG} unit-test: