Tích hợp Tailscale vào Docker: Nối mạng đa Cloud an toàn, không cần mở Port

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

2 giờ sáng và sự cố đứt kết nối đa Cloud

Màn hình PagerDuty sáng rực lúc 2:15 sáng. Worker đồng bộ dữ liệu đặt trên Hetzner VPS liên tục ném lỗi connection timed out khi ghi vào cụm PostgreSQL trên AWS EC2. Cả hai phía đều chạy Docker. Trước đó, team chọn cách kết nối qua Public IP rồi cấu hình whitelist trong Security Group để tiết kiệm thời gian.

Sự cố ập đến khi Hetzner gán lại dải IP sau một đợt bảo trì mạng. Whitelist trên AWS vô hiệu ngay lập tức. Toàn bộ pipeline xử lý đơn hàng tê liệt suốt 45 phút. Mở toang port 5432 ra ngoài Internet thì rủi ro bị bot quét qua Shodan sau chưa đầy 15 phút. Dựng AWS NAT Gateway và Direct Connect thì đội chi phí thêm ít nhất 32 USD mỗi tháng cho từng cụm. Đây là lúc mô hình mạng nội bộ dạng mesh trở thành bắt buộc.

Ba hướng tiếp cận khi nối Docker container liên server

Khi các cụm container nằm rải rác ở AWS, DigitalOcean, Hetzner hay máy chủ văn phòng, bạn thường có 3 lựa chọn:

  • Cách 1: Mở Port Public và Whitelist IP (Tạm bợ)
    Map port container ra IP public (-p 5432:5432) rồi dùng UFW hoặc Security Group chặn IP. Triển khai trong 2 phút nhưng bảo trì rất cực khi IP thay đổi liên tục.
  • Cách 2: Cài WireGuard trực tiếp lên Host OS (Hạ tầng truyền thống)
    Dựng WireGuard trên từng máy chủ, tạo file wg0.conf rồi cấu hình iptables để đẩy traffic từ Docker bridge sang VPN. Hiệu năng cao nhưng dễ xung đột network stack của Docker khi cập nhật OS.
  • Cách 3: Chạy Tailscale Sidecar trong Docker (Container-level Mesh)
    Mỗi stack dịch vụ có riêng một container Tailscale. Container ứng dụng gắn trực tiếp vào network stack của Tailscale thông qua chỉ thị network_mode: "service:...".

So sánh chi tiết ba giải pháp

Tiêu chí Public Port + Whitelist WireGuard trên Host OS Tailscale Sidecar Container
Độ an toàn Thấp, dễ lộ port nếu firewall lỗi Rất cao Rất cao (Zero Trust)
Bảo trì khi mở rộng Ác mộng khi có trên 5 server Tốn công quản lý key và NAT Tự động 100% qua Control Plane
Tác động đến máy chủ Host Mở port trực tiếp trên Host Can thiệp sâu vào kernel/iptables Cô lập hoàn toàn trong Docker
Độ phức tạp cấu hình Rất thấp Trung bình đến cao Chỉ 1 file docker-compose.yml

Tại sao Tailscale Sidecar tối ưu hơn cài trên Host?

Trong một đợt nâng cấp Ubuntu 20.04 lên 22.04 trước đây, bảng iptables của máy chủ bị reset. Các rule chuyển tiếp giữa Docker bridge và wg0 biến mất sạch sẽ, làm sập toàn bộ kết nối giữa 3 microservice. Team mất 3 tiếng mò log giữa đêm chỉ để debug iptables.

Mô hình Sidecar giải quyết triệt để vấn đề này. Mạng được đóng gói đi kèm ứng dụng. Khi bạn dọn container sang một VPS mới hoặc chuyển giữa các nhà cung cấp Cloud, bạn chỉ cần gõ docker compose up -d là mọi thứ thông mạng ngay lập tức, không phụ thuộc vào cấu hình của máy chủ bên dưới.

Hướng dẫn cấu hình Tailscale Sidecar với Docker Compose

Giả sử chúng ta có 2 dịch vụ cần giao tiếp an toàn:

  • Server A (AWS EC2): Chạy database PostgreSQL.
  • Server B (Hetzner VPS): Chạy Worker service cần query dữ liệu từ Server A.

Bước 1: Tạo Tailscale Auth Key

Truy cập vào trang quản trị Tailscale, chọn Settings > Keys > Generate auth key. Bật tùy chọn Reusable và gắn tag (ví dụ tag:server). Việc gắn tag giúp key không bị hết hạn sau 90 ngày và tự động cấp quyền theo ACL định trước.

Bước 2: Cấu hình cụm Database trên Server A

Tạo file docker-compose.yml trên Server A với nội dung:

services:
  tailscale-db:
    image: tailscale/tailscale:v1.64.0
    container_name: tailscale-db
    hostname: db-node
    environment:
      - TS_AUTHKEY=tskey-auth-kXXXXX-XXXXXXXXXXXX
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_EXTRA_ARGS=--accept-routes=false
    volumes:
      - ./tailscale-state:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
      - NET_RAW
    restart: unless-stopped

  postgres:
    image: postgres:15-alpine
    container_name: production-postgres
    network_mode: "service:tailscale-db"
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: SecretPassword123!
      POSTGRES_DB: app_db
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    depends_on:
      - tailscale-db

volumes:
  pgdata:

3 chi tiết kỹ thuật quan trọng:

  • Không khai báo ports: Container PostgreSQL không expose port ra host, vô hình trước mọi máy quét bên ngoài.
  • Chung Network Namespace: Dòng network_mode: "service:tailscale-db" khiến PostgreSQL lắng nghe trực tiếp trên IP nội bộ của Tailscale.
  • Mount thư mục state: Volume ./tailscale-state lưu giữ identity của node. Nếu thiếu volume này, mỗi lần chạy lại container hệ thống sẽ sinh ra một node mới trên Tailscale Admin.

Bước 3: Khởi chạy và kiểm tra node

Khởi động stack trên Server A:

docker compose up -d
docker compose logs -f tailscale-db

Khi thấy log xuất hiện dòng Boot: ... Success; node is db-node (100.x.y.z), cụm database đã gia nhập mạng mesh an toàn.

Bước 4: Kết nối từ Server B

Trên Server B (đã tham gia cùng mạng Tailscale), bạn có thể kiểm tra đường truyền và latency trực tiếp:

# Ping trực tiếp qua MagicDNS, độ trễ thường chỉ dao động từ 20-35ms giữa các datacenter EU
tailscale ping db-node

# Kiểm tra port PostgreSQL qua mạng riêng WireGuard
nc -zv db-node 5432

Màn hình hiển thị Connection to db-node 5432 port [tcp/*] succeeded!. Chuỗi kết nối của Worker giờ đây chỉ cần trỏ về postgres://admin:SecretPassword123!@db-node:5432/app_db.

Lưu ý thực chiến trên môi trường Production

  • Cấp quyền tối thiểu: Chỉ dùng cap_add: [NET_ADMIN, NET_RAW]. Tránh dùng privileged: true để giảm nguy cơ container breakout.
  • Khai thác MagicDNS: Dùng hostname db-node thay vì hardcode dải IP 100.x.y.z để tránh phải sửa file config khi node tái cấp phát IP.
  • Giám sát DERP Relay: Lệnh tailscale status cho bạn biết kết nối là direct (P2P WireGuard) hay đi qua relay (DERP). Nếu thấy relay, hãy mở UDP port 41641 ở chiều Outbound để đạt throughput cao nhất.
Share: