前言:
目前arm系統(tǒng)越來(lái)越常見(jiàn),對(duì)鏡像的多架構(gòu)需求也越來(lái)越大。對(duì)于同一個(gè)鏡像,最簡(jiǎn)單的辦法就是在amd64或arm機(jī)器上build后通過(guò)不同的tag進(jìn)行區(qū)分,比如 nginx:v1-amd64 、 nginx:v1-arm64 ,但這種方式比較丑陋,而且沒(méi)有對(duì)應(yīng)架構(gòu)的機(jī)器用來(lái)構(gòu)建怎么辦?
目前最新的辦法就是使用buildx來(lái)進(jìn)行構(gòu)建,不過(guò)這個(gè)特性目前默認(rèn)是沒(méi)啟用的,需要在docker的配置文件中添加 "experimental": true 后重啟docker服務(wù)生效。
首先執(zhí)行下面的命令讓amd64的機(jī)器也可以構(gòu)建arm的鏡像:
docker run --rm --privileged tonistiigi/binfmt:latest --install all
然后創(chuàng)建一個(gè)新的build實(shí)例:
docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master
這樣準(zhǔn)備工作就全都做好了。
接下來(lái)以一個(gè)kubebuilder命令創(chuàng)建的operator項(xiàng)目默認(rèn)的Dockerfile為例:
# Build the manager binary FROM --platform=$TARGETPLATFORM golang:1.16 as builder ARG TARGETOS TARGETARCH WORKDIR /workspace # Copy the Go Modules manifests COPY go.mod go.mod COPY go.sum go.sum # cache deps before building and copying source so that we don't need to re-download as much # and so that source changes don't invalidate our downloaded layer RUN go mod download # Copy the go source COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ # Build RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM --platform=$TARGETPLATFORM gcr.io/distroless/static:nonroot WORKDIR / COPY --from=builder /workspace/manager . USER 65532:65532 ENTRYPOINT ["/manager"]
修改點(diǎn)有2個(gè):
--platform=$TARGETPLATFORM GOOS=${TARGETOS} GOARCH=${TARGETARCH}
這些TARGET開(kāi)頭的變量可以在參考鏈接2里看到全部含義。
接下來(lái)使用這個(gè)文件進(jìn)行構(gòu)建:
docker buildx build -t hello/namespace/name:v1 -f Dockerfile . --platform linux/amd64,linux/arm64 --push
注意這里的 buildx 、 –platform 參數(shù)后面跟隨需要構(gòu)建的版本、以及 –push ,buildx構(gòu)建的多架構(gòu)鏡像要么使用這個(gè)參數(shù)push到鏡像倉(cāng)庫(kù),要么使用 –load 加載到本地,不可省略。
構(gòu)建完成后就會(huì)生成相應(yīng)的多架構(gòu)鏡像了,可以使用 docker manifest inspect 來(lái)進(jìn)行驗(yàn)證,比如:
docker manifest inspect hello/namespace/name:v1 { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", "manifests": [ { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 1367, "digest": "sha256:a7b99854e13939e3beaf21c1da8b4654022018eda9f438155b18ae16eeff49a5", "platform": { "architecture": "amd64", "os": "linux" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 2169, "digest": "sha256:844885928813685ffa8c5ea4c6e9e7a09d95ac79e417afae0be7b73086762cfd", "platform": { "architecture": "arm64", "os": "linux" } } ] }
可以看出確實(shí)是生成了多個(gè)架構(gòu)的鏡像,使用時(shí)直接在不同架構(gòu)的機(jī)器上pull就會(huì)自動(dòng)下載對(duì)應(yīng)的架構(gòu)鏡像了。或者也可以使用 docker pull –platform arm64|amd64 xxxxx 來(lái)指定拉取鏡像的架構(gòu)。
對(duì)于某些沒(méi)有辦法使用buildx的場(chǎng)景,我們可以手動(dòng)build不同架構(gòu)的鏡像,然后再手動(dòng)創(chuàng)建manifest,比如:
# 指定拉取amd64架構(gòu) docker pull --platform amd64 gcr.io/distroless/static:nonroot # 重新打tag docker tag 9ef34 hello/ns/static:nonroot-amd64 # 推送 docker push hello/ns/static:nonroot-amd64 # 指定拉取arm64架構(gòu) docker pull --platform arm64 gcr.io/distroless/static:nonroot # 重新打tag docker tag 91714 hello/ns/static:nonroot-arm64 # 推送 docker push hello/ns/static:nonroot-arm64 ## 制作manifest docker manifest create hello/ns/static:nonroot hello/ns/static:nonroot-amd64 hello/ns/static:nonroot-arm64 docker manifest push hello/ns/static:nonroot docker manifest rm hello/ns/static:nonroot
重點(diǎn)是最后3行,push manifest后使用就和第一種方法一樣了。
另外如果想進(jìn)行多架構(gòu)構(gòu)建有幾個(gè)注意點(diǎn):
yum|apt install
其他 查看鏡像倉(cāng)庫(kù)里都有哪些鏡像:
curl -u "用戶(hù)名":"密碼" -X GET http://鏡像地址:5000/v2/_catalog?n=2000 | python -m json.tool
查看鏡像有哪些tag:
curl -u "用戶(hù)名":"密碼" -X GET http://鏡像地址:5000/v2/命名空間/鏡像名稱(chēng)/tags/list | python -m json.tool