强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Docker 部署

Docker 部署

概述

Docker 是一种轻量级的容器化技术,可以将 FFmpeg 及其依赖打包到容器中,实现一致的部署环境。本章介绍如何使用 Docker 部署 FFmpeg,包括硬件加速配置和流媒体服务搭建。

基础镜像

官方镜像

# 拉取官方镜像
docker pull jrottenberg/ffmpeg

# 查看镜像信息
docker run --rm jrottenberg/ffmpeg -version

# 运行基本命令
docker run --rm -v $(pwd):/work jrottenberg/ffmpeg -i /work/input.mp4 /work/output.mp4

常用 FFmpeg Docker 镜像

镜像说明大小
jrottenberg/ffmpeg最流行的 FFmpeg 镜像~70MB
linuxserver/ffmpegLinuxServer 维护~80MB
mwader/static-ffmpeg静态编译版本~30MB
alphine/ffmpeg基于 Alpine Linux~40MB

自定义基础镜像

# Dockerfile.ubuntu
FROM ubuntu:22.04

# 安装依赖
RUN apt-get update && apt-get install -y \
    software-properties-common \
    && add-apt-repository ppa:ubuntuhandbook1/ffmpeg6 \
    && apt-get update \
    && apt-get install -y ffmpeg \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work

ENTRYPOINT ["ffmpeg"]
# 构建镜像
docker build -t my-ffmpeg -f Dockerfile.ubuntu .

# 运行
docker run --rm -v $(pwd):/work my-ffmpeg -i input.mp4 output.mp4

Dockerfile 编写

基本 Dockerfile

# 基于 Alpine Linux(轻量级)
FROM alpine:3.18

# 安装 FFmpeg
RUN apk add --no-cache ffmpeg

# 设置工作目录
WORKDIR /work

# 设置入口点
ENTRYPOINT ["ffmpeg"]

# 默认参数
CMD ["-version"]

完整功能 Dockerfile

# 基于 Ubuntu
FROM ubuntu:22.04

# 设置环境变量
ENV DEBIAN_FRONTEND=noninteractive

# 安装 FFmpeg 和依赖
RUN apt-get update && apt-get install -y \
    ffmpeg \
    libavcodec-extra \
    frei0r-plugins \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# 创建工作目录
WORKDIR /work

# 创建输出目录
RUN mkdir -p /output

# 设置入口点
ENTRYPOINT ["ffmpeg"]

# 默认参数
CMD ["-version"]

带编解码器支持的 Dockerfile

# 支持更多编解码器
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

# 安装依赖
RUN apt-get update && apt-get install -y \
    software-properties-common \
    && add-apt-repository ppa:ubuntuhandbook1/ffmpeg6 \
    && apt-get update \
    && apt-get install -y \
    ffmpeg \
    libx264-dev \
    libx265-dev \
    libvpx-dev \
    libfdk-aac-dev \
    libmp3lame-dev \
    libopus-dev \
    libass-dev \
    libfreetype6-dev \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]

从源码编译 Dockerfile

# 从源码编译 FFmpeg
FROM ubuntu:22.04 AS builder

ENV DEBIAN_FRONTEND=noninteractive

# 安装编译依赖
RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    nasm \
    yasm \
    pkg-config \
    libx264-dev \
    libx265-dev \
    libvpx-dev \
    libfdk-aac-dev \
    libmp3lame-dev \
    libopus-dev \
    libass-dev \
    libfreetype6-dev \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# 下载 FFmpeg 源码
RUN git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg

WORKDIR /ffmpeg

# 编译 FFmpeg
RUN ./configure \
    --prefix=/usr/local \
    --enable-gpl \
    --enable-nonfree \
    --enable-libx264 \
    --enable-libx265 \
    --enable-libvpx \
    --enable-libfdk-aac \
    --enable-libmp3lame \
    --enable-libopus \
    --enable-libass \
    --enable-libfreetype \
    && make -j$(nproc) \
    && make install

# 最终镜像
FROM ubuntu:22.04

# 复制编译好的 FFmpeg
COPY --from=builder /usr/local /usr/local

# 安装运行时依赖
RUN apt-get update && apt-get install -y \
    libx264-163 \
    libx265-199 \
    libvpx7 \
    libfdk-aac2 \
    libmp3lame0 \
    libopus0 \
    libass9 \
    libfreetype6 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]

硬件加速配置

NVIDIA GPU 支持

# 支持 NVIDIA GPU
FROM nvidia/cuda:12.0.0-runtime-ubuntu22.04

ENV DEBIAN_FRONTEND=noninteractive

# 安装 FFmpeg
RUN apt-get update && apt-get install -y \
    ffmpeg \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 运行带 GPU 的容器
docker run --rm --gpus all \
    -v $(pwd):/work \
    nvidia-ffmpeg \
    -hwaccel cuda -i input.mp4 \
    -c:v h264_nvenc output.mp4

NVIDIA Docker Compose

# docker-compose.yml
version: '3.8'

services:
  ffmpeg:
    build:
      context: .
      dockerfile: Dockerfile.nvidia
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
    volumes:
      - ./input:/work/input
      - ./output:/work/output
    command: >
      -hwaccel cuda
      -i /work/input/input.mp4
      -c:v h264_nvenc
      -preset fast
      /work/output/output.mp4

Intel QSV 支持

# 支持 Intel QSV
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

# 安装 Intel Media SDK 和 FFmpeg
RUN apt-get update && apt-get install -y \
    ffmpeg \
    intel-media-va-driver \
    i965-va-driver \
    vainfo \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 运行带 QSV 的容器
docker run --rm \
    --device /dev/dri:/dev/dri \
    -v $(pwd):/work \
    qsv-ffmpeg \
    -hwaccel qsv -i input.mp4 \
    -c:v h264_qsv output.mp4

VAAPI 支持(AMD/Intel)

# 支持 VAAPI
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

# 安装 VAAPI 和 FFmpeg
RUN apt-get update && apt-get install -y \
    ffmpeg \
    mesa-va-drivers \
    vainfo \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]
# 运行带 VAAPI 的容器
docker run --rm \
    --device /dev/dri:/dev/dri \
    -v $(pwd):/work \
    vaapi-ffmpeg \
    -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 \
    -i input.mp4 \
    -c:v h264_vaapi output.mp4

Docker Compose

基本 Compose 配置

# docker-compose.yml
version: '3.8'

services:
  ffmpeg:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./input:/work/input
      - ./output:/work/output
    working_dir: /work
    # 默认命令
    command: ["-version"]

批量处理 Compose

# docker-compose.batch.yml
version: '3.8'

services:
  # 批量转码服务
  transcode:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./input:/work/input
      - ./output:/work/output
    working_dir: /work
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        for file in input/*.mp4; do
          filename=$$(basename "$$file")
          echo "处理: $$filename"
          ffmpeg -y -i "$$file" \
            -c:v libx264 -crf 23 -preset medium \
            -c:a aac -b:a 128k \
            "output/$$filename"
        done

多服务 Compose

# docker-compose.services.yml
version: '3.8'

services:
  # 转码服务
  transcoder:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./videos:/videos
    networks:
      - ffmpeg-net
    deploy:
      replicas: 3

  # HLS 生成服务
  hls-generator:
    build:
      context: .
      dockerfile: Dockerfile.hls
    volumes:
      - ./videos:/videos
      - ./hls:/hls
    networks:
      - ffmpeg-net

  # Web 服务器
  nginx:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./hls:/usr/share/nginx/html
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    networks:
      - ffmpeg-net

networks:
  ffmpeg-net:

流媒体服务

Nginx RTMP 服务

# Dockerfile.nginx-rtmp
FROM tiangolo/nginx-rtmp

# 复制配置
COPY nginx.conf /etc/nginx/nginx.conf

# 创建目录
RUN mkdir -p /var/www/html /var/log/nginx

EXPOSE 1935 80
# nginx.conf
worker_processes auto;
rtmp_auto_push on;

events {
    worker_connections 1024;
}

rtmp {
    server {
        listen 1935;
        
        application live {
            live on;
            record off;
            
            # HLS
            hls on;
            hls_path /var/www/html/live;
            hls_fragment 3;
            hls_playlist_length 60;
            
            # 录制
            # record all;
            # record_path /var/www/html/recordings;
            # record_unique on;
        }
    }
}

http {
    server {
        listen 80;
        
        location /live {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /var/www/html;
            add_header Cache-Control no-cache;
            add_header Access-Control-Allow-Origin *;
        }
    }
}
# docker-compose.rtmp.yml
version: '3.8'

services:
  nginx-rtmp:
    build:
      context: .
      dockerfile: Dockerfile.nginx-rtmp
    ports:
      - "1935:1935"
      - "8080:80"
    volumes:
      - ./hls:/var/www/html
    restart: unless-stopped

  ffmpeg-transcoder:
    image: jrottenberg/ffmpeg
    depends_on:
      - nginx-rtmp
    volumes:
      - ./videos:/videos
    command: >
      -re -i /videos/input.mp4
      -c:v libx264 -preset fast -tune zerolatency
      -b:v 3M -maxrate 3M -bufsize 6M
      -c:a aac -b:a 128k
      -f flv rtmp://nginx-rtmp/live/stream
    restart: unless-stopped

SRS(Simple Realtime Server)

# docker-compose.srs.yml
version: '3.8'

services:
  srs:
    image: ossrs/srs:5
    ports:
      - "1935:1935"
      - "1985:1985"
      - "8080:8080"
    volumes:
      - ./conf:/usr/local/srs/conf
      - ./objs/nginx/html:/usr/local/srs/objs/nginx/html
    restart: unless-stopped

MediaMTX(原 rtsp-simple-server)

# docker-compose.mediamtx.yml
version: '3.8'

services:
  mediamtx:
    image: bluenviron/mediamtx
    ports:
      - "8554:8554"  # RTSP
      - "1935:1935"  # RTMP
      - "8888:8888"  # HLS
      - "8889:8889"  # WebRTC
    volumes:
      - ./mediamtx.yml:/mediamtx.yml
    restart: unless-stopped

实用 Docker 配置

持久化存储

# docker-compose.storage.yml
version: '3.8'

services:
  ffmpeg:
    image: jrottenberg/ffmpeg
    volumes:
      # 本地目录
      - ./input:/work/input
      - ./output:/work/output
      
      # Docker 命名卷
      - videos:/videos
      
      # 只读挂载
      - ./config:/config:ro

volumes:
  videos:
    driver: local

环境变量配置

# docker-compose.env.yml
version: '3.8'

services:
  ffmpeg:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - FFMPEG_CODEC=libx264
      - FFMPEG_CRF=23
      - FFMPEG_PRESET=medium
      - FFMPEG_AUDIO_CODEC=aac
      - FFMPEG_AUDIO_BITRATE=128k
    volumes:
      - ./input:/work/input
      - ./output:/work/output
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        for file in /work/input/*.mp4; do
          filename=$$(basename "$$file")
          ffmpeg -y -i "$$file" \
            -c:v $${FFMPEG_CODEC} -crf $${FFMPEG_CRF} -preset $${FFMPEG_PRESET} \
            -c:a $${FFMPEG_AUDIO_CODEC} -b:a $${FFMPEG_AUDIO_BITRATE} \
            "/work/output/$$filename"
        done

资源限制

# docker-compose.resources.yml
version: '3.8'

services:
  ffmpeg:
    image: jrottenberg/ffmpeg
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '1.0'
          memory: 1G
    volumes:
      - ./input:/work/input
      - ./output:/work/output

健康检查

# docker-compose.health.yml
version: '3.8'

services:
  ffmpeg:
    build:
      context: .
      dockerfile: Dockerfile
    healthcheck:
      test: ["CMD", "ffmpeg", "-version"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    volumes:
      - ./input:/work/input
      - ./output:/work/output

高级用法

多阶段构建

# Dockerfile.multistage
# 阶段 1: 编译
FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    build-essential \
    git \
    nasm \
    yasm \
    libx264-dev \
    libx265-dev \
    && rm -rf /var/lib/apt/lists/*

RUN git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg

WORKDIR /ffmpeg
RUN ./configure \
    --prefix=/usr/local \
    --enable-gpl \
    --enable-libx264 \
    --enable-libx265 \
    && make -j$(nproc) \
    && make install

# 阶段 2: 运行时
FROM ubuntu:22.04

COPY --from=builder /usr/local /usr/local

RUN apt-get update && apt-get install -y \
    libx264-163 \
    libx265-199 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]

脚本封装

# Dockerfile.script
FROM jrottenberg/ffmpeg

# 复制处理脚本
COPY scripts/ /scripts/
RUN chmod +x /scripts/*.sh

WORKDIR /work
ENTRYPOINT ["/bin/sh"]
#!/bin/bash
# scripts/batch_transcode.sh

INPUT_DIR=${1:-/work/input}
OUTPUT_DIR=${2:-/work/output}
CODEC=${3:-libx264}
CRF=${4:-23}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    echo "处理: $filename"
    ffmpeg -y -i "$file" \
        -c:v "$CODEC" -crf "$CRF" \
        -c:a aac -b:a 128k \
        "$OUTPUT_DIR/$filename"
done
# docker-compose.script.yml
version: '3.8'

services:
  batch-transcode:
    build:
      context: .
      dockerfile: Dockerfile.script
    volumes:
      - ./input:/work/input
      - ./output:/work/output
    command: ["/scripts/batch_transcode.sh", "/work/input", "/work/output", "libx264", "23"]

Web API 服务

# app.py
from flask import Flask, request, jsonify, send_file
import subprocess
import os
import uuid

app = Flask(__name__)

UPLOAD_DIR = '/work/uploads'
OUTPUT_DIR = '/work/outputs'

os.makedirs(UPLOAD_DIR, exist_ok=True)
os.makedirs(OUTPUT_DIR, exist_ok=True)

@app.route('/transcode', methods=['POST'])
def transcode():
    if 'file' not in request.files:
        return jsonify({'error': 'No file provided'}), 400
    
    file = request.files['file']
    filename = f"{uuid.uuid4()}.mp4"
    input_path = os.path.join(UPLOAD_DIR, filename)
    output_path = os.path.join(OUTPUT_DIR, f"output_{filename}")
    
    file.save(input_path)
    
    # 获取参数
    crf = request.form.get('crf', '23')
    preset = request.form.get('preset', 'medium')
    
    # 执行转码
    cmd = [
        'ffmpeg', '-y', '-i', input_path,
        '-c:v', 'libx264', '-crf', crf, '-preset', preset,
        '-c:a', 'aac', '-b:a', '128k',
        output_path
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    
    if result.returncode == 0:
        return jsonify({
            'success': True,
            'output': f"/download/{os.path.basename(output_path)}"
        })
    else:
        return jsonify({'error': result.stderr}), 500

@app.route('/download/<filename>')
def download(filename):
    return send_file(
        os.path.join(OUTPUT_DIR, filename),
        as_attachment=True
    )

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
# Dockerfile.api
FROM jrottenberg/ffmpeg

# 安装 Python 和 Flask
RUN apk add --no-cache python3 py3-pip
RUN pip3 install flask

# 复制应用
COPY app.py /app/app.py

WORKDIR /app
EXPOSE 5000

CMD ["python3", "app.py"]
# docker-compose.api.yml
version: '3.8'

services:
  ffmpeg-api:
    build:
      context: .
      dockerfile: Dockerfile.api
    ports:
      - "5000:5000"
    volumes:
      - uploads:/work/uploads
      - outputs:/work/outputs
    restart: unless-stopped

volumes:
  uploads:
  outputs:

最佳实践

镜像优化

# 优化的 Dockerfile
FROM alpine:3.18 AS builder

# 安装编译依赖
RUN apk add --no-cache \
    build-base \
    nasm \
    yasm \
    x264-dev \
    x265-dev \
    libvpx-dev

# 编译 FFmpeg
RUN wget https://ffmpeg.org/releases/ffmpeg-6.0.tar.gz \
    && tar xzf ffmpeg-6.0.tar.gz \
    && cd ffmpeg-6.0 \
    && ./configure \
        --prefix=/usr/local \
        --enable-gpl \
        --enable-libx264 \
        --enable-libx265 \
        --enable-libvpx \
        --disable-doc \
        --disable-debug \
    && make -j$(nproc) \
    && make install

# 最终镜像
FROM alpine:3.18

COPY --from=builder /usr/local /usr/local

RUN apk add --no-cache \
    x264-libs \
    x265-libs \
    libvpx

# 清理
RUN rm -rf /var/cache/apk/*

WORKDIR /work
ENTRYPOINT ["ffmpeg"]

安全配置

# docker-compose.security.yml
version: '3.8'

services:
  ffmpeg:
    image: jrottenberg/ffmpeg
    # 非 root 用户运行
    user: "1000:1000"
    # 只读文件系统
    read_only: true
    # 临时文件系统
    tmpfs:
      - /tmp
    # 安全选项
    security_opt:
      - no-new-privileges:true
    # 资源限制
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
    volumes:
      - ./input:/work/input:ro
      - ./output:/work/output

日志配置

# docker-compose.logging.yml
version: '3.8'

services:
  ffmpeg:
    image: jrottenberg/ffmpeg
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    volumes:
      - ./input:/work/input
      - ./output:/work/output

常见问题

问题 1:权限问题

# 解决权限问题
docker run --rm -v $(pwd):/work --user $(id -u):$(id -g) jrottenberg/ffmpeg -i input.mp4 output.mp4

问题 2:GPU 访问问题

# 检查 GPU 是否可用
docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi

# 确保安装 nvidia-container-toolkit
# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html

问题 3:网络问题

# 使用 host 网络
services:
  ffmpeg:
    image: jrottenberg/ffmpeg
    network_mode: host

注意事项

  1. 镜像大小:选择合适的 base 镜像,避免过大
  2. 权限管理:使用非 root 用户运行容器
  3. 资源限制:设置 CPU 和内存限制
  4. 存储管理:使用 volumes 持久化数据
  5. 网络安全:限制容器网络访问

业务场景

场景 1:视频转码微服务

# docker-compose.transcoding.yml
version: '3.8'

services:
  # Redis 队列
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

  # 转码 Worker
  worker:
    build:
      context: .
      dockerfile: Dockerfile.worker
    depends_on:
      - redis
    environment:
      - REDIS_URL=redis://redis:6379
    deploy:
      replicas: 4
    volumes:
      - videos:/videos

  # API 服务
  api:
    build:
      context: .
      dockerfile: Dockerfile.api
    ports:
      - "5000:5000"
    depends_on:
      - redis
    environment:
      - REDIS_URL=redis://redis:6379
    volumes:
      - videos:/videos

volumes:
  videos:

场景 2:直播转码集群

# docker-compose.live.yml
version: '3.8'

services:
  # Nginx RTMP
  nginx-rtmp:
    image: tiangolo/nginx-rtmp
    ports:
      - "1935:1935"
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - hls:/var/www/html

  # 转码服务
  transcoder:
    image: jrottenberg/ffmpeg
    depends_on:
      - nginx-rtmp
    command: >
      -re -i rtmp://nginx-rtmp/live/source
      -c:v libx264 -preset fast -tune zerolatency
      -b:v 3M -maxrate 3M -bufsize 6M
      -c:a aac -b:a 128k
      -f flv rtmp://nginx-rtmp/live/stream
    deploy:
      replicas: 2
    restart: unless-stopped

  # HLS 服务器
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - hls:/usr/share/nginx/html
      - ./nginx-hls.conf:/etc/nginx/nginx.conf:ro

volumes:
  hls:

扩展阅读

  1. Docker 官方文档
  2. Docker Compose 文档
  3. NVIDIA Container Toolkit
  4. FFmpeg Docker 镜像
  5. SRS 文档

总结

本章介绍了 FFmpeg 的 Docker 部署,包括:

  • Docker 镜像选择和构建
  • 硬件加速配置
  • Docker Compose 编排
  • 流媒体服务搭建

掌握 Docker 部署可以帮助您构建可移植、可扩展的视频处理系统。