CI/CD(1/2)
パイプラインと AWS 想定。
CI/CD: AWS想定 (仮)
全体像
flowchart LR
Dev[Developer] -->|push| GH[GitHub]
GH -->|trigger| GHA[GitHub Actions]
GHA -->|OIDC AssumeRole| IAM[AWS IAM Role]
GHA -->|build push| ECR[Amazon ECR]
GHA -->|update service| ECS[ECS Fargate]
ECS -->|read| SM[Secrets Manager]
ECS -->|read write| S3[Amazon S3]
ECS -->|stdout| CWL[CloudWatch Logs]
ECS -->|connect| RDS[Amazon RDS]
ECS -->|connect| EC[ElastiCache Redis]
サービス選定 (仮想定)
| 役割 | サービス | 備考 |
|---|---|---|
| ソース管理 | GitHub | AWS CodeCommitは実務でほぼ使わない |
| CI (テスト/lint) | GitHub Actions | 無料枠あり、エコシステム豊富 |
| CD (ビルド/デプロイ) | GitHub Actions + AWS CLI | CodePipeline/CodeBuildは選定しない |
| イメージレジストリ | Amazon ECR | プライベートで安全 |
| 実行環境 | Amazon ECS (Fargate) | サーバレス、運用負荷低 |
| シークレット管理 | AWS Secrets Manager / SSM Parameter Store | 機密度で使い分け |
| ログ集約 | CloudWatch Logs | Datadog連携も可 |
| 監視・アラート | CloudWatch Alarms (+ Datadog 任意) | |
| ストレージ | Amazon S3 (本番) / MinIO (ローカル) | (11.4) |
| RDB | Amazon RDS (PostgreSQL/MySQL) / Aurora | |
| キャッシュ | ElastiCache (Redis) | |
| ロードバランサ | ALB | HTTPSはACMで証明書管理 |
| CDN | CloudFront | 静的アセット高速化 |
| Infra as Code | Terraform / AWS CDK | Terraformが事実上の標準 |
.github/workflows/ci.yml (テスト/Lint、PR・push時)
name: CI
on:
pull_request:
branches: [main, develop]
push:
branches: [main, develop]
permissions:
contents: read
id-token: write # OIDC用
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build dev image
run: docker compose build app
- name: Lint
run: docker compose run --rm app ruff check .
- name: Type check
run: docker compose run --rm app mypy src
- name: Test
run: docker compose run --rm app pytest -v --cov=src --cov-report=xml
- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage.xml
CI もローカルと同じ Docker環境で実行する。「ローカルで通ったらCIでも通る」を担保する基本戦術。
.github/workflows/deploy.yml (main マージ時)
name: Deploy to ECS
on:
push:
branches: [main]
permissions:
contents: read
id-token: write
env:
AWS_REGION: ap-northeast-1
ECR_REPOSITORY: app
ECS_CLUSTER: app-cluster
ECS_SERVICE: app-service
CONTAINER_NAME: app
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
aws-region: ${{ env.AWS_REGION }}
- name: Login to ECR
id: ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push image
run: |
IMAGE="${{ steps.ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}"
docker build -f docker/app/Dockerfile -t $IMAGE .
docker push $IMAGE
echo "IMAGE=$IMAGE" >> $GITHUB_ENV
- name: Render new task definition
id: taskdef
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: .aws/task-definition.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ env.IMAGE }}
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.taskdef.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
OIDC認証 (重要)
長期アクセスキーをGitHub Secretsに保存しない。代わりに OIDC でその場限りの認証情報を発行する。
セットアップ手順:
- AWS IAM で OIDC Identity Provider を追加 (
token.actions.githubusercontent.com) - GitHubリポジトリ・ブランチ限定の IAM Role を作成
- Role の信頼ポリシーで Repository / Branch を絞る (他リポジトリから乗っ取られないため)
- GitHub Actions 側で
aws-actions/configure-aws-credentials@v4でRole Assume
信頼ポリシー例 (mainブランチのみ許可):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012: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:my-org/my-repo:ref:refs/heads/main"
}
}
}]
}
ECS Task Definition の要点 (.aws/task-definition.json 抜粋)
{
"family": "app",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/AppTaskRole",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"containerDefinitions": [
{
"name": "app",
"image": "PLACEHOLDER",
"essential": true,
"portMappings": [{"containerPort": 8000, "protocol": "tcp"}],
"environment": [
{"name": "AWS_REGION", "value": "ap-northeast-1"},
{"name": "S3_BUCKET_UPLOADS", "value": "app-prod-uploads"}
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:app/db-url"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/app",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "app",
"awslogs-create-group": "true"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8000/healthz || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 10
}
}
]
}