
简介面向需要在离线、内网或等保隔离环境中部署ZLMediaKit流媒体服务的运维与开发人员这里提供了一份以Docker方式打包的zlm完整运行环境可免去手动编译和依赖配置带来的不确定性。压缩包共2个文件tar归档文件承载docker镜像sh脚本负责自动化安装整体约208.73MB体积精简拷贝到目标机器即可开始部署。目前已有375人学习下载适合熟悉容器基础、希望快速完成zlm上线的工程技术人员。借助这份资源使用者可以绕开镜像仓库不可达的障碍直接获得离线镜像和脚本化安装流程镜像内已包含必要组件脚本进一步降低了指令输入错误的风险部署过程更稳妥、更高效。资源包结构紧凑文件类型清晰便于在项目交付或生产变更时留档备查。1. zlm docker 离线安装真正要解决的并不是 Docker 命令而是镜像从哪里来zlm docker 离线安装是内网部署流媒体服务时绕不开的一步。ZLMZLMediaKit是高性能开源流媒体服务器支持 RTSP、RTMP、HLS、HTTP-FLV 和 WebRTC很多安防项目、视频中台、直播系统都拿它做接入和分发。内网交付和别的场景不同现场服务器不连外网不能直接 docker pull也不能指望在部署时临时下载依赖。所以离线安装的核心问题只有一个——把镜像在能联网的机器上准备好再以文件形式带进内网最后用 docker load 跑起来。这篇文章按一线交付的顺序拆解整个流程镜像怎么拉、怎么导出、端口怎么映射、配置怎么改、怎么验证以及我踩过的五个典型坑。适合要去做内网项目交付的运维同事也适合刚接触 ZLM、想在隔离环境里复现的团队。2. 离线镜像制作在一台联网机器上完成拉取、导出与架构核对2.1 拉取镜像并锁定版本不要默认去拉 latest离线部署的起点是有一台能访问 Docker Hub 的机器。常见做法是在这台机器上把 zlmediakit/zlmediakit 镜像拉下来保存成 tar 包再拷贝到内网。这里第一个忠告就是不要顺手拉 latest。ZLM 迭代速度很快latest 标签在不同时间拉下来的内容可能完全不同配置文件字段、可执行程序、Web 页面都会变。今天你在联网机器上拉 latest 做出来的包到三个月后现场再用镜像本身没问题但和你之前调试时用的版本可能已经对不上了。我一般会先到 Docker Hub 确认 zlmediakit/zlmediakit 有哪些标签固定一个明确的 tag。比如官方长期维护的 master 标签它在 Docker Hub 上会持续构建但比 latest 要可预期。如果你们项目有内部统一版本建议以这个为准。# 在能上网的机器上执行 docker pull zlmediakit/zlmediakit:master # 查看镜像基本信息记录架构和版本字段 docker image inspect zlmediakit/zlmediakit:master | grep -E Architecture|Os|Created这里解释一下关键参数zlmediakit/zlmediakit是官方镜像名master是标签。docker image inspect输出的Architecture和Os会在后面做架构核对时用到Created可以辅助判断镜像构建时间。很多人在这个阶段漏掉记录到了内网才发现镜像架构不对最后只能重新折腾一遍。2.2 导出与压缩用 docker save 把镜像变成单个文件镜像拉下来后下一步是导出。docker save会把镜像的所有层打包成一个 tar 文件可以用-o指定输出路径。如果你在内网服务器上只有一块 40G 的系统盘就得先算好 tar 包体积避免拷贝过去解压时把 docker 的存储目录撑爆。# 导出镜像到当前目录 docker save zlmediakit/zlmediakit:master -o zlm-master.tar # 看一眼文件大小 ls -lh zlm-master.tar # 计算 sha256后面传输完要核对 sha256sum zlm-master.tar | tee zlm-master.tar.sha256这个 tar 文件包含完整的镜像层信息拷到内网后可以直接docker load。如果文件太大建议用 gzip 压缩一次实测 ZLM 镜像压缩后体积能明显减小。还有一个更省事的办法docker load 本身支持 gzip压缩后的 tar.gz 不需要解压直接 load 即可。# 压缩并生成校验值 gzip -k zlm-master.tar sha256sum zlm-master.tar.gz | tee zlm-master.tar.gz.sha256参数说明-k保留原始 tar 文件防止压缩后原文件被删掉sha256 文件要和 tar 包放在一起传到了内网第一件事就是先核对校验值。这一步虽然简单但很多线上事故都是因为传输中途文件损坏等到 docker load 报错才回头检查。2.3 架构核对x86 和 ARM 的镜像千万别当成同一个包内网服务器常见两类 CPU 架构x86_64 和 ARM64。Docker 拉镜像时默认拉取与当前机器架构匹配的镜像。如果你在 x86 电脑上 save 出来的镜像拷贝到 ARM 开发板上 load启动时会直接报可执行文件格式错误。这个错误在 docker images 阶段完全看不出来只有运行容器时才暴露。所以导出前务必确认现场机器的架构。最稳妥的做法是先在目标机器上执行uname -m拿到确认的架构后回到联网机器用--platform拉取对应架构的镜像。# 在内网机器上确认 CPU 架构 uname -m # 在联网机器上拉取 ARM64 架构的镜像假设现场是 ARM docker pull --platform linux/arm64 zlmediakit/zlmediakit:master # 再次确认镜像架构避免拉错 docker image inspect zlmediakit/zlmediakit:master | grep -E Architecture|Os需要注意--platform拉取的前提是镜像仓库里有对应架构的镜像。如果现场机器不是常见的 amd64 或 arm64而是小众架构最好在同类架构的机器上先试跑一次不要等到交付现场再试。我自己的习惯是任何离线交付任务先从现场拿到uname -m结果再开始做包。3. 内网导入与容器启动从 tar 包到正式运行的 ZLM 服务3.1 docker load 导入镜像先校验再导入拿到 tar 包后第一步不是急着 load而是先核对 sha256。常见做法是把传输前记录的 sha256 值与现场文件重新计算的结果对比一致再导入。如果校验值对不上说明文件在拷贝过程中损坏重传一次最保险别强行导入。# 在内网机器上执行重新计算文件的 sha256 sha256sum -c zlm-master.tar.gz.sha256 # 校验通过后导入镜像 docker load -i zlm-master.tar.gz # 确认镜像列表里有 zlmediakit docker images | grep zlmediakitsha256sum 命令的-c参数表示从文件读取期望校验值并自动比对。导入成功后docker images 输出会显示zlmediakit/zlmediakit和对应的 tag如果看到REPOSITORY一列为空说明 load 出来的是 untagged 镜像需要用docker tag补上一个标签。这种情况通常是因为原镜像没有完整的仓库名不影响运行但会影响后续管理。3.2 启动容器端口映射是 ZLM 可用性的关键ZLM 工作时要监听多个端口启动容器时这些端口必须都映射到宿主机。下面是我在一个内网交付里实际用过的启动命令端口分配和注释都标清楚了。# 创建宿主机配置目录 mkdir -p /data/zlm/conf # 启动 zlm 容器映射主要协议端口 docker run -d --name zlm \ --restartalways \ -p 1935:1935 \ -p 554:554 \ -p 8080:80 \ -p 10000:10000/udp \ -p 30000-30500:30000-30500/udp \ -v /data/zlm/conf:/opt/media/conf \ zlmediakit/zlmediakit:master挑几个容易理解错的参数说明一下-p 1935:1935是 RTMP 推拉流端口-p 554:554是 RTSP 端口-p 8080:80把容器内 HTTP 端口映射到宿主机 8080用于 HTTP-FLV、Web 管理页面和 API 调用。10000/udp是 RTP 接收端口30000-30500/udp是 RTP 代理端口范围如果涉及 RTSP 的 UDP 传输这两个映射缺一不可。-v /data/zlm/conf:/opt/media/conf把配置目录挂载到宿主机这是后面改配置的基础。--restartalways很重要。内网服务器重启后容器能自动拉起否则一次断电就可能让整个流媒体服务瘫在那里你还要找现场的人帮忙手动启动那很被动。3.3 配置文件挂载让容器里的 config.ini 不再是黑匣子容器启动后ZLM 会在/opt/media/conf下自动生成默认配置文件config.ini。因为我们挂载了宿主机目录可以从宿主机直接看到并修改它。如果启动后这个目录是空的先看容器日志确认 MediaServer 是否成功运行。# 查看容器启动日志确认没有异常退出 docker logs zlm 21 | tail -n 30 # 等待几秒后列出挂载目录内容 ls -l /data/zlm/conf正常情况下/data/zlm/conf下会出现config.ini。这时我建议立刻把这文件备份一份比如cp config.ini config.ini.default后面改乱了还能恢复。如果目录一直为空可以从容器里手工拷贝默认配置出来# 如果自动生成失败手动从容器拷贝配置 docker cp zlm:/opt/media/conf/config.ini /data/zlm/conf/config.ini # 重启容器让配置生效 docker restart zlm这里有个容易翻车的点挂载路径必须是/opt/media/conf不是/opt/media/conf/config.ini。我有一次把-v /data/zlm/config.ini:/opt/media/conf/config.ini直接挂成单文件启动后容器内目录被文件覆盖ZLM 直接找不到配置日志里全是配置加载失败。正确做法是把宿主机目录和容器目录建立绑定配置文件放到目录里维护。4. 配置、推流与 API 验证确认 ZLM 真的在为你工作4.1 第一次推流前至少要改的两个配置ZLM 默认配置可以跑起来但内网环境建议至少改两处。第一处是[api]段的secret这是 HTTP API 的访问密钥。默认是空的内网里其他机器可以直接访问/index/api/接口存在风险。第二处是[http]段的端口容器内默认 80我们启动时已经映射到宿主 8080所以不用改。如果你现场 8080 被其他服务占用改宿主机映射即可不用动容器内配置。用编辑器打开/data/zlm/conf/config.ini找到[api]段设置一个足够复杂的密钥[api] secretmyPrivateSecret123保存后重启容器docker restart zlm注意重启后容器内进程会重新加载配置。如果改错了配置项MediaServer 可能起不来此时用docker logs zlm查看日志。日志中会明确提示哪一行配置解析失败按提示修正即可。4.2 用 ffmpeg 推流再用 ffplay 拉流验证配置改好后整个链路是否真的可用最简单的方式是用 ffmpeg 推一路流再用 ffplay 去拉。先在现场服务器或同网段的机器上准备一个测试视频文件。# 推流端把 test.mp4 通过 RTMP 推到 ZLM ffmpeg -re -stream_loop -1 -i test.mp4 \ -vcodec copy -acodec aac \ -f flv rtmp://192.168.1.10:1935/live/test参数说明-re按文件原始帧率读取模拟实时流-stream_loop -1无限循环推流-vcodec copy -acodec aac表示视频流直接拷贝不转码音频压成 aac这样 CPU 占用低。推流地址中的192.168.1.10是 ZLM 所在宿主机的局域网 IPlive是应用名test是流名这两段可以自己定。推流命令执行后在另一个终端拉流验证# 拉流端用 RTMP 播放验证 ffplay -fflags nobuffer rtmp://192.168.1.10:1935/live/test-fflags nobuffer可以降低播放延迟适合快速验证。能出画面说明 RTMP 推拉流链路没有问题。再试一下 RTSP 协议# 用 RTSP 协议拉流验证 ffplay rtsp://192.168.1.10:554/live/test这一步很关键因为有些现场只允许用 RTSP 接流如果 RTMP 正常但 RTSP 不通问题多半出在 UDP 端口映射上。4.3 用 HTTP API 检查流列表和服务器状态ZLM 内置了 HTTP API不用额外装监控插件。推流成功后调用getMediaList接口能看到当前在线流列表这是判断服务是否健康的直接依据。# 查看当前在线流列表注意带上 secret curl http://192.168.1.10:8080/index/api/getMediaList?secretmyPrivateSecret123返回的 JSON 里会包含流的app、stream、online等字段。如果返回空列表说明推流没上来或者推流配置有问题。再进一步检查服务器配置是否正常被 API 读取# 读取服务器配置验证 API 鉴权是否生效 curl http://192.168.1.10:8080/index/api/getServerConfig?secretmyPrivateSecret123 | head -c 500这个命令只为验证两件事HTTP 服务正常监听 8080secret 鉴权生效。如果返回401或403说明密钥不对如果连接超时说明端口映射或防火墙有问题回到第 3 章排查。5. 避坑与常见问题五个让我熬夜的 ZLM 离线部署坑5.1 现象容器启动立刻退出报错 exec format error第一次在 ARM 开发板上做 ZLM 离线安装时我按 x86 机器上的镜像直接 load结果docker run拉起来后容器秒退docker logs里写着exec format error。当时第一反应是镜像损坏但重新导入还是报同样的错。原因其实很简单镜像架构和宿主机架构不匹配。docker load只负责把镜像放进本地镜像仓库它不校验宿主机能不能运行真正执行的时候操作系统才发现 ELF 文件格式不对。解决方法是回到联网机器用--platform拉取目标架构镜像导出后再来一次。同时我每次都会在导出前把docker image inspect输出的架构信息写进交付文档现场导入前先对照一次省得白跑一趟。5.2 现象容器状态正常但宿主机 8080 端口怎么都不通有一次在客户机房docker ps显示 zlm 容器运行中但外部访问 8080 一直超时。我先在宿主机本机访问curl http://127.0.0.1:8080/index/api/getMediaList也是超时说明问题不在防火墙而在容器或端口映射。原因后来找到我启动容器时把-p 8080:80写成了-p 8080:8080容器内 ZLM 监听的是 80于是映射端口和实际监听端口错位。还有一次是宿主机上 nginx 占用了 8080导致 docker 启动时写映射成功但请求被 nginx 吃掉。解决时先看监听端口ss -tlnp | grep 8080确认监听进程是谁再用docker logs zlm看 MediaServer 实际监听端口最后用curl http://127.0.0.1:8080/在宿主机上做本地探测逐步缩小范围。5.3 现象改好的 config.ini 重启容器后变成默认配置这是配置挂载最容易踩的坑。我在一次交付中把宿主机配置文件直接挂在/opt/media/conf/config.ini单文件上结果容器启动后 ZLM 重新生成了默认配置我改的密钥全部丢失API 访问全都 401。原因就是挂载点不对。ZLM 的配置目录是/opt/media/conf容器启动时如果发现目录下没有 config.ini会自动生成一个默认配置。单文件挂载会让容器把这个文件当成整个目录导致默认配置写不进去。解决方法是始终挂载目录也就是-v /data/zlm/conf:/opt/media/conf而不是挂文件。确认方式也很简单重启容器后执行docker exec zlm ls -l /opt/media/conf/config.ini看到文件存在且修改时间是对应的就说明挂载正确。5.4 现象tar 包传到内网后 docker load 报 invalid tar header有次交付时现场工程师用 U 盘从 Windows 机器拷贝 tar 包docker load 直接报archive/tar: invalid tar header。我一开始怀疑是导出过程有问题但重新导出验证 sha256 又对不上现场文件的哈希。原因基本可以锁定在传输环节。U 盘拷贝、FTP 传文件、微信传文件都可能破坏二进制完整性尤其 FTP 如果配置成 ASCII 模式tar 包必坏。docker load 对 tar 结构严格校验任何截断都会报这个错。解决方式就是回头检查 sha256。我在第 2 章就强调过sha256 文件必须和 tar 包一起传输现场先跑sha256sum -c不一致就直接重传别在坏文件上浪费时间。另外gzip压缩过的包还可以用gzip -t测试完整性# 压缩包完整性质检 gzip -t zlm-master.tar.gz echo ok5.5 现象RTMP 推流正常但 RTSP 拉流一直黑屏一个现场项目里ffmpeg 推 RTMP 很顺利但用 RTSP 拉同一路流客户端始终黑屏或一直转圈。排查应用配置、鉴权、防火墙之后问题落在 UDP 端口映射上。RTSP 拉流走 UDP 传输时RTP 数据需要 ZLM 监听 UDP 端口。我启动容器时只映射了10000/udp漏掉了30000-30500/udp这个 UDP 端口段导致 RTP 包送不进来。解决方式就是在第 3 章的启动命令里补上端口段并确保防火墙放行# 检查防火墙是否有放行这两段端口 firewall-cmd --list-ports # 临时放行 UDP 端口段 firewall-cmd --add-port30000-30500/udp --permanent firewall-cmd --reload从那之后我每次做 ZLM 离线部署都会把10000/udp和30000-30500/udp写进部署文档再也不用现场临时查端口。6. 让离线安装变得可复现版本锁定、一键自检与部署留痕6.1 一键自检脚本把验证步骤固化到脚本里第 4 章的验证步骤虽然清晰但现场不可能每次都手敲。我通常会把检查写成一个 shell 脚本放在服务器上交付时直接执行。脚本内容不复杂但覆盖了进程、端口、API、流列表四个维度。#!/bin/bash # ZLM 离线部署自检脚本 echo 1. docker 服务状态 systemctl is-active docker echo 2. zlm 容器状态 docker ps --filter namezlm --format {{.Status}} echo 3. 关键端口监听 ss -tlnp | grep -E :(1935|554|8080)\b || echo 端口未监听 echo 4. HTTP API 可用性 curl -s http://127.0.0.1:8080/index/api/getServerConfig?secretmyPrivateSecret123 | head -c 300 echo 5. 当前在线流列表 curl -s http://127.0.0.1:8080/index/api/getMediaList?secretmyPrivateSecret123 | head -c 500这个脚本用法很简单bash check_zlm.sh每个步骤都输出明确结果。如果看到docker 服务未启动直接systemctl start docker如果看到容器状态为空先用docker logs zlm查原因如果端口未监听回到端口映射检查。脚本里的 secret 要和配置保持一致否则 API 那几步会返回鉴权错误这也是一种验证配置一致性的手段。6.2 部署留痕把版本号、架构、端口写进交付记录离线部署最大的痛点不是第一次装而是半年后现场出问题没人记得当时装的是哪个版本、改了哪些配置。我现在做任何内网交付都会顺手生成一份部署记录跟着镜像包一起留在服务器上。记录内容固定几项镜像 tag 和 digest、宿主机架构、容器端口映射、config.ini 的 secret、zlm 容器启动命令、自检脚本执行结果。也有同事嫌麻烦觉得现场能跑就行。我的回答是离线包不像在线包出了问题没法当场 docker pull 重新拉一个所有排障都依赖历史记录。有一次我在远程处理现场问题现场只有一位基础很弱的同事我让他执行记录里的自检脚本再照着文档逐项报结果半小时就把端口映射问题定位了。如果没有那页记录远程排查会非常痛苦。从那以后我每次做完离线安装都会强制自己走一遍这个流程写记录、跑自检、备份 config.ini。希望帮到你。本文还有配套的精品资源点击获取