Dokku Haproxy 代理插件实战指南:基于 EasyHaproxy 的 Docker 标签路由、SSL 与运维全解析
【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku
Haproxy 代理插件是 Dokku(自 0.28.0 起提供)中基于 Docker 标签(label)机制的轻量级反向代理方案,通过 EasyHaproxy 实现对容器流量的自动发现与路由。本文围绕 docs/networking/proxies/haproxy.md 展开,完整讲解插件命令、切换流程、属性配置、标签管理、SSL/Let's Encrypt 集成及报告查看,并深入
plugins/haproxy-vhosts的源码实现与测试用例,帮助你从安装、启停到排障全链路掌握该代理方案。
插件概述与适用场景
Dokku 官方默认代理实现是 nginx(nginx-vhosts),而 Haproxy 插件则提供了另一条路由路径:它不直接维护自己的配置文件,而是利用 EasyHaproxy 实现的 Docker 标签集成——部署时由插件把路由规则以容器标签的形式注入容器,EasyHaproxy 作为独立容器监听 Docker API,动态生成 Haproxy 配置并完成请求转发。
该集成在 Dokku 0.28.0 中引入,其核心优势在于:路由规则完全由容器标签驱动,标签变化(域、端口映射)由 EasyHaproxy 自动感知,无需像 nginx 方案那样在宿主机上管理虚拟主机配置。需要说明的是,本仓库 plugins/haproxy-vhosts/Dockerfile 当前指定的镜像为byjg/easy-haproxy:6.1.1(该文件通过FROM行定义了默认镜像,报告输出中的 computed image 即由此解析而来)。
插件命令速查
插件对外暴露以下命令(也可通过dokku help查看完整说明):
haproxy:report [<app>] [<flag>] # Displays a haproxy report for one or more apps haproxy:logs [--num num] [--tail] # Display haproxy log output haproxy:set <app> <property> (<value>) # Set or clear an haproxy property for an app haproxy:show-config <app> # Display haproxy compose config haproxy:start # Starts the haproxy server haproxy:stop # Stops the haproxy server此外还有标签管理相关的子命令haproxy:labels:add、haproxy:labels:remove、haproxy:labels:show,其实现位于 plugins/haproxy-vhosts/command-functions,内部委托给 proxy 插件的通用标签函数(cmd-proxy-labels-*),只是代理类型固定为haproxy。
环境要求
使用 Haproxy 插件集成要求宿主机安装 Docker 的docker-compose-plugin。从源码实现看,haproxy:start、haproxy:stop、haproxy:show-config三个命令在 plugins/haproxy-vhosts/command-functions 中都会先调用fn-is-compose-installed做前置校验,未安装时直接报错 "Required docker compose plugin is not installed"。Docker 官方文档提供了该插件的安装说明,请根据你的 Docker 安装方式(发行版包管理器或 Docker 官方脚本)选择对应的安装途径。
路由规则与工作机制
Haproxy 插件在路由上有几条明确的规则,理解它们有助于避免部署后的“意外”:
- 标签驱动:Haproxy 集成通过附加到容器上的 Docker 标签暴露。标签的变化需要应用重新部署(deploy)或重建(rebuild)才会生效。
- 仅 web 进程注入标签:Haproxy 会尊重其他容器上已存在的标签,但插件只会为
web类型的进程注入 Haproxy 标签。对应源码见 plugins/haproxy-vhosts/docker-args-process-deploy 开头的判断:PROC_TYPE != "web"时直接返回。 - 端口映射限制:目前仅支持
http:80和https:443两种端口映射。源码中会遍历ports-get得到的端口映射,挑选主机端口为 80/443 的映射作为候选;若未找到 80 端口映射,会输出 "Warning: http:80 port mapping not found" 并退而使用第一个 http 映射。 - 健康检查通过即路由:只要容器处于运行状态且通过健康检查,请求就会被路由到该容器,不存在 nginx 方案中的“维护中页面”等中间状态。
标签注入的源码细节
在 plugins/haproxy-vhosts/docker-args-process-deploy 中可以看到完整的标签生成逻辑:插件会依次检查proxy-type是否为haproxy、proxy-is-enabled是否为true、domains-vhost-enabled是否启用,随后将应用的域名列表(来自domains-list,多个域名以逗号拼接)和端口映射组合成一组标签,例如:
--label haproxy.<app>-web.localport=<container_port> --label haproxy.<app>-web.mode=http --label haproxy.<app>-web.port=80 --label haproxy.<app>-web.host=<comma-separated-domains>当存在 https 端口映射或启用了 Let's Encrypt 时,还会注入redirect_ssl=true与一组-https后缀的标签(如haproxy.<app>-web-https.letsencrypt=true、haproxy.<app>-web-https.port=443);否则注入redirect_ssl=false。用户自定义标签则从fn-proxy-get-labels-file-path "haproxy" "$APP"指定的标签文件中读取并追加到--label参数中。
部署完成后,plugins/haproxy-vhosts/core-post-deploy 会输出 "Routing app via haproxy" 的日志信息,提示该应用已由 Haproxy 接管路由。
切换到 Haproxy 代理
[!WARNING] 在同一台 Dokku 主机上同时使用多个代理插件可能导致请求路由冲突,应尽量避免。由于默认代理实现是 nginx,切换到 Haproxy 前建议先停止 nginx 服务。
针对某个应用切换到 Haproxy 使用proxy:set命令:
dokku proxy:set node-js-app type haproxy这会启用基于 Docker 标签的 Haproxy 集成,之后所有部署都会注入 Haproxy 可读取的路由标签。由于标签机制的特性,必须执行一次部署或重建请求才能成功路由:
dokku ps:rebuild node-js-app同样地,修改域名(domains)或端口映射(port mappings)之后,也需要重新部署或重建应用才会生效。
启动与停止 Haproxy 容器
Haproxy 本身运行在一个由 Docker Compose 管理的独立容器中,启动与停止均由插件命令完成。
启动
dokku haproxy:start该命令内部(见 plugins/haproxy-vhosts/command-functions 的cmd-haproxy-start)会先把proxy-status属性写为started,再基于模板生成 compose 文件并执行docker compose up。compose 模板位于 plugins/haproxy-vhosts/templates/compose.yml.sigil,其服务配置要点如下:
- 镜像:使用
HAPROXY_IMAGE(计算值,默认来自插件 Dockerfile 的FROM行); - 环境变量:
EASYHAPROXY_DISCOVER=docker(通过 Docker API 发现容器)、EASYHAPROXY_LABEL_PREFIX=haproxy(只读取haproxy前缀的标签)、EASYHAPROXY_LOG_LEVEL、EASYHAPROXY_REFRESH_CONF、CERTBOT_LOG_LEVEL与HAPROXY_LOG_LEVEL,并关闭统计端口(HAPROXY_STATS_PORT=false); - 网络与端口:
network_mode: bridge,映射80:80;仅当设置了HAPROXY_LETSENCRYPT_EMAIL时才映射443:443并注入EASYHAPROXY_LETSENCRYPT_EMAIL与EASYHAPROXY_LETSENCRYPT_SERVER; - 持久化与自愈:
restart: unless-stopped,并将宿主机的/var/run/docker.sock以只读方式挂载进容器——这是 EasyHaproxy 监听 Docker 事件/轮询标签变化的数据来源。
停止
dokku haproxy:stop对应cmd-haproxy-stop:先把proxy-status写为stopped,再执行docker compose down。容器会被停止并从系统中移除;如果容器本来就没有运行,该命令不会产生任何效果。
查看 compose 配置
调试时可用以下命令查看实际生成的 compose 配置(即渲染后的模板内容):
dokku haproxy:show-config它同样要求 docker compose 插件已安装,输出由模板渲染出的完整 compose 文件。
属性(Property)配置
插件所有属性均为全局属性(global only),只能通过dokku haproxy:set --global <property> <value>设置。校验逻辑见 plugins/haproxy-vhosts/subcommands/set:GLOBAL_ONLY_KEYS包含全部五个属性,对非--global作用域设置会直接报错 "The key ... can only be set globally";合法的属性键为image、log-level、letsencrypt-email、letsencrypt-server、refresh-conf。
可设置属性的完整说明如下:
| Property | Scope | Default | Report flags | Description |
|---|---|---|---|---|
image | global only | parsed fromplugins/haproxy-vhosts/Dockerfile | --haproxy-global-image,--haproxy-computed-image | Docker image used to run the Haproxy container |
letsencrypt-email | global only | none | --haproxy-global-letsencrypt-email,--haproxy-computed-letsencrypt-email | Contact email enabling letsencrypt; empty disables https issuance |
letsencrypt-server | global only | https://acme-v02.api.letsencrypt.org/directory | --haproxy-global-letsencrypt-server,--haproxy-computed-letsencrypt-server | ACME directory used when requesting certificates |
log-level | global only | ERROR | --haproxy-global-log-level,--haproxy-computed-log-level | Haproxy log level |
refresh-conf | global only | 10 | --haproxy-global-refresh-conf,--haproxy-computed-refresh-conf | Seconds between Haproxy polls of the Docker API for label changes |
从 plugins/haproxy-vhosts/internal-functions 的fn-haproxy-computed-*系列函数可以看出“global 原始值”与“computed 生效值”的关系:computed 值在 global 值为空时回退到内置默认值(如log-level默认ERROR且会转为大写,refresh-conf默认10,letsencrypt-server默认https://acme-v02.api.letsencrypt.org/directory),image默认值则直接解析 plugins/haproxy-vhosts/Dockerfile 的FROM行。
自定义容器镜像
默认镜像虽然写死在插件中,但可以通过--global标志的image属性覆盖:
dokku haproxy:set --global image byjg/easy-haproxy:4.0.0修改后需重启 Haproxy 容器(dokku haproxy:stop && dokku haproxy:start)使其生效。
查看容器日志
需要确认 Haproxy 是否按预期运行时,可查看其容器日志:
dokku haproxy:logs该命令支持以下修饰参数:
--num NUM # the number of lines to display --tail # continually stream logs组合使用示例:
dokku haproxy:logs --tail --num 10上述命令会持续流式输出日志,并先显示最近 10 行历史。从实现看,fn-haproxy-logs(plugins/haproxy-vhosts/internal-functions)实际执行的是docker logs haproxy-haproxy-1,--tail对应--follow,--num默认值为 100。顺带说明,官方文档中“从 vector 容器输出日志”的描述对应的是其他代理插件(如 Caddy/Traefik)的日志链路,本插件的日志对象是名为haproxy-haproxy-1的 compose 服务容器。
修改日志级别
Haproxy 日志输出默认级别为ERROR,可通过--global标志的log-level属性修改:
dokku haproxy:set --global log-level DEBUG修改后需要重启 Haproxy 容器。
修改刷新间隔
EasyHaproxy 默认每10秒轮询一次 Docker API 以感知标签变化,可通过refresh-conf属性调整(全局唯一,不能按应用设置):
dokku haproxy:set --global refresh-conf 5设置为空值会重置回默认值。修改后同样需要重启 Haproxy 容器。
标签管理(Label Management)
插件允许为应用添加自定义容器标签,这些标签会在部署时注入容器,用于配置插件默认行为之外的 Haproxy 特性。可用的标签集合请参阅上游 EasyHaproxy 文档。
添加标签
dokku haproxy:labels:add node-js-app haproxy.directive value该命令会为应用的容器添加标签haproxy.directive=value。添加后需重建或重新部署应用,标签才会应用到运行中的容器:
dokku ps:rebuild node-js-app移除标签
dokku haproxy:labels:remove node-js-app haproxy.directive移除指定标签后,同样需要重建或重新部署应用:
dokku ps:rebuild node-js-app查看标签
查看应用的全部自定义容器标签:
dokku haproxy:labels:show node-js-app只查看某个标签的具体值,则附加标签名:
dokku haproxy:labels:show node-js-app haproxy.directive从实现上看,这三个命令(plugins/haproxy-vhosts/command-functions)分别映射到cmd-proxy-labels-add/cmd-proxy-labels-remove/cmd-proxy-labels-show,标签持久化在插件各自的标签文件中;docker-args-process-deploy触发器会在部署时逐行读取该文件并追加--label参数,同时注意它会在自定义标签外再包一层单引号,因此自定义标签值中应避免出现需要 shell 转义的字符。
SSL 配置与 Let's Encrypt 集成
Haproxy 插件只支持通过其内置的 Let's Encrypt 集成自动签发证书;由certs插件提供的托管证书(managed certificates)会被忽略。
启用 Let's Encrypt
默认情况下 letsencrypt 处于禁用状态,https 端口映射会被忽略。通过--global标志的letsencrypt-email属性启用:
dokku haproxy:set --global letsencrypt-email automated@dokku.sh启用后需要重启 Haproxy 容器,并重建应用(让新标签生效)。此后所有 http 请求都会被重定向到 https。从 compose 模板(plugins/haproxy-vhosts/templates/compose.yml.sigil)可见:只有设置了EASYHAPROXY_LETSENCRYPT_EMAIL时,容器才会映射443:443端口并注入 letsencrypt 相关环境变量,这也是“未启用时不监听 443”的根本原因。
自定义 Let's Encrypt 服务器
letsencrypt 集成默认使用生产环境服务器。如需切换(例如联调或压测时使用 staging 环境),设置letsencrypt-server属性:
dokku haproxy:set --global letsencrypt-server https://acme-staging-v02.api.letsencrypt.org/directory修改后需要重启 Haproxy 容器并重建应用,证书才会从新服务器签发。
查看应用报告
使用haproxy:report可以查看应用当前的 Haproxy 配置报告:
dokku haproxy:report输出示例:
=====> node-js-app haproxy information Haproxy computed image: byjg/easy-haproxy:4.0.0 Haproxy computed letsencrypt email: Haproxy computed letsencrypt server: https://acme-v02.api.letsencrypt.org/directory Haproxy computed log level: ERROR Haproxy computed refresh conf: 10 Haproxy global image: Haproxy global letsencrypt email: Haproxy global letsencrypt server: Haproxy global log level: Haproxy global refresh conf:其中global-<prop>键保存原始全局值,未设置时为空;computed-<prop>键保存部署时实际生效的值,全局值为空时回退到内置默认值。报告也可以针对单个应用查看:
dokku haproxy:report node-js-app还支持传入 flag 只输出某一项的具体值:
dokku haproxy:report node-js-app --haproxy-computed-image报告功能的 Go 实现位于 plugins/haproxy-vhosts/report.go(ReportSingleApp与各report*函数),入口分发在 plugins/haproxy-vhosts/src/subcommands/subcommands.go,且支持--format stdout|json输出格式;haproxy:report --global还可查看全局级别的报告。
内部属性
以下属性不由haproxy:set管理,而是插件内部记录的状态:
| Property | Description | Source |
|---|---|---|
proxy-status | started/stoppedstate of the haproxy compose project | cmd-haproxy-start/cmd-haproxy-stopinplugins/haproxy-vhosts/command-functions |
该属性的作用体现在 plugins/haproxy-vhosts/pre-restore:当proxy-status为started时,pre-restore触发器会在 Dokku 恢复(如主机重启后的应用恢复流程)时自动调用cmd-haproxy-start重新拉起 Haproxy 容器;若启动失败会输出警告 "Failed to restore haproxy proxy, requests may not route as expected"。
测试与验证
仓库中 tests/unit/haproxy-vhosts.bats 与 tests/unit/haproxy.bats 覆盖了该插件的核心行为(如属性设置、报告输出、compose 模板渲染等),tests/unit/report.bats 则覆盖报告命令的多种 flag 组合。在自行修改或排查插件问题时,可以参考这些 Bats 用例了解预期的命令输出格式与属性回退行为。
常见问题与最佳实践
- 切换后请求 404 / 路由不生效:绝大多数情况是因为切换代理类型、修改域名或端口映射后未执行部署或重建。记得运行
dokku ps:rebuild <app>。 - 443 端口未监听:确认是否已设置
letsencrypt-email——未设置时 compose 模板不会映射 443 端口,https 端口映射会被忽略。 - 修改属性后不生效:
image、log-level、letsencrypt-*、refresh-conf均为全局属性且作用于 Haproxy 容器本身,修改后必须dokku haproxy:stop && dokku haproxy:start(或haproxy:start自动拉起)重启容器。 - 不要混用多个代理插件:同机多代理会导致请求路由冲突,切换前先停掉 nginx 服务。
- 观察 EasyHaproxy 动态行为:若标签变化未按预期生效,用
dokku haproxy:logs --tail观察容器日志,并可通过dokku haproxy:show-config确认生成的 compose 配置是否符合预期。
通过上述命令与配置,你可以在 Dokku 上完整落地一套基于 Haproxy/EasyHaproxy 的容器路由方案,并借助 Docker 标签机制灵活扩展其路由与 TLS 行为。
【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考