SSD Nodes Learn 🎉 VPS dari $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-13

Cara Capai Host dan Kontena Lain Melalui Gluetun

Kontena dalam rangkaian Gluetun tiada antara muka rangkaian sendiri. Ketahui cara terbitkan port pada Gluetun dan hadkan akses subnet untuk trafik luar terowong VPN anda.

Apa yang berlaku apabila kontena menyertai rangkaian gluetun

Kontena yang menetapkan network_mode: service:gluetun tidak mempunyai antara muka rangkaiannya sendiri. Ia menyertai ruang nama rangkaian (network namespace) gluetun, jadi penerbitan port dan peraturan firewall bukan lagi sifat kontena tersebut, sebaliknya menjadi sifat servis gluetun. Setiap jawapan di bawah berpunca daripada fakta tersebut.

Ruang nama rangkaian ialah salinan peribadi kernel bagi tindanan rangkaian: antara muka sendiri, jadual penghalaan sendiri, peraturan firewall sendiri dan soket pendengar sendiri. Docker memberikan setiap kontena satu ruang nama secara lalai. Apabila anda menulis network_mode: service:gluetun, Docker melangkau langkah tersebut dan meletakkan kontena baharu di dalam ruang nama yang sudah dimiliki oleh gluetun. Kontena tersebut mengekalkan sistem failnya sendiri dan fail /etc/hosts miliknya sendiri, dan fail kedua itu penting kemudian nanti.

Anda boleh melihatnya secara terus.

docker inspect -f '{{.HostConfig.NetworkMode}}' qbittorrent

Perintah itu mencetak container: diikuti dengan ID kontena gluetun, sedangkan kontena biasa akan mencetak bridge. Panduan ini menyambung daripada menghalakan trafik Docker melalui VPN dengan gluetun: terowong berfungsi, dan kini tiada apa-apa yang boleh berhubung dengan kontena tersebut.

Terbitkan port pada gluetun, bukan pada aplikasi

Tinggalkan blok ports: pada servis yang menetapkan network_mode dan Docker akan menolak untuk mencipta kontena tersebut:

Error response from daemon: conflicting options: port publishing and the container type network mode

Sebabnya adalah jelas. Menerbitkan port bermakna menambah peraturan NAT (network address translation) yang menghalakan port hos ke dalam ruang nama rangkaian kontena, dan kontena ini tidak mempunyainya. Pindahkan pemetaan tersebut ke servis gluetun. Nombor port tidak berubah, kerana aplikasi masih mendengar pada port tersebut di dalam ruang nama yang dikongsi.

services:
  gluetun:
    ports:
      - "8080:8080/tcp"   # qBittorrent web UI

  qbittorrent:
    network_mode: "service:gluetun"
    # no ports: block here

Blok expose: pada servis yang bergantung juga tidak berguna, dan blok networks: di sana merupakan halangan mutlak: Compose melaporkan bahawa servis mengisytiharkan network_mode dan networks yang saling eksklusif, dan enggan memuatkan fail tersebut sama sekali.

Satu akibat akan timbul kemudian. Setiap kontena dalam ruang nama berkongsi ruang port yang tunggal, jadi dua aplikasi yang kedua-duanya menggunakan 8080 sebagai lalai akan bertembung, dan aplikasi kedua yang cuba bermula akan gagal dengan ralat address already in use. Tukar salah satu daripadanya dalam konfigurasi masing-masing, contohnya pemboleh ubah WEBUI_PORT pada imej qBittorrent LinuxServer, kemudian terbitkan nombor baharu tersebut pada gluetun.

Bagaimanakah kontena di sebalik gluetun berhubung antara satu sama lain?

Di dalam namespace, kontena tersebut sudah berkongsi antara muka loopback. Kontena di sebalik gluetun mencapai kontena saudaranya pada 127.0.0.1:<port> tanpa melibatkan sebarang rangkaian Docker.

Dari luar namespace, kontena tersebut tidak mempunyai nama. DNS terbenam Docker menyelesaikan nama servis kepada alamat servis tersebut pada rangkaian yang ditentukan pengguna, manakala kontena ini tidak mempunyai alamat pada mana-mana rangkaian. Oleh itu, kontena biasa seperti Sonarr tidak mencapai klien torrent pada http://qbittorrent:8080. Ia mencapainya pada http://gluetun:8080, kerana soket tersebut sedang mendengar dalam namespace gluetun, pada alamat gluetun. Perkara ini mengejutkan mereka yang mengetahui cara rangkaian Docker Compose dan nama servis berfungsi dan menjangkakan penamaan biasa digunakan. Ia juga berfungsi tanpa menerbitkan apa-apa ke hos, memandangkan kedua-dua kontena berada pada rangkaian Compose yang sama.

Periksa DNS sebelum anda menyahpepijat perkara lain. Gluetun menjalankan resolvernya sendiri dan menulis semula /etc/resolv.conf dalam kontena miliknya sendiri, tetapi /etc/resolv.conf adalah fail bagi setiap kontena, jadi fail yang ditulis oleh gluetun bukanlah fail yang dibaca oleh aplikasi anda.

docker exec qbittorrent cat /etc/resolv.conf

Bagaimanakah cara untuk mencapai servis yang berjalan pada hos Docker?

Gunakan host.docker.internal. Ia memerlukan dua tetapan di dua tempat berbeza, kerana terdapat dua perkara berbeza yang tidak berfungsi.

Nama perlu diletakkan dahulu. /etc/hosts adalah mengikut kontena, jadi entri extra_hosts perlu diletakkan pada kontena aplikasi, bukan pada gluetun.

  prowlarr:
    network_mode: "service:gluetun"
    extra_hosts:
      - "host.docker.internal:host-gateway"

host-gateway ialah nilai khas yang digantikan oleh Docker dengan alamat dalaman hos itu sendiri. Pada pemasangan Docker Linux biasa, ini adalah alamat jambatan docker0, yang biasanya 172.17.0.1. Sahkan alamat anda dengan ip -4 addr show docker0 pada VPS. Docker Desktop menyelesaikan nama ini secara automatik, itulah sebabnya panduan yang ditulis pada komputer riba melangkau baris extra_hosts dan fail yang sama kemudiannya gagal pada pelayan.

Laluan perlu diletakkan kedua. Menambah nama hanya memberitahu kontena alamat mana yang perlu digunakan. Paket tersebut masih keluar melalui laluan lalai gluetun, iaitu terowong, dan tembok api gluetun akan menggugurkannya. Simptomnya ialah sambungan yang tergantung dan kemudian tamat masa, bukannya sambungan yang ditolak. Penolakan bermaksud paket telah sampai dan sesuatu menjawab tidak. Tamat masa bermaksud paket tidak pernah sampai.

  gluetun:
    environment:
      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32

Kemudian, pastikan servis hos benar-benar mendengar pada alamat tersebut. Pelayan PostgreSQL yang terikat hanya pada 127.0.0.1 tidak boleh dicapai dari mana-mana kontena, sama ada melalui terowong atau tidak, kerana 127.0.0.1 di dalam ruang nama (namespace) adalah loopback ruang nama itu sendiri. Ikat ia pada 172.17.0.1 sebaliknya: ia menerima sambungan daripada kontena sambil kekal di luar antara muka awam. Sahkan dengan ss -lntp | grep 5432 pada hos.

Perkara yang sebenarnya diubah oleh FIREWALL_OUTBOUND_SUBNETS

Dokumentasi gluetun menerangkannya sebagai subnet yang dipisahkan dengan koma yang dibenarkan untuk diakses oleh gluetun dan kontena yang berkongsi tindanan rangkaiannya, serta menyatakan bahawa ia melibatkan perubahan pada firewall dan penghalaan. Kedua-dua bahagian ini adalah penting. Gluetun menambah laluan untuk setiap subnet yang disenaraikan melalui gateway bridge Docker, supaya paket untuk alamat tersebut keluar melalui eth0 dan bukannya melalui terowong. Ia juga membuka firewall untuk subnet tersebut, kerana gluetun akan menggugurkan trafik keluar yang tidak menuju ke pelayan VPN.

Tulis nilai tersebut tanpa ruang selepas koma.

      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,192.168.1.0/24,100.64.7.9/32

Terdapat dua sifat yang mudah terlepas pandang. Ini adalah tetapan peringkat namespace, jadi ia terpakai kepada setiap kontena di belakang gluetun, bukan hanya kepada kontena yang anda fikirkan. Selain itu, ia hanya untuk trafik keluar: ia mengawal sambungan yang dimulakan oleh kontena. Sambungan yang tiba di port yang diterbitkan melalui laluan yang berbeza dan tidak memerlukan kemasukan di sini.

Mengakses UI web daripada peer Tailscale

Tailscale memberikan setiap mesin alamat dalam 100.64.0.0/10, iaitu julat yang dikhaskan untuk carrier grade NAT. Kedua-dua arah memerlukan kaedah yang berbeza.

Arah masuk adalah yang paling mudah. Menerbitkan 8080:8080 pada gluetun akan mengikat port tersebut pada semua alamat hos, dan antara muka tailscale0 hos adalah salah satu daripadanya, jadi peer membuka http://<machine-name>:8080 dan mencapai container tersebut. Gluetun tidak memainkan peranan dalam laluan itu, kerana peraturan NAT Docker berada pada hos, di luar namespace.

Untuk menjadikan UI hanya boleh dicapai melalui tailnet, ikat port yang diterbitkan kepada alamat Tailscale hos dan bukannya kepada semua alamat.

    ports:
      - "100.101.102.103:8080:8080/tcp"

Cari alamat tersebut dengan tailscale ip -4 pada hos. Pengikatan adalah kawalan yang lebih kukuh daripada peraturan firewall dalam situasi ini, kerana port tersebut tidak dibuka langsung pada antara muka awam. Ia juga mengelakkan masalah dalam Docker menerbitkan port terus melepasi ufw.

Arah keluar adalah tempat FIREWALL_OUTBOUND_SUBNETS kembali. Jika container perlu memanggil peer, tambahkan alamat peer tersebut, dan utamakan /32 bagi setiap peer berbanding keseluruhan /10. Nama MagicDNS tidak akan diselesaikan di dalam container, kerana container tidak menggunakan resolver hos, jadi gunakan alamat 100.x berangka atau sematkannya dengan baris extra_hosts. Perkara yang sama terpakai apabila anda menjalankan pelayan kawalan Tailscale anda sendiri dengan Headscale.

Fail compose lengkap untuk bentuk lazim

Pelanggan muat turun di sebalik VPN, dua UI web yang hanya menjawab pada tailnet, dan satu kontena yang membaca pangkalan data PostgreSQL yang berjalan pada hos.

services:
  gluetun:
    image: qmcgaw/gluetun:latest
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - "127.0.0.1:8000:8000/tcp"        # gluetun control server, host only
      - "100.101.102.103:8080:8080/tcp"  # qBittorrent UI, tailnet only
      - "100.101.102.103:9696:9696/tcp"  # Prowlarr UI, tailnet only
    volumes:
      - ./gluetun:/gluetun
    environment:
      - VPN_SERVICE_PROVIDER=mullvad
      - VPN_TYPE=wireguard
      - WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
      - WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
      - SERVER_CITIES=Amsterdam
      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,100.64.7.9/32
      - TZ=Europe/Amsterdam
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    network_mode: "service:gluetun"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - WEBUI_PORT=8080
    volumes:
      - ./qbittorrent:/config
      - /srv/downloads:/downloads
    depends_on:
      gluetun:
        condition: service_healthy
    restart: unless-stopped

  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    network_mode: "service:gluetun"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - PROWLARR__POSTGRES__HOST=host.docker.internal
      - PROWLARR__POSTGRES__PORT=5432
      - PROWLARR__POSTGRES__USER=prowlarr
      - PROWLARR__POSTGRES__PASSWORD=${PROWLARR_DB_PASSWORD}
      - PROWLARR__POSTGRES__MAINDB=prowlarr-main
      - PROWLARR__POSTGRES__LOGDB=prowlarr-log
    volumes:
      - ./prowlarr:/config
    depends_on:
      gluetun:
        condition: service_healthy
    restart: unless-stopped

Baca fail tersebut untuk memahami corak, bukan nama produk. Kedua-dua UI diterbitkan pada gluetun dan diikat pada alamat tailnet hos, jadi ia hanya menjawab pada Tailscale dan bukan di tempat lain. Hanya Prowlarr yang membawa baris extra_hosts, kerana Prowlarr ialah kontena yang menyelesaikan host.docker.internal. FIREWALL_OUTBOUND_SUBNETS menamakan dua alamat tunggal: alamat jambatan Docker hos, supaya Prowlarr boleh membuka sambungan pangkalan data, dan satu peer tailnet.

Pelayan PostgreSQL sengaja tidak disertakan dalam fail tersebut. Ia berjalan pada VPS sebagai servis sistem biasa yang mendengar pada 172.17.0.1:5432. Itu adalah lapisan yang sama seperti arr stack pada Docker Compose, dengan pangkalan data dialihkan ke luar Docker.

Pastikan kunci peribadi WireGuard tidak berada dalam fail compose. ${WIREGUARD_PRIVATE_KEY} membaca daripada fail .env di sebelahnya, corak yang diliputi dalam fail env dan rahsia untuk Docker Compose. Klausa condition: service_healthy menggunakan healthcheck yang sudah disertakan oleh imej gluetun, jadi tiada apa-apa yang bermula sehingga terowong melaporkan dirinya aktif. Healthcheck Compose menerangkan bentuk umum.

Menerbitkan pada setiap alamat dan bukannya hanya pada tailnet

Gugurkan awalan alamat dan ikatan port pada 0.0.0.0, yang termasuk IP awam VPS. Lakukan ini hanya di sebalik firewall yang anda kawal, dan baca nota ufw di atas terlebih dahulu.

    ports:
      - "8080:8080/tcp"

Sahkan terowong masih membawa trafik

Jalankan permintaan yang sama sebanyak dua kali, sekali dari dalam namespace dan sekali dari hos, kemudian bandingkan hasilnya.

docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org
curl -s https://api.ipify.org

Permintaan pertama sepatutnya memaparkan alamat keluar pembekal VPN anda. Permintaan kedua sepatutnya memaparkan alamat VPS. Jika kedua-duanya sepadan, trafik kontena tidak melalui terowong tersebut, dan setiap pembaikan dalam panduan ini tidak relevan sehingga perkara itu diperbetulkan.

Jadual penghalaan menunjukkan apa yang berada di luar terowong dan apa yang tidak.

docker run --rm --network=container:gluetun alpine:3.22 ip route show

Laluan lalai (default route) sepatutnya menghala ke antara muka terowong, tun0. Di bawahnya, anda sepatutnya melihat satu laluan bagi setiap entri dalam FIREWALL_OUTBOUND_SUBNETS, yang menghala ke gateway bridge Docker. Sebarang laluan lain yang keluar melalui eth0 adalah trafik yang memintas VPN.

Pelayan kawalan Gluetun melaporkan IP awam yang sama pada port 8000, di /v1/publicip/ip. Versi terkini memerlukan anda mengkonfigurasi pengesahan untuk laluan pelayan kawalan, jadi sediakan perkara tersebut sebelum bergantung kepadanya.

Kebocoran yang disebabkan oleh subnet yang salah

FIREWALL_OUTBOUND_SUBNETS ialah lubang yang anda tebuk pada firewall secara sengaja, jadi saiz lubang tersebut menentukan tahap risiko. Empat cara yang menjadikannya terlalu besar:

  • 0.0.0.0/0 menghantar segala-galanya ke luar terowong. Dua semakan IP di atas mengesan perkara ini pada percubaan pertama, kerana ia akan memulangkan alamat yang sama.
  • Julat yang lebih luas daripada sasaran. Membuka 10.0.0.0/8 untuk mencapai satu mesin di 10.0.1.7 juga membuka setiap alamat yang mungkin diiklankan oleh rakan setara torrent dalam julat tersebut. Tulis 10.0.1.7/32.
  • Julat yang bertindih dengan alamat terowong itu sendiri. Dokumentasi gluetun memberi amaran bahawa ini menyebabkan gluetun menghantar trafik VPN keluar melalui bridge, yang akan memutuskan port forwarding. Semak nilai WIREGUARD_ADDRESSES anda sebelum membuka sebarang julat peribadi.
  • 100.64.0.0/10 untuk Tailscale. Ini bermakna kira-kira empat juta alamat dibuka hanya untuk mencapai satu rakan setara. Senaraikan rakan setara yang anda perlukan sebagai entri /32.

Ingat bahawa tetapan ini meliputi keseluruhan namespace. Membuka subnet supaya pengindeks boleh mencapai servis hos akan membuka subnet yang sama untuk klien torrent yang berkongsi namespace tersebut. Jalankan semula semakan IP awam selepas setiap perubahan pada pemboleh ubah ini, kerana ia adalah satu-satunya ujian yang menunjukkan sama ada perubahan tersebut memberikan hasil yang anda inginkan.

Perkara yang tergendala apabila anda memulakan semula gluetun

gluetun menguasai namespace, jadi kitaran hayat gluetun adalah kitaran hayat namespace tersebut. Memulakan kontena yang bergantung kepadanya semasa gluetun tidak aktif akan gagal serta-merta:

Error response from daemon: cannot join network of a non running container

Memulakan semula gluetun secara terus merupakan kegagalan yang lebih sukar dikesan. Kontena yang bergantung kepadanya terus berjalan sementara namespace yang disambungkan kepadanya dibina semula di bawahnya, jadi docker ps melaporkan semuanya sihat walaupun tiada respons. Selepas sebarang perubahan pada servis gluetun, cipta semula keseluruhan kumpulan tersebut dan bukannya memulakan semula satu bahagian sahaja.

docker compose up -d --force-recreate

Perkara yang sama terpakai untuk kemas kini imej. Menarik imej gluetun baharu dan mencipta semula servis itu sahaja akan menyebabkan servis lain merujuk kepada namespace yang tidak lagi wujud.

FAQ

Mengapakah Docker memaparkan ralat "port publishing and the container type network mode"?

Ini berlaku kerana blok ports: masih diletakkan pada servis yang turut menetapkan network_mode: service:gluetun. Menerbitkan port akan menambah peraturan NAT yang menghalakan port hos ke dalam namespace rangkaian kontena, sedangkan kontena dalam mod ini tidak mempunyai namespace tersebut. Padamkan blok ports: daripada servis tersebut dan tambahkan pemetaan yang sama pada servis gluetun. Nombor port kekal sama kerana aplikasi masih mendengar pada port tersebut di dalam namespace yang dikongsi.

Bagaimanakah kontena lain boleh mencapai servis yang berada di sebalik gluetun?

Kontena di dalam namespace yang sama boleh mencapai satu sama lain melalui 127.0.0.1. Kontena di luar namespace tersebut perlu menggunakan nama servis gluetun, jadi http://gluetun:8080 berfungsi manakala http://qbittorrent:8080 tidak. Kontena aplikasi tidak memegang sebarang alamat pada mana-mana rangkaian Docker, jadi pelayan DNS terbenam tidak mempunyai alamat untuk diselesaikan bagi nama tersebut. Tiada penerbitan port diperlukan untuk perkara ini, selagi kedua-dua kontena berkongsi rangkaian Compose yang sama.

Apakah yang perlu saya letakkan dalam FIREWALL_OUTBOUND_SUBNETS?

Hanya alamat yang perlu dihubungi oleh kontena di sebalik gluetun, dan tuliskan alamat tersebut sekhusus mungkin. Sebuah mesin tunggal ialah /32. Dua entri yang biasa digunakan ialah hos Docker pada 172.17.0.1/32 dan satu /32 bagi setiap peer Tailscale yang anda hubungi. Jangan sekali-kali menambah 0.0.0.0/0, dan jangan tambah julat yang bertindih dengan alamat tunnel VPN anda sendiri. Sambungan masuk ke port yang diterbitkan tidak memerlukan entri di sini.

Mengapakah kontena tidak dapat menyelesaikan nama Tailscale MagicDNS saya?

MagicDNS berfungsi dengan menghalakan resolver hos ke pelayan DNS Tailscale, manakala kontena tidak menggunakan resolver hos. Kontena menggunakan apa sahaja yang ditetapkan oleh /etc/resolv.conf miliknya, yang mana di sebalik gluetun, ia merujuk kepada tetapan DNS gluetun. Sahkan dengan docker exec <container> cat /etc/resolv.conf. Gunakan alamat berangka 100.x bagi peer tersebut, atau tetapkan nama tersebut dengan entri extra_hosts pada kontena berkenaan.

Bagaimanakah cara untuk saya mengesahkan trafik masih melalui VPN?

Jalankan satu permintaan dari dalam namespace dan permintaan yang sama dari hos, kemudian bandingkan jawapannya. docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org sepatutnya memaparkan alamat keluar pembekal VPN anda, manakala curl -s https://api.ipify.org pada VPS akan memaparkan alamat VPS tersebut. Jika kedua-dua jawapan adalah sama, bermakna tunnel tidak membawa trafik kontena tersebut. Jalankan semakan ini semula selepas setiap perubahan pada FIREWALL_OUTBOUND_SUBNETS.