AWSコンテナ設計・構築本格入門を読んだ感想など

私はAWSの資格は持っているもののECSは業務で使ったことがなく、何かのハンズオンでちょっと触ったくらいの知識量でした。
そんな時[AWSコンテナ設計・構築\[本格\]入門 \| SBクリエイティブ](https://www.sbcr.jp/product/4815607654/)という本が体系的にまとまっててイイ！という話を聞いたので実際に買って読んで手を動かしてみました。

中身の９割くらいはインフラはこうあるべき論、ベストプラクティス、そしてサンプルアプリケーションをECSとその他サービスで動かすというもので、評判通りこの本一冊で初心者から中級者にステップアップできるのではないかと思いました。

ただ出てくる各サービスの説明は最低限に収まっているのである程度の事前知識は必要だと感じました。その場合は別の本などでAWSサービス全般をインプットする必要がありそうです。

私にとっては既に一通り触っていたのでそこまで新鮮ではありませんでしたが、こうあるべき論とベストプラクティスはなるほどなと思いました。firelensやfargateなど比較的新しめの機能も触れてよかったです。

本書にも繰り返し書かれていることですが、この本に書かれていることはあくまで基本というか骨子なので実際にインフラを構築する場合はその要件に併せて適切に構築する必要があります。

私も今後業務でECSを触りそうなので、その時本に書いてあったことが思い出せるよう自分がなるほどと思ったことを書いておきます。AWS認定資格SAA、SAPを持っているものの実際に構築したことはない…という私のような方には役立つかもしれません。
そして良さそうだなと思ったら是非[AWSコンテナ設計・構築\[本格\]入門 \| SBクリエイティブ](https://www.sbcr.jp/product/4815607654/)を買ってみてください。初心者の方はもちろん既に業務でECSを使っている方でも、ベストプラクティスやネットワーク構成など得るものは少なくないのではないかと思います。


## コンテナ設計に必要なAWS基礎知識

* 次に挙げるような一部の例を除きFargate一択
    * 従来のオンプレミスの経験やナレッジを活用したい
    * デプロイとスケール速度を極限まで早くしたい
        * イメージのキャッシュをEC2に保持できるため
    * ２００GB以上のストレージを使う必要がある
    * KubernetesのDaemonSetを使いたい
    * GPUを使いたい
* ECSとKubernetesどっちを採用するか問題
    * Kubernetes
        * 運用フェーズを含めてインフラもしっかり見られる体制がある場合
        * AWS以外のクラウドやオンプレを視野に入れている場合
    * ECS
        * それ以外

## コンテナを利用したAWSアーキテクチャ

* Well-Architectedフレームワークの活用
    * 運用上の優秀性
        * どのようにシステムの状態を把握するか
        * どのように不具合の修正を容易にするか
        * どのようにデプロイのリスクを低減するか
        * モニタリングの主な目的はシステムの可用性を維持するために問題発生に気がつくこと
        * オブザーバビリティ（可観測性）→システム全体を俯瞰しつつ、内部状態まで深掘りできるような状態
        * ロギング設計
            * CloudWatch Logsサブスクリプションフィルター
                * ログ内に特定文字列が含まれているログの抽出
                * CloudWatch Logs Insightsを使いたい
            * FireLens
                * CloudWatch Logs以外のAWSサービスやAWS外のSaaSへのログ転送（もちろんCloudWatch Logsにも転送できる）
                * ログ出力先を複数指定したい場合（コンテナごとにログの出力先は１つしか指定できない）
                * 大量のログが出力される場合（料金面）
        * メトリクス設計
            * CloudWatchメトリクス
                * ECSサービス単位（ECSタスクではない）
                    * CPUUtilization
                    * MemoryUtilization
            * CloudWatch Container Insights
                * ECSタスク単位
                * CPUとメモリの他にネットワーク、ストレージ、ECSタスク、ECSサービスのメトリクスが取得できる
                * ダッシュボードが用意される
                * 別途料金がかかるので注意
            * ☆クラウドを利用することで、安定稼働やハードウェアの追加調達の判断ではなく、リソース効率性やコンピューティングリソースの妥当性、オートスケールの発動条件にモニタリングの用途を変えることができる。そのためサーバに関する監視設計への関心よりも、サービス側に対して関心を寄せることができる
        * トレース設計
            * X-Ray
                * アプリケーションの内部処理の呼び出しやサービス間のトランザクション情報
                * サービスマップのダッシュボードでシステム全体の可視化
                * ECSタスク定義の中にアプリケーションコンテナとX-Rayコンテナを同梱＋アプリケーションにX-RayのSDKでコーディングすることで使える
                * IAM権限(AWSXRayDaemonWriteAccess)が必要(タスク実行ロールではなく、ECSタスクロールへの権限付与)
                * エンドポイントがAWSパブリックネットワークに存在するので、VPC EndpointやNATゲートウェイなど考慮が必要
        * CI/CD設計
            * 常にテストして自動でプロダクション環境にリリース可能な状態としておくことでアジリティを高める
            * ソフトウェア開発サイクルを自動化・高速化するために用いられる手法
                * 変更を自動でリリースできるようになり開発に集中できる
                * 迅速に不具合を発見でき品質改善の効率化が図れる
                * コンテナのポータビリティ、再現性、軽量さと相性がいい
            * 構成について
                * 環境ごとにアカウントを用意
                * CodeCommitは環境間で共有リソースにする（資産管理、運用の複雑性を回避）
                    * CodeBuildは環境ごとに配置（環境ごとに動作が違う。本番はステージングでテスト・ビルドしたイメージをそのまま使う）
                    * CodeDeployも環境ごとに配置（アカウントを跨いだ設計が発生して複雑になってしまう）
                    * ECRは開発とステージング兼本番の２つを共有リソースに保持（開発は開発者が手動でイメージの操作をできるように、本番はできないようにすることで開発者の自由度が高まる）
                        * 全てのECRを共有リソース用アカウントに集約することで情報源を一元化できる
                        * CI/CD及びコンテナのAWSリソース定義が各環境のアカウント間で同一になる
            * アプリケーション開発が本格化する前にCI/CDパイプラインを用意する
            * イメージのメンテナンス運用
                * ライフサイクルポリシーを使って一定期間の身、もしくは指定した世代分のみ保持（コスト）
                * 複数環境で使う場合はライフサイクルポリシーによって意図せず消されてしまう可能性がある
                    * 環境ごとに固有のタグ識別文字をつけて、タグごとのライフサイクルポリシーを指定する
                    * この時コードリポジトリのコミットIDも付与するとECSで展開されているコンテナがどのソースコードバージョンで稼働しているのかが明確になる
            * ガバナンス・コンプライアンスの考慮
                * ECS: IAMポリシーによる書き込み・実行制御
                * ECR: リポジトリポリシーによるアクセス制御
                * S3: バケットポリシーによるアクセス制御
                * CodePipeline上に承認アクションを設定
            * Bastion設計
                * セッションマネージャーを活用することでアタックサーフェスを減らせるし、OSに関する責任をAWS側に移譲できる
    * セキュリティ
        * コンテナ開発のセキュリティベストプラクティス
            * NIST SP８００-１９０
                * ※ECS/Fargate利用時に考慮が必要な項目は☆
                * イメージ
                    * ☆イメージの脆弱性
                        * →ECRによる脆弱性スキャン
                            * 有効化するだけで追加コストなしにプッシュ時にスキャンしてくれる
                        * →Trivyによる脆弱性スキャン
                            * ベースイメージに含まれるOSパッケージだけでなく、アプリケーションの依存関係(pip, gem, npmなど)もスキャンできる
                            * 実行方法がシンプルでCI/CDパイプラインに組み込みやすい
                        * 継続的なスキャン
                            * イメージは時間が経てば内部コンポーネントのバージョンが古くなり脆弱性が発見される恐れがある
                            * 頻繁にデプロイされない（スキャンされない）プロダクトについては、CloudWatch Eventsから定期的にLambdaを実行してECRの手動スキャン実行などが必要
                    * ☆イメージ設定の不具合
                        * Dockle
                            * コンテナベストプラクティスチェックツール
                            * The Center for Internet Security(CIS)やDocker社でベストプラクティスが公開されているが、こちらのチェックを実行できる
                            * 簡単に実行が可能なのでこちらもCI/CDプロセスに導入し継続的なイメージ設定のチェックを行う
                    * ☆埋め込まれたマルウェア
                        * 提供元が不明なベースイメージの利用は避ける
                        * GuardDutyを使って外部との不正な通信をチェックする
                            * VPCフローログ、CLoudTrailイベントログ、DNSログなどから悪意のある通信を検知してくれる
                    * ☆埋め込まれた平文の秘密情報
                        * Secrets ManagerやSSMパラメータストアのARNと環境変数名をタスク定義内でマッピング
                            * コンテナ内でOSの環境変数として認識される
                    * ☆信頼できないイメージの使用
                        * イメージとレジストリを一元管理
                            * 検証が不十分な外部コンテナイメージを利用しない
                            * 自分達がビルドし十分にテストされたイメージを利用する
                        * ECRは対応していないが、Docker HubであればDocker Content Trust(DCT)を使ってイメージの整合性と公開社情報を検証できる
                * レジストリ
                    * レジストリへのセキュアでない接続
                    * ☆レジストリ内の古いイメージ
                        * 使用しない古いイメージは脆弱性が含まれていつ可能性があるので適切に削除する
                        * ECRではイメージタグの上書きを禁止するIMMUTABLE設定を行う
                            * イメージを上書きすることができるが、タグが上書きされると既に存在しているイメージのタグも外れてしまう
                            * コンテナイメージの一貫性を維持、不正なイメージの混入を防ぐためこの設定を有効化
                    * ☆不十分な認証・認可制限
                        * ECRはプライベートリポジトリとして作成
                            * パブリックリポジトリを作成できないよう(ecr-public:*をDeny)、IAMポリシーを作成しSCPに適用(予防的ガードレール戦略）
                        * リポジトリポリシーを設定
                            * イメージのプッシュ: CICodeBuild(からのみ許可）
                            * イメージのプル: IAMユーザ及びECSからのみ許可
                * オーケストレータ
                    * ☆無制限の管理アクセス
                        * 最小権限のみを割り当てる
                        * 業務フローによってはIAMグループごとにアクセスできるECSクラスターを限定してもいいが、要件次第
                        * 権限を縛りすぎると開発アジリティを損ねるので考慮する
                    * ☆コンテナ間ネットワークトラフィックの不十分な分離
                        * Fargateが起動するとタスクごとにENIが割り当てられる
                        * VPC全体を俯瞰して考える
                    * ワークロードの機微性レベルの混合
                    * オーケストレー他のノードの信頼性
                * コンテナ
                    * ランタイムソフトウェア内の脆弱性
                    * ☆コンテナからの無制限ネットワークアクセス
                        * パブリックネットワーク→VPC
                            * WAFを利用してIP、ポート番号、HTTPヘッダの付与、メソッド限定、SQLインジェクションなどの防御をする
                                * CloudFront, ALB, API Gatewayに対応
                            * ECSタスクは通信はALBに紐づくセキュリティグループIDからのみ許可
                        * ECSタスク間
                            * ALB, ECSタスクの各セキュリティーグループ間でそれぞれセキュリティーグループID指定で通信を最小限に限定する
                        * VPC→パブリックネットワーク
                            * ECSタスクはPrivate Subnetに置く
                            * NATゲートウェイを利用する
                            * さらに絞りたい時はVPC Endpointを各サービス（S3, ECR, CloudWatch Logsなど）ごとに用意する
                                * ただしインターフェース型のVPC Endpointは時間と処理量によって課金されるので注意
                    * セキュアでないコンテナランタイムの設定
                    * ☆アプリケーションの脆弱性
                        * ECSタスク定義でコンテナのルートファイルシステムアクセスを読み取り専用に変更
                    * ☆未承認のコンテナ
                        * 稼働するコンテナが適切な承認プロセスを経た状態でデプロイされていることを徹底
                        * ECRのリポジトリポリシーでCI/CD以外のイメージプッシュを拒否
                        * IAMユーザに対してECSタスク定義の更新を拒否
                * ホスト
                    * 大きなアタックサーフェス
                    * 共有カーネル
                    * ホストOSコンポーネントの脆弱性
                    * 不適切なユーザーアクセス権
                    * ホストファイルシステムへの改ざん
    * 信頼性
        * Well-Architectedフレームワークの設計原則
            * 障害から自動的に復旧する
            * 復旧手順をテストする
            * 水平方向にスケールしてワークロード全体の可用性を高める
            * キャパシティを推測することをやめる
            * オートメーションで変更を管理する
        * マルチAZ構成
            * FargateでECSを動かすとECSサービス内部のスケジューラがベストエフォートでAZタスクをAZ間でバランスしてくれる
        * 障害時切り離しと復旧
            * RunningTaskCount（ECSサービスの稼働タスク数）かTaskCount（ECSクラスターの稼働タスク数）とCloudWatchアラームを組み合わせてECSタスク障害を検知する
            * ECSは常にタスク数を設定値に保つのでわざわざアラーム通知しなくてもいい場合がある
            * ECSの場合、リタイアイベントなどでSIGTERMシグナルが送られる場合があり処理の生合成が必要な場合、アプリケーションを適切にハンドリングする必要がある
                * SIGTERMの応答が３０秒ない場合はSIGKILLが発行される
        * システムメンテナンス時におけるサービス停止
            * ECSタスクが登録されているターゲットグループへの転送ルールとメンテナンス用の固定レスポンスを返却するルールを用意
            * メンテナンス時にコンソールやLambdaを使用してリスナールールの優先度を変更
    * パフォーマンス効率
        * **ビジネスで求められるシステムへの需要を満たしつつ、技術領域の進歩や環境の変化に対応可能なアーキテクチャを目指すこと**
        * パフォーマンス設計の目的はビジネスで求められるシステムへの需要を満たすことなため、ビジネス上の要件が前提
        * とはいえクラウドでリソースを容易にスケールできるため厳密な見積は不要
        * 既存のワークロードを模倣したベンチマークや負荷テストでパフォーマンス要件を満たすかどうかの確認は必要
        * 流れ
            * ビジネス上のパフォーマンス要件の確認
                * 最低要件を確認
            * リソースの割り当て
                * ある程度余裕を考えながらリソースの割り当て
            * スケール戦略の検討
                * スケールアウトかスケールアップか
                    * アプリケーションの特性に依存するが一般的にはスケールアウト構成
                        * リソース上限に到達しにくい
                        * タスクの停止が不要
                        * スケール判断の自動化がやりやすい
                        * 可用性と耐障害性が向上する
                    * リージョンあたりの起動可能なFargateのECSタスク数は１０００なので注意
                    * またECSタスクごとにENIが割り当てられるのでIPアドレスの枯渇に注意
                * ステップスケーリングポリシー
                    * メトリクスの値に応じてステップを刻みスケーリング
                * ターゲット追跡スケーリングポリシー (おすすめ)
                    * メトリクスの値を維持するようにスケーリング
                    * 注意事項
                        * メトリクスがターゲット値を超えている場合のみスケールアウトできる
                        * スケールインはスケールアウトに比べて緩やかに実行される
                        * 複数のメトリクスのターゲット値を定義できる
                            * スケールアウトはいずれかのターゲット値が超過すれば行われる
                            * スケールインは全てのターゲット値が下回らなければ行われない
            * テストの実施
                * 負荷試験前に事前に少量のリクエストを実行して以下の確認もしておく
                    * 期待する一連のメトリクスが取得できているか
                    * アプリケーションからエラーが出力されていないか
                    * ログに欠損はないか、ログレベルは妥当か
            * メトリクスの確認
                * パフォーマンス要件で定義したリクエスト量が満たせているか
                * リソースの余剰や逼迫が発生していないか
                * スケールイン・スケールアウトは正しく発動するか
                * スケールアウト、スケールイン時にアプリケーションからエラーが出力されていないか
            * リソース割り当てやスケール戦略の見直し
                * リソース割り当てやスケールの閾値を見直す
                * Well-Architectedフレームワークにの書いてあるがより頻繁に実験し試行錯誤しながら最適な比較検証を行うべき
        * マインドセットについて
            * 運用コストとのバランス
            * 最適なリソースを判断するために必要なメトリクスを収集し、適切なサイジングを行う
    * コスト最適化
        * コストの利用状況を把握し無駄を削減
        * クラウド利用の方が高くつく可能性もある
        * ECSタスク数とリソースのサイジング
            * アプリケーションの稼働に必要十分なリソース量を定めることがコスト最適化の基本
        * Compute Savings Plans (Fargate)
            * 1年または３年の期間で指定リソースの利用事前コミットによる割引
        * ECRコンテナイメージ
            * イメージを適切に削除することはコスト削減に加え運用管理やセキュリティにおいても有効
            * ライフサイクルポリシーの設定で実現可能
        * 開発・ステージング環境のECSタスク稼働時間
            * 夜間不要なことが多いのでCloudWatch EventsからLambdaを発火させECSサービスのタスク数を更新
            * 開発環境は可用性を犠牲にして最小限のタスク数にする
        * Fargate Spot
            * 停止をある程度許容する代わりに約７割引で使える
            * 使用する場合はECSサービスにキャパシティプロバイダ戦略の設定が必須
                * FARGATEとFARGATE_SPOTそれぞれのベース起動数とウェイト起動数を指定してオンデマンドとスポットの起動割合を指定する
            * タスクが終了するまで２分間の猶予がある
                * CloudWatch EventsにECSタスクの状態変更がイベント通知される
                * 実行中のタスクに対してSIGTERMシグナルも送信される
        * コンテナイメージサイズの削減
            * Fargateはイメージをキャッシュしないため、タスクが増えるたびに転送量がかかる
            * コンテナ構成を最小限にすることでコストメリットがある
                * またダウンロード時間が短くなり起動が速くなる
                * 不要なライブラリなどを取り除くことで堅牢になる

## コンテナを構築する

* ネットワーク設計
    * 用途(ingress, application, db, management)、NW区分(public/private)、AZ(1a, 1c)、CIDR、サブネット名（sbctr-subnet-NW区分-用途-AZ）
        * サブネットのネットワーク部分は間隔をあけておく
        * これらを表にまとめておく
        * 単一のAZしか使わない想定でもAZ障害に備えてAZを２つ確保しておく
    * ルーティングテーブルは共通のものを一つ作成
        * 通信の特性を示すネーミング
* ECRは手動で作成
* Cloud9からECRにアクセスするためには別途IAMポリシーの割り当てが必要
* エンドポイント
    * com.amazonaws.(region).ecr.api: ECR API読み出しに利用 (インターフェース型エンドポイント)
    * com.amazonaws.(region).ecr.dkr: dockerクライアントコマンドの呼び出しに利用（インターフェース型エンドポイント）
    * com.amazonaws.(region).s3: dockerイメージ取得に利用 (ゲートウェイ型VPCエンドポイント)
* Fargateの仕組み
    * タスクは複数のコンテナからなる
    * タスクはマイクロVM上で稼働する
    * マイクロVMはFirecrackerと呼ばれるシステムで稼働する
* ECSサービス設定のロードバランサーの設定で、本番用リスナーとテスト用リスナーの二つ紐づけられるので、リスナーを二つ作っておく
* ECSサービス設定でBlue/Greenデプロイメントの設定を行うが、デフォルトではアプリケーションリリース後即座に切り替わる設定となっている
    * 変更するためにはCodeDeployのDeployment settings内のTraffic reroutingの設定を行う
* Fargate1.4.0以降はSecrets ManagetおよびSystems Managerにアクセスするためにはインターフェース型VPCエンドポイントが必要（以前はFargate ENIというAWS管理のENIが利用されていて通信が利用者で管理できなかった）
* サブネットはサービスごとに分ける
    * ALB
    * ECS(Front, Backend), FrontBackend間のALBも
    * Cloud9
    * Aurora
    * VPC Endpoint
* CodeBuildでdocker hubからイメージをプルすると、IPガチャの結果によってはTo many Requestで失敗する可能性がある
    * 対処法は次の３つ
        * CodeBuildでdocker loginする
        * ECRにあらかじめベースイメージを登録する
        * VPC内でCodeBuildを起動する
* CodePipelineのappspec.yml内のTaskDefinitionの値は`<TASK_DEFINITION>`と指定すれば[自動的に置き換えられる](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-ecs-ecr-codedeploy.html)
* CodePipelineのtaskdef.jsonのContainerDefinitionのimageの値は`<IMAGE1_NAME>`と指定すれば[自動的に置き換えられる](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-ecs-ecr-codedeploy.html)
* 攻撃の防御はなるべく攻撃者の近くでブロックした方がいいため、WAFはCloudFrontで行うことが推奨される
* AWS WAF
    * ルール
        * リクエストの検査方法の定義
            * 特定のIPアドレスだけ許可
            * 特定のヘッダのみ許可など
    * ルールグループ
        * ルールの集約
        * ルールの優先順位を設定できる
        * ルールにはWCU (WAF Capacity Unit)が定められており、合計が上限値を超えないようにしないといけない
            * なおWCUはルールグループ作成時のみ設定が可能
    * ウェブACL
        * ルールグループと適用するAWSリソースを紐づける
        * 複数のルールグループを紐付け可能
            * ただしWCU上限は１５００
        * ルールグループとウェブACLはそれぞれGlobalか各Regionを指定する必要があり、ウェブACLに設定するルールグループはそれぞれ同一である必要がある
            * またCloudFrontの場合はGlobal、ALBの場合はRegionである必要がある
* ECRの単一リポジトリにタグ違いで複数のシステムのイメージを格納するのもあり
* firelensの設定方法 (カスタムしたfluentbitを使う場合)
    * 1. オリジナルのfluentbitをECRにプッシュしておく
    * 2. タスク定義で「FireLens の統合を有効にする」にチェックを入れ、タイプとイメージを指定する
    * 3. log_routerというコンテナ定義が追加されるのでCPUなどを設定
    * ４。 firelensに流したいログを出力するコンテナのログ設定をawsfirelensに変更
    * 5. fluentbitのカスタム設定ファイルをタスク定義にJSONで指定 (firelensConfiguration)
* bastionサーバをFargateで構築するときのポイント
    * ssmエージェントを実行するイメージを作成（create-activationが必要）
    * タスクロールとしてSystems Managerへ権限を渡すためのiam:PassRoleとアクティベーション用の権限が必要
    * Systems Managerに渡すIAMロールとしてAmazonSSMManagedInstanceCore管理ポリシーがアタッチされたIAMロールが必要
    * ssmmessagesとssmのVPCエンドポイントが必要
    * Systems Managerのインスタンスティアをアドバンスドインスタンスティアに設定変更する必要がある