Traefik 全自动 HTTPS 方案

Traefik 全自动 HTTPS 方案

Traefik 是一个用于 Docker 中服务以不同子域名公开到服务器 80/443 端口的工具.

  1. 可以自动实现源站 HTTPS.
  2. 配合 Cloudflare 的泛域名解析可以自动化子域名建立.
  3. 实现各种附加功能 (Basic Auth, 负载均衡, etc.)

但是, Traefik 由于配置文件编写比较复杂, 使用的人并不多. 这里分享一个简单的解决方案.

原理

Traefik 启动时会读取配置文件和 Docker Compose 配置中的各个服务. 请求进入时候, 将会按这样一个顺序解析:

 - Entrypoint (入口, 80/443)
 -> Router (规则, 例如子域名为 www)
 -> Service (服务)
 -> LoadBalancer (源地址, 这里可以设定用 Docker 容器内的哪个端口)

同时, 启动时会根据你设定的域名, 向 Let’s Encrypt 申请泛域名 ACME 证书, 用于 HTTPS.

安装配置

首先当然需要 Docker 和 Docker Compose (Tutorial).

然后, 新建一个文件夹, 新建一个 docker-compose.yml:

services:
  traefik:
    container_name: traefik
    image: traefik
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    security_opt:
      - no-new-privileges:true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/static.yml:/traefik.yml:ro
      - ./traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
      - ./traefik/acme.json:/acme.json:rw
      - ./traefik/traefik.log:/var/log/traefik.log:rw
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - ./.env/traefik.env
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.routers.traefik.middlewares=user-auth@file

然后创建一个文件夹 traefik 存配置和日志, 一个文件夹 .env 存敏感的密钥:

mkdir traefik
touch traefik/{static.yml, dynamic.yml, acme.json, traefik.log}
mkdir .env
touch .env/traefik

这几个文件分别的作用及内容:

  • traefik/static.yml: 静态配置, 改了需要重启 Traefik.
  • traefik/dynamic.yml: 动态配置, 改了不需要重启 Traefik.
  • traefik/acme.json: 申请 HTTPS 时自动存储的密钥.
  • traefik/traefik.log: 日志文件.
  • .env/traefik: 密钥存储文件.

接下来需要填入三个文件:

第一个文件 traefik/static.yml (请将example.com 替换为你的域名):

log:
  level: INFO
  filePath: "/var/log/traefik.log"
api:
  dashboard: true
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: le-dns
        domains:
          - main: example.com
            sans:
              - "*.example.com"
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: web
    defaultRule: Host(`{{if index .Labels "com.docker.compose.service" }}{{ index .Labels "com.docker.compose.service" }}.example.com{{else}}{{ trimPrefix `/` .Name }}.example.com{{end}}`)
  file:
    filename: "/etc/traefik/dynamic.yml"
certificatesResolvers:
  le-dns:
    acme:
      storage: "/acme.json"
      email: [email protected]
      dnsChallenge:
        provider: cloudflare

这里需要解释一下:

  1. defaultRule 这一行, 表示当启用 traefik 时, 默认使用 docker-compose 中的服务名作为子域名.
  2. provider 这一行, 如果不使用 cloudflare 作为 DNS 服务商, 可以自行根据WIKI修改.

第二个文件 traefik/dynamic.yml:

http:
  routers:
    all-https:
      rule: "HostRegexp(`{host:.+}`)"
      entryPoints:
        - web
      tls: true
      middlewares:
        - to-https
      priority: 1000
      service: noop@internal
  middlewares:
    to-https:
      redirectScheme:
        scheme: https
    user-auth:
      basicAuth:
        users:
          - "user:$2y$05$A72U7Ngb6LYYBV.PUawrFOII9tAMMiV.fk8tJQwsHts1Q6yrxLpTS"

这里需要解释一下:

  1. all-https 这一行, 表示在 web 入口 (80端口, 也就是 HTTP) 请求任何子域名时, 使用 to-https 中间件 (也就是重定向到 https). 该条目优先级为 1000, 也就是我们如果设定某个服务优先级为 1000 以上, 就可以不自动跳转 HTTPS.
  2. user-auth 这一行, 表示使用 user-auth 这个中间件的服务, 将启用 Basic Auth 认证, 用户列表可以通过如下命令生成:
    docker run --entrypoint htpasswd httpd:2 -Bbn user 123456

第三个文件 .env/traefik:

CLOUDFLARE_DNS_API_TOKEN=<Cloudflare 40 位 Token>

这个主要是用于启用 HTTPS 时, 需要通过 Let’s Encrypt 的所有权认证, Traefik 会自动新建一个临时的 DNS 解析记录用于认证. 怎么获得?

进入 Cloudflare 控制台, 进入 My Profile 中的 API Tokens, 创建一个 Token, 需要在 All zones 具有 Zone.Zone, Zone.DNS 权限, 完成后复制Token填入.

然后, 创建一个 Cloudflare 的泛解析记录到服务器:

如果你的 DNS 服务商不支持, 你也可以一个一个子域名创建.

这里解释一下, 如果你有其他的子域名:

- *.example.com -> 1.1.1.1
- abc.example.com -> 2.2.2.2

那么 abc.example.com 的解析优先, 不会被泛解析记录覆盖.

最后, 使用任何 Docker 服务并自动部署!

chatgpt-next-web 为例:

docker-compose.yaml 中的services 中新建一个 next 服务:

services:
  traefik:
    ...
  next:
    image: yidadaa/chatgpt-next-web
    container_name: next
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.services.next.loadbalancer.server.port=3000

然后在 docker-compose.yml 同级目录运行:

docker-compose up -d

这样, 这个网站就会自动部署到 next.example.com, 并连接到容器内服务的 3000 端口.

如果你需要自定义子域名:

services:
  traefik:
    ...
  next:
    image: yidadaa/chatgpt-next-web
    container_name: next
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.routers.next.rule=Host(`nextchat.example.com`)
      - traefik.http.services.next.loadbalancer.server.port=3000

这样就会自动部署到 nextchat.example.com

其他功能

如果有其他功能的需求, 可以回复更新.

18 个赞

感谢大佬分享!

感谢分享大佬厉害啊

1 个赞

路由报错了


会出现这个, 但是不影响使用, 如果在意可以修改:
dynamic.yml 为:

http:
  routers:
    all-https:
      rule: "HostRegexp(`{host:.+}`)"
      entryPoints:
        - web
      tls: true
      middlewares:
        - to-https
      priority: 1000
      service: noop@internal
  middlewares:
    to-https:
      redirectScheme:
        scheme: https
    user-auth:
      basicAuth:
        users:
          - "user:$2y$05$A72U7Ngb6LYYBV.PUawrFOII9tAMMiV.fk8tJQwsHts1Q6yrxLpTS"

加了 service: noop@internal 这一行.
帖子里已做了对应的修改.
Thanks.

不错不错 赞啦 :+1: