Cara Halakan Kontena Docker Melalui VPN Gluetun
Apabila anda menggunakan namespace rangkaian Gluetun, port kontena akan hilang. Ketahui sebab ia berlaku dan dapatkan fail Docker Compose yang betul untuk menyelesaikannya.
Mengapa port hilang apabila anda menghalakan kontena Docker melalui VPN
Untuk menghalakan kontena Docker melalui VPN, anda memberikan terowong kepada satu kontena, kemudian menyambungkan kontena lain ke namespace rangkaiannya menggunakan network_mode: "service:gluetun". Penyambungan itulah bahagian yang mengejutkan pengguna. Kontena yang disambungkan tidak lagi mempunyai rangkaian sendiri, jadi port yang diterbitkan dan nama servis Docker miliknya akan hilang bersamanya. Terbitkan port pada kontena VPN sebaliknya, dan kontena lain akan mencapai aplikasi tersebut pada nama kontena VPN berkenaan.
Tinggalkan blok ports: pada kontena yang disambungkan dan Docker akan menolak untuk menciptanya sama sekali:
Error response from daemon: conflicting options: port publishing and the container type network modeAlat yang digunakan di sini ialah Gluetun, iaitu kontena yang bersambung dengan pembekal VPN (virtual private network) komersial melalui WireGuard atau OpenVPN dan membawa firewall sendiri. Release v3.41.3 adalah versi semasa setakat Ogos 2026. Contoh-contoh ini menggunakan Mullvad dengan WireGuard, jadi anda memerlukan akaun dan kunci daripada pembekal anda. Jika anda lebih suka menamatkan terowong pada perkakasan milik anda sendiri, menjalankan pelayan WireGuard anda sendiri pada VPS membina hujung satu lagi, dan wg-easy dalam Docker membungkusnya dalam antara muka web.
Fungsi sebenar network_mode: "service:gluetun"
Setiap bekas Docker biasanya mendapat ruang nama rangkaian (network namespace) sendiri: antara muka, jadual penghalaan, peraturan firewall dan soket pendengar yang tersendiri. Mod service: melangkau langkah tersebut dan memulakan bekas di dalam ruang nama gluetun. Satu ruang nama bermaksud satu alamat IP, dan ini mengubah enam perkara.
- Aplikasi tidak mempunyai alamat sendiri. Alamatnya ialah alamat gluetun.
- Aplikasi tidak disambungkan kepada mana-mana rangkaian Docker, jadi nama servisnya tidak pernah didaftarkan dan tidak pernah diselesaikan (resolve). Bekas lain mesti menggunakan
gluetun. - Bekas di dalam ruang nama yang sama berhubung antara satu sama lain melalui
localhost. - Dua bekas dalam satu ruang nama tidak boleh mendengar pada port yang sama. Dokumentasi Gluetun menyatakan perkara ini dengan jelas: tiada penyelesaian (workaround) untuknya.
- Keupayaan (capabilities) adalah milik bekas, bukan ruang nama. Gluetun memegang
NET_ADMINdan/dev/net/tunkerana ia mencipta antara muka terowong. Bekas yang dilampirkan tidak mewarisi keupayaan tersebut. - Compose akan menolak sebarang fail di mana satu servis menetapkan kedua-dua
network_modedannetworks. Lampirkan gluetun pada rangkaian anda, dan aplikasi tersebut akan mengikutinya.
Memulakan semula gluetun akan memutuskan sambungan semua yang dilampirkan kepadanya. Itu adalah tingkah laku yang didokumentasikan, dan itulah sebabnya gluetun memulakan semula proses VPN di dalam bekas dan bukannya keluar apabila sambungan gagal. Selepas anda memulakan semula atau mencipta semula gluetun, mulakan semula bekas yang dilampirkan kepadanya.
Fail compose yang berfungsi
services:
gluetun:
image: qmcgaw/gluetun:v3
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- SERVER_CITIES=Amsterdam
- TZ=Europe/Amsterdam
env_file:
- ./gluetun.env
volumes:
- ./gluetun:/gluetun
ports:
- 127.0.0.1:8080:8080/tcp
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun"
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
- WEBUI_PORT=8080
volumes:
- ./qbittorrent:/config
- ./downloads:/downloads
depends_on:
gluetun:
condition: service_healthy
restart: unless-stoppedTag :v3 ialah keluaran stabil terbaharu dalam siri v3. Tag :latest menghala ke commit terakhir cawangan master, iaitu versi pembangunan terkini, jadi jangan tetapkan :v3 pada mesin yang anda tidak mahu nyahpepijat pada hari Selasa.
WEBUI_PORT=8080 mestilah sepadan dengan port yang diterbitkan, kerana qBittorrent terikat di dalam namespace gluetun dan peraturan penerbitan menghantar trafik hos ke port 8080 di sana. Jika anda menukar satu nombor tanpa menukar nombor yang satu lagi, port tersebut tidak akan memberikan sebarang respons. 127.0.0.1:8080:8080 mengekalkan antara muka web pada alamat loopback hos. 8080:8080 biasa akan menerbitkan pada setiap antara muka dan menulis peraturan firewallnya sendiri, yang merupakan cara port yang diterbitkan Docker terlepas terus daripada ufw.
Jalankan servis, kemudian semak mengikut urutan ini:
docker compose up -d
docker compose ps
docker compose logs gluetun | tail -30docker compose ps sepatutnya menunjukkan gluetun sebagai healthy dan qbittorrent sebagai running. Kemudian, sahkan alamat keluar dari dalam namespace tersebut, iaitu semakan yang menentukan segala-galanya:
docker run --rm --network=container:gluetun alpine:3.22 sh -c "apk add wget && wget -qO- https://ipinfo.io"Medan ip dalam JSON tersebut sepatutnya merupakan alamat pembekal VPN anda. Jika ia adalah alamat pelayan anda sendiri, aplikasi tersebut tidak berada di dalam tunnel, dan tiada apa-apa di bawah ini akan berfungsi seperti yang diterangkan.
Simpan kunci di luar fail compose
gluetun.env menyimpan kelayakan, dan ia tidak dimasukkan ke dalam git:
WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
WIREGUARD_ADDRESSES=10.64.222.21/32Kedua-dua nilai diperoleh daripada fail konfigurasi WireGuard yang anda jana dalam kawasan akaun pembekal anda. Tetapkan fail tersebut kepada mod 600. Bersikap jujur tentang faedah yang diperoleh: kunci tersebut tidak berada dalam repositori anda, namun docker inspect gluetun masih mencetak setiap pemboleh ubah persekitaran kepada sesiapa sahaja yang boleh mencapai soket Docker. Fail persekitaran dan rahsia dalam Docker Compose membincangkan pilihan yang lebih kukuh.
Cara kontena di luar terowong berhubung dengan kontena di dalamnya
Kedua-dua arah berfungsi, dan setiap satu menggunakan nama yang berbeza. Kedua-dua kontena memerlukan rangkaian Docker yang dikongsi, iaitu rangkaian gluetun, memandangkan kontena yang dilampirkan tidak mempunyai rangkaiannya sendiri. Cara rangkaian Docker Compose disambungkan merangkumi tetapan lalai.
Dari luar ke dalam, gunakan nama gluetun dan port yang didengar oleh aplikasi tersebut. Kontena reverse proxy mencapai antara muka web qBittorrent pada gluetun:8080. Tiada entri ports: diperlukan untuk itu, kerana trafik antara kontena kekal dalam rangkaian Docker dan tidak pernah menyentuh port hos.
Dari dalam ke luar, gunakan nama servis kontena yang satu lagi, contohnya postgres:5432. Gluetun telah menyelesaikan nama kontena lain dari dalam namespace-nya sejak v3.41, jadi tetapkan versi tersebut atau yang lebih baharu jika nama gagal diselesaikan.
Firewall gluetun menentukan siapa yang dibenarkan membuka sambungan kepadanya. Trafik daripada rangkaian Docker gluetun sendiri dibenarkan. Pelanggan pada subnet yang berbeza, komputer riba pada LAN anda atau kontena pada rangkaian bridge yang berasingan, akan digugurkan (dropped) sehingga anda menamakan subnet tersebut:
FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24Maksud yang didokumentasikan adalah tepat: subnet yang dipisahkan dengan koma yang dibenarkan untuk diakses oleh Gluetun dan kontena yang berkongsi stack rangkaiannya.
Sambungan masuk dari internet adalah masalah yang berasingan. Rakan setara (peers) klien torrent tiba dari sisi VPN, jadi menerbitkan port 6881 pada hos tidak memberi kesan kepada mereka. Anda memerlukan port yang diforward daripada pembekal anda, dan port tersebut disenaraikan dalam FIREWALL_VPN_INPUT_PORTS, yang membenarkan port dari sisi pelayan VPN. Ini adalah bahagian yang sering dibiarkan rosak dalam media stack yang dibina dengan Docker Compose.
Kill switch: apa yang berlaku apabila tunnel terputus
Corak ini menjadi kompleks apabila berlaku kegagalan. Kontena yang dilampirkan tidak mempunyai laluan kedua. Satu-satunya laluan keluar dari mesin tersebut ialah namespace yang dikongsi, jadi apabila tunnel terputus, tiada laluan sandaran. Firewall Gluetun menguatkuasakan peraturan yang sama dari sisi lain: trafik keluar mesti melalui tunnel atau ke endpoint pelayan VPN, dan semua trafik lain akan digugurkan. Tiada tempoh di mana paket bocor keluar melalui antara muka biasa semasa klien membuat sambungan semula.
Gluetun memantau sambungannya sendiri. Setiap minit, ia menghantar ICMP echo (ping) ke alamat dalam HEALTH_ICMP_TARGET_IPS, yang secara lalai ditetapkan kepada 1.1.1.1,8.8.8.8. Setiap lima minit, ia melakukan dial TCP dan TLS (transport layer security) penuh ke HEALTH_TARGET_ADDRESSES, yang secara lalai ditetapkan kepada cloudflare.com:443,github.com:443. Apabila ujian ini gagal, ia memulakan semula VPN di dalam kontena dan merekodkannya:
WARN [vpn] restarting VPN because it failed to pass the healthcheck: periodic check: dialing: dial tcp4: lookup cloudflare.com: i/o timeoutBaca log kontena yang dilampirkan dengan mengambil kira urutan tersebut. Baris seperti connection refused, operation not permitted dan i/o timeout di dalam aplikasi adalah kesan daripada tunnel yang terputus, bukan puncanya. Dokumentasi Gluetun menyatakan perkara ini secara jelas, kerana pengguna sering melaporkan kesan tersebut dan menghabiskan masa berjam-jam untuk menyiasatnya.
HEALTH_RESTART_VPN=on ialah tetapan lalai dan harus dibiarkan aktif. Matikan tetapan ini hanya semasa anda menyahpepijat kegagalan tertentu, kerana jika ia dimatikan, tunnel yang terputus akan kekal terputus.
Susunan: menghentikan stack daripada bermula sebelum tunnel aktif
Imej ini menyertakan Docker healthcheck:
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /gluetun-entrypoint healthcheckPerintah tersebut menjalankan salinan gluetun kedua yang berjangka pendek, yang menanyakan pelayan kesihatan bagi salinan yang sedang berjalan pada http://127.0.0.1:9999/. Tunnel yang berfungsi akan menjawab 200 OK. Tunnel yang rosak akan menjawab 500 Internal server error dengan rentetan ralat, dan kontena akan ditandakan sebagai tidak sihat selepas satu kegagalan.
condition: service_healthy ialah perkara yang menunggu untuk itu. depends_on: [gluetun] biasa hanya menunggu kontena untuk bermula, yang berlaku beberapa saat sebelum jabat tangan (handshake) selesai, jadi aplikasi bermula dalam rangkaian yang mati dan sering berputus asa pada percubaan sambungan pertamanya. Healthcheck dalam Docker Compose menerangkan sintaks dan medan masa tersebut.
Satu had sering memerangkap pengguna. Compose menilai syarat tersebut sekali sahaja, semasa ia mencipta kontena. Ia tidak menghentikan atau memulakan semula aplikasi kemudian jika gluetun menjadi tidak sihat. Pemulihan automatik dalaman gluetun menangani kes tersebut, itulah sebabnya ia memulakan semula proses VPN dan bukannya kontena.
Semak kebocoran DNS sebelum anda mempercayai persediaan tersebut
DNS (domain name system) merupakan kebocoran yang tetap berlaku walaupun terowong telah dikonfigurasikan dengan betul. Gluetun menjalankan resolvernya sendiri di dalam namespace dan memajukan pertanyaan melalui DoT (DNS over TLS) ke Cloudflare secara lalai: DNS_UPSTREAM_RESOLVER_TYPE=dot dan DNS_UPSTREAM_RESOLVERS=cloudflare. Biarkan kedua-duanya tanpa diubah supaya carian anda disulitkan dan dihantar melalui terowong.
Tetapan yang menyebabkan masalah ini ialah DNS_UPSTREAM_PLAIN_ADDRESSES. Pengguna cenderung memilih tetapan ini apabila sesuatu nama gagal diselesaikan dan mereka mahu penghala (router) atau resolver pembekal perkhidmatan mereka yang menjawab pertanyaan tersebut. Dokumentasi Gluetun menyatakan kosnya dengan jelas: semua trafik DNS tidak akan melalui terowong VPN dan akan bocor keluar daripadanya. Trafik anda kekal peribadi, namun senarai nama hos anda tidak. Versi WireGuard bagi kesilapan yang sama dibincangkan dalam DNS yang berhenti menyelesaikan nama hos melalui terowong WireGuard.
Untuk mengujinya, tetapkan HTTPPROXY=on pada gluetun dan terbitkan 8888:8888/tcp, kemudian halakan pelayar ke proksi tersebut dan muatkan ujian kebocoran DNS. Hasilnya harus memaparkan nama pembekal anda atau Cloudflare, dan bukannya penghala rumah anda. Dokumentasi Gluetun sendiri memberi amaran bahawa sesetengah ujian kebocoran melaporkan hasil yang ganjil, kerana resolver di dalam namespace tersebut merupakan perantara caching tempatan dan bukannya pelayan yang menjawab pertanyaan akhir. Anggap negara yang salah atau resolver ISP anda sendiri sebagai petunjuk sebenar kebocoran.
Menambah Tailscale di samping sidecar VPN, dan yang mana satu akan menang
Tailscale ialah rangkaian tindanan (overlay network) yang dibina berasaskan WireGuard untuk mencapai mesin anda sendiri, dan pengguna menjalankannya di samping VPN pembekal untuk mengekalkan laluan pentadbir ke dalam stack. Kedua-duanya jarang bertembung, atas sebab yang perlu difahami. Dokumentasi Tailscale menyatakan tetapan lalai: ia bertindak sebagai rangkaian tindanan, ia hanya menghalakan trafik antara peranti yang menjalankan Tailscale, dan ia tidak menyentuh trafik internet awam anda.
Jadi jawapannya bergantung pada satu tetapan.
- Tailscale dalam kontena sendiri, konfigurasi lalai: ia tidak pernah melihat trafik keluar aplikasi. Gluetun membawa kesemuanya. Tailscale mencapai aplikasi pada
gluetun:8080, sama seperti mana-mana kontena luar yang lain. - Tailscale dilampirkan pada namespace gluetun dengan
network_mode: "service:gluetun": ia memerlukancap_addsendiri baginet_admindannet_raw, kerana keupayaan (capabilities) tidak disertakan bersama namespace tersebut. Dalam mod rangkaian ruang pengguna (userspace networking) lalai,TS_USERSPACEdihidupkan, tailscaled tidak mencipta sebarang antara muka dan berfungsi sebagai proksi SOCKS5 atau HTTP, jadi ia tidak boleh mengubah penghalaan. Gluetun masih membawa segala-galanya. - Perkara yang sama, dengan
TS_USERSPACE=false: tailscaled mencipta peranti tunnel dan memasang laluan, tetapi hanya untuk julat tailnet100.64.0.0/10ditambah sebarang laluan subnet yang anda iklankan denganTS_ROUTES. Trafik awam masih keluar melalui gluetun. - Mana-mana yang di atas dengan exit node dipilih,
sudo tailscale set --exit-node=<exit-node-ip>: Tailscale menuntut laluan lalai dan menang. Jangan gabungkan itu dengan gluetun. Satu laluan lalai, satu pemilik.
Satu kesan sampingan dapat dilihat apabila Tailscale berjalan di dalam tunnel. Peer-nya melihat alamat pembekal VPN, jadi jangkakan ia akan lebih kerap kembali kepada relay. tailscale status menunjukkan relay "..." di sebelah peer dan bukannya direct apabila perkara itu berlaku. Sambungan berfungsi dan ia lebih perlahan. Jika rangkaian tindanan adalah satu-satunya perkara yang anda perlukan, perbezaan antara WireGuard biasa dan Tailscale adalah titik permulaan yang lebih baik.
Perkara yang gagal, dan mesej yang akan anda lihat
Docker enggan mencipta container aplikasi. Error response from daemon: conflicting options: port publishing and the container type network mode bermaksud blok ports: masih berada pada servis yang dilampirkan. Pindahkan ia ke gluetun.
Compose enggan menerima keseluruhan fail. Sesuatu servis tidak boleh menetapkan kedua-dua network_mode dan networks serentak. Letakkan rangkaian pada gluetun.
Container lain tidak dapat menyelesaikan (resolve) aplikasi. curl: (6) Could not resolve host: qbittorrent adalah kelakuan yang betul, kerana container yang dilampirkan tidak menyertai sebarang rangkaian dan tidak mendaftarkan sebarang nama. Gunakan gluetun dan port tersebut.
Container kedua yang dilampirkan tidak mahu bermula. Dua proses dalam satu namespace tidak boleh mengikat (bind) port yang sama, dan proses yang gagal akan melaporkan bahawa alamat tersebut sudah digunakan. Tukar port dalaman aplikasi, atau jalankan gluetun kedua.
Aplikasi tiada rangkaian selepas anda menyentuh gluetun. Memulakan semula atau mencipta semula gluetun akan memutuskan ketersambungan untuk semua yang dilampirkan kepadanya. Mulakan semula container tersebut.
Halaman kecil dimuatkan tetapi halaman besar tergantung. Itu adalah isu MTU (maximum transmission unit). Terowong menambah overhead, dan sesuatu pada laluan tersebut menggugurkan paket yang terlalu besar tanpa menghantar ralat kembali. Rendahkan WIREGUARD_MTU, cuba 1400, kemudian 1320.
Gluetun tidak pernah menjadi sihat (healthy). Semakan permulaan menamakan suspek pertama: WARN [vpn] restarting VPN because it failed to pass the healthcheck: startup check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout. Periksa sama ada kunci telah tamat tempoh, kemudian sama ada senarai pelayan sudah lapuk, dan seterusnya sama ada firewall hos anda menyekat UDP keluar.
FAQ
Mengapa port yang diterbitkan oleh kontena saya berhenti berfungsi di sebalik Gluetun?
Kerana network_mode: "service:gluetun" meletakkan kontena di dalam namespace rangkaian gluetun, dan satu namespace hanya mempunyai satu alamat IP serta satu set port yang mendengar. Aplikasi tersebut terus mendengar, tetapi peraturan penerbitan (publish rule) mesti berada pada kontena yang memiliki namespace tersebut. Pindahkan senarai ports: ke servis gluetun. Jika anda membiarkannya pada servis yang dilampirkan, Docker tidak akan menciptanya: Error response from daemon: conflicting options: port publishing and the container type network mode.
Bagaimanakah cara saya mencapai kontena di dalam terowong VPN daripada kontena di luarnya?
Gunakan nama servis gluetun dan port yang didengar oleh aplikasi tersebut, contohnya gluetun:8080. Kontena yang dilampirkan tidak mempunyai rangkaian Docker sendiri, jadi namanya tidak akan dapat diselesaikan (resolve). Tiada apa-apa yang perlu diterbitkan untuk trafik antara kontena. Bagi arah sebaliknya, kontena di dalam namespace boleh mencapai kontena luar melalui nama servisnya, seperti postgres:5432, pada Gluetun v3.41 dan versi lebih baharu. Pelanggan pada subnet berbeza, seperti komputer riba pada LAN anda, akan disekat oleh firewall gluetun sehingga anda menambah subnet tersebut ke dalam FIREWALL_OUTBOUND_SUBNETS.
Adakah Gluetun berfungsi sebagai kill switch apabila VPN terputus?
Ya, atas dua sebab serentak. Kontena yang dilampirkan tidak mempunyai laluan (route) kecuali laluan di dalam namespace yang dikongsi, jadi terowong yang mati menyebabkan ia tidak mempunyai laluan keluar dari mesin tersebut. Firewall Gluetun juga hanya membenarkan trafik keluar melalui terowong dan ke titik akhir pelayan VPN. Gluetun kemudian memulakan semula VPN secara dalaman, mencatatkan WARN [vpn] restarting VPN because it failed to pass the healthcheck, dan bukannya keluar (exit), kerana setiap kontena yang dilampirkan akan kehilangan rangkaiannya apabila gluetun sendiri dimulakan semula.
Tailscale dan Gluetun dalam stack yang sama: yang mana satu membawa trafik keluar?
Gluetun, dalam setiap konfigurasi kecuali satu. Tailscale secara lalai hanya menghalakan trafik antara peranti dalam tailnet anda dan membiarkan trafik awam tidak terganggu. Dalam mod userspace lalai imej kontena, ia tidak mencipta sebarang antara muka (interface), jadi ia tidak boleh menjejaskan penghalaan. Dengan TS_USERSPACE=false, ia hanya memasang laluan untuk 100.64.0.0/10 dan subnet yang anda iklankan. Pengecualiannya ialah exit node: sudo tailscale set --exit-node=<exit-node-ip> menjadikan Tailscale sebagai laluan lalai, dan dalam keadaan itu ia akan mengambil alih. Pilih satu produk untuk mengawal laluan lalai daripada menyusun kedua-duanya sekali.