ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker部署Zabbix实战:镜像选型、网络排查与告警处理指南

Docker部署Zabbix实战:镜像选型、网络排查与告警处理指南 最近在社区里总能看到一类问题把我逗乐了一边是新手问“zabbix server必须装到麒麟系统服务器版本吗”一边是踩坑老手在问“docker安装mysql失败怎么办”“docker网络不通怎么排查”。说实话用Docker搭Zabbix这件事难点从来不在那几个docker run命令而在于把数据库、Server、Web前端、Agent这几号角色放在同一个网络里还要让它们安心把数据写进你指定的数据卷。这篇内容就是基于我自己的实际部署经历从镜像选型、compose编排、高频故障排查到上线后的告警日常一次性讲清楚。适合正在选型的运维同学也适合那些已经装完但每天被“问题已解决”“告警怎么手动消除”折磨的一线值班人员。1. 为什么把Zabbix装进容器先聊动机再谈方案1.1 从“必须装到麒麟系统吗”这个热搜说起我先把结论扔在前面不需要。Zabbix官方镜像跑在哪个Linux发行版的宿主机上跟镜像内部是什么系统没有直接关系。你宿主机用麒麟、用CentOS、用Ubuntu、用Debian都行前提就两条内核版本能跑当代Docker端口不被占用。为什么网上那么多人纠结系统问题因为传统二进制安装的Zabbix本质上是一整套本地依赖环境PHP版本、Apache/Nginx、数据库客户端、字体库、时区配置全部要跟发行版自带的包管理配合。CentOS 7默认的PHP 5.4带不动新版Zabbix前端Ubuntu 22.04的PHP 8.1又时不时冒出graph字体缺失的问题。这些坑叠加起来就会让人产生“是不是必须换系统才能装”的错觉。容器化之后这套依赖全被锁进镜像里了。Zabbix Server镜像内部自带编译好的PHP环境前端Web镜像内置Nginx数据库镜像更是开箱即用。宿主机要做的就是保证Docker守护进程稳定、磁盘空间够、网络策略不封端口。换句话讲你的注意力应该从“发行版包怎么装”彻底转移到“镜像版本怎么选、环境变量怎么配、数据卷怎么挂”上。1.2 容器对监控这类基础设施的真正价值我见过很多团队把Zabbix容器化当成分散式部署的银弹其实它的核心价值不是“跑得快”而是“可预期”。拿最常见的升级场景对比一下二进制升级Zabbix 5.0到6.4你要先备份数据库、停服务、编辑一堆配置文件、跑升级脚本任何一个依赖不对就原地崩溃容器化的升级路径简单粗暴——先备份数据卷目录然后docker compose pull拉新版本镜像再docker compose up -d替换容器。数据库schema升级由Zabbix Server启动时自动执行前端版本由镜像tag锁定整个过程几乎没有人工介入的缝隙。还有一个很容易被忽略的点容器化之后监控数据的备份变得极其直观。以前你要想备份Zabbix的历史监控数据得先搞清楚数据库在哪里、用哪种备份策略搞得像个考古项目。现在只要把PostgreSQL数据卷挂载到宿主机目录备份就是tar整个目录的事恢复也一样。这种“数据与程序分离”的思维用一次就会爱上。1.3 什么场景下我不建议上容器也不是所有场景都适合立刻容器化。第一宿主机内核太老比如CentOS 6这种连新版Docker都装不上硬上只会换来一堆兼容性报错。第二你需要在监控采集链路上做非常底层的网络定制比如自定义复杂iptables规则来配合多网卡分流容器网络会引入一层NAT排查问题时会多一个变量。第三资源极度紧张的小机器内存低于2G还要在同一台机器上跑数据库加Server加Web那无论是不是容器都会很吃力不如直接用Agent单机模式做轻量采集。所以我的观点很明确容器化是更优实践但它不是魔法。真正决定成败的永远是三个前置决策——数据卷挂载对不对、自定义网络建没建、镜像tag是不是同一版本线。2. 镜像组合与网络规划部署前容易被忽略的两个决策点2.1 数据库镜像选MySQL还是PostgreSQLZabbix官方对MySQL和PostgreSQL都提供完整的镜像组合并没有哪个“不支持”的说法但实际部署的体感差别还是挺大的。我先给一张选型对照表对比维度PostgreSQL方案MySQL方案镜像名zabbix-server-pgsql、zabbix-web-nginx-pgsqlzabbix-server-mysql、zabbix-web-nginx-mysql内存占用相对更省空闲时能压到几百MB基线稍高需要额外tuning历史数据表维护大表清理和分区维护更顺手也能用但要花精力调参数团队熟悉度看团队积累团队若已有MySQL体系则上手更快适合场景全新部署、预算有限已有MySQL运维体系、数据要进统一审计我给新项目的建议是除非你们团队已经有明确的MySQL绑定需求否则全新部署优先选PostgreSQL。原因也很朴实Zabbix的历史数据表写放大很夸张PG在这类时序读写场景下的锁竞争更小默认配置下内存也更可控。如果你在某个机房看到一台2G内存的小机器上跑Zabbix全套容器pgsql方案大概率比mysql方案活得舒服。镜像tag方面别碰latest一定要锁定版本线。Zabbix从6.4开始有明确的LTS版本目前常用的长期支持版是7.0线镜像tag类似alpine-7.0-latest。server、web、agent三个镜像尽量保持同一个版本线否则会出现“前端提示Server version mismatch”这种看着像网络故障、实际是版本不一致的玄学问题。2.2 自定义bridge网络与host模式的生死差异Docker网络模式里bridge、host、none三兄弟中host模式看起来最省事实际上最坑。host模式下容器直接复用宿主机网络栈端口不带映射直接暴露听着方便但容器之间没法通过服务名解析地址所有配置都得写localhost或者具体IP。一旦以后要拆成web与server分离部署那些硬编码地址会让你怀疑人生。自定义bridge网络才是我推荐的方案。在这个模式下每个容器有独立IP同网络内的容器可以直接用服务名互相访问。拿生活打比方bridge网络就像一个小区每栋楼有门牌号你喊“张三”就能找到人host模式是所有人挤在一层楼距离确实近但谁是谁全靠大喊大叫管理和隔离都很乱。实际操作中还有个小坑如果你手动执行过docker network create zbx-net那么在compose文件里引用这个网络时要标记external: true否则compose会先尝试创建结果报“network not found”。我更推荐直接在compose文件里声明网络让compose自己管理生命周期这样团队换人接手时看一份文件就能还原全部结构。2.3 数据卷先于容器存在一次“容器重删后配置全没”的反思说个我自己翻车的真事。有段时间为了在Zabbix前端显示中文我把一个中文字体文件copy进容器里覆盖了默认字体当时界面马上就正常了我还挺得意。结果过了几天觉得容器日志太多手贱执行了docker compose up -d --force-recreate再打开页面中文全变成方块。原因很简单所有落在容器可写层的改动容器一重建就全部归零。后来我把字体文件放到宿主机的/mnt/zabbix/fonts目录再挂载进容器重启多少次都不会丢。这只是一个小字体文件丢了顶多重配一次但数据库目录不挂载就是事故了。Zabbix几年的历史监控数据全在PG数据卷里容器一删数据灰飞烟灭。我的规范是容器层不保存任何有状态数据数据库盘、配置目录、字体目录、报警脚本全部用bind mount指到宿主机固定路径比如/mnt/zabbix/pgdata。这里还要特别提醒一句docker volume prune这个清理命令会把未被任何容器使用的具名卷直接干掉如果你用的是compose自动创建的具名卷误删后想恢复只能靠备份。用bind mount到固定目录则直观得多备份就是tar -czf backup.tar.gz /mnt/zabbix。3. 可直接落地的compose编排数据库、Server、Web一次起齐3.1 完整docker-compose.yml参考下面这份编排是我目前生产环境在用的简化版改成你的IP、密码和路径就能直接跑。我用的是Zabbix 7.0长期支持版搭配PostgreSQL 16镜像Web前端用官方自带的Nginx版本。version: 3.8 networks: zbx-net: driver: bridge services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: zabbix POSTGRES_PASSWORD: zabbix_pwd POSTGRES_DB: zabbix TZ: Asia/Shanghai volumes: - /mnt/zabbix/pgdata:/var/lib/postgresql/data networks: - zbx-net healthcheck: test: [CMD-SHELL, pg_isready -U zabbix -d zabbix] interval: 10s timeout: 5s retries: 5 restart: unless-stopped zabbix-server: image: zabbix/zabbix-server-pgsql:alpine-7.0-latest environment: DB_SERVER_HOST: postgres POSTGRES_USER: zabbix POSTGRES_PASSWORD: zabbix_pwd POSTGRES_DB: zabbix TZ: Asia/Shanghai ports: - 10051:10051 volumes: - /etc/localtime:/etc/localtime:ro depends_on: postgres: condition: service_healthy networks: - zbx-net restart: unless-stopped zabbix-web: image: zabbix/zabbix-web-nginx-pgsql:alpine-7.0-latest environment: ZBX_SERVER_HOST: zabbix-server DB_SERVER_HOST: postgres POSTGRES_USER: zabbix POSTGRES_PASSWORD: zabbix_pwd POSTGRES_DB: zabbix PHP_TZ: Asia/Shanghai ports: - 8080:8080 - 8443:8443 depends_on: - zabbix-server networks: - zbx-net restart: unless-stopped zabbix-agent: image: zabbix/zabbix-agent:alpine-7.0-latest container_name: zabbix-agent environment: ZBX_HOSTNAME: Zabbix server TZ: Asia/Shanghai ports: - 10050:10050 networks: - zbx-net restart: unless-stopped启动命令就两行docker compose up -d docker compose ps等几秒后浏览器访问http://宿主机IP:8080默认账号Admin默认密码zabbix。第一次登录后第一件事就是改密码这个没得商量。3.2 逐项解读环境变量它们各自在等谁很多新手看到一堆环境变量就头大其实这些变量回答的都是一件事你在这个容器里要连谁。POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB是PostgreSQL容器首次初始化时创建账号和库用的三个容器里的这三组值必须完全一致否则就会报数据库连接失败。DB_SERVER_HOST: postgres是Zabbix Server容器里最重要的配置它告诉Server程序数据库的主机名就叫postgres。这个“postgres”不是IP而是我们在同一网络里定义的容器服务名。正因为Server和数据库在同一个zbx-net网络里Docker的DNS解析会把“postgres”自动解析成数据库容器的IP。同理Web容器里的ZBX_SERVER_HOST: zabbix-server也是靠服务名找到Zabbix Server。理解了这一点你就不会再问“为什么我的容器里localhost不通”这种问题了——bridge网络下面localhost永远是容器自己找同伙必须喊服务名。depends_on配合condition: service_healthy是另一个关键点。PostgreSQL容器启动不代表数据库已经完成初始化如果没有健康检查Zabbix Server很可能在数据库还没就绪时就开始连接结果就是一连串的“Connection refused”。有了pg_isready健康检查Compose会等数据库真正可以被连接时才启动Server容器。注意这个写法需要新版Docker Compose v2如果你还在用老版docker-compose就把condition那段删掉靠restart: unless-stopped让Server自动重试。3.3 镜像下载慢与“docker compose安装”这类前置问题镜像下载慢是大陆机房永恒的话题不一定是你网速的问题很可能是Docker Hub的出口本身就抽风。通用解法是给Docker配置镜像加速器编辑/etc/docker/daemon.json加入registry-mirrors字段填一个你常用云厂商提供的镜像加速地址然后重启Docker服务。配置完可以用docker info查看Registry Mirrors是否生效。如果你的环境连外网都不通那就得走离线镜像路线在一台能联网的机器上docker pull所需的镜像然后docker save打成tar包再docker load导入目标机器。这个方法看着笨但在等保机房和隔离网环境里是最可靠的。还有一类问题是“docker compose安装”。新版Docker安装包默认带docker compose插件但有些老教程只让你装docker-ce顺手把docker-compose-plugin也装上否则执行docker compose up时会提示command not found。装了插件后可以用docker compose version确认。如果你非要用老版docker-compose二进制也不是不行但新版插件无论是语法支持还是命令输出都比老版舒服太多能用新就上新。4. 从启动报错到网络不通容器化Zabbix的高频故障排查链路4.1 docker API权限permission denied的完整解决路径刚装好Docker就翻车的第一道坎十有八九是这个报错permission denied while trying to connect to the docker api。这个报错的根源非常直白——你的当前用户不在docker组里而Docker CLI需要读取/var/run/docker.sock这个socket文件。查看这个socket的权限你可以发现它只对root用户和docker组开放了读写权限。解决办法是把自己的账号加进docker组sudo usermod -aG docker yourusername注意这条命令执行后要重新登录终端或者执行newgrp docker切换当前组否则当前会话依然继承旧的权限列表。有些桌面环境更省事但原理一样。这里要敲个警钟能操作Docker等价于能在宿主机上为所欲为因为容器是可以挂载宿主机目录的。所以生产环境里别随便给无关账号开docker组权限使用sudo反而更安全因为至少多一层审计。如果你是Windows或macOS用户报错往往不是socket权限而是Docker Desktop启动失败提示类似virtualization support not detected。这是系统虚拟化没开或者WSL 2组件缺失。Windows要在BIOS里打开CPU虚拟化并安装WSL 2内核更新包macOS老款Intel机器要确认HyperKit框架没被其他安全软件拦截。总之先把Docker Desktop的状态图标点绿再做后面的容器操作。4.2 数据库起不来的两种根因与日志定位法容器编排完最常出现的是PostgreSQL容器一直在Restarting网页一打开就是“Database connection failed”。遇到这个情况别急着怀疑密码先按下面的排查链路走一遍。第一步docker compose ps -a看哪个容器状态异常。第二步docker logs --tail 100 postgres看数据库容器日志。日志能告诉你绝大多数真相。我实际处理过的最典型案例有两个。根因APOSTGRES_DB配置不一致。数据库初始化时建的库叫zabbix但Server容器里POSTGRES_DB写成了zabbix_test两者不匹配数据库日志直接报database zabbix_test does not exist。这种问题在compose文件里一眼就能对比出来但新手往往会先跑去检查网络。根因B宿主机挂载目录的权限不对。PostgreSQL容器内的进程用户UID是999如果你手动创建了/mnt/zabbix/pgdata且owner是root容器启动时初始化和写入都会失败日志里会出现Permission denied。解决方法是chown -R 999:999 /mnt/zabbix/pgdata但如果这个目录里已经有残留的partial数据建议先清空目录再重新初始化否则PG会一直报文件已存在相关错误。还有一类更隐蔽的根因是磁盘满。Zabbix历史数据表吃空间速度远超你的直觉尤其默认保留周期没调过的情况下一块20G的数据盘撑不了几个月。日志出现No space left on device时先df -h看挂载点然后去前端调整Housekeeper保留策略或者把历史数据表分区方案配上。4.3 容器间“互相找不到”的真正排查顺序如果数据库正常但打开Web页面提示“Unable to connect to Zabbix server [zabbix-server]:10051”这就是典型的容器间网络互通问题。我说一下我自己的排查顺序基本能定位90%的场景。先确认三个容器是不是都在同一个自定义网络里。执行docker inspect zabbix-server --format {{json .NetworkSettings.Networks}}看返回的网络名是不是zbx-net。如果某个服务忘了写networks段它会被塞进默认的bridge网络跟zbx-net里的容器互不可见。这个坑在compose文件里特别容易漏因为compose不强制要求每个服务都声明网络。接着验证DNS解析。正常来说在Server容器里执行getent hosts postgres应该返回一个172.x开头的IP。如果能解析出来说明网络层面没问题问题大概率在Zabbix Server服务本身这时再看docker logs zabbix-server的日志。如果解析失败就去docker network inspect zbx-net看容器的IP归属确认是不是有容器掉线。最后再强调一个容易混淆的概念端口映射和容器间通信是两码事。容器A没发布10051端口到宿主机不影响容器A和容器B在同一个自定义网络里互相访问10051端口。只有宿主机外部或跨主机访问才依赖ports映射。所以排查Web到Server的连接问题时先看容器网络别一上来就改防火墙。4.4 时间、语言与Web端访问异常容易被忽略的隐藏细节有一类问题不属于启动故障但上线后一定会碰到就是时区。容器默认使用UTC时间不处理的话你会发现监控趋势图的横轴永远比北京时间慢8小时告警时间也怪怪的。在compose里给Server、Web、Agent三个容器都加上TZ: Asia/Shanghai再挂载/etc/localtime:/etc/localtime:ro一套操作下来才算是“入乡随俗”。再一个是中文字体。Zabbix前端界面可以切中文但镜像里不包含中文字体库仪表盘上的中文或者图形里的中文标签会显示成方块。两个解决方案一个是把宿主机里的中文字体文件挂载到容器里然后在前端界面“字体”配置里指定该字体文件名字第二个更省事直接给Web容器挂载一个包含fonts-noto-cjk的字体目录。字体问题是小问题但因为是视觉层面的用户感知特别强建议新部署时直接配好。如果Web页面打开慢还要看一眼Nginx容器日志里有没有大量失败解析记录。前端与Server分离部署后Web容器去解析Server时如果走的是外部域名或公网IPDNS解析抖动会直接拖垮页面响应。后面会聊到分离部署这里先记住能走内网IP就绝不走公网域名。5. 纳入主机与处理告警上线后真正会用的日常操作5.1 新主机监控三步agent容器、主机配置、连通性测试部署完成只是开始真正的高频操作是把新机器纳入监控。我习惯的做法是三步走。第一步在被监控机器上起一个agent容器。日常建议用host网络模式这样agent共享宿主机网络栈Zabbix Server直接连被监控机的IP就能通信省去容器网络路由的麻烦docker run -d --name zabbix-agent --network host \ -e ZBX_HOSTNAMEweb-server-01 \ -e ZBX_SERVER_HOST192.168.1.10 \ zabbix/zabbix-agent:alpine-7.0-latest第二步在Zabbix Web界面里“数据采集→主机”创建主机主机名称必须和容器里的ZBX_HOSTNAME完全一致然后填IP地址端口默认10050。第三步配置模板并检查可用性。最常用的模板是“Linux by Zabbix agent”应用到主机后过一两分钟再去主机列表看可用性变成绿色就说明链路通。这里有一个非常容易误判的地方被动模式下Zabbix Server要去连Agent的10050端口主动模式下Agent反而会主动连Server的10051端口。你如果只想让监听的机器主动往外连可以选“主动模式”模板但无论哪种模式两边端口在防火墙和安全组里都要放通对应方向。我之前把一台数据库主机加入监控半天都是灰色“Zabbix agent is not running”最后发现是agent容器用了bridge模式Server拿到的是NAT地址路由怎么都搭不上。改成--network host后立刻解决。5.2 “问题已解决”是什么意思手动消除告警的正确姿势很多人搜“zabbix监控系统显示的主机问题已经解决”“告警如何手动消除”本质上是对Zabbix问题事件机制不熟。我先铺垫一个概念当某个监控项从异常恢复到正常值Zabbix会自动把这条触发器问题标记为“已解决RESOLVED”此时问题列表里那行会变为绿色不再触发动作。你不需要做任何手动操作。但如果你在值班系统里看到告警邮件早已发送最终需要在Zabbix里留下处理闭环那就要手工确认一次。具体操作路径进入“监测→问题”勾选要处理的问题点击底部“更新”把状态改为“已解决”并填写备注比如“凌晨2点的磁盘告警已扩容并观察30分钟”。这相当于你在告警事件上盖了一个“我处理过”的章后续审计和复盘都有据可查。团队协作时还可以给问题打“确认”标记表示已有人接手避免多个人同时盯着同一个报警重复处理。比手动消除更科学的做法是提前使用维护窗口。比如夜里要升级数据库、割接网络可以提前在“配置→维护”里创建一段维护周期把涉及的主机加进去。维护期间这些主机的告警会被主动抑制不会半夜震你手机。维护结束后Zabbix会继续正常监控。这套机制想清楚了“告警如何手动消除”就不再是操作问题而是流程设计问题。5.3 生产环境下的Web与Zabbix分离部署经验当单机容量吃紧或者出于安全要求想把Web前端和Zabbix Server拆到不同机器时容器化的灵活性就体现出来了。核心思路是不再依赖同一个bridge网络内的服务名解析而是通过宿主机IP显式互联。分离部署至少要把端口和服务拆开Zabbix Server机器上只保留Server容器和PostgreSQL容器Web容器跑在另一台机器上。Web容器里的ZBX_SERVER_HOST要改成Zabbix Server所在机器的IP而不是服务名DB_SERVER_HOST改成数据库所在机器的IP。Zabbix Web前端是直连数据库读数据的所以数据库端口也要对Web容器开放。跨主机通信的端口放行策略务必收敛数据库端口PostgreSQL默认5432只放行给Zabbix Server和Web前端所在机器的IPServer端口10051放行给Web前端和所有主动模式的AgentAgent端口10050放行给Zabbix Server。云环境里安全组别用0.0.0.0/0加个白名单不费事。如果还想再进一步给Web前端套一层反向代理做域名和HTTPS那我建议用Nginx或Caddy。有个细节容易被忽略Zabbix 6的某些页面交互会用到WebSocket反向代理配置里需要给对应location加上Upgrade和Connection头否则前端操作偶尔会卡住。前端页面的静态资源也可以让反向代理加一层缓存体感会好很多。最后分享几句个人体会我用Docker搭Zabbix也有两年多了最大的感受是真正让人省心的不是“docker compose up -d”那一瞬间的丝滑而是数据卷和编排文件带来的可预期性。我现在每次升级前的固定动作就是先tar备份整个/mnt/zabbix目录然后docker compose pull拉新镜像再docker compose up -d。如果哪次升级后Web页面提示连不上Server我从来不会去翻网络图而是老老实实检查三个环境变量DB_SERVER_HOST、ZBX_SERVER_HOST、POSTGRES_PASSWORD十次有八次问题就出在这三个值上。最后再分享一个小习惯给每个容器都配上restart: unless-stopped但遇到需要变更配置时一定先docker compose down再改再起不要让服务在异常状态下反复重启否则日志会刷得你怀疑人生。希望这篇分享对正在部署Zabbix的你有点帮助也欢迎大家把自己踩过的更离奇的坑发在评论区我顺手记进排查笔记。
返回列表