Hướng dẫn cài đặt Traefik v3 trên Fedora Server với Podman: Tự động hóa SSL và Routing

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

Khi chuyển khoảng 20 microservice nội bộ từ Docker Swarm sang Fedora Server chạy Podman, rào cản lớn nhất của mình là cấu hình Nginx. Cứ mỗi lần dựng container mới, quy trình lại lặp lại: mở nginx.conf, viết block upstream, reload service rồi chạy Certbot lấy SSL. Chỉ cần ai đó gõ nhầm một dấu chấm phẩy, cả cụm proxy gián đoạn. Traefik giải quyết dứt điểm sự cồng kềnh này: tự đọc event từ socket của Podman, định tuyến qua nhãn (labels) và xin chứng chỉ Let’s Encrypt tự động.

Fedora là môi trường lý tưởng cho Podman vì được Red Hat ưu tiên cập nhật kernel mới và tích hợp sâu cgroups v2. Kết hợp Traefik v3 với Podman trên Fedora 39/40 mang lại một cụm proxy nhẹ, chạy ổn định và tốn rất ít công bảo trì.

Quick start (triển khai trong 5 phút)

Chuẩn bị trước một tên miền đã trỏ bản ghi A/AAAA về IP máy chủ Fedora, đồng thời mở port 80 và 443 trên firewall:

sudo firewall-cmd --permanent --add-port={80,443}/tcp
sudo firewall-cmd --reload

1. Kích hoạt Podman System Socket

Traefik giao tiếp với Podman qua socket tương thích chuẩn Docker API. Hãy kích hoạt socket cấp systemd:

sudo systemctl enable --now podman.socket
# Kiểm tra socket đã hoạt động chưa
curl -s --unix-socket /run/podman/podman.sock http://d/v1.41/version | jq .Version

2. Tạo thư mục và file cấu hình cho Traefik

Tạo một thư mục riêng tại /opt/traefik để chứa cấu hình và file lưu chứng chỉ:

sudo mkdir -p /opt/traefik
cd /opt/traefik
sudo touch acme.json
sudo chmod 600 acme.json

Tạo file cấu hình tĩnh traefik.yml:

api:
  dashboard: true
  insecure: false

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  letsencrypt:
    acme:
      email: "[email protected]"
      storage: "/acme.json"
      httpChallenge:
        entryPoint: web

Lưu ý: Thay [email protected] bằng email thật để nhận cảnh báo khi chứng chỉ sắp hết hạn.

3. Khởi chạy Traefik bằng Podman Compose

Tạo file /opt/traefik/compose.yml:

version: "3.8"

services:
  traefik:
    image: docker.io/library/traefik:v3.0
    container_name: traefik
    restart: always
    ports:
      - "80:80"
      - "443:443"
    security_opt:
      - label=disable
    volumes:
      - /run/podman/podman.sock:/var/run/docker.sock:ro
      - /opt/traefik/traefik.yml:/traefik.yml:ro,z
      - /opt/traefik/acme.json:/acme.json:z
    networks:
      - proxy-net

networks:
  proxy-net:
    name: proxy-net

Chạy container Traefik:

podman compose up -d

4. Dựng thử một ứng dụng test có SSL

Dựng một web server Nginx nhỏ để xác thực tính năng tự động cấp chứng chỉ:

podman run -d --name demo-web \
  --network proxy-net \
  --label "traefik.enable=true" \
  --label "traefik.http.routers.demo.rule=Host(\`app.example.com\`)" \
  --label "traefik.http.routers.demo.entrypoints=websecure" \
  --label "traefik.http.routers.demo.tls.certresolver=letsencrypt" \
  --label "traefik.http.services.demo.loadbalancer.server.port=80" \
  docker.io/library/nginx:alpine

Truy cập https://app.example.com trên trình duyệt. Bạn sẽ thấy kết nối HTTPS hợp lệ ngay sau khoảng 3 đến 5 giây mà không cần cấu hình thêm bất kỳ dòng lệnh Nginx nào trên host.

Bản chất kỹ thuật cần nắm rõ

Cơ chế Provider và Docker API Compatibility

Traefik không polling container định kỳ. Thay vào đó, nó lắng nghe trực tiếp luồng event từ podman.socket.

Khi bạn gõ podman run hoặc tắt container, Traefik bắt được tín hiệu ngay tức khắc. Bảng định tuyến trong RAM được cập nhật lại sau chưa đầy 10 mili-giây. Toàn bộ quá trình diễn ra ngầm, không reload tiến trình và không làm rớt các kết nối TCP hiện có.

Nhờ Podman cung cấp API chuẩn Docker qua Unix socket, Traefik dùng thẳng provider docker có sẵn mà không cần cài thêm plugin ngoài.

Xử lý rào cản SELinux trên Fedora Server

Fedora bật SELinux ở chế độ Enforcing theo mặc định. Nếu mount file hoặc socket thiếu ngữ cảnh bảo mật, container sẽ dính lỗi Permission Denied ngay lập tức.

Các điểm mấu chốt cần lưu ý:

  • Flag :z ở volume gán nhãn SELinux chia sẻ (shared context container_file_t) cho file cấu hình traefik.yml và acme.json.
  • Tuyệt đối không dùng cờ :z với file socket hệ thống /run/podman/podman.sock. Việc relabel socket hệ thống có thể gây lỗi quyền cho systemd. Thay vào đó, hãy vô hiệu hóa hạn chế nhãn trên riêng container Traefik bằng tùy chọn security_opt: ["label=disable"].

Vòng đời cấp phát SSL Let’s Encrypt

Khi request đầu tiên gửi tới domain app.example.com qua port 443:

  1. Traefik kiểm tra file acme.json.
  2. Nếu chưa có cert, Traefik kích hoạt ACME client gửi yêu cầu cấp mới đến Let’s Encrypt.
  3. Let’s Encrypt gửi challenge HTTP-01 về port 80. Traefik chặn riêng request này và trả về token xác thực hợp lệ.
  4. Sau khi xác thực thành công, Let’s Encrypt gửi về cặp chứng chỉ cùng khóa riêng tư (private key).
  5. Traefik lưu thông tin vào acme.json rồi nạp thẳng vào bộ nhớ. Khi hạn dùng của chứng chỉ còn dưới 30 ngày, Traefik sẽ tự động renew định kỳ.

Cấu hình nâng cao

1. Bảo vệ Dashboard của Traefik bằng Basic Auth

Dashboard mặc định đã tắt chế độ công khai (insecure: false). Để mở dashboard qua subdomain riêng (như traefik.example.com), ta kết nối router tới service nội bộ api@internal kèm middleware xác thực:

# Tạo hash mật khẩu (cài đặt: sudo dnf install -y httpd-tools)
htpasswd -nb admin MatKhauManh_2026@Secure

Khai báo thêm labels cho service Traefik trong compose.yml:

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
      - "traefik.http.routers.dashboard.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xyz...chuoi_hash_o_tren"

Lưu ý: Trong file compose, mọi ký tự $ trong chuỗi mật khẩu hash phải được viết thành $$ để tránh bị hiểu nhầm là biến môi trường.

2. Cấp chứng chỉ Wildcard qua DNS-01 Challenge (Cloudflare)

Phương thức HTTP-01 bị giới hạn: không cấp được chứng chỉ wildcard dạng *.example.com và bắt buộc server phải mở port 80 ra Internet. Với các service nội bộ, DNS challenge là lựa chọn tối ưu:

# Bổ sung vào traefik.yml
certificatesResolvers:
  dns-cloudflare:
    acme:
      email: "[email protected]"
      storage: "/acme.json"
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

Thêm API token của Cloudflare vào biến môi trường trong file compose của Traefik:

    environment:
      - CF_DNS_API_TOKEN=vj98aX_example_cloudflare_token

Kinh nghiệm vận hành thực tế trên Production

Vài lưu ý quan trọng giúp bạn tránh sự cố khi vận hành hệ thống:

  • Chế độ phân quyền của file acme.json: Traefik sẽ lập tức dừng khởi động nếu file này có quyền khác 600. Nếu bạn vô tình dùng lệnh chmod thành 644 hoặc copy đè từ máy khác sang, Traefik sẽ báo lỗi permissions 0644 for /acme.json are too open.
  • Cẩn thận với rate-limit của Let’s Encrypt: Khi thử nghiệm cấu hình, hãy trỏ tạm về staging server bằng cách thêm caServer: "https://acme-staging-v02.api.letsencrypt.org/directory". Let’s Encrypt áp trần 5 lần thử thất bại mỗi giờ cho một account/hostname. Vượt ngưỡng này, IP của bạn sẽ bị tạm khóa cấp cert.
  • Bật Access Log dạng JSON để tiện trace lỗi: Thêm accessLog: { format: json } vào file cấu hình. Định dạng JSON giúp bạn lọc và đẩy log trực tiếp qua Fluentbit/Promtail về Grafana Loki, hiển thị rõ thời gian phản hồi upstream và HTTP status của từng container.
  • Lỗi 404 do lệch Network: 90% trường hợp web báo lỗi 404 sau khi gắn label là do container ứng dụng chưa join vào network proxy-net cùng Traefik. Bridge network mặc định của Podman không tự phân giải tên máy chủ giữa các container với nhau.
Share: