ALB Mengembalikan 502 Bad Gateway: Panduan Diagnosa Lengkap Saat Instance Healthy

Situasi ini muncul lebih sering dari yang seharusnya: ALB melaporkan 502 Bad Gateway, tapi target group menunjukkan semua instance berstatus Healthy. Health check lolos, tapi request nyata gagal. Ini bukan kontradiksi — ini sinyal bahwa masalah bukan di ketersediaan instance, melainkan di level respons HTTP atau protokol koneksi antara ALB dan aplikasi.

TL;DR: Penyebab Umum 502 di ALB

PenyebabLapisanSinyal Utama
Respons HTTP tidak valid dari aplikasiAplikasiAccess log ALB: 502, target_status_code kosong atau -
Koneksi ditutup prematur oleh targetTransporttarget_status_code: -, error_reason: Target.ResponseHeaderTimeout
Mismatch protokol (HTTP vs HTTPS di target group)ProtokolSSL handshake error di log aplikasi
Keep-alive timeout tidak selarasKoneksi502 intermiten, tidak konsisten di semua request
Response header terlalu besarHTTP502 konsisten untuk endpoint tertentu
Target group protocol mismatchKonfigurasi502 sejak deployment atau perubahan konfigurasi

Cara ALB Memproses Request dan Menghasilkan 502

Sebelum mendiagnosa, penting memahami di mana 502 bisa lahir. ALB bertindak sebagai reverse proxy — ia menerima koneksi dari client, membuka koneksi baru ke target, lalu meneruskan respons. Jika target mengembalikan respons yang tidak bisa diparsing sebagai HTTP valid, atau koneksi ke target gagal setelah request dikirim, ALB menghasilkan 502 sendiri. Health check menggunakan path dan interval terpisah, jadi instance bisa lolos health check tapi tetap gagal melayani request produksi.

sequenceDiagram participant C as Client participant ALB as ALB participant TG as Target Group participant App as Aplikasi (Instance) C->>ALB: HTTPS Request ALB->>App: HTTP/HTTPS Request (koneksi baru) Note over ALB,App: Protokol sesuai konfigurasi target group alt Respons Valid App->>ALB: HTTP 200 OK ALB->>C: HTTP 200 OK else Respons Tidak Valid / Timeout / Koneksi Putus App-->>ALB: Connection Reset / Timeout / Malformed Response ALB->>C: HTTP 502 Bad Gateway end Note over TG,App: Health Check berjalan terpisah TG->>App: GET /health (interval terpisah) App->>TG: HTTP 200 OK Note over TG: Status: Healthy (tidak terpengaruh 502)
  1. Client → ALB: Koneksi HTTPS diterima, TLS di-terminate di ALB.
  2. ALB → Target: ALB membuka koneksi baru ke instance menggunakan protokol yang dikonfigurasi di target group (HTTP atau HTTPS).
  3. Target → ALB: Jika respons tidak valid, timeout, atau koneksi putus — ALB menghasilkan 502 ke client.
  4. Health Check: Berjalan independen dengan path dan interval sendiri. Tidak mewakili perilaku request produksi.

Langkah 1: Baca Access Log ALB — Ini Sumber Kebenaran Pertama

Sebelum menyentuh instance, aktifkan dan baca access log ALB. Log ini mencatat target_status_code dan error_reason per request — dua field yang langsung membedakan apakah masalah ada di sisi ALB atau target. Tanpa log ini, semua diagnosa berikutnya hanya tebakan.

Aktifkan access log jika belum aktif:

aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/1234567890abcdef \
  --attributes Key=access_logs.s3.enabled,Value=true \
               Key=access_logs.s3.bucket,Value=my-alb-logs-bucket \
               Key=access_logs.s3.prefix,Value=my-alb

Setelah log tersedia, query dengan Amazon Athena atau unduh dan grep secara lokal. Field kritis yang perlu diperhatikan:

# Format field penting di access log ALB:
# type timestamp elb client:port target:port request_processing_time 
# target_processing_time response_processing_time elb_status_code 
# target_status_code ... error_reason

# Cari semua baris dengan status 502
grep ' 502 ' access_log.log | awk '{print $14, $15, $NF}'
# Output: elb_status_code target_status_code error_reason

Interpretasi hasil:

  • target_status_code: - → ALB tidak menerima respons valid dari target. Masalah ada di koneksi atau aplikasi crash sebelum menulis respons.
  • target_status_code: 200 tapi elb_status_code: 502 → Jarang, tapi bisa terjadi jika respons corrupt setelah header dikirim.
  • error_reason: Target.ResponseHeaderTimeout → Target terlalu lama mengirim header respons.
  • error_reason: Target.ConnectionError → Koneksi ke target gagal atau ditutup paksa.

Langkah 2: Verifikasi Konfigurasi Protokol Target Group

Ini penyebab yang paling sering terlewat karena tidak ada error eksplisit di console. Jika target group dikonfigurasi dengan protokol HTTPS tapi aplikasi hanya mendengarkan HTTP (atau sebaliknya), ALB akan gagal melakukan handshake dan menghasilkan 502. Health check bisa tetap lolos jika health check dikonfigurasi dengan protokol yang berbeda dari traffic produksi.

aws elbv2 describe-target-groups \
  --target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/1234567890abcdef \
  --query 'TargetGroups[*].{Protocol:Protocol,Port:Port,HealthCheckProtocol:HealthCheckProtocol,HealthCheckPort:HealthCheckPort}'

Bandingkan output dengan port yang benar-benar didengarkan aplikasi di instance:

# Jalankan di instance target via SSM Session Manager
ss -tlnp | grep LISTEN

Jika protokol target group adalah HTTPS, ALB akan mencoba TLS handshake ke instance. Pastikan aplikasi memang mendengarkan dengan TLS di port tersebut, bukan plain HTTP.

Langkah 3: Periksa Keep-Alive Timeout — Penyebab 502 Intermiten yang Paling Sering Salah Didiagnosa

502 yang muncul tidak konsisten — kadang berhasil, kadang gagal, tanpa pola jelas — hampir selalu mengarah ke masalah keep-alive timeout. ALB mempertahankan koneksi persistent ke target untuk efisiensi. Jika aplikasi menutup koneksi keep-alive lebih cepat dari yang ALB harapkan, ALB bisa mencoba menggunakan koneksi yang sudah ditutup oleh aplikasi, menghasilkan 502.

Bayangkan ALB seperti kasir yang menyimpan antrian koneksi terbuka ke dapur. Jika dapur menutup jendela lebih cepat dari yang kasir tahu, pesanan berikutnya jatuh ke lantai.

ALB idle timeout default adalah 60 detik. Nilai ini bisa diubah, dan harus lebih kecil dari idle timeout keep-alive di sisi aplikasi. Periksa konfigurasi saat ini:

aws elbv2 describe-load-balancer-attributes \
  --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/1234567890abcdef \
  --query 'Attributes[?Key==`idle_timeout.timeout_seconds`]'

Aturan praktisnya: idle timeout keep-alive di aplikasi harus lebih besar dari idle timeout ALB. Jika menggunakan Nginx, periksa keepalive_timeout. Jika menggunakan Node.js HTTP server, periksa server.keepAliveTimeout. Jika menggunakan Gunicorn atau uWSGI, pastikan keep-alive diaktifkan dan timeout dikonfigurasi lebih tinggi dari ALB.

Untuk menyesuaikan idle timeout ALB (misalnya turunkan ke 30 detik jika aplikasi menutup koneksi di 45 detik):

aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/1234567890abcdef \
  --attributes Key=idle_timeout.timeout_seconds,Value=30

Langkah 4: Validasi Respons HTTP dari Aplikasi

ALB membutuhkan respons HTTP yang valid — status line yang benar, header yang parseable, dan tidak ada karakter ilegal. Aplikasi yang mengembalikan respons malformed (misalnya header dengan karakter non-ASCII, status line yang tidak standar, atau body yang dikirim sebelum header selesai) akan menyebabkan ALB menghasilkan 502 meski instance berstatus Healthy.

Uji respons langsung dari instance, bypass ALB sepenuhnya:

# Jalankan dari instance lain di VPC yang sama, atau via SSM
curl -v --max-time 10 http://<instance-private-ip>:<port>/<path> 2>&1 | head -50

Perhatikan output verbose curl. Jika ada anomali di response header — karakter aneh, header duplikat, atau koneksi yang ditutup sebelum body selesai — itu konfirmasi masalah ada di aplikasi, bukan di ALB atau jaringan.

Untuk endpoint yang menghasilkan 502 secara konsisten, bandingkan dengan endpoint yang berhasil. Jika hanya endpoint tertentu yang gagal, periksa apakah endpoint tersebut menghasilkan response header yang lebih besar atau memiliki logika yang berbeda.

Langkah 5: Periksa Security Group dan Konektivitas Jaringan

Meski instance berstatus Healthy di target group, ada skenario di mana koneksi ke port tertentu bisa diblokir. Health check menggunakan port dan path yang dikonfigurasi — jika health check port berbeda dari traffic port, instance bisa Healthy tapi traffic produksi tetap diblokir.

# Verifikasi security group yang attached ke instance target
aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query 'Reservations[*].Instances[*].SecurityGroups'
# Verifikasi inbound rules security group tersebut
aws ec2 describe-security-groups \
  --group-ids sg-1234567890abcdef0 \
  --query 'SecurityGroups[*].IpPermissions'

Security group instance harus mengizinkan inbound traffic dari security group ALB pada port aplikasi. Jika menggunakan referensi security group (bukan CIDR), pastikan referensinya ke security group ALB yang benar, bukan security group lain.

graph LR ALB["ALB Security Group"] -->|inbound dari SG ALB| IG["Instance Security Group"] IG --> App["Aplikasi Port 8080"] HC["Health Check Port: traffic-port"] -.->|probe independen| App style ALB fill:#FF9900,color:#fff style IG fill:#1A73E8,color:#fff style App fill:#34A853,color:#fff style HC fill:#EA4335,color:#fff

Langkah 6: Periksa Log Aplikasi di Instance Target

Setelah access log ALB mengkonfirmasi bahwa target_status_code adalah - (tidak ada respons), langkah selanjutnya adalah melihat apa yang terjadi di sisi aplikasi. Crash, OOM kill, atau exception yang tidak tertangani bisa menyebabkan koneksi ditutup tanpa mengirim respons HTTP apapun.

# Akses instance via SSM Session Manager (tidak perlu SSH/bastion)
aws ssm start-session \
  --target i-1234567890abcdef0 \
  --region us-east-1
# Di dalam session, periksa log aplikasi dan system
journalctl -u my-application-service --since '10 minutes ago' --no-pager

# Atau jika menggunakan file log
tail -n 200 /var/log/my-app/error.log

# Periksa OOM kill
dmesg | grep -i 'killed process'

# Periksa resource utilization saat ini
top -bn1 | head -20

Pola yang sering ditemukan: aplikasi crash karena memory exhaustion tepat saat menerima request besar, tapi recovery-nya cepat sehingga health check berikutnya sudah lolos. Instance kembali Healthy, tapi 502 sudah terjadi dan mungkin akan berulang.

Pengalaman Nyata: Salah Diagnosa yang Menghabiskan 3 Jam

Skenario yang pernah terjadi: 502 muncul hanya pada request POST dengan payload lebih dari 1MB, GET selalu berhasil. Instance Healthy. Access log menunjukkan target_status_code: - dan error_reason: Target.ConnectionError. Asumsi awal: network issue atau security group.

Setelah 2 jam memeriksa VPC flow log dan security group tanpa hasil, akhirnya curl langsung ke instance dengan payload besar mengungkap masalah sebenarnya: Nginx di instance dikonfigurasi dengan client_max_body_size 1m — request di atas 1MB langsung ditolak Nginx dengan menutup koneksi, bukan dengan mengembalikan 413. Nginx menutup koneksi TCP tanpa mengirim HTTP response, sehingga ALB menerima connection reset dan menghasilkan 502.

Perbaikannya satu baris di konfigurasi Nginx. Tapi pelajarannya: health check tidak pernah menguji payload besar. Instance Healthy hanya berarti path health check bisa diakses — tidak lebih dari itu.

Ringkasan Alur Diagnosa 502 ALB

graph TD Start(["502 Bad Gateway Instance Healthy"]) --> A["Baca Access Log ALB target_status_code & error_reason"] A --> B{"target_status_code?"} B -->|"- (kosong)"| C{"error_reason?"} B -->|"Ada kode (mis. 200)"| D["Respons corrupt setelah header — periksa aplikasi"] C -->|"Target.ResponseHeaderTimeout"| E["Aplikasi lambat merespons Periksa performa & timeout"] C -->|"Target.ConnectionError"| F["Koneksi ditutup prematur Periksa keep-alive & crash log"] C -->|"Tidak ada / lainnya"| G["Periksa protokol target group vs port yang didengarkan aplikasi"] F --> H["Cek log aplikasi di instance via SSM Session Manager"] G --> I["Verifikasi security group dan konektivitas jaringan"] H --> J(["Temukan root cause dan perbaiki"]) I --> J D --> J E --> J style Start fill:#EA4335,color:#fff style J fill:#34A853,color:#fff

IAM Policy untuk Akses Diagnosa

Untuk menjalankan semua perintah diagnosa di atas, engineer membutuhkan permission berikut. Terapkan prinsip least privilege — berikan hanya pada role atau user yang memang melakukan troubleshooting.

🔽 Klik untuk melihat IAM Policy diagnosa 502 ALB
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ALBDiagnostics",
      "Effect": "Allow",
      "Action": [
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DescribeLoadBalancerAttributes",
        "elasticloadbalancing:DescribeTargetGroups",
        "elasticloadbalancing:DescribeTargetHealth",
        "elasticloadbalancing:ModifyLoadBalancerAttributes"
      ],
      "Resource": "*"
    },
    {
      "Sid": "EC2Diagnostics",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances",
        "ec2:DescribeSecurityGroups"
      ],
      "Resource": "*"
    },
    {
      "Sid": "SSMAccess",
      "Effect": "Allow",
      "Action": [
        "ssm:StartSession",
        "ssm:DescribeSessions",
        "ssm:TerminateSession"
      ],
      "Resource": "*"
    },
    {
      "Sid": "S3LogAccess",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-alb-logs-bucket",
        "arn:aws:s3:::my-alb-logs-bucket/*"
      ]
    }
  ]
}

Wrap-Up: ALB 502 Selalu Punya Jejak — Temukan di Log yang Benar

502 dari ALB dengan instance Healthy bukan anomali sistem — ini adalah sinyal bahwa lapisan antara ALB dan aplikasi perlu diperiksa lebih dalam dari sekadar status health check. Mulai dari access log ALB untuk mendapatkan error_reason, lalu ikuti jalur yang ditunjukkan: protokol mismatch, keep-alive timeout, respons malformed, atau crash aplikasi. Setiap penyebab meninggalkan jejak berbeda di log.

Untuk referensi lebih lanjut, lihat dokumentasi resmi AWS:

Glosarium

IstilahDefinisi
502 Bad GatewayError HTTP yang dihasilkan ALB ketika target tidak mengembalikan respons HTTP yang valid atau koneksi ke target gagal.
Target GroupKumpulan target (instance, IP, Lambda) yang menerima traffic dari ALB berdasarkan listener rule.
Health CheckProbe periodik yang ALB kirim ke target untuk menentukan apakah target layak menerima traffic. Berjalan independen dari traffic produksi.
Keep-Alive TimeoutDurasi koneksi TCP persistent dipertahankan antara ALB dan target sebelum ditutup karena tidak ada aktivitas.
error_reasonField di access log ALB yang menjelaskan alasan spesifik kegagalan request, seperti Target.ResponseHeaderTimeout atau Target.ConnectionError.

Komentar

Postingan populer dari blog ini

EC2 Tidak Bisa Akses Internet di Custom VPC: Cara Pasang Internet Gateway dan Update Route Table