点击展开更新日志

2025

11-23

【新增】创建了这篇文章,几乎完成了所有内容

nexttime

会有些什么呢(❁´◡`❁)

源起

其实也没有特别的起因,只是突然想折腾,想找个网关应用做 Docker 统一入口。那么,原来的 openresty 不香了吗?真要从功能性上说,感觉二者差不多,第一次需要配置的内容也不少,单纯就是想折腾了。

概要

引用官网介绍: “Traefik 是您的一站式、自托管、云原生、基于 GitOps 驱动的应用代理、API 网关和 API 管理平台。”当然,这是商业版的功能,我们能免费用的开源版本就只有描述的一丢丢功能了。

简言之,traefik(发音:traffic) 也是一款反向代理和负载均衡器,只是相较于 Nginx,与 Docker,k8s 容器环境适配性更高:

  • 能够读取 Docker 标签(label) 进行配置
  • 动态配置:可以直接监听 Docker 守护进程,自动发现容器并自动配置路由、证书(需要提前配置好规则)

部署

环境要求:

  • Docker/Docker compose(建议)
  • (建议)SSL证书
  • htpasswd 工具,可以用网页工具,搜一下就有

以下部署文件结合了官方文档和实际情况,仅供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
services:
traefik:
container_name: traefik
image: traefik:latest
restart: unless-stopped
security_opt:
- no-new-privileges:true
command:
# EntryPoints 入口点配置
## 定义名为 web 的入口点,监听 80 端口
- "--entrypoints.web.address=:80"
## 强制HTTPS重定向
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
## 定义名为 websecure 的入口点,监听 443 端口
- "--entrypoints.websecure.address=:443"
## 启动 TLS,默认支持http2
- "--entrypoints.websecure.http.tls=true"


# 动态配置
# Attach the static configuration tls.yaml file that contains the tls configuration settings
## 证书及其它通用中间件配置
# - "--providers.file.filename=/dynamic/tls.yaml"
- "--providers.file.directory=/dynamic"
- "--providers.file.watch=true" # 启用监控,文件更改后自动更新

# Providers
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
## 只监听并代理到 app Docker 网络的容器
- "--providers.docker.network=app"

# API & Dashboard
- "--api.dashboard=true"
- "--api.insecure=true"

# Observability 日志及指标等可观测项,我因为需要接入
- "--log.level=INFO"
- "--accesslog=true"
- "--metrics.prometheus=true"

ports:
- "80:80"
- "443:443"
- "8081:8080"

volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/certs:ro
- ./dynamic:/dynamic:ro

# Traefik Dynamic configuration via Docker labels
labels:
# Enable self‑routing 将 traefik 自身纳入路由管理
- "traefik.enable=true"

# Dashboard router
- "traefik.http.routers.dashboard.rule=Host(`traefik.mydomain.top`)" # 访问域名
- "traefik.http.routers.dashboard.entrypoints=websecure"
# 声明为内部服务
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls=true"

# Basic‑auth middleware :启动基本认证,密码采用htpasswd加密
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:{SHA}MAh0E5q7UL4auBtOnVyA8zE1PT4="
- "traefik.http.routers.dashboard.middlewares=dashboard-auth@docker"

# 监听的容器网络,如果有其它网络的容器需要纳入管理,需要手动添加,这里比如我将loki网络的容器接了进来
networks:
- app
- loki

networks:
app:
external: "true"
loki:
external: "true"
  1. 创建容器目录

    1
    mkdir -p /data/docker/traefik/{certs,dynamic}
    • certs: 保存SSL证书
    • dynamic: 动态配置
  2. 将以上内容保存为 docker-compose.yml 文件

    已对各配置项进行了必要说明,如果仍有疑问请自行查询。

  3. 将证书放到 certs 目录下,比如文件名为: cert.crtkey.pem

  4. dynamic 目录下创建文件 tls.yaml

    1
    2
    3
    4
    5
    # tls.yaml
    tls:
    certificates:
    - certFile: /certs/cert.crt
    keyFile: /certs/key.pem
  5. 启动服务

  6. 测试验证
    此时就可以通过域名访问管理面板了。如果暂时不需要证书或者想通过ip访问,注释/删除 https 相关部分,同时将 --api.insecure=false 设置为 false。

Docker 服务配置

现在就需要将我们的 Docker 服务纳入 traefik 进行配置,实现方式就只需要为应用加上相关标签配置即可。

adguardHome 为例,在 docker-compose.yml 中添加 traefik 标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
services:
adguardhome:
container_name: adguardhome
image: adguard/adguardhome
ports:
- 53:53/tcp
- 53:53/udp
# - 3000:3000/tcp
- 853:853/tcp
- 784:784/udp
- 853:853/udp
- 8853:8853/udp
- 5443:5443/tcp
- 5443:5443/udp
volumes:
- agh_data:/opt/adguardhome/work
- agh_conf:/opt/adguardhome/conf
restart: unless-stopped
labels:
# 启用 traefik 管理容器
- "traefik.enable=true"
# 定义 Router: 使用 websecure 443端口作为入口点
- "traefik.http.routers.adguardhome.entrypoints=websecure"
# 定义路由规则:域名
- "traefik.http.routers.adguardhome.rule=Host(`ah.mydomain.top`)"
# 启用 TLS(HTTPS)
- "traefik.http.routers.adguardhome.tls=true"
# 定义 Services: 服务端口
- "traefik.http.services.adguardhome.loadbalancer.server.port=3000"
- "traefik.http.routers.adguardhome.middlewares=securityHeaders@file"
# 指定服务所在网络,如果不修改服务原有网络,则 traefik 需要新增该网络
- "traefik.docker.network=adguardhome"

networks:
- adguardhome

volumes:
agh_data:
name: agh_data
agh_conf:
name: agh_config
networks:
adguardhome:
external: "true"

然后重新部署,确认好域名解析正确(需要解析到 traefik 所在服务),就可以在管理面板看到新增的 adguardHome 服务,就可以通过域名访问了。此外,原本保留的管理端口 3000 映射也可以选择注释掉,通过 traefik 内部网络路由,不直接对外。

总结,将已有应用集成到 traefik 需要做的配置:

  1. 移除端口映射
  2. 添加 traefik 标签
  3. 确保容器和 traefik 处于同一容器网络。那么是否可以将 traefik 默认的 app 网络加到每个容器呢,至少在我的环境中不可行(cloudbeaver无法连接数据库,仅保留单一内部网络正常),所以不考虑了。

如果你想让一个域名下的不同路径代理到不同的服务,可以使用 PathPrefix 规则:

1
2
3
4
5
6
7
# 示例:通过路径代理到服务 A
labels:
- "traefik.enable=true"
- "traefik.http.routers.app-a.entrypoints=web"
# 域名不变,但路径不同
- "traefik.http.routers.app-a.rule=Host(`serviceA.mydomain.top`) && PathPrefix(`/service-a`)"
- "traefik.http.services.app-a.loadbalancer.server.port=9000"

非 Docker 服务配置

其它一些比如 pve 或网络模式为 host 的服务也可以使用 traefik 代理,这里就需要使用 traefik file provider,以 pve 为例。

  1. 启动 file provider:

    1
    2
    3
    4
    5
    6
     # 原始配置
    - "--providers.file.filename=/dynamic/tls.yaml"

    # ➡️ 修改为:加载整个目录
    - "--providers.file.directory=/dynamic"
    - "--providers.file.watch=true" # 启用监控,文件更改后自动更新
  2. dynamic 目录下新增 pve.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # dynamic/pve.yaml

    http:
    # 1. 定义路由器 (Router)
    routers:
    pve-router:
    # ⚠️ 关键:使用 websecure 入口点 (443)
    entryPoints:
    - websecure
    # 路由规则:匹配 pve.mydomain.com 域名
    rule: Host(`pve.mydomain.com`)
    service: pve-service
    # 启用 TLS
    tls: true

    # 2. 定义服务 (Service)
    services:
    pve-service:
    loadBalancer:
    servers:
    # ⚠️ 关键:指向 PVE 的 IP 和默认 HTTPS 端口 8006
    - url: "https://192.168.1.100:8006"
    # PVE 使用自签名证书,需要告诉 Traefik 忽略证书验证
    serversTransport: pve-transport

    # 3. 定义传输配置 (ServersTransport)
    # 用于处理 PVE 的自签名证书
    serversTransports:
    pve-transport:
    # ⚠️ 关键:跳过后端 HTTPS 证书验证
    insecureSkipVerify: true
    • traefik 默认支持 websocket,因此无需特别配置。
  3. 重新部署

证书

当前我的情况是配置了通知手动做更新,还没有试过 traefik 自带的,先空着,有时间弄了再补充。

解惑

指定服务端口:- "traefik.http.services.homepage.loadbalancer.server.port=3000"这行是否是必要的,为什么使用loadbalancer?

“traefik.http.services.homepage.loadbalancer.server.port=3000” 是必要的,但并不是因为真的在“负载均衡”。它的作用是告诉 Traefik:“Homepage 容器内部的服务端口是 3000。”

当 Traefik 收到一个针对 homepage.mydomain.top 的请求时,它会查找名为 homepage 的服务定义,然后使用该定义中的端口号 3000,通过 Docker 内部网络将请求转发给 homepage 容器。如果没有显式指定 3000 端口,traefik 会查找 Docker 镜像元数据中定义的第一个暴露端口,可能会转发失败。

使用 loadbalancer 是因为这是 Traefik Service 的唯一类型,即使你只有一个后端容器(您的 homepage 实例),Traefik 也把它看作是一个负载均衡器,只不过这个负载均衡器只有一个后端目标。

容器配置traefik.enable=true有什么作用,有无安全风险,是否对外暴露端口?

作用是启动服务发现,因为在 traefik 配置中配置了:

1
2
# 在 traefik 服务的 command 中
- "--providers.docker.exposedbydefault=false"

默认情况下,不会代理任何容器,使用 traefik.enable=true 标签就是明确告诉 traefik 代理当前容器。本身不会对外暴露端口。

如果pve有多个实例(集群部署),如何配置多个ip端口

对于 PVE 集群或具有多个后端实例的服务,只需要在 loadBalancer 的配置下添加多个 server URL 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# dynamic/pve.yaml (修改后的 Service 部分)

http:
# ... (Router 配置保持不变)

services:
pve-service: # 服务的名称保持不变
loadBalancer:
servers:
# 实例 A (主节点)
- url: "https://192.168.1.100:8006"
# 实例 B (副节点 1)
- url: "https://192.168.1.101:8006"
# 实例 C (副节点 2)
- url: "https://192.168.1.102:8006"
# 其他配置保持不变(例如:insecureSkipVerify: true)
serversTransport: pve-transport

# ... (ServersTransport 配置保持不变)

默认使用轮询(roundrobin)进行流量分发,对于PVE需要保持用户会话的应用,可以配置粘性会话(Cookie-based Affinity):

1
2
3
4
5
6
7
8
9
# 示例:启用基于 Cookie 的粘性会话
loadBalancer:
servers:
# ... 多个服务器
sticky:
cookie:
name: traefik_pve_sticky
secure: true
httpOnly: true

这里的 httpOnly: true 表示当一个 Cookie 设置了 httpOnly: true 属性后,该 Cookie 将无法通过客户端脚本(例如 JavaScript 中的 document.cookie API)被访问、读取或修改。主要目的是防御 XSS(跨站脚本工具)。

portainer 配置 traefik 之后域名访问报错:ERR_HTTP2_PROTOCOL_ERROR

这是因为双重HTTPS导致,Traefik 向 Portainer 的 9443 端口发送了一个 HTTP 请求,但 Portainer 期望收到一个 TLS/HTTPS 握手请求。Portainer 无法理解这个非加密的请求,因此立即关闭连接,导致 Traefik 无法完成响应,最终浏览器收到 ERR_HTTP2_PROTOCOL_ERROR。

需要在标签中加入 - "traefik.http.services.potainer.loadbalancer.server.scheme=https" ,明确说明使用 https 访问后端。