1# Copyright 2016 The Kubernetes Authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# Build the etcd image
16#
17# Usage:
18# [BUNDLED_ETCD_VERSIONS=3.4.18 3.5.8] [REGISTRY=registry.k8s.io] [ARCH=amd64] [BASEIMAGE=busybox] make (build|push)
19#
20# The image contains different etcd versions to simplify
21# upgrades. Thus be careful when removing any versions from here.
22#
23# NOTE: The etcd upgrade rules are that you can upgrade only 1 minor
24# version at a time, and patch release don't matter.
25#
26# Except from etcd-$(version) and etcdctl-$(version) binaries, we also
27# need etcd and etcdctl binaries for backward compatibility reasons.
28# That binary will be set to the last version from $(BUNDLED_ETCD_VERSIONS).
29BUNDLED_ETCD_VERSIONS?=3.4.18 3.5.12
30
31# LATEST_ETCD_VERSION identifies the most recent etcd version available.
32LATEST_ETCD_VERSION?=3.5.12
33
34# REVISION provides a version number for this image and all it's bundled
35# artifacts. It should start at zero for each LATEST_ETCD_VERSION and increment
36# for each revision of this image at that etcd version.
37REVISION?=0
38
39# IMAGE_TAG Uniquely identifies registry.k8s.io/etcd docker image with a tag of the form "<etcd-version>-<revision>".
40IMAGE_TAG=$(LATEST_ETCD_VERSION)-$(REVISION)
41
42ARCH?=amd64
43
44# Operating systems supported: linux, windows
45OS ?= linux
46# OS Version for the Windows images: 1809, ltsc2022
47OSVERSION ?= 1809
48
49# The output type could either be docker (local), or registry.
50# If it is registry, it will also allow us to push the Windows images.
51OUTPUT_TYPE ?= docker
52
53ALL_OS = linux windows
54ALL_ARCH.linux = amd64 arm arm64 ppc64le s390x
55ALL_OS_ARCH.linux = $(foreach arch, ${ALL_ARCH.linux}, linux-$(arch))
56ALL_ARCH.windows = amd64
57ALL_OSVERSIONS.windows := 1809 ltsc2022
58ALL_OS_ARCH.windows = $(foreach arch, $(ALL_ARCH.windows), $(foreach osversion, ${ALL_OSVERSIONS.windows}, windows-$(arch)-${osversion}))
59ALL_OS_ARCH = $(foreach os, $(ALL_OS), ${ALL_OS_ARCH.${os}})
60
61IMAGE_SUFFIX.linux = $(OS)-$(ARCH)
62IMAGE_SUFFIX.windows = $(OS)-$(ARCH)-$(OSVERSION)
63IMAGE_SUFFIX := ${IMAGE_SUFFIX.${OS}}
64
65# Image should be pulled from registry.k8s.io, which will auto-detect
66# region (us, eu, asia, ...) and pull from the closest.
67REGISTRY?=registry.k8s.io
68# Images should be pushed to staging-k8s.gcr.io.
69PUSH_REGISTRY?=staging-k8s.gcr.io
70
71MANIFEST_IMAGE := $(PUSH_REGISTRY)/etcd
72
73# Install binaries matching base distro permissions
74BIN_INSTALL := install -m 0555
75
76# Hosts running SELinux need :z added to volume mounts
77SELINUX_ENABLED := $(shell cat /sys/fs/selinux/enforce 2> /dev/null || echo 0)
78
79ifeq ($(SELINUX_ENABLED),1)
80 DOCKER_VOL_OPTS?=:z
81endif
82
83# This option is for running docker manifest command
84export DOCKER_CLI_EXPERIMENTAL := enabled
85# golang version should match the golang version of the official build from https://github.com/etcd-io/etcd/releases.
86GOLANG_VERSION := 1.20.13
87GOARM?=7
88TEMP_DIR:=$(shell mktemp -d)
89
90DOCKERFILE.linux = Dockerfile
91DOCKERFILE.windows = Dockerfile.windows
92DOCKERFILE := ${DOCKERFILE.${OS}}
93
94ifeq ($(ARCH),amd64)
95 BASEIMAGE?=registry.k8s.io/build-image/debian-base:bookworm-v1.0.2
96endif
97ifeq ($(ARCH),arm)
98 BASEIMAGE?=registry.k8s.io/build-image/debian-base-arm:bookworm-v1.0.2
99endif
100ifeq ($(ARCH),arm64)
101 BASEIMAGE?=registry.k8s.io/build-image/debian-base-arm64:bookworm-v1.0.2
102endif
103ifeq ($(ARCH),ppc64le)
104 BASEIMAGE?=registry.k8s.io/build-image/debian-base-ppc64le:bookworm-v1.0.2
105endif
106ifeq ($(ARCH),s390x)
107 BASEIMAGE?=registry.k8s.io/build-image/debian-base-s390x:bookworm-v1.0.2
108endif
109
110BASE.windows = mcr.microsoft.com/windows/nanoserver
111
112RUNNERIMAGE.windows?=$(BASE.windows):$(OSVERSION)
113RUNNERIMAGE.linux?=gcr.io/distroless/static:latest
114RUNNERIMAGE := ${RUNNERIMAGE.${OS}}
115
116QEMUVERSION?=5.2.0-2
117
118build:
119 # Explicitly copy files to the temp directory
120 $(BIN_INSTALL) migrate-if-needed.sh $(TEMP_DIR)
121 $(BIN_INSTALL) migrate-if-needed.bat $(TEMP_DIR)
122 install $(DOCKERFILE) $(TEMP_DIR)
123
124 # Compile migrate
125 migrate_tmp_dir=$(shell mktemp -d); \
126 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) \
127 /bin/bash -c "CGO_ENABLED=0 GO111MODULE=off go build -o /build/migrate k8s.io/kubernetes/cluster/images/etcd/migrate"; \
128 $(BIN_INSTALL) $${migrate_tmp_dir}/migrate $(TEMP_DIR);
129
130ifeq ($(ARCH),amd64)
131
132 # Do not compile if we should make an image for amd64, use the official etcd binaries instead
133 # For each release create a tmp dir 'etcd_release_tmp_dir' and unpack the release tar there.
134ifeq ($(OS),windows)
135 for version in $(BUNDLED_ETCD_VERSIONS); do \
136 etcd_release_tmp_dir=$(shell mktemp -d); \
137 curl -sSL --retry 5 https://github.com/etcd-io/etcd/releases/download/v$$version/etcd-v$$version-windows-amd64.zip -o etcd-v$$version-windows-amd64.zip; \
138 unzip -q -d $$etcd_release_tmp_dir etcd-v$$version-windows-amd64.zip; \
139 rm etcd-v$$version-windows-amd64.zip; \
140 $(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)/; \
141 $(BIN_INSTALL) $(TEMP_DIR)/etcd.exe $(TEMP_DIR)/etcd-$$version.exe; \
142 $(BIN_INSTALL) $(TEMP_DIR)/etcdctl.exe $(TEMP_DIR)/etcdctl-$$version.exe; \
143 done
144else
145 for version in $(BUNDLED_ETCD_VERSIONS); do \
146 etcd_release_tmp_dir=$(shell mktemp -d); \
147 curl -sSL --retry 5 https://github.com/etcd-io/etcd/releases/download/v$$version/etcd-v$$version-linux-amd64.tar.gz | tar -xz -C $$etcd_release_tmp_dir --strip-components=1; \
148 $(BIN_INSTALL) $$etcd_release_tmp_dir/etcd $$etcd_release_tmp_dir/etcdctl $(TEMP_DIR)/; \
149 $(BIN_INSTALL) $(TEMP_DIR)/etcd $(TEMP_DIR)/etcd-$$version; \
150 $(BIN_INSTALL) $(TEMP_DIR)/etcdctl $(TEMP_DIR)/etcdctl-$$version; \
151 done
152endif
153
154else
155
156 # Download etcd in a golang container and cross-compile it statically
157 # For each release create a tmp dir 'etcd_release_tmp_dir' and unpack the release tar there.
158 arch_prefix=""
159 ifeq ($(ARCH),arm)
160 arch_prefix="GOARM=$(GOARM)"
161 endif
162
163 # use '/go/src/go.etcd.io/etcd' to build etcd 3.4 and later.
164 for version in $(BUNDLED_ETCD_VERSIONS); do \
165 etcd_release_tmp_dir=$(shell mktemp -d); \
166 etcd_build_dir="/go/src/github.com/coreos/etcd"; \
167 if [ $$(echo $$version | cut -d. -f2) -gt 3 ]; then \
168 etcd_build_dir="/go/src/go.etcd.io/etcd"; \
169 fi; \
170 docker run --rm --interactive -v $${etcd_release_tmp_dir}:/etcdbin golang:$(GOLANG_VERSION)$(DOCKER_VOL_OPTS) /bin/bash -c \
171 "git clone https://github.com/etcd-io/etcd $$etcd_build_dir \
172 && cd $$etcd_build_dir \
173 && git checkout v$${version} \
174 && $(arch_prefix) GOARCH=$(ARCH) ./build.sh \
175 && cp -f bin/$(ARCH)/etcd* bin/etcd* /etcdbin; echo 'done'"; \
176 $(BIN_INSTALL) $$etcd_release_tmp_dir/etcd $$etcd_release_tmp_dir/etcdctl $(TEMP_DIR)/; \
177 $(BIN_INSTALL) $(TEMP_DIR)/etcd $(TEMP_DIR)/etcd-$$version; \
178 $(BIN_INSTALL) $(TEMP_DIR)/etcdctl $(TEMP_DIR)/etcdctl-$$version; \
179 done
180
181 # Add this ENV variable in order to workaround an unsupported arch blocker
182 # On arm (which is 32-bit), it can't handle >1GB data in-memory
183 ifeq ($(ARCH),arm)
184 cd $(TEMP_DIR) && echo "ENV ETCD_UNSUPPORTED_ARCH=$(ARCH)" >> $(DOCKERFILE)
185 endif
186endif
187
188 docker run --rm --privileged multiarch/qemu-user-static:$(QEMUVERSION) --reset -p yes
189 docker buildx version
190 BUILDER=$(shell docker buildx create --use)
191
192 # And build the image
193 docker buildx build \
194 --pull \
195 --provenance=false \
196 --sbom=false \
197 --output=type=$(OUTPUT_TYPE) \
198 --platform "$(OS)/$(ARCH)" \
199 -t $(REGISTRY)/etcd:$(IMAGE_TAG)-$(IMAGE_SUFFIX) \
200 --build-arg BASEIMAGE=$(BASEIMAGE) \
201 --build-arg RUNNERIMAGE=$(RUNNERIMAGE) \
202 -f $(TEMP_DIR)/$(DOCKERFILE) \
203 $(TEMP_DIR)
204 docker buildx rm $$BUILDER
205
206push: build
207
208# split words on hyphen, access by 1-index
209word-hyphen = $(word $2,$(subst -, ,$1))
210
211sub-build-%:
212 $(MAKE) OUTPUT_TYPE=docker OS=$(call word-hyphen,$*,1) ARCH=$(call word-hyphen,$*,2) build
213
214all-build: $(addprefix sub-build-,$(ALL_OS_ARCH))
215
216sub-push-image-%:
217 $(MAKE) OUTPUT_TYPE=registry OS=$(call word-hyphen,$*,1) ARCH=$(call word-hyphen,$*,2) OSVERSION=$(call word-hyphen,$*,3) REGISTRY=$(PUSH_REGISTRY) push
218
219all-push-images: $(addprefix sub-push-image-,$(ALL_OS_ARCH))
220
221# NOTE(claudiub): A non-default builder instance is needed in order to build Windows images.
222all-push: all-push-images push-manifest
223
224push-manifest:
225 docker manifest create --amend $(MANIFEST_IMAGE):$(IMAGE_TAG) $(shell echo $(ALL_OS_ARCH) | sed -e "s~[^ ]*~$(MANIFEST_IMAGE):$(IMAGE_TAG)\-&~g")
226 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
227 # 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.
228 # we use awk to also trim the quotes around the OS version string.
229 set -x; \
230 for arch in $(ALL_ARCH.windows); do \
231 for osversion in ${ALL_OSVERSIONS.windows}; do \
232 full_version=`docker manifest inspect ${BASE.windows}:$${osversion} | grep "os.version" | head -n 1 | awk -F\" '{print $$4}'` || true; \
233 docker manifest annotate --os windows --arch $${arch} --os-version $${full_version} ${MANIFEST_IMAGE}:${IMAGE_TAG} ${MANIFEST_IMAGE}:${IMAGE_TAG}-windows-$${arch}-$${osversion}; \
234 done; \
235 done
236 docker manifest push --purge ${MANIFEST_IMAGE}:${IMAGE_TAG}
237
238unit-test:
239 docker run --rm --interactive -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes$(DOCKER_VOL_OPTS) -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \
240 /bin/bash -c "CGO_ENABLED=0 go test -v k8s.io/kubernetes/cluster/images/etcd/migrate"
241
242# Integration tests require both a golang build environment and all the etcd binaries from a `registry.k8s.io/etcd` image (`/usr/local/bin/etcd-<version>`, ...).
243# Since the `registry.k8s.io/etcd` image is for runtime only and does not have a build golang environment, we create a new docker image to run integration tests
244# with.
245build-integration-test-image: build
246 cp -r $(TEMP_DIR) $(TEMP_DIR)_integration_test
247 cp Dockerfile $(TEMP_DIR)_integration_test/Dockerfile
248 docker build \
249 --pull \
250 -t etcd-integration-test \
251 --build-arg BASEIMAGE=golang:$(GOLANG_VERSION) \
252 --build-arg RUNNERIMAGE=$(RUNNERIMAGE) \
253 $(TEMP_DIR)_integration_test
254
255integration-test:
256 docker run --rm --interactive -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes$(DOCKER_VOL_OPTS) -e GOARCH=$(ARCH) etcd-integration-test \
257 /bin/bash -c "CGO_ENABLED=0 go test -tags=integration k8s.io/kubernetes/cluster/images/etcd/migrate -args -v 10 -logtostderr true"
258
259integration-build-test: build-integration-test-image integration-test
260test: unit-test integration-build-test
261all: all-build test
262.PHONY: build push push-manifest all-push all-push-images all-build unit-test build-integration-test-image integration-test integration-build-test test
View as plain text