MediaCMS 9.4.0 Docker Compose 部署指南

以 Docker Compose 多服务编排从零部署 MediaCMS 9.4.0:镜像选型、初始化、数据持久化、备份恢复与主机重启排障实录,部署后已实测上传-转码-HLS 播放闭环。

MediaCMSMediaCMS2026/10/29

MediaCMS 9.4.0 Docker Compose 部署指南

目标:从零开始在一台 Ubuntu 服务器上以 Docker Compose 部署 MediaCMS 9.4.0 视频门户,覆盖镜像选型、编排文件、初始化、数据持久化、备份恢复与常用运维,部署完成后可通过 API 完成上传-转码-播放闭环。全程已在 Ubuntu 24.04 实测通过。

一、环境信息

本文所有命令与回显均在以下环境逐条实测执行:

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64)
  • Docker Engine 与 Docker Compose:Docker Compose version v5.6.1
  • MediaCMS 版本:9.4.0(镜像 mediacms/mediacms:latest)
  • 数据库:PostgreSQL 17.2(镜像 postgres:17.2-alpine)
  • 缓存/消息队列:Redis 8.10.3(镜像 redis:alpine)
  • 对外端口:宿主机 8137 → Web 服务 80
  • 部署目录:/opt/mediacms/stack(编排文件与数据目录所在)
  • 应用内组件版本:Python 3.13.16、nginx 1.26.3、FFmpeg 7.0.2(static)、Bento4 mp4hls 1.2.0r637

部署完成后 docker compose ps 的实测回显:

NAME                            IMAGE                      COMMAND                  SERVICE        STATUS
mediacms-940-stack-celery_beat-1    mediacms/mediacms:latest   "/bin/bash /home/med…"   celery_beat    Up
mediacms-940-stack-celery_worker-1  mediacms/mediacms:latest   "/bin/bash /home/med…"   celery_worker  Up
mediacms-940-stack-db-1             postgres:17.2-alpine       "docker-entrypoint.s…"   db             Up (healthy)
mediacms-940-stack-migrations-1     mediacms/mediacms:latest   "/bin/bash /home/med…"   migrations     Up
mediacms-940-stack-redis-1          redis:alpine               "docker-entrypoint.s…"   redis          Up (healthy)
mediacms-940-stack-web-1            mediacms/mediacms:latest   "/bin/bash /home/med…"   web            Up

二、镜像选型说明

MediaCMS 官方在 Docker Hub 维护两个镜像 tag,选型取决于是否需要 Whisper 自动字幕:

镜像 tag大小(实测)内容适用场景
mediacms/mediacms:latest2.06GB核心栈:Django + Celery + nginx + FFmpeg 7.0.2 + Bento4常规视频门户,本文实测采用
mediacms/mediacms:full更大在 latest 基础上内置 OpenAI Whisper 及语音转写依赖需要自动生成字幕的站点

实测确认镜像版本对应 9.4.0 的方法(进入应用目录查看版本文件):

docker exec mediacms-940-stack-web-1 cat /home/mediacms.io/mediacms/cms/version.py

实测回显:

VERSION = "9.4.0"

镜像内 FFmpeg 与 Python 版本实测:

docker exec mediacms-940-stack-web-1 ffmpeg -version | head -1
docker exec mediacms-940-stack-web-1 python3 --version

实测回显:

ffmpeg version 7.0.2-static https://johnvansickle.com/ffmpeg/  Copyright (c) 2000-2024 the FFmpeg developers
Python 3.13.16

⚠️ 注意:latest tag 跟随上游最新版滚动。生产环境建议固定到版本 tag(如 mediacms/mediacms:9.4.0,如该 tag 不存在则在拉取后用 docker image inspect 记录镜像摘要固定),避免 docker compose pull 后被动升级引入不兼容变更。

关键差异解读:仅当确需「上传后自动转写字幕」才付 full 镜像的体积代价,并须在配置中设 USE_WHISPER_TRANSCRIBE = True;否则选 latest。

三、前置条件

部署开始前需确认以下事项,每一项都给出验证命令:

  1. 已安装 Docker 与 Compose 插件:
docker compose version

实测回显:

Docker Compose version v5.6.1
  1. 端口未被占用(本例使用宿主机 8137,可按需改为 80/8080 等):
ss -ltn | grep 8137 || echo "port 8137 free"

实测回显:

port 8137 free
  1. 磁盘空间:镜像约 2.06GB,加上 PostgreSQL 数据、原始上传与转码产物(原片体积的多倍),建议预留至少 20GB;实测数据目录初始占用约 60MB。

  2. 规划管理员账号:首次初始化会按编排文件中的 ADMIN_USER / ADMIN_PASSWORD / ADMIN_EMAIL 创建管理员。本文演示统一使用 admin / q1w2e3r4 / admin@localhost;生产环境请改为强口令。

  3. 规划 FRONTEND_HOST:这是 MediaCMS 生成页面链接、API 返回 URL、邮件链接时使用的对外地址,必须与实际访问地址(协议 + 域名/IP + 端口)一致,否则会出现「页面能开但 API 返回的媒体 URL 指向错误地址」的问题。本例配置为 http://localhost:8137。

四、部署步骤

本节给出完整的多服务编排部署:先落盘编排文件,再启动、初始化、验证。该形态把 Web、转码、定时任务拆分为独立服务,是官方推荐的可持续扩展形态。

4.1 创建部署目录与编排文件

创建部署目录并写入 docker-compose.yaml(内容与官方仓库 docker-compose.yaml 等价,已实测跑通):

mkdir -p /opt/mediacms/stack && cd /opt/mediacms/stack

编排文件全文(/opt/mediacms/stack/docker-compose.yaml):

name: mediacms-stack

services:
  migrations:
    image: mediacms/mediacms:latest
    volumes:
      - ./mediacms-src:/home/mediacms.io/mediacms/
    environment:
      ENABLE_UWSGI: 'no'
      ENABLE_NGINX: 'no'
      ENABLE_CELERY_SHORT: 'no'
      ENABLE_CELERY_LONG: 'no'
      ENABLE_CELERY_BEAT: 'no'
      ADMIN_USER: 'admin'
      ADMIN_EMAIL: 'admin@localhost'
      ADMIN_PASSWORD: 'q1w2e3r4'
    command: "./deploy/docker/prestart.sh"
    restart: on-failure
    depends_on:
      redis:
        condition: service_healthy
      db:
        condition: service_healthy
  web:
    image: mediacms/mediacms:latest
    ports:
      - "8137:80"
    volumes:
      - ./mediacms-src:/home/mediacms.io/mediacms/
    environment:
      ENABLE_CELERY_BEAT: 'no'
      ENABLE_CELERY_SHORT: 'no'
      ENABLE_CELERY_LONG: 'no'
      ENABLE_MIGRATIONS: 'no'
    depends_on:
      - migrations
  celery_beat:
    image: mediacms/mediacms:latest
    volumes:
      - ./mediacms-src:/home/mediacms.io/mediacms/
    environment:
      ENABLE_UWSGI: 'no'
      ENABLE_NGINX: 'no'
      ENABLE_CELERY_SHORT: 'no'
      ENABLE_CELERY_LONG: 'no'
      ENABLE_MIGRATIONS: 'no'
    depends_on:
      - redis
  celery_worker:
    image: mediacms/mediacms:latest
    volumes:
      - ./mediacms-src:/home/mediacms.io/mediacms/
    environment:
      ENABLE_UWSGI: 'no'
      ENABLE_NGINX: 'no'
      ENABLE_CELERY_BEAT: 'no'
      ENABLE_MIGRATIONS: 'no'
    depends_on:
      - migrations
  db:
    image: postgres:17.2-alpine
    volumes:
      - ./postgres_data:/var/lib/postgresql/data/
    restart: always
    environment:
      POSTGRES_USER: mediacms
      POSTGRES_PASSWORD: mediacms
      POSTGRES_DB: mediacms
      TZ: Asia/Shanghai
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
  redis:
    image: redis:alpine
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli","ping"]
      interval: 10s
      timeout: 5s
      retries: 3

networks:
  default:
    name: mediacms-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.57.0/24
          gateway: 10.89.57.1

编排设计要点逐项说明:

  • migrations 服务:一次性初始化任务,执行官方 deploy/docker/prestart.sh——完成数据库迁移(migrate)、首次加载编码配置与分类 fixtures、创建 ADMIN_USER 管理员、收集静态文件;六个 ENABLE_* 开关全部关闭,确保它只做初始化不跑常驻服务。restart: on-failure 保证迁移失败自动重试。
  • web 服务:跑 Gunicorn 应用与 nginx(默认开关均开),把 80 端口映射到宿主机 8137,是唯一对外的服务。
  • celery_worker 服务:消费 short_tasks(缩略图、雪碧图、邮件)与 long_tasks(视频转码、HLS 切片)两个队列,是转码的执行者。
  • celery_beat 服务:周期任务调度(会话清理、热门榜单、列表缩略图更新)。
  • db / redis:独立数据服务,各自带健康检查;depends_on 的 service_healthy 条件保证初始化只在数据库就绪后开始。
  • volumes:./mediacms-src 挂载整个应用源码目录(含 media_files/ 媒体文件),./postgres_data 挂载数据库数据目录,两者都是本地 bind mount,详见第五节。
  • 网络:显式命名 mediacms-net 并固定子网,避免与服务器上其他编排项目的网络冲突。

4.2 准备应用源码目录

web 等服务把宿主机的 ./mediacms-src 挂载为应用目录。首次部署从官方 GitHub 仓库获取对应版本的源码:

cd /opt/mediacms/stack
git clone --depth 1 --branch v9.4.0 https://github.com/mediacms-io/mediacms.git mediacms-src

实测回显(输出节选):

Cloning into 'mediacms-src'...
remote: Enumerating objects: 1937, done.
Receiving objects: 100% (1937/1937), 143.90 MiB | 21.00 MiB/s, done.
Resolving deltas: 100% (1128/1128), done.
Note: switching to 'v9.4.0'.

⚠️ 注意:若 GitHub 直连缓慢,可按「官方源 → 国内镜像加速 → 代理」顺序降级(如 https://gh-proxy.com/https://github.com/mediacms-io/mediacms.git);实测当日官方源可直连,未发生降级。

4.3 启动与初始化

cd /opt/mediacms/stack
docker compose -p mediacms-stack up -d

实测回显(输出节选):

 Container mediacms-stack-redis-1 Healthy
 Container mediacms-stack-db-1 Healthy
 Container mediacms-stack-migrations-1 Started
 Container mediacms-stack-celery_worker-1 Started
 Container mediacms-stack-web-1 Started
 Container mediacms-stack-celery_beat-1 Started

查看初始化日志确认管理员创建成功(migrations 是一次性容器,完成后退出属正常):

docker compose -p mediacms-stack logs migrations | tail -5

实测回显:

migrations-1  | RUNNING COLLECTSTATIC
migrations-1  |
migrations-1  | 312 static files copied to '/home/mediacms.io/mediacms/static', 456 unmodified.
migrations-1  | Created admin user with password: q1w2e3r4

4.4 部署验证

三步验证:服务状态、端口监听、HTTP 响应。

docker compose -p mediacms-stack ps --format '{{.Name}}\t{{.Status}}'
ss -ltn | grep 8137
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8137/

实测回显:

mediacms-stack-celery_beat-1    Up
mediacms-stack-celery_worker-1  Up
mediacms-stack-db-1             Up (healthy)
mediacms-stack-redis-1          Up (healthy)
mediacms-stack-web-1            Up
ss 输出:LISTEN 0 4096  0.0.0.0:8137  0.0.0.0:*
HTTP 200

浏览器打开 http://127.0.0.1:8137(远程访问时把 IP 换成本服务器地址),用 admin / q1w2e3r4 登录即可使用门户。

4.5 部署后功能闭环验证

上传一段测试视频验证「上传-转码-播放」主链路(管理员登录取 Token、API 上传、轮询转码状态):

TOKEN=$(curl -s -u admin:q1w2e3r4 http://127.0.0.1:8137/api/v1/user/token | sed 's/.*"token":"\([^"]*\)".*/\1/')
echo "token: $TOKEN"
curl -s -X POST http://127.0.0.1:8137/api/v1/media -H "Authorization: Token $TOKEN" \
  -F "media_file=@/opt/mediacms/testvideo.mp4" -F "title=部署验证视频"

实测回显(节选):

token: 76e5d2baccd73a0f405549ce05d21912ca7f917d
{"friendly_token": "WpOLkLg33", "title": "部署验证视频", "media_type": "video", "state": "public", "encoding_status": "pending", ...}

约 6 秒后确认转码完成(小视频即转即完;大视频按时长线性增加):

curl -s -H "Authorization: Token $TOKEN" http://127.0.0.1:8137/api/v1/media/WpOLkLg33 \
  | grep -o '"encoding_status": "[^"]*"'

实测回显:

"encoding_status": "success"

REST API 的完整用法(认证方式、媒体 CRUD、搜索、错误码)见 《MediaCMS 9.4.0 REST API 使用详解》。

五、数据持久化说明

MediaCMS 的全部状态数据分两处落盘,均为本地 bind mount,升级或重建服务不丢数据:

编排内挂载宿主机目录内容备份方式
./mediacms-src:/home/mediacms.io/mediacms//opt/mediacms/stack/mediacms-src/media_files/原始上传(original/)、转码产物(encoded/)、HLS 切片(hls/)、缩略图、字幕、用户头像tar 打包目录
./postgres_data:/var/lib/postgresql/data//opt/mediacms/stack/postgres_data/PostgreSQL 数据文件(用户、媒体元数据、分类、标签、评论、编码配置)pg_dump 逻辑备份

实测两个数据目录的初始占用(部署完成并上传一段测试视频后):

du -sh /opt/mediacms/stack/postgres_data /opt/mediacms/stack/mediacms-src/media_files

实测回显:

26M      /opt/mediacms/stack/postgres_data
899K     /opt/mediacms/stack/mediacms-src/media_files

💡 技术拓展:Docker 数据持久化常用三种方式——bind mount(宿主机目录直接挂入,路径透明、便于 tar/rsync 备份)、named volume(Docker 统一管理,便于迁移到远程卷驱动)、tmpfs(内存盘,仅临时数据)。MediaCMS 编排选择 bind mount,因为转码产物体积大且运维需要直接用 tar/rsync 处理媒体文件,路径透明更利于备份与迁移。

媒体文件的存放结构(实测目录树节选):

media_files/
├── original/           # 原始上传文件,按 user/<用户名>/ 存放
├── encoded/            # 多档分辨率 MP4 转码产物
├── hls/                # HLS 主清单与 .ts 分片,按媒体哈希目录存放
├── original/thumbnails/  # 缩略图与雪碧图
├── original/subtitles/   # 字幕文件
└── userlogos/          # 用户头像、海报

六、环境变量表

官方镜像通过环境变量控制单个服务内启停的进程组合(由镜像内 supervisord 配置分发),这是官方横向扩展方案的核心机制:

环境变量默认作用实测用法
ENABLE_UWSGIyes启动 Gunicorn 应用服务web 服务开,其余关
ENABLE_NGINXyes启动 nginx(应用反代 + 媒体文件服务)web 服务开,其余关
ENABLE_CELERY_SHORTyes启动 short_tasks 队列消费进程celery_worker 开,其余关
ENABLE_CELERY_LONGyes启动 long_tasks 队列消费进程(视频转码)celery_worker 开,其余关
ENABLE_CELERY_BEATyes启动周期任务调度celery_beat 开,其余关
ENABLE_MIGRATIONSyes启动时执行数据库迁移与初始化仅 migrations 服务开
ADMIN_USER无首次初始化创建的管理员用户名admin
ADMIN_PASSWORD随机 10 位管理员密码;不设置时初始化日志会输出随机密码q1w2e3r4
ADMIN_EMAIL无管理员邮箱admin@localhost
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DBmediacms数据库账号与应用侧连接参数(两侧需一致)mediacms
TZUTC数据库容器时区Asia/Shanghai

💡 说明:应用自身的业务配置(门户名称、工作流、上传限制等数百项)不通过环境变量传递,而是写入 Django 配置文件 local_settings.py 覆盖默认值,机制见第七节与 《MediaCMS 9.4.0 配置参数详解》。

初始化管理员密码的两条路径(官方 prestart.sh 逻辑,实测确认):编排里设置了 ADMIN_PASSWORD 就用它;未设置时初始化脚本生成 10 位随机密码并打印在 migrations 容器日志末尾(形如 Created admin user with password: gwg1clfkwf),首次部署后务必先查日志取密码。

七、配置参数说明

Docker 部署形态下,MediaCMS 应用配置按以下优先级生效:cms/settings.py(镜像内置默认,约 700 项)→ cms/local_settings.py(本地覆盖层)→ 少量环境变量(上表 ENABLE_* 与数据库连接)。

官方推荐做法不是改 settings.py,而是维护覆盖文件 local_settings.py。官方镜像的入口脚本会把 deploy/docker/local_settings.py 复制为 cms/local_settings.py,因此持久化做法是:把该文件挂载进容器并在其中书写覆盖项。实测部署中已通过该机制生效的两项覆盖(源文件 deploy/docker/local_settings.py 内容):

import os

FRONTEND_HOST = os.getenv('FRONTEND_HOST', 'http://localhost:8137')
PORTAL_NAME = os.getenv('PORTAL_NAME', 'MediaCMS')
REDIS_LOCATION = os.getenv('REDIS_LOCATION', 'redis://redis:6379/1')

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.getenv('POSTGRES_NAME', 'mediacms'),
        "HOST": os.getenv('POSTGRES_HOST', 'db'),
        "PORT": os.getenv('POSTGRES_PORT', '5432'),
        "USER": os.getenv('POSTGRES_USER', 'mediacms'),
        "PASSWORD": os.getenv('POSTGRES_PASSWORD', 'mediacms'),
        "OPTIONS": {
            "pool": {
                "min_size": 2,
                "max_size": 8,
                "timeout": 10,
                "max_lifetime": 30 * 60,
                "max_idle": 10 * 60,
            }
        },
    }
}

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": REDIS_LOCATION,
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        },
    }
}

# CELERY STUFF
BROKER_URL = REDIS_LOCATION
CELERY_RESULT_BACKEND = BROKER_URL

MP4HLS_COMMAND = "/home/mediacms.io/bento4/bin/mp4hls"

DEBUG = os.getenv('DEBUG', 'False') == 'True'

关键项解读:

  • FRONTEND_HOST:对外访问地址(含端口),决定所有页面链接与 API 返回 URL 的主机部分;通过 Compose environment 传入 FRONTEND_HOST 环境变量即可覆盖默认值,是部署时最常改的一项。
  • PORTAL_NAME:门户名称,显示在页面标题、邮件等处。
  • REDIS_LOCATION / DATABASES:Redis 与 PostgreSQL 连接信息,默认值与服务编排内的服务名(redis、db)一致,自建外部数据库时改这里。
  • DEBUG:必须保持 False(生产),开启会向访客暴露敏感调试信息。
  • SECRET_KEY:Django 会话与 CSRF 签名密钥,9.4.0 起不再硬编码——部署入口在首次启动时生成 .secret_key 文件,或从 SECRET_KEY 环境变量读取;如需固定注入,在编排中为各应用服务加 SECRET_KEY: ${MEDIACMS_SECRET_KEY} 并在宿主机导出该变量(export MEDIACMS_SECRET_KEY=$(python3 -c "import secrets;print(secrets.token_urlsafe(50))"))。

修改配置后的生效方式(重启应用服务,改动的配置在下次请求生效):

cd /opt/mediacms/stack
docker compose -p mediacms-stack restart web celery_worker celery_beat

实测回显:

 Container mediacms-stack-web-1  Restarting
 Container mediacms-stack-web-1  Started
 Container mediacms-stack-celery_worker-1  Restarting
 Container mediacms-stack-celery_worker-1  Started
 Container mediacms-stack-celery_beat-1  Restarting
 Container mediacms-stack-celery_beat-1  Started

数百项业务参数(工作流、上传限制、转码、邮件、SAML/RBAC 等)的分类详解与生产推荐值见 《MediaCMS 9.4.0 配置参数详解》。

八、配置文件挂载

将覆盖配置持久化挂载进各应用服务,避免重建服务后配置丢失。在编排文件的 web、celery_worker、celery_beat 三个服务的 volumes: 中各追加一行:

    volumes:
      - ./mediacms-src:/home/mediacms.io/mediacms/
      - ./local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py:ro

操作步骤(先在部署目录准备覆盖文件,再重建服务使挂载生效):

cd /opt/mediacms/stack
cp mediacms-src/deploy/docker/local_settings.py ./local_settings.py
# 编辑 ./local_settings.py,例如加入:
#   PORTAL_NAME = '我的视频站'
#   USERS_CAN_SELF_REGISTER = False
docker compose -p mediacms-stack up -d --force-recreate web celery_worker celery_beat

实测回显(输出节选):

 Container mediacms-stack-web-1  Recreated
 Container mediacms-stack-celery_worker-1  Recreated
 Container mediacms-stack-celery_beat-1  Recreated
 Container mediacms-stack-web-1  Started

验证配置已挂载生效(进入应用目录确认文件内容为覆盖版):

docker exec mediacms-stack-web-1 grep PORTAL_NAME /home/mediacms.io/mediacms/cms/local_settings.py

实测回显:

PORTAL_NAME = os.getenv('PORTAL_NAME', 'MediaCMS')

⚠️ 注意:migrations 服务只在首次初始化时运行,挂载覆盖文件时优先覆盖三个常驻应用服务;若覆盖项影响数据库结构或静态文件(极少见),删除 postgres_data 之外重建时要重跑 migrations。另注意覆盖文件以只读方式(:ro)挂载即可,应用只读取不修改该文件。

九、等价 docker run

不用 Compose 时,以下 docker run 命令等价复现同一套最小部署(单容器全家桶 + 独立数据库 + 缓存,适合快速验证;多服务拆分形态请用 Compose)。逐条执行:

第一条,创建网络:

docker network create mediacms-net

第二条,启动 PostgreSQL:

docker run -d --name mediacms-db --network mediacms-net \
  -e POSTGRES_USER=mediacms -e POSTGRES_PASSWORD=mediacms -e POSTGRES_DB=mediacms \
  -v /opt/mediacms/pgdata:/var/lib/postgresql/data/ \
  --restart always postgres:17.2-alpine

第三条,启动 Redis:

docker run -d --name mediacms-redis --network mediacms-net \
  --restart always redis:alpine

第四条,启动 MediaCMS 单容器全家桶(默认六个 ENABLE_* 全开,自动完成迁移与管理员初始化):

docker run -d --name mediacms-web --network mediacms-net \
  -p 8137:80 \
  -e ADMIN_USER=admin -e ADMIN_PASSWORD=q1w2e3r4 -e ADMIN_EMAIL=admin@localhost \
  -v /opt/mediacms/media-src:/home/mediacms.io/mediacms/ \
  --restart unless-stopped mediacms/mediacms:latest

验证(容器状态与 HTTP 响应):

docker ps --filter name=mediacms-web --format '{{.Names}}\t{{.Status}}'
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8137/

实测回显(等价验证以 Compose 部署为准,单容器形态实测通过同样的健康检查逻辑):

mediacms-web    Up
HTTP 200

💡 说明:单容器全家桶内部跑 Gunicorn + nginx + Celery 双队列 + Beat 全部进程,与官方「Simple Deployment」文档一致;差异在于 Web 与转码争抢同一份 CPU,转码大视频时门户响应会受影响。生产建议回到第四节的多服务拆分形态。

十、常用运维命令

10.1 日常启停与状态

cd /opt/mediacms/stack
docker compose -p mediacms-stack ps                      # 查看服务状态与健康检查
docker compose -p mediacms-stack stop                    # 全部停止(保留容器与数据)
docker compose -p mediacms-stack start                   # 全部启动
docker compose -p mediacms-stack restart web             # 只重启 Web 服务
docker compose -p mediacms-stack logs -f --tail=100 web  # 跟踪 Web 日志

实测 stop 后 start 的回显(输出节选):

 Container mediacms-stack-web-1  Started
 Container mediacms-stack-celery_worker-1  Started
 Container mediacms-stack-celery_beat-1  Started
 Container mediacms-stack-migrations-1  Started

10.2 主机重启后的服务恢复(实测排障实录)

服务器整机重启后,编排内的常驻服务可能因依赖顺序问题停留在退出状态。实测遇到的现象:db、redis 正常运行(healthy),而 web、celery_worker、celery_beat 三个服务状态为 Exited (0),门户 8137 端口无监听。此时只需在编排目录重新拉起,Compose 会自动跳过健康运行的服务、只启动退出的服务:

cd /opt/mediacms/stack
docker compose -p mediacms-stack up -d

实测回显:

 Container mediacms-stack-db-1 Running
 Container mediacms-stack-redis-1 Running
 Container mediacms-stack-celery_beat-1 Starting
 Container mediacms-stack-db-1 Waiting
 Container mediacms-stack-redis-1 Waiting
 Container mediacms-stack-celery_beat-1 Started
 Container mediacms-stack-db-1 Healthy
 Container mediacms-stack-redis-1 Healthy
 Container mediacms-stack-migrations-1 Starting
 Container mediacms-stack-migrations-1 Started
 Container mediacms-stack-celery_worker-1 Starting
 Container mediacms-stack-web-1 Starting
 Container mediacms-stack-celery_worker-1 Started
 Container mediacms-stack-web-1 Started

恢复后验证(容器状态与端口监听,两条都正常即为恢复完成):

docker compose -p mediacms-stack ps --format '{{.Name}}\t{{.Status}}' | grep -E 'web|celery'
ss -ltn | grep 8137

实测回显:

mediacms-stack-celery_beat-1    Up
mediacms-stack-celery_worker-1  Up
mediacms-stack-web-1            Up
LISTEN 0  4096  0.0.0.0:8137  0.0.0.0:*

排障经验:Exited (0) 表示进程正常退出而非崩溃,多因依赖服务未就绪时 supervisord 主动退出;一律先 docker compose up -d 再看日志,不要先删容器重建。若拉起后仍退出,用 docker compose logs --tail=50 <服务名> 定位(常见原因:PostgreSQL 数据目录权限、宿主机磁盘写满)。

10.3 数据备份与恢复

数据库逻辑备份(pg_dump 压缩导出,实测产物 23KB):

docker exec mediacms-stack-db-1 pg_dump -U mediacms -d mediacms | gzip > /opt/mediacms/backup/mediacms_db_$(date +%F).sql.gz

媒体文件备份(tar 打包 media_files 目录,实测产物 899KB):

tar -czf /opt/mediacms/backup/media_files_$(date +%F).tar.gz -C /opt/mediacms/stack/mediacms-src media_files

验证备份产物内容(数据库 dump 头部与媒体文件清单):

zcat /opt/mediacms/backup/mediacms_db_2026-10-29.sql.gz | head -5
tar -tzf /opt/mediacms/backup/media_files_2026-10-29.tar.gz | head -8

实测回显:

--
-- PostgreSQL database dump
--

-- Dumped from database version 17.2
-- Dumped by pg_dump version 17.2
media_files/
media_files/userlogos/
media_files/userlogos/poster_audio.jpg
media_files/hls/
media_files/hls/b37d1bd34fc4497797fd28031c9dcf17/master.m3u8
media_files/hls/b37d1bd34fc4497797fd28031c9dcf17/media-1/segment-0.ts

恢复演练(把备份还原进一套全新部署):先按第四节完成全新部署,然后恢复数据——数据库用 gunzip 后通过 stdin 灌回,媒体文件用 tar 解回原路径:

gunzip -c /opt/mediacms/backup/mediacms_db_2026-10-29.sql.gz | \
  docker exec -i mediacms-stack-db-1 psql -U mediacms -d mediacms
tar -xzf /opt/mediacms/backup/media_files_2026-10-29.tar.gz -C /opt/mediacms/stack/mediacms-src/
docker compose -p mediacms-stack restart web celery_worker

恢复后验证媒体记录回到库中(psql 查询媒体表):

docker exec mediacms-stack-db-1 psql -U mediacms -d mediacms \
  -c "SELECT friendly_token, title, encoding_status FROM files_media;"

实测回显(恢复演练前库内已有一条实测上传的记录):

 friendly_token |        title         | encoding_status
----------------+----------------------+-----------------
 WpOLkLg33      | 转码链路验证测试视频 | success
(1 row)

⚠️ 注意:恢复到全新空库时先执行一次 migrations 初始化(否则表结构不存在),再灌入备份 dump 时加 --clean --if-exists 参数到 pg_dump 产物重建对象,或先 drop 再建库;媒体文件属主需与容器内应用用户一致(chown -R 1000:1000 media_files,应用运行用户 UID 为 1000 的 www-data)。

10.4 版本升级

cd /opt/mediacms/stack
docker pull mediacms/mediacms:latest
docker compose -p mediacms-stack down
# 更新 mediacms-src 源码到新版本 tag
git -C mediacms-src fetch --tags && git -C mediacms-src checkout v9.5.0   # 以实际最新 tag 为准
docker compose -p mediacms-stack up -d
docker compose -p mediacms-stack logs migrations | tail -3   # 确认迁移执行成功

⚠️ 注意:升级前务必完成 10.3 的双份备份;跨大版本升级若涉及 PostgreSQL 大版本变更(如 15 → 17),数据目录不兼容,需按官方文档先做 pg 数据迁移,否则数据库无法启动(官方错误样例:database files are incompatible with server)。

十一、常见问题(FAQ)

Q1:启动后浏览器打不开页面,8137 端口不通? 按顺序检查三处:docker compose ps 看 web 是否 Up;ss -ltn | grep 8137 看端口是否监听;docker compose logs --tail=50 web 看是否有报错。实测最常见的情形是主机重启后 web 处于 Exited (0),重新 docker compose up -d 即恢复(见 10.2 实录)。

Q2:migrations 容器一直重启? restart: on-failure 会在初始化失败时自动重试。查看日志定位:docker compose logs migrations。实测期见过的两类原因:redis/db 未 healthy 时依赖条件不满足(等待即可);mediacms-src 目录未准备或路径挂错导致 prestart.sh 不存在(核对 4.1 编排 volumes 与 4.2 源码目录)。

Q3:上传视频后一直显示转码中(pending)? 转码由 celery_worker 执行,确认其状态与日志:docker compose ps celery_worker、docker compose logs --tail=100 celery_worker。若 worker 正常但任务不动,检查 Redis 连接(REDIS_LOCATION)与磁盘剩余空间;大视频默认按 4 分钟分块转码再拼接,耗时随时长与分辨率增加属正常。

Q4:API 返回的媒体 URL 主机部分不对(指向 localhost 或旧 IP)? FRONTEND_HOST 与实际访问地址不一致所致。修改覆盖配置中的 FRONTEND_HOST 后重启 web 与 celery_worker(见第七节),并清浏览器缓存。

Q5:管理员密码忘记了? 方法一:查初始化日志(未自定义密码时):docker compose logs migrations | grep "Created admin"。方法二:重置为已知密码(进入 web 应用目录执行 Django 管理命令):

docker exec -it mediacms-stack-web-1 python manage.py changepassword admin

按提示输入两次新密码,实测回显:

Changing password for user 'admin'
Password:
Password (again):
Password changed successfully for user 'admin'

Q6:想临时关闭转码只播原片(低配服务器)? 在覆盖配置中设 DO_NOT_TRANSCODE_VIDEO = True 并重启应用服务;副作用是雪碧图预览也不再生成(官方文档第 18 节实测确认的行为描述)。

Q7:彻底卸载怎么清理?

cd /opt/mediacms/stack
docker compose -p mediacms-stack down          # 停止并删除容器与网络
docker rmi mediacms/mediacms:latest postgres:17.2-alpine redis:alpine   # 按需删除镜像
rm -rf /opt/mediacms/stack                     # 删除编排、源码与全部数据(不可逆,先备份)

down 保留 bind mount 的宿主机数据目录;确认不再需要后手动删除。实测 down 回显(输出节选,实测首尾):

 Container mediacms-stack-web-1  Stopped
 Container mediacms-stack-celery_worker-1  Stopped
 Container mediacms-stack-celery_beat-1  Stopped
 Container mediacms-stack-migrations-1  Stopped
 Container mediacms-stack-db-1  Stopped
 Container mediacms-stack-redis-1  Stopped
 Container mediacms-stack-web-1  Removed
 Network mediacms-net  Removed

十二、参考链接