本地服务镜像推送到阿里云ACR

1. 构建并推送自定义镜像到阿里云 ACR

  • 你已经在项目根目录(有 docker-compose.yml 的位置),并且各服务的 Dockerfile 路径如下:
bash 复制代码
docker/comfy-lipsync/Dockerfile
docker/cosyvoice/Dockerfile
docker/whisper-service/Dockerfile
第一步:登录阿里云容器镜像服务
bash 复制代码
docker login --username=aalliiyy registry.cn-hangzhou.aliyuncs.com
第二步:为每个镜像重新打标签

标签格式:

bash 复制代码
registry.cn-hangzhou.aliyuncs.com/<命名空间>/<仓库名>:<版本号>

你的命名空间:xxx

你的仓库:xxx_repository
注意:同一个仓库可以存放多个不同镜像(通过不同 tag 区分),所以标签可以像下面这样:

bash 复制代码
# comfy-lipsync
# comfy-lipsync
docker tag flower-ai/comfy-lipsync:latest registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:comfy-lipsync-v1.0
# cosyvoice
docker tag flower-ai/cosyvoice:latest  registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:cosyvoice-v1.0
# whisper-service
docker tag flower-ai/whisper-service:1.0.0 registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:whisper-v1.0
# ollama-deepseek
docker tag flower-ai/ollama-deepseek:v1.0 registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:ollama-deepseek-v1.0

如果这些镜像还没有构建,你需要先在项目根目录执行 docker compose build comfy-lipsync cosyvoice whisper-service 来生成。

第三步:推送镜像到 ACR
bash 复制代码
docker push registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:comfy-lipsync-v1.0
docker push registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:cosyvoice-v1.0
docker push registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:whisper-v1.0
docker push registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:ollama-deepseek-v1.0

2. 客户拉取镜像后,如何启动服务并发起 API 请求。

拉取镜像
bash 复制代码
docker pull registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:comfy-lipsync-v1.0
docker pull registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:cosyvoice-v1.0
docker pull registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:whisper-v1.0
docker pull registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:ollama-deepseek-v1.0
客户运行服务(以 comfy-lipsync 为例)
bash 复制代码
docker run -d --gpus all \
  -p 8189:8189 \
  -v /path/to/your/models:/comfyui/models \
  -v /path/to/audio:/comfyui/input/audio \
  -v /path/to/output:/comfyui/output \
  -e NVIDIA_VISIBLE_DEVICES=all \
  registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:comfy-lipsync-v1.0

启动后,服务监听 8189 端口。

服务的 API 端点
服务 端口 主要调用方式
comfy-lipsync 8189 HTTP REST API(ComfyUI 格式),调用 /prompt 接口生成唇同步视频
cosyvoice 8003 HTTP API ,用于语音合成,具体端点取决于你的实现(如 /generate)
whisper-service 8004 HTTP API ,上传音频文件进行语音识别,通常为 /asr 接口

3. 许可证镜像

3.1 创建许可证校验镜像

在项目 docker/license-guard/ 下新建两个文件:

  • docker/license-guard/check_license.py
bash 复制代码
#!/usr/bin/env python3
import os
import sys
import time
import requests

VERIFY_URL = "https://xxxx.com/license/check"   # 替换为你的线上地址
RETRY_INTERVAL = 10      # 秒
MAX_RETRIES = 30         # 最多等 5 分钟

def verify_online(license_key):
    """调用线上 API 验证 License Key,返回 True/False"""
    try:
        resp = requests.post(
            VERIFY_URL,
            json={"license_key": license_key},
            timeout=10
        )
        resp.raise_for_status()
        data = resp.json()
        return data.get("valid", False)
    except Exception as e:
        print(f"验证请求失败: {e}", flush=True)
        return False

if __name__ == "__main__":
    key = os.environ.get("LICENSE_KEY", "")
    if not key:
        print("❌ 未设置 LICENSE_KEY 环境变量", flush=True)
        sys.exit(1)

    # 重试逻辑:万一网络暂时不通,给几次机会
    for i in range(1, MAX_RETRIES + 1):
        print(f"正在进行许可证校验 ({i}/{MAX_RETRIES})...", flush=True)
        if verify_online(key):
            os.makedirs("/license", exist_ok=True)
            with open("/license/valid.flag", "w") as f:
                f.write("ok")
            print("✅ 许可证验证通过,服务即将启动", flush=True)
            # 保持容器运行,持续持有标记
            while True:
                time.sleep(600)
        else:
            print(f"❌ 验证失败,{RETRY_INTERVAL}秒后重试...", flush=True)
            time.sleep(RETRY_INTERVAL)

    print("❌ 超过最大重试次数,许可证无效。", flush=True)
    sys.exit(1)
  • docker/license-guard/Dockerfile
bash 复制代码
FROM python:3.11-slim
RUN pip install requests
COPY check_license.py /check_license.py
RUN chmod +x /check_license.py
CMD ["python", "/check_license.py"]
3.2 构建并推送 license-guard 镜像
bash 复制代码
cd docker/license-guard
docker build -t flower-ai/license-guard:v1.0 .

# 统一推送到你的 ACR 仓库
docker tag flower-ai/license-guard:v1.0 registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:license-guard-v1.0

docker push registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:license-guard-v1.0

现在你的私有仓库里有 5 个标签:

ollama-deepseek-v1.0, comfy-lipsync-v1.0, cosyvoice-v1.0, whisper-v1.0, license-guard-v1.0

3.3 创建 docker-compose.prod.yml

用你已经构建好的 ollama-deepseek-v1.0 替换掉官方 ollama/ollama,并加入 license-guard 及启动等待逻辑。
完整文件示例

yaml 复制代码
services:
  # ==================== 许可证校验服务(新增)====================
  license-guard:
    image: registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:license-guard-v1.0
    container_name: license-guard
    restart: "no"            # 校验通过就退出?不,这里用持续运行便于管理,也可改为 on-failure
    environment:
      - LICENSE_KEY=${LICENSE_KEY}   # 从 .env 文件读入
    volumes:
      - license_data:/license        # 共享卷,存放标记文件
    networks:
      - flower-network

  # ========== DeepSeek 本地模型服务(替换为你自己的镜像)==========
  ollama-deepseek:
    image: registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:ollama-deepseek-v1.0
    container_name: flower-deepseek-local
    restart: unless-stopped
    ports:
      - "11434:11434"
    environment:
      - OLLAMA_KEEP_ALIVE=24h
    dns:
      - 8.8.8.8
      - 223.5.5.5
    volumes:
      - license_data:/license:ro     # 🆕 只读挂载许可证标记卷
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    # 🆕 启动前等待标记文件,最多等待 5 分钟
    command: >
      sh -c "
        echo '等待许可证校验通过...' &&
        for i in \$(seq 1 30); do
          if [ -f /license/valid.flag ]; then
            echo '许可证有效,启动 Ollama';
            exec ollama serve;
          fi;
          sleep 10;
        done;
        echo '许可证无效或超时,退出';
        exit 1
      "
    networks:
      - flower-network
    depends_on:
      license-guard:
        condition: service_healthy   # 等 license-guard 健康运行后再启动?可简化为 depends_on 不加 condition,但需要确保 license-guard 先于它启动
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
      interval: 30s
      timeout: 10s
      retries: 3

  # ========== ComfyUI 唇同步服务 ==========
  comfy-lipsync:
    image: registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:comfy-lipsync-v1.0
    container_name: comfy-lipsync
    ports:
      - "8189:8189"
    volumes:
      - ./data/audio:/comfyui/input/audio
      - ./data/models:/comfyui/models:ro
      - ./data/models/reactor:/comfyui/models/reactor
      - ./data/models/facedetection:/comfyui/models/facedetection
      - ./data/input/lipsync:/comfyui/input
      - ./data/output/lipsync:/comfyui/output
      - ./data/workflows:/comfyui/workflows
      - license_data:/license:ro          # 🆕 共享卷
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - COMFYUI_MODELS_PATH=/comfyui/models
      - PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    shm_size: '8gb'
    mem_limit: '32g'
    memswap_limit: '36g'
    # 🆕 启动前等待许可证标记
    command: >
      sh -c "
        echo '等待许可证...' &&
        for i in \$(seq 1 30); do
          if [ -f /license/valid.flag ]; then break; fi;
          sleep 10;
        done;
        mkdir -p /comfyui/user/default &&
        ln -sfn /comfyui/workflows /comfyui/user/default/workflows &&
        python main.py --listen 0.0.0.0 --port 8189 --enable-cors-header --lowvram
      "
    depends_on:
      - license-guard
    networks:
      - flower-network

  # ========== CosyVoice 服务 ==========
  cosyvoice:
    image: registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:cosyvoice-v1.0
    container_name: cosyvoice
    restart: unless-stopped
    ports:
      - "8003:8003"
    environment:
      TZ: Asia/Shanghai
      HF_HOME: /cache/huggingface
      TORCH_HOME: /cache/torch
      XDG_CACHE_HOME: /cache/xdg
      COSYVOICE_HOME: /opt/CosyVoice
      COSYVOICE_MODEL_DIR: /models/cosyvoice/CosyVoice-300M-SFT
      PYTHONPATH: /opt/CosyVoice:/opt/CosyVoice/third_party/Matcha-TTS
      NVIDIA_VISIBLE_DEVICES: all
      VOICE_PROFILE_DIR: /app/voice_profiles
      VOICE_REF_DIR: /app/voice_refs
      FLOWER_INTERNAL_TOKEN: ""
    volumes:
      - ./data/models:/models:ro
      - ./data/cache/cosyvoice/huggingface:/cache/huggingface
      - ./data/cache/cosyvoice/torch:/cache/torch
      - ./data/cache/cosyvoice/xdg:/cache/xdg
      - ./data/output/cosyvoice:/app/audio
      - ./data/voice_refs:/app/voice_refs
      - ./data/voice_profiles/cosyvoice:/app/voice_profiles
      - license_data:/license:ro           # 🆕 共享卷
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    shm_size: "8gb"
    mem_limit: "16g"
    # 🆕 启动前等待许可证
    command: >
      sh -c "
        echo '等待许可证校验...' &&
        for i in \$(seq 1 30); do
          if [ -f /license/valid.flag ]; then break; fi;
          sleep 10;
        done;
        [ -f /license/valid.flag ] || exit 1;
        python /opt/CosyVoice/start.py        # 假设你的 CosyVoice 启动命令
      "
    depends_on:
      - license-guard
    networks:
      - flower-network

  # ========== whisper-service ==========
  whisper-service:
    image: registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository:whisper-v1.0
    container_name: whisper-service
    restart: unless-stopped
    ports:
      - "8004:8004"
    environment:
      WHISPER_MODEL_ROOT: ./data/models/whisper
      WHISPER_WORK_DIR: ./data/work/whisper
      WHISPER_DEVICE: cpu
      WHISPER_COMPUTE_TYPE: int8
      WHISPER_MODEL: medium
      WHISPER_CPU_THREADS: 4
      WHISPER_NUM_WORKERS: 1
      WHISPER_MAX_CONCURRENT_TASKS: 1
    volumes:
      - ./data/models/whisper:/data/models/whisper:ro
      - ./data/work/whisper:/data/work/whisper
      - license_data:/license:ro             # 🆕 共享卷
    deploy:
      resources:
        limits:
          cpus: "4.0"
          memory: 8G
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    # 🆕 启动前等待许可证
    command: >
      sh -c "
        echo '等待许可证校验...';
        for i in \$(seq 1 30); do
          if [ -f /license/valid.flag ]; then break; fi;
          sleep 10;
        done;
        [ -f /license/valid.flag ] || exit 1;
        python app.py          # 请替换为你的 Whisper 服务启动命令
      "
    depends_on:
      - license-guard
    networks:
      - flower-network

volumes:
  license_data:           # 共享卷声明

networks:
  flower-network:
    driver: bridge

注意

  • CosyVoice 和 whisper 的启动命令请替换成你镜像 CMD 的实际命令(比如 python main.py 等)。
  • 若原 Dockerfile 里已有 CMD,这里用 command 覆盖会丢失原 CMD,所以必须把完整启动命令写出来。
  • 上面写的 depends_on: license-guard 保证了 license-guard 先启动,但因为它要做校验并写文件,我们又在其他容器的 command 里加了等待循环,双重保险。
3.4 客户使用
  1. 获取镜像包:购买后,给阿里云 ACR 的拉取命令(用你自己的账号或临时令牌),他一次性拉取全部 5 个镜像。
  2. 提供拿到 .env 文件模板(作为交付物的一部分直接提供给用户),用户购买后拿到 License Key,填入 .env 文件:
bash 复制代码
LICENSE_KEY=your-key-from-flower-ai
  1. 启动,启动容器时,license-guard 会调用你的 API 在线验证 Key,通过后所有服务才能运行。:
bash 复制代码
docker compose -f docker-compose.prod.yml up -d
  1. 若 LICENSE_KEY 正确,license-guard 会写标记文件,其他容器依次启动;若 Key 无效,license-guard 退出,其他容器等待超时后自动退出,服务整体不可用。

所有镜像都在同一个私有仓库,授权逻辑集中在 license-guard,Key 可以随时更换(更换后需重启 license-guard 容器即可)。
重新构建并推送 license-guard-v1.0(或新标签 license-guard-v2.0)。

你需要实现的线上 API 端点(xxxx.com/license/check):

接收 POST 请求,Body JSON: {"license_key": "xxx"}

在你的数据库里校验 Key 是否存在、未过期、未超过设备数等

返回 {"valid": true} 或 {"valid": false, "message": "原因"}

3.5 一次性拉取全部 5 个镜像的命令

方案 A:提供 pull-all.sh (Linux/macOS) / pull-all.ps1 (Windows)

bash 复制代码
#!/bin/bash
REGISTRY="registry.cn-hangzhou.aliyuncs.com/flower_shine_raod/flower_shine_raod_repository"

IMAGES=(
  "license-guard-v1.0"
  "ollama-deepseek-v1.0"
  "comfy-lipsync-v1.0"
  "cosyvoice-v1.0"
  "whisper-v1.0"
)

for tag in "${IMAGES[@]}"; do
  echo "正在拉取 $REGISTRY:$tag ..."
  docker pull "$REGISTRY:$tag"
done
echo "全部拉取完成!"

方案 B :docker compose pull

如果用户已经有了你提供的 docker-compose.prod.yml,且文件中的镜像地址都已写死,他们可以直接在 compose 文件目录下执行:

bash 复制代码
docker compose -f docker-compose.prod.yml pull

这会一次性拉取 compose 文件中定义的所有镜像。

3.6 4. 完整交付物清单(用户购买后得到什么)
文件/信息 说明
阿里云 ACR 拉取凭证 你为分发单独创建的一个 RAM 子账号(仅 ReadOnly 权限),给用户提供 docker login 命令和密码,或使用容器镜像服务的临时令牌。
docker-compose.prod.yml 包含 5 个服务的编排文件,其中的镜像地址指向你的 ACR,depends_on 和启动等待脚本已写好。
.env 模板 内容为 LICENSE_KEY=,用户填入购买时获得的 Key。
License Key 你从自己的后台生成的唯一 Key,通过邮件/站内信方式交给用户。

交付流程

  1. 用户在你的个人网站(或通过其他方式)下单付款。
  2. 你在后台生成一个 License Key,并记录到数据库。
  3. 你将上述 4 样东西打包为一个 zip 或提供下载链接。
  4. 用户解压后:
    • 先执行 docker login ... 登录你的 ACR;
    • 编辑 .env,填入 License Key;
    • 运行 docker compose -f docker-compose.prod.yml up -d。
  5. 系统启动,license-guard 在线验证 Key,验证通过后所有 AI 服务正常运行
相关推荐
byoass2 小时前
企业云盘全文检索实战:Elasticsearch集成与分布式搜索
网络·分布式·安全·elasticsearch·云计算·全文检索
云布道师2 小时前
阿里云 Tablestore 为 Hermes Agent 构建记忆系统最佳实践
网络·人工智能·阿里云
翼龙云_cloud2 小时前
云代理商:云端部署的Hermes Agent 如何接入钉钉?
人工智能·云计算·ai 智能体·hermes agent·hermes
池央4 小时前
基于腾讯云架构部署OpenClaw并实现与Telegram终端集成的全链路技术解析与实践指南
架构·云计算·腾讯云·腾讯云openclaw玩虾大赛
面汤放盐14 小时前
何时使用以及何时不应使用微服务:没有银弹
java·运维·云计算
FairGuard手游加固1 天前
双云权威认证|FairGuard游戏加固上架华为云、阿里云商店
游戏·阿里云·华为云
浪客川1 天前
UniFFI 网络接口实战:从阿里云 AI 到移动端集成
人工智能·阿里云·云计算
今天又在写代码1 天前
Docker部署
java·阿里云·docker