恐らく禁じ手
ハマった事象
DockerFileを書くとき、ARGやENVな変数をコマンド内で使うときは $ENVNAME
で書くが、
少なくともDocker verions 17.05.0-ceでは、Dockerfile中の$ENVNAME
はプリプロセッサ的に事前に展開してコマンド実行するようだ。
例
FROM alpine ARG SOMEARG=1 ENV SOMEENV=hehehe RUN CMDENV=foo RUN echo \"$SOMEARG\" \"$SOMEENV\" \"$CMDENV\"
実行結果
yu@yu:~/test$ docker build --rm . Sending build context to Docker daemon 4.096kB Step 1/5 : FROM alpine ---> caf27325b298 Step 2/5 : ARG SOMEARG=1 ---> Using cache ---> 1efffbdfdb5c Step 3/5 : ENV SOMEENV hehehe ---> Using cache ---> 0b585450b6db Step 4/5 : RUN CMDENV=foo ---> Using cache ---> 1e28e889dfdf Step 5/5 : RUN echo \"$SOMEARG\" \"$SOMEENV\" \"$CMDENV\" ---> Running in f3083830fbf4 "1" "hehehe" "" ---> 5648473c7075 Removing intermediate container f3083830fbf4 Successfully built 5648473c7075 yu@yu:~/test$
echoした結果に注目。 RUN CMDENV=foo
がきいていない。
"1" "hehehe" ""
恐らく?だが、RUN echo \"$SOMEARG\" \"$SOMEENV\" \"$CMDENV\"
の箇所でコマンドラインで実行されるのはecho "1" "hehehe" ""
になっているからだと思う。(Dockerのコード見る気力がないのでこれ以上は勘弁)
それでも実行したい
バージョン変わるとつぶされるかもしれないが、いったんは下記でhackできた。
RUN `echo 実行したいコマンド \$環境変数名`
yu@yu:~/test$ cat Dockerfile env.sh LoadConfigScript yu@yu:~/test$ cat Dockerfile FROM alpine ARG SOMEARG=1 ENV SOMEENV=hehehe RUN CMDENV=hoo \ && `echo echo \$SOMEARG \$SOMEENV \$CMDENV` \ && `echo mkdir \$SOMEARG \$SOMEENV \$CMDENV` \ && `echo ls -l \$SOMEARG \$SOMEENV \$CMDENV` yu@yu:~/test$ yu@yu:~/test$ docker build --rm . Sending build context to Docker daemon 4.096kB Step 1/4 : FROM alpine ---> caf27325b298 Step 2/4 : ARG SOMEARG=1 ---> Using cache ---> 1efffbdfdb5c Step 3/4 : ENV SOMEENV hehehe ---> Using cache ---> 0b585450b6db Step 4/4 : RUN CMDENV=hoo && `echo echo \$SOMEARG \$SOMEENV \$CMDENV` && `echo mkdir \$SOMEARG \$SOMEENV \$CMDENV` && `echo ls -l \$SOMEARG \$SOMEENV \$CMDENV` ---> Running in f482c9fbcd9c 1 hehehe hoo 1: total 0 hehehe: total 0 hoo: total 0 ---> f19485b7afb9 Removing intermediate container f482c9fbcd9c Successfully built f19485b7afb9 yu@yu:~/test$
\はDockefileのエスケープ文字だが、$に対して実施してもコマンドラインで渡しても$はただの文字として機能する。
しかし、~
などで一度コマンド置換すると、環境変数として評価するみたいだ。
制約
DockefileのbuildはStepごとに中間結果をキャッシュし、Dockefileを修正したときキャッシュのある部分から再ビルドする。
・・・が、上記例でいうところのRUN CMDENV=hoo
の環境変数が残留になるスコープは、Step実行して評価されたbuild回でのみ。
(プロセスが一旦落ちるから?)
下記のDockerFileがあったとして、RUN FOO=var
のStepがキャッシュによりスキップされた場合、Step2の出力は環境変数FOOが未設定の扱いとなる。
FROM alpine RUN FOO=var RUN `echo echo \$FOO`
多分Dockerの中の人たちには、どうしようもないバグとして思われているんだろうなぁ。