GitHub×AWS実践ハンズオン:ActionsでEC2自動デプロイから始める連携活用術

Cloud & Infra
スポンサーリンク

GitHubとAWSを組み合わせることで、強力な自動化基盤を構築できます。この記事では、GitHub ActionsとAWSサービスを連携させた実践的なハンズオンを紹介します。初心者でも確実に実装できるよう、GitHub ActionsでのEC2自動デプロイから、S3静的サイト公開、Lambda関数デプロイまで、段階的に解説します。

スポンサーリンク

GitHub×AWSで実現できること

自動化の全体像

コード変更(git push)
  ↓
GitHub Actions自動実行
  ↓
ビルド・テスト
  ↓
AWSへ自動デプロイ
  ↓
本番環境更新

メリット:

  • 手動デプロイの排除
  • ヒューマンエラー削減
  • リリース時間短縮
  • 一貫性のある環境構築

できることの例

サービス用途難易度
EC2Webアプリ自動デプロイ⭐⭐
S3静的サイト公開
Lambdaサーバーレス関数デプロイ⭐⭐
ECSコンテナアプリデプロイ⭐⭐⭐
CodeDeployブルーグリーンデプロイ⭐⭐⭐

事前準備

必要なもの

GitHub側:

  • GitHubアカウント
  • リポジトリ(Public/Private)

AWS側:

  • AWSアカウント
  • IAMユーザー(適切な権限)
  • 対象サービス(EC2/S3など)

IAMユーザー作成

手順:

1. AWSコンソール → IAM
2. Users → Add users
3. ユーザー名: github-actions-user
4. Access key - Programmatic access ✅
5. Attach policies directly:
   - AmazonEC2FullAccess(EC2デプロイ用)
   - AmazonS3FullAccess(S3デプロイ用)
6. Create user
7. Access Key ID と Secret Access Key を保存

注意: 本番環境では最小権限の原則に従い、必要な権限のみ付与してください。

GitHub Secretsに認証情報を保存

1. GitHubリポジトリ → Settings
2. Secrets and variables → Actions
3. New repository secret

登録する情報:
- AWS_ACCESS_KEY_ID: (IAMのAccess Key ID)
- AWS_SECRET_ACCESS_KEY: (IAMのSecret Access Key)
- AWS_REGION: ap-northeast-1(東京リージョン)

ハンズオン1:EC2にNode.jsアプリを自動デプロイ

システム構成

GitHub Repository
  ↓ git push
GitHub Actions
  ↓ SSH接続
EC2インスタンス
  ↓ アプリ更新
Webアプリ公開

EC2インスタンスの準備

1. インスタンス起動:

AMI: Amazon Linux 2023
インスタンスタイプ: t2.micro(無料枠)
セキュリティグループ:
  - SSH (22) ← 自分のIP
  - HTTP (80) ← 0.0.0.0/0
  - Custom (3000) ← 0.0.0.0/0(開発用)

2. SSH接続して環境構築:

ssh -i your-key.pem ec2-user@your-ec2-ip

# Node.jsインストール
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install 20
nvm use 20

# アプリディレクトリ作成
mkdir ~/myapp
cd ~/myapp

# pm2インストール(プロセス管理)
npm install -g pm2

3. SSH鍵をGitHub Secretsに登録:

秘密鍵(your-key.pem)の内容をコピー

GitHub Secrets に登録:
- EC2_SSH_KEY: (秘密鍵の内容)
- EC2_HOST: (EC2のパブリックIP)
- EC2_USER: ec2-user

サンプルアプリ作成

package.json:

{
  "name": "github-aws-demo",
  "version": "1.0.0",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.0"
  }
}

server.js:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.send(`
    

GitHub Actions × AWS EC2

自動デプロイ成功!

デプロイ時刻: ${new Date().toLocaleString('ja-JP')}

`); }); app.get('/health', (req, res) => { res.json({ status: 'OK' }); }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });

GitHub Actions設定

.github/workflows/deploy-ec2.yml:

name: Deploy to EC2

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Deploy to EC2
        env:
          SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
          HOST: ${{ secrets.EC2_HOST }}
          USER: ${{ secrets.EC2_USER }}
        run: |
          # SSH秘密鍵を設定
          echo "$SSH_KEY" > private_key.pem
          chmod 600 private_key.pem
          
          # EC2にファイル転送
          scp -i private_key.pem -o StrictHostKeyChecking=no -r \
            server.js package.json ${USER}@${HOST}:~/myapp/
          
          # EC2でアプリ更新
          ssh -i private_key.pem -o StrictHostKeyChecking=no ${USER}@${HOST} << 'EOF'
            cd ~/myapp
            npm install
            pm2 restart myapp || pm2 start server.js --name myapp
            pm2 save
          EOF
          
          # 秘密鍵削除
          rm -f private_key.pem
      
      - name: Verify deployment
        run: |
          sleep 5
          curl -f http://${{ secrets.EC2_HOST }}:3000/health || exit 1
          echo "Deployment successful!"

デプロイ実行

1. リポジトリにプッシュ:

git add .
git commit -m "Add EC2 auto deploy"
git push origin main

2. GitHub Actionsで確認:

Actions タブ
→ Deploy to EC2 ワークフロー
→ ログ確認
→ ✅ 成功

3. ブラウザでアクセス:

http://your-ec2-ip:3000
→ 「自動デプロイ成功!」表示

完了! コードを変更してpushするたびに自動デプロイされます。

GitHub Actionsの実践例は「【試行錯誤の記録】GitHub ActionsでWordPress自動投稿!403エラーとの戦い」でも紹介しています。

ハンズオン2:S3で静的サイトを自動公開

システム構成

GitHub Repository(HTML/CSS/JS)
  ↓ git push
GitHub Actions
  ↓ AWS CLI
S3バケット
  ↓ 静的Webホスティング
インターネット公開

S3バケット作成

1. バケット作成:

1. S3コンソール → Create bucket
2. Bucket name: my-github-demo-site(グローバルで一意)
3. Region: ap-northeast-1
4. Block Public Access: すべてOFF(公開用)
5. Create bucket

2. 静的Webホスティング有効化:

バケット → Properties
→ Static website hosting → Enable
→ Index document: index.html
→ Error document: error.html
→ Save changes

3. バケットポリシー設定:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-github-demo-site/*"
    }
  ]
}

サンプルサイト作成

index.html:




  
  
  GitHub Actions × S3
  


  

GitHub Actions × S3

静的サイトの自動デプロイ成功!

style.css:

body {
  font-family: Arial, sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  margin: 0;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.container {
  background: white;
  padding: 2rem;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.3);
  text-align: center;
}

h1 {
  color: #333;
  margin-bottom: 1rem;
}

script.js:

document.getElementById('timestamp').textContent = 
  `最終デプロイ: ${new Date().toLocaleString('ja-JP')}`;

GitHub Actions設定

.github/workflows/deploy-s3.yml:

name: Deploy to S3

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
      
      - name: Sync to S3
        run: |
          aws s3 sync . s3://my-github-demo-site \
            --exclude ".git/*" \
            --exclude ".github/*" \
            --delete
      
      - name: Invalidate CloudFront (optional)
        run: |
          echo "CloudFront未使用の場合はスキップ"
          # aws cloudfront create-invalidation \
          #   --distribution-id YOUR_DISTRIBUTION_ID \
          #   --paths "/*"

デプロイ実行

1. プッシュ:

git add .
git commit -m "Add S3 static site deploy"
git push origin main

2. S3エンドポイントにアクセス:

http://my-github-demo-site.s3-website-ap-northeast-1.amazonaws.com

完了! HTMLを変更するたびに自動で公開されます。

ハンズオン3:Lambda関数の自動デプロイ

システム構成

GitHub Repository(Lambda関数)
  ↓ git push
GitHub Actions
  ↓ zip & AWS CLI
Lambda関数更新
  ↓ API Gateway
REST API公開

Lambda関数作成

1. AWSコンソールでLambda作成:

Lambda → Create function
Function name: github-demo-function
Runtime: Node.js 20.x
Create function

2. 環境変数設定(オプション):

Configuration → Environment variables
Key: STAGE
Value: production

サンプル関数

index.js:

exports.handler = async (event) => {
  console.log('Event:', JSON.stringify(event));
  
  const name = event.queryStringParameters?.name || 'World';
  const timestamp = new Date().toISOString();
  
  return {
    statusCode: 200,
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*'
    },
    body: JSON.stringify({
      message: `Hello, ${name}!`,
      timestamp: timestamp,
      environment: process.env.STAGE || 'development',
      deployedBy: 'GitHub Actions'
    })
  };
};

package.json:

{
  "name": "github-lambda-demo",
  "version": "1.0.0",
  "description": "GitHub Actions Lambda deployment demo"
}

GitHub Actions設定

.github/workflows/deploy-lambda.yml:

name: Deploy to Lambda

on:
  push:
    branches: [main]
    paths:
      - 'lambda/**'

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      - name: Install dependencies
        working-directory: ./lambda
        run: npm install --production
      
      - name: Create deployment package
        working-directory: ./lambda
        run: |
          zip -r function.zip .
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
      
      - name: Deploy to Lambda
        working-directory: ./lambda
        run: |
          aws lambda update-function-code \
            --function-name github-demo-function \
            --zip-file fileb://function.zip
      
      - name: Wait for update
        run: |
          aws lambda wait function-updated \
            --function-name github-demo-function
      
      - name: Test Lambda function
        run: |
          aws lambda invoke \
            --function-name github-demo-function \
            --payload '{"queryStringParameters":{"name":"GitHub"}}' \
            response.json
          cat response.json

API Gateway設定(オプション)

REST APIとして公開:

1. API Gateway → Create API
2. REST API → Build
3. Create resource: /hello
4. Create method: GET
5. Integration type: Lambda Function
6. Lambda Function: github-demo-function
7. Deploy API → Stage: prod

エンドポイント:

https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/hello?name=World

応用パターン

パターン1:環境別デプロイ

複数環境の管理:

on:
  push:
    branches:
      - main      # 本番環境
      - develop   # 開発環境

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Set environment
        run: |
          if [ "${{ github.ref }}" == "refs/heads/main" ]; then
            echo "ENVIRONMENT=production" >> $GITHUB_ENV
            echo "LAMBDA_FUNCTION=prod-function" >> $GITHUB_ENV
          else
            echo "ENVIRONMENT=development" >> $GITHUB_ENV
            echo "LAMBDA_FUNCTION=dev-function" >> $GITHUB_ENV
          fi
      
      - name: Deploy
        run: |
          aws lambda update-function-code \
            --function-name ${{ env.LAMBDA_FUNCTION }} \
            --zip-file fileb://function.zip

パターン2:ブルーグリーンデプロイ

無停止デプロイ:

- name: Create new version
  run: |
    VERSION=$(aws lambda publish-version \
      --function-name my-function \
      --query 'Version' --output text)
    echo "VERSION=$VERSION" >> $GITHUB_ENV

- name: Update alias
  run: |
    aws lambda update-alias \
      --function-name my-function \
      --name production \
      --function-version ${{ env.VERSION }}

パターン3:ロールバック機能

デプロイ失敗時の自動ロールバック:

- name: Deploy and test
  id: deploy
  run: |
    # デプロイ
    aws lambda update-function-code ...
    
    # ヘルスチェック
    if ! curl -f https://api.example.com/health; then
      exit 1
    fi

- name: Rollback on failure
  if: failure() && steps.deploy.outcome == 'failure'
  run: |
    aws lambda update-function-code \
      --function-name my-function \
      --s3-bucket backup-bucket \
      --s3-key previous-version.zip

トラブルシューティング

問題1:EC2デプロイでSSH接続失敗

エラー:

Permission denied (publickey)

原因:

  • SSH鍵の改行コードが正しくない
  • 鍵の権限が適切でない

解決:

# 改行コードを明示的に処理
run: |
  echo "$SSH_KEY" | tr -d '\r' > private_key.pem
  chmod 600 private_key.pem

問題2:S3デプロイで403エラー

原因:

  • IAMユーザーに権限がない
  • バケットポリシーが未設定

解決:

1. IAMユーザーに AmazonS3FullAccess 付与
2. バケットポリシーで PublicReadGetObject 設定
3. ブロックパブリックアクセスを確認

問題3:Lambda関数が更新されない

原因:

  • 関数名が間違っている
  • zipファイルサイズ超過(50MB制限)

解決:

# 関数名確認
aws lambda list-functions

# zipサイズ確認
ls -lh function.zip

# 大きい場合はS3経由でデプロイ
aws s3 cp function.zip s3://my-bucket/
aws lambda update-function-code \
  --function-name my-function \
  --s3-bucket my-bucket \
  --s3-key function.zip

セキュリティベストプラクティス

1. IAM権限の最小化

NG(フルアクセス):

{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

OK(必要な権限のみ):

{
  "Effect": "Allow",
  "Action": [
    "s3:PutObject",
    "s3:DeleteObject",
    "lambda:UpdateFunctionCode"
  ],
  "Resource": [
    "arn:aws:s3:::my-bucket/*",
    "arn:aws:lambda:ap-northeast-1:*:function:my-function"
  ]
}

2. Secrets管理

GitHub Secrets以外の選択肢:

  • AWS Secrets Manager:より高度な管理
  • AWS Systems Manager Parameter Store:無料枠あり

GitHub ActionsでSecrets Manager使用:

- name: Get secrets
  run: |
    SECRET=$(aws secretsmanager get-secret-value \
      --secret-id my-app-secret \
      --query SecretString --output text)
    echo "::add-mask::$SECRET"
    echo "SECRET=$SECRET" >> $GITHUB_ENV

3. 監査ログ

CloudTrailで操作履歴記録:

CloudTrail → Create trail
→ S3バケットに操作ログ保存
→ GitHub Actionsの操作も記録

まとめ

GitHub ActionsとAWSの連携で、強力な自動化基盤を構築できます。

実装したハンズオン:

  1. EC2自動デプロイ:Node.jsアプリをSSH経由でデプロイ
  2. S3静的サイト:HTML/CSS/JSを自動公開
  3. Lambda関数:サーバーレス関数の自動更新

メリット:

  • 手動作業の削減
  • デプロイミスの防止
  • 一貫性のある環境
  • リリース時間の短縮

コスト:

  • GitHub Actions:パブリックリポジトリは無料
  • AWS無料枠:1年間または月次制限内
  • 実質コスト:ほぼ無料で始められる

まずは簡単なS3デプロイから始めて、徐々に複雑な構成に挑戦してみましょう!

関連記事

  • 【試行錯誤の記録】GitHub ActionsでWordPress自動投稿!403エラーとの戦い
【試行錯誤の記録】GitHub ActionsでWordPress自動投稿!403エラーとの戦い
はじめにVSCodeでMarkdownを書いて、git push するだけでWordPressに自動投稿される――そんな夢のようなシステムを作ろうと、GitHub Actions連携に挑戦しました。しかし、待っていたのは 403 Forbi...
  • GitHub CLI完全ガイド:コマンドラインでGitHub操作を効率化
GitHub CLI完全ガイド:コマンドラインでGitHub操作を効率化
GitHub CLI(ghコマンド)は、GitHubの操作をコマンドラインで完結できる公式ツールです。ブラウザを開かずに、リポジトリ作成からIssue管理、Pull Request作成まで、すべてターミナルで行えます。この記事では、実際の導...
  • VSCodeでGitを使いこなす:初心者から実務まで完全ガイド
VSCodeでGitを使いこなす完全ガイド|GUI操作とCLIコマンドを徹底比較
はじめにVSCodeでアプリケーション開発をしている方の多くが、Gitによるバージョン管理を導入しています。しかし、「CLIコマンドならわかるけど、VSCodeのGUI操作がよくわからない」「どのメニューから操作すればいいのか迷う」といった...

コメント