SpeedVideo Docker Compose 部署指南

以 Apache HTTP Server + FFmpeg 制备固定码率片源,用 Docker Compose 从零部署 SpeedVideo 等效视频测试源:片源制备、站点配置、启动验证、限速弱网模拟与常见问题排障。

SpeedVideoSpeedVideo视频测试流媒体2026/10/25

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,digest sha256:a30cd4cd57f3…)
  • 片源工具:FFmpeg 7.0.2(static 构建,含 libx264/aac/hls muxer)
  • 服务端口:HTTP 8080、HTTPS 8443(均映射到宿主机同号端口)
  • 部署目录:/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

三、前置条件

  1. 服务器已安装 Docker 与 Compose 插件,docker compose version 可用;
  2. 8080/8443 端口空闲:
$ ss -ltn | grep -E '8080|8443'
(无输出,表示两个端口均未被占用)
  1. 准备 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.mp41280×7201500 kbps4.2 MB1543 kbps
sv-720p-2500k.mp41280×7202500 kbps6.8 MB2594 kbps
sv-1080p-3000k.mp41920×10803000 kbps8.1 MB3088 kbps
sv-1080p-5000k.mp41920×10805000 kbps13.2 MB5147 kbps
sv-2160p-12000k.mp43840×216012000 kbps31.1 MB12312 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 "*"全开 CORSWeb 播放器(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。片源与配置都在该目录内,无其他残留。

十一、参考链接