ようへいの日々精進XP

よかろうもん

Alpine Linux Docker イメージを利用した Dockerfile 内で if else を使う時のメモ

tl;dr

Alpine Linux な Docker イメージを利用して Dockerfile を認めた際, カジュアルに if eles を使ったらちょっとハマったのでメモしておきます.

要件

  • Alpine Linux なコンテナイメージを使う
  • ビルド時の引数に FOO=foo1 を渡す, 尚, FOO の値は fooNN の部分は連番となる
  • ビルド引数 FOOfoo* の時とそれ以外で処理を分岐させる

Dockerfile

以下, サンプルの Dockerfile です.

FROM alpine

ARG FOO
ENV FOO=${FOO}

RUN apk update && apk add bash
RUN bash -c "if [[ ${FOO} =~ foo* ]]; then \
  echo 'fooooooooooooooooooooooooooooooooo'; \
else \
  echo 'No foo'; \
fi"

今回, 実は bash をインストールしている点が肝です.

実際にビルドすると以下のような結果となります.

# --build-arg FOO=foo1000 の場合
$ docker build -t alpine-docker:sample .  --build-arg FOO=foo1000
Sending build context to Docker daemon  3.072kB
... 略 ...
 ---> a10df44fcd50
Step 5/5 : RUN bash -c "if [[ ${FOO} =~ foo* ]]; then   echo 'fooooooooooooooooooooooooooooooooo'; else   echo 'No foo'; fi"
 ---> Running in 84ead69e85aa
fooooooooooooooooooooooooooooooooo
Removing intermediate container 84ead69e85aa
 ---> c6fea257b9c4
Successfully built c6fea257b9c4
Successfully tagged alpine-docker:sample

# --build-arg FOO=bar
$ docker build -t alpine-docker:sample .  --build-arg FOO=bar --no-cache
Sending build context to Docker daemon  3.072kB
... 略 ...
Step 5/5 : RUN bash -c "if [[ ${FOO} =~ foo* ]]; then   echo 'fooooooooooooooooooooooooooooooooo'; else   echo 'No foo'; fi"
 ---> Running in 4a4ae9072ebb
No foo
Removing intermediate container 4a4ae9072ebb
 ---> be1603b0ba4b
Successfully built be1603b0ba4b
Successfully tagged alpine-docker:sample

いい感じです.

ハマったところ

今回, ハマった原因は if 文を使ったことというよりも, Bash でしか利用出来ない比較演算子を利用して正規表現比較をやろうとしたことでした.

www.gnu.org

if [[ ${FOO} =~ foo* ]]; then
  echo 'fooooooooooooooooooooooooooooooooo';
else
  echo 'No foo'
fi

これですね.

Alpine Linux コンテナを素で起動した後, シェルは sh 又は ash です. shash も以下のように /bin/busyboxシンボリックリンクです.

$ docker run --rm -t -i alpine ash
/ # which ash
/bin/ash
/ # which sh
/bin/sh
/ # ls -l /bin/ash
lrwxrwxrwx    1 root     root            12 Apr 23 06:25 /bin/ash -> /bin/busybox
/ # ls -l /bin/sh
lrwxrwxrwx    1 root     root            12 Apr 23 06:25 /bin/sh -> /bin/busybox

試しに上記のスクリプトを実行してみます.

/ # if [[ "foo" =~ foo* ]]; then
>   echo 'fooooooooooooooooooooooooooooooooo';
> else
>   echo 'No foo'
> fi
ash: =~: unknown operand
No foo

ash: =~: unknown operand となり正しい比較が出来ません...ちなみに, 以下のように書いても正しい比較が出来ません.

/ # if [[ "foo1" == foo* ]]; then
>   echo 'fooooooooooooooooooooooooooooooooo';
> else
>   echo 'No foo'
> fi
No foo

そうですね. Alpine Linux を利用する場合, 何も考えずにコマンドを並べると ash の限定的な機能にハマる可能性があるということですね.

ちなみに, bash をインストールして確認してみました.

/ # apk add bash
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
(1/4) Installing ncurses-terminfo-base (6.1_p20200118-r4)
(2/4) Installing ncurses-libs (6.1_p20200118-r4)
(3/4) Installing readline (8.0.1-r0)
(4/4) Installing bash (5.0.11-r1)
Executing bash-5.0.11-r1.post-install
Executing busybox-1.31.1-r9.trigger
OK: 8 MiB in 18 packages
/ #
/ # bash
bash-5.0#
bash-5.0# if [[ "foo1" == foo* ]]; then
>   echo 'fooooooooooooooooooooooooooooooooo';
> else
>   echo 'No foo'
> fi
fooooooooooooooooooooooooooooooooo
bash-5.0# if [[ "foo1" =~ foo* ]]; then
>   echo 'fooooooooooooooooooooooooooooooooo';
> else
>   echo 'No foo'
> fi
fooooooooooooooooooooooooooooooooo

意図したような結果となりました.

以上

メモでした. そもそも, Dockerfile の中で if else を使うことが正しいのかどうか判りませんが, 今後も多分ハマると思います.