CI/CD dengan GitHub Actions & OIDC
Access key jangka panjang di secrets repositori adalah kredensial yang tidak pernah kedaluwarsa dan bisa dipakai dari mana saja. OIDC menggantinya dengan token berumur satu jam yang hanya berlaku untuk repositori dan cabang tertentu.
Intisari
- GitHub menerbitkan token OIDC per jalannya workflow; AWS menukarnya jadi kredensial sementara.
- Nol access key tersimpan di GitHub โ tidak ada yang bisa bocor.
- Batasi
subdi kebijakan kepercayaan ke repositori dan cabang tertentu. - Urutan deploy: uji โ build & push image โ migrasi โ perbarui service โ tunggu stabil.
- Tag image dengan SHA commit supaya rollback berarti sesuatu.
Menyiapkan kepercayaan
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com
{
"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",
"token.actions.githubusercontent.com:sub": "repo:nurhadidev/toko:ref:refs/heads/main"
}
}
}]
}
Kondisi sub adalah keseluruhan keamanannya. Kalau ditulis
repo:nurhadidev/* โ atau lebih buruk, memakai StringLike dengan
* โ maka repositori mana pun di organisasimu, termasuk yang bisa diedit kontributor luar
lewat pull request, bisa mengambil peran deploy produksimu. Kunci ke repositori dan cabang
tertentu. Untuk deploy dari tag, gunakan repo:org/repo:ref:refs/tags/v* dengan
StringLike yang dibatasi.
Workflow
name: deploy
on:
push:
branches: [main]
permissions:
id-token: write # WAJIB untuk OIDC
contents: read
env:
AWS_REGION: ap-southeast-1
ECR_REPO: 123456789012.dkr.ecr.ap-southeast-1.amazonaws.com/toko/server
CLUSTER: toko
SERVICE: toko-server
jobs:
uji:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: "1.26", cache: true }
- run: test -z "$(gofmt -l .)"
- run: go vet ./...
- uses: golangci/golangci-lint-action@v6
- run: go test -race ./...
deploy:
needs: uji
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubDeploy
aws-region: ${{ env.AWS_REGION }}
- uses: aws-actions/amazon-ecr-login@v2
- name: Build & push
run: |
TAG=${GITHUB_SHA::7}
docker buildx build --platform linux/arm64 \
--build-arg VERSI=$TAG --build-arg COMMIT=$GITHUB_SHA \
--cache-from type=gha --cache-to type=gha,mode=max \
-t $ECR_REPO:$TAG --push .
echo "TAG=$TAG" >> $GITHUB_ENV
- name: Migrasi database
run: |
ARN=$(aws ecs run-task --cluster $CLUSTER \
--task-definition toko-migrate --launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[$SUBNETS],securityGroups=[$SG]}" \
--query 'tasks[0].taskArn' --output text)
aws ecs wait tasks-stopped --cluster $CLUSTER --tasks $ARN
KODE=$(aws ecs describe-tasks --cluster $CLUSTER --tasks $ARN \
--query 'tasks[0].containers[0].exitCode' --output text)
if [ "$KODE" != "0" ]; then
echo "migrasi gagal dengan exit code $KODE"
exit 1
fi
- name: Perbarui service
run: |
DEF=$(aws ecs describe-task-definition --task-definition toko-server \
--query taskDefinition)
BARU=$(echo $DEF | jq --arg IMG "$ECR_REPO:$TAG" \
'.containerDefinitions[0].image = $IMG
| del(.taskDefinitionArn, .revision, .status,
.requiresAttributes, .compatibilities,
.registeredAt, .registeredBy)')
REV=$(aws ecs register-task-definition --cli-input-json "$BARU" \
--query 'taskDefinition.taskDefinitionArn' --output text)
aws ecs update-service --cluster $CLUSTER --service $SERVICE \
--task-definition $REV --force-new-deployment
- name: Tunggu stabil
run: aws ecs wait services-stable --cluster $CLUSTER --services $SERVICE
Urutan langkahnya bukan selera. Migrasi berjalan sebelum service diperbarui, sehingga kode baru selalu bertemu skema yang sudah siap โ dan karena migrasinya kompatibel dua arah (Fase 4), kode lama yang masih berjalan selama rolling update juga tetap bekerja. Membalik urutannya berarti kode baru bisa bertemu skema lama.
Peran deploy yang sesempit mungkin
ecr:GetAuthorizationToken (*)
ecr:BatchCheckLayerAvailability repositori toko/* saja
ecr:PutImage, ecr:UploadLayerPart repositori toko/* saja
ecs:RegisterTaskDefinition (*) โ tidak bisa dibatasi resource
ecs:UpdateService, ecs:DescribeServices service tertentu saja
ecs:RunTask, ecs:DescribeTasks cluster tertentu saja
iam:PassRole HANYA ke ecsTaskExecutionRole dan tokoTaskRole
iam:PassRole yang tidak dibatasi adalah eskalasi hak istimewa. Dengan
PassRole: * plus RunTask, siapa pun yang bisa menjalankan workflow dapat
menjalankan container dengan peran IAM apa pun di akunmu โ termasuk peran administrator.
Selalu batasi ke daftar ARN peran yang eksplisit.
Rollback
# Kembali ke revisi task definition sebelumnya
aws ecs update-service --cluster toko --service toko-server \
--task-definition toko-server:41
# Revisi itu menunjuk tag image SHA yang berbeda โ inilah kenapa
# tag "latest" membuat rollback tidak berarti apa-apa.
| Perubahan | Bisa di-rollback? |
|---|---|
| Kode aplikasi | โ revisi task definition sebelumnya |
| Konfigurasi | โ revisi sebelumnya |
| Migrasi yang menambah kolom | โ kode lama mengabaikannya |
| Migrasi yang menghapus kolom | โ kode lama akan gagal |
| Data yang sudah ditransformasi | โ butuh pemulihan dari backup |
Inilah alasan aturan migrasi kompatibel dua arah dari Fase 4 sangat penting di sini. Rollback kode itu murah dan cepat; rollback skema tidak ada. Kalau deploy-mu menghapus kolom, kamu kehilangan kemampuan kembali ke versi sebelumnya โ tepat pada saat kamu paling membutuhkannya.
Deploy yang lebih aman
| Cara | Yang didapat |
|---|---|
| Rolling update (bawaan ECS) | Sederhana; sedikit ada dua versi bersamaan |
| Circuit breaker ECS | Rollback otomatis saat deploy gagal โ nyalakan |
| Blue/green (CodeDeploy) | Pengalihan trafik terkendali, uji dulu di listener tes |
| Canary | 10% trafik dulu, pantau, baru sisanya |
| Feature flag | Melepas kode โ menyalakan fitur (Fase 11) |
Latihan: siapkan peran OIDC yang dibatasi ke satu repositori dan cabang main,
lalu jalankan workflow deploy. Setelah berhasil, ubah kondisi sub ke cabang lain dan
jalankan lagi dari main โ baca pesan penolakan STS-nya. Terakhir, lakukan rollback dengan
menunjuk revisi task definition sebelumnya dan pastikan aplikasinya benar-benar kembali ke versi lama.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.