0
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 で S3 + CloudFront へ自動デプロイ

0
Last updated at Posted at 2026-09-19

GitHub Actions で S3 + CloudFront へ自動デプロイ

はじめに

個人のポートフォリオサイトを、GitHub Actions で AWS の S3 + CloudFront に自動デプロイしています。サイトはビルド不要の静的サイト(HTML / CSS / JavaScript)で、main ブランチに push すると数十秒で本番に反映されます。

この仕組みは 2026 年 4 月に作りましたが、最初の形のまま使い続けてきたわけではありません。運用している間に、デプロイ先を一本化し、公開するファイルの選び方を変え、非推奨になった Node.js 20 への対応も行いました。

この記事では、まず現在の構成を説明し、そのあとで3回の変更について「何に困って、どう直したか」を順に紹介します。

対象読者は次のような方です。

  • 静的サイトを GitHub Actions で S3 + CloudFront に自動デプロイしたい方
  • すでにデプロイ用の workflow を持っていて、見直すきっかけを探している方

現在の構成

workflow の全体像

workflow は .github/workflows/deploy.yml の1ファイルで、prepare と deploy-aws の2つのジョブで構成しています。

.github/workflows/deploy.yml
name: Deploy Portfolio

on:
  push:
    branches:
      - main
  workflow_dispatch:

concurrency:
  group: deploy-production
  cancel-in-progress: true

jobs:
  prepare:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Create deployment bundle
        shell: bash
        run: |
          mkdir -p _site
          rsync -av ./*.html _site/
          rsync -av ./static/ _site/static/

      - name: Upload deployment bundle
        uses: actions/upload-artifact@v7
        with:
          name: site-bundle
          path: _site

  deploy-aws:
    needs: prepare
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    env:
      AWS_REGION: ${{ secrets.AWS_REGION }}
      S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
      CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
    steps:
      - name: Download deployment bundle
        uses: actions/download-artifact@v8
        with:
          name: site-bundle
          path: _site

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v6
        with:
          aws-region: ${{ env.AWS_REGION }}
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}

      - name: Sync files to S3
        run: aws s3 sync ./_site "s3://${S3_BUCKET_NAME}" --delete

      - name: Invalidate CloudFront cache
        run: aws cloudfront create-invalidation --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" --paths "/*"

prepare ジョブは、公開するファイルだけを _site/ にまとめ、artifact としてアップロードします。deploy-aws ジョブはその artifact を受け取り、aws s3 sync --delete で S3 と同期したあと、CloudFront のキャッシュを /* で無効化します。--delete を付けているので、リポジトリから消したファイルは S3 からも消えます。

main 以外のブランチで動作を試したいときは、workflow_dispatch で手動実行できます。また、concurrency で同じグループのデプロイが重なったときは古い実行をキャンセルするので、短い間隔で push しても最後の内容が反映されます。

リージョン、バケット名、Distribution ID、IAM Role の ARN は、すべてリポジトリの Secrets から参照しています。

OIDC で AWS に入る

AWS の認証には、アクセスキーではなく GitHub の OIDC を使っています。deploy-aws ジョブに id-token: write の権限を与え、aws-actions/configure-aws-credentials で IAM Role を引き受けます。GitHub の Secrets に長期間有効なアクセスキーを保存しなくて済むのが、この方式の利点です。

IAM Role の信頼ポリシーでは、token.actions.githubusercontent.com:sub の条件を次の値にしています。

repo:Termnix-IT/portfolio-site:ref:refs/heads/main

ワイルドカードは使わず、このリポジトリの main ブランチから実行された workflow だけが Role を引き受けられるように絞っています。

Role に必要な権限は、デプロイで使う次のものだけです。

  • s3:ListBucket
  • s3:GetObject
  • s3:PutObject
  • s3:DeleteObject
  • cloudfront:CreateInvalidation

変遷1: GitHub Pages と AWS の二重デプロイをやめた

最初の workflow には、deploy-aws とは別に deploy-pages というジョブがあり、同じファイル群を GitHub Pages と AWS の両方にデプロイしていました。prepare で作った artifact を2つのジョブで使い回す構成で、ジョブを分けているのはこの名残です。

2026 年 5 月に、GitHub Pages 側のジョブを削除して AWS に一本化しました。理由は2つあります。

1つ目は、2か所へのデプロイを両方とも管理し続けるコストです。デプロイ先が2つあると、設定の管理もデプロイ先の数だけ必要になります。

2つ目は、サイトの機能が AWS でしか成り立たなくなったことです。問い合わせフォームは、AWS Lambda の Function URL にフォームの内容を JSON で POST し、SES でメールを送る仕組みにしています。また、ドメインや SES、Lambda などの周辺リソースは Terraform で管理しています。GitHub Pages 側では同じサイトを再現できないため、二重に持っておく意味がなくなりました。

変遷2: 除外リスト方式から列挙方式へ

最初の workflow では、リポジトリ全体を _site/ にコピーし、公開しないファイルを --exclude で1つずつ除いていました。

変更前(抜粋)
          rsync -av \
            --exclude ".git/" \
            --exclude ".github/" \
            --exclude ".gitignore" \
            --exclude "README.md" \
            --exclude "docs/" \
            --exclude "_site/" \
            --exclude "CLAUDE.md" \
            ./ _site/

この方式では、公開しないファイルがリポジトリに増えるたびに、除外リストへの追記が必要になります。実際に、運用ドキュメントの MAINTENANCE.md を追加したときと、ドキュメントを docs/ にまとめたときに、それぞれ workflow を修正しました。

ファイル名の変更にも追従が必要です。README のファイル名を README.MD から README.md に変えたとき、rsync 側の除外指定は同じコミットで直しました。一方で、aws s3 sync 側に書いていた --exclude "README.MD" は古い表記のまま残っていました。rsync の段階で README はすでに除外されていたため実害はありませんでしたが、除外指定が2か所に分かれていると、片方だけ直し忘れることがあります。

除外リスト方式には、追記を忘れると公開すべきでないファイルが S3 に出てしまうという問題もあります。そこで 2026 年 5 月に、公開するもの(ルートの *.html と static/)だけを列挙する方式に切り替えました。

変更後
          mkdir -p _site
          rsync -av ./*.html _site/
          rsync -av ./static/ _site/static/

この変更で、リポジトリに README やドキュメントを追加しても、workflow を触る必要がなくなりました。aws s3 sync 側の --exclude も不要になったので、あわせて削除しました。

列挙方式では、逆に「公開したいファイルを列挙し忘れる」ことが起こり得ます。このサイトは公開するファイルがルートの HTML と static/ 配下に限られているので、列挙する対象が少なく、この方式が合っていました。

変遷3: Node.js 20 非推奨への対応

2026 年 9 月に、workflow で使っている action がすべて Node.js 20 ベースであり、runner によって Node.js 24 で強制的に実行されていることに気づきました。宣言しているランタイムと実際に動いているランタイムが食い違っている状態だったので、各 action を現行のメジャーバージョンに上げました。

action 変更前 変更後
actions/checkout v4 v7
actions/upload-artifact v4 v7
actions/download-artifact v4 v8
aws-actions/configure-aws-credentials v4 v6

メジャーバージョンを上げる前に、それぞれの破壊的変更がこの workflow に影響しないかを確認しました。

  • actions/download-artifact の v5 では、ダウンロード先のパスの扱いが変わりました。ただし、この変更は artifact を ID で指定してダウンロードする場合のもので、名前(site-bundle)で指定しているこの workflow には影響しません。
  • aws-actions/configure-aws-credentials の v5 では、入力値の扱いが変わりました。ただし、この変更は真偽値の入力に関するもので、この workflow は真偽値の入力を渡していないため影響しません。

ubuntu-latest の runner は、Node.js 24 版の action が必要とするバージョンをすでに満たしていたので、runner 側の変更は必要ありませんでした。バージョンを上げたあとのデプロイも、これまでどおり成功しています。

まとめ

自動デプロイは一度作ると動き続けるので、手を入れるきっかけを見落としがちです。このサイトでは、次の3つのタイミングで workflow を見直しました。

  • サイトの機能が特定の環境に依存するようになったとき、デプロイ先を一本化して管理コストを減らした
  • 除外リストへの追記や直し忘れが起きたとき、公開するものだけを列挙する方式に切り替えた
  • action のランタイムが非推奨になったとき、破壊的変更の影響を確認してからメジャーバージョンを上げた

同じように静的サイトを自動デプロイしている方は、公開対象の選び方と、使っている action のバージョンから見直してみてください。

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