概要
ECSで稼働させているサービスをGithubActionからデプロイする際は、ECSタスク定義をJsonファイルで定義して、GithubActionのワークフロー内でaws-actions/amazon-ecs-render-task-definition@v1を利用してタスク定義を上書きした上でECSリソースに反映する、というのが一般的な流れです。
TerraformなどのIaCを利用している場合は、Terraform内で対象のjsonファイルを呼び出すことにより、タスク定義の二重管理についても避けることができます。
しかし、このようにECSのタスク定義を管理する箇所を分離すると、感覚的にどこで何を管理しているのか分かりにくくなります。そこで、ECSのタスク定義をTerraformで直接管理しつつ、GithubActionでデプロイするための手順を考え、その手順について以下にまとめました。
ECSのタスク定義をterraformで定義する
ECSのタスク定義の例を以下に示します。
注目する点は、imageのバージョンを変数で定義している点です。Github Actionでデプロイする際に、この変数を更新することになります。
#タスク定義
resource "aws_ecs_task_definition" "example_task" {
family = "example-task"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([{
name = "example-container",
#imageのバージョンを変数で定義
image = "example-image:${var.image_version}",
essential: true,
memoryReservation = 256,
memory = 512,
cpu = 256,
portMappings: [
{
protocol: "tcp",
containerPort: 80
}
],
}])
}
variables.tfには以下のようにimage_versionを空で定義します。
variable "image_version" {}
この記事ではモジュール化した場合の例は示しませんが、例えば上位ディレクトリのmain.tfでECSのモジュールを呼び出す際には、上位ディレクトリのvariables.tfでもimage_versionは空で定義してください。
GithubActionでデプロイする際のワークフローを定義
GithubActionでデプロイする際のワークフローを定義します。
ECRにimageをpushするstepと、terraform applyを実行するstepを定義します。
name: deploy
on:
push:
branches:
- main
env:
AWS_REGION: ap-northeast-1
#imageのタグをgithubのshaにすることで、常に最新のimageをデプロイする
IMAGE_VERSION: ${{ github.sha }}
jobs:
deploy:
name: Push to ECR
runs-on: ubuntu-latest
steps:
- name: setup aws cli
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- uses: actions/checkout@v2
- uses: aws-actions/amazon-ecr-login@v1
id: amazon-ecr-login
- name: push ex-repo image to ECR
env:
ECR_REGISTRY: ${{ steps.amazon-ecr-login.outputs.registry }}
ECR_REPOSITORY: ex-image
run: |
docker build -t ex-image -f ./Dockerfile.prod ./
docker tag ex-image:latest ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_VERSION }}
docker push ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_VERSION }}
terraform:
name: Depploy to AWS ECS
runs-on: ubuntu-latest
#terraformを管理しているディレクトリに移動
defaults:
run:
working-directory: terraform-folder
steps:
- uses: actions/checkout@v2
#terraformのバージョンを指定
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.6.3
#terraform initを実行
- name: Terraform Init
run: terraform init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
#terraform applyを実行
#引数にimage_versionを渡す
- name: Terraform Apply
run: terraform apply -var "image_version=${{ env.IMAGE_VERSION }}" -auto-approve
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
ポイントとしては、まずGithubActionのワークフローでimageのタグをgithubのshaにすることで、常に最新のimageをデプロイするようにしています。
また、イメージをビルドしてshaをタグに付与しています。
そして、Terraformのワークフローでterraform applyを実行する際に、image_versionにshaを渡しています。
以上の内容をセットアップすることで、ECSのタスク定義をTerraformで直接管理しながら、Github ActionでCDを実現することができます、
運用上の注意点
この方法では、ECS以外のリソースを更新するためにTerraformをapplyする際にも、image_versionを引数として渡す必要が出てきます。AWS CLIやコンソールからの目視で現在のimage_versionを確認する必要があるため、ヒューマンエラーが起きる可能性が高いです。
そのため、Terraformを構築する際にリソース単位でtfstaeを分けるなどの対策が必要になります。