Docker memberi container-container-mu jaringan virtual sendiri: tiap container dapat IP sendiri, nama yang bisa dipanggil container lain, dan localhost miliknya sendiri. Dari luar, container cuma bisa dicapai lewat port yang sengaja kamu buka. Bayangin gedung kantor. Tiap ruangan punya nomor extension telepon. Di gedung baru ada resepsionis: sebut nama ruangannya, kamu langsung disambungkan. Menelepon "nomor sendiri" dari sebuah ruangan cuma menyambung ke ruangan itu. Dan dari jalan, orang cuma bisa menelepon ruangan yang punya saluran langsung ke luar.
🧪 Tebakan cepat: di dalam container app,
DATABASE_URLmasihpostgres://catatan:...@localhost:5432/catatan, seperti waktu catatan-api jalan langsung diweb-1. Postgres sekarang jalan di containerdb. Apakah app-nya tersambung? Tebak dulu, jawabannya ada di bagian "localhost di dalam container".
Network bridge: tiap container punya IP sendiri
Network driver menentukan cara container terhubung. Yang paling sering dipakai bridge, dan itu juga default-nya: container di satu host disambungkan lewat switch virtual. Driver lain: host (container memakai jaringan host langsung, tanpa isolasi port), none (tanpa jaringan sama sekali), dan overlay (menyambung container di banyak host, untuk Docker Swarm).
Ada dua macam bridge:
| Default bridge | Bridge buatanmu | |
|---|---|---|
| Cara dapat | container tanpa --network |
docker network create catatan-net |
| Contoh subnet | 172.17.0.0/16 |
172.18.0.0/16 |
| Panggil container lewat nama | gak bisa | bisa |
Memanggil container lewat nama
Di bridge buatanmu, Docker menjalankan DNS sendiri: nama container (atau nama service di Compose) otomatis jadi nama host.
$ docker network create catatan-net
$ docker run -d --name db --network catatan-net \
-e POSTGRES_USER=catatan -e POSTGRES_PASSWORD=$DB_PASSWORD \
-v db-data:/var/lib/postgresql/data postgres:16
$ docker run -d --name catatan-api --network catatan-net \
-e DATABASE_URL=postgres://catatan:$DB_PASSWORD@db:5432/catatan \
-p 127.0.0.1:8080:3000 ghcr.io/budi/catatan-api:a41c9e2
$ docker logs catatan-api
Server jalan di http://localhost:3000
--network catatan-netmemasang kedua container di network yang sama. Dari dalamcatatan-api, namadbditerjemahkan ke172.18.0.2.docker network inspect catatan-netmenampilkan isinya: subnet172.18.0.0/16, dan tiap container dengan IP-nya (dbdi172.18.0.2/16,catatan-apidi172.18.0.3/16).- Di default bridge, nama gak bisa dipakai: app gagal dengan
getaddrinfo ENOTFOUND db.
Docker Compose membuat bridge buatan otomatis untuk tiap project (catatan-api_default), jadi di Compose, nama service dipakai sebagai hostname: db:5432, redis:6379. Itu sebabnya file Compose di lesson sebelumnya bisa menulis @db:5432 begitu saja.
localhost di dalam container
localhost (127.0.0.1) di dalam container berarti container itu sendiri, bukan web-1 dan bukan container lain. Di container app gak ada Postgres, jadi koneksinya ditolak:
Gagal konek ke database: connect ECONNREFUSED 127.0.0.1:5432
Jadi gak, app-nya gak tersambung. Itu jawaban tebakannya. Ganti host-nya dengan nama container atau service: postgres://catatan:...@db:5432/catatan. Kalau container justru perlu memanggil program yang jalan langsung di host, Docker Desktop menyediakan nama host.docker.internal; di Linux, tambahkan --add-host=host.docker.internal:host-gateway.
Port: dipublikasikan atau cuma dicatat
Container di bridge gak bisa dicapai dari luar host sampai port-nya dipublikasikan dengan -p:
| Opsi | Bisa dibuka dari | Tampil di docker ps |
|---|---|---|
(tanpa -p, cuma EXPOSE 3000) |
container lain di network yang sama | 3000/tcp |
-p 8080:3000 |
web-1 dan seluruh internet |
0.0.0.0:8080->3000/tcp |
-p 127.0.0.1:8080:3000 |
web-1 saja |
127.0.0.1:8080->3000/tcp |
- Formatnya
HOST:CONTAINER:-p 8080:3000artinya port 8080 di host diteruskan ke port 3000 di container. Diweb-1,curl http://localhost:8080/healthdijawab app. - Tanpa IP di depan, Docker membuka port di
0.0.0.0, artinya semua alamat host, termasuk IP publik203.0.113.10. Dengan127.0.0.1:di depan, cuma host itu sendiri yang bisa membuka. Biasanya Nginx diweb-1yang meneruskan ke sana (lesson Reverse Proxy). - Antar container di network yang sama, port gak perlu dipublikasikan sama sekali:
catatan-apimenyambung kedb:5432walaudbgak punya-p.
Latihan lesson ini mengurai teks opsi -p itu: "8080:3000" jadi { host: "0.0.0.0", hostPort: 8080, containerPort: 3000 }, dan "127.0.0.1:3000:3000" jadi { host: "127.0.0.1", hostPort: 3000, containerPort: 3000 }. Potong di :; dua bagian berarti IP-nya default 0.0.0.0, tiga bagian berarti bagian pertama IP-nya. Port-nya angka, jadi ubah dengan Number(...).
⚠️ Jebakan yang sering ketemu
localhostuntuk container lain. Di dalam container,localhostitu dirinya sendiri. Pakai nama container atau service.- Mengandalkan nama di default bridge. Nama cuma bisa dipanggil di bridge buatanmu (atau network Compose). Di default bridge hasilnya
ENOTFOUND. -p 5432:5432untuk database di server. Postgres jadi bisa dicapai dari internet. Docker membuka port lewat aturan iptables-nya sendiri, jadi firewallufwdiweb-1gak menghalanginya. Database gak perlu-p; kalau memang perlu dari host, pakai127.0.0.1:5432:5432. Pengamanan lainnya ada di lesson Docker Security Basics.- Port tertukar. Kiri port host, kanan port container.
-p 3000:8080meneruskan ke port 8080 di container, padahal app mendengarkan 3000.
TL;DR: Network driver default Docker adalah bridge. Di bridge buatanmu (docker network create) dan di network yang dibuat Compose, Docker punya DNS sendiri, jadi container saling memanggil lewat nama (db:5432); di default bridge, nama gak bisa dipakai. localhost di dalam container berarti container itu sendiri, bukan host dan bukan container lain. EXPOSE cuma mencatat port; -p HOST:CONTAINER (-p 8080:3000) yang meneruskan port host ke container, di 0.0.0.0 (semua alamat, termasuk internet) kecuali kamu menulis IP di depannya (-p 127.0.0.1:8080:3000). Antar container di network yang sama gak perlu -p, dan database di server jangan dipublikasikan ke 0.0.0.0.