บอกลาการจำพอร์ต Docker ด้วย Tailscale Services

Homelab ของผมรันบนเครื่อง 5 เครื่องพร้อมกับ Docker container อีกประมาณ 30 ตัว เป็นเวลานานมากที่ผมต้องเข้าถึงพวกมันผ่านโฟลเดอร์บุ๊กมาร์กที่เต็มไปด้วย URL อย่าง http://100.101.12.4:8096 ซึ่งไม่สะดวกเอาเสียเลย

แม้ Tailscale จะช่วยให้ผมเข้าสู่เครือข่ายจากที่ไหนก็ได้ แต่ปัญหาเดิมก็ยังอยู่คือผมต้องคอยจำว่าแอปไหนรันอยู่บนพอร์ตอะไรของเครื่องไหน

วิธีแก้ปัญหาทั่วไปต่างก็มีข้อจำกัด การใช้ reverse proxy พร้อมโดเมนส่วนตัวนั้นทำได้จริง แต่ก็กลายเป็นภาระในการบำรุงรักษาเพิ่มขึ้น ทุกครั้งที่มีแอปใหม่ต้องแก้ไฟล์คอนฟิกซ้ำซ้อน ส่วนการใช้ Tailscale sidecar ให้แต่ละ container ก็ทำให้รายชื่อในหน้า Machines ยาวเหยียดจนจัดการยาก

เมื่อ Tailscale เปิดตัว Services มันคือคำตอบที่ผมต้องการ นั่นคือการตั้งชื่อให้ "แอป" แทนที่จะเป็น "เครื่อง" ผมจึงเปลี่ยนมาใช้รูปแบบนี้ตั้งแต่นั้นมา และนี่คือสรุปวิธีการตั้งค่ารวมถึงสิ่งที่ควรรู้ก่อนเริ่มต้นครับ

Tailscale Service คืออะไรกันแน่

Service คือการกำหนดชื่อพร้อม Virtual IP บน tailnet ของคุณ เช่น jellyfin.your-tailnet.ts.net โดยไม่ได้ผูกติดกับอุปกรณ์ใดเครื่องหนึ่ง แต่จะใช้วิธีให้เครื่องในเครือข่ายทำหน้าที่โฆษณา (advertise) บริการนั้นออกมา เพื่อให้ Tailscale ส่งเส้นทาง (route) จากไคลเอนต์ไปยังเครื่องปลายทางได้อย่างถูกต้อง

ด้วยวิธีนี้ Docker host ของคุณจะยังคงเป็นเครื่องเดียวใน tailnet แต่แอปต่างๆ จะปรากฏเป็น Services แยกต่างหาก ข้อดีคือคุณสามารถกำหนดกฎการเข้าถึง (Access rules) ไปที่ Service ได้โดยตรง เช่น อนุญาตให้บางคนเข้าถึง Jellyfin ได้โดยไม่ต้องให้สิทธิ์เข้าถึงโฮสต์ทั้งเครื่อง

นี่คือแผนผังการทำงานเมื่อตั้งค่าเสร็จสิ้น:

Request path for a Tailscale Service: a phone on the tailnet opens jellyfin.your-tailnet.ts.net, Tailscale routes it to the Docker host, tailscaled terminates HTTPS and forwards plain HTTP with an X-Forwarded-For header to the Jellyfin container on its Docker network IP

รายละเอียดสำคัญในภาพคือ tailscaled บนโฮสต์จะทำหน้าที่จบการเชื่อมต่อ HTTPS และสื่อสารด้วย plain HTTP ไปยังแอปของคุณ พร้อมส่งส่วนหัว X-Forwarded-For ไปด้วย ซึ่งทำให้ Tailscale ทำหน้าที่เสมือนเป็น reverse proxy ในตัวนั่นเอง

ขั้นตอนการตั้งค่าด้วยตัวเอง

ก่อนจะเริ่มทำระบบอัตโนมัติ ผมแนะนำให้ลองตั้งค่าด้วยตัวเองกับแอปเดียวก่อน โดยมีสิ่งที่ต้องเตรียม 3 ส่วน ดังนี้

1. โฮสต์ที่ติดแท็ก (tagged host): Services จะถูกโฆษณาผ่านเครื่องที่ติดแท็กไว้เท่านั้น ให้คุณกำหนดแท็กให้กับ Docker host ดังนี้:

sudo tailscale up --advertise-tags=tag:server --reset

2. นโยบายการเข้าถึง: ในหน้า admin console ส่วน Access controls ต้องมีแท็กเหล่านั้นอยู่ และการตั้งค่า auto-approver จะช่วยลดขั้นตอนการกดอนุมัติด้วยมือในภายหลัง:

{
  "tagOwners": {
    "tag:server": ["autogroup:admin"],
    "tag:container": ["tag:server"]
  },
  "autoApprovers": {
    "services": {
      "tag:container": ["tag:server"]
    }
  }
}

3. การสร้าง Service: ในหน้า Services ของ admin console ให้เลือก Advertise และ Define a Service ตั้งชื่อว่า jellyfin พร้อมกำหนดแท็ก tag:container จากนั้นสั่งให้โฮสต์ชี้ไปยังแอปเป้าหมาย:

tailscale serve --service=svc:jellyfin --https=443 127.0.0.1:8096

หากคุณเปิดใช้งาน HTTPS ในหน้า DNS ของ admin console ไว้ ตอนนี้คุณจะสามารถเข้าถึง https://jellyfin.your-tailnet.ts.net ได้ทันทีจากทุกอุปกรณ์ที่เชื่อมต่อ Tailscale

ทำไมผมถึงจบลงด้วยการเขียนสคริปต์

ปัญหาคือหากคุณมี 30 แอป ขั้นตอนข้างต้นจะน่าเบื่อมาก เพราะต้องตั้งค่าทั้งในคอนโซลและรันคำสั่ง serve แยกทุกแอป แถมเมื่อมีการย้ายหรือลบ container ข้อมูลก็ไม่อัปเดตตามอัตโนมัติ

ผมจึงเขียนโปรแกรมด้วยภาษา Go ชื่อว่า DockTail เพื่อมาแก้ปัญหานี้ โดยมันจะคอยเฝ้าดู Docker events และเปลี่ยน Docker labels ให้กลายเป็นค่าคอนฟิกโดยอัตโนมัติ ดังตัวอย่างใน Compose file นี้:

services:
  docktail:
    image: ghcr.io/marvinvr/docktail:latest
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/run/tailscale:/var/run/tailscale
    environment:
      - TAILSCALE_OAUTH_CLIENT_ID=${TAILSCALE_OAUTH_CLIENT_ID}
      - TAILSCALE_OAUTH_CLIENT_SECRET=${TAILSCALE_OAUTH_CLIENT_SECRET}
  jellyfin:
    image: jellyfin/jellyfin:latest
    restart: unless-stopped
    labels:
      - "docktail.service.enable=true"
      - "docktail.service.name=jellyfin"
      - "docktail.service.port=8096"
      - "docktail.service.service-port=443"

การใช้เครื่องมือช่วยเฝ้าดู Docker สำคัญมาก เพราะเมื่อ container ถูกสร้างใหม่ IP ภายในเครือข่าย Docker มักจะเปลี่ยนไป หากเราตั้งค่า serve แบบคงที่ (static) ระบบจะใช้งานไม่ได้เมื่อ IP เปลี่ยนไปจากเดิมครับ

When the Jellyfin container is recreated, its Docker network IP changes from 172.18.0.5 to 172.18.0.9. A serve config written by hand still points at the old address, while a tool that watches Docker events updates it

สองสิ่งที่ควรรู้ก่อนใช้งาน

หาก Service ไม่ปรากฏ: มักเกิดจากปัญหาเรื่องแท็ก ต้องตรวจสอบว่าโฮสต์ติดแท็กถูกต้องหรือไม่ผ่านคำสั่ง tailscale status และเช็คว่าคุณได้กดยืนยัน Service ในหน้าจัดการแล้วหรือยัง

การตรวจสอบ proxy headers: บางแอปเช่น Home Assistant จะปฏิเสธคำขอ (400 Bad Request) เพราะเห็น header X-Forwarded-For จาก Tailscale คุณต้องตั้งค่าให้แอปเชื่อถือ proxy ในไฟล์ configuration.yaml ก่อนจึงจะใช้งานได้

สรุป: คุ้มค่าที่จะเปลี่ยนไหม?

สำหรับผมถือว่าคุ้มค่ามาก เพราะคอนฟิกเครือข่ายย้ายมาอยู่รวมกับ Compose file ของแอปโดยตรง การจัดการแอปจึงทำได้เบ็ดเสร็จในที่เดียว และหน้า Machines ก็ดูสะอาดตาขึ้นมาก

อย่างไรก็ตาม ผมยังคงใช้ reverse proxy สำหรับบริการสาธารณะที่ต้องการโดเมนของตัวเองอยู่ ส่วน Tailscale Services เหมาะที่สุดสำหรับการใช้งานภายในที่ต้องการความสะดวกและปลอดภัยครับ

หากใครสนใจลองใช้วิธีแบบ label สามารถดูรายละเอียดได้ที่ github.com/marvinvr/docktail หรือแวะไปอ่านคู่มือได้ที่ docktail.org/docs ครับ

Docktail.

Source: It's FOSS
ดูแลงานแปลและเรียบเรียงโดย AttapolK

ความคิดเห็น (0)

เข้าสู่ระบบเพื่อร่วมแสดงความเห็น

สมัครสมาชิก

มาเป็นคนแรกที่แสดงความเห็นกันเลยโบร