GitHub Actions を使ってS3 に自動デプロイ

5月 6, 2023AWS,CI/CD,CloudFront,GitHubActions,S3

概要

  • 今回は、GitHub Actions を使ってS3 にコンテンツを自動デプロイする方法を紹介します。GitHub Actions は、GitHub が提供するCI/CD です。GitHub リポジトリにGitHub Actions のワークフローを作成し、コードからビルド、テスト、デプロイ等のジョブを自動化することができます。
  • GitHub Actions ワークフローは、リポジトリにイベントが発生した時 (新しいコードのpull request や issue の作成等) にトリガーされる様に構成できます。詳しい GitHub Actions の説明は、こちらのGitHub Docs を参照ください。
  • GitHub Actions ワークフローは、リポジトリ内の .github/workflows ディレクトリ配下に、YAMLを用いてジョブを定義します。ジョブは並列で実行されます。また、他のジョブとの依存関係を定義することもできます。詳しいワークフローの構文は、こちらのGitHub Docs を参照ください。
  • GitHub Actions の課金は、プライベートリポジトリをGitHub ホステッド ランナーで使用する場合に、無料枠 + 従量課金となります(パブリックリポジトリは無料)。 実行環境にはGitHub ホステッド ランナーとセルフホステッド ランナーがあり、通常はGitHub ホステッド ランナーとなります。セルフホスト ランナーと GitHub ホステッド ランナーの違いは、こちらのGitHub Docs を参照ください。

 

今回のシステム構成概要

  • 今回は、CloudFront + S3 を利用した静的ウェブコンテンツを配信するシステム構成を想定します。ユーザーがGitHub のリポジトリにコンテンツをpush すると、GitHub Actions のワークフローがスタートし、S3 sync が行われ、S3 にコンテンツがデプロイされます。

 

 

GitHub Actions を使った S3デプロイ環境の構築

IAM ロールの作成

  • 今回はIAM ユーザー (アクセスキー) を作成する代わりに、IAM のID プロバイダ (IdP)を使用します。この結果、外部ユーザー ID (GitHub Actions) にAWS アカウント内の AWS リソースに対するアクセス許可を与えることができます。
  • IAM ロールを作成する前に、事前にID プロバイダを追加します。IAM の「ID プロバイダ」を選択し、ID プロバイダの画面から「プロバイダを追加」を選択します。

 

  • 次にプロバイダの設定において、以下の設定を行います。
    • プロバイダのタイプに「OpenID Connect」を選択する。
    • プロバイダのURL に「https://token.actions.githubusercontent.com」を入力する。「サムプリントを取得」を選択する。
    • 対象者に「sts.amazonaws.com」を入力する。
    • 「プロバイダを追加」を選択する。

 

  • ID プロバイダが追加されました。続けて、コンソール上部の「ロールの割り当て」を選択します。

 

  • 以下の画面が表示されました。「新しいロールを作成」を選択し、「次へ」を選択します。

 

  • ロールの作成において、エンティティに「ウェブ ID」を選択、ID プロバイダーに先ほど作成した「token.actions.githubusercontent.com」を選択します。

  • IAM ロールにアタッチするポリシーを作成します。今回は、S3 sync を行うため以下の権限をポリシーに定義します。

  • 以下JSON の S3バケット名を修正して貼り付けてください。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::[bucket-name]",
                "arn:aws:s3:::[bucket-name]/*"
            ]
        }
    ]
}

 

  • 最後にロール名を指定し、ロールの作成を行います。

 

IAMロールの作成 (追記)

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"Federated": "arn:aws:iam::012345678910:oidc-provider/token.actions.githubusercontent.com"
			},
			"Action": "sts:AssumeRoleWithWebIdentity",
			"Condition": {
				"StringEquals": {
					"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
				},
				"StringLike": {
					"token.actions.githubusercontent.com:sub": "repo:GitHubOrg/GitHubRepo:*"
				}
			}
		}
	]
}

 

 

GitHub Actions のワークフロー作成

  • 次に、GitHub Actions のワークフローを作成しましょう。事前に、GitHub のログインおよびリポジトリを作成しておきます。(今回はプライベートリポジトリを使用します)
  • 対象リポジトリの「Actions」を選択、続けて「set up a workflow yourself」を選択します。

 

  • Edit の画面に次に記載するYAML を貼り付け、環境変数をカスタマイズします。
  • ファイル名に任意の名前を付け、commit します。

 

  • 上記に使用したYAML は以下となります。AWS_ROLENAME に先ほど作成したIAM ロールの名前を指定します。AWS_S3_BUCKET にデプロイ先のバケット、DEST_DIR にデプロイ先のフォルダを指定します。
  • GitHub Actions ワークフローには、OIDC の権限設定が必要です。configure-aws-credentials を呼ぶ前に、permissions の定義が必要になります。
  • run に指定されているaws コマンドを使用し、S3 sync を行います。--exclude '.*git*'のオプションは、.git や .github のフォルダをデプロイから除外するために指定しています。
name: Upload files to S3

on:
  push:
    branches:
    - main

env:
  MY_AWS_REGION: 'ap-northeast-1'
  AWS_ROLENAME: 'role_githubactions_s3sync'
  AWS_S3_BUCKET: 'bucket-name'
  SOURCE_DIR: '.'
  DEST_DIR: 'dir'

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
    - name: Checkout
      uses: actions/checkout@master
    - name: Configure AWS credentials with IAM Role
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::${{secrets.AWS_ACCOUNT_ID}}:role/${{env.AWS_ROLENAME}}
        aws-region: ${{env.MY_AWS_REGION}}
    - name: Copy files to S3
      run: |
          aws s3 sync ${{env.SOURCE_DIR}} s3://${{env.AWS_S3_BUCKET}}/${{env.DEST_DIR}}/ --exclude '.*git*'

 

  • 今回、環境変数 MY_AWS_REGION, AWS_ROLENAME, AWS_S3_BUCKET, SOURCE_DIR, DEST_DIR はコード内のenv で定義していますが、環境変数 AWS_ACCOUNT_ID はリポジトリ内の シークレット (暗号化された環境変数)で定義しました。
  • 以下画面からシークレットとしてAWS_ACCOUNT_ID を定義し、AWSアカウント番号を設定します。

 

GitHubにpushしてS3 への自動デプロイを試す

  • それでは、最後にGitHubにpush を行って、GitHub Actions を使った S3デプロイが動作するかを試しましょう。
  • 以下は、S3 デプロイ前のコンソール画面です。

 

  • GitHubにcommit および push を行いました。Actions の画面からワークフローがスタートしたことを確認します。
  • 以下の様に、デプロイのジョブが実行されていることが分かります。

 

  • ワークフローによるデプロイのジョブは成功しました。

 

 

  • 各ステップを展開すると、詳しいログが確認できます。

 

  • 無事に、GitHub Actions を使った S3デプロイ環境の構築が完了しました!
  • 以下は、S3 デプロイ後のコンソール画面です。コンテンツの最終更新日時が変更されていることが分かります。

 

 

デプロイ失敗のCase Example

  • 私がデプロイに失敗した際のCase を共有いたします。

 

Case1: Region is not valid

  • 環境変数にAWS_REGION を使用した場合、configure-aws-credentials のジョブ実行時に、以下のエラーが発生。デプロイに失敗しました。
    • Region is not valid: ${env.AWS_REGION}

  • 明確な原因は分かりませんが、configure-aws-credentials 内の変数のハンドリングが適切ではないと思われます。
  • 対処方法は、環境変数 AWS_REGIONMY_AWS_REGION に名前を変更します。エラーは解消されました。

 

Case2: Credentials could not be loaded

  • configure-aws-credentials のジョブ実行時に、以下のエラーが発生。デプロイに失敗しました。
    • Error: Credentials could not be loaded, please check your action inputs: Could not load credentials from any providers

  • OIDC には、以下のpermissions 設定を追加する必要がありました。エラーは解消されました。
permissions:
id-token: write
contents: read

 

Case3: Request ARN is invalid

  • configure-aws-credentials のジョブ実行時に、以下のエラーが発生。デプロイに失敗しました。
    • Request ARN is invalid

  • 変数を参照するには、式構文 (${{ }}) を使用する必要がありました。詳細は、こちらのドキュメントを参照。
    • 誤:${env.AWS_ROLENAME}
    • 正:${{env.AWS_ROLENAME}}