こんにちは。XI本部クラウドイノベーションセンターの柴田です。
最近ではアプリケーションを実行する手段としてコンテナ技術が広く用いられています。
しかし、本番環境で稼働できる品質のコンテナイメージを作る際、初学者がコンテナ設計の段階で見落としがちなポイントがいくつかあります。
そこで今回はコンテナイメージを設計する際の推奨事項をいくつかご紹介したいと思います。
- コンテナとは
- コンテナイメージを設計する際の推奨事項
- おわりに
- 参考
コンテナとは
コンテナは仮想化技術の一種です。あらかじめ作成したコンテナイメージをもとに、異なる環境上で環境の影響を受けずにコンテナ化したアプリケーションを実行できます。
コンテナにはいくつかのメリットがあります。
- 可搬性が高い:コンテナを使うことで、あらかじめ作成したコンテナイメージをもとに、異なる環境上で環境の影響を受けずにコンテナ化したアプリケーションを実行できます。例えば、開発環境と本番環境の間に差異があって開発環境で動いたアプリケーションを本番環境にデプロイしたところ動かなかった、といった事態をある程度防ぐことができます。
- 仮想マシンと比べて軽量:従来のハイパーバイザー型の仮想マシンと異なり、コンテナはホストマシンのカーネルを利用してプロセスとして起動します。そのためコンテナは仮想マシンと比べて高速に起動・停止できます。
コンテナイメージを設計する際の推奨事項
The Twelve-Factor Appに従ってアプリケーションを実装する
コンテナ化するアプリケーションは The Twelve-Factor App に従っていることが望ましいです。The Twelve-Factor Appはモダンなアプリケーション開発における12個のベストプラクティスです。
- コードベース :バージョン管理されている1つのコードベースと複数のデプロイ
- 依存関係:依存関係を明示的に宣言し分離する
- 設定:設定を環境変数に格納する
- バックエンドサービス:バックエンドサービスをアタッチされたリソースとして扱う
- ビルド、リリース、実行:ビルド、リリース、実行の3つのステージを厳密に分離する
- プロセス:アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
- ポートバインディング:ポートバインディングを通してサービスを公開する
- 並行性:プロセスモデルによってスケールアウトする
- 廃棄容易性:高速な起動とグレースフルシャットダウンで堅ろう性を最大化する
- 開発/本番一致:開発、ステージング、本番環境をできるだけ一致させた状態を保つ
- ログ:ログをイベントストリームとして扱う
- 管理プロセス:管理タスクを1回限りのプロセスとして実行する
詳細は The Twelve-Factor App を参照ください。
コンテナのライフサイクルは仮想マシンと比べて短く
- 基盤障害とそれに伴う自動復旧
- オートスケーリング
- 新しいバージョンのコンテナイメージのデプロイ
などをトリガーにコンテナの終了・再作成が頻繁に発生します。アプリケーションのコンテナをいつでも安全に終了できることができるよう、特に「6.プロセス」「9.廃棄容易性」「11.ログ」は適切に実装することが望ましいです。
プロセス:アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
コンテナが永続データやセッション情報を持っている場合、コンテナを安全に終了するのが難しくなります。永続データやセッション情報はできるだけ外部のデータストアで管理し、コンテナはなるべくステートレスにするのが望ましいです。
廃棄容易性:高速な起動とグレースフルシャットダウンで堅ろう性を最大化する
アプリケーションのコンテナはいつでもユーザーへの影響なく安全に終了できるよう実装されていることが望ましいです。
アプリケーションを安全に終了する方法の1つにSIGTERMシグナルの適切な処理が挙げられます。多くのコンテナオーケストレーションツールはコンテナの終了時にSIGTERMシグナルを送信します。例えばWebアプリケーションならSIGTERMシグナルを受け取ったら現在のリクエストを全て処理した後で安全に終了するようアプリケーションを実装するとよいでしょう。
ログ:ログをイベントストリームとして扱う
アプリケーションのログをファイルとして出力している場合、コンテナの終了に伴うログ消失の恐れがあります。アプリケーションのログは標準出力・標準エラー出力に出力し、コンテナオーケストレーションツール側で収集・保存することが望ましいです。
1コンテナにつき1アプリケーション
1つのコンテナにつき1つのアプリケーションのみを起動することが望ましいです。
仮想マシンでは1つの仮想マシン上でWeb・AP・DBなど複数のアプリケーションを起動することがありましたが、コンテナでは1つのコンテナ上に複数のアプリケーションを起動することは推奨されません。かわりに、アプリケーションごとにコンテナを起動してください。
コンテナイメージに余分なパッケージを含めない
攻撃者による攻撃を防ぐため、本番環境で実行するコンテナイメージは、余分なパッケージがインストールされておらず、攻撃の余地は少ないことが望ましいです。
適切なベースイメージを選択する
アプリケーションをコンテナ化する際、まずベースとなるコンテナイメージを選択します。ベースイメージは軽量かつセキュアなものを選択することが望ましいです。具体的にはまず distroless の使用を検討するとよいでしょう。
マルチステージビルドを活用する
ソースコードやコンパイラなど、アプリケーションのビルド時には必要だけれども実行時には不要なパッケージがあります。 マルチステージビルド を活用することで、アプリケーションのビルド用コンテナイメージと実行用コンテナイメージを分離し、最終的なビルド成果物のコンテナイメージに含まれるパッケージを最小化できます。
コンテナイメージに機密情報を含めない
コンテナイメージにはクレデンシャルなどの機密情報を含めないでください。かわりに、クレデンシャルなどの機密情報はコンテナの実行時に環境変数経由でアプリケーションへ渡すようにしてください。
また以下のようなDockerfileを書かないようにしてください。rm
コマンドで機密情報を削除してもコンテナイメージのレイヤーとして機密情報が残ります。
# 機密情報をコンテナイメージへ追加 COPY credentials ./ # 何らかの処理を実行 RUN ... # コンテナイメージから不要になった機密情報を削除(実際にはcredentialsはレイヤとしてコンテナイメージに残っている) RUN rm ./credentials
コンテナ内におけるアプリケーションの実行ユーザーを root
以外に変更する
コンテナ内におけるアプリケーションの実行ユーザーは root
以外に変更することが望ましいです。
万が一攻撃者にアプリケーションの脆弱性を悪用された場合、実行ユーザーが root
だと、コンテナだけでなく、コンテナの稼働するホストマシンも攻撃される恐れがあります。
ベストプラクティスに従ってDockerfileを書く
Dockerfileはベストプラクティスに従って記述することが望ましいです。
Dockerfileがベストプラクティスに従っているかを機械的にチェックしてくれるツールも存在します。
コンテナイメージの脆弱性スキャンを実施する
コンテナイメージに脆弱性のあるパッケージが含まれていないか定期的にスキャンすることが望ましいです。
コンテナイメージの脆弱性スキャンを実行するツールには例えば以下があります。
また主要なイメージレジストリではコンテナイメージの登録時に自動的に脆弱性スキャンを実行する機能を提供しています。
おわりに
本記事では、まだコンテナをあまり使い馴れていない初学者の方向けに、コンテナイメージを設計する際の推奨事項をいくつかご紹介しました。本記事が読んでいただいた方のお役に立てば幸いです。
参考
- The Twelve-Factor App (日本語訳)
- Best practices for writing Dockerfiles | Docker Documentation
- コンテナ構築のおすすめの方法 | Cloud アーキテクチャ センター | Google Cloud
- Dockerfileのベストプラクティス Top 20 | Sysdig
- ECS のアプリケーションを正常にシャットダウンする方法 | Amazon Web Services ブログ
- What a Windows Container looks like ? - Speaker Deck
- 軽量Dockerイメージに安易にAlpineを使うのはやめたほうがいいという話 - inductor's blog
- Kubernetes完全ガイド 第2版 - インプレスブックス
- Docker/Kubernetes開発・運用のためのセキュリティ実践ガイド(Compass Booksシリーズ) | マイナビブックス
執筆:@shibata.takao、レビュー:@fhiroaki (Shodoで執筆されました)