医疗AI模型与控制器自动化CI/CD流水线 - 详解

摘要
在医疗人工智能系统中,算法模型与机器人控制器往往以不同节奏演进,如何在保证安全合规的前提下实现高频迭代,是工程与研究并重的关键问题。本文提出并实现了一套面向 ROS2 的完整 CI/CD 流水线方案,融合 GitHub Actions、Docker 与 colcon 构建工具,支持(1)PR 级别的自动编译与测试,(2)基于变更感知的“模型/控制器”分离构建,(3)SemVer(vX.Y.Z)与模型版本(model_vN)双通道标签管理,(4)安全扫描、SBOM 生成与证据归档,(5)金丝雀发布与自动回滚,以及(6)Prometheus 指标与分布式追踪。实验与工程验证表明,该方案能在保证可追溯与合规的同时,将端到端交付时间显著缩短,并提升部署质量与可观测性。
关键词:ROS2,CI/CD,医疗 AI,Docker,colcon,语义化版本,SBOM,可观测性
引言
近年来,医疗 AI 从离线诊断走向闭环智能干预,对工程化提出更高要求:模型频繁更新、控制器稳定演进、环境异构(x86/ARM/GPU)与严格的合规审计。传统“一体化构建”难以兼顾效率与可追溯性。本文聚焦 ROS2 生态,构建一条可复用、可审计、可回滚的 CI/CD 流水线,并给出可直接落地的工程实践。
本文贡献如下:
- 提出模型/控制器解耦的 CI/CD 架构,面向模型权重变更自动递增
model_vN,同时保留 SemVer 版本线。 - 给出可落地的仓库结构、Dockerfile 与 GitHub Actions 工作流,覆盖 PR、发布、自动版本管理与安全扫描。
- 集成多架构构建、金丝雀发布与可观测性,保障质量与运维效率。
背景与相关工作
围绕 ROS2 的持续集成已有初步实践,但医疗场景提出了更严格的合规与可追溯要求。语义化版本管理(SemVer)常见于应用发布,而数据/模型工件的生命周期管理(ML/DL weights + metadata)仍需更细粒度控制。本文在现有 CI/CD 与容器化技术之上,结合模型元数据与镜像标签管理,形成“代码与模型双版本”治理框架。
完整CI/CD流水线架构
1. 仓库结构设计
medical-ai-robot/
├── .github/
│ └── workflows/
│ ├── pr-ci.yml # PR自动测试
│ ├── release-on-tag.yml # 标签发布
│ ├── auto-bump-model.yml # 模型版本自动管理
│ └── security-scan.yml # 安全扫描
├── docker/
│ ├── model.Dockerfile # AI模型镜像
│ ├── controller.Dockerfile # 控制器镜像
│ └── base.Dockerfile # 共享基础镜像
├── src/
│ ├── ai_model/ # ROS2 AI模型包
│ │ ├── package.xml
│ │ ├── launch/inference.launch.py
│ │ └── scripts/
│ └── controller/ # ROS2控制包
│ ├── package.xml
│ └── launch/
├── models/
│ ├── manifest.yaml # 模型元数据
│ └── weights/ # 模型权重
│ └── mednet_v1.pt
├── tests/
│ ├── unit/ # 单元测试
│ └── integration/ # 集成测试
└── docs/
└── architecture.md
Docker实现
base.Dockerfile (共享基础层)
FROM ros:humble-ros-core
# 系统依赖
RUN apt-get update && apt-get install -y \
python3-pip \
python3-colcon-common-extensions \
ros-humble-rmw-cyclonedx-cpp \
&& rm -rf /var/lib/apt/lists/*
# Python依赖
COPY requirements.txt /tmp/
RUN pip3 install -r /tmp/requirements.txt
# 环境配置
ENV ROS_DOMAIN_ID=42
ENV RMW_IMPLEMENTATION=rmw_fastrtps_cpp
ENV PYTHONUNBUFFERED=1
model.Dockerfile (AI模型专用)
ARG MODEL_VERSION
ARG WEIGHTS_CHECKSUM
FROM base as builder
WORKDIR /ws
# 构建阶段
COPY src ./src
RUN rosdep update && \
rosdep install --from-paths src --ignore-src -r -y && \
. /opt/ros/humble/setup.sh && \
colcon build --packages-select ai_model
# 运行阶段
FROM ros:humble-ros-base
WORKDIR /app
# 复制构建产物
COPY --from=builder /ws/install ./ros_ws
COPY models ./models
# 元数据标签
LABEL ai.model.version=${MODEL_VERSION}
LABEL ai.model.checksum=${WEIGHTS_CHECKSUM}
LABEL maintainer="medical-ai-team"
# 健康检查
HEALTHCHECK --interval=30s \
CMD ros2 service list | grep -q model_inference || exit 1
# 启动命令
ENTRYPOINT ["/bin/bash", "-lc", "source ros_ws/setup.bash && ros2 launch ai_model inference.launch.py"]
GitHub Actions工作流
1. PR CI工作流 (.github/workflows/pr-ci.yml)
name: PR CI Pipeline
on:
pull_request:
branches: [main]
paths-ignore:
- 'docs/**'
permissions:
contents: read
checks: write
jobs:
code-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint Python
run: |
pip install flake8 black
flake8 src/ tests/
black --check src/ tests/
build-test:
runs-on: ubuntu-latest
container: ros:humble-ros-base
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt-get update
rosdep update
rosdep install --from-paths src --ignore-src -r -y
- name: Build packages
run: colcon build --merge-install
- name: Run tests
run: |
colcon test
colcon test-result --all --verbose
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./lcov.info
2. 模型自动版本管理 (.github/workflows/auto-bump-model.yml)
name: Auto Model Management
on:
push:
branches: [main]
paths:
- 'models/**'
permissions:
contents: write
packages: write
jobs:
model-bump:
runs-on: ubuntu-latest
outputs:
new_version: ${
{
steps.bump.outputs.version }}
checksum: ${
{
steps.checksum.outputs.sha }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install yq
run: sudo snap install yq
- name: Compute checksum
id: checksum
run: |
NEW=$(find models/ -type f -exec sha256sum {} \; | sort | sha256sum | cut -d' ' -f1)
echo "sha=$NEW" >> $GITHUB_OUTPUT
- name: Bump model version
id: bump
run: |
CURR=$(yq e '.model_version' models/manifest.yaml)
NEXT="model_v$((${CURR#model_v} + 1))"
yq e ".model_version = \"$NEXT\"" -i models/manifest.yaml
yq e ".weights_checksum = \"${
{ steps.checksum.outputs.sha }}\"" -i models/manifest.yaml
echo "version=$NEXT" >> $GITHUB_OUTPUT
- name: Commit changes
run: |

浙公网安备 33010602011771号