Traefik 实战:自动申请 HTTPS 证书并代理 Docker 服务
Traefik 是一款反向代理软件,相比传统工具,具有更强的自动化能力。例如,它能自动感知后端服务的变化,实时更新路由配置而无需重启;还内置支持 ACME,可自动申请并续签 HTTPS 证书。
本文切入点是 Traefik 与 Docker 的集成,并实现自动配置 HTTPS,其它功能暂不涉及。
参考的是官方文档:https://doc.traefik.io/traefik/v3.4/user-guides/docker-compose/acme-dns/
和官方文档中不同的是,我们将 Traefik 和后端服务放在不同的 compose 文件中,通过 external network 连接到一个共享网络,这样服务之间更加解藕。而且更加体现 Traefik 自动感知和自动创建路由的能力,比如 Traefik 服务先启动,然后开始监听 Docker 容器事件,如果监听到后端服务配置了带有 Traefik 的 label,能够自动创建路由;反过来,当后端服务先启动,Traefik 后启动之后,会一次性扫描正在运行中的容器,找到符合的容器并自动创建路由。
创建共享网络
如上面提到的,我们采用共享网络的方式来使用 Traefik。运行如下命令来创建:
docker network create traefik-proxy
配置和运行 Traefik 服务
创建 docker-compose.yaml
文件:
services:
traefik:
image: "traefik:v3.4"
restart: "unless-stopped"
ports:
- "443:443"
command:
# 注释掉这两项,开发调试才会使用
# - "--log.level=DEBUG"
# - "--api.insecure=true"
# 表示 Traefik 会去监听 Docker 的容器事件,自动发现服务
- "--providers.docker=true"
# 默认不暴露容器,只有显式加了 "traefik.enable=true" label 的容器才会被代理
- "--providers.docker.exposedbydefault=false"
# 监听 443 端口,用于接收 HTTPS 请求
- "--entryPoints.websecure.address=:443"
# 启用名为 dnsresolver_cf 的解析器,用 DNS 方式自动申请 HTTPS 证书
- "--certificatesresolvers.dnsresolver_cf.acme.dnschallenge=true"
# 指定 dnsresolver_cf 使用哪个提供商,比如这里使用 Cloudflare,
# 那么 Traefik 会使用 Cloudflare 的 API 来完成 DNS 验证,从而申请 Let's Encrypt 证书。
# 更多 providers 可参考:https://doc.traefik.io/traefik/v3.4/https/acme/#providers
- "--certificatesresolvers.dnsresolver_cf.acme.dnschallenge.provider=cloudflare"
# 指定 dnsresolver_cf 的证书存放位置
- "--certificatesresolvers.dnsresolver_cf.acme.storage=/letsencrypt/cf.json"
environment:
# Cloudflare provider 需要的环境变量,此 token 在 Cloudflare 后台申请,
# 且需要具有 Zone:Zone:Read, Zone:DNS:Edit 权限。
- "CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}"
volumes:
# 证书存放位置
- "./letsencrypt:/letsencrypt"
# 必须挂载,这是 Docker 守护进程的 Unix Socket 接口,Traefik 通过它才能实现监听容器事件
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- "traefik-proxy"
networks:
traefik-proxy:
external: "true"
name: "traefik-proxy"
创建 .env
文件,因为我们使用到了 CF_DNS_API_TOKEN
环境变量,所以需要在 docker-compose.yaml
同一文件夹内创建此文件:
CF_DNS_API_TOKEN=your_token
最后使用如下命令来运行 Traefik 服务:
docker compose up -d
配置和运行 whoami
whoami 是上文提到的后端服务,是一个简单的 HTTP server。
创建 docker-compose.yaml
文件:
services:
whoami:
image: "traefik/whoami"
restart: "unless-stopped"
labels:
# 因为我们配置了 "--providers.docker.exposedbydefault=false", 所以需要显式启用该服务的暴露
- "traefik.enable=true"
# 定义路由器的规则:当请求的 Host 是 whoami.mwum.com(改成你自己的域名)时,触发该路由器,
# 路由名称是 whoami,可以改成其它的,注意下方的 entrypoints / tls 配置名称也要一致
- "traefik.http.routers.whoami.rule=Host(`whoami.mwum.com`)"
# 通过 "--entryPoints.websecure.address=:443" 这里配置的 websecure 路由到 whoami 服务
- "traefik.http.routers.whoami.entrypoints=websecure"
# 这里的 certresolver 填成 "--certificatesresolvers.dnsresolver_cf.acme.dnschallenge=true" 这里所启用的
- "traefik.http.routers.whoami.tls.certresolver=dnsresolver_cf"
networks:
- "traefik-proxy"
networks:
traefik-proxy:
external: "true"
name: "traefik-proxy"
使用如下命令来运行 whoami 服务:
docker compose up -d
进一步解释 Traefik 的自动发现
现在我们能够通过 https://whoami.mwum.com/ 使用 HTTPS 方式访问到 whoami 服务了。
可以看到我们在 Traefik 的配置中并没有任何和 whoami 相关的内容,这在传统的反向代理工具中是不可思议的。在 Docker 中我们是通过 label 的方式去实现的,通过给 whoami 服务添加 label 来告诉 Traefik 是否代理服务,host 是什么,如何路由,使用哪个证书解析器等,且 Traefik 能够自动发现 whoami 服务。
另外你可能会好奇我们没有在 whoami 中配置暴露什么端口,Traefik 是如何知道的呢?
我们对 whoami 服务运行如下命令可以看到:
$ docker compose ps
Name Command State Ports
------------------------------------------
whoami_whoami_1 /whoami Up 80/tcp
whoami 没有对外暴露任何端口,只是在容器内暴露了 80 端口,我们在 Traefik 中也没有配置要监听 whoami 服务的什么端口,但是 Traefik 默认会自动读取 whoami 服务的 EXPOSE
端口,作为目标服务的后端地址。