SpeedVideo Docker Compose 部署指南
以 Apache HTTP Server + FFmpeg 制备固定码率片源,用 Docker Compose 从零部署 SpeedVideo 等效视频测试源:片源制备、站点配置、启动验证、限速弱网模拟与常见问题排障。
SpeedVideo Docker Compose 部署指南
目标:在 Linux 服务器上用 Docker Compose 从零部署一套 SpeedVideo 视频测试源服务器——以 Apache HTTP Server 分发固定码率的 MP4 渐进下载片源与 HLS 切片片源,向 SpeedVideo 客户端、播放器或 curl/ffprobe 等测试工具提供标准化测试流。全文步骤(片源制备 → 站点配置 → 编排启动 → 拉流验证)已在 Ubuntu 24.04 服务器上实测通过。
一、环境信息
- 操作系统:Ubuntu 24.04.5 LTS(x86_64)
- 容器运行时:Docker(已配置镜像加速代理);编排:Docker Compose v2
- 基础镜像:
httpd:2.4(Apache HTTP Server 2.4.69,镜像体积 177MB,digestsha256:a30cd4cd57f3…) - 片源工具:FFmpeg 7.0.2(static 构建,含 libx264/aac/hls muxer)
- 服务端口:HTTP
8080、HTTPS8443(均映射到宿主机同号端口) - 部署目录:
/opt/speedvideo/(compose 项目、站点片源、服务配置均在其下)
💡 说明:华为 mLAB 官方 SpeedVideo 视频测试服务器套件为 Windows Server 2012 申请制分发(见本专栏 《SpeedVideo 介绍与选型指南》),本文给出的是同角色的 Linux 自建实现:片源为文件、客户端按 HTTP 渐进下载(HPD)/HLS 拉流,与官方方案「文件式片源、无推流端」的形态一致。
二、镜像选型说明
| 候选镜像 | 说明 | 结论 |
|---|---|---|
httpd:2.4(官方) | Apache HTTP Server 2.4.69,自带 mod_ratelimit/mod_ssl/mod_headers,静态文件分发性能成熟 | 采用:测试源的本质是「高性能静态文件 HTTP 分发」,Apache 官方镜像开箱即用 |
nginx:stable | 同样胜任静态分发 | 备选;Apache 的 mod_ratelimit 限速配置更直观,本文选 Apache |
| 直播媒体服务器镜像(SRS/MediaMTX 等) | 面向推流直播 | 不适用:测试源片源是文件,不需要推流链路 |
版本核验回显(实测):
$ docker images --digests httpd:2.4
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
httpd 2.4 sha256:a30cd4cd57f3f3c3be9b2d16384d2488839996bb14c67df2b51f1ecbb3e584a1 a30cd4cd57f3 4 days ago 177MB
三、前置条件
- 服务器已安装 Docker 与 Compose 插件,
docker compose version可用; - 8080/8443 端口空闲:
$ ss -ltn | grep -E '8080|8443'
(无输出,表示两个端口均未被占用)
- 准备 FFmpeg 用于制备片源(任选其一):
- 发行版仓库:
apt install -y ffmpeg; - 或官方 static 构建:从
https://johnvansickle.com/ffmpeg/下载ffmpeg-release-amd64-static.tar.xz解压使用。本文实测使用 FFmpeg 7.0.2 static:
- 发行版仓库:
$ ./ffmpeg -version | head -1
ffmpeg version 7.0.2-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2024 the FFmpeg developers
⚠️ 网络说明:FFmpeg static 构建托管在国外站点,若直连缓慢可走代理
curl -x http://<代理地址>:<端口> -LO <URL>;本文写作时官方站直连下载成功。
四、部署步骤
4.1 规划目录结构
mkdir -p /opt/speedvideo/{site/clips,site/hls,conf/extra/tls,deploy}
最终结构如下(本站实测产物):
/opt/speedvideo/
├── deploy/docker-compose.yml # 编排文件
├── conf/httpd.conf # Apache 主配置
├── conf/extra/speedvideo.conf # 测试源站点配置(虚拟主机/MIME/缓存/限速/鉴权/TLS)
├── conf/extra/tls/sv.crt|sv.key# 自签名证书
└── site/ # 站点根:片源与索引页
├── index.html # 片源目录页(含下行测速小组件)
├── healthz.txt # 健康检查端点(内容 "ok")
├── clips/sv-*.mp4 # 5 档固定码率 MP4 片源
├── hls/sv-1080p/ # HLS 片源(index.m3u8 + 10 个 2 秒分片)
└── secure/auth-check.txt # 鉴权验证端点
4.2 制备固定码率 MP4 片源
测试片源的要求是码率恒定、参数已知——只有片源码率固定,测出的下行速率才能反推网络质量。用 FFmpeg 的 testsrc2 测试图 + 正弦音源生成,x264 用 -b:v/-maxrate/-bufsize 锁定码率,-movflags +faststart 把 moov 元数据前置(Web 播放器可边下边播):
$ ./ffmpeg -hide_banner -y -f lavfi -i testsrc2=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=1000:sample_rate=48000 \
-t 20 -c:v libx264 -preset veryfast -b:v 1500k -maxrate 1500k -bufsize 3000k \
-pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart \
/opt/speedvideo/site/clips/sv-720p-1500k.mp4
(输出节选,实测末尾)
[libx264 @ 0x1b3b92c0] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 @ 0x1b3b92c0] kb/s:1531.05
[aac @ 0x1b3bfe40] Qavg: 1279.058
实测编码器统计 kb/s:1531.05,落在 1500k 目标码率 ±3% 以内。按同一命令模板调整 size 与 -b:v/-maxrate,共生成 5 档片源(20 秒、30fps、H.264 + AAC 48kHz):
| 文件 | 分辨率 | 目标码率(视频) | 实测文件大小 | 实测视频码率 |
|---|---|---|---|---|
| sv-720p-1500k.mp4 | 1280×720 | 1500 kbps | 4.2 MB | 1543 kbps |
| sv-720p-2500k.mp4 | 1280×720 | 2500 kbps | 6.8 MB | 2594 kbps |
| sv-1080p-3000k.mp4 | 1920×1080 | 3000 kbps | 8.1 MB | 3088 kbps |
| sv-1080p-5000k.mp4 | 1920×1080 | 5000 kbps | 13.2 MB | 5147 kbps |
| sv-2160p-12000k.mp4 | 3840×2160 | 12000 kbps | 31.1 MB | 12312 kbps |
生成后逐档用 ffprobe 复核(实测回显节选,1080p/5Mbps 档):
$ ./ffprobe -v error -show_entries format=duration,size,bit_rate \
-show_entries stream=codec_name,codec_type,width,height,bit_rate \
-of default=noprint_wrappers=1 /opt/speedvideo/site/clips/sv-1080p-5000k.mp4
codec_name=h264
width=1920
height=1080
bit_rate=5147490
codec_name=aac
bit_rate=127989
duration=20.000000
size=13212039
💡 技术拓展:FFmpeg 内置测试图源不止
testsrc2。常见选项——
源 画面 适用 testsrc经典彩条+滚动时钟 通用兼容性测试 testsrc2多图块轮换(含文字/运动/渐变) 码率恒定、画质评估 smptehdbarsSMPTE HD 彩条 广电/显示器对齐 本文采用
testsrc2:画面元素丰富且各帧复杂度稳定,配合-maxrate/-bufsize能得到恒定码率。
4.3 制备 HLS 切片片源
HLS 是 Safari/iOS 原生支持的切片协议。直接从已生成的 1080p/3Mbps MP4 无损转封装(-c copy 不重编码),切成 2 秒一个分片:
$ mkdir -p /opt/speedvideo/site/hls/sv-1080p
$ ./ffmpeg -hide_banner -y -i /opt/speedvideo/site/clips/sv-1080p-3000k.mp4 \
-c copy -hls_time 2 -hls_playlist_type vod \
-hls_segment_filename /opt/speedvideo/site/hls/sv-1080p/seg_%03d.ts \
/opt/speedvideo/site/hls/sv-1080p/index.m3u8
(输出节选,实测末尾)
[hls @ 0x1c569400] Opening '.../seg_008.ts' for writing
[hls @ 0x1c569400] Opening '.../seg_009.ts' for writing
size=N/A time=00:00:19.93 bitrate=N/A speed=1.3e+03x
验证播放清单内容(实测):
$ head -8 /opt/speedvideo/site/hls/sv-1080p/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:2.000000,
seg_000.ts
#EXTINF:2.000000,
#EXT-X-PLAYLIST-TYPE:VOD 表示这是点播清单,客户端可一次性拉全部分片。
4.4 编写 Apache 站点配置
主配置 conf/httpd.conf 在官方镜像默认值基础上做 4 处改动(完整差异):启用 mod_socache_shmcb、mod_ratelimit、mod_ssl 三个模块;Listen 80 改为 Listen 8080 + Listen 8443;全局 ServerName speedvideo.local;文件末尾追加站点配置引入与 KeepAlive 调优:
ServerName speedvideo.local
Listen 8080
Listen 8443
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ratelimit_module modules/mod_ratelimit.so
LoadModule ssl_module modules/mod_ssl.so
# SpeedVideo 测试源站点配置
IncludeOptional conf/extra/speedvideo.conf
KeepAlive On
KeepAliveTimeout 5
MaxKeepAliveRequests 100
站点配置 conf/extra/speedvideo.conf 全文(关键项逐项解说见七节):
<VirtualHost *:8080>
ServerName speedvideo.local
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
AddType application/vnd.apple.mpegurl .m3u8
AddType video/mp2t .ts
AddType video/mp4 .mp4
Header set Access-Control-Allow-Origin "*"
<FilesMatch "\.m3u8$">
Header set Cache-Control "max-age=1, must-revalidate"
</FilesMatch>
<FilesMatch "\.ts$">
Header set Cache-Control "max-age=10"
</FilesMatch>
<FilesMatch "\.mp4$">
Header set Cache-Control "max-age=60"
</FilesMatch>
<Location "/clips/sv-2160p-12000k.mp4">
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 2000
</Location>
<Location "/secure/">
AuthType Basic
AuthName "SpeedVideo Test Source"
AuthUserFile "/usr/local/apache2/conf/extra/.htpasswd"
Require valid-user
</Location>
<Location "/status">
SetHandler server-status
Require ip 127.0.0.1
</Location>
ErrorLog "/usr/local/apache2/logs/sv_error.log"
CustomLog "/usr/local/apache2/logs/sv_access.log" "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
</VirtualHost>
<VirtualHost *:8443>
ServerName speedvideo.local
DocumentRoot "/usr/local/apache2/htdocs"
SSLEngine on
SSLCertificateFile "/usr/local/apache2/conf/extra/tls/sv.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/extra/tls/sv.key"
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
<Directory "/usr/local/apache2/htdocs">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
AddType application/vnd.apple.mpegurl .m3u8
AddType video/mp2t .ts
Header set Access-Control-Allow-Origin "*"
Header always set Strict-Transport-Security "max-age=31536000"
</VirtualHost>
生成自签名证书与鉴权账号文件(演示账号 root,密码 q1w2e3r4):
$ openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-subj "/C=CN/ST=Shanghai/L=Shanghai/O=SpeedVideo Lab/CN=speedvideo.local" \
-addext "subjectAltName=DNS:speedvideo.local,IP:127.0.0.1" \
-keyout /opt/speedvideo/conf/extra/tls/sv.key -out /opt/speedvideo/conf/extra/tls/sv.crt
(实测:生成 2048 位 RSA 自签名证书,无报错)
$ docker run --rm httpd:2.4 htpasswd -nbB root q1w2e3r4 > /opt/speedvideo/conf/extra/.htpasswd
$ cat /opt/speedvideo/conf/extra/.htpasswd
root:$2y$05$uoy4Lfk/UOgE3h88I5wHOu3yEAmrcS8W5bqrshZwIpthyMSuG2efm
htpasswd -nbB 用 bcrypt 生成哈希,不落明文密码。
4.5 编写 docker-compose.yml
deploy/docker-compose.yml 全文:
name: speedvideo-stack
services:
origin:
image: httpd:2.4
ports:
- "8080:8080"
- "8443:8443"
volumes:
- ../site:/usr/local/apache2/htdocs:ro
- ../conf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
- ../conf/extra:/usr/local/apache2/conf/extra:ro
restart: unless-stopped
healthcheck:
test: ["CMD", "/bin/bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/8080; printf 'GET /healthz.txt HTTP/1.0\\r\\n\\r\\n' >&3; head -1 <&3 | grep -q 200"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
要点解读:
volumes三个挂载均为:ro只读——测试源不写入任何数据,片源与配置不可被服务进程篡改;healthcheck用 bash 的/dev/tcp直接对 8080 端口发 HTTP 请求探测/healthz.txt,不依赖 curl/wget;restart: unless-stopped保证服务器重启后测试源自恢复。
4.6 启动与验证
$ cd /opt/speedvideo/deploy && docker compose up -d
Network speedvideo-stack_default Creating
Network speedvideo-stack_default Created
Container speedvideo-stack-origin-1 Creating
Container speedvideo-stack-origin-1 Created
Container speedvideo-stack-origin-1 Starting
Container speedvideo-stack-origin-1 Started
$ docker compose ps
NAME IMAGE COMMAND SERVICE STATUS PORTS
speedvideo-stack-origin-1 httpd:2.4 "httpd-foreground" origin Up 8 seconds (healthy) 0.0.0.0:8080->8080/tcp, 0.0.0.0:8443->8443/tcp
STATUS 出现 (healthy) 表示健康检查已通过。再做三项启动后验证(实测回显):
$ curl -s -o /dev/null -w "GET / -> %{http_code}, %{size_download} bytes\n" http://127.0.0.1:8080/
GET / -> 200, 3576 bytes
$ curl -s http://127.0.0.1:8080/healthz.txt
ok
$ curl -sI http://127.0.0.1:8080/clips/sv-1080p-5000k.mp4
HTTP/1.1 200 OK
Server: Apache/2.4.69 (Unix)
ETag: "c99987-65ea5ffd4c9dc"
Accept-Ranges: bytes
Content-Length: 13212039
Access-Control-Allow-Origin: *
Cache-Control: max-age=60
Content-Type: video/mp4
HTTPS 侧同样验证:
$ curl -skI https://127.0.0.1:8443/healthz.txt
HTTP/1.1 200 OK
Server: Apache/2.4.69 (Unix) OpenSSL/3.5.7
Strict-Transport-Security: max-age=31536000
Content-Length: 3
Access-Control-Allow-Origin: *
浏览器打开 http://<服务器IP>:8080/ 可见片源目录页,点击 MP4/M3U8 链接即可拉流,页面内置「开始下行测速(30 MB)」按钮可直接测量下行速率。
五、数据持久化说明
本服务是无状态只读测试源:不产日志文件以外的任何运行时数据,无数据库、无上传目录。需要持久化的只有两类宿主侧内容:
| 宿主目录 | 挂载点 | 内容 | 备份建议 |
|---|---|---|---|
/opt/speedvideo/site | /usr/local/apache2/htdocs(ro) | 片源与索引页 | 纳入常规文件备份;片源可用 4.2/4.3 节命令随时重建 |
/opt/speedvideo/conf | /usr/local/apache2/conf(ro) | Apache 配置与 TLS 证书 | 配置纳入版本管理;证书到期按 4.4 节重新生成 |
访问日志与错误日志在服务内部的 logs/sv_access.log、logs/sv_error.log,排障时用 docker compose exec origin tail ... 查看(见九节)。
六、环境变量表
官方 httpd 镜像不提供配置用环境变量——所有行为由挂载的配置文件决定,这是与 nginx 镜像(支持模板变量)的一个重要差异。实际可用的环境变量仅通用项:
| 变量 | 作用 | 本文取值 |
|---|---|---|
TZ | 进程时区 | 默认 UTC(日志时间戳即为 UTC),需要本地时区可设 Asia/Shanghai |
⚠️ 注意:不要尝试用环境变量改端口或文档根——官方镜像启动命令固定为
httpd-foreground,端口由挂载的httpd.conf中Listen决定。
七、配置参数说明
speedvideo.conf 关键项逐项解说:
| 配置项 | 取值 | 作用与理由 |
|---|---|---|
Options -Indexes | 关闭目录索引 | 防止遍历片源目录;调试期可临时改 +Indexes(见常见问题 Q2) |
AddType ... .m3u8/.ts/.mp4 | 三个 MIME 映射 | m3u8 必须是 application/vnd.apple.mpegurl、ts 必须是 video/mp2t,否则 Safari/HLS.js 拒绝播放 |
Header set Access-Control-Allow-Origin "*" | 全开 CORS | Web 播放器(hls.js/video.js)跨域拉流的必要条件 |
Cache-Control(m3u8=1s / ts=10s / mp4=60s) | 分级短缓存 | 清单要求近乎实时,分片与 MP4 允许短缓存降低回源 |
<Location "/clips/sv-2160p-12000k.mp4"> + SetEnv rate-limit 2000 | 限速 2000 KB/s | 用 mod_ratelimit 把 2160p 档限速到约 16Mbps,模拟弱网/拥塞场景 |
<Location "/secure/"> Basic 认证 | root/q1w2e3r4 | 验证客户端的鉴权播放能力 |
<Location "/status"> server-status | 仅 127.0.0.1 | 本机查看连接数/负载,不对外暴露 |
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 | 仅 TLS1.2/1.3 | 淘汰弱协议版本 |
八、等价 docker run
不用 Compose 时等价命令(单行展开,便于理解编排含义):
docker run -d --name speedvideo-origin \
-p 8080:8080 -p 8443:8443 \
-v /opt/speedvideo/site:/usr/local/apache2/htdocs:ro \
-v /opt/speedvideo/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro \
-v /opt/speedvideo/conf/extra:/usr/local/apache2/conf/extra:ro \
--restart unless-stopped \
httpd:2.4
九、常用运维命令
# 查看运行状态与健康度
docker compose ps
# 跟踪访问日志
docker compose exec origin tail -f /usr/local/apache2/logs/sv_access.log
# 配置语法检查(改完配置先查再重载)
docker compose exec origin apachectl -t
# 平滑重载配置(不断连接)
docker compose exec origin apachectl graceful
# 停止 / 启动
docker compose down
docker compose up -d
配置语法检查实测回显:
$ docker compose exec origin apachectl -t
Syntax OK
十、常见问题
Q1:启动后 curl http://127.0.0.1:8080/ 返回 200 但播放器拉 m3u8 失败?
检查响应头 Content-Type 是否为 application/vnd.apple.mpegurl——漏配 4.4 节的 AddType 会导致 HLS 客户端拒绝解析。验证:curl -sI http://127.0.0.1:8080/hls/sv-1080p/index.m3u8 | grep -i content-type,应输出 Content-Type: application/vnd.apple.mpegurl。
Q2:想临时浏览片源目录列表怎么办?
把 Options -Indexes 临时改为 Options +Indexes 并 apachectl graceful,实测目录页返回 200 且可列出 sv-1080p-3000k.mp4 等文件;调完务必改回 -Indexes。
Q3:docker compose ps 一直显示 (health: starting) 最终 unhealthy?
看健康探测依赖的 /healthz.txt 是否存在:curl -s http://127.0.0.1:8080/healthz.txt 应返回 ok。文件缺失时按 4.1 节补建(内容即 ok)。
Q4:HTTPS 访问提示证书不受信任?
本文使用自签名证书(CN=speedvideo.local),curl 加 -k 跳过校验即可;正式使用换成受信 CA 证书,把 sv.crt/sv.key 替换为正式证书与私钥后 apachectl graceful。
Q5:如何确认限速片源生效? 实测:限速 2000 KB/s 的 2160p 档下载 31MB 耗时约 15 秒、速率约 2068 KB/s:
$ curl -s -o /dev/null -w "time=%{time_total}s speed=%{speed_download}B/s\n" http://127.0.0.1:8080/clips/sv-2160p-12000k.mp4
time=15.046049s speed=2068563B/s
Q6:如何卸载清理?
docker compose down 后删除部署目录即可:rm -rf /opt/speedvideo。片源与配置都在该目录内,无其他残留。
十一、参考链接
- Apache HTTP Server 2.4 官方文档:https://httpd.apache.org/docs/2.4/
- httpd 官方镜像说明:https://hub.docker.com/_/httpd
- FFmpeg 官方文档(testsrc2/hls):https://ffmpeg.org/ffmpeg-filters.html 、https://ffmpeg.org/ffmpeg-formats.html
- Apple HLS 技术规范(MIME 要求):https://developer.apple.com/streaming/