LoginSignup
7
3

More than 1 year has passed since last update.

AWS Copilotで検証環境を構築した話

Last updated at Posted at 2021-12-15

はじめに

こんにちは、GxPの@ttanaka-gxpです。
この記事はグロースエクスパートナーズ Advent Calendar 2021の16日目です。

今回は以前から興味のあったAWS Copilotについて、ちょうど業務の中でECS上で動作させるAPIを構築する作業があり、そちらで構築しながら試させてもらった内容を、サンプルアプリも交えつつまとめようと思います。

AWS Copilot CLIについて

公式から抜粋

Copilot CLI は、AWS App Runner、Amazon ECS および AWS Fargate を利用したプロダクションレディなコンテナ化されたアプリケーションのビルド、リリース、そして運用のためのツールです。 開発のスタートからステージング環境へのプッシュ、そして本番環境へのリリースまで、Copilot はアプリケーション開発ライフサイクル全体の管理を容易にします。

具体的にはソースコードとそれを動作させるためのDockerfileがあれば、いくつかのコマンド実行と定義ファイルの修正だけでコンテナアプリケーションとして動作させるための一通りのインフラ構築やCI/CDパイプラインの構築など行ってくれるツールです。

環境構築

  • 実行環境はWindows10
  • リリースノートからcopilot-windows.exeをダウンロードし、copilot.exeにリネームしてパスを通すだけです。
  • 公式のインストール手順はこちら
    • Home brewでのインストールが可能
    • Windows、Mac、Linuxと主要な環境にてバイナリをダウンロードしてインストールも可能

AWS Copilotに登場する概念

主要な概念

概念 内容
Applications その他の概念をまとめる外枠のようなもの
Environments 開発環境、検証環境、本番環境といったServiceやJobが動作する環境
EnvironmentはVPC単位で分離される
Services AWS上で動作させたい1サービスを表す
Copilotで一番重要な概念です
4つタイプが選べるので詳細は後述します
Jobs AWS上で動作させたい1つのバッチジョブを表す
具体的にはイベント起動のECSタスク(現時点ではスケジュール実行のみ)
今回は触れません
Pipelines 各EnviromentのService、Jobに対するビルド・デプロイを実現するCI/CDパイプラインを表す
具体的にはソースステージ、ビルドステージ、デプロイステージの3層構造のCode Pipeline
今回は触れません

概念の関連性

例としてWebアプリとバッチアプリが動作するシステムで、それぞれ開発環境、本番環境があり、各アプリに対するCICDが行える構成の場合におけるイメージ図です。(あくまで例でサンプルアプリではJobとPipelineには触れてません)
Copilot.png

Servicesで選択できる4つのタイプについて

タイプ 構成 用途
Request-Driven Web Service App Runner インターネットからアクセス可能なサービスの構築 ※1
Load Balanced Web Service ALB + ECSサービス インターネットからアクセス可能なサービスの構築 ※1
Backend Service Service Discovery + ECSサービス インターナルなアクセスを想定したサービスの構築
Worker Service SQS + ECSサービス Pub/Subアーキテクチャにおけるサブスクライバーの構築

※1 Request-Driven Web ServiceとLoad Balanced Web Serviceの違いについて
Request-Driven Web Serviceは、App Runner自体がロードバランサ―やオートスケール周りの機能を包括して提供しており手軽にスケーラブルな構成を構築できます。
ただし、WAFが適用できなかったり、カスタマイズできる範囲が限られていたりするので、細かい設定を行いたい場合はLoad Balanced Web ServiceでALB+ECSサービスの構成の方が向いているかと思います。

Load Balanced Web Serviceで構築する

完成後のインフラ構成図

システム構成図.png

api1とapi2は同じSpringアプリをデプロイ、環境変数を読んでHTMLに表示するサンプル実装になってます。
後述のCopilot関連の定義ファイルも含んだサンプル実装はこちらにPushしてあります。

Application作成

copilot app init copilot-demo --resource-tags owner=t.tanaka

--resource-tagsを指定しておくと、Environment、Serviceで作成されるすべてのリソースにも一律タグを付けられるので便利です。

Environment作成

copilot env init --name dev --app copilot-demo --default-config

dev環境を作成しています。
この時点でVPC周辺のリソース、ECSクラスターが作成されます。(色々作るので数分かかる)

Serviceを2つ作成

copilot svc init --name api1 --app copilot-demo --dockerfile ./Dockerfile --port 8080 --svc-type "Load Balanced Web Service"
copilot svc init --name api2 --app copilot-demo --dockerfile ./Dockerfile --port 8080 --svc-type "Load Balanced Web Service"

api1、api2という名前のServiceを作成しています。
この時点でAWS上にService毎のECRが作成され、ローカルにServiceの詳細を定義するための定義ファイルが生成されます。
.\copilot\${service名}\manifest.yml

manifest.ymlを編集

修正後のapi1のmanifest.yml

name: api1
type: Load Balanced Web Service

http:
  path: 'api1'
  healthcheck: '/api1/health'

image:
  build: Dockerfile
  port: 8080

cpu: 256       # ECSタスクに割り当てるCPUユニット
memory: 512    # ECSタスクに割り当てるメモリ
count: 1       # サービスの必要タスク数

network:
  vpc:
    placement: private # タスクをプライベートサブネットに配置する(デフォルトはパブリック)

variables: # 環境変数を定義する
  APP_NAME: api1 # Service毎にHTMLの表示を変えるための環境変数

dev環境へデプロイする

copilot svc deploy --name api1 --env dev
copilot svc deploy --name api2 --env dev

※Dockerビルドも行われるのでアプリのビルド成果物を取り込む場合は事前にビルドしておくこと。
コマンド毎に下記が行われます。(10分位かかります)

  • ローカルでdockerビルドしてECRへプッシュ
  • ALB、ターゲットグループ、ECS周りのリソース作成
  • タスクが起動し先ほどプッシュしたイメージがデプロイ

お掃除

copilot app delete

Are you sure you want to delete application copilot-demo? [? for help] (Y/n)と聞かれるのでyを入力するとcopilot-demoのApplicationに紐づくEnviroment、Serviceが一式削除されます。

使った上で気になった点

ALBのIP制限に制約がある

manifest.ymlにhttp.allowed_source_ipsという設定がありALBに対するIP制限を設定可能だが、これはALBのリスナールールの送信元IPを条件として設定しています。
リスナールールは、IP以外の条件ルールも設定可能だが1つのルールに設定可能な条件は5つまでという制約があり、Copilotでデプロイした場合のデフォルトで、manifest.ymlに定義したhttp.pathのルートとルート以降で2つがパス条件として使われ、DNSを設定している場合は更にホストヘッダーも条件として登録されるため、実質2つしかIP条件として設定できないことになります。

下記スクショの赤枠がデフォルトで設定される条件です。
(案件で構築時の設定なのでGithubのサンプルとは差異があります)
image.png
単純にIPによるアクセス制限を入れいたいだけであればセキュリティグループで設定したいところですが、私が試したCopilotのバージョン時点ではALBのセキュリティグループに制限を入れる方法はありませんでした。
(案件の時はIPが2つだと足りなかったので手でセキュリティグループに制限を追加しています)

エラー時はCloudFormationのスタックを見る

manifest.ymlで構文エラーはコンソールに表示されるログから原因がわかるので対応できるのですが、デプロイ先環境にてリソース作成上限にあったってしまうような、実行してみないとわからない類のエラーについては正直コンソールのログだけでは詳細が分からないことが何度かありました。

プライベートサブネットにタスクを配置する構成の場合、パブリックサブネットにNAT Gatewayがデプロイされるのですが、下記はNAT GatewayにアタッチするEIPがリソース作成上限に引っかかってしまった際のログです。
image.png
赤文字のところでエラーになっているのはわかるが、詳細が分かりません。
image.png
CloudFormationのスタックを見てみたところThe maximum number of addresses has been reached.と出ており、この記事にたどり着き原因がわかりました。
他にも大阪リージョンにデプロイしようとしてCloudMapがリージョンとしてサポートされていなく、デプロイできなかった時などもスタックのログを見ることで大体のエラーの解消出来ました。

カスタムドメインを設定したい場合はapp init時に指定する必要がある

カスタムドメインを設定する場合は下記の設定が必要です。(公式はこちら

  1. app init時のパラメータで--domain ${カスタムドメイン}を渡して実行
  2. manifest.ymlにhttp.aliasにカスタムドメインを指定

app init時にドメインの指定をしなかった場合はapp deleteしてinitし直す必要があるようです。
最初manifest.ymlの書き方しか見ていなく作り直すことになりました…
ただ、deleteしてもmanifest.ymlは残り、svc initしても既に同じService名でフォルダがある場合はmanifest.ymlを上書きしないので、そのまま流用して再作成できます。

まとめ

CloudFormationやTerraformといったIaCツールで1から定義する場合、結構な労力が必要になるので数コマンドで一通りの環境が構築できるのはかなり手軽に感じています。
トラブルシューティングで、AWS全般の知識はある程度必要になるかと思いますが、IaCや手で構築するにしても必要なレベルの知識だと思うのでそこまで大きな問題ではないと思います。
ただ、何度かスクラップ&ビルドで作り直したりしたのでいくら同じ定義で環境が作れるとしても、本番環境で扱うのはまだちょっとリスクがありそうな印象です。
あと、ALB周りの設定をもう少し弄れるようになってほしいなというのが個人的な感想です。

今回、案件で作った検証環境ではDBが不要だったので触れませんでしたが、RDS、S3、DynamoDBといったストレージ周りと連携する機能CICDを構築する機能などがあり、本格的に使っていくならこの辺りも検証して記事にしていきたいと思います。(きっと…)

参考資料

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3