AWS Well-Architected コンテナビルドレンズを要約してみた
こんにちは。
ご機嫌いかがでしょうか。
"No human labor is no human error" が大好きな吉井 亮です。
AWS Well-Architected Framework 読んでますか?(挨拶)
このベストプラクティス集はアーキテクチャの評価、設計時の参照先、費用対効果と信頼性が高いシステム構築にとても役立つドキュメントです。
基本となる6本の柱に加えて、特定領域にフォーカスしたレンズが用意されています。
そして、アメリカ時間の2022年10月21日に新しいレンズ Container Build Lens が追加されました。
ドキュメントを読み込んでうえで自分なりの解釈を加えて要約します。
AWS Well-Architected 6つの柱を知りたい
サマリーの前に基本的な6つの柱を知りたいという方は以下のリンクを参照ください。
AWS Well-Architected
[初心者向け]AWS Well-Architected ドキュメントの歩き方
Container Build Lens とは
AWS 上でコンテナアプリケーションを構築するためのクラウドネイティブなアプローチを採用するのに役立つベストプラクティス集です。
ビジネス要件に沿って適切な設計・実装を行うためのガイダンスとして利用可能です。
基本的な6つの柱に沿っての解説がなされています。
- 運用上の優秀性の柱
- コンテナイメージのライフサイクル管理
- コンテナイメージへのオブザーバビリティの実装
- セキュリティの柱
- コンテナアプリケーションに対する最小権限の確保
- ビルドインフラに対するアクセス制御
- 脆弱性の検出を緩和
- コンテナイメージへの攻撃対象領域の最小化
- 機密データの処理
- 信頼性の柱
- コンテナリソースの監視と追跡
- イメージビルドとテスト自動化の実装
- 親コンテナへの更新のロールアウト
- コンテナアプリケーションの状態監視
- パフォーマンス効率の柱
- イメージサイズの縮小
- イメージプル時間の改善
- イメージの更新
- コスト最適化の柱
- 効率的なコンテナイメージの設計
- 自動スケーリングのサポート
- 複数のコンピュティングタイプの使用
- 起動時間オーバーヘッドの短縮
- 持続可能性の柱
- エネルギー効率の高いハードウェアでコンテナ実行
- エネルギー効率の高いハードウェアでコンテナビルド
設計原則
コンテナイメージを構築および管理するための一般的な設計原則です。
イメージサイズの最小化
イメージサイズを最小化する、つまり、余計なファイルやプロセスをイメージに含めないことは多くのメリットがあります。
- 攻撃対象領域の縮小
- コンテナの起動の高速化
- スケーリング速度の向上
- ストレージ量の最小化
イメージサイズを最小化するには以下の対策を採用します。
- 依存関係の数を減らす
- マルチステージビルドを使用する
- コンテナを実行するために必要なバイナリのみを含める
- データは外部システム (DB、NFS、S3 など) に保存する
マルチステージビルドを使用する
マルチステージビルドは、アプリケーションの実行に使用される最終イメージからイメージのビルドフェーズを分割するメカニズムです。
「イメージサイズの最小化」に書いたようなメリットを享受することが可能です。
組織全体で標準化
組織がコンテナイメージに対して一貫したガバナンスを求めているのであれば、親イメージは標準化されたイメージを使うと良いでしょう。
組織固有の構成を親イメージに含め、各チームはその親イメージに固有アプリケーションを追加していく使い方を想定しています。
バージョンの検証と制限
イメージのタグ付けを利用して、イメージにメタ情報を追加します。
バージョンや安定性を示すタグを付与して体系化します。
”Latest” は極力使用せず、上書き禁止の不変なタグを使用します。
イメージスキャン戦略を実装する
定期的なイメージスキャンを実装します。
- 静的スキャン
- コンテナレジストリにプッシュされたイメージをスキャンする
- 公開されているバグや CVE データベースに登録された脆弱性をスキャンする
- Amazon Inspector やサードパーティツール
- 動的スキャン
- 稼働中のコンテナに対するスキャン
- サードパーティツール
そもそものアプリケーションコードに対するセキュリティスキャンを導入すると安全だと思います。
ステートレスアクションを実行するように設計する
セッション情報などのステートはデータストアに保存します。
運用上の優秀性の柱
運用上の優秀性の観点での解説です。
CONTAINER_BUILD_OPS_01: コンテナとイメージのライフサイクルをどのように管理していますか?
コンテナイメージに何が含まれているかを理解することが重要です。
ベンダー公式イメージを使うことは効率的ではありますが、自社アプリケーション実行には不要なパッケージが含まれている可能性があります。
最小限のコンテナイメージから開始し必要パッケージのみを追加する手順を作成します。
一度イメージをビルドしたら、そのイメージを全ての実行環境で使用します。これにより環境ごとの差分がなくなり一貫性が保たれます。
CI/CD プロセスを導入してビルドとテストを自動化します。
パッケージングツールを利用します。Kubernetes 用の Helm や Kustomize、Amazon ECS 用の AWS Copilot、Docker 用の Docker Swarm などが用意されています。
CONTAINER_BUILD_OPS_02: コンテナ化されたワークロードがビジネス目標を達成しているかどうかをどのように確認しますか?
コンテナ化されたワークロードの状態を把握します。テレメトリやログを収集し、それらから必要のあるしきい値と情報を特定し、対処に備えることでユーザーへの影響を最小に抑えます。
コンテナのヘルスチェックを導入します。これにより、オーケストレーションツールは期待通りに動作していないコンテナへのトラフィック転送を停止します。
実行中のコンテナによって生成されたログは外部に転送します。アプリケーションから出力するログは標準出力または標準エラーに書き込み、標準出力を CloudWatch Logs に転送するといった使い方になります。
セキュリティの柱
コンテナイメージの設計およびビルド中に導入される可能性がある脆弱性に対処するに関する記述です。
ONTAINER_BUILD_SEC_01: コンテナイメージが最小権限 ID を使用していることをどのように確認しますか?
コンテナ内で実行されるプロセスはデフォルトでは root ユーザーで実行されます。
悪意のある攻撃からシステムを防御するための対策を取り入れます。
最も簡単な方法は Dockerfile の USER ディレクティブを使い一般ユーザーを追加することです。
CONTAINER_BUILD_SEC_02: ビルドインフラストラクチャへのアクセスをどのように制御していますか?
CI/CD パイプラインにセキュリティ対策を追加します。デプロイ後だけではなく開発サイクルの初期段階からセキリティテストを導入することが重要です。
また、パイプラインそのものの保護も必要です。パイプラインにアクセス可能な ID のアクセス権は最小権限の原則に従います。
CONTAINER_BUILD_SEC_03: コンテナイメージ内の脆弱性をどのように検出して対処していますか?
コンテナイメージは定期的にスキャンし、脆弱性が含まれていないことを確認します。
大きく分けて2種類のスキャン、静的スキャンと動的スキャンが存在します。動く前 (デプロイする前) のスキャンが静的スキャン、動いているコンテナに対するスキャンが動的です。
Inspector は静的スキャンの一種です。静的/動的ともに様々なツールがサードパーティから提供されています。どちら一方のスキャンではなく双方を導入しセキュリティ事故のリスクを減らします。
CONTAINER_BUILD_SEC_04: コンテナイメージの境界をどのように管理していますか?
様々な手段を講じて攻撃対象領域を減らします。
- シェルやパッケージ マネージャーを使用せずにディストリビューションのないイメージを実行する
- ソースからオープンソースライブラリを構築する
- ライブラリの脆弱性をスキャンする
- setuid/setgid フラグを削除して特権エスカレーションを防止する
- Dockerfile をリントする
- アプリケーションが必要としない不要なバイナリを削除する
- 読み取り専用ルートファイルシステムで動作する
攻撃対象領域を減らすこととは別に、親イメージが信頼できるのかを判断することも大切です。
CI/CD パイプラインにはパブリックイメージを直接組み込むことはせず、ECR などのプライベートレジストリを使用して自組織のコントロール下に置くことをお勧めします。
CONTAINER_BUILD_SEC_05: コンテナ化されたアプリケーション内でデータをどのように処理しますか?
機密データをコンテナイメージにハードコートすることは避けます。Secrets Manager や Parameter Store などのシークレット管理サービスに機密データを格納し、実行時に値を取得するようにします。
永続化データはコンテナ外部に保管します。Aurora、RDS、DynamoDB、EFS、S3 などストレージが堅牢なサービスを利用します。
信頼性の柱
信頼性を実現するための適切な設計、修復自動化に関する記述です。
CONTAINER_BUILD_REL_01: コンテナが消費する CPU とメモリの量をどのように制限しますか?
コンテナが消費する CPU と RAM を制限します。
ECS はタスク定義、 EKS は YAML のリソースセクションに設定項目が存在します。
CONTAINER_BUILD_REL_02: コンテナアプリケーションで永続データをどのように処理しますか?
画像処理アプリケーションなど永続データが必要なケースがあります。Aurora、RDS、DynamoDB、EFS、S3 などの永続ストレージなサービスを利用します。
CONTAINER_BUILD_REL_03: コンテナのビルドとテストをどのように自動化しますか?
開発したコンテナアプリケーションはできるだけ早くテストをすることが望ましいと考えます。開発者のローカルでテストをするのか、AWS 上の開発環境でテストをするのかを最初に決定します。
AWS 上の各実行環境をサポートするように CI/CD パイプラインを構築します。
CONTAINER_BUILD_REL_04: 親イメージまたはベースイメージに更新をカスケードするにはどうすればよいですか?
コンテナイメージ管理に階層アプローチを導入します。階層アプローチはメンテナンス効率化、再利用性向上、セキュリティ維持を維持するのに役立ちます。
例として以下のカテゴリ階層を導入します。
- 中間ベースイメージ
- アプリケーション・サーバー
- アプリケーションのソースコードまたはバイナリ
コンテナイメージには適切なタグを付けます。
コンテナイメージとタグに直接的な相関関係を持たせます。
例えば、1.0 は最新のパッチリリースを示し、1.0.1, 1.0.2, 1.0.3 を含みます。
CONTAINER_BUILD_REL_05: コンテナの状態をどのように監視しますか?
コンテナにヘルスチェックまたはプローブを追加します。
EKS 内の liveness および readiness プローブ、または、ECS 内の定義ファイル内のヘルスチェックを検討します。
パフォーマンス効率の柱
パフォーマンス効率の柱は、要件を満たすためにコンピューティング リソースを効率的に使用し、需要の変化やテクノロジの進化に合わせてその効率を維持することに重点を置いています。
CONTAINER_BUILD_PERF_01: コンテナイメージのサイズをどのように縮小しますか?
親イメージには Alpine や scratch などの軽量イメージを使用します。
マルチステージビルドと軽量ベースイメージを組み合わせて可能な限り最小のイメージを作成します。
コンテナごとに1つのプロセスを実行します。このことにより 関心の分離 の実装が容易になります。関心の分離には、保守性の向上、安定性の向上、水平スケールの容易さなど様々なメリットがあります。
.dockerignore ファイルを使ってビルドの不要なファイルや機密性の高いファイルをイメージから除外します。
CONTAINER_BUILD_PERF_02: コンテナイメージのプル時間をどのように短縮しますか?
コンテナレジストリとクラスターはできるだけ近い場所に配置します。AWS 内に双方を持つのであれば同じリージョンに配置します。
コンテナイメージの最小化はプル時間短縮にも効果を発揮します。
CONTAINER_BUILD_PERF_03: ターゲットイメージの一貫した結果を確実に得るにはどうすればよいですか?
親イメージに Latest タグを使用することは控えます。 Latest には重大な変更が含まれている可能性があります。
CONTAINER_BUILD_PERF_04: 親イメージに更新されたバージョンを確実に使用するにはどうすればよいですか?
チームまたは組織全体で共通の親イメージを使用している場合、親イメージの更新に関する通知を関係者へ送信する必要があります。
CONTAINER_BUILD_PERF_05: 時間の経過とともに一貫したパフォーマンス結果を得るにはどうすればよいですか?
システムのパフォーマンスは時間ともに低下する可能性があるものです。パフォーマンスの低下を特定するために、自動化されたテストおよび監視システムを導入することが重要です。
テストツールと監視ツールが CI/CD 全体をカバー可能なことを事前に確認しておきます。
CONTAINER_BUILD_PERF_06: ターゲット イメージのサイズをどのように最適化しますか?
コンテナイメージに含まれるレイヤーを少なくします。
例えば、Dockerfile に記述する RUN ステートメントはコマンド毎に複数行に分けることはせず && で接続し極力1行にまとめます。
コスト最適化の柱
コンテナイメージの構築、実行、および運用のコストを削減するのに役立つガイドです。
CONTAINER_BUILD_COST_01: 不必要なコストを回避するために、コンテナのビルドプロセスをどのように設計しますか?
ロールバックに備えて複数のコンテナイメージを保持したいケースがあります。このような例では、4つ以上前のバージョンには戻らない、3ヶ月以上前のバージョンには戻らないといったポリシーを検討します。
古いコンテナイメージをコンテナレジストリから削除することでコストを節約します。
Examples of lifecycle policies に ECR でのライフサイクル例が示されています。
CONTAINER_BUILD_COST_02: 不必要なコストを回避するために、コンテナのビルドプロセスをどのように設計しますか?
(項目が別で同じ質問文です…)
ビルドプロセスは、ビルドごとにリソースを消費します。リソースを消費する = 課金が発生するということになります。ビルド時間を短縮することがコスト効率にとって重要です。
コンテナイメージのビルドステップでは、アプリケーションコードで使用されているすべての必要な依存関係、ライブラリ、およびモジュールがコンテナイメージにダウンロードされます。 不要な依存関係を除外し、ビルド時間を短くします。
組織内で共通的に使用する OS パッケージやランタイムが存在する場合には、それらをプレインストールする親イメージを用意することをお勧めします。 この親イメージを使用するコンテナイメージのビルド時間が短縮されます。
CONTAINER_BUILD_COST_03: コンテナイメージには、アプリケーションの実行に関連するもののみが含まれていることを確認してください
コンテナイメージのサイズは、イメージがコンテナレジストリからプルされるのに必要な時間に影響します。イメージサイズが大きい (数百または数千 MB) と、アプリケーションの起動時間が遅くなり、次のような問題が発生する可能性があります。
- イメージがプルされるのを待っている間、コンピューティング リソースを浪費
- スケールアウト操作が遅い
また、コンテナイメージはレジストリに保管されるため、コンテナイメージサイズはレジストリの利用料金に影響します。
CONTAINER_BUILD_COST_04: コンテナイメージのサイズをどのように縮小しますか?
いくつかの手法を用いてコンテナイメージサイズを小さく維持します。
- スクラッチイメージからのコンテナイメージのビルド
- 特に、OS からの最小限の外部依存関係 (Go、Rust、C など) を持つ実行可能アプリケーションをコンテナー化する場合に当てはまります
- 軽量のベースイメージを使用する
- RUNコマンドを連鎖させて命令の数を減らす
- 依存関係のサイズを減らすために、パッケージ マネージャー フラグの使用を検討する
- apt-get install --no-install-recommends など
- マルチステージビルドを使用する
- Dockerfile のベストプラクティスに従う
- ADD の代わりに COPY を使用
- WORKDIR を使用する場合は絶対パスを使用
- .dockerignore ファイルを使用してビルドプロセスから不要なファイルを除外
CONTAINER_BUILD_COST_05: 自動スケーリングと正常な終了をサポートするために、コンテナ化されたアプリケーションをどのように設計しますか?
コンテナアプリケーションはシステムシグナル (SIGTERM) を受け取り正常にシャットダウンするよう構成します。これはコストとは直接関係ありませんが、スケールインやスポットインスタンス利用に悪影響を及ぼす可能性があります。
コスト観点以外でもとても重要なロジックです。全てのコンテナアプリケーションに実装をしてみてください。
ECS のアプリケーションを正常にシャットダウンする方法
CONTAINER_BUILD_COST_06: 複数の CPU アーキテクチャをサポートするために、コンテナ化されたアプリケーションをどのように設計しますか?
ビルドプロセスのデフォルトの動作は、ビルドするインスタンスのアーキテクチャで実行するように設計されたコンテナイメージを作成することです。
CPU アーキテクチャごとにイメージを作成するには、x86 インスタンスと ARM ベースのインスタンスで同じビルド プロセスを実行します。
CONTAINER_BUILD_COST_07: コンテナ化されたアプリケーションの起動時のコストをどのように最小化しますか?
コンテナアプリケーションの起動時間が長くなると、コンピューティングリソースが浪費される可能性があります。
エントリポイントスクリプトはシンプルな実行となるよう工夫します。例えば、ファイルをダウンロードするといった処理はイメージビルド時に実行しておきエントリポイントスクリプトには含めないようにします。
CONTAINER_BUILD_COST_08: コンテナのビルドプロセスを作成するためにどのシステムを使用していますか?
ビルドシステムの開発、保守、運用を考慮してツールを選定します。
EC2 Image Builder、CodeBuild、CodePipeline などのマネージドサービスを使用して運用上のオーバーヘッドを削減します。AWS サービスの他に様々なサードパーティツールも世の中には存在します。
持続可能性の柱
持続可能性の柱は、環境の持続可能性に焦点を当てています。
CONTAINER_BUILD_SUSTAINABILITY_01: 基礎となるリソースの使用を減らす方法でコンテナ化されたアプリケーションをどのように設計しますか?
コンテナイメージのビルドにかかる時間を短縮します。これによりリソースの消費を削減します。
コンテナイメージのサイズを小さくすることで、このイメージがインスタンスにプルされるまでの時間を短縮します。これは、コンテナイメージを実行および保存するためのリソースの使用量削減に役立ちます。
CONTAINER_BUILD_SUSTAINABILITY_02: コンテナ化されたアプリケーションをエネルギー効率の高いハードウェアで実行するためにどのようにサポートしていますか?
コンテナ化されたアプリケーションがさまざまなインスタンスタイプとアーキテクチャで実行できることを確認します。複数のアーキテクチャで実行可能であればエネルギー効率の高いハードウェアへの移行が容易になります。
CONTAINER_BUILD_SUSTAINABILITY_03: 基盤となるリソースの効率を向上させるために、ビルドツールとサービスをどのように設計しますか?
CodeBuild などの動的に生成されるビルドサーバーを利用します。ビルドプロセス開始時にインフラがプロビジョニングされ、ビルドプロセス終了と同時にインフラが削除されます。
親イメージまたはベースイメージは最新状態に更新します。アップデートがコンピュートリソースの消費削減に役立つ可能性があります。
未使用のコンテナイメージは定期的に削除します。
参考
AWS Well-Architected
Container Build Lens – AWS Well-Architected
以上、吉井 亮 がお届けしました。