Dockerfile の RUN instruction で heredoc 記法をそのまま使うとコマンドが non-zero exit status で死んでも docker build が成功してしまう

tl; dr

  • Dockerfile の heredoc 機能の中で凝ったことをやるときはコマンド群の最初に set -e とか書くのが無難そう

近年 Dockerfile 内で heredoc 記法が使えるようになったことが知られていて、

www.docker.com

割と凝ったことができる機能で、シンプルには以下のように RUN にずらずら書くときシュッと書けて便利、というのがわかりやすいと思う。

思うんだけど、こういうふうに heredoc の中でなんかミスってしまったときに何が起こるかというと、

# syntax=docker/dockerfile:1.3-labs
FROM debian

RUN <<EOF
  apt-get install packagewhichdoesnotexists
  ls
EOF

こういう感じで docker build は成功扱いになってしまう。

% docker build --no-cache .
[+] Building 2.2s (10/10) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                     0.0s
(中略)
 => CACHED [1/2] FROM docker.io/library/debian@sha256:2ce44bbc00a79113c296d9d25524e15d423b23303fdbbe20190d2f96e0aeb251                                                                                                                   0.0s
 => [2/2] RUN <<EOF (apt-get install packagewhichdoesnotexists...)                                                                                                                                                                       0.3s
 => exporting to image                                                                                                                                                                                                                   0.0s
 => => exporting layers                                                                                                                                                                                                                  0.0s
 => => writing image sha256:e62007bb070cd3bacbf16cb21cdca776022865ee5f8a7b7ca4f037dd712f1230                                                                                                                                             0.0s

Debian に packagewhichdoesnotexists パッケージが存在するわけもなく、直感的には docker build が失敗してほしいと思うんだけど、成功扱いになってしまう……。

実装された p-r を見てみると、上記のように RUN に素朴に heredoc を渡した場合は、その中身が sh -c で実行される、という雰囲気になっているように見えた。heredoc の中身がそのままシェルスクリプトとして実行されるようなものと捉えればよいだろうか。

github.com

という話を同僚の id:dekokun さんとしていて、set -e してみたらどうなるかという話になるわけだけど、こうするとしっかり docker build は失敗してくれた。

# syntax=docker/dockerfile:1.3-labs
FROM debian

RUN <<EOF
  set -e
  apt-get install packagewhichdoesnotexists
  ls
EOF
% docker build --no-cache .
[+] Building 3.2s (9/9) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                     0.0s
(中略)
 => CACHED [1/2] FROM docker.io/library/debian@sha256:2ce44bbc00a79113c296d9d25524e15d423b23303fdbbe20190d2f96e0aeb251                                                                                                                   0.0s
 => ERROR [2/2] RUN <<EOF (set -e...)                                                                                                                                                                                                    0.3s
------
 > [2/2] RUN <<EOF (set -e...):
#9 0.229 Reading package lists...
#9 0.237 Building dependency tree...
#9 0.239 Reading state information...
#9 0.241 E: Unable to locate package packagewhichdoesnotexists
------
executor failed running [/bin/sh -c   set -e
  apt-get install packagewhichdoesnotexists
  ls
]: exit code: 100

完全に忘れてたけどこういう話もある!

Dockerfile の RUN instruction で heredoc 記法を使うときには set -e しないとコマンドが non-zero exit status で死んでも docker build が成功してしまう - polamjaggy nikki

-o pipefail とかも欲しくなるやつ?(わかってない)

2022/07/27 20:05
b.hatena.ne.jp

これも docker build 成功してしまうけど、

# syntax=docker/dockerfile:1.3-labs
FROM debian

RUN <<EOF
  set -e
  apt-get install packagewhichdoesnotexists | cat
  ls
EOF

Debian ではこれは dash で実行されていて、こういう話になってしまう!

Dockerfile 内でこれをやりたい人はいないでしょって感じで厳しい……。

stackoverflow.com

qiita.com