CDKで、ECS(Fargate)ベースのアプリケーションとCICD環境を構築しました。コンテナのCICDにはGitopsを導入しました。CDKでコード化したので、紹介します。説明しないもの各種AWSリソースがどんなものであるかなどは省いています。Kit)を使用したインフラ構築を紹介しています。CDKについて簡単に説明します。Kit)とは、AWSのリソース(S3やLambdaなど)定義やリソース同士の関連をプログラミングコードで記述可能なフレームワークです。CDKはyamlファイルではなくプログラミングコードで記述可能であり、より扱いやすくなったものと言えます。TypeScript、Python、Java、.NETのSDKが提供されています。本記事で紹介するコードはTypeScriptで記述しています。個人的には以下の点で優れている感触です。最上位に「Stack」があります。Stackとして生成されます。Stackの中に「Construct」という要素があり、その中に各種AWSリソースがあります。各種AWSリソースもまたConstructを継承した要素で、入れ子構造にできます。ConstructはStack内に複数作成でき、Construct間の関連も記述できます。上記の図では、以下の2つのConstructがあり、それらが連携して動作するシステムを構築しています。functionが受け取り、SQSへエンキューするSQSからメッセージをサブスクライブし、DynamoDBへ登録したデータを取得するAPIを設置Construct単位で部品化することで、コードとして読み易いだけでなく、似たような構成にバリエーションをつけることで、柔軟なアーキテクチャを構築しやすくなります。その他詳細は以下ドキュメントを参照ください。ドキュメントhttps://docs.aws.amazon.com/cdk/latest/guide/home.htmlAPIリファレンスhttps://docs.aws.amazon.com/cdk/api/latest/アーキテクチャ概要アプリケーションとCICD基盤、Githubリポジトリを含んだ構成図です。CDKで構築した部分、それ以外は手動で事前に作成している部分です。以下の要素で構成されています。CDリポジトリやPullRequestが登場しますが、少々ややこしいためこれらは後述します。アプリケーションリポジトリECSで稼働させるアプリケーションには、サンプルとしてLaravelを使用します。newしただけのものを稼働させます。yarn.lock内部に2つのDockerfileが入っており、それぞれnginx用とLaravel本体用です。Laravelの前段にNginxコンテナを配置し、FastCGIモードでPHPを起動する構成としています。こちらの記事を参考にさせて頂きました。https://qiita.com/kobayashi-m42/items/95ba3f611b3def27241ehttps://qiita.com/tomoyamachi/items/991a0d149e9585cac15f#php-fpmコンテナを作成ひとつのDockerfileにまとめることも出来ると思いますが、複数のDockerコンテナを連携動作させるサンプルという意味合いもあり、webサーバーとアプリケーションサーバーでコンテナを分割した構成にしています。CDKリポジトリCDKリポジトリの概要を紹介します。parameter定義ファイルサンプルCDKアプリケーション構築ある環境における、CDK構築対象の全てのスタック構築に関するコードがbin/example/index.tsに記述されています。scriptsディレクトリ配下に、CDKアプリケーションの作成および削除スクリプトが置かれており、これらスクリプトから環境毎のコンテキストと共にbin/example/index.tsを呼び出しています。実行してCDKアプリケーションを作成します。contextに必要なコンテキストを追加できます。bin/example/index.tsを呼び出す際にコンテキストを指定しています。環境名取得デプロイスクリプトで、環境名をenvコンテキストで指定しています。環境毎に値が異なるプロパティをそれぞれ記述しています。Definitionに指定する、リソース関連プロパティを記述しています。パラメータとシークレットの登録フィールド名リスト取得パラメータとシークレットのフィールド名が記述されたリストを取得しています。上記は各スタックに指定するプロパティは省略しています。managerを使用して扱っています。mangerも変わりません。CDKコードにて扱う型の都合上、以下のように使い分けています。登録スクリプトを置いています。ように、sample部分を環境名にしたファイルを複製し、パラメータ値を記述した上で、以下コマンドで実行します。URLをこの方法で登録しようとすると、エラーとなってしまうため、(本質部分ではないので、詳細は割愛します。managerへは、機密情報のため、手動でAWSコンソールから登録します。CDKコード内で扱うためのユーティリティをそれぞれ用意しています。imageを記述しているのリポジトリです。Gitopsを導入し、CIパイプラインスタックとバックエンドスタックを繋げる役割を果たすのが本リポジトリです。本リポジトリの詳細は後述します。CIパイプラインスタックCIパイプラインスタックでは、CodePipelineに以下のステージを構築しています。DeployはGitopsによるECSのCD(デプロイ)運用を行うためのものです。詳細は後述しますが、ビルドしたイメージを使用したデプロイを行うための準備作業という認識で差し支えありません。イメージタグには、ソースであるGithubリポジトリのコミットIDを使用しています。ここでPushされたイメージが、ECS環境へデプロイされますが、不具合等の理由で、速やかに以前のバージョンへ戻したいという状況が想定されます。ECSでは、タグを含めたイメージURLを指定してデプロイするため、イメージタグにてバージョン管理することが重要ですが、設定の簡潔さと過去バージョンの辿りやすさのバランスをとり、gitリポジトリのコミットIDをそのままイメージタグとして使用しています。buildを行い、イメージをECRリポジトリへプッシュする」というものですが、この部分は必ずしもAWSリソースで構築する必要はなく、CircleCIなど他の手段でも実現可能です。ECRをCDKによる構築対象に含めてしまうと、このような置き換えが難しくなります。交換可能な設計にしておくためにECRリポジトリはスタックから切り離しています。Deploy)CDKリポジトリのlib/application-ci-ecr-stack.tsにCIパイプラインをスタックとして部品化したものを記述しています。以下、CodePipeline作成部分を一部抜粋します。PrepareDeployの各ステージを構築しています。})Sourceには、アプリケーションのGithubリポジトリやブランチ名、Webhookトリガーに必要なOAuthTokenを指定しています。Buildには、アプリケーション内のDockerfile群をビルドし、それぞれECRへプッシュする処理を記述します。ビルド内容をbuildspecに記述し、CodeBuildには環境に応じた環境変数を指定しています。CDリポジトリへのプルリクエストを作成する処理を記述します。ここでは、プルリクエストを作成するため、hubを使用しています。本来は、予めインストール済みのイメージを作成し、使用するべきですが、ここでは直接インストールしています。CDリポジトリへの変更内容は後述しますが、imegedefinitions.jsonのimageUriを、ビルドしたイメージのタグを使用するように置換しています。Task定義と、それを取り巻くVPC、ALB、データベース(Aurora)、ドメイン、SSL化などの関連リソースを合わせて構築しています。Serviceへアクセスし、アプリケーションからはセキュアなサブネットに配置されたデータベースとの接続可能となるところまで構築されます。ECSには複数のサービスを設定可能な構成としており、今回はlaravelアプリケーションのみですが、ほかのアプリケーションも追加可能な拡張性を持たせています。Serviceへ作成したタスク定義を適用します。Serviceのデプロイ自動化手段ECSサービスのデプロイには、以下の作業が必要です。稼働中のタスクに新たなタスク定義を適用するAWSでこの作業を自動化するためには、以下の方法があります。CDKのCodeDeployはECSプラットフォームをサポートしていないため、後者を採用しています。applications.」と記載されています。1.16.1時点)https://docs.aws.amazon.com/cdk/api/latest/docs/aws-codedeploy-readme.html今後対応されたら、また見直そうと思います。zoneやcertificateは、ひとつのアプリケーションだけでなく、複数の目的で様々な箇所で使用することが想定されます。app.example.comはCDKで構築するアプリケーションで使用するが、blog.example.comは別途構築したWordPressから使用する、などが考えられます。CDKで構築しようとしているリソース群とライフサイクルが異なる場合は、CDKが切り出した方がいいと思います。CodePipelineCDKリポジトリのlib/backend-stack.tsにバックエンドリソースをスタックとして部品化したものを記述しています。Clusterを作成しています。Clusters作成時にパラメータとして指定しています。このように、リソース同士の関連を直感的に記述できます。});ALBとECSタスク定義、ECSサービス、CDパイプラインここからは、サービス単位(=アプリケーション単位)で作成します。今回は1つのサービス(Laravelアプリケーション)のみですが、複数のサービスを追加できるような作りにしています。Definition、そしてサービスのロードバランサー(ALB)を作成しています。Serviceの作成には、aws-ecs-patternsのApplicationLoadBalancedFargateServiceを使用しています。AレコードやSSL証明書とを紐づける必要があります。本来は、これらのリソースもCDKコード上で作成する必要がありますが、ApplicationLoadBalancedFargateServiceはこの辺りも一緒に生成してくれます。ServiceへデプロイするCodePipelineを構築します。CDリポジトリによるプルリクエストとマージによって、ECSサービスをデプロイする仕組みを導入しており、Weaveworksが提唱しているkubernetesに対するデプロイ管理手法であるGitopsを参考にしています。Gitopsについては以下記事が参考になります。https://www.infoq.com/jp/news/2018/11/gitops-weaveworks/GItopsはkubernetesに適用する仕組みとして提唱されていますが、実のところECSが提供するものはKubernetesと同じような機能であり、コンテナのデプロイ時に検討するべきこともわりと似たような概念です。リリースバージョンをGitで管理でき、Gitに対する操作だけでリリース/ロールバックが可能です。リリース/ロールバックを実施する時点では、コードやDockerイメージのビルドは既に完了しているため、ビルドを待つ必要はなく、監視の必要があるのは、純粋にECSサービスのデプロイの時間だけです。AWSの仕組みを知らないアプリケーション開発者も、Gitの操作のみでリリースを行うことも可能です。AWSをメンテするエンジニアやデプロイモニタリングは必要ですが、普段の運用時の負荷を削減し、アプリケーションエンジニアのAWS学習コストを減らせるため、チームとしてもワークし易くなるかと思います。CDリポジトリecs配下にECSサービス毎にディレクトリを作成し、imagedefinitions.jsonを格納します。imagedefinitions.jsonimagedefinitions.jsonには、ECSサービス内のタスク毎に適用するDockerイメージのURIを記述します。]コード変更からECSデプロイまでの流れアプリケーションのデプロイは以下の流れで行われます。CDKで構築した、ECS(Fargate)ベースのLaravelアプリケーションとCICD環境を紹介しました。ECSを取り上げ、CICDも含めた環境を考えていったところ、本記事のような構成になりました。改善の余地はあると思いますが、参考になれば幸いです。CDKでは、サーバレスアプリケーション構築でも威力を発揮するため、今後いくつかのアーキテクチャパターンを作成していこうと思います。CDリポジトリコード変更からECSデプロイまでの流れまとめ。