GithubActionsでECRにコンテナをデプロイする方法【Terraform】

最終的なコードはこちらです。

GithubActionsに永続的な認証を渡さない方法を使用しています。

環境

  • Terraform 1.1.9
    Docker Imageを使用しました。
  • provider hashicorp/aws 4.12.0
  • docker 20.10.10
Terraform環境をDockerで構築する方法Terraform環境をDockerで構築する方法

.gitignoreを作成

.gitignore

*.tfstate
# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version 
# control as they are data points which are potentially sensitive and subject 
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc

誤ってAWSのアクセスキー等をGithubに上げてしまわないよう.gitignoreを設定します。

Terraformのバージョン、プロバイダーの指定

provider.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.12.0"
    }
  }

  required_version = "1.1.9"
}

provider "aws" {
  region = "ap-northeast-1"
}

Terraformはバージョンによってstateファイル等の仕様が違うのでトラブルを未然に防ぐためにバージョンを指定しておきます。

ECRを作成

ecr.tf
resource "aws_ecr_repository" "sample" {
  name = "sample"
}

resource "aws_ecr_lifecycle_policy" "sample" {
  policy = jsonencode(
    {
      "rules" : [
        {
          "rulePriority" : 1,
          "description" : "Hold only 10 images, expire all others",
          "selection" : {
            "tagStatus" : "any",
            "countType" : "imageCountMoreThan",
            "countNumber" : 10,
          },
          "action" : {
            "type" : "expire"
          }
        }
      ]
    }
  )

  repository = aws_ecr_repository.sample.name
}
sh
terraform init
terraform apply
yes

Dockerイメージをプッシュするためのリポジトリを作成しています。

ライフサイクルポリシーは無くても動きますが今回は10枚のイメージだけを保持するように設定しています。

IAMロールを作成

iam.tf
resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"

  client_id_list = ["sts.amazonaws.com"]

  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}

resource "aws_iam_role" "github" {
  name = "github"

  assume_role_policy = jsonencode(
    {
      "Version" : "2012-10-17",
      "Statement" : [
        {
          "Effect" : "Allow",
          "Principal" : {
            "Federated" : "${aws_iam_openid_connect_provider.github.arn}"
          },
          "Action" : "sts:AssumeRoleWithWebIdentity",
          "Condition" : {
            "StringLike" : {
              "token.actions.githubusercontent.com:sub" : "repo:${var.github_user_name}/${var.github_repository_name}:*"
            }
          }
        }
      ]
    }
  )
}

resource "aws_iam_policy" "github" {
  name = "github"
  policy = jsonencode(
    {
      "Version" : "2012-10-17",
      "Statement" : [
        {
          "Effect" : "Allow",
          "Action" : [
            "ecr:GetAuthorizationToken",
          ],
          "Resource" : "*"
        },
        {
          "Effect" : "Allow",
          "Action" : [
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage",
            "ecr:BatchCheckLayerAvailability",
            "ecr:PutImage",
            "ecr:InitiateLayerUpload",
            "ecr:UploadLayerPart",
            "ecr:CompleteLayerUpload"
          ],
          "Resource" : "${aws_ecr_repository.sample.arn}"
        }
      ]
    }
  )
}

resource "aws_iam_role_policy_attachment" "role_deployer_policy_ecr_power_user" {
  role       = aws_iam_role.github.name
  policy_arn = aws_iam_policy.github.arn
}
variables.tf
variable "github_repository_name" {
    type = string
}

variable "github_user_name" {
    type = string
}
terraform.tfvars
github_repository_name = "<リポジトリ名>"
github_user_name = "<ユーザー名>"

自分のユーザー名、リポジトリ名を入力してください。

sh
terraform init
terraform apply
yes

AWSの認証情報をOpenIDプロバイダを使用して付与します。

これによって永続的な認証情報を持たせずGithubアクションからAWSサービスを呼び出すことができます。

IAMロールは特定のリポジトリのみが引き受けられるようにし、付与するポリシーも使用する最小限のものにしています。

許可するリポジトリを実行時に指定できるようにvariables.tfに変数を定義しています。

Dockerfileを作成

docker/Dockerfile
FROM hello-world

プッシュするイメージを作成します。

今回はサンプルなのでhello-worldイメージを読み込むだけにしています。

Github Secretsを設定

ECRリポジトリのURLとIAMロールのARNをメモします。

sh
terraform state show aws_ecr_repository.sample
terraform state show aws_iam_role.github

Githubにアクセスしてリポジトリの[Settings]を開きます。

先ほどメモしたURLとARNを[Secrets]に追加します。

こちらはGithubアクションで使用します。

Githubアクションを作成

.github/workflows/deploy.yml
name: deploy

on:
  push:
    branches:
      - main

env:
  IMAGE_TAG: ${{ github.sha }}

jobs:
  deploy:
    name: Push Image to AWS ECR
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          REPOSITORY_URL: ${{ secrets.ECR_REPOSITORY_URL }}
        run: |
          docker build -t $REPOSITORY_URL:$IMAGE_TAG  ./docker/
          docker push $REPOSITORY_URL:$IMAGE_TAG

mainブランチにプッシュするとGithubActionsが起動しECRにイメージがデプロイされます。

処理の大まかな流れはこんな感じです。

  • Checkout
    Githubアクションがリポジトリにアクセスできるようにする
  • Configure AWS credentials
    AWSのサービスを呼び出す権限を取得(IAMロールを引き受ける)
  • Login to Amazon ECR
    ECRにログイン
  • Build, tag, and push image to Amazon ECR
    イメージをビルドしてECRにプッシュ

参考文献

gitignore/Terraform.gitignore

Configuring OpenID Connect in Amazon Web Services

aws-actions/amazon-ecr-login

1 COMMENT

コメントを残す

メールアドレスが公開されることはありません。