FireRedASR-AED-L集群化部署实战:利用Docker Compose编排多服务

你是不是也遇到过这样的场景:一个语音识别服务,平时用着挺好,但一到业务高峰期,用户一多,服务就卡顿甚至崩溃。单机部署的瓶颈,在真实的生产环境中很快就会暴露出来。

今天,我们就来聊聊如何给一个强大的语音识别模型——FireRedASR-AED-L——搭建一个更健壮、能扛住高并发的“家”。我们不搞复杂的Kubernetes,就用大家熟悉的Docker Compose,手把手带你搭建一个包含负载均衡、缓存和日志收集的迷你生产集群。整个过程就像搭积木,清晰又实用。

通过这篇教程,即使你之前没有太多集群部署的经验,也能跟着步骤,构建出一个可以水平扩展、稳定可靠的服务架构。我们最终的目标是:让你部署的服务,既能充分利用GPU资源快速推理,又能优雅地应对大量用户的并发请求。

1. 理解我们的集群蓝图

在动手写代码之前,我们先花几分钟看看要搭建的这个“房子”长什么样,心里有个谱。

想象一下,我们的语音识别服务就像一个厨房。以前只有一个厨师(单服务),订单多了就忙不过来。现在,我们打算做如下改造:

  1. 聘请多位厨师(多实例模型服务):同时处理更多订单。
  2. 设立一个前台(Nginx负载均衡器):客户来的订单由前台均匀地分给空闲的厨师,避免有人累死有人闲死。
  3. 建立一个成品暂存柜(Redis缓存):如果两个客户点了完全一样的菜(相同的音频),直接从柜子里拿做好的,不用再让厨师做一遍,极大提升效率。
  4. 安装一个监控记录仪(日志收集服务):记录每位厨师的工作状态、每道菜的制作时间,方便我们排查问题、优化流程。

这就是我们即将用Docker Compose实现的架构。各个服务会运行在独立的“容器”房间里,但它们之间可以通过内部网络顺畅通信。docker-compose.yml 这个文件,就是整个房子的建筑图纸。

2. 准备你的“建筑工地”:环境与项目结构

开始施工前,确保你的“工地”符合要求,并把“建材”摆放整齐。

2.1 基础环境要求

  • 操作系统:推荐 Ubuntu 20.04/22.04 或 CentOS 7/8。本文以Ubuntu为例。
  • Docker Engine:版本 20.10.0 或更高。确保Docker已安装并运行。
  • Docker Compose:版本 v2.0.0 或更高。推荐使用Docker Compose Plugin(docker compose命令),它与Docker集成更好。
  • NVIDIA GPU:由于FireRedASR-AED-L是GPU推理模型,你需要有NVIDIA显卡并安装好对应的驱动、CUDA Toolkit以及 NVIDIA Container Toolkit(原名nvidia-docker2)。这是让Docker容器能用上GPU的关键。
  • 资源:建议至少8GB可用内存和20GB磁盘空间。

2.2 创建项目目录

打开终端,创建一个清晰的项目目录,所有文件都放在里面。

mkdir -p firedredasr-cluster
cd firedredasr-cluster

接下来,在这个目录里,我们会创建以下核心文件:

firedredasr-cluster/
├── docker-compose.yml       # 编排蓝图:核心中的核心
├── nginx/
│   ├── nginx.conf          # 负载均衡器的配置规则
│   └── conf.d/
│       └── default.conf    # 具体的服务代理配置(可选)
├── model-service/
│   └── Dockerfile          # 构建模型推理服务的镜像
├── .env                    # 环境变量配置文件(用于灵活调整参数)
└── logs/                   # 挂载的日志目录(Docker Compose启动后自动生成)

3. 编写核心蓝图:docker-compose.yml

这是整个部署的灵魂,定义了所有服务、网络和资源。我们一步一步来构建它。

首先,创建一个名为 .env 的文件,用来管理一些可变配置,这样以后修改端口、实例数等会非常方便。

# .env 文件内容
COMPOSE_PROJECT_NAME=firedredasr-cluster
MODEL_SERVICE_SCALE=2          # 模型服务启动的实例数量,实现横向扩展
NGINX_HOST_PORT=8080           # 映射到宿主机的访问端口
REDIS_PASSWORD=your_secure_password_here  # Redis密码,请务必修改!

现在,创建并编辑 docker-compose.yml 文件。

# docker-compose.yml
version: '3.8'

# 定义所有服务共享的网络,方便服务间通过服务名通信
networks:
  asr-network:
    driver: bridge

# 定义所有服务
services:
  # 服务1: 模型推理服务 (可以启动多个实例)
  model-service:
    build: ./model-service  # 使用子目录下的Dockerfile构建镜像
    image: firedredasr-aed-l:latest
    deploy:
      replicas: ${MODEL_SERVICE_SCALE:-1}  # 通过.env文件控制实例数量,默认1个
    ports:
      - "8000"  # 内部端口8000,暴露给集群内部,宿主机不直接映射
    environment:
      - CUDA_VISIBLE_DEVICES=0  # 指定容器使用哪块GPU,多卡环境可调整
      - MODEL_PATH=/app/model
      - REDIS_HOST=redis-service
      - REDIS_PORT=6379
      - REDIS_PASSWORD=${REDIS_PASSWORD}
    volumes:
      - ./logs/model:/app/logs  # 将容器内日志挂载到宿主机
    networks:
      - asr-network
    depends_on:
      - redis-service
    # 关键:声明GPU资源需求,确保容器能访问GPU
    runtime: nvidia
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # 服务2: Nginx负载均衡器
  nginx-lb:
    image: nginx:alpine
    ports:
      - "${NGINX_HOST_PORT}:80"  # 将宿主机的8080端口映射到Nginx容器的80端口
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro  # 挂载自定义配置
      - ./logs/nginx:/var/log/nginx
    networks:
      - asr-network
    depends_on:
      - model-service
    # 健康检查,确保后端服务就绪
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80"]
      interval: 30s
      timeout: 10s
      retries: 3

  # 服务3: Redis缓存服务
  redis-service:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}  # 启动时设置密码
    ports:
      - "6379:6379"  # 暴露端口,方便宿主机调试,生产环境可注释掉
    environment:
      - REDIS_PASSWORD=${REDIS_PASSWORD}
    volumes:
      - redis-data:/data  # 持久化Redis数据
      - ./logs/redis:/var/log/redis
    networks:
      - asr-network

  # 服务4: 日志收集/查看服务 (可选,简化版用Filebeat或直接挂载查看)
  # 这里我们用一个简单的日志聚合容器作为示例,实际生产可用ELK/EFK栈
  log-viewer:
    image: alpine:latest
    command: tail -f /var/log/model/*.log /var/log/nginx/*.log  # 持续查看日志
    volumes:
      - ./logs:/var/log
    networks:
      - asr-network
    depends_on:
      - model-service
      - nginx-lb

# 定义命名卷,用于Redis数据持久化
volumes:
  redis-data:
    driver: local

关键点解释:

  • deploy.replicas:这是实现“横向扩展”的魔法指令。通过修改 .env 文件中的 MODEL_SERVICE_SCALE,你可以一键调整模型服务的实例数量(比如从2个变成4个)。
  • runtime: nvidiadeploy.resources:这两部分配置共同确保了 model-service 容器能够访问宿主机的GPU。这是GPU服务能跑起来的前提。
  • 服务发现:在同一个自定义网络 asr-network 中,容器可以通过服务名直接通信。例如,model-service 容器里可以用 redis-service 这个主机名连接到Redis。
  • depends_on:定义了服务的启动顺序,但不保证依赖服务完全就绪。对于Redis,我们可能需要在应用代码中添加重连逻辑。

4. 配置各个“房间”:服务细节配置

蓝图有了,现在需要配置每个房间的具体功能。

4.1 构建模型推理服务 (model-service/Dockerfile)

首先,我们需要创建模型服务的镜像。假设你已经有了FireRedASR-AED-L的模型文件和推理代码。

# model-service/Dockerfile
FROM nvidia/cuda:12.1.1-runtime-ubuntu22.04

# 设置工作目录和安装系统依赖
WORKDIR /app
RUN apt-get update && apt-get install -y \
    python3-pip \
    python3-dev \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 复制依赖文件并安装Python包
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

# 复制模型文件和应用程序代码
COPY model /app/model
COPY app.py /app/
COPY utils /app/utils

# 暴露端口
EXPOSE 8000

# 启动命令:这里假设你的应用使用uvicorn启动一个FastAPI服务
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]

你的 requirements.txt 需要包含必要的库,比如 fastapi, uvicorn, torch, transformers, redis 等。app.py 是你的主应用文件,需要实现语音识别接口,并集成Redis缓存逻辑(例如,对音频MD5值进行缓存)。

4.2 配置负载均衡器 (nginx/nginx.conf)

Nginx的配置决定了流量如何分发。我们创建一个基础的负载均衡配置。

# nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;

    # 定义上游服务器组,即我们的多个model-service实例
    upstream asr_backend {
        # 使用Docker Compose的服务名和内部端口
        # Nginx会自动进行DNS解析,实现负载均衡
        server model-service:8000;
        # 注意:由于我们使用了`deploy.replicas`,这里实际上会有多个容器实例。
        # Docker Compose的服务发现机制会处理多个实例的负载。
        # 更高级的配置可以使用`least_conn`等负载均衡算法。
    }

    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass http://asr_backend; # 将请求转发到上游组
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_connect_timeout 60s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
        }

        # 可选:添加一个健康检查端点
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }
}

这个配置将所有到达Nginx 80端口的请求,轮询(默认方式)地转发到名为 model-service 的后端服务组。Docker Compose会确保这个服务名解析到所有正在运行的 model-service 容器实例。

5. 启动集群与验证

一切就绪,现在可以启动我们的集群了。

5.1 构建并启动所有服务

在项目根目录(docker-compose.yml 所在目录)执行:

# 使用 docker compose (v2) 命令
docker compose --env-file .env up -d --build
  • --build:强制重新构建 model-service 镜像。
  • -d:在后台运行。
  • --env-file .env:指定环境变量文件。

命令执行后,Docker Compose会依次拉取镜像、构建自定义镜像、创建网络和卷,并启动所有服务。

5.2 检查服务状态

使用以下命令查看所有容器的运行状态:

docker compose ps

你应该能看到四个服务(model-service 会有多个实例)的状态都是 Up。也可以查看日志:

# 查看所有服务的日志
docker compose logs -f
# 查看特定服务(如模型服务)的日志
docker compose logs -f model-service

5.3 验证服务

  1. 验证Nginx负载均衡:打开浏览器,访问 http://你的服务器IP:8080/health(端口来自 .env 中的 NGINX_HOST_PORT),应该看到返回 healthy
  2. 验证模型服务:向 http://你的服务器IP:8080/v1/recognize(假设这是你的识别接口)发送一个测试音频POST请求。使用工具如 curl 或 Postman。
  3. 验证Redis缓存:进入Redis容器执行命令,查看是否有缓存键生成。
    docker compose exec redis-service redis-cli -a $REDIS_PASSWORD
    > KEYS *  # 查看所有键,执行一次识别请求后,应该能看到相关缓存
    
  4. 验证横向扩展:尝试增加实例数。先修改 .env 文件,将 MODEL_SERVICE_SCALE 改为3,然后运行:
    docker compose up -d --scale model-service=3
    
    再次运行 docker compose ps,你会看到 model-service 变成了3个实例。Nginx会自动将新实例纳入负载均衡。

6. 生产环境进阶考量

我们搭建的已经是一个可用的集群雏形。但要用于真实生产,还需要考虑更多:

  • GPU资源分配策略:如果有多张GPU卡,可以在 docker-compose.yml 中通过 CUDA_VISIBLE_DEVICES 和环境变量,为不同 model-service 实例分配不同的GPU,实现真正的并行计算。
  • 服务健康检查:在 docker-compose.yml 中为 model-service 添加 healthcheck 配置,让Nginx只将流量转发给健康的实例。
  • 配置管理:将更多配置(如模型路径、超时时间)移入 .env 文件或专门的配置管理服务。
  • 日志收集:示例中的 log-viewer 仅用于演示。生产环境应使用 ELK (Elasticsearch, Logstash, Kibana) 或 EFK (Elasticsearch, Fluentd, Kibana) 栈来集中收集、分析和展示日志。
  • 监控与告警:集成 PrometheusGrafana,监控各个服务的CPU、内存、GPU使用率、请求延迟、错误率等关键指标,并设置告警。
  • 数据持久化与备份:确保 redis-data 卷有可靠的备份策略。模型文件等也应考虑持久化存储。
  • 安全:关闭不必要的端口(如生产环境可以不暴露Redis的6379端口),使用强密码,考虑在Nginx层配置SSL/TLS加密。

整体走下来,你会发现用Docker Compose搭建一个基础的生产集群并没有想象中那么复杂。它把服务定义、网络、存储都抽象成了清晰的配置文件,管理起来非常直观。对于中小型项目或者作为更复杂编排系统(如Kubernetes)的过渡方案,这完全够用。

这次实战的核心,不仅仅是学会写一个 docker-compose.yml 文件,更是理解“服务化”和“编排”的思想。通过将应用拆分成独立的、可扩展的服务单元,并用一个统一的工具来管理它们的生命周期和相互关系,我们构建的系统弹性和可维护性都大大增强了。当你需要更多算力时,简单地修改一个数字就能增加实例;当某个服务出问题时,它不会拖垮整个系统。这种架构带来的安心感,在业务快速增长时尤为重要。

获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐