Tiga pertanyaan menentukan hidup sebuah container di jaringan: bagaimana container mencapai internet, bagaimana container saling menemukan, dan bagaimana dunia luar masuk ke dalamnya. Ketiganya dijawab oleh kombinasi perangkat kernel — bridge, veth, netfilter, dan DNS — yang dikendalikan engine container. Chapter ini membedah lapisan-lapisan itu dari bawah ke atas: driver jaringan, mekanisme NAT di host, format port publishing, hingga resolusi nama antar container. Tanpa docker di lingkungan latihan, kita memakai fixture aturan NAT dan JSON hasil docker network inspect yang dibedah dengan jq.
Empat Driver Jaringan #
Setiap container menempel pada satu network, dan setiap network punya driver — keputusan arsitektural tentang seberapa jauh container diisolasi dari jaringan host:
| Driver | Cara kerja | Kapan dipakai |
|---|---|---|
bridge | container dapat IP privat (172.17.x.x), keluar lewat NAT | default; aplikasi biasa |
host | container memakai stack jaringan host langsung, tanpa isolasi | butuh performa jaringan maksimum |
none | hanya ada loopback lo di dalam container | job batch tanpa jaringan |
overlay | jaringan virtual yang merentang lintas host | cluster multi-mesin |
Driver bridge adalah dunia yang kita tempati sepanjang section ini — aplikasi terisolasi rapi tetapi tetap bisa keluar-masuk lewat terjemahan alamat di host. Driver host menghapus isolasi itu sepenuhnya: container mendengarkan port di host seolah proses biasa, jadi dua container tidak bisa sama-sama memakai port 80. Driver overlay baru relevan saat satu aplikasi tersebar di banyak mesin, topik di luar cakupan chapter ini.
Bridge dan NAT di Balik Layar #
Saat engine container pertama kali jalan, ia membuat docker0 — bridge di host dengan subnet 172.17.0.0/16. Setiap container yang menyala mendapat sepasang perangkat virtual: satu ujung veth menempel di bridge, ujung lainnya menjadi eth0 di dalam container. Sketsa lengkap alurnya:
klien 203.0.113.5
|
| TCP ke 192.0.2.10 port 8080
v
+--------------- host (laptop-budi) ---------------+
| eth0 192.0.2.10 |
| | |
| | rule DNAT: 8080 -> 172.17.0.2:80 |
| v |
| docker0 172.17.0.1/16 (bridge) |
| | | |
| veth veth |
| | | |
| web :80 db :5432 |
| 172.17.0.2 172.17.0.3 |
+---------------------------------------------------+Dua arah lalu lintas melewati host dengan dua perlakuan berbeda. Arah keluar (container ke internet): paket bersumber 172.17.0.2 tidak akan pernah dijawab oleh internet, karena alamat itu bersifat privat, jadi rule MASQUERADE menulis ulang sumbernya menjadi IP host. Arah masuk (internet ke container): rule DNAT mengubah tujuan paket dari host port 8080 menjadi 172.17.0.2 port 80. Berkas nat-rules di bundle materi memuat keduanya dalam format iptables:
cat nat-rules
echo '== terjemahan port host yang masuk =='
cari() {
hasil=$(grep -- "--dport $1 " nat-rules | sed 's/.*--to-destination //')
if [ -n "$hasil" ]; then
echo "port host $1 -> $hasil"
else
echo "port host $1 -> tidak ada rule, koneksi ditolak"
fi
}
cari 8080
cari 5432Output yang Dihasilkan:
# aturan gaya iptables yang dipasang engine container di table nat
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -d 172.17.0.2 --dport 80 -j ACCEPT
-A DOCKER ! -i docker0 -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
== terjemahan port host yang masuk ==
port host 8080 -> 172.17.0.2:80
port host 5432 -> tidak ada rule, koneksi ditolakMari kita bedah baris demi baris:
- Baris MASQUERADE: seluruh paket bersumber subnet 172.17.0.0/16 yang bukan menuju container lain (
! -o docker0) keluar dengan alamat host — inilah kenapa container bisa memanggil server publik. - Baris ACCEPT: meloloskan paket balasan koneksi yang sudah tercatat — ditangani connection tracking kernel, bukan ditulis manual.
- Baris DNAT: satu-satunya pintu masuk — paket TCP ke port 8080 di host dialihkan ke 172.17.0.2 port 80.
- Fungsi
cari(): membaca tabel rule dengangrep; spasi di akhir pola"--dport $1 "mencegah port 8080 ikut cocok dengan 80801.
Balasan menemukan jalan pulang lewat connection tracking: kernel mencatat setiap terjemahan yang pernah dilakukan, jadi paket balasan dari 172.17.0.2 otomatis ditulis balik menjadi 192.0.2.10:8080 sebelum dikirim ke klien. Port 5432 tidak punya rule DNAT — koneksi dari luar ditolak, persis seperti hasil cari 5432.
Membaca Format -p #
Port publishing ditulis dengan opsi -p dan format [hostIP:]hostPort:containerPort — bertambah ke kiri bila butuh kontrol lebih. Kita bedah dua bentuknya dengan fungsi parser kecil:
bedah() {
spec=$1
kontainer=${spec##*:}
sisa=${spec%:*}
if [ "$sisa" = "$spec" ]; then
hostip=0.0.0.0
hostport=$spec
else
case $sisa in
*:*) hostip=${sisa%%:*}; hostport=${sisa##*:} ;;
*) hostip=0.0.0.0; hostport=$sisa ;;
esac
fi
printf '%s -> dengar di %s:%s, teruskan ke container port %s\n' \
"$spec" "$hostip" "$hostport" "$kontainer"
}
bedah 8080:80
bedah 127.0.0.1:5432:5432Output yang Dihasilkan:
8080:80 -> dengar di 0.0.0.0:8080, teruskan ke container port 80
127.0.0.1:5432:5432 -> dengar di 127.0.0.1:5432, teruskan ke container port 5432Tiga operasi string membongkar spesifikasinya:
${spec##*:}: membuang segalanya sebelum titik dua terakhir — port container, bagian yang selalu wajib ada.${spec%:*}: menyisakan bagian depan; bila hasilnya sama dengan masukan, berarti tidak ada titik dua sama sekali.case $sisa in *:*): bagian depan yang masih mengandung titik dua berarti ada IP host di dalamnya —%%:*mengambil IP,##*:mengambil port host.- IP kosong berarti
0.0.0.0: mendengarkan di semua interface host.
DNS Antar Container #
Alamat 172.17.0.2 tidak boleh dikodekan di aplikasi — IP container berubah setiap start. Lapisan berikutnya menyelesaikannya dengan nama: pada user-defined network, engine container menjalankan DNS internal di 127.0.0.11, dan nama service (di compose) atau --name (di docker run) menjadi hostname yang bisa di-resolve. Aplikasi cukup menyambung ke db — bukan 172.20.0.3. Berkas toko-net.json di bundle materi adalah hasil docker network inspect toko-net; kita bacanya dengan jq:
jq -r '.[0].Name, .[0].Driver, .[0].IPAM.Config[0].Subnet' toko-net.json
echo '== container yang tergabung =='
jq -r '.[0].Containers | to_entries[] | "\(.value.Name) -> \(.value.IPv4Address)"' toko-net.json | sort
echo '== resolusi nama db =='
jq -r '.[0].Containers | to_entries[] | select(.value.Name == "db") | .value.IPv4Address' toko-net.jsonOutput yang Dihasilkan:
toko-net
bridge
172.20.0.0/16
== container yang tergabung ==
db -> 172.20.0.3/16
web -> 172.20.0.2/16
== resolusi nama db ==
172.20.0.3/16Tiga kueri itu bertingkat:
.Name, .Driver, .IPAM.Config[0].Subnet: identitas jaringan — nama, driver, dan subnet yang dialokasikan (172.20.0.0/16, satu blok privat per network).Containers | to_entries[]: fieldContainersadalah objek yang key-nya ID container;to_entriesmengubahnya menjadi daftar pasangan agar bisa diiterasi dan diformat ulang.select(.value.Name == "db"): menyaring entri bernamadb— persis query yang dilayani DNS internal ketika aplikasi menanyakan namadb.
Jaringan menjawab pertanyaan pergerakan — data bergerak masuk, keluar, dan menyamping. Pertanyaan penutupnya tentang waktu: ke mana data pergi ketika containernya dihapus? Jawabannya ada di lapisan penyimpanan — volume, bind mount, dan tmpfs — topik Chapter Container Storage .
Referensi lanjut: dokumentasi jaringan Docker untuk pembahasan tiap driver beserta topology-nya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
toko-net.json
Unduh berkas[
{
"Name": "toko-net",
"Id": "b7a1f9e4c2d8",
"Created": "2026-01-15T09:30:00Z",
"Driver": "bridge",
"IPAM": {
"Config": [
{
"Subnet": "172.20.0.0/16",
"Gateway": "172.20.0.1"
}
]
},
"Containers": {
"2f1b7c9d3e5a": {
"Name": "db",
"IPv4Address": "172.20.0.3/16"
},
"8e4a1d6c7b2f": {
"Name": "web",
"IPv4Address": "172.20.0.2/16"
}
}
}
]