docker-compose部署电视直播源:从离线安装到健康检查实战指南

docker-compose部署电视直播源:从离线安装到健康检查实战指南 很多折腾过电视直播源的朋友应该都有过这样的经历手里攒了一批 m3u 源想在局域网里给电视盒子、手机、电脑统一提供播放地址但要么零散地跑了好几个进程要么一台机器上手动敲 docker run 敲到眼晕。“docker-compose 启动 tv”这个项目说白了就是把一套电视直播源服务用 docker-compose 编排起来一次性启动、统一管理、重启自动拉起。我这边实际跑下来的环境是 Debian 服务器2G 内存常年开机配置好之后基本不用管。这个方案特别适合家里有一台常开小主机的朋友也适合刚接触容器编排、想找一个非“hello world”级别的实战项目练手的人。这套东西看起来简单但真正从零到一跑通里面有几个绕不开的环节离线或内网环境怎么装 docker-compose、compose 文件里哪些参数必须认真对待、容器起来之后怎么验证服务真的能用、以及遇到端口冲突和容器反复退出时怎么定位。下面我把整个实施过程拆开讲每个环节都会解释为什么这么做以及在什么场景下你会踩到坑。1. 项目整体设计与思路拆解1.1 先想明白你这是“启动一个容器”还是“启动一个服务”很多人第一反应是“docker run 一下不就行了”但实际用下来你会发现电视直播源这个场景很少是单容器能解决的。你需要一个 Web 服务给局域网设备提供 m3u 文件下载或在线播放可能需要一个定时任务去更新源列表可能还要加一个健康检查来保证服务可用。如果全部靠 docker run 一次次敲命令会越来越长改一个参数就要重新梳理整个命令而且机器重启之后这些容器能不能自己起来也是个问题。docker-compose 的价值就在这里把多个容器的启动顺序、网络、端口、挂载目录、环境变量、重启策略全部写进一个 YAML 文件像写配置文件一样管理服务。真正执行的时候一条命令就把整个服务栈拉起来不用记一堆参数。1.2 为什么在这个场景里 docker-compose 是刚需电视直播源服务有个特点它需要长时间稳定运行。你不想隔三差五去机器前面敲命令更不想因为一次断电重启所有容器都变成 Exited 状态。compose 文件里的 restart: always 就能解决这个问题Docker 守护进程启动时会自动把容器拉起来。另外端口映射这种容易出错的地方写进配置文件里一目了然比命令行容易维护得多。还有一个现实问题很多人的服务器是内网环境或者不想每次都在线拉镜像。docker-compose 配合离线安装的 Docker可以实现完全离线部署配置一次后续基本不依赖外网。这也是我后面专门写离线安装 docker-compose 的原因之一。1.3 整体流程梳理从零到一整个流程分四步第一步准备好 Docker 和 docker-compose 环境重点解决离线安装问题。第二步设计服务目录结构把电视源文件单独挂载出来和容器解耦。第三步编写 docker-compose.yml定义服务、端口、卷、重启策略和资源限制。第四步启动容器验证服务能访问让局域网设备可以拉流播放。后面所有内容都是围绕这个流程展开的。2. 先把基础工具备齐离线安装 docker-compose 的完整过程2.1 先确认你的服务器架构这是很多人第一步就忽略的。docker-compose 的二进制文件是区分 CPU 架构的x86_64 和 ARM64 不能混用。执行 uname -m 看一下输出$ uname -m x86_64如果是 x86_64对应 amd64如果是 aarch64对应 arm64。树莓派和部分国产小主机就是 arm64 的下载错架构装上去只会报 “Exec format error”。2.2 下载对应版本的 docker-compose 二进制离线安装最直接的方式是找一台能上网的机器从 Docker Compose 的 GitHub Releases 页面下载对应架构的二进制文件。文件名一般是 docker-compose-linux-x86_64 或 docker-compose-linux-aarch64。在有网机器上执行wget https://github.com/docker/compose/releases/download/v2.24.2/docker-compose-linux-x86_64建议优先下载 v2.x 的最新稳定版因为 v1 的 Python 实现已经停止维护很久了。下载好的文件不用解压它就是单个可执行文件。2.3 传输到目标服务器并安装把文件传到目标服务器方式不限scp、U 盘、内网共享都行。传上去之后执行安装chmod x docker-compose-linux-x86_64 sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose docker-compose --version如果看到类似 Docker Compose version v2.24.2 的输出就说明安装成功了。这里有一点要提醒/usr/local/bin 必须在系统的 PATH 环境变量里绝大多数 Linux 发行版默认都在如果你改过 PATH需要自己确认一下。2.4 另一种离线方案pip 轮子包安装如果你目标服务器上已经装了 Python 3而且实在找不到合适的二进制可以考虑 pip 离线安装。在有网机器上先下载所有依赖包pip download docker-compose -d ./compose-packages然后把整个 compose-packages 目录拷贝到目标服务器再执行pip install --no-index --find-links./compose-packages docker-compose这个方案的好处是跨版本兼容性比较稳定坏处是 docker-compose 的 Python 依赖非常多下载和安装过程都比较繁琐而且安装出来的版本通常停留在 v1.x。我的建议是能装二进制就优先用二进制pip 方案作为备选。2.5 离线安装 Docker 本身的补充说明docker-compose 再怎么说也只是个编排工具底层还是要依赖 Docker 引擎。离线安装 Docker 的常规思路是在有网机器上用 apt 把 deb 包全部下载下来或者直接下载 Docker 官方静态二进制包拷贝到目标机器解压后丢进 /usr/local/bin。如果只是普通内网环境也可以直接把 /var/lib/docker 和 /etc/docker 一块备份迁移但跨机器硬件架构不同会出现镜像不兼容所以最稳的还是先在目标架构机器上重新加载所有镜像。这一块展开能写很长但和标题核心相关的是 docker-compose所以我只提醒一句离线部署时Docker 镜像同样需要用 docker save 和 docker load 来处理后面第 5 节我专门讲。3. 编排文件设计与核心配置解析3.1 一份可以直接用的 docker-compose.yml我的实际配置文件大概长这样你可以直接抄version: 3.8 services: tv: image: nginx:alpine container_name: tv restart: always ports: - 8080:80 volumes: - ./m3u:/usr/share/nginx/html:ro - ./logs:/var/log/nginx environment: - TZAsia/Shanghai healthcheck: test: [CMD, wget, -q, --spider, http://localhost/playlist.m3u] interval: 30s timeout: 10s retries: 3先别急着启动我把每个配置项的用意讲清楚。3.2 镜像选择为什么用 nginx:alpine 而不是一个大而全的镜像电视直播源服务的核心需求是把 m3u 文件通过 HTTP 协议暴露给局域网设备。这个需求用 nginx 一个软件就能搞定没必要上一整套媒体服务器。nginx:alpine 的体积很小大概 20MB 出头内网传输和存储都轻松而且 Alpine 基础镜像的 CVE 暴露面比完整版小很多。如果你需要的是更复杂的服务比如把 HLS/m3u8 流做代理转换那 nginx 可能不够用可以换成专门的流媒体代理镜像。但作为第一版跑通流程nginx:alpine 是最不容易出意外的选择。3.3 端口映射只映射内网端口别盲目映射 0.0.0.0ports 配置 8080:80 意思是把宿主机的 8080 端口映射到容器的 80 端口。这里有几个细节容器内的 80 端口是 nginx 默认监听端口不用改。宿主机端口尽量选一个不常用的比如 8080、8899避免和本机其他服务冲突。如果这台服务器部署在公网或者有公网暴露风险建议把监听地址也限制住。比如只监听内网网卡192.168.1.100:8080:80。写成 0.0.0.0:8080:80 时所有网络接口都会暴露这个端口这不是什么安全漏洞但没必要把家里内网服务暴露到不该暴露的地方去。3.4 卷挂载把 m3u 目录独立出来这是整套方案最值得学习的一点我特意把 ./m3u 目录以只读方式挂载到容器的 /usr/share/nginx/html 下这里有两个考量。第一个考量是解耦。m3u 文件是经常要修改的今天加一个源明天删一个失效源。文件放在宿主机目录里你直接编辑文件容器不用重启nginx 会实时响应新文件因为静态文件是动态读取磁盘的。第二个考量是安全。挂载选项里的 :ro 表示只读容器里就算被入侵了也改不了宿主机上的原文件。这个习惯虽然在这个小项目里看不出多大差别但如果你把同一个目录挂载给多个容器只读挂载能避免容器之间互相踩踏。logs 目录挂载是让自己排查问题时省心。nginx 的访问日志会记录每个播放请求的来源 IP 和状态码源失效时排查很有用。3.5 环境变量和时区TZAsia/Shanghai 不是必须的但 nginx 默认容器时区是 UTC日志时间会和你本地时间差 8 个小时。播放出问题要查日志时时间对不上会很烦躁所以建议写上。3.6 restart 策略为什么是 always 而不是 unless-stoppedrestart: always 的含义是只要 Docker 守护进程还活着容器无论因为什么原因退出都会立即重启包括机器开机后自动拉起的场景。unless-stopped 的区别在于如果你手动 docker stop 过容器守护进程不会在下次开机时自动拉起它。对电视服务这种“希望它一直在跑”的场景always 更合适。唯一需要注意的是如果容器因为配置错误陷入 crash loop比如端口被占用它会一直重启刷日志这时候你要先查日志把根因解决掉而不是靠调 restart 策略去兜底。3.7 健康检查让“服务是不是真的可用”变成可观测的状态healthcheck 配置了我用 think up 一个 wget 请求访问 http://localhost/playlist.m3u。这个检查不是看容器进程有没有活着而是看应用层面能不能正常响应。加了这个之后docker-compose ps 输出里会显示容器的健康状态不健康了能第一时间发现。curl 在 nginx:alpine 镜像里默认是没有的所以健康检查用了 wgetnginx:alpine 自带 busybox 的 wget。这个细节经常坑到人你用 curl 做健康检查大概率会报 127 command not found。4. 启动流程与验证从配置文件到局域网可播放4.1 启动前的检查清单执行 docker-compose up -d 之前我习惯按这个清单过一遍当前目录下有没有 m3u 子目录目录里有没有 playlist.m3u 文件。nginx 挂在空目录上也能启动但健康检查会失败因为文件不存在。宿主机 8080 端口有没有被占用用 ss -tlnp | grep 8080 确认。docker 服务是否已经启动systemctl status docker 看一下。如果之前已经启动过旧版本容器先 docker-compose down 再把旧容器清掉。检查清单看起来啰嗦但能帮你避免大多数“启动失败”的低级错误。4.2 启动命令新旧两种写法的区别现在执行启动命令docker-compose up -d如果你装的是新版 Docker Engine 自带的 compose 插件也可以写成docker compose up -d注意上面的区别带横线的 docker-compose 是独立二进制命令不带横线的 docker compose 是 Docker 引擎的插件子命令。两种写法在绝大多数场景下行为一致但混用容易造成困惑。我建议固定用一种比如服务器上装了独立二进制就统一用 docker-compose别一会这个一会那个。启动之后查看状态docker-compose ps正常情况下 tv 服务的状态应该是 Up如果配置了 healthcheck状态栏会显示 (healthy) 或 (starting)。4.3 看日志确认服务真的正常容器能起来不代表服务一定正常我一直坚持看日志再下结论。docker-compose logs -f tv第一次启动时你应该能看到类似这样的日志/docker-entrypoint.sh: Configuration complete; ready for start up然后 Access Log 里会出现健康检查的请求记录每 30 秒一条 GET /playlist.m3u 返回 200。看到这个基本可以放心了。4.4 本机验证 HTTP 服务执行curl -I http://127.0.0.1:8080/playlist.m3u如果返回 HTTP/1.1 200 OK说明 nginx 已经在正常工作。这一步能快速区分问题出在容器内部还是网络层。4.5 让局域网内的播放器能够拉流找到服务器的内网 IP比如 192.168.1.100那么局域网内任意设备访问http://192.168.1.100:8080/playlist.m3u用 VLC 的话打开网络串流把这个地址贴进去能弹画面就成了。Kodi 的 PVR IPTV Simple Client 也支持直接填 m3u 地址这个地址在电视盒子上最常用。如果你的设备访问不了优先检查服务器防火墙。Debian/Ubuntu 上执行 sudo ufw status如果启用了 ufw需要放行 8080 端口sudo ufw allow 8080/tcp4.6 日常更新电视源的正确姿势更新源列表不需要动容器。直接编辑宿主机 ./m3u 目录下的 playlist.m3u 文件保存之后所有播放器刷新一下就能读到新内容。如果你想临时增加一个测试源文件直接丢进 m3u 目录访问对应文件名即可。如果是修改了 docker-compose.yml 里的配置比如端口号才需要执行 docker-compose up -d 让它重新创建容器。日常只更新 m3u 文件的情况下千万别动不动就重启容器没意义还会打断正在观看的流。5. 常见问题与排查技巧实录5.1 端口被占用导致容器起不来这是最常见的启动失败原因。现象是 docker-compose ps 里服务状态反复 Exited (0) 或 Exited (1)docker-compose logs 里能看到 bind() to 0.0.0.0:8080 failed (98: Address already in use)。排查方式ss -tlnp | grep 8080看是哪个进程占了端口。处理办法两种停掉那个进程或者改 compose 文件里的左边宿主机端口改成 8081、8899 这类不常用的。改完执行 docker-compose up -d 让它重建容器。5.2 电视源文件访问返回 404如果你把 m3u 文件命名为 tv.m3u却用 playlist.m3u 去访问那必然 404。看清楚文件名再访问。另外nginx 对目录访问和文件访问的行为不一样如果是子目录里的文件比如 ./m3u/channels/index.m3u那么 URL 就是 http://IP:8080/channels/index.m3u。挂载点结构要和目录结构对应起来这个最好在写挂载配置时就想清楚。5.3 容器一直重启状态显示 unhealthy 或 Exited (1)先别急着重启看日志才是正路docker-compose logs --tail100如果看到错误信息是 nginx 配置里有重复的 server block多半是你写到 logs 挂载目录里的东西被 nginx 当配置加载了。还有种情况是 m3u 目录是空的健康检查一直失败但因为 restart: always 的存在容器不会退出只是状态一直是 unhealthy。这是健康检查的正常表现不是故障补上文件就好。5.4 离线环境拉取镜像失败怎么处理目标机器完全离线的情况下docker-compose up -d 会卡在 Pulling nginx:alpine 上。这时候需要在一台有网机器上提前拉好镜像导出再到目标机器导入docker pull nginx:alpine docker save nginx:alpine -o nginx-alpine.tar然后把 tar 文件拷贝到目标机器docker load -i nginx-alpine.tar镜像导入之后再执行 docker-compose up -d就不会再去远程仓库拉取了。docker-compose 检测到本地已有镜像会直接使用。这个技巧在国产内网环境、离线机房环境特别实用。5.5 新版 docker compose 和旧版 docker-compose 命令混用导致找不到命令现在的 Docker Engine 往往自带 compose 插件可以直接输入 docker compose。但如果你按网上老教程装了独立二进制却在用 docker compose不带横线可能会遇到 “docker: compose is not a docker command” 的报错。解决办法就是统一命令。用 docker-compose --version 确认装的是独立版本后续全用 docker-compose如果想用 docker compose就把独立二进制删掉让系统使用 Docker 自带的插件。5.6 容器日志无限增长把磁盘撑爆nginx 的访问日志如果长时间不处理在长时间运行的服务器上确实能积累到几个 GB。配置 Docker 的日志轮转可以解决这个问题在 /etc/docker/daemon.json 里加{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }改完重启 Docker 服务。这个配置对后续所有容器生效每个容器的日志文件最多保留 3 个 10MB 的文件超出就自动清理。比起定时手动删日志这个方案省心得多。5.7 源失效导致播放卡顿这锅不能全让容器背很多朋友反馈“电视源卡顿”上来就想调容器参数其实大部分卡顿和容器本身没关系。m3u 源是外部的源服务器带宽不稳定、源失效、延迟高都会导致播放中断。排查思路是先用 VLC 单独打开源地址看是不是源本身的问题然后测一下内网到服务器的延迟ping 一下服务器 IP。如果你用的播放器支持缓冲调节适当调大缓冲时长对源抖动会有帮助。6. 后续还可以怎么扩展6.1 自动更新源列表手动维护 m3u 文件虽然简单但源多了会很烦。可以先用宿主机上的 cron 或 systemd timer 定时下载一个已知的 m3u 列表存到 ./m3u/ 里。因为文件是挂载给 nginx 的更新后播放器刷新即可生效不需要重启容器。6.2 备份 compose 目录整个服务的核心就是 docker-compose.yml 和 m3u 目录加起来可能不到 1MB。把这个目录打包备份到网盘、移动硬盘或另一台机器出问题的时候恢复成本几乎为零。docker-compose 的好处在这时候体现得很明显配置即代码换台机器也能无缝重建。6.3 资源限制如果你这台服务器还跑着其他服务建议给容器加上资源限制避免 TV 服务突发流量影响其他应用deploy: resources: limits: cpus: 0.5 memory: 256M注意 deploy.resources 这个字段在旧版 docker-compose 里直接支持如果你用的是新版 docker compose v2也支持。加上之后容器最多只能用半个 CPU 核和 256MB 内存对 nginx 服务来说绰绰有余。这套方案我实际跑了挺长时间感触最深的一点是真正稳定好用的东西不需要复杂配置越简单出故障的概率越低。很多人一上来就堆一堆媒体服务器、数据库、队列结果光排障就费了大半天。用 nginx docker-compose 把基础服务跑通后面再按需叠加功能这种扩张思路在个人项目里是最务实的。最后提醒一句不管你怎么改 compose 文件改之前先复制一份备份改完再 docker-compose config 校验一下语法能帮你省掉不少无谓的重启时间。