1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actionsを使ってECS Fargate(AWS)へ自動デプロイした

Posted at

はじめに

GitHub Actions(以下GHA)を使ってECS Fargate(AWS)へ自動デプロイしたので手順を記します。

対象

こちらのDjangoアプリをデプロイします。

今回はmainブランチへのマージをトリガーにECRへのコンテナイメージのプッシュとECSのサービスの更新が確認できれば良いため、リソースは最小構成にします。

Architecture3.jpg

手順

1. GHAからAWSへの連携

連携する際のクレデンシャルとしてSecrets又はOpenID Connectがありますが、セキュリティ面からOpenID Connectを選択します。

概要 メリット デメリット
Secrets GitHub上に保存したワードでAWSにアクセスする ・設定が容易 ・長期保存のため漏洩した際の影響が大きい
・ローテーション作業が必要
OpenID Connect GitHubからトークンを取得し、AWSとの間でトークンと一時クレデンシャルを交換し、一時クレデンシャルでAWSにアクセスする ・一時保存のため漏洩した際の影響が小さい
・ローテーション作業が不要
・設定が複雑(ただし初回のみ)

OpenID Connect Providerの作成

トークンと一時クレデンシャルを交換するために必要な設定です。

ターミナル
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list 1234567890123456789012345678901234567890

引用:example-github-cicd

IAMポリシーの作成・ロールへのアタッチ

どこから(GitHubレポジトリ)どこに(AWS)アクセスできるかを制御します。

ポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Principal": {
        "Federated": "arn:aws:iam::${AWS_ID}:oidc-provider/${PROVIDER_URL}"
      },
      "Condition": {
        "StringLike": {
          "${PROVIDER_URL}:sub": "repo:${GITHUB_REPOSITORY}:*"
        }
      }
    }
  ]
}

引用:example-github-cicd

ワークフローの作成

下記のdeploy.ymlに認証アクションを組み込み(Get tmp credentials)、一時クレデンシャルを取得します。その際に必要なパラメータ(AWSアカウントIDなど)についてはSecrets(GitHub上で設定)に登録して使用します。こちらはクレデンシャルではないため、万が一漏れても影響は限定的です。

2. GHAからAWSへコンテナデプロイ

IAMポリシーの作成・ロールへのアタッチ

ECR, ECSへアクセスするため、ECRへのイメージのプッシュやECSのサービス更新権限等を加味したIAMポリシーを作成し、ロールへアタッチします。

ポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ecr:CompleteLayerUpload",
                "ecr:GetDownloadUrlForLayer",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart",
                "ecs:DescribeTaskDefinition",
                "ecs:RegisterTaskDefinition",
                "ecs:UpdateService",
                "ecs:DescribeServices"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "iam:PassedToService": "ecs-tasks.amazonaws.com"
                }
            }
        }
    ]
}

引用:example-github-cicd

ワークフローの作成

.github/workflows/deploy.yml
name: Deploy
on:
  pull_request:
    branches:
      - main
    types: [closed]
env: # AWSの認証アクションへ指定する入力パラメータを環境変数として定義
  ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ID }}:role/${{ secrets.ROLE_NAME }}
  SESSION_NAME: gh-oidc-${{ github.run_id }}-${{ github.run_attempt }}
jobs:
  deploy:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write # OIDCトークンの取得を許可
    env:
      ECS_CLUSTER_NAME: django-tw-cluster
      ECS_SERVICE_NAME: django-tw-service
    steps:
      - uses: actions/checkout@v4

      - name: Get tmp credentials # AWSの認証アクション
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ env.ROLE_ARN }}
          role-session-name: ${{ env.SESSION_NAME }}
          aws-region: ap-northeast-1
      
      - name: Login to ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: web push to ECR
        id: build-web
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: django-tw/web
        run: |
          docker build -t $ECR_REPOSITORY -f Dockerfile .
          docker tag $ECR_REPOSITORY:latest $ECR_REGISTRY/$ECR_REPOSITORY:latest
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

      - name: db push to ECR
        id: build-db
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: django-tw/db
        run: |
          docker build -t $ECR_REPOSITORY -f Dockerfile.db .
          docker tag $ECR_REPOSITORY:latest $ECR_REGISTRY/$ECR_REPOSITORY:latest
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

      - name: nginx push to ECR
        id: build-nginx
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: django-tw/nginx
        run: |
          docker build -t $ECR_REPOSITORY -f Dockerfile.nginx .
          docker tag $ECR_REPOSITORY:latest $ECR_REGISTRY/$ECR_REPOSITORY:latest
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
      
      - name: Deploy to ECS
        run: |
          aws ecs update-service --service $ECS_SERVICE_NAME --cluster $ECS_CLUSTER_NAME --force-new-deployment

解説

  • mainブランチにマージされたらECS Fargateへデプロイされるようにするため、イベント(on)とif文を工夫します

  • AWSの一時クレデンシャルを取得してからデプロイする必要があるため、Get tmp credentialsで認証アクションを用いて一時クレデンシャルを取得します(コメント部分)
  • メインの処理はビルドとデプロイです。ビルドはLogin to ECR + xxx push to ECR(xxxはコンテナ名)、デプロイはDeploy to ECSで記述しています。これらのビルドとデプロイは各々アクションに切り出してコンポーネント化できそうですが、今回はわかりやすくシェルコマンドで実装しました

おわりに

無事デプロイできました。次はTerraformでインフラを定義します。

image.png

参考文献

全体的に以下を参考とさせていただきました。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?