ようへいの日々精進XP

よかろうもん

Dockerfile 内で条件に応じて処理を変えたかったので試行錯誤したメモ

tl;dr

Docker イメージをビルドする際に、--build-arg に指定した条件に応じて、処理を変えたかったので試行錯誤してみました。

Dockerfile 例

以下のような Dockerfile があったとする。

FROM alpine

# 条件 A の時
ADD scripts/test1.sh /tmp/test.sh

# 条件 B の時
ADD scripts/test2.sh /tmp/test.sh

# /tmp/test.sh を実行
RUN /tmp/test.sh
  • 条件 test1 の時 (--build-arg TEST_ARG=test1) には、test1.sh/tmp/test.shADD したい
  • 条件 test2 の時 (--build-arg TEST_ARG=test2) には、test2.sh/tmp/test.shADD したい
  • 最後に /tmp/test.sh を実行する

スクリプトは以下のような内容。

$ cat scripts/test1.sh
#!/bin/sh

echo "test1"

$ cat scripts/test2.sh
#!/bin/sh

echo "test2"

先述の Dockerfile をそのまま実行すると、以下のように出力される。

$ docker build --no-cache=true --tag test-build .
Sending build context to Docker daemon  5.632kB
Step 1/4 : FROM alpine
 ---> a24bb4013296
Step 2/4 : ADD scripts/test1.sh /tmp/test.sh
 ---> 0a4e47647476
Step 3/4 : ADD scripts/test2.sh /tmp/test.sh
 ---> 7cac3bdbf5f7
Step 4/4 : RUN /tmp/test.sh
 ---> Running in d2b82c5d3aef
test2
Removing intermediate container d2b82c5d3aef
 ---> 3ebafdf8e1ec
Successfully built 3ebafdf8e1ec
Successfully tagged test-build:latest

さて、どうしようか。

試行錯誤 (1) シェルスクリプトに手をいれる

それぞれのシェルスクリプトを結合して、以下のような内容に修正。

$ vim scripts/tests.sh
#!/bin/sh

if [ "${TEST_ARG}" = "test1" ];then
  echo "test1"
else
  echo "test2"
fi

Dockerfile を以下のように修正。

FROM alpine

ARG TEST_ARG 

ADD scripts/tests.sh /tmp/test.sh

RUN /tmp/test.sh

実行してみる。

# TEST_ARG=test1
$ docker build --build-arg TEST_ARG=test1 --no-cache=true --tag test-build .
Sending build context to Docker daemon  5.632kB
Step 1/4 : FROM alpine
 ---> a24bb4013296
Step 2/4 : ARG TEST_ARG
 ---> Running in 6bee4b0a77d4
Removing intermediate container 6bee4b0a77d4
 ---> 18791606eb7d
Step 3/4 : ADD scripts/tests.sh /tmp/test.sh
 ---> 2ef0dc18c220
Step 4/4 : RUN /tmp/test.sh
 ---> Running in 1bdcf49a1b3f
test1
Removing intermediate container 1bdcf49a1b3f
 ---> b44ffe9d5479
Successfully built b44ffe9d5479
Successfully tagged test-build:latest

# TEST_ARG=test2
$ docker build --build-arg TEST_ARG=test2 --no-cache=true --tag test-build .
Sending build context to Docker daemon  5.632kB
Step 1/4 : FROM alpine
 ---> a24bb4013296
Step 2/4 : ARG TEST_ARG
 ---> Running in 0dcfd7ce440f
Removing intermediate container 0dcfd7ce440f
 ---> 5383560dc479
Step 3/4 : ADD scripts/tests.sh /tmp/test.sh
 ---> 4827a378a33c
Step 4/4 : RUN /tmp/test.sh
 ---> Running in 061058c84bac
test2
Removing intermediate container 061058c84bac
 ---> cef33b3ef95d
Successfully built cef33b3ef95d
Successfully tagged test-build:latest

一応、意図した挙動になっている。

念の為、docker history で確認する。

$ docker build --build-arg TEST_ARG=test1 --no-cache=true --tag test-build .
$ docker history test-build
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
00b3da2b79ae        3 seconds ago       |1 TEST_ARG=test1 /bin/sh -c /tmp/test.sh       0B
1d612168b8e3        4 seconds ago       /bin/sh -c #(nop) ADD file:802b1103fc27ad8ee…   85B
32af95883761        4 seconds ago       /bin/sh -c #(nop)  ARG TEST_ARG                 0B
a24bb4013296        7 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           7 months ago        /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b…   5.57MB

ただ、このパターンだと、シェルスクリプト (≒アプリケーション) に手を入れる必要があるし、インフラ側の都合をアプリケーション側に押し付けるような感じがする。

試行錯誤 (2) Dockerfile に手をいれる

シェルスクリプトには手を入れずに Dockerfile 内で完結したい場合、以下のように Dockerfile を修正した。

ARG TEST_ARG 

FROM alpine AS build_test1
ONBUILD ADD scripts/test1.sh /tmp/test.sh

FROM alpine AS build_test2
ONBUILD ADD scripts/test2.sh /tmp/test.sh

FROM build_${TEST_ARG}

RUN /tmp/test.sh

マルチステージビルド を使う。

実行してみる。

# TEST_ARG=test1
$ docker build --build-arg TEST_ARG=test1 --no-cache=true --tag test-build .
Sending build context to Docker daemon  5.632kB
Step 1/7 : ARG TEST_ARG
Step 2/7 : FROM alpine AS build_test1
 ---> a24bb4013296
Step 3/7 : ONBUILD ADD scripts/test1.sh /tmp/test.sh
 ---> Running in 533fe2b2f259
Removing intermediate container 533fe2b2f259
 ---> 354bf4f2c5b6
Step 4/7 : FROM alpine AS build_test2
 ---> a24bb4013296
Step 5/7 : ONBUILD ADD scripts/test2.sh /tmp/test.sh
 ---> Running in 5dd2be2f59e8
Removing intermediate container 5dd2be2f59e8
 ---> ef43e969bb08
Step 6/7 : FROM build_${TEST_ARG}
# Executing 1 build trigger
 ---> 3454bad7d13f
Step 7/7 : RUN /tmp/test.sh
 ---> Running in 0387de5f001c
test1
Removing intermediate container 0387de5f001c
 ---> be4d9823b31a
Successfully built be4d9823b31a
Successfully tagged test-build:latest

# TEST_ARG=test2
$ docker build --build-arg TEST_ARG=test2 --no-cache=true --tag test-build .
Sending build context to Docker daemon  5.632kB
Step 1/7 : ARG TEST_ARG
Step 2/7 : FROM alpine AS build_test1
 ---> a24bb4013296
Step 3/7 : ONBUILD ADD scripts/test1.sh /tmp/test.sh
 ---> Running in f481f96b5139
Removing intermediate container f481f96b5139
 ---> 70758139a3f8
Step 4/7 : FROM alpine AS build_test2
 ---> a24bb4013296
Step 5/7 : ONBUILD ADD scripts/test2.sh /tmp/test.sh
 ---> Running in c2f835a5bdfe
Removing intermediate container c2f835a5bdfe
 ---> 6b435562e6b6
Step 6/7 : FROM build_${TEST_ARG}
# Executing 1 build trigger
 ---> 6436710016c2
Step 7/7 : RUN /tmp/test.sh
 ---> Running in 364a63da9c54
test2
Removing intermediate container 364a63da9c54
 ---> 16ab720a3284
Successfully built 16ab720a3284
Successfully tagged test-build:latest

念の為、docker history で確認する。

$ docker build --build-arg TEST_ARG=test1 --no-cache=true --tag test-build .
$ docker history test-build
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
d664199dd041        3 seconds ago       /bin/sh -c /tmp/test.sh                         0B
ecf320aaadc6        3 seconds ago       /bin/sh -c #(nop) ADD file:1ab674a4350c46957…   24B
34b4004c7932        4 seconds ago       /bin/sh -c #(nop)  ONBUILD ADD scripts/test1…   0B
a24bb4013296        7 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           7 months ago        /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b…   5.57MB

なるほど。

TEST_ARG=test1の条件でイメージが生成されていることが判る。

ということで

試行錯誤 (1) と試行錯誤 (2) のどちらが良いのか。

  • 試行錯誤 (1) パターンは、イメージ生成に合わせて、アプリケーション (本記事ではシェルスクリプト) の改修が必要
  • 試行錯誤 (2) パターンは、アプリケーションの改修は不要、ちょっと Dockerfile が長くなる

という理由で、個人的には、(2) が良いかな。

参考

docs.docker.jp

stackoverflow.com