使用 docker manifest 構建跨平臺鏡像
在當今的軟件開發領域中,構建跨平臺應用程序已經成爲了一個普遍存在的需求。不同的操作系統、硬件架構需要不同的鏡像環境來支持它們。Docker 作爲一個廣泛應用的容器化技術,必然需要能夠支持構建跨平臺鏡像,本文將介紹如何使用 docker manifest
來實現構建跨平臺鏡像。
簡介
docker manifest
是 Docker 的一個命令,它提供了一種方便的方式來管理不同操作系統和硬件架構的 Docker 鏡像。通過 docker manifest
,用戶可以創建一個虛擬的 Docker 鏡像,其中包含了多個實際的 Docker 鏡像,每個實際的 Docker 鏡像對應一個不同的操作系統和硬件架構。
docker manifest
命令本身並不執行任何操作。爲了操作一個 manifest
或 manifest list
,必須使用其中一個子命令。
manifest
可以理解爲是一個 JSON 文件,單個 manifest
包含有關鏡像的信息,例如層(layers)、大小(size)和摘要(digest)等。
manifest list
是通過指定一個或多個(理想情況下是多個)鏡像名稱創建的鏡像列表(即上面所說的虛擬 Docker 鏡像)。可以像普通鏡像一樣使用 docker pull
和 docker run
等命令來操作它。manifest list
通常被稱爲「多架構鏡像」。
注意:
docker manifest
命令是實驗性的,還未轉正。旨在用於測試和反饋,因此其功能和用法可能會在不同版本之間發生變化。
準備工作
工欲善其事,必先利其器,如果想使用 docker manifest
構建多架構鏡像,需要具備以下條件。
-
機器上安裝了 Docker。
-
需要註冊一個 Docker Hub 賬號。
-
最少有兩個不同平臺的主機,用來驗證
docker manifest
鎖構建出來的多架構鏡像正確性(可選)。 -
聯網,
docker manifest
命令是需要聯網使用的。
爲不同平臺構建鏡像
本文中演示程序所使用的環境是 Apple M2 芯片平臺。本地的 Docker 版本如下:
$ docker version
Client:
Cloud integration: v1.0.29
Version: 20.10.21
API version: 1.41
Go version: go1.18.7
Git commit: baeda1f
Built: Tue Oct 25 18:01:18 2022
OS/Arch: darwin/arm64
Context: default
Experimental: true
Server: Docker Desktop 4.15.0 (93002)
Engine:
Version: 20.10.21
API version: 1.41 (minimum version 1.12)
Go version: go1.18.7
Git commit: 3056208
Built: Tue Oct 25 17:59:41 2022
OS/Arch: linux/arm64
Experimental: false
containerd:
Version: 1.6.10
GitCommit: 770bd0108c32f3fb5c73ae1264f7e503fe7b2661
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0
準備 Dockerfile
首先準備如下 Dockerfile 文件,用來構建鏡像。
FROM alpine
RUN uname -a > /os.txt
CMD cat /os.txt
這個鏡像非常簡單,構建時將 uname -a
命令輸出信息(即當前操作系統的相關信息)寫入 /os.txt
,運行時將 /os.txt
內容輸出。
構建 arm64 平臺鏡像
因爲本機爲 Apple M2 芯片,所以使用 docker build
命令構建鏡像默認爲 arm64
平臺鏡像。構建命令如下:
$ docker build -t jianghushinian/echo-platform-arm64 .
[+] Building 15.6s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 94B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 15.5s
=> [1/2] FROM docker.io/library/alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc01 0.0s
=> CACHED [2/2] RUN uname -a > /os.txt 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:f017783a39920aa4646f87d7e5a2d67ab51aab479147d60e5372f8749c3742bb 0.0s
=> => naming to docker.io/jianghushinian/echo-platform-arm64 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
注意:
jianghushinian
是我的 Docker Hub 用戶名,你在構建鏡像時應該使用自己的 Docker Hub 用戶名。
如果得到如上類似輸出,表明構建成功。
使用 docker run
運行容器進行測試:
$ docker run --rm jianghushinian/echo-platform-arm64
Linux buildkitsandbox 5.15.49-linuxkit #1 SMP PREEMPT Tue Sep 13 07:51:32 UTC 2022 aarch64 Linux
輸出內容中的 aarch64
就表示 ARMv8
架構。
現在我們需要將鏡像推送到 Docker Hub,確保在命令行中已經使用 docker login
登錄過 Docker Hub 的情況下,使用 docker push
命令推送鏡像:
$ docker push jianghushinian/echo-platform-arm64
Using default tag: latest
The push refers to repository [docker.io/jianghushinian/echo-platform-arm64]
dd0468cb6cb1: Pushed
07d3c46c9599: Mounted from jianghushinian/demo-arm64
latest: digest: sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583 size: 735
瀏覽器中登錄 Docker Hub 查看推送成功的鏡像:
echo-platform-arm64
構建 amd64 平臺鏡像
無需切換設備,在 Apple M2 芯片的機器上我們可以直接構建 amd64
也就是 Linux 平臺鏡像,docker build
命令提供了 --platform
參數可以構建跨平臺鏡像。
$ docker build --platform=linux/amd64 -t jianghushinian/echo-platform-amd64 .
[+] Building 15.7s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 36B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 15.3s
=> CACHED [1/2] FROM docker.io/library/alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300 0.0s
=> [2/2] RUN uname -a > /os.txt 0.2s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:5c48af5176402727627cc18136d78f87f0793ccf61e3e3fb4df98391a69e9f70 0.0s
=> => naming to docker.io/jianghushinian/echo-platform-amd64 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
鏡像構建成功後,同樣使用 docker push
命令推送鏡像到 Docker Hub:
$ docker push jianghushinian/echo-platform-amd64
Using default tag: latest
The push refers to repository [docker.io/jianghushinian/echo-platform-amd64]
9499dee27c9f: Pushed
8d3ac3489996: Mounted from jianghushinian/demo-amd64
latest: digest: sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359 size: 735
瀏覽器中登錄 Docker Hub 查看推送成功的鏡像:
你也許會好奇,在 Apple M2 芯片的主機設備上運行 amd64
平臺鏡像會怎樣。目前咱們構建的這個簡單鏡像其實是能夠運行的,只不過會得到一條警告信息:
$ docker run --rm jianghushinian/echo-platform-amd64
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
Linux buildkitsandbox 5.15.49-linuxkit #1 SMP PREEMPT Tue Sep 13 07:51:32 UTC 2022 x86_64 Linux
輸出內容中的 x86_64
就表示 AMD64
架構。
注意:雖然這個簡單的鏡像能夠運行成功,但如果容器內部程序不支持跨平臺,
amd64
平臺鏡像無法在arm64
平臺運行成功。
同樣的,如果我們登錄到一臺 amd64
架構的設備上運行 arm64
平臺鏡像,也會得到一條警告信息:
# docker run --rm jianghushinian/echo-platform-arm64
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
Linux buildkitsandbox 5.15.49-linuxkit #1 SMP PREEMPT Tue Sep 13 07:51:32 UTC 2022 aarch64 Linux
在 amd64
架構的設備上運行 amd64
平臺鏡像則不會遇到警告問題:
# docker run --rm jianghushinian/echo-platform-amd64
Linux buildkitsandbox 5.15.49-linuxkit #1 SMP PREEMPT Tue Sep 13 07:51:32 UTC 2022 x86_64 Linux
使用 manifest 合併多平臺鏡像
我們可以使用 docker manifest
的子命令 create
創建一個 manifest list
,即將多個平臺的鏡像合併爲一個鏡像。
create
命令用法很簡單,後面跟的第一個參數 jianghushinian/echo-platform
即爲合併後的鏡像,從第二個參數開始可以指定一個或多個不同平臺的鏡像。
$ docker manifest create jianghushinian/echo-platform jianghushinian/echo-platform-arm64 jianghushinian/echo-platform-amd64
Created manifest list docker.io/jianghushinian/echo-platform:latest
如上輸出,表明多架構鏡像構建成功。
注意:在使用
docker manifest create
命令時,確保待合併鏡像都已經被推送到 Docker Hub 鏡像倉庫,不然報錯no such manifest
。這也是爲什麼前文在構建鏡像時,都會將鏡像推送到 Docker Hub。
此時在 Apple M2 芯片設備上使用 docker run
啓動構建好的跨平臺鏡像 jianghushinian/echo-platform
:
$ docker run --rm jianghushinian/echo-platform
Linux buildkitsandbox 5.4.0-80-generic #90-Ubuntu SMP Fri Jul 9 22:49:44 UTC 2021 aarch64 Linux
沒有任何問題,就像在啓動 jianghushinian/echo-platform-arm64
鏡像一樣。
現在我們可以將這個跨平臺鏡像推送到 Docker Hub,不過,這回我們需要使用的命令不再是 docker push
而是 manifest
的子命令 docker manifest push
:
$ docker manifest push jianghushinian/echo-platform
Pushed ref docker.io/jianghushinian/echo-platform@sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359 with digest: sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359
Pushed ref docker.io/jianghushinian/echo-platform@sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583 with digest: sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583
sha256:87b51c1835f13bb722bbb4279fcf50a6da0ecb852433a8f1c04e2f5fe93ac055
瀏覽器中登錄 Docker Hub 查看推送成功的鏡像:
echo-platform
進入鏡像信息詳情頁面的 Tags
標籤,能夠看到鏡像支持 amd64
、arm64/v8
這兩個平臺。
現在,我們可以在 amd64
架構的設備上同樣使用 docker run
命令啓動構建好的跨平臺鏡像 jianghushinian/echo-platform
:
# docker run --rm jianghushinian/echo-platform
Linux buildkitsandbox 5.4.0-80-generic #90-Ubuntu SMP Fri Jul 9 22:49:44 UTC 2021 x86_64 Linux
輸出結果沒有任何問題。可以發現,無論是 arm64
設備還是 amd64
設備,雖然同樣使用 docker run --rm jianghushinian/echo-platform
命令啓動鏡像,但它們的輸出結果都表明啓動的是當前平臺的鏡像,沒有再次出現警告。
manifest 功能清單
docker manifest
不止有 create
一個子命令,可以通過 --help/-h
參數查看使用幫助:
$ docker manifest --help
Usage: docker manifest COMMAND
The **docker manifest** command has subcommands for managing image manifests and
manifest lists. A manifest list allows you to use one name to refer to the same image
built for multiple architectures.
To see help for a subcommand, use:
docker manifest CMD --help
For full details on using docker manifest lists, see the registry v2 specification.
EXPERIMENTAL:
docker manifest is an experimental feature.
Experimental features provide early access to product functionality. These
features may change between releases without warning, or can be removed from a
future release. Learn more about experimental features in our documentation:
https://docs.docker.com/go/experimental/
Commands:
annotate Add additional information to a local image manifest
create Create a local manifest list for annotating and pushing to a registry
inspect Display an image manifest, or manifest list
push Push a manifest list to a repository
rm Delete one or more manifest lists from local storage
Run 'docker manifest COMMAND --help' for more information on a command.
可以發現,docker manifest
共提供了 annotate
、create
、inspect
、push
、rm
這 5 個子命。
接下來我們分別看下這幾個子命令的功能。
create
先從最熟悉的 create
子命令看起,來看下它都支持哪些功能。
$ docker manifest create -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker manifest create MANIFEST_LIST MANIFEST [MANIFEST...]
Create a local manifest list for annotating and pushing to a registry
EXPERIMENTAL:
docker manifest create is an experimental feature.
Experimental features provide early access to product functionality. These
features may change between releases without warning, or can be removed from a
future release. Learn more about experimental features in our documentation:
https://docs.docker.com/go/experimental/
Options:
-a, --amend Amend an existing manifest list
--insecure Allow communication with an insecure registry
筆記:可以看到輸出結果第一行的提示,短標誌
-h
已經被棄用,推薦使用--help
查看子命令幫助信息。
可以發現,create
子命令支持兩個可選參數 -a/--amend
用來修訂已存在的多架構鏡像。
指定 --insecure
參數則允許使用不安全的(非 https)鏡像倉庫。
push
push
子命令我們也見過了,使用 push
可以將多架構鏡像推送到鏡像倉庫。
來看下 push
還支持設置哪些可選參數。
$ docker manifest push -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker manifest push [OPTIONS] MANIFEST_LIST
Push a manifest list to a repository
EXPERIMENTAL:
docker manifest push is an experimental feature.
Experimental features provide early access to product functionality. These
features may change between releases without warning, or can be removed from a
future release. Learn more about experimental features in our documentation:
https://docs.docker.com/go/experimental/
Options:
--insecure Allow push to an insecure registry
-p, --purge Remove the local manifest list after push
同樣的,push
也有一個 --insecure
參數允許使用不安全的(非 https)鏡像倉庫。
-p/--purge
選項的作用是推送本地鏡像到遠程倉庫後,刪除本地 manifest list
。
inspect
inspect
用來查看 manifest
/manifest list
所包含的鏡像信息。
其使用幫助如下:
$ docker manifest inspect -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker manifest inspect [OPTIONS] [MANIFEST_LIST] MANIFEST
Display an image manifest, or manifest list
EXPERIMENTAL:
docker manifest inspect is an experimental feature.
Experimental features provide early access to product functionality. These
features may change between releases without warning, or can be removed from a
future release. Learn more about experimental features in our documentation:
https://docs.docker.com/go/experimental/
Options:
--insecure Allow communication with an insecure registry
-v, --verbose Output additional info including layers and platform
--insecure
參數允許使用不安全的(非 https)鏡像倉庫。這已經是我們第三次看見這個參數了,這也驗證了 docker manifest
命令需要聯網才能使用的說法,因爲這些子命令基本都涉及到和遠程鏡像倉庫的交互。
指定 -v/--verbose
參數可以輸出更多信息,包括鏡像的 layers
和 platform
信息。
使用示例如下:
$ docker manifest inspect jianghushinian/echo-platform
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 735,
"digest": "sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 735,
"digest": "sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
}
}
]
}
從輸出信息中可以發現,我們構建的多架構鏡像 jianghushinian/echo-platform
包含兩個 manifest
,可以支持 amd64
/arm64
架構,並且都爲 linux
系統下的鏡像。
指定 -v
參數輸出更詳細信息:
$ docker manifest inspect -v jianghushinian/echo-platform
[
{
"Ref": "docker.io/jianghushinian/echo-platform:latest@sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359",
"Descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359",
"size": 735,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
"SchemaV2Manifest": {
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1012,
"digest": "sha256:5c48af5176402727627cc18136d78f87f0793ccf61e3e3fb4df98391a69e9f70"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2818413,
"digest": "sha256:59bf1c3509f33515622619af21ed55bbe26d24913cedbca106468a5fb37a50c3"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 211,
"digest": "sha256:1e5897976ad1d3969268a18f4f0356a05875baf0225e39768a9066f43e950ebd"
}
]
}
},
{
"Ref": "docker.io/jianghushinian/echo-platform:latest@sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583",
"Descriptor": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583",
"size": 735,
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
}
},
"SchemaV2Manifest": {
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1027,
"digest": "sha256:f017783a39920aa4646f87d7e5a2d67ab51aab479147d60e5372f8749c3742bb"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2715434,
"digest": "sha256:9b3977197b4f2147bdd31e1271f811319dcd5c2fc595f14e81f5351ab6275b99"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 212,
"digest": "sha256:edf2b8e1db64e4f46a2190a3dfcb74ae131ae13ad43fcfedde4c3f304c451f7d"
}
]
}
}
]
annotate
annotate
子命令可以給一個本地鏡像 manifest
添加附加的信息。這有點像 K8s Annotations 的意思。
其使用幫助如下:
$ docker manifest annotate -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker manifest annotate [OPTIONS] MANIFEST_LIST MANIFEST
Add additional information to a local image manifest
EXPERIMENTAL:
docker manifest annotate is an experimental feature.
Experimental features provide early access to product functionality. These
features may change between releases without warning, or can be removed from a
future release. Learn more about experimental features in our documentation:
https://docs.docker.com/go/experimental/
Options:
--arch string Set architecture
--os string Set operating system
--os-features strings Set operating system feature
--os-version string Set operating system version
--variant string Set architecture variant
可選參數列表如下:
| 選項 | 描述 | | --- | --- | | --arch | 設置 CPU 架構信息。 | | --os | 設置操作系統信息。 | | --os-features | 設置操作系統功能信息。 | | --os-version | 設置操作系統版本信息。 | | --variant | 設置 CPU 架構的 variant 信息(翻譯過來是 “變種” 的意思),如 ARM 架構的 v7、v8 等。 |
例如設置操作系統版本信息,可以使用如下命令:
$ docker manifest annotate --os-version macOS jianghushinian/echo-platform jianghushinian/echo-platform-arm64
現在使用 inspect
查看鏡像信息已經發生變化:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 735,
"digest": "sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 735,
"digest": "sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583",
"platform": {
"architecture": "arm64",
"os": "linux",
"os.version": "macOS",
"variant": "v8"
}
}
]
}
rm
最後要介紹的子命令是 rm
,使用 rm
可以刪除本地一個或多個多架構鏡像(manifest lists
)。
$ docker manifest rm -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker manifest rm MANIFEST_LIST [MANIFEST_LIST...]
Delete one or more manifest lists from local storage
EXPERIMENTAL:
docker manifest rm is an experimental feature.
Experimental features provide early access to product functionality. These
features may change between releases without warning, or can be removed from a
future release. Learn more about experimental features in our documentation:
https://docs.docker.com/go/experimental/
使用示例如下:
$ docker manifest rm jianghushinian/echo-platform
現在使用 inspect
查看鏡像信息已經不在有 os.version
信息了,因爲本地鏡像 manifest lists
信息已經被刪除,重新從遠程鏡像倉庫拉下來的多架構鏡像信息並不包含 os.version
。
$ docker manifest inspect jianghushinian/echo-platform
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 735,
"digest": "sha256:13cbf21fc8078fb54444992faae9aafca0706a842dfb0ab4f3447a6f14fb1359",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 735,
"digest": "sha256:8eb172234961bf54a01e83d510697f09646c43c297a24f839be846414dfaf583",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
}
}
]
}
總結
本文主要介紹瞭如何使用 docker manifest
來實現構建跨平臺鏡像。
首先對 docker manifest
進行了簡單介紹,它是 Docker 的一個子命令,本身並不執行任何操作,爲了操作一個 manifest
或 manifest list
,必須使用它包含的子命令。
接着我們又在 Apple M2 芯片設備上構建了不同平臺的鏡像,然後使用 manifest list
的能力將其合併成跨平臺鏡像。
最後對 docker manifest
支持的所有子命令都進行了講解。
參考
docker manifest 官方文檔:https://docs.docker.com/engine/reference/commandline/manifest/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/DsqD_iQTPGu1HjiF0czckg