502 dari Cloudflare Tunnel, ternyata cuma soal nama service
Tunnel bilang 502, semua kontainer hijau. Ternyata DNS jaringan compose cuma kenal nama service, dan alias enam baris langsung beres.
Ringkasan
Docker nyala semua tapi URL publik malah 502 terus bikin bingung. Ternyata ingress tunnel nyari host proxy padahal nama service aslinya plane-proxy jadi nggak ketemu DNS. Akhirnya ditambahin alias proxy di compose biar nyambung lagi langsung 200.
Halaman 502 Bad Gateway itu muncul pas semua kontainer hijau. docker compose ps rapi, nggak ada yang restart berulang. Dari dalam host, proxy-nya pun jawab banget: curl ke port 8199 balik 200. Tapi begitu buka URL publik lewat tunnel, cuma 502 terus.
Dugaan pertama saya ke arah yang keliru: mungkin config proxy-nya salah, mungkin token tunnel-nya bermasalah, atau jangan-jangan ada ingress rule di dashboard Cloudflare yang nunjuk ke tempat yang nggak tepat. Saya buka ulang file config proxy, bandingin baris per baris. Nggak ada yang aneh. Semua jawab normal dari dalam.
Ternyata masalahnya jauh lebih bawah: resolusi nama. Di dashboard, ingress menunjuk ke http://proxy:8199. Nama proxy itu nggak pernah terdaftar di mana pun, karena service di file compose saya bernama plane-proxy. Docker Compose cuma mendaftarkan nama service ke DNS di jaringannya, dan setiap kontainer bisa manggil kontainer lain lewat nama service itu [1]. Nama pendek buatan dashboard nggak otomatis ikut kebawa.
Artinya tunnel-nya sendiri sehat. Dokumentasi Cloudflare bilang jelas: 502 di tunnel artinya tunnel sudah tersambung ke jaringan Cloudflare, tapi cloudflared gagap reach origin yang dituliskan di ingress rule, dan masalahnya ada di antara cloudflared dengan service lokal [3]. Dua fakta ini ketemu di satu titik: cloudflared cari host bernama proxy, DNS jaringan compose bilang host itu nggak ada, dan setiap request pun berakhir jadi 502 [4].
Perbaikannya cuma enam baris di bawah service plane-proxy:
networks:
default:
aliases:
# cloudflared ingress menunjuk ke http://proxy:8199,
# sedangkan nama service-nya plane-proxy
- proxyContainer di-recreate, URL publik dibuka, dan kali ini yang balik 200 dengan HTTPS utuh.
Kok bisa sesimpel itu? Karena aliases memang didesain buat ini: mendeklarasikan hostname alternatif untuk sebuah service di network tertentu, dan kontainer lain bebas manggil lewat nama service atau alias tersebut [2]. Alias-nya network-scoped, jadi dia hidup di network compose itu saja, persis di tempat cloudflared mencari.
Nama service itu kontrak, bukan detail
Yang bikin insiden ini nempel itu karena sederhananya. Nggak ada config yang salah, nggak ada yang rusak. Cuma ada dua dunia yang pakai nama berbeda buat benda yang sama, dan Docker Compose nggak punya cara buat tahu bahwa dashboard Cloudflare di luar sana menyebut service saya dengan nama lain. Config di dashboard itu hidup di luar repo, nggak ikut nongol di git diff, dan justru karena itu gampang banget kelewat.
Setelah kejadian ini saya mulai melihat nama service sebagai kontrak API. Begitu nama dipakai sistem lain, mengganti namanya sama saja dengan mengubah endpoint diam-diam: dashboard tunnel, healthcheck, sampai script CI yang hardcode hostname lama bisa rontok tanpa satu pun error di build. Rename service sekarang nggak pernah saya lakukan sebelum ngecek siapa saja yang manggil nama lama.
Docker sebenernya nyediain dua pintu resmi buat nama alternatif. Yang satu aliases di blok networks, yang satu links. Dokumentasinya sendiri bilang links itu bukan syarat buat komunikasi antar-service, karena secara default semua service sudah bisa saling reach lewat nama service [8]. Buat kasus saya, aliases lebih pas: eksplisit, kebaca di file compose, dan alasan keberadaannya jelas.
Jebakan kecil yang menggoda
Ada satu jalan pintas yang hampir saya ambil: pasang container_name: proxy biar nama pendeknya resolve. Untung saya cek dulu. Selain menempel ke satu kontainer saja, compose bahkan menolak scale lebih dari satu kontainer kalau container_name dipasang, dan error-nya baru muncul pas kamu butuh scale [8]. Menurut saya ini bukan tempat buat solusi naming: kalau tujuannya kontrak buat kontainer lain, alias di network tetap lebih jujur karena dia memang didesain buat itu.
Kesalahan yang saya buat sendiri di sini: begitu compose file diadaptasi dari template upstream, nama service ikut contoh baru, sementara ingress di dashboard masih nyebut nama lama. Satu baris beda, semua request kena. Mulai sekarang alias-nya yang saya pin di compose, biar kalau nama service berubah lagi, dunia luar nggak pernah ikut rusak.