构建多种系统架构支持的 Docker 镜像 — docker manifest 命令详解

    例如我们在 Linux x86_64 中构建一个 username/test 镜像。

    构建镜像后推送到 Docker Hub,之后我们尝试在树莓派 Linux arm64v8 中使用这个镜像。

    1. $ docker run -it --rm username/test

    可以发现这个镜像根本获取不到。

    要解决这个问题,通常采用的做法是通过镜像名区分不同系统架构的镜像,例如在 Linux x86_64Linux arm64v8 分别构建 username/testusername/arm64v8-test 镜像。运行时使用对应架构的镜像即可。

    这样做显得很繁琐,那么有没有一种方法让 Docker 引擎根据系统架构自动拉取对应的镜像呢?

    这是什么原因呢?

    原因就是 golang:alpine 官方镜像有一个 。

    当用户获取一个镜像时,Docker 引擎会首先查找该镜像是否有 manifest 列表,如果有的话 Docker 引擎会按照 Docker 运行环境(系统及架构)查找出对应镜像(例如 golang:alpine)。如果没有的话会直接获取镜像(例如上例中我们构建的 username/test)。

    我们可以使用 $ docker manifest inspect golang:alpine 查看这个 manifest 列表的结构。

    该命令属于实验特性,请参考 开启实验特性 一节。

    1. {
    2. "schemaVersion": 2,
    3. "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
    4. "manifests": [
    5. {
    6. "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    7. "size": 1365,
    8. "digest": "sha256:5e28ac423243b187f464d635bcfe1e909f4a31c6c8bce51d0db0a1062bec9e16",
    9. "platform": {
    10. "architecture": "amd64",
    11. "os": "linux"
    12. }
    13. },
    14. {
    15. "size": 1365,
    16. "digest": "sha256:2945c46e26c9787da884b4065d1de64cf93a3b81ead1b949843dda1fcd458bae",
    17. "platform": {
    18. "os": "linux",
    19. "variant": "v7"
    20. }
    21. },
    22. {
    23. "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    24. "size": 1365,
    25. "digest": "sha256:87fff60114fd3402d0c1a7ddf1eea1ded658f171749b57dc782fd33ee2d47b2d",
    26. "platform": {
    27. "architecture": "arm64",
    28. "os": "linux",
    29. "variant": "v8"
    30. }
    31. },
    32. {
    33. "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    34. "size": 1365,
    35. "digest": "sha256:607b43f1d91144f82a9433764e85eb3ccf83f73569552a49bc9788c31b4338de",
    36. "platform": {
    37. "architecture": "386",
    38. "os": "linux"
    39. }
    40. },
    41. {
    42. "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    43. "size": 1365,
    44. "platform": {
    45. "architecture": "ppc64le",
    46. "os": "linux"
    47. }
    48. },
    49. {
    50. "size": 1365,
    51. "digest": "sha256:69f5907fa93ea591175b2c688673775378ed861eeb687776669a48692bb9754d",
    52. "platform": {
    53. "architecture": "s390x",
    54. "os": "linux"
    55. }
    56. }
    57. ]
    58. }

    可以看出 manifest 列表中包含了不同系统架构所对应的镜像 digest 值,这样 Docker 就可以在不同的架构中使用相同的 manifest (例如 golang:alpine) 获取对应的镜像。

    首先在 Linux x86_64 构建 username/x8664-test 镜像。并在 Linux arm64v8 中构建 username/arm64v8-test 镜像,构建好之后推送到 Docker Hub。

    创建 manifest 列表

    当要修改一个 manifest 列表时,可以加入 -a,--amend 参数。

    1. # $ docker manifest annotate [OPTIONS] MANIFEST_LIST MANIFEST
    2. $ docker manifest annotate username/test \
    3. username/x8664-test \
    4. --os linux --arch x86_64
    5. $ docker manifest annotate username/test \
    6. username/arm64v8-test \
    7. --os linux --arch arm64 --variant v8

    这样就配置好了 manifest 列表。

    查看 manifest 列表

    最后我们可以将其推送到 Docker Hub。

    1. $ docker manifest push username/test

    测试

    我们在 Linux x86_64 Linux arm64v8 中分别执行 命令,发现可以正确的执行。

    详细了解 manifest 可以阅读官方博客。