Dockerfile: RUN 命令支持内置Shell脚本, 从此告别 && 链接符号

建议点击 查看原文 查看最新内容。

原文链接: https://typonotes.com/posts/2023/09/03/dockerfile-buildkit-here-syntax-wonderful/

前几天, 我测试 Dockerfile 的 Here-Doc 语法 , 说其是 鸡肋语法, 是我 浅薄 了。

重新看了 docker 官网文档关于 buildkit 的介绍, 从 docker engine 23.0 开始就是默认 builder 了。

BuildKitopen_in_new is an improved backend to replace the legacy builder. BuildKit is the default builder for users on Docker Desktop, and Docker Engine as of version 23.0.

换句话说, 我们上一篇文章说的的诸多不便, 都自然消失了。

之所以得出 HereDoc 是鸡肋语法谬论, 我仔细分析了一下, 有以下几个原因。

  1. 我测试服务器上的 docker 版本过低, (20.x)
  2. 太久没写 基础镜像 了, 因此没有复杂的 RUNCOPY 命令。

基于以上几点, 我做了一些优化。

  1. 于是我根据官网文档,重新安装 docker engine, 将版本升级到了 24.0.5
  2. 找到了在 Docker Hub 上的 redis 官方 dockerfile 重新测试。

得到的结论是, Here-Doc 语法真的是 太香了

一句话总结: 不仅指支持多行语法, 是直接支持内置 Shell 脚本

超级棒的 Dockerfile Here-Document Syntax 语法

首先, 按照官网文档将 docker engine 版本升到最新。 以 ubuntu - docker 为例

1
2
sudo apt-get install docker-ce docker-ce-cli containerd.io \
    docker-buildx-plugin docker-compose-plugin

其次, 在 Docker Hub 上找到对应的 redis 官方 dockerfile

可以看到, RUN 内容虽然没有通过 && 链接, 但是都是使用 ; 和 \ 进行 分段,换行 管理的, 还是有一定不便。

稍微改造一下, (1) 删除所有 链接作用; 和 \, (2) 取消 RUN 的所有缩进。 这就是 一个Shell脚本 了。

注意: 这里必须使用以下格式, 否则 局部变量向下无法传递

1
2
3
4
RUN <<EOT
#!/bin/bash
# statment
EOT

直接使用 docker build 命令构建镜像, 看看执行结果

Troubleshooting

变量传递问题

关于以下两种模式, 官网文档并没有说明为什么。 仅从对比实验效果上推测, 具体实现还没有研究。

模式1 bash 直接根在 EOT 后面。

1
2
3
4
RUN <<EOT bash
dist=$(uname -s)
wget -O example.com/app-${dist} app-${dist}
EOT

这种模式下, 第一行的变量 dist 在第二行 wget 中无法使用。 因此 wget 行实际解析出来的命令为

wget -O example.com/app- app-

模式2, 在多行内容中 首行 指定解释器 #!/bin/bash, 则所有内容整体被看作一个 Shell 脚本。

1
2
3
4
5
RUN <<EOT
#!/bin/bash
dist=$(uname -s)
wget -O example.com/app-${dist} app-${dist}
EOT

这种模式下, wget 行实际解析出来的命令为

wget -O example.com/app-Linux app-Linux

符合预期

没有 buildkit 配置文件

ls: cannot access '/root/.docker/buildx/instances/default': No such file or directory

找不到默认的 buildx 配置, 使用如下即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    "Name": "localbuilder",
    "Driver": "docker-container",
    "Nodes": [
        {
            "Name": "localbuilder0",
            "Endpoint": "unix:///var/run/docker.sock",
            "Platforms": [
                {
                    "architecture": "amd64",
                    "os": "linux"
                },
                {
                    "architecture": "arm64",
                    "os": "linux"
                }
            ],
            "Flags": null,
            "ConfigFile": "",
            "DriverOpts": {}
        }
    ],
    "Dynamic": false
}