
简介面向系统管理员与运维工程师的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 要连 MySQLWeb 要连 ServerAgent 要上报给 Server四个容器之间靠 Docker 网络里的服务名互相解析。这套协作方式和部署微服务项目是同一套逻辑——理解了 Zabbix 的 compose以后看任何微服务编排文件都不会发怵。所以选型时不要把注意力全放在哪个镜像最方便先看宿主能不能跑稳 Docker。对比项Docker DesktopDocker CE compose 插件适用宿主Windows 10/11、macOSLinux 服务器、云主机安装方式安装包 GUI包管理器装 daemon CLI虚拟化依赖WSL2 / Hyper-V无原生容器compose 支持自带安装docker-compose-plugin典型报错virtualisation support wasnt detectedpermission denied 访问 daemon socket2.2 Windows 11 最常见的一道坎虚拟化报错与 WSL2Windows 上装 Docker Desktop 最容易翻车的就是启动瞬间弹一句Docker Desktop failed to start because virtualisation support wasnt 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.03306保存监控配置、历史数据、事件记录zabbix/zabbix-server-mysql10051接收 Agent 上报、执行主动探测、计算触发器zabbix/zabbix-web-nginx-mysql8080Nginx PHP 前端人机交互入口zabbix/zabbix-agent210050被监控主机上的采集端支持主动和被动两种模式四个镜像的 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 上加了healthcheckserver 用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 直接发起探测不经过 agentagent 宕机都不影响探测结果。最常用的就是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 服务是否应答icmppingICMP 连通性检测主机是否在线这类 key 在监控项配置 → 类型里要选Simple check如果用默认的 Zabbix agent 类型去填这个 keyserver 会把它当成 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 服务加 healthcheckserver 的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/Shanghaiweb 容器额外加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先登录拿 tokencurl -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}, timeout10, ) 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 稳稳跑起来。本文还有配套的精品资源点击获取