SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-13

วิธีเชื่อมต่อ Gluetun กับคอนเทนเนอร์อื่นใน Docker

เรียนรู้วิธีตั้งค่าเครือข่ายเมื่อคอนเทนเนอร์ใช้ network namespace ของ Gluetun พร้อมวิธีเปิดพอร์ตและจัดการกฎไฟร์วอลล์เพื่อเข้าถึงโฮสต์หรือบริการอื่นได้อย่างถูกต้อง

เกิดอะไรขึ้นเมื่อคอนเทนเนอร์เข้าร่วมเครือข่ายของ gluetun

คอนเทนเนอร์ที่ตั้งค่า network_mode: service:gluetun จะไม่มีอินเทอร์เฟซเครือข่ายเป็นของตัวเอง โดยจะเข้าไปอยู่ใน network namespace ของ gluetun แทน ส่งผลให้การเปิดพอร์ต (port publishing) และกฎไฟร์วอลล์ไม่ได้ขึ้นอยู่กับคอนเทนเนอร์นั้นอีกต่อไป แต่จะกลายเป็นคุณสมบัติของบริการ gluetun โดยตรง ทุกคำตอบด้านล่างนี้ล้วนเป็นผลสืบเนื่องมาจากข้อเท็จจริงดังกล่าว

Network namespace คือสำเนาส่วนตัวของ stack เครือข่ายในระดับเคอร์เนล ซึ่งประกอบด้วยอินเทอร์เฟซ ตารางเส้นทาง (routing table) กฎไฟร์วอลล์ และซ็อกเก็ตที่รอรับการเชื่อมต่อ (listening sockets) ของตัวเอง โดยปกติ Docker จะสร้าง namespace ให้คอนเทนเนอร์ละหนึ่งชุด แต่เมื่อคุณระบุ network_mode: service:gluetun ตัว Docker จะข้ามขั้นตอนดังกล่าวและนำคอนเทนเนอร์ใหม่ไปใส่ไว้ใน namespace ที่ gluetun ใช้งานอยู่แล้ว คอนเทนเนอร์จะยังคงมีระบบไฟล์และไฟล์ /etc/hosts เป็นของตัวเอง ซึ่งไฟล์หลังนี้จะมีความสำคัญในภายหลัง

คุณสามารถตรวจสอบเรื่องนี้ได้โดยตรง

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

คำสั่งดังกล่าวจะแสดงผล container: ตามด้วย ID ของคอนเทนเนอร์ gluetun ในขณะที่คอนเทนเนอร์ทั่วไปจะแสดงผลเป็น bridge คู่มือนี้จะอธิบายต่อจากส่วนที่ การกำหนดเส้นทางทราฟฟิก Docker ผ่าน VPN ด้วย gluetun ได้ทิ้งไว้ นั่นคือเมื่ออุโมงค์เชื่อมต่อทำงานได้แล้ว แต่ในขณะนี้ไม่มีสิ่งใดสามารถสื่อสารกับคอนเทนเนอร์ได้เลย

เผยแพร่พอร์ตที่ gluetun ไม่ใช่ที่ตัวแอปพลิเคชัน

หากทิ้งบล็อก ports: ไว้ที่ service ซึ่งตั้งค่า network_mode เอาไว้ Docker จะปฏิเสธการสร้างคอนเทนเนอร์:

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

สาเหตุนั้นตรงไปตรงมา การเผยแพร่พอร์ตคือการเพิ่มกฎ NAT (network address translation) เพื่อส่งต่อพอร์ตของโฮสต์เข้าไปยัง network namespace ของคอนเทนเนอร์ แต่คอนเทนเนอร์นี้ไม่มี namespace เป็นของตนเอง ให้ย้ายการแมปพอร์ตไปไว้ที่ service ของ gluetun แทน หมายเลขพอร์ตไม่ต้องเปลี่ยนแปลง เนื่องจากแอปพลิเคชันยังคงฟังอยู่ที่พอร์ตเดิมภายใน shared namespace นั้น

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

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

บล็อก expose: บน service ที่เป็น dependency ก็ไร้ประโยชน์เช่นกัน และบล็อก networks: ในจุดนั้นถือเป็นข้อผิดพลาดร้ายแรง: Compose จะรายงานว่า service ดังกล่าวประกาศใช้ network_mode และ networks ซึ่งขัดแย้งกันเอง และจะปฏิเสธการโหลดไฟล์นั้นทันที

ผลกระทบหนึ่งจะตามมาในภายหลัง คอนเทนเนอร์ทุกตัวใน namespace เดียวกันจะใช้พื้นที่พอร์ตชุดเดียวกัน ดังนั้นหากมีแอปพลิเคชันสองตัวที่ค่าเริ่มต้นเป็น 8080 ทั้งคู่จะเกิดการชนกัน และตัวที่เริ่มทำงานทีหลังจะล้มเหลวด้วยข้อผิดพลาด address already in use ให้เปลี่ยนค่าพอร์ตของแอปพลิเคชันตัวใดตัวหนึ่งในการตั้งค่าของแอปนั้นเอง เช่น ตัวแปร WEBUI_PORT ในอิมเมจ qBittorrent ของ LinuxServer จากนั้นจึงเผยแพร่หมายเลขพอร์ตใหม่ที่ gluetun

คอนเทนเนอร์ที่อยู่หลัง gluetun สื่อสารกันเองได้อย่างไร

ภายในเนมสเปซเดียวกัน คอนเทนเนอร์เหล่านี้ใช้ loopback interface ร่วมกันอยู่แล้ว คอนเทนเนอร์ที่อยู่หลัง gluetun จึงสามารถเข้าถึงคอนเทนเนอร์พี่น้องได้ที่ 127.0.0.1:<port> โดยไม่ต้องผ่าน Docker network ใดๆ

จากภายนอกเนมสเปซ คอนเทนเนอร์นี้จะไม่มีชื่อเรียก ระบบ DNS ฝังตัวของ Docker จะแปลงชื่อบริการ (service name) ไปเป็นที่อยู่ของบริการนั้นบนเครือข่ายที่ผู้ใช้กำหนด แต่คอนเทนเนอร์นี้ไม่มีที่อยู่บนเครือข่ายใดเลย ดังนั้นคอนเทนเนอร์ทั่วไปอย่าง Sonarr จึงไม่สามารถเข้าถึง torrent client ที่ http://qbittorrent:8080 ได้ แต่จะเข้าถึงได้ที่ http://gluetun:8080 เนื่องจาก socket กำลังรอรับการเชื่อมต่ออยู่ในเนมสเปซของ gluetun และใช้ที่อยู่ของ gluetun สิ่งนี้มักทำให้ผู้ที่คุ้นเคยกับ วิธีการทำงานของ Docker Compose networks และ service names และคาดหวังว่าจะใช้การตั้งชื่อแบบปกติเกิดความสับสน นอกจากนี้ยังสามารถทำงานได้โดยไม่ต้องเปิดพอร์ตใดๆ ออกสู่โฮสต์ เนื่องจากคอนเทนเนอร์ทั้งสองอยู่ใน Compose network เดียวกัน

ให้ตรวจสอบ DNS ก่อนเริ่มการแก้ไขปัญหาอื่นใด gluetun มีตัวแก้ไข DNS (resolver) ของตัวเองและเขียนทับไฟล์ /etc/resolv.conf ภายในคอนเทนเนอร์ของมันเอง แต่ /etc/resolv.conf เป็นไฟล์ที่แยกเฉพาะแต่ละคอนเทนเนอร์ ดังนั้นไฟล์ที่ gluetun เขียนขึ้นจึงไม่ใช่ไฟล์ที่แอปพลิเคชันของคุณอ่าน

docker exec qbittorrent cat /etc/resolv.conf

ฉันจะเข้าถึงบริการที่ทำงานอยู่บน Docker host ได้อย่างไร

ให้ใช้ host.docker.internal โดยต้องตั้งค่าสองจุดในตำแหน่งที่ต่างกัน เนื่องจากมีสองส่วนที่ทำงานไม่สัมพันธ์กัน

ชื่อต้องมาก่อน /etc/hosts เป็นการตั้งค่าราย container ดังนั้นรายการ extra_hosts จึงต้องอยู่ใน container ของแอปพลิเคชัน ไม่ใช่ใน gluetun

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

host-gateway เป็นค่าพิเศษที่ Docker จะแทนที่ด้วยที่อยู่ภายในของตัว host เอง บนการติดตั้ง Docker แบบ Linux ทั่วไป ค่านี้คือที่อยู่ของ bridge docker0 ซึ่งโดยปกติคือ 172.17.0.1 ให้ตรวจสอบค่าของคุณด้วยคำสั่ง ip -4 addr show docker0 บน VPS สำหรับ Docker Desktop นั้นจะ resolve ชื่อนี้ได้เอง ซึ่งเป็นเหตุผลว่าทำไมคู่มือที่เขียนบนแล็ปท็อปถึงข้ามบรรทัด extra_hosts ไป แต่เมื่อนำไปใช้บนเซิร์ฟเวอร์จริงกลับใช้งานไม่ได้

เส้นทาง (route) ต้องมาเป็นลำดับที่สอง การเพิ่มชื่อเพียงอย่างเดียวเป็นการบอก container ว่าต้องใช้ที่อยู่ใด แต่แพ็กเก็ตยังคงออกผ่านเส้นทางเริ่มต้นของ gluetun ซึ่งก็คือ tunnel และ firewall ของ gluetun จะดรอปแพ็กเก็ตนั้นทิ้ง อาการที่พบคือการเชื่อมต่อค้างแล้วหมดเวลา (timeout) แทนที่จะถูกปฏิเสธ (refused) การถูกปฏิเสธหมายความว่าแพ็กเก็ตไปถึงแล้วแต่มีบางอย่างตอบกลับมาว่าไม่ได้ ส่วนการหมดเวลาหมายความว่าแพ็กเก็ตไปไม่ถึง

  gluetun:
    environment:
      - FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32

จากนั้นให้ตรวจสอบว่าบริการบน host กำลังฟัง (listen) อยู่ที่ที่อยู่นั้นจริงหรือไม่ PostgreSQL server ที่ผูก (bind) ไว้กับ 127.0.0.1 เพียงอย่างเดียวจะไม่สามารถเข้าถึงได้จาก container ใดๆ ไม่ว่าจะผ่าน tunnel หรือไม่ก็ตาม เพราะ 127.0.0.1 ภายใน namespace คือ loopback ของตัว namespace เอง ให้เปลี่ยนไปผูกกับ 172.17.0.1 แทน ซึ่งจะยอมรับการเชื่อมต่อจาก container โดยที่ยังคงไม่เปิดเผยบน public interface ให้ตรวจสอบด้วยคำสั่ง ss -lntp | grep 5432 บน host

สิ่งที่ FIREWALL_OUTBOUND_SUBNETS เปลี่ยนแปลงจริง

เอกสารประกอบของ gluetun อธิบายว่าค่านี้คือรายการ subnet ที่คั่นด้วยเครื่องหมายจุลภาค ซึ่งอนุญาตให้ gluetun และคอนเทนเนอร์ที่ใช้ network stack ร่วมกันสามารถเข้าถึงได้ โดยระบุว่ามีการเปลี่ยนแปลงทั้งในส่วนของ firewall และ routing ซึ่งทั้งสองส่วนมีความสำคัญเท่ากัน gluetun จะเพิ่มเส้นทาง (route) สำหรับแต่ละ subnet ที่ระบุผ่าน Docker bridge gateway เพื่อให้แพ็กเก็ตสำหรับที่อยู่เหล่านั้นออกทาง eth0 แทนที่จะออกทางอุโมงค์ VPN นอกจากนี้ยังเป็นการเปิด firewall สำหรับ subnet เหล่านั้นด้วย เนื่องจากโดยปกติแล้ว gluetun จะทิ้ง (drop) ทราฟฟิกขาออกที่ไม่มุ่งหน้าไปยังเซิร์ฟเวอร์ VPN

ให้ระบุค่าโดยไม่มีช่องว่างหลังเครื่องหมายจุลภาค

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

มีคุณสมบัติสองประการที่มักถูกมองข้าม ประการแรก การตั้งค่านี้เป็นระดับ namespace ดังนั้นจึงมีผลกับทุกคอนเทนเนอร์ที่อยู่เบื้องหลัง gluetun ไม่ใช่แค่คอนเทนเนอร์ที่คุณตั้งใจไว้เท่านั้น ประการที่สอง การตั้งค่านี้ควบคุมเฉพาะทราฟฟิกขาออก (outbound) เท่านั้น คือควบคุมการเชื่อมต่อที่คอนเทนเนอร์เป็นผู้เริ่ม ส่วนการเชื่อมต่อที่เข้ามายังพอร์ตที่เปิดไว้ (published port) จะใช้เส้นทางอื่นและไม่จำเป็นต้องระบุในส่วนนี้

การเข้าถึงเว็บ UI จาก Tailscale peer

Tailscale กำหนดที่อยู่ให้กับทุกเครื่องใน 100.64.0.0/10 ซึ่งเป็นช่วงที่สงวนไว้สำหรับ carrier grade NAT การเชื่อมต่อทั้งสองทิศทางจำเป็นต้องใช้วิธีการที่แตกต่างกัน

การเชื่อมต่อขาเข้า (Inbound) เป็นส่วนที่ง่ายกว่า การประกาศ 8080:8080 บน gluetun จะทำการ bind พอร์ตนั้นเข้ากับทุกที่อยู่ของโฮสต์ และอินเทอร์เฟซ tailscale0 ของโฮสต์ก็เป็นหนึ่งในนั้น ดังนั้น peer จึงสามารถเปิด http://<machine-name>:8080 เพื่อเข้าถึงคอนเทนเนอร์ได้ โดยที่ gluetun ไม่ได้มีส่วนเกี่ยวข้องในเส้นทางดังกล่าว เนื่องจากกฎ NAT ของ Docker อยู่ที่ระดับโฮสต์ ซึ่งอยู่นอกเนมสเปซ

เพื่อให้สามารถเข้าถึง UI ได้ผ่านทาง tailnet เท่านั้น ให้ทำการ bind พอร์ตที่ประกาศไว้เข้ากับที่อยู่ Tailscale ของโฮสต์แทนที่จะ bind กับทุกที่อยู่

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

ค้นหาที่อยู่ดังกล่าวได้ด้วยคำสั่ง tailscale ip -4 บนโฮสต์ การ bind ในลักษณะนี้เป็นการควบคุมที่เข้มงวดกว่ากฎไฟร์วอลล์ เนื่องจากพอร์ตจะไม่ถูกเปิดบนอินเทอร์เฟซสาธารณะเลย นอกจากนี้ยังช่วยเลี่ยงปัญหาใน การที่ Docker ประกาศพอร์ตโดยข้าม ufw

การเชื่อมต่อขาออก (Outbound) คือจุดที่ FIREWALL_OUTBOUND_SUBNETS เข้ามามีบทบาท หากคอนเทนเนอร์จำเป็นต้องเรียกใช้งาน peer ให้เพิ่มที่อยู่ของ peer นั้น และควรเลือกใช้ /32 แยกตามราย peer แทนการใช้ /10 ทั้งหมด ชื่อ MagicDNS จะไม่สามารถ resolve ภายในคอนเทนเนอร์ได้ เนื่องจากคอนเทนเนอร์ไม่ได้ใช้ตัว resolver ของโฮสต์ ดังนั้นให้ใช้ที่อยู่ 100.x แบบตัวเลข หรือกำหนดค่าไว้ตายตัวด้วยบรรทัด extra_hosts หลักการเดียวกันนี้ยังใช้ได้เมื่อคุณรัน Tailscale control server ของคุณเองด้วย Headscale

ไฟล์ compose ฉบับสมบูรณ์สำหรับโครงสร้างทั่วไป

ไคลเอนต์ดาวน์โหลดที่อยู่หลัง VPN, เว็บ UI สองรายการที่ตอบสนองเฉพาะบน tailnet และคอนเทนเนอร์หนึ่งรายการที่อ่านฐานข้อมูล PostgreSQL ที่รันอยู่บนโฮสต์

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

ให้ศึกษาไฟล์นี้เพื่อดูรูปแบบการตั้งค่ามากกว่าชื่อผลิตภัณฑ์ เว็บ UI ทั้งสองรายการถูกเผยแพร่ผ่าน gluetun และผูกไว้กับที่อยู่ tailnet ของโฮสต์ ดังนั้นจึงตอบสนองผ่าน Tailscale เท่านั้นและไม่ตอบสนองจากที่อื่น มีเพียง Prowlarr เท่านั้นที่มีบรรทัด extra_hosts เนื่องจาก Prowlarr เป็นคอนเทนเนอร์ที่ทำหน้าที่แก้ไข host.docker.internal ส่วน FIREWALL_OUTBOUND_SUBNETS ระบุที่อยู่สองรายการ ได้แก่ ที่อยู่ Docker bridge ของโฮสต์เพื่อให้ Prowlarr เชื่อมต่อฐานข้อมูลได้ และที่อยู่ของ peer ใน tailnet อีกหนึ่งรายการ

เซิร์ฟเวอร์ PostgreSQL ไม่ได้รวมอยู่ในไฟล์นี้โดยเจตนา โดยรันอยู่บน VPS ในฐานะ system service ปกติที่ฟังพอร์ต 172.17.0.1:5432 ซึ่งเป็นการจัดเลเยอร์แบบเดียวกับ arr stack บน Docker Compose โดยย้ายฐานข้อมูลออกมาไว้นอก Docker

ควรเก็บ WireGuard private key ไว้นอกไฟล์ compose โดยใช้ ${WIREGUARD_PRIVATE_KEY} เพื่ออ่านค่าจากไฟล์ .env ที่วางอยู่ข้างกัน ซึ่งเป็นรูปแบบที่อธิบายไว้ใน ไฟล์ env และ secrets สำหรับ Docker Compose ส่วนคำสั่ง condition: service_healthy จะใช้ healthcheck ที่มาพร้อมกับอิมเมจ gluetun เพื่อให้มั่นใจว่าไม่มีบริการใดเริ่มทำงานจนกว่า tunnel จะรายงานสถานะว่าพร้อมใช้งาน Compose healthchecks อธิบายรูปแบบการใช้งานทั่วไปไว้

การเผยแพร่บนทุกที่อยู่แทนที่จะเป็นเฉพาะ tailnet

ให้ลบ prefix ของที่อยู่และการผูกพอร์ตบน 0.0.0.0 ออก ซึ่งรวมถึง IP สาธารณะของ VPS ด้วย ให้ดำเนินการนี้เฉพาะเมื่ออยู่หลังไฟร์วอลล์ที่คุณควบคุมได้เท่านั้น และโปรดอ่านหมายเหตุเกี่ยวกับ ufw ด้านบนก่อน

    ports:
      - "8080:8080/tcp"

ยืนยันว่า tunnel ยังคงรับส่งข้อมูล

ให้รันคำขอเดิมสองครั้ง ครั้งแรกจากภายใน namespace และครั้งที่สองจาก host จากนั้นนำมาเปรียบเทียบกัน

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

คำสั่งแรกควรแสดงที่อยู่ IP ขาออกของผู้ให้บริการ VPN ของคุณ ส่วนคำสั่งที่สองควรแสดงที่อยู่ IP ของ VPS หากทั้งสองค่าตรงกัน แสดงว่าข้อมูลของ container ไม่ได้วิ่งผ่าน tunnel และการแก้ไขใดๆ ในคู่มือนี้จะไม่มีผลจนกว่าจะแก้ไขปัญหานี้ให้ถูกต้อง

ตาราง routing จะแสดงให้เห็นว่าข้อมูลส่วนใดวิ่งผ่าน tunnel และส่วนใดไม่ได้วิ่งผ่าน

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

default route ควรชี้ไปยัง interface ของ tunnel คือ tun0 ด้านล่างนั้น คุณควรเห็น route หนึ่งรายการต่อหนึ่ง entry ใน FIREWALL_OUTBOUND_SUBNETS ซึ่งชี้ไปยัง gateway ของ Docker bridge หากมี route อื่นใดที่ออกผ่าน eth0 แสดงว่าข้อมูลส่วนนั้นข้ามการทำงานของ VPN ไป

control server ของ Gluetun จะรายงาน IP สาธารณะเดียวกันที่พอร์ต 8000 ผ่านทาง /v1/publicip/ip สำหรับเวอร์ชันใหม่ๆ คุณจำเป็นต้องตั้งค่าการยืนยันตัวตนสำหรับเส้นทางของ control server ดังนั้นควรตั้งค่าส่วนนี้ให้เรียบร้อยก่อนที่จะใช้งาน

ความเสียหายที่เกิดจากการกำหนด subnet ผิดพลาด

FIREWALL_OUTBOUND_SUBNETS คือช่องโหว่ที่คุณเจาะผ่านไฟร์วอลล์ด้วยความตั้งใจ ดังนั้นขนาดของช่องโหว่จึงเท่ากับขนาดของความเสี่ยง ต่อไปนี้คือ 4 วิธีที่ทำให้ช่องโหว่นี้ใหญ่เกินความจำเป็น:

  • 0.0.0.0/0 ส่งทุกอย่างออกไปนอกอุโมงค์ การตรวจสอบ IP สองรายการข้างต้นจะตรวจพบปัญหานี้ในการรันครั้งแรก เนื่องจากทั้งสองจะส่งคืนค่าที่อยู่เดียวกัน
  • ช่วงที่กว้างกว่าเป้าหมาย การเปิด 10.0.0.0/8 เพื่อเข้าถึงเครื่องเดียวที่ 10.0.1.7 จะเป็นการเปิดที่อยู่ทุกหมายเลขที่ peer ของ torrent อาจประกาศไว้ในช่วงนั้นด้วย ให้ระบุเป็น 10.0.1.7/32
  • ช่วงที่ทับซ้อนกับที่อยู่ของอุโมงค์เอง เอกสารของ gluetun เตือนว่าการทำเช่นนี้จะทำให้ gluetun ส่งทราฟฟิก VPN ออกไปทาง bridge แทน ซึ่งจะทำให้การทำ port forwarding ใช้งานไม่ได้ ให้ตรวจสอบค่า WIREGUARD_ADDRESSES ของคุณก่อนเปิดช่วงที่อยู่ส่วนตัวใดๆ
  • 100.64.0.0/10 สำหรับ Tailscale ซึ่งเป็นการเปิดที่อยู่ประมาณสี่ล้านหมายเลขเพื่อให้เข้าถึง peer ได้เพียงเครื่องเดียว ให้ระบุรายชื่อ peer ที่คุณต้องการเป็นรายการ /32 แทน

โปรดจำไว้ว่าการตั้งค่านี้ครอบคลุมทั้ง namespace การเปิด subnet เพื่อให้ indexer เข้าถึง host service ได้ จะเป็นการเปิด subnet เดียวกันนั้นให้กับ torrent client ที่แชร์ namespace เดียวกันด้วย ให้รันการตรวจสอบ public IP ซ้ำทุกครั้งหลังเปลี่ยนตัวแปรนี้ เพราะเป็นการทดสอบเดียวที่แสดงให้เห็นว่าการเปลี่ยนแปลงนั้นส่งผลตามที่คุณต้องการหรือไม่

สิ่งที่เกิดขึ้นเมื่อรีสตาร์ท gluetun

gluetun เป็นผู้ควบคุม namespace ดังนั้นวงจรชีวิตของ gluetun จึงเป็นวงจรชีวิตของ namespace นั้นด้วย การเริ่มคอนเทนเนอร์ที่ขึ้นต่อกันในขณะที่ gluetun หยุดทำงานจะล้มเหลวทันที:

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

การรีสตาร์ท gluetun โดยตรงเป็นความล้มเหลวที่เงียบกว่า คอนเทนเนอร์ที่ขึ้นต่อกันจะยังคงทำงานอยู่ ในขณะที่ namespace ที่พวกมันเชื่อมต่ออยู่ถูกสร้างขึ้นใหม่ภายใต้คอนเทนเนอร์เหล่านั้น ส่งผลให้ docker ps รายงานว่าทุกอย่างปกติ แต่ไม่มีการตอบสนองใดๆ หลังจากมีการเปลี่ยนแปลงใดๆ กับบริการ gluetun ให้สร้างกลุ่มบริการทั้งหมดขึ้นใหม่แทนการรีสตาร์ทเพียงส่วนใดส่วนหนึ่ง

docker compose up -d --force-recreate

กรณีนี้รวมถึงการอัปเดตอิมเมจด้วย การดึงอิมเมจ gluetun ใหม่และสร้างบริการนั้นขึ้นมาใหม่เพียงอย่างเดียว จะทำให้บริการอื่นชี้ไปยัง namespace ที่ไม่มีอยู่จริงอีกต่อไป

FAQ

ทำไม Docker ถึงแจ้งเตือนเรื่อง "port publishing and the container type network mode"?

เพราะมีการระบุบล็อก ports: ไว้ในเซอร์วิสที่ตั้งค่า network_mode: service:gluetun เอาไว้ด้วย การเปิดพอร์ต (publishing) จะเพิ่มกฎ NAT เพื่อส่งต่อพอร์ตจากโฮสต์เข้าไปยัง network namespace ของคอนเทนเนอร์ แต่คอนเทนเนอร์ที่อยู่ในโหมดนี้จะไม่มี namespace เป็นของตัวเอง ให้ลบบล็อก ports: ออกจากเซอร์วิสนั้น แล้วย้ายการแมปพอร์ตไปไว้ที่เซอร์วิส gluetun แทน โดยใช้หมายเลขพอร์ตเดิมได้เลย เนื่องจากแอปพลิเคชันยังคงฟังพอร์ตนั้นอยู่ภายใน namespace ที่ใช้ร่วมกัน

คอนเทนเนอร์อื่นจะเข้าถึงเซอร์วิสที่อยู่หลัง gluetun ได้อย่างไร?

คอนเทนเนอร์ที่อยู่ใน namespace เดียวกันสามารถเข้าถึงกันได้ผ่าน 127.0.0.1 ส่วนคอนเทนเนอร์ที่อยู่นอก namespace ให้ใช้ชื่อเซอร์วิสของ gluetun ดังนั้น http://gluetun:8080 จะใช้งานได้ในขณะที่ http://qbittorrent:8080 ใช้งานไม่ได้ เนื่องจากคอนเทนเนอร์ของแอปพลิเคชันไม่มีที่อยู่ IP บนเครือข่าย Docker ใดๆ ทำให้ DNS server ภายในไม่สามารถแปลงชื่อของมันได้ ไม่จำเป็นต้องเปิดพอร์ตสำหรับกรณีนี้ ตราบใดที่คอนเทนเนอร์ทั้งสองอยู่ในเครือข่าย Compose เดียวกัน

ฉันควรใส่ค่าอะไรใน FIREWALL_OUTBOUND_SUBNETS?

ให้ใส่เฉพาะที่อยู่ที่คอนเทนเนอร์หลัง gluetun จำเป็นต้องเชื่อมต่อออกไปเท่านั้น โดยระบุให้แคบที่สุดเท่าที่จะทำได้ หากเป็นเครื่องเดียวให้ใช้ /32 รายการที่พบบ่อยคือที่อยู่ของ Docker host ที่ 172.17.0.1/32 และ /32 สำหรับ Tailscale peer แต่ละตัวที่คุณเรียกใช้งาน ห้ามใส่ 0.0.0.0/0 และห้ามใส่ช่วง IP ที่ทับซ้อนกับที่อยู่ tunnel ของ VPN ของคุณเอง สำหรับการเชื่อมต่อขาเข้าผ่านพอร์ตที่เปิดไว้ ไม่จำเป็นต้องระบุค่าในส่วนนี้

ทำไมคอนเทนเนอร์ถึงไม่สามารถแปลงชื่อ Tailscale MagicDNS ได้?

MagicDNS ทำงานโดยชี้ตัวแปลงชื่อ (resolver) ของโฮสต์ไปยัง DNS server ของ Tailscale แต่คอนเทนเนอร์ไม่ได้ใช้ resolver ของโฮสต์ มันจะใช้ค่าที่ระบุใน /etc/resolv.conf ของตัวเอง ซึ่งเมื่ออยู่หลัง gluetun ค่านี้จะเป็นการตั้งค่า DNS ของ gluetun ให้ตรวจสอบด้วย docker exec <container> cat /etc/resolv.conf โดยให้ใช้ที่อยู่ 100.x แบบตัวเลขของ peer นั้นๆ หรือกำหนดชื่อด้วยรายการ extra_hosts ในคอนเทนเนอร์นั้นแทน

ฉันจะตรวจสอบได้อย่างไรว่าทราฟฟิกยังคงวิ่งผ่าน VPN?

ให้รันคำขอหนึ่งรายการจากภายใน namespace และรันคำขอเดียวกันจากโฮสต์ จากนั้นเปรียบเทียบผลลัพธ์ docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org ควรแสดงที่อยู่ขาออกของผู้ให้บริการ VPN ของคุณ ในขณะที่ curl -s https://api.ipify.org บน VPS ควรแสดงที่อยู่ของ VPS หากผลลัพธ์ทั้งสองเหมือนกัน แสดงว่า tunnel ไม่ได้นำทราฟฟิกของคอนเทนเนอร์ไป ให้ทำการตรวจสอบนี้อีกครั้งหลังจากการเปลี่ยนแปลง FIREWALL_OUTBOUND_SUBNETS ทุกครั้ง