Dokku 集成 Traefik 代理:基于 Docker Label 的应用路由、健康检查与 Letsencrypt 配置实战指南
【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku
导读
本文面向使用 Dokku(Docker 驱动的 PaaS)构建与托管应用生命周期的开发者,系统讲解如何将默认的 nginx 代理切换为 Traefik,并利用 Traefik 的 Docker Label 集成实现应用路由。你将掌握:Traefik 插件的路由规则与端口映射约定、app.json健康检查到 Traefik 标签的自动转换、容器生命周期管理(traefik:start/stop/logs/show-config)、自定义标签注入、Letsencrypt 证书签发(TLS-ALPN-01 与 DNS-01 两种挑战模式),以及通过traefik:report排障与审计配置的完整方法。文中所有结论均可在 plugins/traefik-vhosts 插件的源码与模板中找到实现依据。
Traefik 插件概述与工作原理
Traefik 是 Dokku 的可选反向代理实现,自 Dokku 0.28.0 起提供官方集成(见 docs/networking/proxies/traefik.md 顶部说明)。与 nginx 代理“由 Dokku 生成并管理配置文件”不同,Traefik 插件走的是Docker Label 驱动的路线:Dokku 在部署时把路由规则以容器标签的形式注入web进程容器,Traefik 通过 Docker Provider 监听这些标签并自动完成服务发现与路由,无需 Dokku 侧再维护代理配置文件。
插件的命令入口如下(完整命令清单见 plugins/traefik-vhosts/commands 与 plugins/traefik-vhosts/help-functions):
traefik:report [<app>] [<flag>] # Displays a traefik report for one or more apps traefik:logs [--num num] [--tail] # Display traefik log output traefik:set <app> <property> (<value>) # Set or clear an traefik property for an app traefik:show-config <app> # Display traefik compose config traefik:start # Starts the traefik server traefik:stop # Stops the traefik server前置要求
使用 Traefik 插件的前提是 Docker 主机已安装docker-compose-plugin。插件通过docker compose up/docker compose down管理 Traefik 容器——这一点可以直接在 plugins/traefik-vhosts/command-functions 的cmd-traefik-start/cmd-traefik-stop中看到:两者都会先调用fn-is-compose-installed检查 compose 插件,失败则直接dokku_log_fail "Required docker compose plugin is not installed",随后分别调用 common 模块的compose-up与compose-down。
# 安装 docker-compose-plugin(以 Debian/Ubuntu 系为例,具体以 Docker 官方安装文档为准) apt-get install docker-compose-plugin路由规则:理解 Traefik 插件的边界
[!WARNING] 在同一台 Dokku 主机上同时使用多个代理插件会导致请求路由异常,应当避免。由于默认代理实现是 nginx,切换 Traefik 前建议先停止 nginx 服务。
Traefik 插件的路由行为遵循以下规则(同样反映在 plugins/traefik-vhosts/docker-args-process-deploy 的标签注入逻辑中):
- Traefik 集成通过容器上附加的Docker labels暴露,标签变更需要应用重新部署或重建(
dokku ps:rebuild)才会生效; - 虽然 Traefik 会尊重其他容器携带的标签,但插件只给
web进程注入 Traefik 标签(docker-args-process-deploy中PROC_TYPE != "web"时直接return); - 仅支持
http:80与https:443端口映射,多端口映射时只代理一个 http 端口和一个 https 端口; - 找不到
http:80映射时,取第一个 http 端口映射为 http 请求服务; - 找不到
https:443映射时,取第一个 https 端口映射为 https 请求服务; - 完全没有 https 映射时,https 请求会复用
http:80的容器端口; - 容器通过运行与健康检查后,请求立即被路由;
app.json中带path属性的 readiness 健康检查会被自动转换为 Traefik 健康检查标签。
从源码看,端口映射的解析发生在 docker-args-process-deploy:插件遍历ports-get返回的端口映射列表,分别记录http与https方案下的“首个候选端口”和“恰好为 80/443 的精确端口”,优先使用精确映射,找不到时回退到首个候选并在日志中打印Warning: http:80 port mapping not found。
标签注入的完整链路
部署时,docker-args-process-deploy触发器会拼装出如下形式的标签($APP-$PROC_TYPE形如node-js-app-web):
--label traefik.enable=true --label traefik.http.services.$APP-$PROC_TYPE-http.loadbalancer.server.port=<container-port> --label traefik.http.routers.$APP-$PROC_TYPE-http.entrypoints=<http-entry-point> --label traefik.http.routers.$APP-$PROC_TYPE-http.service=$APP-$PROC_TYPE-http --label "traefik.http.routers.$APP-$PROC_TYPE-http.rule=Host(`app.example.com`) || Host(`app2.example.com`)"域名为多个时,规则由Host(\domain1`) || Host(`domain2`)拼接而成(见 [docker-args-process-deploy](https://link.gitcode.com/i/add60937da2ec4faa8b8e1b5b7398633#L84-L99));存在 https 端口时还会追加traefik.http.routers...https.tls.certresolver=leresolver`,将 TLS 证书解析器指向插件内置的 letsencrypt resolver。
健康检查:从 app.json 到 Traefik 标签
当应用的app.json中定义了带path属性的 readiness 健康检查时,Dokku 自动生成 Traefik 健康检查标签,Traefik 会在路由流量前先对容器执行健康检查。属性映射关系如下:
| app.json Property | Traefik Label Property | Description |
|---|---|---|
path | healthcheck.path | The HTTP path to check (required) |
scheme | healthcheck.scheme | The scheme to use (httporhttps) |
port | healthcheck.port | The port to check |
timeout | healthcheck.timeout | Timeout in seconds (formatted asXs) |
wait | healthcheck.interval | Interval between checks in seconds (formatted asXs) |
示例app.json:
{ "healthchecks": { "web": [ { "name": "web readiness check", "path": "/health", "timeout": 5, "type": "readiness", "wait": 10 } ] } }[!NOTE] 只使用第一个带
path属性的 readiness 健康检查。liveness、startup 及基于命令的健康检查不会被转换为 Traefik 标签。
实现层面,docker-args-process-deploy 使用 jq 过滤器选择健康检查并生成对应标签:
hc_filter='.healthchecks.web // [] | map(select(.type == "readiness" and .path != null and .path != "")) | .[0]'随后将timeout与wait分别格式化为${timeout}s、${wait}s,写入loadbalancer.healthcheck.timeout与loadbalancer.healthcheck.interval标签——与文档映射表中“formatted asXs”的说明完全一致。
切换到 Traefik 代理
为指定应用启用 Traefik 代理使用proxy:set:
dokku proxy:set node-js-app type traefik这将启用基于 Docker 标签的 Traefik 集成,此后所有部署都会注入 Traefik 可读取的路由标签。由于标签集成机制,需要一次部署或重建后请求才会被成功路由:
dokku ps:rebuild node-js-app域名或端口映射的任何变更同样需要一次部署或重建。插件通过docker-args-process-deploy中的proxy-type、proxy-is-enabled、domains-vhost-enabled三个触发器组合判断是否注入标签(docker-args-process-deploy),并在core-post-deploy阶段打印Routing app via traefik(见 plugins/traefik-vhosts/core-post-deploy)。
管理 Traefik 容器
启动 Traefik 容器
dokku traefik:start该命令内部通过docker compose up启动 Traefik 容器。从 command-functions 可以看到它还会先写入proxy-status属性为started,并创建权限为600的${DOKKU_LIB_ROOT}/data/traefik/traefik-acme.json(ACME 证书存储文件),然后基于 sigil 模板生成 compose 文件并执行compose-up。
停止 Traefik 容器
dokku traefik:stop容器会被停止并移除。若容器本就没有运行,该命令不会产生任何副作用。实现上调用compose-down并将proxy-status写为stopped(command-functions)。
查看 Traefik 容器日志
dokku traefik:logs支持两个修饰参数:
--num NUM # the number of lines to display --tail # continually stream logs组合使用示例——持续输出日志并保留最近 10 行历史:
dokku traefik:logs --tail --num 10实现中--num默认值为 100,--tail对应 docker logs 的--follow(internal-functions),日志容器名为traefik-traefik-1。
显示 Traefik compose 配置
调试时可用traefik:show-config输出当前生成的 compose 配置:
dokku traefik:show-config该命令将 sigil 模板渲染结果打印到标准输出。模板位于 plugins/traefik-vhosts/templates/compose.yml.sigil,其中包含入口点声明(--entrypoints.http.address=:80)、Docker provider(--providers.docker、--providers.docker.exposedByDefault=false)、API/Dashboard 开关、ACME resolver、数据卷(/var/run/docker.sock只读挂载与${DOKKU_LIB_ROOT}/data/traefik)等关键配置。插件还支持通过traefik-template-source触发器替换自定义模板(internal-functions)。
自定义 Traefik 容器镜像
默认镜像被硬编码,但可通过--global标志覆盖image属性:
dokku traefik:set --global image traefik:v2.8注意:当前仓库 plugins/traefik-vhosts/Dockerfile 的FROM traefik:v3.7.12才是实际默认镜像。未设置image属性时,computed 值会从该 Dockerfile 的FROM行解析得到(internal-functions)。因此上述示例命令请按你的目标版本改写,例如dokku traefik:set --global image traefik:v3.7.12。文档中报告示例输出的traefik:v2.8属于历史示例,不代表当前仓库的默认值。
全局属性配置
所有 Traefik 属性均为 global-only 作用域,即只能通过--global标志设置(traefik:set命令会对非全局 key 报错,见 subcommands/set)。
修改 entrypoint 名称
自托管 Traefik 实例的入口点名称可能与默认的http、https不同,可通过http-entry-point与https-entry-point属性自定义:
dokku traefik:set --global http-entry-point web dokku traefik:set --global https-entry-point websecure设置后需重建或重新部署应用,标签中的entrypoints值才会更新。
修改日志级别
Traefik 日志级别默认ERROR,可通过log-level属性调整(computed 值会强制大写):
dokku traefik:set --global log-level DEBUG修改后需要重启 Traefik 容器:
dokku traefik:restart # 实际操作为 traefik:stop && traefik:start[!NOTE] 插件本身没有
traefik:restart子命令,文档明确要求“修改后重启容器”,可依次执行dokku traefik:stop与dokku traefik:start。compose 模板中设置了restart: unless-stopped(compose.yml.sigil)。
Label 管理:注入自定义 Traefik 标签
插件允许为应用添加自定义容器标签,用于扩展默认行为之外的 Traefik 配置(例如中间件、限流、重定向等,可参考 Traefik 官方文档中可用的标签)。这些标签在部署时注入容器。
添加标签
dokku traefik:labels:add node-js-app traefik.directive value这会为应用容器添加traefik.directive=value标签,之后需重建或重新部署:
dokku ps:rebuild node-js-app移除标签
dokku traefik:labels:remove node-js-app traefik.directive移除后同样需要重建或重新部署才生效。
查看标签
查看应用全部自定义标签:
dokku traefik:labels:show node-js-app查看指定标签的值:
dokku traefik:labels:show node-js-app traefik.directive实现上,这三个命令分别委托给 proxy 插件的cmd-proxy-labels-add/remove/show,并传入代理类型traefik(command-functions);部署时自定义标签从fn-proxy-get-labels-file-path "traefik" "$APP"指向的标签文件读取并追加到docker run参数(docker-args-process-deploy)。
SSL 配置:Letsencrypt 证书自动化
Traefik 插件仅支持通过其内置 letsencrypt 集成签发自动证书,certs插件托管的证书会被忽略。
启用 letsencrypt 集成
默认情况下 letsencrypt 是禁用的,https 端口映射会被忽略。设置letsencrypt-email属性即可启用:
dokku traefik:set --global letsencrypt-email automated@dokku.sh启用后需要重建应用并重启 Traefik 容器,此后所有 http 请求都会被重定向到 https。从 compose.yml.sigil 可以看到,设置邮箱后模板会追加 http 到 https 的重定向入口点配置并开放443端口;ACME resolver(leresolver)的caserver、email、storage=/data/acme.json也会被一并渲染(compose.yml.sigil)。
自定义 letsencrypt 服务器
默认使用 letsencrypt 生产服务器,可通过letsencrypt-server属性切换到其他 ACME 目录(例如预发布环境):
dokku traefik:set --global letsencrypt-server https://acme-staging-v02.api.letsencrypt.org/directory修改后需重启 Traefik 容器并重建应用以从新服务器获取证书。
切换到 DNS-01 挑战模式
默认 Traefik 使用 TLS-ALPN-01 挑战(challenge-mode默认tls)。当需要通配符证书、或 443 端口不可达时,可切换到 DNS-01 挑战:
- 设置挑战模式为
dns:
dokku traefik:set --global challenge-mode dns- 设置 DNS 提供商:
dokku traefik:set --global dns-provider cloudflare- 配置 DNS 提供商所需的环境变量,变量名前缀为
dns-provider-:
dokku traefik:set --global dns-provider-cf_api_email user@example.com dokku traefik:set --global dns-provider-cf_api_key your-api-keydns-provider-前缀会被剥离、变量名大写后传入 Traefik 容器。例如dns-provider-cf_api_email变为CF_API_EMAIL。这一转换在 compose.yml.sigil 中通过replace "dns-provider-" "" | upper完成,所有dns-provider-*属性由fn-traefik-dns-provider-env-vars收集(internal-functions)。
每个配置的变量会以--traefik-global-dns-provider-<env_var>形式出现在traefik:report中。由于这些值是提供商凭据,默认报告输出(包括聚合的dokku report)会将其掩码为*******;显式请求该 flag 或使用 json 格式渲染时返回原始值:
dokku traefik:report --global --traefik-global-dns-provider-cf_api_emaildokku traefik:report --global --format json | jq -r '."global-dns-provider-cf_api_email"'配置完成后需重启 Traefik 容器并重建应用。支持哪些 DNS 提供商及其必需环境变量,以 Traefik 官方 DNS Challenge 文档为准。切换回 TLS 挑战模式:
dokku traefik:set --global challenge-mode tls掩码逻辑在 report.go 的maskedReportFunc中实现:除--format json或显式指定对应 flag 外,非空值一律替换为*******;dns-provider-*动态属性同样经过该函数处理(report.go)。
暴露 Traefik API 与 Dashboard
Traefik 暴露了 API 和 Dashboard,Dokku 出于安全原因默认全部关闭,可按需开启并定制。
[!WARNING] 启用 Dashboard 的用户应同时启用 API 基础认证。
启用 API
dokku traefik:set --global api-enabled true启用后需重启 Traefik 容器。
启用 Dashboard
dokku traefik:set --global dashboard-enabled true启用后需重启 Traefik 容器。从 compose.yml.sigil 可看到,API 开启后会为 Traefik 容器自身注入traefik.enable=true、API 路由(Host(\traefik.dokku.me`)→api@internal`)等标签。
启用 API 基础认证
建议开启 API 或 Dashboard 时同时启用基础认证。认证只作用于 API/Dashboard,不影响应用。basic-auth-username与basic-auth-password两个属性必须同时设置,否则不生效:
dokku traefik:set --global basic-auth-username username dokku traefik:set --global basic-auth-password password启用后需重启 Traefik 容器。实现上,用户名与密码通过htpasswd -nb生成 htpasswd 格式的认证串并写入traefik.http.middlewares.auth.basicauth.users标签(internal-functions、compose.yml.sigil)。
自定义 API 主机名
API 与 Dashboard 的默认主机名为traefik.dokku.me,可通过api-vhost属性修改:
dokku traefik:set --global api-vhost lb.dokku.me启用后需重启 Traefik 容器。另外两个相关属性api-entry-point与api-entry-point-address可进一步定制 API 的入口点名称与监听地址(host:port格式),设置后会在 compose 中新增独立入口点并映射端口(compose.yml.sigil)。
查看 Traefik 报告
使用traefik:report查看应用的 Traefik 配置:
dokku traefik:report=====> node-js-app traefik information Traefik computed api enabled: false Traefik computed api vhost: traefik.dokku.me Traefik computed challenge mode: tls Traefik computed dashboard enabled: false Traefik computed http entry point: http Traefik computed https entry point: https Traefik computed image: traefik:v3.7.12 Traefik computed letsencrypt email: Traefik computed letsencrypt server: https://acme-v02.api.letsencrypt.org/directory Traefik computed log level: ERROR Traefik global api enabled: Traefik global api vhost: Traefik global challenge mode: Traefik global dashboard enabled: Traefik global http entry point: Traefik global https entry point: Traefik global image: Traefik global letsencrypt email: Traefik global letsencrypt server: Traefik global log level:[!NOTE] 上表中的
Traefik computed image已按当前仓库实际默认值标注为traefik:v3.7.12(解析自 Dockerfile),文档原示例中的traefik:v2.8为旧版本输出。
global-<prop>键保存原始全局值,未设置时为空;computed-<prop>键保存部署时实际生效的值,全局值为空时回退到内置默认值。这一“global 为空则取默认值”的逻辑在 internal-functions 的每个fn-traefik-computed-*函数中实现,与 report.go 的 computed 报告函数一一对应。
也可以针对特定应用查看:
dokku traefik:report node-js-app还可以传入 flag 只输出某一项的值:
dokku traefik:report node-js-app --traefik-computed-api-enabled属性速查表
可设置属性
所有 Traefik 属性均为 global-only,使用traefik:set --global <property> <value>设置。
| Property | Scope | Default | Report flags | Description |
|---|---|---|---|---|
api-enabled | global only | false | --traefik-global-api-enabled,--traefik-computed-api-enabled | Whentrue, enables the Traefik HTTP API |
api-entry-point | global only | none | --traefik-global-api-entry-point,--traefik-computed-api-entry-point | Name of the entry point used by the Traefik API |
api-entry-point-address | global only | none | --traefik-global-api-entry-point-address,--traefik-computed-api-entry-point-address | Address (host:port) the Traefik API listens on |
api-vhost | global only | traefik.dokku.me | --traefik-global-api-vhost,--traefik-computed-api-vhost | Virtual host that routes to the Traefik API |
basic-auth-password | global only | none | --traefik-global-basic-auth-password,--traefik-computed-basic-auth-password(masked as*******in the default stdout report; the raw value is returned when queried via--format jsonor when one of these flags is requested explicitly) | Password for basic auth in front of the API/dashboard |
basic-auth-username | global only | none | --traefik-global-basic-auth-username,--traefik-computed-basic-auth-username | Username for basic auth in front of the API/dashboard |
challenge-mode | global only | tls | --traefik-global-challenge-mode,--traefik-computed-challenge-mode | ACME challenge method used by Traefik (tls,http, ordns) |
dashboard-enabled | global only | false | --traefik-global-dashboard-enabled,--traefik-computed-dashboard-enabled | Whentrue, enables the Traefik dashboard |
dns-provider | global only | none | --traefik-global-dns-provider,--traefik-computed-dns-provider | Lego DNS provider name used whenchallenge-modeisdns |
dns-provider-<ENV_VAR> | global only | none | --traefik-global-dns-provider-<env_var>(masked as*******in the default stdout report; the raw value is returned when queried via--format jsonor when this flag is requested explicitly) | Per-provider environment variables passed to the Traefik container;<ENV_VAR>is the upstream variable name (e.g.dns-provider-cloudflare-api-token) |
http-entry-point | global only | http | --traefik-global-http-entry-point,--traefik-computed-http-entry-point | Entry point name handling plaintext HTTP traffic |
https-entry-point | global only | https | --traefik-global-https-entry-point,--traefik-computed-https-entry-point | Entry point name handling TLS-terminated HTTPS traffic |
image | global only | parsed from plugins/traefik-vhosts/Dockerfile(当前为traefik:v3.7.12) | --traefik-global-image,--traefik-computed-image | Docker image used to run the Traefik container |
letsencrypt-email | global only | none | --traefik-global-letsencrypt-email,--traefik-computed-letsencrypt-email | Contact email enabling letsencrypt; empty disables https issuance |
letsencrypt-server | global only | https://acme-v02.api.letsencrypt.org/directory | --traefik-global-letsencrypt-server,--traefik-computed-letsencrypt-server | ACME directory used when requesting certificates |
log-level | global only | ERROR | --traefik-global-log-level,--traefik-computed-log-level | Traefik log level |
[!NOTE]
traefik:set仅接受上表中的 key 以及dns-provider-<ENV_VAR>形式的动态 key,其他 key 会被拒绝(校验逻辑见 subcommands/set)。
内部属性
以下属性不由traefik:set管理,而是由插件内部记录:
| Property | Description | Source |
|---|---|---|
proxy-status | started/stoppedstate of the traefik compose project | cmd-traefik-start/cmd-traefik-stopin plugins/traefik-vhosts/command-functions |
进一步探索
- docs/networking/proxies/traefik.md:官方 Traefik 集成文档(本文的原始依据);
- plugins/traefik-vhosts/templates/compose.yml.sigil:Traefik 容器的 compose 渲染模板,入口点、ACME、API 标签、端口与卷均在此定义;
- plugins/traefik-vhosts/docker-args-process-deploy:部署时标签注入与健康检查转换的核心实现;
- plugins/traefik-vhosts/internal-functions:全部 global/computed 属性的取值与默认值回退逻辑;
- plugins/traefik-vhosts/report.go 与 plugins/traefik-vhosts/report_test.go:报告输出与凭据掩码实现及测试;
- plugins/traefik-vhosts/Dockerfile:当前默认 Traefik 镜像版本(
traefik:v3.7.12); - 代理相关的通用概念可参考 docs/networking/proxies 目录下的其他代理文档(nginx、Caddy、HAProxy、OpenResty、Traefik)。
【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考