简介:面向系统管理员与运维工程师的Docker安装Zabbix实践资源,提供了两种部署路径:一是通过Docker Compose定义Zabbix服务器、MySQL数据库及前端服务,二是利用宝塔面板数据库创建容器,并配有环境变量与端口映射示例。Zabbix无需在目标设备安装代理即可监控网络服务、服务器和网络设备,支持CPU、内存、磁盘空间、网络流量等实时指标与报警,也可扩展自定义监控脚本和模板,因此该资源适合希望快速搭建集中监控平台的中初级运维人员。整个压缩包仅11KB,包含5个文件,主要有docker-compose.yml、README.md、index.html、.inscode等,其中compose文件可直接用于多容器编排,说明文档帮助理解配置步骤,HTML文件则对应Zabbix前端访问页面,整体结构精简,便于直接复用。目前已有45人学习下载,对于正在选型或初次接触容器化监控部署的读者,可作为清晰、可操作的部署模板,省去从零摸索的时间。
1. Docker安装Zabbix:一架由四个容器托起来的监控系统
生产环境的告警往往不是监控软件不够强,而是安装过程先把你劝退的。Docker安装Zabbix这条路径,把Zabbix拆成 MySQL、Server、Web前端、Agent 四个容器,用一条docker compose up -d全部拉起,省掉编译源码、手动配置 LAMP 那一长串痛苦步骤。我拿一台 CentOS 7.9 机器从零跑到图表出数、告警推送,全程没碰过编译器和 php.ini 的手工调参。这篇记录按我实际操作的顺序来,先解决 Docker 环境选型和虚拟化报错,再拆 compose 里每个环境变量的含义,然后是 Web 初始化、添加第一台主机、用net.tcp.port做简单检查,最后一章用 Zabbix API 验证监控链路是否真的通了。适合被安装过程折磨过、或者想搞清楚 Zabbix 容器间如何协作的运维和开发。
2. 先把 Docker 环境立住:Desktop 与 CE 的选型、虚拟化报错和权限修复
2.1 选型:Docker Desktop 还是纯 CE,取决于宿主系统而不是个人习惯
Windows 和 macOS 上最省事的当然是 Docker Desktop,图形界面管理资源、右键就能设置内存上限;但到了 Linux 服务器上,尤其 CentOS 7.9 这种还在服役的老系统,没人会装 Desktop,装的是docker-ce加docker-compose-plugin,原因很简单:Desktop 依赖桌面环境,服务器上没这个必要,而且多一层图形管控反而是负担。
这里有一个很多人没想透的点:Zabbix 在 Docker 里不是"一个镜像搞定一切",它天生就是多容器协作。Server 要连 MySQL,Web 要连 Server,Agent 要上报给 Server,四个容器之间靠 Docker 网络里的服务名互相解析。这套协作方式和部署微服务项目是同一套逻辑——理解了 Zabbix 的 compose,以后看任何微服务编排文件都不会发怵。所以选型时不要把注意力全放在"哪个镜像最方便",先看宿主能不能跑稳 Docker。
| 对比项 | Docker Desktop | Docker CE + compose 插件 |
|---|---|---|
| 适用宿主 | Windows 10/11、macOS | Linux 服务器、云主机 |
| 安装方式 | 安装包 + GUI | 包管理器装 daemon + CLI |
| 虚拟化依赖 | WSL2 / Hyper-V | 无(原生容器) |
| compose 支持 | 自带 | 安装docker-compose-plugin |
| 典型报错 | virtualisation support wasn't detected | permission denied 访问 daemon socket |
2.2 Windows 11 最常见的一道坎:虚拟化报错与 WSL2
Windows 上装 Docker Desktop 最容易翻车的就是启动瞬间弹一句Docker Desktop failed to start because virtualisation support wasn't detected。这句报错我看到过无数次,九成情况不是 Docker 本身的问题,而是宿主虚拟化层没准备好。
按顺序排查:先在任务管理器 → 性能 → CPU 里确认"虚拟化"那一栏是"已启用"。如果显示未启用,就得进 BIOS 打开 Intel VT-x 或 AMD-V,笔记本尤其容易默认关着。确认虚拟化已开启后,再以管理员身份打开 PowerShell,执行下面两条命令开启必需功能:
bcdedit /set hypervisorlaunchtype auto wsl --updatehypervisorlaunchtype auto是让 Windows 的 Hypervisor 随系统启动,Docker Desktop 依赖它做隔离。wsl --update保证 WSL2 内核是最新的,否则 Desktop 也会启动失败。然后到"启用或关闭 Windows 功能"里勾上三样:Hyper-V、适用于 Linux 的 Windows 子系统、虚拟机平台。改完重启一次,再打开 Docker Desktop 基本就能过。
我在一台 Windows 11 机器上遇到过更隐蔽的情况:设备管理器里能看到"虚拟机平台",但 Docker Desktop 照样报虚拟化检测不到。查下来是之前装过 VM 类虚拟机软件,Hyper-V 和它冲突导致 hypervisor 没起来。卸载第三方虚拟化组件、再次执行bcdedit /set hypervisorlaunchtype auto后问题消失。遇到玄学报错时,先别急着找 Docker 的毛病,回到 Windows 自身的虚拟化状态排查。
2.3 Linux 主机安装:CentOS 7.9 的 docker-ce 安装与现实问题
Linux 服务器上安装 Docker 反而是最不折腾的。CentOS 7.9 还是常见的生产系统,用 yum 官方源安装即可。注意 Docker 官方仓库地址不要写错,装完以后要顺手装docker-compose-plugin,新版docker compose命令就是它提供的,老教程让你pip install docker-compose的那一套在新环境里不用再碰。
yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable --now docker docker versionyum-config-manager --add-repo会把 Docker 官方仓库写进yum.repos.d,后续安装的包都从那里来。docker-compose-plugin是独立的一个包,它装好了以后docker compose version才有输出,注意是带空格的docker compose,老命令docker-compose(带横杠)是另一个独立二进制,两者别混。最后systemctl enable --now docker同时完成开机自启和立即启动,省一步是一次。
装完立刻会遇到一个几乎人人都有的权限问题:普通用户执行 docker 命令报Got permission denied while trying to connect to the Docker daemon socket。原因很直接——/var/run/docker.sock的属主组是 root,当前用户不在 docker 组里。把用户加进去再刷新组权限:
sudo usermod -aG docker ${USER} newgrp dockernewgrp docker是让当前会话立即生效,不用重新登录。如果是远程 SSH 环境,这条命令后新开的会话也会带着 docker 组权限。另外国内网络环境下,docker pull经常卡在拉镜像阶段,我的习惯是一开始就在/etc/docker/daemon.json里配好镜像加速地址:
{ "registry-mirrors": ["https://你的加速地址"] }改完systemctl daemon-reload && systemctl restart docker再拉镜像。这一步做在前面,后面拉 Zabbix 全家桶会顺畅很多,别等卡住了才想起来配。
3. Zabbix 容器化方案:四个镜像的选型与 compose 参数逐项拆解
3.1 镜像选型:Zabbix 在 Docker 里从来不是单容器
Zabbix 官方在 Docker Hub 上维护的镜像里,和"全家桶"相关的有两个方向:一个是zabbix/zabbix-appliance,一个是我们下面要用的拆分版镜像。appliance把所有组件塞进一个容器,启动确实快,但坏处很明显——MySQL、Server、Web 全挤在一起,日志混成一锅粥,以后想单独升级其中一个组件或者换版本,牵一发动全身,所以我从来不用它做正经环境。
真正值得写进 compose 的是这四个镜像:
| 镜像 | 容器内端口 | 在 compose 里的作用 |
|---|---|---|
mysql:8.0 | 3306 | 保存监控配置、历史数据、事件记录 |
zabbix/zabbix-server-mysql | 10051 | 接收 Agent 上报、执行主动探测、计算触发器 |
zabbix/zabbix-web-nginx-mysql | 8080 | Nginx + PHP 前端,人机交互入口 |
zabbix/zabbix-agent2 | 10050 | 被监控主机上的采集端,支持主动和被动两种模式 |
四个镜像的 tag 必须对齐。我用alpine-7.0-latest作为版本后缀,Server 和 Web 的镜像 tag 要一致,否则前端的 API 版本和后端不匹配,登录页面能打开但拿不到主机列表,表现非常诡异。MySQL 倒是独立版本,8.0 就够用,Zabbix 7.0 官方支持 MySQL 8.0。
3.2 写 docker-compose.yml:四个服务的环境变量逐个说明
下面这份 compose 是我在 CentOS 7.9 上实际跑过的版本,把关键参数都加了注释。服务名mysql、zabbix-server同时承担了 Docker 网络内的主机名解析,所以 Web 容器里DB_SERVER_HOST填mysql而不是 127.0.0.1——在容器世界里,localhost 指向的是容器自己,不是别人。
services: mysql: image: mysql:8.0 container_name: zabbix-mysql environment: MYSQL_ROOT_PASSWORD: root-pass-123 MYSQL_DATABASE: zabbix MYSQL_USER: zabbix MYSQL_PASSWORD: zabbix-pass-123 TZ: Asia/Shanghai volumes: - zabbix-db:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-proot-pass-123"] interval: 10s timeout: 5s retries: 5 zabbix-server: image: zabbix/zabbix-server-mysql:alpine-7.0-latest container_name: zabbix-server environment: DB_SERVER_HOST: mysql MYSQL_DATABASE: zabbix MYSQL_USER: zabbix MYSQL_PASSWORD: zabbix-pass-123 TZ: Asia/Shanghai ports: - "10051:10051" depends_on: mysql: condition: service_healthy zabbix-web: image: zabbix/zabbix-web-nginx-mysql:alpine-7.0-latest container_name: zabbix-web environment: DB_SERVER_HOST: mysql ZBX_SERVER_HOST: zabbix-server PHP_TZ: Asia/Shanghai ports: - "8080:8080" depends_on: - zabbix-server zabbix-agent2: image: zabbix/zabbix-agent2:alpine-7.0-latest container_name: zabbix-agent2 environment: ZBX_SERVER_HOST: zabbix-server ZBX_HOSTNAME: zabbix-agent2 ports: - "10050:10050" volumes: zabbix-db:这里有几个参数值得单独说。depends_on只保证启动顺序,不保证"依赖的服务已经可用",所以 MySQL 上加了healthcheck,server 用condition: service_healthy等 MySQL 真正能响应才启动,这是解决"容器全起来了但互相连不上"的治本方案。PHP_TZ只作用于 Web 前端,它决定了图表横轴时间显示成哪个时区,后面避坑章节里还会遇到它。Agent2 的ZBX_SERVER_HOST决定 agent 向哪个 server 上报,这里填服务名zabbix-server,它俩在同一 compose 网络里可以直接解析。
如果你本来就有现成的 MySQL 8 实例,不想再起一个数据库容器,就把 compose 里的 mysql 服务整体拿掉,把 server 和 web 的DB_SERVER_HOST改成那个数据库的 IP,并把MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD换成数据库里实际存在的账号。Zabbix 的 schema 是 server 启动时自动初始化的,不用手工导入 SQL。
3.3 启动与验证:从 compose 到浏览器之间还有几步
compose 文件保存为docker-compose.yml后,在同一个目录执行:
docker compose up -d docker compose psup -d是后台拉起全部服务,docker compose ps看每个容器的状态。正常情况四个容器都应该是 Up 状态,MySQL 显示 healthy。如果 ps 里看到Restarting,别慌,先看日志:
docker compose logs -f zabbix-serverlogs -f实时跟踪某个服务的输出。server 容器第一次启动会做数据库初始化,日志里出现database is not populated之类的提示属于正常过程,等十几秒它会自动建表。真正需要警惕的是反复出现Cannot connect to MySQL,那是连库参数有问题,往下看避坑章节第一条就是它。
容器都稳定后,浏览器访问http://服务器IP:8080,能看到 Zabbix 的初始化向导,说明容器协作已经打通,进入第 4 章的 Web 配置。
4. 把监控跑起来:Web 初始化、加入第一台主机与 net.tcp.port 简单检查
4.1 Web 初始化:数据库连接地址从 localhost 变成容器名
浏览器打开http://localhost:8080,进入安装向导。语言选 English 还是中文都行,后面可以改。到数据库配置页时,最关键的改动是数据库地址——不要填 localhost,填mysql,端口 3306,数据库名zabbix,用户zabbix,密码zabbix-pass-123。这套参数和 compose 里 MySQL 的MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD完全对应。整个向导流程和 Zabbix 官网的安装文档一致,唯一的区别是"数据库主机"这一栏从本机安装时的 localhost 换成了 Docker 网络里的服务名。
初始化完成后进入登录页,默认账号是Admin,密码zabbix。第一次登录会提示改密码,建议当场改掉,Zabbix 的默认密码出了名的不安全,暴露在公网环境里分分钟被扫描器拿下。
改完密码后建议顺手确认一下时区配置。如果 Web 里显示的时间比本地时间慢 8 小时,说明前端容器PHP_TZ没生效,返回 compose 把PHP_TZ: Asia/Shanghai加上再docker compose up -d zabbix-web重建一次。这一步现在做,后面看图表时区才不用返工。
4.2 添加第一台主机:agent2 容器如何被 server 认领
Agent2 已经在 compose 里启动了,现在要让它被 Zabbix Server 认领。进入Configuration → Hosts,点右上角Create host。Host name 填zabbix-agent2,这必须和 agent2 容器里ZBX_HOSTNAME环境变量一致,否则 server 不认这个主机名。Interfaces 区域点 Add 添加 Agent 接口,IP 或 DNS 这一栏填zabbix-agent2——因为在同一个 Docker 网络里,server 可以直接用服务名解析到这个容器,不用像物理机那样填 IP。
Templates 区域搜索Linux by Zabbix agent,把它关联到这台主机,这是 Zabbix 内置的 Linux 模板,自带 CPU、内存、磁盘、网络等几十个监控项,够用。保存后等待一两分钟,主机列表那一行 Availability 图标会从灰色变成绿色,绿色表示 server 已经能通过 10050 端口拿到 agent 的返回数据。
从 server 容器内部手动验证是最快的方式:
docker exec -it zabbix-server zabbix_get -s zabbix-agent2 -p 10050 -k "system.cpu.load[all,avg1]"zabbix_get是 server 镜像自带的调试工具,-s指定目标主机,-p指定 agent 端口,-k是监控项 key。如果返回一个像0.150000这样的数字,说明 agent 数据链路完全打通,不用再去看了。
4.3 不依赖 agent 的探测:net.tcp.port 这个 key 怎么用
有些场景里被监控对象并不想装 agent,比如只关心某个端口还通不通,这时候 Zabbix 提供的"简单检查"(simple check)就派上用场了。它由 server 直接发起探测,不经过 agent,agent 宕机都不影响探测结果。最常用的就是net.tcp.port。
新建监控项,key 填:
net.tcp.port[,8080]第一个参数是 IP,留空表示使用主机的 Address;第二个参数是端口。它的语义是"server 尝试 TCP 连接主机的 8080 端口,能连上返回 1,连不上返回 0"。由于默认 30 秒更新一次,写在触发器里做端口存活告警非常顺手。
| 简单检查 key | 作用 | 典型用法 |
|---|---|---|
net.tcp.port[,8080] | TCP 端口连通性 | 探测 Web 服务是否存活 |
net.udp.port[,161] | UDP 端口探测 | 探测 SNMP 服务是否应答 |
icmpping | ICMP 连通性 | 检测主机是否在线 |
这类 key 在"监控项配置 → 类型"里要选Simple check,如果用默认的 Zabbix agent 类型去填这个 key,server 会把它当成 agent 的监控项去请求 10050 端口,结果一定报"不支持的监控项"。这个选择器和 key 的对应关系是最容易踩的坑,新手在这里翻车率很高。
5. Docker 安装 Zabbix 常见问题:五条血泪排查记录
5.1 现象:Server 日志刷屏Cannot connect to MySQL
启动后docker compose logs -f zabbix-server不断输出Cannot connect to MySQL server [mysql:3306],或者 Web 初始化时数据库连接步骤直接红字报错。
原因基本是三个:MySQL 容器还没就绪 server 就开始连;DB_SERVER_HOST写成了 127.0.0.1,而容器里的 localhost 根本不是 MySQL;还有一个很容易被忽略——MYSQL_PASSWORD在 mysql 容器和 server 容器里填得不一致,创建用户时用的是 A 密码,server 连接时用的是 B 密码。
解决:compose 里 MySQL 服务加 healthcheck,server 的depends_on用condition: service_healthy(第 3 章的写法就是标准答案,直接抄);数据库主机统一填服务名mysql;三处密码——mysql 的MYSQL_PASSWORD、server 的MYSQL_PASSWORD、Web 向导里填的密码——必须是同一个值。
5.2 现象:图表横轴显示 Universal Time,时间对不上
监控图表里时间轴显示Universal Time,或者比北京时间慢 8 小时,数据本身没问题但看起来极其别扭。
原因:Zabbix 的时间显示是前端行为,由PHP_TZ决定;同时 server 和 MySQL 各自的时区也会影响告警通知里带的时间戳。只改一处,另外两处没改,就会出现这种"半对半错"。
解决:compose 里三个容器全部加TZ: Asia/Shanghai,web 容器额外加PHP_TZ: Asia/Shanghai,然后docker compose up -d重建所有容器。历史数据的时间戳在存储层是 Unix 时间戳,不会因为重建丢失或偏移,前端时区修正后图表立刻变正常。
5.3 现象:告警想手动消除,Close 按钮却是灰的
某个问题已经处理完,但 Zabbix 页面上告警还挂着,点击 Close 按钮没反应,或者按钮根本是灰的不可点。
原因:Zabbix 的"手动关闭"是特权操作,需要两把钥匙——触发器允许手动关闭 + 当前用户角色有problem.close权限。默认情况下触发器并没有勾选允许手动关闭,按钮自然是灰的。
解决:进入Configuration → Hosts找到对应主机,打开触发器编辑页,在 Problem details 区域勾选Allow manual close,保存后回到Monitoring → Problems,勾选目标问题,点底部 Close 按钮。如果按钮仍然不可用,再检查当前用户角色是否具备关闭权限。
5.4 现象:计划性报表报错Report manager is disabled.
在 Reports → Scheduled reports 里创建了报表,点执行或等待定时触发时,报错提示报表管理器被禁用。
原因:Zabbix 的报表功能在 7.0 里默认并没有完全打开,需要在管理后台手动把报告管理器状态切为启用。这个开关藏得比较深,很多人找半天找不到。
解决:进入Administration → General → Reporting,把Report manager的状态从 Disabled 改为 Enabled,保存。之后再回到 Scheduled reports 重新执行一次,报表就能正常生成。顺手把网页右上角用户角色里的"管理报表"权限也确认一遍,避免有权限但没角色授权的情况。
5.5 现象:agent 容器活着,Web 上主机状态却一直是红色
docker compose ps里 agent2 容器正常 Up,但 Web 界面主机 Availability 一直是红色报警,或者主机列表显示不可达。
原因:server 和 agent2 不在同一个 Docker 网络。常见于手动给某个容器单独指定了--network,或者 compose 文件里定义了一个自定义网络但把服务分到了两段。Docker 的默认 bridge 网络里服务名解析只能覆盖同一网络的容器。
解决:把 compose 里的四个服务全部放进同一个网络段,不显式指定网络时它们默认就在同一个网络里,直接能用服务名互通。如果已经手动建了自定义网络,就在 compose 里给每个服务都声明这个networks。检查手段很简单:
docker exec -it zabbix-agent2 getent hosts zabbix-server这条命令能解析出zabbix-server的 IP 就说明网络互通,解析不到就一定是网络隔离问题,去排查 compose 的网络声明。
6. 进阶:用 Zabbix API 验证监控链路,把告警收口到自己的脚本里
6.1 用 curl 拿到 token,再用 Python 拉一次主机列表
UI 上看到绿色图标只说明数据进了前端,还不够严谨。我一般会再走一遍 API,确认数据真正从 agent 流到了 Zabbix 的数据库里。Zabbix 的 API 入口是同一个 URL,先登录拿 token:
curl -s -X POST http://localhost:8080/api_jsonrpc.php \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"user.login","params":{"username":"Admin","password":"改过的密码"},"id":1}'返回的 JSON 里result字段就是 token,默认两个小时过期。拿到 token 后用 Python 请求host.get和item.get,一次性把主机状态和 CPU 负载拉下来:
import json import requests ZABBIX_URL = "http://localhost:8080/api_jsonrpc.php" TOKEN = "上一步返回的token" def call(method, params): resp = requests.post( ZABBIX_URL, json={"jsonrpc": "2.0", "method": method, "params": params, "auth": TOKEN, "id": 1}, timeout=10, ) resp.raise_for_status() return resp.json()["result"] hosts = call("host.get", {"output": ["hostid", "host", "status"], "selectInterfaces": ["ip", "dns"]}) for h in hosts: print(h["hostid"], h["host"], h["status"])host.get返回每个主机的 ID、名称和状态,status为 0 表示启用监控。能跑通这段脚本,说明从 agent 采集到数据库存储再到 API 查询的整条链路是通的,比盯着 UI 图标可靠得多。
6.2 用 problem.get 判断"主机问题已经解决"并推送恢复消息
做告警收口时,我习惯用problem.get拉当前未恢复的问题,再用event.get查恢复事件,两轮比对就能判断哪些告警消失了。判断"问题已解决"的本质:上一轮还在 problem 列表里、这一轮不在了,同时出现了对应的 recovery 事件。
def current_problems(): return call("problem.get", { "recent": True, "output": ["eventid", "name", "severity", "clock"], }) def recent_recoveries(): return call("event.get", { "source": 3, "object": 0, "value": 1, "sortfield": ["clock"], "sortorder": "DESC", "limit": 20, "output": ["objectid", "name", "clock", "value"], })source: 3表示触发器事件,value: 1表示恢复事件。拿到恢复事件后,对照你的业务维护窗口,再决定是否推送"已恢复"通知,比直接在 Zabbix 里配动作灵活得多。从那以后,我每次在 compose 里调整环境变量、换镜像 tag,都会强制走一遍这个 API 脚本,确认最新值真的能通过item.get取到,才敢说部署完成。这套验证习惯帮我拦截过好几次"界面显示正常但数据没更新"的假象。希望这篇记录能帮你在 Docker 上少走几趟弯路,把 Zabbix 稳稳跑起来。
本文还有配套的精品资源,点击获取