GitHub Actions で AWS と連携する CI/CD 環境を構築する
はじめに
GitHub Actions は、GitHubのイベントに応じて様々なワークフローを自動で実行できるGitHubの機能です。
GitHub Actions を使うと、GitHub への push などのイベントをトリガーに自動テストやビルドを実行する CI (Continuous Delivery) に加えて、デプロイ準備やデプロイまでを自動で実行する CD (Continuous Deployment) 環境を簡単に実現できます🐱
今回は GitHub Actions でバックエンドのソースを AWS EC2 へ、フロントエンドのソースを AWS S3 へデプロイする CI/CD 環境を構築しましたので、その備忘録です。
フォルダ構成としては、下記の通りです。
/
├── backend/ # バックエンド関連のコードや設定
├── frontend/ # フロントエンド関連のコードや設定
├── .github/ # GitHub関連の設定
│ └── workflows/ # GitHub Actionsのワークフロー定義
│ └── <ワークフローファイル名>.yaml # ワークフローファイル
└── README.md # プロジェクトの説明書
GitHub Actionsの基本構造
GitHub Actionsは、ワークフロー、トリガー、ジョブ、ステップ、アクションから構成され、YAML 構文を使用してCI/CDプロセスのワークフローを定義します。
各ワークフローは、コード リポジトリ内の .github/workflows
という名前のディレクトリに個別の YAML ファイルとして格納します。
まずは基本的なワークフローの例を見てみましょう👀
name: CI/CD Workflow Sample # ワークフローの名前
on: # トリガーの定義
push: # プッシュイベント
branches:
- main # mainブランチへのプッシュ
jobs: # ジョブの定義
build: # ジョブ名
runs-on: ubuntu-latest # 実行環境
steps: # ステップの定義
- name: Checkout Code # ステップ名
uses: actions/checkout@v4 # リポジトリのチェックアウト
- run: echo "Hello World!" # コマンドを実行
1. ワークフロー (Workflow)
- ワークフローは、GitHub Actionsの一連のプロセスを定義します。
- YAML形式で記述され、特定のイベント(例: プッシュやプルリクエスト)に応じて自動的に実行されます。
2. トリガー (Triggers)
- ワークフローが実行される条件を指定します。
- 主なトリガーには
push
、pull_request
、schedule
、workflow_dispatch
、workflow_run
などがあります。
3. ジョブ (Jobs)
- ジョブは、ワークフロー内で実行される一連のステップです。
- 各ジョブには、実行環境(
runs-on
)を指定します。 - ジョブごとに異なる実行環境(マシン)が起動するイメージです。
4. ステップ (Steps)
- ステップは、ジョブ内で実行される個々の操作です。
- ステップは、外部アクション(
uses
)を呼び出したり、シェルコマンド(run
)を実行したりします。
5. アクション (Actions)
- アクションは再利用可能なコードの単位で、特定の機能を提供します。
-
uses: actions/~
で定義しています。 - GitHub Marketplaceからアクションを取得したり、自分で作成したりできます。
参考:Enhance your workflow with extensions
EC2 へ自動デプロイする ワークフロー
GitHub Actionsから EC2 へ、バックエンドのソースを自動デプロイするワークフロー定義の例です。
- .github/workflows/backend.ymlファイルの中身
# ワークフローの名前
name: backend
# ワークフローのトリガーの定義
on:
push: # プッシュイベントをトリガーとする
branches:
- main # mainブランチにプッシュされたとき
paths:
- 'backend/**' # backendディレクトリ内の変更
- 'github/**' # githubディレクトリ内の変更
# すべてのジョブで共通して使用される設定を定義
defaults:
run:
working-directory: backend # 作業ディレクトリをbackendに設定
# ワークフローで実行するジョブの定義
jobs:
####### Build(CI) #######
build:
runs-on: ubuntu-latest # ジョブを実行するOS(ランナー)
steps:
- name: Checkout Code
uses: actions/checkout@v4 # リポジトリのチェックアウト
- name: Setup Node.js
uses: actions/setup-node@v4 # Node.jsのセットアップ
with:
node-version: 18.x # 使用するNode.jsのバージョン
- name: Install Dependencies
run: npm ci # 依存関係をインストール
- name: Run Tests
run: npm run test -- --coverage # テストを実行し、カバレッジを取得
- name: Build Application
run: npm run build # プロダクションビルドを実行
- name: Start Application
run: node dist/index.js & # ビルドしたアプリケーションをバックグラウンドで起動
- name: Wait for Application
run: npx wait-on -t 10000 http://localhost:8080/api/health # アプリケーションが起動するまで待機
- name: Run API Tests
run: npm run test:api # APIテストを実行
# アーティファクトの保存
- name: Upload Coverage Artifact
uses: actions/upload-artifact@v4
if: always() # 常にアーティファクトをアップロード
with:
name: coverage # アーティファクトの名前
path: backend/coverage/** # 保存するカバレッジデータのパス
retention-days: 5 # 保存期間(5日)
- name: Upload Build Artifact
uses: actions/upload-artifact@v4
with:
name: dist # アーティファクトの名前
path: backend/dist/index.js # 保存するビルド成果物のパス
retention-days: 5 # 保存期間(5日)
####### Deploy(CD) #######
deploy:
needs: build # buildジョブが完了していることが前提
if: github.event_name == 'push' # pushイベントの場合のみ実行
runs-on: ubuntu-latest # ジョブを実行するOS(Ubuntuの最新バージョン)
steps:
- name: Download Build Artifact
uses: actions/download-artifact@v4 # buildジョブからアーティファクトをダウンロード
with:
name: dist # ダウンロードするアーティファクトの名前
path: backend/dist # ダウンロード先のパス
- name: Install SSH Key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.BACKEND_SERVER_SSH_KEY }} # 秘密鍵を使用
known_hosts: ${{ secrets.BACKEND_SERVER_KNOWN_HOSTS }} # known_hostsの設定
- name: Upload Application
run: rsync -v dist/index.js ${SERVER_USER}@${SERVER_IP}:~/index.js # rsyncでファイルをアップロード
env:
SERVER_USER: ${{ secrets.BACKEND_SERVER_USER }} # サーバーユーザー名
SERVER_IP: ${{ secrets.BACKEND_SERVER_IP }} # サーバーIP
- name: Restart Backend Service
run: ssh ${SERVER_USER}@${SERVER_IP} sudo systemctl restart backend # サーバーでサービスを再起動
env:
SERVER_USER: ${{ secrets.BACKEND_SERVER_USER }}
SERVER_IP: ${{ secrets.BACKEND_SERVER_IP }}
- name: Check Backend Service Status
run: ssh ${SERVER_USER}@${SERVER_IP} sudo systemctl status backend # サーバーでサービスの状態を確認
env:
SERVER_USER: ${{ secrets.BACKEND_SERVER_USER }}
SERVER_IP: ${{ secrets.BACKEND_SERVER_IP }}
補足
-
test:api
:package.jsonのscripts内に任意のコマンド名を定義できます。ここに記載されたscriptは、npm run [スクリプト名]
で実行できます。"scripts": { "dev": "nodemon", "test": "jest", "test:api": "stepci run stepci/workflow.yml", ・・・ },
-
--coverage:
:テストフレームワーク(例えば、JestやMochaなど)に対するオプションで、テストカバレッジのレポートを生成することを指示します。テストカバレッジは、実行されたテストによってコードのどの部分がカバーされているかを示す指標で、品質保証やバグの早期発見に役立ちます。 - GitHub Actionsのアーティファクトとは、ワークフローの実行中に生成されるファイルやデータのことを指します。これには、ビルド成果物、テスト結果、ログファイル、設定ファイルなどが含まれます。後で参照したり他のワークフローで使用したりするために使用します。
- シークレット情報:GitHub > [Settings] > [Secrets and variables] > [Actions]に環境変数として保存しています。
- Jobの間で実行されている環境は共有されない(別の綺麗な環境で実行される)為、buildジョブで生成されたartifactをdeployジョブでダウンロードして使用するようにしています。
AWSとGitHub ActionsをOpenID Connectで連携する
GitHub Actionsから S3 へ、フロントエンドのソースを自動デプロイする際の認証方法としては OpenID Connect を使用します。
GitHub Actionsのマニュアル:アマゾン ウェブ サービスでの OpenID Connect の構成 を参考にしながら設定を進めます。
手順としては、以下の通りです。
- AWS への ID プロバイダーの追加
- 自動デプロイのためのIAMロール設定
- GitHub Actions ワークフローを更新する
「2. 自動デプロイのためのIAMロール設定」では、GitHub Actions での自動デプロイ用にIAMロールを作成します。[信用されたエンティティタイプ]に、IAM Role のカスタム信頼ポリシーを下記のように設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "<作成したIDプロバイダのARN>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:<GitHubの組織またはアカウント名>/<GitHubのリポジトリ名>:ref:refs/heads/main"
}
}
}
]
}
次に、S3にソースをアップロードするためのIAMポリシーを付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<バケット名>"
},
{
"Sid": "AllowPutObject",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<バケット名>/*"
}
]
}
S3 へ自動デプロイする ワークフロー
GitHub Actionsから S3 へ自動デプロイするワークフロー定義の例です。
- .github/workflows/frontend.ymlファイルの中身
name: frontend # ワークフローの名前
on: # ワークフローのトリガーを定義
push: # プッシュイベントで実行
branches:
- main # mainブランチへのプッシュ時にトリガー
paths:
- 'frontend/**' # frontendディレクトリ内の変更
- 'github/**' # githubディレクトリ内の変更
defaults:
run: # ジョブ実行時のデフォルト設定
working-directory: frontend # 作業ディレクトリをfrontendに設定
permissions: # ワークフローに必要な権限を指定
id-token: write # JWTリクエストに必要な権限
contents: read # actions/checkoutに必要な権限
jobs: # ワークフロー内のジョブを定義
deploy: # デプロイジョブ
runs-on: ubuntu-latest # ジョブを実行するOS(Ubuntuの最新バージョン)
steps: # ジョブ内で実行されるステップのリスト
- name: Checkout Code
uses: actions/checkout@v4 # リポジトリの内容をチェックアウトするアクション
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v3 # AWSの認証情報を設定するアクション
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-oidc-Role # 使用するIAMロールのARN
aws-region: ap-northeast-1 # AWSリージョンを指定
- name: Sync to S3
run: aws s3 sync . s3://<バケット名> --exact-timestamps --region ap-northeast-1 # S3にファイルを同期アップロード
さいごに
無事 GitHub Actions でフロントエンド/バックエンドのソースを各サービスへデプロイする CI/CD 環境を構築できました💮
以上、えみり〜でした|ωΦ)ฅ
Discussion