Menggunakan AWS Spot Instance sebagai CI/CD Runner
Solusi Efisien untuk Pipeline Berat (Terraform, Sonar, Java, dll)
Dalam banyak organisasi, CI/CD pipeline perlahan berubah dari sekadar build & test menjadi mini data center sementara:
- Terraform → plan/apply besar, provider banyak
- SonarQube → static analysis berat, butuh CPU & RAM tinggi
- Java / Gradle / Maven → build lambat, memory hungry
- Docker build → layer besar, I/O intensif
Masalah klasik yang sering muncul:
Runner self-hosted mahal
- Harus always-on
- Over-provisioned demi peak load
Shared runner bottleneck
- Job saling antre
- Pipeline makin lama → developer frustrasi
Inefficient cost
- Resource besar tapi dipakai sebentar
- Idle > 70% waktu
Dari sinilah ide menggunakan AWS Spot Instance sebagai ephemeral CI/CD runner jadi sangat masuk akal.
Kenapa Spot Instance Cocok untuk CI/CD?
Karakteristik CI/CD Job
CI/CD job umumnya:
- Stateless
- Ephemeral
- Bisa di-retry
- Tidak butuh uptime panjang
Ini sangat cocok dengan karakteristik Spot Instance:
- Harga hingga 70–90% lebih murah
- Bisa terminate kapan saja
- Tidak cocok untuk long-running critical workload
CI/CD runner yang:
dibuat → dipakai → dihancurkan adalah spot workload by design.
Konsep Arsitektur
Garis besar arsitekturnya:
GitHub Action Trigger
|
v
Request Runner
|
v
AWS EC2 Spot Instance
(Self-hosted Runner)
|
v
Run CI Job
|
v
Terminate Instance
Prinsip utamanya:
- Runner tidak selalu hidup
- Runner dibuat hanya saat job akan jalan
- Runner mati setelah job selesai
Manfaat Utama
Cost Reduction yang Signifikan
Contoh kasar:
| Instance Type | On-Demand | Spot |
|---|---|---|
| c6i.4xlarge | ~$0.68/hr | ~$0.18/hr |
CI job 15 menit:
- On-Demand: ~$0.17
- Spot: ~$0.045
Kalikan ratusan pipeline per hari → penghematan brutal.
Scalability Tanpa Drama
- Parallel job? → parallel instance
- Tidak perlu mikir capacity planning runner
- Tidak ada antrean panjang
Clean Environment per Job
Setiap job:
- VM fresh
- Tidak ada “sisa state”
- Lebih reproducible
- Lebih aman (credential tidak bocor antar job)
Risiko & Trade-off (Harus Jujur)
Spot Interruption
AWS bisa terminate instance kapan saja (2 menit notice).
Mitigasi:
Job idempotent
Retry strategy di GitHub Actions
Split pipeline (build → test → deploy)
Jangan pakai untuk:
- Production deploy critical tanpa retry
- Long migration job
Startup Time
- Cold start EC2 ±1–2 menit
Solusi:
- Custom AMI (runner + tools sudah terinstall)
- Atau snapshot golden image
Kompleksitas Setup
- Lebih kompleks dibanding shared runner
- Tapi worth it untuk workload berat
Contoh Implementasi: GitHub Actions + AWS Spot Runner
Opsi Arsitektur (Recommended)
Gunakan ephemeral self-hosted runner:
1 workflow untuk:
- Request EC2 Spot
- Register runner ke GitHub
Runner otomatis self-terminate setelah job
IAM Role untuk EC2
EC2 butuh permission:
- Register GitHub runner
- Terminate dirinya sendiri
Contoh policy minimal:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:TerminateInstances"
],
"Resource": "*"
}
]
}
User Data Script (Runner Bootstrap)
Contoh user-data.sh:
#!/bin/bash
set -e
# Install dependencies
yum update -y
yum install -y docker git jq
systemctl start docker
# Install GitHub Runner
RUNNER_VERSION="2.316.0"
mkdir /actions-runner
cd /actions-runner
curl -o actions-runner.tar.gz -L \
https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
tar xzf actions-runner.tar.gz
# Register runner
./config.sh \
--url https://github.com/ORG/REPO \
--token ${RUNNER_TOKEN} \
--name spot-runner-$(hostname) \
--labels spot,large \
--unattended \
--ephemeral
# Run runner
./run.sh
# After job finish, terminate instance
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
aws ec2 terminate-instances --instance-ids $INSTANCE_ID --region ap-southeast-1
--ephemeralpenting supaya runner auto-unregister.
GitHub Action – Request Spot Instance
Workflow contoh:
name: Heavy CI with Spot Runner
on:
workflow_dispatch:
push:
branches: [ main ]
jobs:
request-runner:
runs-on: ubuntu-latest
outputs:
runner-label: ${{ steps.set-label.outputs.label }}
steps:
- id: set-label
run: echo "label=spot" >> $GITHUB_OUTPUT
- name: Launch Spot Instance
run: |
aws ec2 run-instances \
--instance-type c6i.4xlarge \
--image-id ami-xxxx \
--instance-market-options MarketType=spot \
--iam-instance-profile Name=ci-runner-role \
--user-data file://user-data.sh \
--region ap-southeast-1
build:
needs: request-runner
runs-on: [self-hosted, spot]
steps:
- uses: actions/checkout@v4
- name: Terraform Plan
run: terraform plan
- name: Sonar Scan
run: ./gradlew sonar
Best Practice Tambahan
Gunakan Label Runner
Pisahkan:
spot-smallspot-largespot-java
Supaya workflow jelas resource-nya.
Custom AMI
Preinstall:
- Java
- Terraform
- Docker
- Sonar scanner
Startup time jauh lebih cepat.
Retry Strategy
strategy:
fail-fast: false
max-parallel: 3
Dan di level job:
timeout-minutes: 60
Kapan Pendekatan Ini Sangat Layak?
✅ Sangat direkomendasikan jika:
- CI berat
- Cost mulai tidak masuk akal
- Banyak parallel job
- Tim mature secara DevOps
❌ Kurang cocok jika:
- CI kecil & jarang
- Tim belum siap operasional EC2
- Tidak toleran terhadap retry
Kesimpulan
Menggunakan AWS Spot Instance sebagai CI/CD runner bukan sekadar optimasi cost — ini perubahan mindset:
CI/CD bukan server permanen, tapi compute on demand.
Dengan pendekatan ini:
- Pipeline lebih cepat
- Cost jauh lebih murah
- Infrastructure lebih bersih
- Developer experience naik
Dan yang paling penting: resource besar dipakai hanya saat dibutuhkan, bukan 24/7.