Dockerは、コンテナ化を実現するための強力なツールであり、多くの開発者や運用エンジニアにとって不可欠な存在になっています。このツールを使うことで、アプリケーションとその依存関係をパッケージ化し、一貫性のある動作環境を簡単に配布することができます。Dockerfileは、このプロセスを自動化するための設定ファイルであり、ここでBashを実行する方法とそれに関連するベストプラクティスについて解説します。
DockerfileでBashを実行する基本
Dockerfileは、テキスト形式のファイルであり、Dockerイメージの構築手順を定義します。Bashを実行するには、Dockerfile内で適切な命令を追加する必要があります。最も基本的な方法は、RUN命令を使ってBashのスクリプトやコマンドを実行することです。
FROM ubuntu:latest
# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y bash
# Bashコマンドを実行
RUN bash -c "echo 'Hello, Docker!'"
上記の例では、Ubuntuの最新バージョンをベースにしてBashがインストールされ、bash -cで単純なEchoコマンドを実行しています。
ENTRYPOINTとCMDの活用
Bashスクリプトをエントリーポイントとして設定すると、コンテナがデフォルトでこのスクリプトを実行するようにすることができます。ENTRYPOINT命令は、コンテナ内で実行されるデフォルトのプロセスを定義します。
FROM ubuntu:latest
# Bashをインストール
RUN apt-get update && apt-get install -y bash
# スクリプトをコピーして実行
COPY my-script.sh /usr/local/bin/my-script.sh
RUN chmod +x /usr/local/bin/my-script.sh
ENTRYPOINT ["bash", "/usr/local/bin/my-script.sh"]
ここでは、コンテナが起動するとmy-script.shが実行される設定になっています。
ベストプラクティス
Dockerfileを書く際には、いくつかのベストプラクティスを守ることが重要です。これにより、効率的で保守しやすいイメージを作成することが可能になります。
レイヤー削減
Dockerイメージは各命令に対してレイヤーを生成します。大量のレイヤーがあると、ビルド時間が長くなり、イメージが無駄に大きくなる可能性があります。RUN命令はできるだけまとめ、一度に実行すると良いでしょう。
RUN apt-get update && \
apt-get install -y bash && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
キャッシュの効率化
Dockerのキャッシュ機能を効率的に利用するために、頻繁に変更される命令をできるだけ後に配置します。これにより、変更があっても早い段階のキャッシュが無駄にならず効率的です。
セキュリティの確保
DockerコンテナでのBashスクリプト実行時には、入力の検証や正当性確認を行い、スクリプトが意図しない動作をするリスクを最小限に抑えましょう。また、可能であればシェルスクリプトの代わりに専用のエントリーポイントスクリプトを使うことを検討してください。
シンプルな設計
必要以上に複雑なスクリプトや動作を避け、できるだけシンプルな設計を心がけましょう。これにより、管理やトラブルシューティングが容易になります。
RUNとCMD/ENTRYPOINTのシェル形式・exec形式の違い
DockerfileでBashを使う命令にはシェル形式とexec形式(JSON配列)の2種類の書き方があり、挙動が異なります。この違いを理解していないと、シグナルが正しく伝わらない・環境変数が展開されない、といった問題が起こります。
# シェル形式:/bin/sh -c 経由で実行される(変数展開・パイプ・&&が使える)
RUN echo "Hello, $HOME"
CMD echo "container started"
# exec形式:シェルを介さず直接実行される(配列で記述、変数展開はされない)
RUN ["bash", "-c", "echo Hello, $HOME"]
CMD ["bash", "/usr/local/bin/my-script.sh"]
シェル形式はシグナル(SIGTERMなど)が/bin/shプロセスに渡ってからアプリケーションに転送されるため、コンテナ停止時にシグナルが正しく伝わらないことがあります。CMD/ENTRYPOINTで長時間稼働するプロセスを起動する場合は、シグナルを直接受け取れるexec形式を使うのが推奨です。
SHELL命令でデフォルトシェルを変更する
Dockerのシェル形式命令(RUNなど)は既定で/bin/sh -cを使いますが、SHELL命令でBashに変更すると、Bash固有の機能(set -o pipefailやプロセス置換など)がシェル形式のまま使えるようになります。
FROM ubuntu:latest
RUN apt-get update && apt-get install -y bash
# 以降のシェル形式命令はbashで実行される
SHELL ["/bin/bash", "-c"]
# pipefailを有効にすることで、パイプ内のコマンドが失敗したらビルドも失敗させられる
RUN set -o pipefail && curl -fsSL https://example.com/install.sh | bash
pipefailが無効なシェル(sh)のままだと、パイプの途中でコマンドが失敗してもビルドが成功扱いになってしまうことがあるため、パイプを使うビルド手順ではこの設定が特に重要です。
マルチステージビルドでのBash活用
マルチステージビルドでは、ビルド用ステージでBashスクリプトを使って依存関係の準備やテストを行い、最終イメージには成果物だけをコピーすることで、イメージサイズを小さく保てます。
FROM ubuntu:latest AS builder
RUN apt-get update && apt-get install -y bash build-essential
COPY build.sh .
RUN bash build.sh
FROM ubuntu:latest AS runtime
COPY --from=builder /app/dist /app/dist
CMD ["/app/dist/start.sh"]
ビルドステージ(builder)にだけBashやビルドツールを入れ、最終ステージには実行に必要な成果物だけをコピーすることで、本番イメージを軽量かつセキュアに保てます。
Dockerfileでbashを実行するときによくあるエラーと対処法
「/bin/sh: bash: not found」というエラーが出る
AlpineベースのイメージなどBashが標準で入っていない場合に発生します。Alpineではapt-getではなくapkでBashを追加します。
FROM alpine:latest
RUN apk add --no-cache bash
RUN bash -c "echo 'Hello from bash'"
スクリプトを実行すると「Permission denied」になる
COPYしたスクリプトに実行権限が付与されていないことが原因です。COPY後にchmod +xを忘れずに実行してください。
COPY my-script.sh /usr/local/bin/my-script.sh
RUN chmod +x /usr/local/bin/my-script.sh
ENTRYPOINT ["bash", "/usr/local/bin/my-script.sh"]
環境変数がexec形式のCMD/ENTRYPOINTで展開されない
exec形式(JSON配列)はシェルを経由しないため、$VARのような変数展開は行われません。変数展開が必要な場合は、シェル形式を使うか、exec形式内で明示的にbash -cを呼び出してください。
# NG:$NAMEがそのまま文字列として出力される
CMD ["echo", "Hello, $NAME"]
# OK:bash -c 経由で変数展開させる
CMD ["bash", "-c", "echo Hello, $NAME"]
関連記事
- Docker Composeでexec bashを使いこなそう!効率的なコンテナ操作ガイド
- Docker Execコマンドでコンテナ内のBashを利用する方法と注意点
- Dockerとは?初心者向け完全ガイド:コンテナの基礎から環境構築・デプロイまで
まとめ
DockerfileでBashを実行することは、コンテナ化された環境で柔軟なスクリプトの実行を可能にします。適切な命令を使用し、ベストプラクティスを守ることで、安全かつ効率的なDockerイメージを構築することができます。これらの方法を覚えておくことで、様々なユースケースに対応できるようになりますので、ぜひ実践してみてください。


コメント