Networking Docker Compose: DNS, Host, dan Port
Pahami network bridge default, DNS berdasarkan nama service, kapan host mode layak digunakan, berbagi network antarproject, serta port publik yang melewati UFW.
Hal yang dibangun Compose sebelum aplikasi Anda dimulai
Networking Docker Compose dimulai dengan satu aturan: docker compose up membuat network privat untuk project, menghubungkan setiap service ke network tersebut, dan memungkinkan service-service itu saling menjangkau berdasarkan nama service. Anda tidak perlu menulis satu baris networks: pun untuk mendapatkannya. Sebagian besar kebingungan tentang networking Compose terjadi karena default tersebut sudah tersedia.
Berikut file sederhana. Simpan sebagai compose.yaml di direktori bernama shop.
services:
web:
image: nginx:1.27
ports:
- "8080:80"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: exampleJalankan dan lihat yang dibuat Docker:
docker compose up -d
docker network lsDaftar tersebut sekarang berisi network bernama shop_default. Compose menamainya <project>_default, dan nama project secara default menggunakan nama direktori dalam huruf kecil. Ganti dengan docker compose -p myproject up -d atau dengan name: myproject tingkat teratas di dalam file. Driver-nya adalah bridge, yaitu switch virtual di dalam host. Setiap container mendapat alamat pada subnet privat, dan traffic keluar diterjemahkan ke alamat host saat keluar.
docker compose down menghapus network tersebut. Karena itu, container lama dari project sebelumnya dapat membuat network tetap terbuka: Docker menolak operasi tersebut dengan error while removing network: network shop_default has active endpoints, dan solusinya adalah menghentikan atau menghapus container yang masih terhubung ke network itu.
Jika Compose masih baru bagi Anda, tata letak file Compose dan perintah siklus hidup layak dibaca terlebih dahulu, karena semua bagian di bawah ini mengasumsikan Anda dapat memulai dan menghentikan project.
DNS berdasarkan nama layanan adalah bagian yang sering terlewat oleh pemula
Pada jaringan yang ditentukan pengguna, Docker menjalankan server DNS tertanam yang dapat diakses setiap container di 127.0.0.11. Server ini menyelesaikan nama layanan menjadi alamat container saat ini. Jadi, web dapat terhubung ke database menggunakan nama host db pada port 5432, tanpa konfigurasi apa pun.
docker compose exec web getent hosts dbPerintah tersebut mencetak baris seperti 172.18.0.2 db. Jika tidak mencetak apa pun, kedua layanan tidak berada di jaringan yang sama.
Kesalahan yang hampir semua orang lakukan setidaknya sekali adalah menggunakan localhost dalam konfigurasi aplikasi. Di dalam container, localhost merujuk ke container tersebut, bukan ke host dan bukan ke layanan lain. Klien Postgres melaporkannya dengan jelas:
could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP connections on port 5432?String koneksi yang benar adalah postgresql://postgres:example@db:5432/postgres. Bagian host-nya adalah nama layanan.
Ada dua hal yang dapat menghemat waktu nantinya. Nama layanan mengarah ke container yang sedang berjalan, sehingga docker compose up -d --scale web=3 menghasilkan satu nama dengan tiga alamat. Klien yang menyimpan cache DNS selamanya akan tetap mengarah ke container yang sudah tidak aktif. Selain itu, jaringan lama bridge yang digunakan oleh docker run biasa tanpa --network sama sekali tidak memiliki resolusi nama. Karena itu, panduan tentang container link dari 2016 tidak sesuai dengan kondisi yang Anda lihat.
Anda tidak memerlukan ports: untuk menghubungkan dua layanan
ports: memublikasikan port container pada host. Fitur ini digunakan untuk lalu lintas yang datang dari luar Docker. Fitur ini tidak terkait dengan lalu lintas antarlayanan, yang sudah berfungsi pada seluruh rentang port di jaringan project.
Karena itu, ports: - "5432:5432" yang sering ditambahkan ke layanan database tidak memberikan manfaat dan justru menimbulkan risiko nyata: konfigurasi tersebut mengekspos Postgres pada antarmuka publik server. Hapus konfigurasi itu. Jika Anda ingin mengaksesnya dari laptop untuk melakukan migrasi, ikat ke loopback dengan "127.0.0.1:5432:5432" dan akses melalui tunnel SSH. Perbedaan antara listening socket, port yang dipublikasikan, dan aturan firewall dibahas dalam cara kerja port dan layanan yang melakukan listening di Linux.
expose: hanya berfungsi sebagai dokumentasi dalam Compose. Konfigurasi ini tidak membuka apa pun, karena tidak ada akses yang ditutup antarkontainer pada jaringan yang sama.
Kapan network_mode host tepat digunakan dan apa konsekuensinya
Mode host menghapus namespace jaringan milik container dan memungkinkan proses menggunakan interface host secara langsung.
services:
probe:
image: alpine:3.20
network_mode: host
command: sleep infinityAda alasan yang jelas untuk menggunakannya. Proses yang perlu melihat traffic broadcast atau multicast pada jaringan lokal, seperti penemuan perangkat untuk server media atau hub otomasi rumah, tidak dapat melihatnya dari balik bridge karena bridge tidak meneruskan traffic tersebut ke container. Agen pemantauan yang membaca penghitung interface host memerlukan interface host. Anda juga melewati satu tahap translasi alamat, yang penting pada laju paket tinggi.
Konsekuensinya bersifat spesifik.
ports: berhenti berfungsi. Docker memperingatkan bahwa port yang dipublikasikan akan diabaikan saat menggunakan mode jaringan host, dan container akan melakukan binding pada port apa pun yang digunakan prosesnya. Dua container dalam mode host yang ingin menggunakan port 8080 akan mengalami konflik, dan container kedua berhenti dengan bind: address already in use.
Resolusi nama berdasarkan nama layanan tidak tersedia untuk kedua arah. Container tidak berada di jaringan project, sehingga tidak dapat me-resolve db, dan layanan lain juga tidak dapat me-resolve container tersebut. Container hanya dapat menjangkaunya melalui port yang dipublikasikan pada host, biasanya pada 127.0.0.1.
Isolasi tidak tersedia. Proses yang melakukan binding pada 0.0.0.0 di dalam container mode host akan listening pada setiap interface server Anda, termasuk interface publik, sama seperti paket yang diinstal dengan apt. Ada satu keuntungannya: traffic ini mengikuti jalur input normal, sehingga aturan UFW berlaku untuknya. Hal ini tidak berlaku pada port yang dipublikasikan.
Mode host adalah fitur Linux Docker Engine. Docker Desktop hanya mendukungnya mulai versi 4.34 dan setelah Anda mengaktifkannya. Selain itu, container tidak dapat melakukan binding pada alamat IP host, dan hanya TCP serta UDP yang ditangani. Jika separuh tim Anda menggunakan server Linux dan separuh lainnya menggunakan Docker Desktop, file yang sama dapat berperilaku berbeda.
Gunakan mode host saat Anda memerlukan interface host. Jangan menggunakannya untuk memperbaiki masalah koneksi karena biasanya mode ini mengganti satu masalah dengan masalah yang lebih sulit.
Menghubungkan dua project Compose dengan network eksternal
Network yang dibuat oleh satu project tidak terlihat oleh project lain. Karena itu, reverse proxy di proxy/compose.yaml tidak dapat melihat aplikasi di app/compose.yaml, meskipun berada di server yang sama. Solusinya adalah network yang tidak dimiliki oleh kedua project tersebut.
Buat network tersebut satu kali secara manual:
docker network create edgeKemudian deklarasikan sebagai eksternal di setiap project. Sisi proxy:
services:
proxy:
image: traefik:v3.1
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- edge
networks:
edge:
name: edge
external: trueSisi aplikasi:
services:
app:
image: nginx:1.27
networks:
- edge
- internal
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
networks:
- internal
networks:
edge:
name: edge
external: true
internal:external: true memberi tahu Compose untuk terhubung ke network yang sudah ada, bukan membuat network baru, dan membiarkannya tetap ada setelah docker compose down. Key name: yang terpisah lebih penting daripada yang terlihat: tanpa key tersebut, Compose mencari network yang namanya persis edge. Dengan key tersebut, Anda dapat menggunakan satu nama network di file dan nama lain di host.
Jika network tidak ada, Compose menolak untuk memulai dan melaporkan bahwa network telah dideklarasikan sebagai eksternal tetapi tidak ditemukan. Buat network tersebut terlebih dahulu.
Perhatikan penggunaan internal dalam file aplikasi. Database hanya berada di network lokal project tersebut, sehingga proxy tidak dapat menjangkaunya dan hanya app yang dapat mengaksesnya. Menambahkan internal: true di bawah network membatasi lebih jauh dengan menghapus rute network tersebut ke dunia luar sepenuhnya. Ini merupakan pengaturan default yang baik untuk database, tetapi ada konsekuensi yang perlu diketahui sebelum menerapkannya: container pada network internal tidak dapat mengunduh apa pun, sehingga entrypoint yang menjalankan apt-get update atau pip install saat startup akan berhenti dan kemudian gagal karena timeout.
Untuk contoh konfigurasi lengkap dengan aturan routing dan sertifikat, lihat menjalankan beberapa aplikasi di balik satu instance Traefik.
Port yang dipublikasikan melewati UFW
Ini adalah bagian dari jaringan Compose yang dapat berubah menjadi insiden keamanan. Anda memublikasikan sebuah port, memastikan UFW aktif dan menolak semua koneksi kecuali SSH, tetapi layanan tersebut tetap dapat diakses dari internet.
sudo ufw status
curl http://203.0.113.10:8080UFW menyatakan bahwa port tersebut diblokir. curl tetap mengembalikan halaman. Tidak ada yang rusak. Docker menulis aturan penerjemahan alamat dan penerusan miliknya sendiri langsung ke iptables. Lalu lintas menuju port container yang dipublikasikan diteruskan ke container, bukan dikirimkan ke host. Karena itu, lalu lintas tersebut tidak melewati chain yang dikelola UFW untuk lalu lintas yang ditujukan secara lokal. Aturan Docker juga dicocokkan sebelum aturan UFW.
Perbaikan singkatnya adalah memublikasikan port hanya pada alamat yang diperlukan:
ports:
- "127.0.0.1:8080:80"Dengan demikian, sisi host terikat ke loopback. Port tersebut dapat diakses dari server itu sendiri dan melalui tunnel SSH, tetapi tidak dari tempat lain. Tempatkan titik masuk publik di belakang reverse proxy yang memang memublikasikan port 80 dan 443. Penjelasan lengkapnya, termasuk chain DOCKER-USER untuk kasus ketika Anda harus memfilter port yang dipublikasikan, tersedia di mengapa Docker memublikasikan port langsung melewati UFW dan cara memperbaikinya.
Cara men-debugnya dengan empat perintah
Mulailah dengan memeriksa jaringan yang sebenarnya digunakan setiap container:
docker network inspect shop_defaultBlok Containers mencantumkan setiap container yang terhubung beserta alamatnya. Layanan yang tidak ada dalam daftar tersebut berada di jaringan lain, menggunakan mode host, atau tidak sedang berjalan.
Uji resolusi nama dari container sementara yang terhubung ke jaringan yang sama. Dengan demikian, Anda tidak memerlukan alat apa pun di dalam image Anda sendiri:
docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432nslookup yang gagal menunjukkan masalah pada resolusi nama atau keanggotaan jaringan. nslookup yang berhasil, sementara nc gagal, berarti layanan sedang berjalan tetapi tidak listening pada port tersebut, atau listening pada 127.0.0.1 di dalam containernya sendiri, bukan pada 0.0.0.0. Kondisi terakhir umum terjadi pada server pengembangan. Perbaikannya dilakukan pada alamat bind aplikasi, bukan pada Docker.
Ada satu kegagalan lain yang terlihat seperti bug Docker. Jika container dapat saling berkomunikasi tetapi tidak dapat menjangkau mesin di jaringan kantor atau VPN, kemungkinan subnet Docker bertumpang tindih dengan jaringan tersebut. Secara default, Docker mengalokasikan subnet mulai dari 172.17.0.0/16. Pindahkan pool tersebut di /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.200.0.0/16", "size": 24 }
]
}Kemudian jalankan sudo systemctl restart docker dan buat ulang jaringan yang terdampak, karena jaringan yang sudah ada tetap menggunakan subnet saat jaringan tersebut dibuat.
FAQ
Mengapa container saya tidak dapat saling menjangkau berdasarkan nama layanan?
Keduanya tidak berada di jaringan yang sama. Compose menempatkan setiap layanan di <project>_default secara otomatis, tetapi saat Anda menambahkan daftar networks: ke suatu layanan, daftar tersebut menjadi seluruh jaringan untuk layanan itu dan jaringan default tidak lagi diterapkan secara implisit. Jalankan docker network inspect <network> dan pastikan kedua container muncul di blok Containers. Periksa juga bahwa tidak ada layanan yang menggunakan network_mode: host, karena container dalam mode host tidak berada di jaringan Docker mana pun dan tidak dapat menemukan nama layanan.
Apakah saya perlu memublikasikan port agar satu layanan dapat menjangkau layanan lain?
Tidak. Di jaringan Compose, setiap port pada setiap container dapat dijangkau oleh container lain di jaringan tersebut. ports: hanya digunakan untuk mengekspos container agar menerima traffic dari luar Docker, sedangkan expose: adalah dokumentasi. Memublikasikan port database merupakan kebiasaan yang umum dan berisiko, karena database menjadi tersedia melalui antarmuka publik server Anda.
Apa perbedaan antara jaringan bridge dan host?
Bridge memberi container namespace jaringan dan alamatnya sendiri pada switch virtual, dengan resolusi nama otomatis antarkontainer serta traffic keluar yang diterjemahkan. Host memberi container akses langsung ke stack jaringan host: tidak ada alamat terpisah, tidak ada resolusi berdasarkan nama layanan, tidak ada publikasi port, dan tidak ada isolasi dari listener lain pada host. Bridge adalah default dan pilihan yang tepat, kecuali proses tersebut memerlukan antarmuka milik host.
Bagaimana cara menghubungkan container dari dua file Compose yang berbeda?
Buat jaringan bersama dengan docker network create edge, lalu deklarasikan jaringan tersebut di kedua file menggunakan external: true dan hubungkan layanan yang perlu berkomunikasi. Compose tidak akan membuat atau menghapus jaringan tersebut. Jika langkah pembuatan dilewati, Compose menolak untuk memulai dan melaporkan bahwa jaringan telah dideklarasikan sebagai eksternal tetapi tidak ditemukan.
Mengapa container saya dapat dijangkau dari internet ketika UFW memblokir port tersebut?
Karena port yang dipublikasikan ditangani oleh aturan penerusan yang ditambahkan Docker ke iptables. Aturan tersebut dicocokkan sebelum aturan UFW, dan traffic yang diteruskan juga tidak melewati chain yang difilter UFW. Ikat sisi host ke loopback menggunakan "127.0.0.1:8080:80" dan tempatkan semua layanan publik di belakang reverse proxy pada port 80 dan 443.