Docker イメージのビルド時にスクリプト・ファイルを必要とする場合、通常はローカルにあるファイルを COPY
でイメージ内へコピーするのが基本です。
しかし、Dockerfile 内でスクリプトを作成したいことが、まれによくあります。
例えば、パッケージマネージャーには無い、何かしらのアプリのインストール・スクリプトなどです。リポジトリに置いても良いのですが、リポジトリに必須でないファイルがあることで慣れないユーザーの目が遊ぶことを避けたい、といった場合の「あるある」です。
「dockerfile内 スクリプト 作成」でググっても、ドンピシャのタイトルの記事がなかったので、自分のググラビリティとして。
TL; DR (今北産業)
-
COPY
指示子で、ヒアドキュメント(heredoc)を使ってファイルを作成する -
使用例:
Dockerfile# ============================================================================= # ビルド ステージ # ============================================================================= FROM alpine WORKDIR /app RUN apt upgrade --no-cache COPY --chmod=755 <<'EOL' /app/entrypoint.sh #!/bin/sh set -e echo "Hello, world" EOL ENTRYPOINT [ "/app/entrypoint.sh" ]
'EOL'
と HEREDOC 指示子をシングル・クオートすることで、スクリプト内に変数があった場合に Docker 側で展開しない(ありのままのスクリプト)を COPY できるようになります。ちなちみに EOL
は一意であれば他の指示子でも構いません('HEREDOC'
など)。
【注意】上記の場合、PID1 は /app/entrypoint.sh
になり echo
はサブ・プロセス(子プロセス)になります。
つまり、この場合、コンテナを終了しても SIGINT などの終了シグナルは echo
に渡されません。
この例では、すぐに実行終了するので特に問題はないのですが echo
の代わりに Web サーバーなどサービス系の常駐するアプリの場合には特に注意が必要です。
Ctrl+C
しても graceful shutdown
(後処理を済ませて安全にアプリを停止)してくれません。
スクリプトが PID 1 による弊害例
FROM alpine
WORKDIR /app
COPY --chmod=755 <<'HEREDOC' /app/entrypoint.sh
#!/bin/sh
sleep 120s
HEREDOC
ENTRYPOINT [ "/app/entrypoint.sh" ]
そのような場合(サブプロセスが発生するケース)には、シェルのスクリプト内の実行には exec
を使います。
- echo "Hello, world"
+ exec /bin/echo "Hello, world"
もしくは、docker run --init myimage:latest
と --init
オプションを付けて PID 1 に tini(docker に組み込まれている init)を利用して実行する方法もあります。
参考文献/あわせて読みたい
- ヒアドキュメント とは @ Qiita
- 知ると便利なヒアドキュメント @ Qiita
- 3分でわかる,Shell Script/Dockerfileの可読性を上げるヒアドキュメントの使い方 @ Qiita
- bashのヒアドキュメントを活用する @ Qiita
- Bourne/Ash shell(shシェル)で複数行をヒアドキュメントで変数に代入する @ Qiita
- Specify an init process | Reference @ docs.docker.com
- Dockerの--initフラグについて | Carpe Diem @ Hatena Blog