ECS Fargate — task definition & service
Fargate menghapus urusan server, tapi menggantinya dengan satu berkas JSON yang setiap fieldnya punya konsekuensi. Ini yang penting dari sekitar lima puluh field yang tersedia.
Intisari
- Kombinasi CPU/memori Fargate terbatas pada daftar yang sah — tidak bebas.
initProcessEnabledatautini: salah satunya wajib, atauSIGTERMdiabaikan.stopTimeoutharus lebih besar dari total waktu graceful shutdown-mu.secretsuntuk rahasia,environmentuntuk yang bukan — nilainya terlihat berbeda.- Dua IAM role yang berbeda:
executionRoleArnuntuk menarik image,taskRoleArnuntuk kode aplikasimu.
Task definition
{
"family": "portal-web",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "1024",
"memory": "2048",
"runtimePlatform": {
"cpuArchitecture": "ARM64",
"operatingSystemFamily": "LINUX"
},
"executionRoleArn": "arn:aws:iam::123456789012:role/portal-ecs-execution",
"taskRoleArn": "arn:aws:iam::123456789012:role/portal-ecs-task",
"containerDefinitions": [
{
"name": "web",
"image": "123456789012.dkr.ecr.ap-southeast-3.amazonaws.com/portal:a1b2c3d",
"essential": true,
"initProcessEnabled": true,
"portMappings": [
{ "containerPort": 4321, "protocol": "tcp", "name": "http" }
],
"environment": [
{ "name": "NODE_ENV", "value": "production" },
{ "name": "HOST", "value": "0.0.0.0" },
{ "name": "PORT", "value": "4321" },
{ "name": "DB_HOST_RW", "value": "portal.abc.ap-southeast-3.rds.amazonaws.com" },
{ "name": "DB_HOST_RO", "value": "portal-ro.abc.ap-southeast-3.rds.amazonaws.com" },
{ "name": "DB_NAME", "value": "portal" },
{ "name": "DB_USER", "value": "portal_app" },
{ "name": "MIDTRANS_PRODUKSI", "value": "true" },
{ "name": "PUBLIC_SITE_URL", "value": "https://portal.contoh.id" }
],
"secrets": [
{ "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:ap-southeast-3:123456789012:secret:portal/db-AbC123:sandi::" },
{ "name": "MIDTRANS_SERVER_KEY", "valueFrom": "arn:aws:secretsmanager:ap-southeast-3:123456789012:secret:portal/midtrans-XyZ:server_key::" },
{ "name": "ASTRO_KEY", "valueFrom": "arn:aws:secretsmanager:ap-southeast-3:123456789012:secret:portal/astro-Def:key::" },
{ "name": "CF_API_TOKEN", "valueFrom": "arn:aws:secretsmanager:ap-southeast-3:123456789012:secret:portal/cf-Ghi:token::" },
{ "name": "ORIGIN_TOKEN", "valueFrom": "arn:aws:secretsmanager:ap-southeast-3:123456789012:secret:portal/origin-Jkl:token::" }
],
"healthCheck": {
"command": ["CMD-SHELL", "wget -qO- http://127.0.0.1:4321/sehat || exit 1"],
"interval": 15,
"timeout": 5,
"retries": 3,
"startPeriod": 30
},
"stopTimeout": 45,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/portal-web",
"awslogs-region": "ap-southeast-3",
"awslogs-stream-prefix": "web",
"mode": "non-blocking",
"max-buffer-size": "4m"
}
},
"ulimits": [{ "name": "nofile", "softLimit": 65536, "hardLimit": 65536 }]
}
]
}
Enam pengaturan yang menentukan
1. stopTimeout
Graceful shutdown (materi sebelumnya):
5 detik menunggu ALB menyadari health check gagal
+ 20 detik menunggu request yang berjalan
+ 2 detik menutup pool database
= 27 detik
stopTimeout: 45 ← harus lebih besar. Maksimum 120 di Fargate.
Kalau stopTimeout lebih kecil dari waktu shutdown-mu, ECS mengirim
SIGKILL di tengah jalan — dan seluruh penanganan yang kamu tulis jadi sia-sia. Default
ECS adalah 30 detik, yang sering tidak cukup. Hitung waktu shutdown-mu yang sebenarnya, lalu beri
margin.
2. initProcessEnabled
Menjalankan proses init sebagai PID 1 yang meneruskan sinyal dengan benar. Alternatif dari
tini di Dockerfile — pakai salah satu, tidak perlu keduanya. Kalau tidak ada satu pun,
SIGTERM tidak sampai ke Node.
3. mode: "non-blocking" pada log
Ini pengaturan yang menyelamatkanmu saat CloudWatch melambat. Dengan mode blocking (default), penulisan log yang lambat akan memblokir aplikasimu — proses Node berhenti menunggu log terkirim. Di trafikmu, gangguan CloudWatch berubah jadi gangguan portal. Mode non-blocking membuang log kalau buffer penuh, yang jauh lebih baik daripada membuang request pembaca.
4. startPeriod pada health check
Tiga puluh detik memberi container waktu memulai sebelum kegagalan health check dihitung. Tanpa itu, task bisa dibunuh saat masih menginisialisasi pool database — dan ECS akan mengulanginya terus dalam lingkaran.
5. Kombinasi CPU/memori yang sah
| CPU | Memori yang sah | Untuk portalmu |
|---|---|---|
| 512 (0,5 vCPU) | 1–4 GB | Terlalu kecil |
| 1024 (1 vCPU) | 2–8 GB | Titik awal yang baik |
| 2048 (2 vCPU) | 4–16 GB | Kalau render berat |
| 4096 (4 vCPU) | 8–30 GB | Jarang perlu |
Node satu proses hanya memakai satu inti untuk JavaScript. Menaikkan vCPU melebihi 2 jarang membantu — lebih baik menambah task daripada memperbesar task. Perkecualiannya: kalau kamu memproses gambar dengan Sharp di container, yang memang memakai banyak inti.
6. Dua IAM role yang berbeda
executionRoleArn | taskRoleArn | |
|---|---|---|
| Dipakai oleh | Agen ECS | Kode aplikasimu |
| Untuk | Menarik image, membaca secret, menulis log | Memanggil API AWS dari dalam kode |
| Izin khas | ECR pull, Secrets Manager read, CloudWatch write | S3 untuk unggahan, SES untuk email |
Mencampur keduanya adalah kesalahan izin yang paling umum di ECS. Kalau aplikasimu tidak
memanggil API AWS sama sekali, taskRoleArn boleh punya nol izin — dan itu bagus. Jangan
memberi taskRoleArn izin membaca Secrets Manager "supaya lebih mudah": secret sudah
disuntikkan sebagai variabel lingkungan oleh execution role, dan memberi kodemu akses langsung berarti
satu kerentanan aplikasi bisa membaca seluruh secret di akunmu.
Definisi service
aws ecs create-service \
--cluster portal \
--service-name portal-web \
--task-definition portal-web \
--desired-count 4 \
--launch-type FARGATE \
--platform-version LATEST \
--network-configuration 'awsvpcConfiguration={
subnets=[subnet-priv-a,subnet-priv-b,subnet-priv-c],
securityGroups=[sg-portal-web],
assignPublicIp=DISABLED}' \
--load-balancers 'targetGroupArn=arn:...:targetgroup/portal-tg/abc,containerName=web,containerPort=4321' \
--health-check-grace-period-seconds 60 \
--deployment-configuration 'maximumPercent=200,minimumHealthyPercent=100,
deploymentCircuitBreaker={enable=true,rollback=true}' \
--enable-execute-command
| Pengaturan | Kenapa |
|---|---|
assignPublicIp=DISABLED | Task di subnet privat; tidak bisa dihubungi dari internet |
minimumHealthyPercent=100 | Kapasitas penuh selama deploy — tidak ada penurunan layanan |
maximumPercent=200 | Task baru dinaikkan dulu sebelum yang lama diturunkan |
deploymentCircuitBreaker | Rollback otomatis kalau deploy gagal |
healthCheckGracePeriod | Jangan bunuh task yang masih memanaskan diri |
enableExecuteCommand | Bisa exec ke container untuk menelusuri masalah |
deploymentCircuitBreaker dengan rollback: true adalah pagar paling
berharga di seluruh konfigurasi ini. Kalau task baru gagal health check berulang kali, ECS
membatalkan deploy dan mengembalikan revisi sebelumnya — otomatis, tanpa ada yang perlu bangun tengah
malam. Ia tidak menangkap semua kegagalan (bug logika tetap lolos), tapi ia menangkap seluruh kelas
"container tidak bisa start".
Menelusuri masalah
# Kenapa task berhenti?
aws ecs describe-tasks --cluster portal --tasks <id> \
--query 'tasks[0].{stop:stoppedReason,containers:containers[].{name:name,reason:reason,exit:exitCode}}'
# Masuk ke container yang sedang jalan
aws ecs execute-command --cluster portal --task <id> \
--container web --interactive --command "/bin/sh"
# Peristiwa service
aws ecs describe-services --cluster portal --services portal-web \
--query 'services[0].events[:10]'
| Gejala | Penyebab biasanya |
|---|---|
CannotPullContainerError | Execution role tidak punya izin ECR, atau tidak ada rute ke ECR dari subnet privat |
ResourceInitializationError untuk secret | Execution role tidak punya secretsmanager:GetSecretValue |
| Task start lalu mati, exit 0 | Proses selesai — biasanya CMD yang salah |
| Task mati, exit 137 | OOM — naikkan memori atau turunkan max-old-space-size |
| Health check selalu gagal | HOST bukan 0.0.0.0 |
| Task berputar terus | startPeriod terlalu pendek |
Latihan: buat task definition di atas dan jalankan service dengan dua task. Uji rollback
otomatis: deploy revisi dengan CMD yang sengaja salah, dan amati circuit breaker
mengembalikannya. Lalu uji graceful shutdown di lingkungan nyata: kirim trafik terus-menerus dengan
hey atau k6 sambil melakukan deploy, dan hitung berapa 5xx yang muncul —
targetnya nol.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.