简介:这份资源面向需要在 Linux 服务器上快速搭建监控平台的运维人员与开发者,提供两种基于 Docker 部署 Zabbix 的完整代码方案:一种通过 Docker Compose 编排 Zabbix 服务端、MySQL 数据库与前端,另一种借助宝塔面板已有数据库创建 Zabbix 容器,适合不同环境与技能层级的用户按需选用。压缩包共 5 个文件,约 11KB,以 docker-compose.yml 为核心编排文件,辅以 README.md 说明文档、index.html 页面、.gitignore 与 .inscode 配置,结构精简、开箱即用。目前已有 46 人学习下载。读者可从中获得可直接复用的容器编排配置、环境变量与端口映射示例,以及两种部署路径的对照参考,便于快速落地 Zabbix 监控并理解其服务组成与部署差异。
1. Docker 装 Zabbix:为什么我劝你别再裸机编译了
如果你还在用源码编译的方式在物理机上装 Zabbix,大概率经历过这样的场景:依赖包版本冲突、PHP 模块缺失、数据库字符集报错,折腾一整天监控页面还是 502。Docker 安装 Zabbix 这件事,本质上解决的不是“能不能装”,而是“能不能在半小时内装完、并且下次换台机器还能一模一样地复现”。它适合两类人:一是手里只有一两台测试机、想快速搭一套监控验证告警链路的运维;二是需要在多个环境里反复部署同一套监控栈、不想每次都被系统差异坑一遍的工程师。这套方案的核心思路是把 Zabbix Server、Web 前端、数据库拆成独立容器,用 Docker Compose 编排,网络和数据卷自己控制。下面我按实际部署顺序,把镜像选型、compose 配置、初始化参数和排错点全部拆开讲。
2. 镜像与组件选型:Zabbix 6.x 的容器拆分逻辑
2.1 为什么官方推荐拆成四个容器
Zabbix 从 5.0 开始官方就提供了多种容器镜像,到了 6.x 这套拆分已经比较成熟。核心组件有四个:zabbix-server(负责采集和告警逻辑)、zabbix-web-nginx-mysql(前端页面)、mysql(数据存储)、zabbix-agent(被监控端采集)。拆开的好处是每个容器可以独立升级、独立调资源,数据库挂了不影响前端静态资源,前端重启也不会中断采集。坏处是网络和环境变量配置变多,第一次搭容易在“容器之间怎么互相找到”这一步卡住。
我一般会先确认三件事:宿主机 Docker 版本不低于 20.10,docker compose 插件可用(docker compose version能输出即可),以及宿主机至少留 2GB 内存给数据库容器。Zabbix Server 本身不重,但 MySQL 在数据量上来后内存占用会明显增长,测试环境 1GB 也能跑,生产环境建议单独规划。
2.2 镜像标签怎么选才不踩版本坑
官方镜像在 Docker Hub 上的标签规则是<版本>-<系统>-<组件>,比如zabbix/zabbix-server-mysql:alpine-6.4-latest。这里有两个容易翻车的点:第一,latest标签会随版本更新漂移,今天拉的和下周拉的可能不是同一个大版本,生产环境一定要锁具体版本号,比如alpine-6.4.10。第二,server 和 web 的版本必须一致,混用 6.0 的 server 和 6.4 的 web 会出现数据库 schema 不匹配,前端直接报“数据库版本不支持”。
选型上我倾向于 alpine 版本,体积小、启动快,缺点是排错时容器内没有 bash,只有 sh,习惯用 bash 调试的人会别扭。如果团队里有人不熟悉 alpine,用ubuntu-6.4-latest也行,代价是镜像大几百 MB。数据库镜像建议用mysql:8.0,不要用latest,MySQL 8.0 和 8.4 在认证插件上有差异,Zabbix 6.4 对 8.4 的支持需要额外配置,新手直接锁 8.0 最省事。
2.3 网络模式:bridge 还是 host
常见做法是用自定义 bridge 网络,让容器通过服务名互相访问。比如 compose 里定义networks: zbx-net,server 连数据库时 host 写mysql而不是 IP。这样容器重建后 IP 变了也不影响配置。host 模式虽然省去端口映射,但容器之间会争抢端口,而且 Zabbix 默认端口 10051 和 80 很容易和宿主机已有服务冲突,不推荐。
端口映射上,Web 前端对外暴露 8080(映射容器 8080),Server 的 10051 端口如果只给内网 agent 用,可以不映射到宿主机,减少暴露面。Agent 容器如果用主动模式,需要能访问 Server 的 10051,走内部网络即可。
3. 用 Docker Compose 跑通最小可用栈
3.1 目录结构和环境变量文件
先在宿主机建一个工作目录,比如/opt/zbx-docker,里面放docker-compose.yml和.env。把密码、时区这些易变项抽到.env里,compose 文件里用${变量名}引用,这样换环境只改一个文件。
# .env 文件内容 MYSQL_ROOT_PASSWORD=ZbxRoot_2024 MYSQL_DATABASE=zabbix MYSQL_USER=zabbix MYSQL_PASSWORD=ZbxUser_2024 TZ=Asia/Shanghai ZBX_SERVER_NAME=zbx-lab这里TZ很关键,Zabbix 前端和数据库时区不一致会导致图表时间偏移 8 小时,很多人以为是采集问题,其实是容器默认 UTC。ZBX_SERVER_NAME是前端登录页显示的名称,不设也能跑,但设了以后多套环境好区分。
3.2 compose 文件逐段拆解
下面这份 compose 是我在测试环境反复用过的版本,四个服务加一个网络一个数据卷。注意 MySQL 的健康检查,没有它 server 会在数据库还没初始化完就启动,然后反复重连失败。
version: '3.8' services: mysql: image: mysql:8.0 container_name: zbx-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} TZ: ${TZ} volumes: - zbx-mysql-data:/var/lib/mysql networks: - zbx-net healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD}"] interval: 10s timeout: 5s retries: 10 zabbix-server: image: zabbix/zabbix-server-mysql:alpine-6.4.10 container_name: zbx-server restart: unless-stopped environment: DB_SERVER_HOST: mysql MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} TZ: ${TZ} ports: - "10051:10051" depends_on: mysql: condition: service_healthy networks: - zbx-net zabbix-web: image: zabbix/zabbix-web-nginx-mysql:alpine-6.4.10 container_name: zbx-web restart: unless-stopped environment: ZBX_SERVER_HOST: zabbix-server DB_SERVER_HOST: mysql MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} PHP_TZ: ${TZ} ZBX_SERVER_NAME: ${ZBX_SERVER_NAME} ports: - "8080:8080" depends_on: - zabbix-server networks: - zbx-net zabbix-agent: image: zabbix/zabbix-agent:alpine-6.4.10 container_name: zbx-agent restart: unless-stopped environment: ZBX_HOSTNAME: zbx-agent-local ZBX_SERVER_HOST: zabbix-server ZBX_SERVER_PORT: 10051 TZ: ${TZ} networks: - zbx-net volumes: zbx-mysql-data: networks: zbx-net: driver: bridge逻辑说明:MySQL 的 healthcheck 用mysqladmin ping确认数据库真正可连接,depends_on配合condition: service_healthy让 server 等数据库就绪后再启动。server 的DB_SERVER_HOST写mysql,这是 compose 服务名,Docker 内置 DNS 会解析到对应容器 IP。web 的ZBX_SERVER_HOST写zabbix-server,同理。agent 的ZBX_SERVER_HOST也指向服务名,主动模式下 agent 会去连 server 的 10051。
参数说明:restart: unless-stopped保证宿主机重启后容器自动拉起,除非手动停过。PHP_TZ是前端容器的时区变量,和TZ分开设是因为镜像里 PHP 读的是PHP_TZ,只设TZ前端时间还是不对。端口映射8080:8080是因为 alpine 版 web 容器内 nginx 监听 8080,不是 80,映射错了会连不上。
3.3 启动顺序和首次验证
在 compose 文件所在目录执行:
docker compose up -d docker compose psps输出里四个容器状态都应该是Up,mysql 显示Up (healthy)。如果 server 反复重启,先看日志:
docker compose logs zabbix-server --tail 50常见的是Access denied for user 'zabbix',说明.env里的密码和 MySQL 初始化时不一致。MySQL 只在数据卷为空时执行初始化,如果之前用旧密码起过,改.env后需要删掉数据卷重建:docker compose down -v再up -d。这一步会清空数据,测试环境无所谓,生产环境要谨慎。
前端验证:浏览器打开http://宿主机IP:8080,默认账号Admin,密码zabbix。登录后进Reports -> System information,看 Server 是否 running,数据库连接是否正常。如果页面能开但 Server 显示 not running,多半是 server 容器没起来或数据库连不上,回到日志排查。
4. 避坑与排查:五个真实翻车记录
4.1 前端 502 但容器都是 Up
现象:docker compose ps四个容器都正常,浏览器访问 8080 返回 502 Bad Gateway。原因通常是 web 容器内 nginx 起来了,但 PHP-FPM 没起来,或者 web 容器连不上 server。先看 web 日志docker compose logs zabbix-web,如果报connection refused到zabbix-server:10051,说明 server 容器虽然 Up 但内部服务没监听。进 server 容器docker exec -it zbx-server sh,执行netstat -tlnp | grep 10051确认端口。如果没监听,多半是数据库连接失败导致 server 启动中断,回到 server 日志看数据库报错。
4.2 图表时间差 8 小时
现象:监控项有数据,但图表横轴时间比实际早 8 小时。原因是容器默认 UTC,数据库存的是 UTC 时间,前端按 UTC 渲染。解决是把.env里TZ和 compose 里PHP_TZ都设成Asia/Shanghai,然后docker compose down && docker compose up -d重建容器。注意数据库里已存的历史数据不会自动转换,新数据才会按新时区写入。如果历史数据也要修正,需要改 MySQL 的global time_zone参数,这个操作有风险,测试环境建议直接清库重来。
4.3 Agent 主动模式连不上 Server
现象:agent 容器日志报no active checks on server,或者 server 前端里 agent 一直显示灰色。原因是 agent 主动模式下需要能解析并连接到ZBX_SERVER_HOST,如果 compose 网络没建好,或者 server 的 10051 没监听,agent 就连不上。先在 agent 容器里ping zabbix-server确认 DNS 解析,再nc -zv zabbix-server 10051确认端口通。如果 DNS 不通,检查两个容器是否在同一个zbx-net网络里。另外 agent 的ZBX_HOSTNAME要和前端里配置的主机名完全一致,大小写敏感,不一致会显示成两台主机。
4.4 MySQL 数据卷权限导致启动失败
现象:mysql 容器启动后立刻退出,日志报Permission denied或Can't create/write to file。原因是宿主机上数据卷目录的属主不是容器内 mysql 用户(uid 999)。如果用命名卷一般不会有这个问题,但如果改成 bind mount 到宿主机目录,就需要chown -R 999:999 /宿主机目录。我一般直接用命名卷,省去权限折腾。如果必须用 bind mount,记得先改属主再启动。
4.5 升级镜像后数据库 schema 不匹配
现象:把镜像标签从 6.4.10 改成 6.4.15 后重启,前端报Database error: version mismatch。原因是 Zabbix 小版本升级有时会带 schema 变更,server 启动时会自动执行数据库升级脚本,但如果 web 容器先起来、server 还没跑完升级,前端就会读到旧 schema。解决顺序是:先docker compose stop zabbix-web,再docker compose up -d zabbix-server,等 server 日志出现database upgrade completed后再docker compose up -d zabbix-web。升级前务必备份数据库卷,docker run --rm -v zbx-mysql-data:/data -v $(pwd):/backup alpine tar czf /backup/zbx-db-backup.tar.gz -C /data .这条命令能把数据卷打包到当前目录。
5. 进阶:把监控栈做成可迁移的模板
5.1 用 override 文件区分环境和生产
基础 compose 文件里不要写死端口和资源限制,用docker-compose.override.yml覆盖。比如测试环境 web 映射 8080,生产环境映射 80,就在 override 里改ports。资源限制也放 override,测试环境给 mysql 限 512M,生产环境给 2G。这样基础文件保持通用,换环境只带一个 override 文件。
# docker-compose.override.yml 示例 services: mysql: deploy: resources: limits: memory: 2G zabbix-web: ports: - "80:8080"逻辑说明:deploy.resources.limits在 compose 非 swarm 模式下也生效,但需要 Docker 20.10 以上。端口覆盖时注意格式是列表,直接写新映射会替换基础文件里的映射,不是追加。
5.2 数据备份与迁移的固定动作
我习惯在每次改配置或升级前执行一次数据库导出,命令固定成脚本放 crontab 里每天跑。导出用mysqldump走容器:
docker exec zbx-mysql mysqldump -u zabbix -p${MYSQL_PASSWORD} zabbix > /opt/zbx-backup/zbx-$(date +%F).sql参数说明:-u zabbix用业务用户而不是 root,权限够导出即可。导出文件按日期命名,保留最近 7 天,用find /opt/zbx-backup -name "*.sql" -mtime +7 -delete清理。迁移到新机器时,先起一套空栈,把 sql 导入新 mysql 容器,再启动 server 和 web,数据就带过去了。注意导入前新库要是空的,否则主键冲突。
5.3 一个验证监控链路是否通的小技巧
装完之后别只看前端页面,用zabbix_get从 server 容器主动拉一次 agent 数据,能直接验证采集链路:
docker exec -it zbx-server zabbix_get -s zbx-agent -k system.cpu.load[all,avg1]如果返回一个浮点数,说明 server 到 agent 的被动采集通了。如果报ZBX_NOTSUPPORTED,说明 agent 没配好对应 key 或者 hostname 不匹配。这个命令比等前端图表出数据快得多,适合部署完立刻验证。我一般会在部署脚本最后加这一句,返回非空就算通过。
这套 Docker 装 Zabbix 的方案我用了快两年,从测试机到小规模生产都跑过,最大的体会是:容器化省掉的是安装时间,但省不掉对组件关系的理解。数据库健康检查、时区、版本一致性这三件事,每次部署都要确认一遍,少一个就等着半夜被报警叫起来。希望帮到你。
本文还有配套的精品资源,点击获取