Menyelami bagaimana kontainer berkomunikasi dan terisolasi: kelima network driver Docker (bridge, host, none, macvlan/ipvlan, overlay), mengapa custom bridge dengan DNS otomatis wajib di produksi, cara kerja docker0, port publishing lewat iptables, dan jebakan-jebakan jaringan yang sering muncul.

Setelah di episode 8 sebelumnya kita menyelesaikan urusan data — bind mounts, named volumes, tmpfs, backup-restore dengan tar, dan PostgreSQL yang selamat dari recreate — pada episode kali ini kita membahas lapisan terakhir sebelum aplikasi kalian benar-benar "berbicara": jaringan. Sejauh ini kita selalu menghubungkan kontainer ke dunia luar dengan -p 3000:3000 — port forwarding — tanpa pernah bertanya apa yang sebenarnya terjadi di balik layar. Pertanyaan itu menjadi mendesak begitu kalian menjalankan lebih dari satu kontainer: aplikasi web butuh bicara ke database, API butuh bicara ke Redis. Bagaimana caranya?
Ini bukan pertanyaan trivial. Di produksi, arsitektur jaringan menentukan keamanan (kontainer apa yang bisa "melihat" kontainer lain), keandalan (apakah aplikasi tetap berfungsi saat IP kontainer berubah), dan skala (bagaimana kontainer di banyak host berkomunikasi). Banyak insiden produksi berakar di sini: aplikasi yang jalan di localhost tapi tidak di kontainer, database yang "tidak bisa dihubungi" karena salah network, atau dua layanan yang saling bentrok port. Memahami model jaringan Docker — bukan sekadar menghafal perintah — akan menyelamatkan kalian dari debugging yang berjam-jam.
Di episode ini kita akan membedah kelima network driver Docker, membandingkan default bridge dengan custom bridge (dan mengapa DNS otomatis mengubah segalanya), membongkar cara kerja docker0 dan port publishing melalui iptables, lalu menutup dengan praktik nyata: aplikasi + Redis berkomunikasi lewat nama kontainer.
Setiap kontainer mendapatkan network namespace sendiri — ini adalah wujud isolasi network yang kita sebut di episode 1 (namespaces di kernel Linux). Di dalam namespace-nya, kontainer punya interface, IP, tabel routing, dan aturan firewall sendiri yang tidak terlihat oleh kontainer lain secara langsung. Bayangkan setiap kontainer sebagai rumah di perumahan: setiap rumah punya alamat dan pintu sendiri, dan untuk saling bicara mereka harus terhubung ke jalan yang sama — itulah peran network driver.
Saat kalian menjalankan docker run tanpa flag network, Docker otomatis menautkan kontainer ke network bridge bawaan. Docker juga membuat beberapa network bawaan lain yang sudah familiar:
docker network lsNETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
c3d4e5f6a1b2 none null localTiga nama itu (bridge, host, none) adalah network bawaan yang tidak bisa dihapus. Di sampingnya, kalian bisa membuat network sendiri — dan di sinilah kontrol sebenarnya berada.
Docker menyediakan lima driver network, masing-masing dengan model isolasi yang berbeda:
1. Bridge (default) — driver default untuk single-host. Docker membuat virtual switch (jembatan) bernama docker0 di host. Setiap kontainer di network ini mendapat IP privat (misalnya 172.17.0.x) dan interface eth0 yang tersambung ke docker0. Kontainer di bridge network yang sama bisa saling bicara lewat IP; untuk keluar ke internet mereka melewati NAT (masquerade) — kita akan bedah mekanismenya sebentar lagi.
2. Host — menghilangkan isolasi network. Kontainer memakai network namespace host secara langsung: IP dan port host dipakai apa adanya, tanpa NAT, tanpa -p. Jika kontainer menjalankan server di port 8080, ia mendengarkan di localhost:8080 host. Keuntungannya: zero overhead network dan latency paling rendah. Risikonya: tidak ada isolasi port — dua kontainer host-mode tidak bisa sama-sama memakai port 8080, dan semua proses host "terlihat" dari dalam kontainer.
3. None — kontainer tanpa network sama sekali. Tidak ada interface selain loopback. Cocok untuk workload yang tidak butuh jaringan dan ingin isolasi maksimal — misalnya job pemrosesan batch yang hanya membaca volume dan menulis hasilnya, atau worker yang komunikasinya lewat file.
4. Macvlan / IPvlan — memberikan kontainer identitas fisik di jaringan lokal. Macvlan memberi tiap kontainer MAC address sendiri di subnet LAN, sehingga kontainer tampak seperti perangkat fisik di jaringan itu (bisa bicara langsung dengan router dan perangkat lain). Ipvlan menggunakan IP address di interface host. Kekurangannya: butuh konfigurasi subnet yang benar, dan ada keterbatasan konektivitas dengan host (lihat pitfalls).
5. Overlay — driver untuk multi-host, digunakan oleh Docker Swarm. Kontainer di banyak node bisa saling bicara seolah-olah berada di satu jaringan virtual, dengan traffic yang dienkapsulasi antar node. Ini fondasi dari cluster yang akan kita pelajari di episode 16-17.
| Driver | Isolasi | Lingkup | Kapan Dipakai |
|---|---|---|---|
bridge | Per-network, NAT keluar | Single host | Default, komunikasi antar kontainer satu host |
host | Tidak ada | Single host | Kinerja maksimal, workload yang butuh port host langsung |
none | Total | Single host | Workload tanpa jaringan |
macvlan/ipvlan | Per-MAC/IP fisik | Jaringan lokal | Kontainer yang harus tampil sebagai device di LAN |
overlay | Terenkapsulasi antar node | Multi host | Swarm cluster |
Sekarang kita sampai pada keputusan paling penting di episode ini. Docker punya dua jenis bridge: default bridge (network bridge bawaan) dan user-defined bridge (network yang kalian buat dengan docker network create). Secara fungsional keduanya mirip — kontainer di dalamnya bisa saling bicara — tapi ada satu perbedaan yang menentukan produksi: DNS.
Pada user-defined bridge, Docker menyuntikkan embedded DNS resolver ke setiap kontainer. Hasilnya, kontainer bisa saling memanggil berdasarkan nama kontainer, dan nama itu di-resolve otomatis ke IP kontainer yang bersangkutan — bahkan jika IP-nya berubah setelah kontainer di-restart.
Pada default bridge, DNS internal ini tidak tersedia. Kontainer hanya bisa saling bicara lewat IP. Dan karena IP kontainer berubah setiap recreate, aplikasi yang menyimpan IP database akan rusak begitu kontainer dibuat ulang. Inilah alasan tegas: jangan pernah memakai default bridge di produksi.
Mari kita buktikan dengan eksperimen kecil. Buat network custom, jalankan dua kontainer, dan coba panggil dengan nama:
docker network create my-net
docker run -d --name redis-cache --network my-net redis:7-alpine
docker run -d --name app --network my-net -e REDIS_URL=redis://redis-cache:6379 my-app:1.0
docker exec app getent hosts redis-cache172.18.0.2 redis-cacheBaris 172.18.0.2 redis-cache membuktikan bahwa nama kontainer redis-cache di-resolve ke IP-nya oleh embedded DNS. Sekarang bayangkan jika kita memakai default bridge: tidak ada cara seperti ini — kalian harus hardcode IP yang bisa berubah, atau memakai mekanisme lama --link (deprecated). Inilah mengapa untuk multi-container, user-defined bridge adalah standar.
Berikut perintah-perintah pengelolaan network yang paling sering dipakai:
docker network create my-net
docker network ls
docker network inspect my-net
docker network connect my-net redis-cache
docker network disconnect my-net redis-cache
docker run -d --network my-net --name app my-app:1.0docker network create my-net membuat network baru dengan driver bridge (default). Opsi berguna: --driver, --subnet, --ip-range, --internal (network tanpa akses internet — bagus untuk database yang hanya boleh dijangkau kontainer lain).docker network inspect my-net menampilkan detail JSON: subnet, gateway, dan — yang paling berguna — daftar kontainer beserta IP-nya.docker network connect dan docker network disconnect menautkan/memutuskan kontainer yang sudah berjalan ke/ dari network, tanpa harus membuat ulang kontainer. Berguna saat aplikasi tiba-tiba butuh akses ke network baru.docker run --network my-net menempatkan kontainer langsung ke network saat pertama kali dibuat.Kontainer bisa terhubung ke lebih dari satu network sekaligus (docker network connect berulang kali). Ini pola yang umum: aplikasi web terhubung ke network frontend (untuk reverse proxy) dan network backend (untuk database), sementara database hanya ada di backend — isolasi zona yang mirip DMZ di firewall klasik.
Sekarang mari buka kap mesinnya. Ketika Docker pertama kali dijalankan, ia membuat interface docker0 di host:
ip addr show docker04: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
link/ether 02:42:ab:1c:2d:3e
inet 172.17.0.1/16 scope global docker0docker0 adalah jembatan Linux (virtual switch) dengan IP 172.17.0.1/16. Setiap kontainer di default bridge mendapat IP dari rentang 172.17.0.0/16, dan setiap interface eth0 kontainer adalah salah satu "port" dari jembatan ini — sama seperti kabel ethernet yang masuk ke switch fisik. Ketika kontainer di bridge network memanggil keluar (misalnya apt-get update), paketnya di-masquerade (NAT) oleh iptables, sehingga tampak datang dari IP host. Kontainer bisa keluar ke internet, tapi dunia luar tidak bisa masuk — kecuali kita membuka pintunya.
Pintu itu dibuka dengan docker run -p 8080:80. Apa yang sebenarnya terjadi? Docker membuat aturan DNAT di iptables yang mentranslasikan paket masuk ke IP host:8080 menjadi IP kontainer:80:
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80Karena -p 8080:80 dipetakan pada port host, hanya ada satu kontainer yang bisa memakai port host tertentu — kalian tidak bisa menjalankan dua kontainer dengan -p 8080:80 sekaligus. Ini salah satu alasan mengapa di produksi kalian memakai satu entry point (reverse proxy, episode 20) yang meneruskan ke banyak kontainer, daripada mem-publish port setiap kontainer langsung.
Perlu juga disebutkan: embedded DNS Docker berjalan di 127.0.0.11 di dalam setiap kontainer. Resolver ini menangani nama kontainer, nama network alias, dan meneruskan query yang tidak dikenal ke DNS host. Itulah mengapa di dalam kontainer, getent hosts redis-cache dan getent hosts google.com keduanya bekerja — keduanya lewat resolver yang sama.
Mari rakit skenario yang akan kalian temui terus-menerus di produksi: aplikasi Node.js yang membaca dari Redis. Dengan custom bridge, aplikasi cukup menyebut redis-cache sebagai host — tanpa peduli IP.
docker network create my-net
docker run -d --name redis-cache --network my-net redis:7-alpine
docker run -d --name app \
--network my-net \
-e REDIS_URL=redis://redis-cache:6379 \
-p 3000:3000 \
my-app:1.0
docker exec app node -e "const r=require('redis').createClient({url:process.env.REDIS_URL}); ..."Di dalam kode aplikasi, koneksi Redis ditulis sebagai:
const client = createClient({ url: "redis://redis-cache:6379" })
await client.connect()
await client.set("kunci", "nilai")Perhatikan: redis-cache di sini bukan alamat internet — ia nama kontainer yang di-resolve oleh embedded DNS 127.0.0.11 di dalam kontainer app ke IP Redis. Inilah keajaiban custom bridge: konfigurasi aplikasi tidak lagi bergantung pada IP, sehingga ketika Redis di-recreate (IP berubah), aplikasi tetap bekerja tanpa mengubah apa pun.
Default bridge tanpa DNS. Kontainer di default bridge hanya bisa saling bicara lewat IP, dan IP berubah setiap recreate. Gunakan user-defined bridge untuk semua komunikasi antar kontainer.
Bentrok port di driver host. Dengan --network host, isolasi port hilang total: kontainer memakai port host secara langsung, sehingga dua kontainer host-mode tidak bisa berbagi port yang sama, dan kalian kehilangan fleksibilitas -p. Driver host hanya pantas untuk workload tertentu yang memang butuh performa maksimal.
Keterbatasan konektivitas macvlan. Kontainer macvlan tidak bisa berkomunikasi dengan host di mana kontainer itu berjalan (oleh desain, untuk mencegah loop pada bridge). Aplikasi yang perlu bicara ke host (misalnya ke service di localhost) akan gagal dengan macvlan. Jika kebutuhan ini ada, pertimbangkan driver lain atau konfigurasi interface tambahan.
Kontainer baru gagal akses internet di --network host atau network none: ini bukan bug — inilah isolasi yang memang kalian minta. Network internal bahkan memblokir akses keluar secara sengaja; pastikan kontainer yang butuh internet berada di network yang benar.
Menyimpan IP antar kontainer. Jangan pernah hardcode IP kontainer di konfigurasi aplikasi. Dengan custom bridge, cukup gunakan nama kontainer — IP adalah detail yang boleh berubah kapan pun.
Tip
Untuk men-debug masalah jaringan, tiga senjata utama: docker network inspect my-net (lihat IP dan anggota network), docker exec app getent hosts <nama> (uji resolve nama), dan docker exec app ping <ip> atau nc -zv <host> <port> (uji koneksi). Dengan image Alpine minimal, ping dan nc mungkin tidak terpasang — instal via apk add iputils netcat-openbsd di kontainer sementara untuk debugging.
Pada episode 9 ini kalian telah menyelami jaringan kontainer hingga level kernel: memahami bahwa isolasi network berbasis network namespace, mengenal kelima driver (bridge untuk single-host, host yang meniadakan isolasi, none untuk isolasi total, macvlan/ipvlan untuk identitas fisik di LAN, dan overlay untuk multi-host Swarm), memahami mengapa default bridge tidak bisa dipakai di produksi (tidak ada DNS otomatis), membuat dan mengelola network sendiri dengan docker network create/ls/inspect/connect/disconnect, membongkar cara kerja docker0 sebagai virtual switch, mempelajari bahwa -p sebenarnya menulis aturan DNAT iptables, dan membuktikan aplikasi + Redis bisa saling bicara lewat nama kontainer berkat embedded DNS di 127.0.0.11.
Inti yang harus kalian bawa pulang: komunikasi antar kontainer harus lewat user-defined bridge dan nama kontainer — bukan IP. Network adalah lapisan arsitektur: dengan mendesain network yang tepat (mana kontainer boleh "melihat" mana), kalian sekaligus mendesain keamanan aplikasi kalian.
Melihat seluruh rangkaian perintah docker run yang panjang — volume, network, env, port — kalian mungkin berpikir: "pasti ada cara yang lebih rapi untuk mengelola semua ini?" Tepat itulah materi berikutnya. Di episode 10 selanjutnya kita akan belajar Docker Compose: mendeklarasikan seluruh aplikasi multi-kontainer — web, API, database, Redis, network, volume — dalam satu file compose.yaml, dan mengganti deretan perintah docker run dengan satu perintah docker compose up -d. Sampai jumpa di episode 10!