我從Caddy遷移到了Traefik


Caddy跟Traefik都有反向代理的功能,主要用途是為平衡負載後端服務並提供自動HTTPS配置。最近我從Caddy轉到使用Traefik,下文來分享一下過程跟一點點感想。

Nginx到Caddy

在講Caddy前必須提一下老牌網頁伺服器Nginx,它一樣有反向代理的功能。在過了Apache2入門後,下一個接觸的網頁伺服器就是Nginx。但Nginx在配置上(尤其是HTTPS)還是挺自由複雜的,需要搭配Cert Bot之類的程式才能做到證書自動續約。雖然可以,但不好用。網上也有開源專案Nginx Proxy Manager幫忙整合HTTPS和提供人性化Web UI配置,(但偶爾會有點不穩定,所以放棄了)

這時候Caddy就出現在我眼前,標榜自動HTTPS和HTTP/2,配置檔案語法也簡單,於是我就毫不猶豫跳槽到Caddy。誰用誰知道,用過都說好,自從用上了Caddy,我就再也不想回去Nginx了。

Cloud Native時代 – Traefik

Caddy確實穩定地服務了好幾年,從在主機直接跑服務到用Docker跑服務,唯有Caddy一直不變(好的意義上)。如果沒有一點點契機,那我還不一定會去動我們的老大Caddy。(契機是什麼?就是我蹲到新的OVH Cloud新加坡機房的VPS機器啦,相同價錢,配置大升級)

Traefik的功能定位上其實跟Caddy是有些微差別,Caddy的定位是傻瓜式配置的Web伺服器,而Traefik則是Cloud Native反向代理伺服器,對Docker有良好支援。正好我99.9%的服務已經轉移到Docker上跑,搭配Traefik有如虎添翼的效果。

對的,Traefik也是用Docker就能跑起來,可以放我在正式環境跑的compose配置當例子:

YAML
services:
  traefik:
    image: traefik:v3.5
    command:
      - --api.dashboard=true
      - --providers.file.filename=/etc/traefik/dynamic.yml
      - --providers.docker=true
      - --providers.docker.network=proxy
      - --providers.docker.exposedByDefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls=true
      - --entrypoints.websecure.http.tls.certresolver=cloudflare
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --certificatesresolvers.cloudflare.acme.dnschallenge=true
      - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare
      - --certificatesresolvers.cloudflare.acme.email=example@example.com
      - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json
    environment:
      - CF_DNS_API_TOKEN=${CLOUDFLARE_API_TOKEN}
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
      - ./dynamic.yml:/etc/traefik/dynamic.yml
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.rule=Host(`traefik.reinforce.moe`)
      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.routers.traefik.middlewares=tinyauth
networks:
  proxy:
    external: true

上面的compose檔案分別做了以下幾件事:

  • 監聽80和443端口
  • TLS證書用Cloudflare DNS方式來驗證
  • 自動將http導向到https
  • 從Docker Label來動態設定服務路由(就是反向代理)
  • 將Traefik Dashboard放到https://traefik.reinforce.moe並添加登入驗證

順帶一提我用的是CLI方式來設定Traefik,其實也可以用YAML檔案來配置,但直接寫在compose檔案的好處是可以用Docker網頁界面(例如Portainer)管理配置,跟SSH和Vim說再見。

Traefik跑起來之後,就可以在其他服務的compose檔案設定Docker Label。這裡繼續大方分享正式環境的compose檔案:

YAML
version: "3.9"
services:
  server:
    build: .
    restart: always
    environment:
      TZ: Asia/Hong_Kong
    volumes:
      - ./data/www:/var/www/html
      - ./data/my_php.ini:/usr/local/etc/php/conf.d/my_php.ini
      - ./data/my_apache2.conf:/etc/apache2/conf-enabled/my_apache2.conf
    extra_hosts:
      - host.docker.internal:host-gateway
    labels:
      traefik.enable: true
      traefik.http.routers.apache2.rule: Host(`reinforce.moe`)
    networks:
      - database
      - proxy
networks:
  database:
    external: true
  proxy:
    external: true

要讓Traefik懂得將https://reinforce.moe/指到上面的PHP伺服器,只要兩行:啟用Traefik和設定域名;完。

還不夠?沒關係我再分享Portainer,這個比較困難,因為Portainer預設用https自簽證書跑,花了我5行Labels跟一個YAML設定檔案才搞定:

YAML
services:
  portainer:
    container_name: portainer
    image: portainer/portainer-ce:lts
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/data
      #ports:
      #- 9443:9443
      #- 8000:8000  # Remove if you do not intend to use Edge Agents
    networks:
      - proxy
    labels:
      traefik.enable: true
      traefik.http.routers.portainer.rule: Host(`portainer.reinforce.moe`)
      traefik.http.services.portainer.loadbalancer.server.port: 9443
      traefik.http.services.portainer.loadbalancer.server.scheme: https
      traefik.http.services.portainer.loadbalancer.serverstransport: skipssl@file
networks:
  default:
    name: portainer_network
  proxy:
    external: true
YAML
http:
  serversTransports:
    skipssl:
      insecureSkipVerify: true

(不知道是Bug還是Feature,沒辦法用CLI或者Label來設定serversTransports,只好用YAML檔案了)

可以看到內部服務就這樣透過Traefik開放出來了,不需要再想要用哪個Port,每個服務實際跑在哪個網址也能一眼看到,開新服務也不用再SSH到主機上用Vim去手動修改Caddy配置檔案,所有東西在Web界面就能做,方便。