ZLMediaKit Docker离线安装完整指南:镜像搬运、配置与避坑

发布时间:2026/10/12 20:17:31
ZLMediaKit Docker离线安装完整指南:镜像搬运、配置与避坑
简介面向需要在离线环境中部署 ZLMediaKit 流媒体服务的运维人员与开发者这份资源提供了 Docker 离线安装所需的完整材料解决内网或无外网环境下无法拉取镜像、安装依赖繁琐的问题。压缩包整体 208.73MB共 2 个文件一个 tar 格式镜像包用于直接导入 Docker 镜像一个 sh 脚本用于执行离线部署与基础配置两者配合可快速完成 zlm 的 Docker 化落地。tar 镜像是核心载体sh 脚本则承担安装入口角色用户可先解压再导入减少在无网服务器上的手工配置成本。部署时无需访问公网镜像仓库也无需手动逐条安装依赖适合生产环境快速交付。资源文件结构清晰适合有一定 Docker 基础但缺少离线安装经验的读者参考也可作为内网环境重复部署时的可复用模板。目前已有 376 人学习对需要快速交付 zlm 容器服务的场景有直接帮助。1. zlm docker 离线安装为什么离线环境才是常态很多流媒体服务并不是部署在随时能上外网的开发机上而是落在医院、园区、工厂甚至应急场景的内网里。此时你想用 docker 快速拉起 ZLMediaKit下文简称 zlm就会发现最常规的docker pull走不通整个安装过程从“拉镜像”变成了“搬镜像”。zlm 是高性能的流媒体服务器支持 RTSP、RTMP、HTTP-FLV、WebRTC 等协议在安防监控、直播推流、智能硬件场景经常能看到它。离线安装的核心不是 docker run 那行命令而是镜像怎么搬、配置怎么调、端口怎么映射、权限怎么给。我第一次在做某公司的内网监控项目时卡在最简单的镜像导入上后面才发现坑都在细节里。这篇笔记会把一条完整的离线部署路径捋清楚从联网机器上导出镜像在内网导入再到配置调整和排错每步都给出命令和参数。适合需要在内网或隔离网络里部署 zlm 的人也适合刚接触 docker 想少踩坑的运维和开发。2. 离线安装前的准备镜像搬运与依赖梳理离线安装 zlm 的思路并不复杂在一台能联网的机器上把镜像拉下来打包成 tar 文件通过 U 盘或内网共享目录拷到离线机器再docker load进去。麻烦的是镜像依赖和后续的版本兼容问题。2.1 在联网机器上拉取 ZLMediaKit 镜像第一步是找一台能访问镜像仓库的机器注意这台机器的架构要和离线机器一致一般是 amd64偶尔有 arm64。我一般先确认目标机器架构避免后面 load 了跑不起来。# 查看本机架构这里以常见情况为例 uname -m # 拉取 zlm 官方镜像latest 或者指定一个版本号 docker pull zlmediakit/zlmediakit:latest # 拉完确认镜像存在 docker images | grep zlmediakituname -m输出 x86_64 或 aarch64离线机器也要先跑一遍保证架构一致。docker run时如果架构不对会提示 exec format error后面会专门说。拉取镜像时建议指定明确的 tag而不是每次都用 latest因为离线包一旦拷进去想再更新版本就得重新走一遍流程tag 固定下来方便记录。如果你在联网机器上拉镜像时心里没底可以先用docker inspect看一下镜像的基础信息包括创建时间、暴露的端口和挂载的卷。这样在离线机器上规划端口和目录挂载时就不用一边看日志一边猜路径。# 查看镜像的配置信息重点关注 ExposedPorts 和 Volumes docker inspect zlmediakit/zlmediakit:latestdocker inspect输出很长可以用--format只过滤端口和挂载点比如docker inspect --format{{json .Config.ExposedPorts}} zlmediakit/zlmediakit:latest。这些信息能帮你提前写出正确的 docker run 命令而不是到了现场才慢慢试。镜像里如果依赖了外部动态库在导出前也最好在容器里跑一次ldd看看省得离线机器报找不到库文件。2.2 导出镜像为 tar 包并压缩镜像拉下来后用docker save导出。tar 包一般几百 MB建议压一下再拷走。我通常还会生成一个 SHA256 校验值跟 tar 包一起拷过去防止拷贝过程中文件损坏。# 导出镜像为 tar 文件 docker save -o zlm-latest.tar zlmediakit/zlmediakit:latest # 查看大小和校验值 ls -lh zlm-latest.tar sha256sum zlm-latest.tar zlm-latest.tar.sha256 # 压缩减少拷贝时间 gzip -9 zlm-latest.tar # 完成后得到 zlm-latest.tar.gz ls -lh zlm-latest.tar.gzdocker save会把镜像的所有层和一个 manifest 打包进去-o指定输出文件名。sha256sum生成校验文件离线机器上导入前可以用sha256sum -c校验哪怕只是多走这一步也能挡掉很多玄学问题。压缩这一步不是必须的但如果离线机器在内网且 U 盘拷贝慢gzip -9能省不少时间。有一个细节压缩后的文件在离线机器上用docker load前需要解压虽然docker load可以直接读 gz 文件但保险起见我一般先解压再 load避免因为 docker 版本太老不支持压缩输入。另外如果你在联网机器上同时拉了同一个镜像的多个版本导出时建议分开导或者明确指定 tag。docker save一次只导一个镜像的某个 tag别用docker save -o all.tar $(docker images -q)这种命令导出来的镜像名字会变成none后面处理起来很麻烦。2.3 离线机器导入与基础校验把 tar 文件拷到离线机器后先校验再导入。校验这一步尤其重要因为 U 盘拷贝或者内网传输偶尔会出现文件损坏直接 load 会报莫名其妙的错误。# 解压如果前面压缩过 gunzip -d zlm-latest.tar.gz # 校验文件完整性正确会输出 OK sha256sum -c zlm-latest.tar.sha256 # 导入 docker load -i zlm-latest.tar # 确认导入后的镜像信息 docker imagesdocker load会把镜像层和 tag 写入本地镜像库。导入后docker images里应该能看到zlmediakit/zlmediakit:latest。这里我最常踩的坑是导入后 tag 信息丢失镜像显示为none:none。因为docker save保存 tag 时依赖 repository 名如果源镜像本身 repository 名不规范或 load 的 docker 版本较旧就会丢 tag。解决方法很简单用docker tag image-id zlmediakit/zlmediakit:latest补一下。导入后做一次基础校验拉起一个临时容器看版本确保镜像在目标机器上能跑# 临时容器运行并打印版本信息跑完即删 docker run --rm --entrypoint /bin/sh zlmediakit/zlmediakit:latest -c ./MediaServer --version || cat /opt/media/version.txt--rm是临时容器跑完自动删除不污染环境。这条命令主要确认三件事镜像能加载、基础库不缺、二进制能执行。如果这条命令报错比如exec format error说明架构不对如果提示找不到动态库说明镜像与宿主系统兼容性有问题。正常出参应该是版本号和构建时间。到这里镜像搬运就算完成了下面准备真正跑服务。3. 用 docker run 快速拉起 ZLMediaKit最小命令与参数说明镜像导入只是第一步真正让 zlm 跑起来还要关心端口、网络和目录。很多人以为 docker run 一行就完事结果拉流拉不出来排查半天发现是端口映射少了。3.1 最小启动命令与端口映射如果离线机器网络环境简单最省事的方式是直接用默认配置启动只映射几个核心端口docker run -d \ --name zlm \ --restartalways \ -p 1935:1935 \ -p 554:554 \ -p 8080:80 \ -p 10000:10000 \ -p 8000:8000 \ zlmediakit/zlmediakit:latest-d后台运行--restartalways让容器在宿主机重启后自动拉起离线环境下没人天天盯着这个参数建议养成习惯。端口映射说明1935 是 RTMP 接入端口554 是 RTSP 端口80 是 HTTP 端口用于 HTTP-FLV、API8000 是 WebRTC 端口10000 是 RTP 端口。如果你只用部分协议可以在配置文件里关掉不需要的但端口宁可多映射也不能少否则后续排查很痛苦。一个很实际的问题zlm 还会用到一些 RTP 动态端口范围bridge 网络下如果不把这些端口映射出来外部推流可能失败。所以有的人干脆用 host 网络省去映射的麻烦。最小启动命令适合临时验证真要长期用最好先看下一小节的网络选型。启动后建议用docker port zlm看一下端口映射是否生效再配合docker logs zlm确认没有任何bind: address already in use之类的错误。如果发现端口冲突要么换宿主机端口映射要么直接改 zlm 监听端口后者在配置章节会讲。3.2 host 与 bridge 网络模式怎么选我见过很多部署 zlm 的人纠结网络模式。bridge 模式是 docker 默认容器有自己的虚拟 IP端口用-p映射好处是隔离坏处是端口映射漏一个就失败一个。host 模式是容器直接共享宿主机网络不需要映射端口所有监听端口直接暴露在宿主机上适合端口很多、动态端口很杂的场景。# host 模式启动不使用 -p 参数 docker run -d \ --name zlm \ --network host \ --restartalways \ zlmediakit/zlmediakit:latesthost 模式下你不需要担心 RTP 大范围端口映射因为容器内绑定 0.0.0.0 时实际就是绑在宿主机网卡上。代价是端口冲突风险高宿主机上如果有其他服务占用了 554 或 8080zlm 会起不来。另外host 模式下容器内看到的 IP 就是宿主机 IPzlm 在回调、上报时能拿到正确的内网 IP省去很多网络地址转换的烦恼。我的建议是离线环境如果是专机专用直接 host 模式如果宿主机上还跑着其他 docker 服务建议 bridge 模式 端口映射并且在配置里把动态端口范围缩小。缩小动态端口范围的方法在配置章节讲。特别注意如果你用了防火墙host 模式需要把相关端口放行bridge 模式则要关注 docker 的 nat 链是否被清理这个问题会在避坑章节提到。两种模式没有绝对好坏。bridge 模式适合宿主机上服务多、端口规划清晰的场景host 模式适合可预见的端口范围很大、且不想维护映射表的场景。建议以离线环境后续会不会新增服务来判断别盲目跟风。3.3 配置文件与日志目录挂载默认容器里写配置是不持久的容器删了配置就没了。离线环境最怕重装所以务必要把配置目录挂载到宿主机。# 先创建一个临时容器把默认配置拷出来 docker run --rm --name zlm-tmp zlmediakit/zlmediakit:latest true docker cp zlm-tmp:/opt/media/conf/. ./zlm-conf/ docker cp zlm-tmp:/opt/media/log/. ./zlm-log/ # 正式启动时挂载配置和日志目录 docker run -d \ --name zlm \ --network host \ --restartalways \ -v $PWD/zlm-conf:/opt/media/conf \ -v $PWD/zlm-log:/opt/media/log \ zlmediakit/zlmediakit:latestdocker run --rm ... true是让容器短暂创建出来但不启动服务这样可以用docker cp把容器内默认配置拿出来。-v $PWD/zlm-conf:/opt/media/conf把宿主机当前目录下的配置文件夹挂到容器内的配置路径。注意路径/opt/media/conf是 zlm 镜像内约定的配置路径如果你不确定先docker exec进容器看一眼目录结构再挂载。挂载目录后修改宿主机上的配置文件可以即时生效zlm 支持部分配置热重载日志也会写到宿主机目录方便用grep排查。这一步在离线环境非常重要因为内网没有日志采集工具所有问题几乎都要靠日志定位。如果挂载后容器启动失败大概率是目录权限问题后面避坑章专门有一条。挂载以后我还建议顺手做一次快照备份cp -r zlm-conf zlm-conf.bak。配置改坏了能马上还原这就是后悔药的用处。离线环境没有在线文档备份就是最便宜的排错手段。4. 离线环境下的配置文件调整从默认到可用默认配置能启动但不一定能满足内网使用场景。离线环境下最常见的是端口抢占、鉴权缺失、回调地址配错。这一章讲清楚配置文件怎么改。4.1 认识 config.ini 的关键配置段zlm 的配置集中在conf/config.ini里常见配置段包括[api]、[rtsp]、[rtmp]、[http]、[rtc]、[hook]。离线部署时我一般重点看三块[api]里的secretAPI 请求的鉴权密钥默认很短建议改掉。[rtsp]/[rtmp]/[http]里的port决定了监听端口默认分别是 554、1935、80。[hook]里的回调地址如果是内网对接业务系统需要指向业务服务器 IP。先看一下当前配置# 在配置挂载目录下操作 cd ./zlm-conf grep -E ^(\[api\]|secret|port) config.ini | head -30grep只显示段名和关键字段。注意config.ini里的注释是分号开头的修改前先备份cp config.ini config.ini.bak。离线环境没有网络查文档备份就是后悔药改错了还能还原。如果宿主机上端口被其他服务占用可以用ss -lntp提前确认。比如ss -lntp | grep -E :554|:1935|:80看哪些端口已经被占用然后决定改配置里的哪个监听端口。zlm 的日志也会在启动时打印实际监听端口注意看docker logs zlm的输出它会明确写HTTP server listen on 0.0.0.0:80这样的信息。4.2 修改 HTTP/RTSP/RTMP 监听端口如果宿主机上 80、554、1935 这几个端口被占用就要改配置。用sed直接替换或手动编辑。我的习惯是用sed因为可以写到部署脚本里下次离线部署直接跑脚本。# 将 HTTP 端口从 80 改为 8080 sed -i s/^port80/port8080/ config.ini # 将 RTSP 端口从 554 改为 8554 sed -i s/^port554/port8554/ config.ini # 将 RTMP 端口从 1935 改为 1936 sed -i s/^port1935/port1936/ config.ini # 检查修改结果 grep -n ^port config.inised -i是原地替换先确认原文件里这些port是否唯一。zlm 配置里[http]段可能有多个 port 字段比如 HTTP 和 HTTPS注意区分。我吃过一个亏用sed s/port80/port8080/把 HTTPS 端口也误改了导致 SSL 功能起不来。后来我改成更精确的匹配比如sed -i /\[http\]/,/^\[/ s/^port80$/port8080/只在[http]段内替换。这条技巧建议学一下能省很多事。修改端口后如果之前用-p映射启动命令里的外部端口也要同步改如果用 host 模式只要改配置就行。端口改完需要重启容器生效docker restart zlm。zlm 部分配置支持热加载但端口这种监听配置必须重启。重启后立刻看两样东西docker logs zlm有没有报端口占用以及ss -lntp确认新端口在监听。把这两步养成习惯就不会出现改完配置发现服务还是旧端口的情况。4.3 开启 API 鉴权与推拉流密钥离线内网不代表安全很多内网设备默认口令泛滥。zlm 的 API 接口如果不设鉴权别人在局域网里直接调接口就能拉流、踢流。建议在[api]段设置一个足够长的 secret# 生成随机密钥 openssl rand -hex 16 # 修改 api 段 secret假设生成的是 abc123...实际请替换 sed -i /\[api\]/,/^\[/ s/^secret.*/secretabc123.../ config.iniopenssl rand -hex 16生成 32 位十六进制串复制到配置里。zlm 的 API 调用需要带secret参数或放在 Header 里具体方式可以看接口文档。这点离线前就要确认因为内网没法临时查文档建议把常用的接口说明打印出来带进去。除了 API 鉴权推拉流密钥也很重要。zlm 支持在 URL 里加签名参数常见是?signxxx。但离线环境业务系统可能不支持动态签名退而求其次是在[rtsp]、[rtmp]段开启enable_auth1然后通过用户名密码方式控制。注意用户名密码配置一般不在 config.ini 里而是在业务层管理。如果你只是简单内网使用至少把 API secret 改掉同时用防火墙限制访问端口避免内网别人乱扫。改完配置后用一条 curl 命令验证 API 鉴权是否生效# 不带 secret应该返回 401 或错误码 curl http://127.0.0.1:8080/index/api/getServerInfo # 带上 secret应该返回服务器信息 curl http://127.0.0.1:8080/index/api/getServerInfo?secretabc123...getServerInfo是一个只看不改的 API适合用来验证鉴权。如果第一条命令返回了正常数据说明 secret 没生效多半是修改段位不对或容器没重启。第二条命令能返回 JSON就说明 API 鉴权已经生效。5. zlm docker 离线安装避坑网络、权限与镜像兼容这一章都是实际部署时会碰到的硬骨头。每一条我都按现象、原因、解决写清楚建议按照顺序看一遍。5.1 坑一导入后镜像仓库名丢失现象docker load -i zlm.tar提示成功但docker images里看到的镜像名是none:nonedocker run 时根本不知道用什么名字启动。原因docker save导出的 tar 里包含的 repository 标签和 load 端 docker 版本不兼容常见于在联网机器上用新版 docker 导出离线机器用老版本 docker 导入。也有情况是源镜像 tag 是 floating tag比如latest在部分版本下会丢标签。解决用镜像 ID 补 tag。先docker images找到那个none镜像的 IMAGE ID然后docker tag IMAGE_ID zlmediakit/zlmediakit:latest docker images | grep zlmediakitdocker tag不会改变镜像内容只是加一个引用。如果担心这条命令记不住可以在离线机器上把镜像 ID 写进部署文档。另一个预防做法是导出时用docker save -o zlm.tar zlmediakit/zlmediakit:1.0.0这种明确版本别用 latestlatest 在部分 docker 版本里就是容易丢标签。5.2 坑二容器时间与宿主机不同步导致鉴权失败现象zlm 的 API 回调里带的时间戳和业务服务器对不上出现过期拒绝或者拉流 URL 里的过期时间比如expire...总是立刻失效。原因容器默认使用 UTC 时间而宿主机是中国时区两者相差 8 小时。zlm 在做签名校验时按本地时间算如果容器时间是 UTC本地业务系统签发的时间戳自然对不上。解决挂载/etc/localtime或设置时区环境变量。我用的是挂载方式docker run -d \ --name zlm \ -v /etc/localtime:/etc/localtime:ro \ -e TZAsia/Shanghai \ zlmediakit/zlmediakit:latest-e TZAsia/Shanghai设置时区环境变量加上-v /etc/localtime:/etc/localtime:ro同步宿主机时区文件。注意宿主机必须是中国的时区配置否则挂载了也白搭。启动后进容器执行date确认时间正确。这个坑非常隐蔽因为服务能启动、推拉流也正常就是鉴权时而成功时而失败排查起来很费时间。5.3 坑三挂载目录权限导致服务反复重启现象启动容器后几秒钟就退出docker logs zlm里出现Permission denied或open log file failed。原因宿主机上创建的zlm-conf、zlm-log目录默认属于 root 用户而容器内进程用户是非 root 的部分镜像用普通用户运行服务导致容器内写日志、写配置等操作没有权限。解决把挂载目录的属主改成容器内用户。先进入容器确认用户 UID然后用宿主机chown# 查看容器内进程用户假设是 uid1000 docker exec -it zlm id # 在宿主机上把挂载目录属主改成 1000示例以实际 uid 为准 chown -R 1000:1000 ./zlm-conf ./zlm-log docker restart zlm如果嫌麻烦可以用chmod -R 777临时解决但不推荐离线环境的服务器一旦被攻破写权限就随便放开。更干净的做法是挂载时加:z或:ZSELinux 环境但内网多数是普通 CentOSchown就够。还有一个注意点如果容器内用户随不同镜像版本变化先id确认再 chown别凭经验。5.4 坑四老版本 docker 导入新镜像报错现象docker load提示invalid reference format或failed to parse manifest导入失败。原因镜像使用的 OCI manifest 格式较新离线机器上 docker 版本太老通常低于 18.06不兼容。或者镜像用了多平台特性但 load 时没有选择平台。解决优先将离线机器的 docker 升级到较新版本但如果离线环境不允许只能在联网导出时指定兼容格式。具体做法是联网机器上使用docker buildx导出特定平台的镜像或者换用旧一点的 zlm 镜像版本。我的经验是离线部署前先在离线机器上docker version查好版本再在联网机器上拉取对应兼容镜像。如果已经报错了别硬猜用tar -tf查看包里的 manifest.json看mediaType是 docker 还是 OCI 格式再去决定换镜像还是升级 docker。5.5 坑五内网无 DNS 导致推流回调失败现象zlm 容器启动了推流也提示成功但业务系统收不到on_publish回调或者回调延迟严重。查看日志发现getaddrinfo错误。原因离线内网没有 DNS 解析服务zlm 配置里hook回调地址如果填的是域名比如http://zc.example.com:8080/index容器解析不了。解决把所有回调地址改成 IP 直连或给容器指定 DNS。我一般直接填 IP[hook] enable1 on_publishhttp://192.168.1.100:8080/index如果必须要用域名启动容器时加--dns 192.168.1.1指内部 DNS。另外容器默认的/etc/resolv.conf可能继承宿主机宿主机本身也是内网 IP 的话要确认这个 IP 可达。这个坑在离线环境出现频率很高因为大家潜意识里觉得内网不需要 DNS但实际上hook回调很多是http://形式一解析就挂。6. 进阶内网私有镜像仓库与拉流验证一次搞定如果离线集群有多台机器每台都靠 U 盘拷贝 tar 包太原始了。更高效的方式是在内网搭一个私有镜像仓库把 tar 包推上去其余机器直接docker pull。同时部署完 zlm 后一定要在离线机器本地验证拉流别等业务系统接不上才发现协议端口有问题。6.1 用 registry 镜像在内网搭一个私有仓库离线机器上先导入 registry 镜像打包过程同 zlm然后启动一个本地仓库# 在离线机器上运行私有仓库监听 5000 端口 docker run -d \ --name registry \ --restartalways \ -p 5000:5000 \ -v $PWD/registry-data:/var/lib/registry \ registry:2-v $PWD/registry-data:/var/lib/registry把仓库存储目录挂到宿主机镜像数据不随容器丢失。启动后其他内网机器拉取时需要把镜像 tag 改为内网IP:5000/zlmediakit:latest然后 pull。注意如果内网机器用 HTTP 访问仓库需要在每台机器的 docker daemon 配置里添加insecure-registries否则 docker 会拒绝普通 HTTP 仓库。6.2 用 ffprobe 验证 RTSP/RTMP 拉流是否正常部署完 zlm 后建议从宿主机上用 ffprobe 直接拉流。没有推流源时可以用 zlm 自带的循环播放功能如果镜像里有媒体文件或先用 ffmpeg 推一个测试流。这里给一条从验证推流到拉流的命令组合# 用 ffmpeg 推一个本地视频文件到 zlm假设 rtmp 端口是 1935 ffmpeg -re -i /path/to/test.mp4 -c copy -f flv rtmp://127.0.0.1:1935/live/test # 然后用 ffprobe 验证 RTMP 拉流 ffprobe -v error -show_entries streamcodec_type -show_entries formatduration -of defaultnoprint_wrappers1 rtmp://127.0.0.1:1935/live/test # 验证 HTTP-FLV ffprobe -v error -show_entries streamcodec_type -of defaultnoprint_wrappers1 http://127.0.0.1:8080/live/test.flvffmpeg -re按视频真实速率推流避免推流速度过快导致卡顿。ffprobe带上-show_entries只输出关键信息返回里能看到stream_typevideo和时长说明拉流正常。如果 ffprobe 卡住无返回大概率是端口没映射或防火墙拦截先用telnet 127.0.0.1 1935测端口通不通。最后说个习惯离线部署我做完一定会docker restart zlm一次然后立刻看docker logs zlm确认没有报错。这种“重启后一次通过”的验证能避免机器断电重启后服务起不来的尴尬。时间、权限、端口这三类问题几乎占了 zlm 离线安装的八成故障按我上面的清单来排查基本都能解决。希望帮到你。本文还有配套的精品资源点击获取