Cara Rangkaian Docker Compose Berfungsi
Ketahui cara Docker Compose menguruskan rangkaian lalai, DNS nama servis, mod hos, dan isu port yang memintas UFW. Fahami konfigurasi rangkaian untuk projek anda.
Apa yang dibina oleh Compose sebelum aplikasi anda bermula
Rangkaian Docker Compose bermula dengan satu peraturan: docker compose up mencipta rangkaian peribadi untuk projek tersebut, menyambungkan setiap servis kepadanya, dan membolehkan servis-servis tersebut berhubung antara satu sama lain menggunakan nama servis. Anda tidak perlu menulis satu baris networks: pun untuk mendapatkannya. Kebanyakan kekeliruan mengenai rangkaian Compose berpunca daripada ketidaktahuan bahawa tetapan lalai itu sudah sedia ada.
Berikut adalah fail kecil. Simpannya sebagai compose.yaml di dalam direktori bernama shop.
services:
web:
image: nginx:1.27
ports:
- "8080:80"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: exampleJalankan ia dan lihat apa yang telah dibuat oleh Docker:
docker compose up -d
docker network lsSenarai tersebut kini mengandungi rangkaian bernama shop_default. Compose menamakannya <project>_default, dan nama projek secara lalai mengikut nama direktori dalam huruf kecil. Anda boleh mengubahnya dengan docker compose -p myproject up -d atau dengan name: myproject di peringkat atas fail tersebut. Drivernya ialah bridge, iaitu suis maya di dalam hos. Setiap kontena mendapat alamat pada subnet peribadi, dan trafik keluar diterjemahkan kepada alamat hos semasa keluar.
docker compose down memadamkan rangkaian itu semula. Inilah sebabnya kontena lama daripada projek terdahulu boleh menyebabkan rangkaian kekal terbuka: Docker akan menolak dengan error while removing network: network shop_default has active endpoints, dan penyelesaiannya adalah dengan menghentikan atau membuang kontena yang masih bersambung dengannya.
Jika Compose adalah perkara baharu bagi anda, susun atur fail Compose dan arahan kitaran hayat adalah wajar dibaca terlebih dahulu, kerana segala maklumat di bawah mengandaikan anda sudah tahu cara untuk memulakan dan menghentikan sesuatu projek.
DNS mengikut nama servis adalah bahagian yang sering terlepas pandang oleh pemula
Pada mana-mana rangkaian yang ditentukan pengguna, Docker menjalankan pelayan DNS terbenam yang dilihat oleh setiap kontena pada 127.0.0.11. Ia menyelesaikan nama servis kepada alamat kontena semasa. Jadi web mencapai pangkalan data pada nama hos db, pada port 5432, tanpa sebarang konfigurasi.
docker compose exec web getent hosts dbPerintah itu mencetak baris seperti 172.18.0.2 db. Jika ia tidak mencetak apa-apa, kedua-dua servis tersebut tidak berada pada rangkaian yang sama.
Kesilapan yang hampir semua orang lakukan sekali adalah menggunakan localhost dalam konfigurasi aplikasi. Di dalam kontena, localhost ialah kontena itu sendiri, bukan hos dan bukan servis yang 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?Rentetan sambungan sepatutnya postgresql://postgres:example@db:5432/postgres. Bahagian hos ialah nama servis.
Dua perincian yang menjimatkan masa kemudian hari. Nama diselesaikan kepada apa sahaja yang sedang berjalan sekarang, jadi docker compose up -d --scale web=3 memberikan satu nama dengan tiga alamat, dan klien yang menyimpan cache DNS selama-lamanya akan terikat pada kontena yang sudah mati. Dan rangkaian legasi bridge yang digunakan oleh docker run biasa tanpa --network tidak dapat menyelesaikan nama kontena langsung: kontena di sana hanya mencari satu sama lain melalui alamat IP, dan mereka mencari nama hos luar melalui salinan /etc/resolv.conf hos. Itulah sebabnya nasihat tentang pautan kontena dari tahun 2016 tidak sepadan dengan apa yang anda lihat.
Anda tidak perlukan ports: untuk menyambungkan dua servis
ports: menerbitkan port kontena pada hos. Ia bertujuan untuk trafik yang datang dari luar Docker. Ia tiada kaitan dengan trafik antara servis, yang sudah pun berfungsi merentasi keseluruhan julat port pada rangkaian projek.
Oleh itu, ports: - "5432:5432" yang ditambah oleh ramai orang pada servis pangkalan data mereka tidak memberikan manfaat malah membawa mudarat: ia mendedahkan Postgres pada antaramuka awam pelayan. Padamkannya. Jika anda mahu ia boleh dicapai dari komputer riba anda untuk tujuan migrasi, ikat ia pada loopback dengan "127.0.0.1:5432:5432" dan capai ia melalui SSH tunnel. Perbezaan antara listening socket, published port, dan peraturan firewall dihuraikan dalam cara port dan servis yang mendengar berfungsi pada Linux.
expose: hanyalah dokumentasi di bawah Compose. Ia tidak membuka apa-apa, kerana tiada apa yang ditutup antara kontena dalam rangkaian yang sama.
Bilakah network_mode host sesuai digunakan, dan apakah kosnya
Mod hos menggugurkan ruang nama rangkaian kontena itu sendiri dan membenarkan proses menggunakan antara muka hos secara terus.
services:
probe:
image: alpine:3.20
network_mode: host
command: sleep infinityTerdapat sebab sebenar untuk menginginkan perkara ini. Proses yang perlu melihat trafik siaran (broadcast) atau multicast pada rangkaian tempatan, seperti penemuan peranti untuk pelayan media atau hab automasi rumah, tidak dapat melihatnya dari sebalik bridge, kerana bridge tidak memajukan trafik tersebut ke kontena. Ejen pemantauan yang membaca pembilang antara muka hos memerlukan antara muka hos tersebut. Dan anda melangkau lompatan terjemahan alamat, yang penting pada kadar paket yang tinggi.
Kosnya adalah khusus.
ports: berhenti berfungsi. Docker memberi amaran bahawa port yang diterbitkan (published ports) dibuang apabila menggunakan mod rangkaian hos, dan kontena mengikat apa sahaja yang diikat oleh prosesnya. Dua kontena mod hos yang mahukan port 8080 akan berlanggar, dan yang kedua akan mati dengan bind: address already in use.
Penyelesaian nama mengikut nama servis hilang dalam kedua-dua arah. Kontena tidak berada pada rangkaian projek, jadi ia tidak dapat menyelesaikan db, dan servis lain tidak dapat menyelesaikannya. Ia masih boleh mencapai servis tersebut melalui port yang diterbitkan pada hos, biasanya pada 127.0.0.1. Pada Linux, ia juga boleh mencapai kontena bridge secara terus pada alamat IP kontena tersebut pada mana-mana port yang mendengar, kerana hos mempunyai akses terbuka kepada kontena pada rangkaian bridge-nya dan kontena mod hos berkongsi stack rangkaian hos. Alamat tersebut berubah apabila kontena dicipta semula, jadi port yang diterbitkan pada loopback adalah laluan yang stabil.
Pengasingan hilang. Proses yang mengikat 0.0.0.0 di dalam kontena mod hos sedang mendengar pada setiap antara muka pelayan anda, termasuk yang awam, sama seperti pakej yang dipasang dengan apt. Terdapat satu kelebihan: trafik ini mengikut laluan input biasa, jadi peraturan UFW memang terpakai kepadanya, yang tidak benar bagi port yang diterbitkan.
Mod hos ialah ciri Linux Docker Engine. Docker Desktop menyokongnya hanya dari versi 4.34 ke atas dan hanya selepas anda mendayakannya, dengan had tambahan bahawa kontena tidak boleh mengikat alamat IP hos dan hanya TCP serta UDP dikendalikan. Jika separuh pasukan anda menggunakan pelayan Linux dan separuh lagi menggunakan Docker Desktop, jangkakan fail yang sama berkelakuan berbeza.
Gunakan mod hos apabila anda memerlukan antara muka hos. Jangan gunakannya untuk membaiki masalah sambungan, kerana ia biasanya menggantikan satu masalah dengan masalah yang lebih sukar.
Menyambungkan dua projek Compose dengan rangkaian luaran
Rangkaian yang dicipta oleh satu projek tidak dapat dilihat oleh projek lain. Itulah sebabnya reverse proxy dalam proxy/compose.yaml tidak dapat melihat aplikasi dalam app/compose.yaml, walaupun berada pada pelayan yang sama. Penyelesaiannya ialah rangkaian yang tidak dimiliki oleh mana-mana projek.
Cipta rangkaian tersebut sekali sahaja secara manual:
docker network create edgeKemudian, isytiharkan rangkaian tersebut sebagai external dalam setiap projek. Bahagian proksi:
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: trueBahagian 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 memberitahu Compose untuk menyambung kepada rangkaian sedia ada dan bukannya mencipta rangkaian baharu, serta membiarkannya kekal di situ semasa docker compose down. Kunci name: yang berasingan adalah lebih penting daripada yang kelihatan: tanpanya, Compose akan mencari rangkaian yang bernama tepat seperti edge, manakala dengannya, anda boleh menamakan rangkaian tersebut dengan satu nama dalam fail anda dan nama lain pada hos.
Jika rangkaian tersebut tidak wujud, Compose enggan bermula dan melaporkan bahawa rangkaian telah diisytiharkan sebagai external tetapi tidak dapat ditemui. Cipta rangkaian tersebut terlebih dahulu.
Perhatikan tindakan fail aplikasi dengan internal. Pangkalan data hanya berada pada rangkaian setempat projek tersebut, jadi proksi tidak dapat mencapainya dan hanya app yang boleh. Menambah internal: true di bawah rangkaian akan pergi lebih jauh dengan membuang laluannya ke dunia luar sepenuhnya. Ini adalah tetapan lalai yang baik untuk pangkalan data, dengan satu kos yang perlu diketahui sebelum anda menetapkannya: kontena pada rangkaian dalaman tidak boleh memuat turun apa-apa, jadi entrypoint yang menjalankan apt-get update atau pip install semasa permulaan akan tergantung dan kemudian gagal dengan ralat timeout.
Untuk persediaan lengkap dengan peraturan penghalaan dan sijil, lihat menjalankan beberapa aplikasi di sebalik satu instans Traefik.
Port yang diterbitkan memintas UFW
Ini adalah bahagian rangkaian Compose yang boleh mencetuskan insiden keselamatan. Anda menerbitkan port, anda menyemak bahawa UFW aktif dan menolak segala-galanya kecuali SSH, namun servis tersebut masih boleh dicapai dari internet.
sudo ufw status
curl http://203.0.113.10:8080UFW menyatakan bahawa port tersebut disekat. curl tetap memaparkan halaman tersebut. Tiada apa-apa yang rosak. Docker menulis peraturan terjemahan alamat dan penghantaran (forwarding) miliknya sendiri terus ke dalam iptables, dan trafik ke port kontena yang diterbitkan dihantar terus ke kontena dan bukannya dihantar ke hos. Oleh itu, trafik tersebut tidak pernah melalui rantaian (chain) yang diuruskan oleh UFW untuk trafik yang ditujukan kepada hos tempatan. Peraturan Docker juga dipadankan sebelum peraturan UFW.
Penyelesaian ringkasnya adalah dengan menerbitkan port hanya di tempat yang anda perlukan:
ports:
- "127.0.0.1:8080:80"Ini mengikat bahagian hos kepada loopback, supaya port tersebut hanya boleh dicapai dari pelayan itu sendiri dan melalui terowong SSH, dan tidak boleh dicapai dari tempat lain. Letakkan titik masuk awam di belakang reverse proxy yang menerbitkan port 80 dan 443 secara sengaja. Penjelasan penuh, termasuk rantaian DOCKER-USER bagi kes di mana anda perlu menapis port yang diterbitkan, terdapat dalam mengapa Docker menerbitkan port terus memintas UFW dan cara membaikinya.
Cara menyahpepijatnya dalam empat perintah
Mulakan dengan bertanya rangkaian mana yang sebenarnya digunakan oleh setiap kontena:
docker network inspect shop_defaultBlok Containers menyenaraikan setiap kontena yang dilampirkan berserta alamatnya. Servis yang tiada dalam senarai itu berada pada rangkaian yang berbeza, atau dalam mod hos, atau tidak berjalan.
Uji penyelesaian nama daripada kontena sementara yang dilampirkan pada rangkaian yang sama, supaya anda tidak memerlukan sebarang alatan di dalam imej 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 penyelesaian nama atau keahlian rangkaian. nslookup yang berjaya sementara nc gagal bermakna nama dan rangkaian adalah baik dan masalahnya ialah sambungan itu sendiri. Jika nc melaporkan Connection refused, punca biasa ialah servis tidak mendengar pada port tersebut, atau sedang mendengar pada 127.0.0.1 di dalam kontena sendiri dan bukannya 0.0.0.0. Yang terakhir ini biasa berlaku dengan pelayan pembangunan, dan pembaikannya adalah pada alamat ikat (bind address) aplikasi, bukan pada Docker. Jika nc tergantung dan tamat masa (timeout), cari sesuatu yang menggugurkan trafik dalam perjalanan, seperti peraturan firewall, atau kontena yang dijeda atau tidak menjawab.
Satu lagi kegagalan yang kelihatan seperti pepijat Docker. Jika kontena boleh bercakap antara satu sama lain tetapi tidak dapat mencapai mesin pada rangkaian pejabat atau VPN anda, subnet Docker mungkin bertindih dengan rangkaian tersebut. Docker memperuntukkan daripada 172.17.0.0/16 ke atas secara lalai. Alihkan pool dalam /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.200.0.0/16", "size": 24 }
]
}Kemudian jalankan sudo systemctl restart docker dan cipta semula rangkaian yang terjejas, kerana rangkaian sedia ada mengekalkan subnet yang digunakan semasa ia dicipta.
FAQ
Mengapa kontena saya tidak dapat berhubung antara satu sama lain menggunakan nama servis?
Kontena tersebut tidak berada pada rangkaian yang sama. Compose meletakkan setiap servis pada <project>_default secara automatik, tetapi sebaik sahaja anda menambah senarai networks: pada sesuatu servis, senarai tersebut menjadi set rangkaian lengkap untuk servis itu dan rangkaian lalai tidak lagi digunakan. Jalankan docker network inspect <network> dan pastikan kedua-dua kontena muncul dalam blok Containers. Pastikan juga tiada servis yang menggunakan network_mode: host, kerana kontena dalam mod host tidak berada pada mana-mana rangkaian Docker dan tidak dapat menyelesaikan nama servis.
Adakah saya perlu menerbitkan (publish) port untuk satu servis berhubung dengan servis lain?
Tidak. Pada rangkaian Compose, setiap port bagi setiap kontena boleh dicapai oleh kontena lain dalam rangkaian yang sama. ports: hanya wujud untuk mendedahkan kontena kepada trafik dari luar Docker, dan expose: hanyalah dokumentasi. Menerbitkan port pangkalan data adalah tabiat biasa yang mahal harganya, kerana ia meletakkan pangkalan data pada antaramuka awam pelayan anda.
Apakah perbezaan antara rangkaian bridge dan host?
Bridge memberikan kontena ruang nama rangkaian dan alamatnya sendiri pada suis maya, dengan resolusi nama automatik antara kontena serta trafik keluar yang diterjemahkan. Host memberikan kontena akses terus kepada stack rangkaian hos: tiada alamat berasingan, tiada resolusi mengikut nama servis, tiada penerbitan port, dan tiada pengasingan daripada pendengar (listener) lain pada hos. Bridge adalah tetapan lalai dan pilihan yang tepat kecuali jika proses tersebut memerlukan antaramuka hos.
Bagaimanakah cara saya menyambungkan kontena daripada dua fail Compose yang berbeza?
Cipta rangkaian kongsi dengan docker network create edge, kemudian isytiharkannya dalam kedua-dua fail dengan external: true dan lampirkan servis yang perlu berhubung. Compose tidak akan mencipta atau memadam rangkaian tersebut. Jika anda melangkau langkah penciptaan, Compose akan enggan bermula dan melaporkan bahawa rangkaian diisytiharkan sebagai luaran tetapi tidak ditemui.
Mengapa kontena saya boleh dicapai dari internet sedangkan UFW menyekat port tersebut?
Ini kerana port yang diterbitkan dikendalikan oleh peraturan forwarding yang ditambah oleh Docker ke dalam iptables. Peraturan tersebut dipadankan sebelum peraturan UFW, dan trafik yang diforward tidak melalui rantaian yang ditapis oleh UFW. Ikat (bind) bahagian hos kepada loopback dengan "127.0.0.1:8080:80" dan letakkan sebarang perkhidmatan awam di belakang reverse proxy pada port 80 dan 443.