SmallThinker-3B-Preview模型部署详解:基于Docker与GitHub Actions的CI/CD流水线
本文详细介绍了如何在星图GPU平台上,通过Docker与GitHub Actions构建CI/CD流水线,实现SmallThinker-3B-Preview模型的自动化部署。该方法将模型封装为标准化镜像,并通过自动化流程完成测试、构建与推送,最终可一键部署至星图GPU平台,快速搭建一个用于文本生成与对话的AI服务接口。
SmallThinker-3B-Preview模型部署详解:基于Docker与GitHub Actions的CI/CD流水线
你是不是也遇到过这样的场景?模型好不容易调好了,准备部署上线,结果在环境配置、依赖安装上卡了大半天。或者每次模型更新,都得手动重新打包镜像、上传、部署,繁琐又容易出错。
今天咱们就来聊聊,怎么给像SmallThinker-3B-Preview这样的模型,搭建一套自动化的部署流水线。简单来说,就是让代码一提交,后面的测试、打包、部署这些事儿,全都自动完成。这套方法特别适合需要频繁迭代模型的团队,能帮你省下大量重复劳动的时间。
这篇文章会手把手带你走通整个流程:从写一个靠谱的Dockerfile把模型环境封装好,到用GitHub Actions设置自动测试和构建,最后把镜像自动推送到星图GPU平台。整个过程,你只需要关心模型代码本身,剩下的交给流水线就行。
1. 环境准备与项目初始化
在开始搭建流水线之前,我们得先把“地基”打好。这里的地基,就是你的代码仓库和本地开发环境。
首先,你需要一个GitHub账号,并创建一个新的仓库,比如可以命名为 smallthinker-3b-deployment。这个仓库将存放我们所有的代码、配置文件和模型服务脚本。
接下来,在本地电脑上,你需要安装几个必要的工具:
- Docker:这是容器化的核心,用于构建和运行我们的模型服务镜像。确保你能在命令行里运行
docker --version并看到版本信息。 - Git:代码版本管理工具,用于和GitHub仓库同步。
- Python 3.8+:我们的模型服务大概率是用Python写的,这是开发环境的基础。
准备好这些之后,把刚刚在GitHub上创建的空仓库克隆到本地:
git clone https://github.com/你的用户名/smallthinker-3b-deployment.git
cd smallthinker-3b-deployment
现在,我们可以在本地项目里创建最基础的文件结构了。一个清晰的结构能让后续工作更顺畅。你可以先创建如下几个目录和文件:
smallthinker-3b-deployment/
├── app/
│ ├── __init__.py
│ ├── main.py # 模型服务的FastAPI主应用
│ └── model_loader.py # 模型加载与推理逻辑
├── requirements.txt # Python依赖列表
├── Dockerfile # 定义Docker镜像
├── .dockerignore # 告诉Docker忽略哪些文件
├── .github/
│ └── workflows/
│ └── ci-cd.yml # GitHub Actions工作流定义
└── README.md
这个结构里,app目录放我们的应用代码,根目录下放各种配置文件。requirements.txt文件尤其重要,它锁定了项目依赖的版本,能保证任何地方构建的环境都是一致的。你可以先根据模型的需要,在里面写上基础的依赖,比如:
fastapi>=0.104.0
uvicorn[standard]>=0.24.0
torch>=2.0.0
transformers>=4.35.0
2. 编写Dockerfile:封装模型运行环境
Dockerfile就像一份食谱,告诉Docker如何一步步构建出我们需要的“菜肴”——也就是一个包含模型和所有运行环境的独立容器。写好它是实现一次构建、到处运行的关键。
一个为AI模型服务优化的Dockerfile,通常会考虑以下几点:选择合适的基础镜像、高效地安装依赖、正确地复制模型文件和应用代码、设置健康的启动命令。
下面是一个针对SmallThinker-3B-Preview这类模型的Dockerfile示例,我们逐段来看:
# 使用带有CUDA的PyTorch官方镜像作为基础,确保GPU支持
FROM pytorch/pytorch:2.1.0-cuda11.8-cudnn8-runtime
# 设置工作目录,后续命令都在这个目录下执行
WORKDIR /app
# 首先单独复制依赖列表文件,利用Docker缓存层加速构建
# 只要requirements.txt没变,就不会重新安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY ./app ./app
# 这里假设模型文件较大,可以考虑从网络位置下载或挂载卷
# 为了示例,我们假设模型已包含在代码库的 `models/` 目录下
# COPY ./models ./models
# 暴露服务运行的端口,FastAPI默认是8000
EXPOSE 8000
# 设置容器启动时执行的命令
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
这份“食谱”做了几件重要的事:
- 选择基础镜像:我们直接用了PyTorch官方镜像,它已经装好了CUDA和PyTorch,省去了自己配置GPU环境的麻烦。
- 利用缓存优化:先单独复制
requirements.txt并安装依赖。这样,当你只修改了应用代码而没改依赖时,Docker可以利用缓存跳过耗时的依赖安装步骤,极大加快构建速度。 - 复制代码:将我们的应用逻辑复制到镜像中。
- 模型处理:注释掉了直接复制模型的步骤。对于大模型,更佳实践是将模型文件放在对象存储(如S3)或通过挂载卷的方式在运行时提供,而不是打包进镜像,这能保持镜像轻量化。你可以根据实际情况调整。
- 启动命令:指定容器启动后,用Uvicorn服务器运行我们的FastAPI应用。
你可以在本地先测试一下这个Dockerfile是否能正确构建:
# 在项目根目录执行,-t 给镜像打个标签,比如 smallthinker-service
docker build -t smallthinker-service .
构建成功后,可以运行它试试看:
docker run -p 8000:8000 smallthinker-service
如果看到Uvicorn启动的日志,说明镜像基本没问题了。不过现在服务可能还跑不起来,因为我们还没写真正的app/main.py。别急,下一步就来完善它。
3. 实现模型服务与应用逻辑
镜像准备好了,接下来得往里面放“灵魂”——也就是让模型跑起来的代码。这里我们用FastAPI来快速搭建一个HTTP API服务,它简单易用,性能也不错。
我们先在 app/model_loader.py 里写模型加载和推理的核心逻辑。这个文件负责和模型直接打交道。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from typing import Optional
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class SmallThinkerModel:
"""
模型加载与推理类。
采用单例模式的思想,在服务启动时加载一次模型。
"""
_model = None
_tokenizer = None
_device = None
@classmethod
def get_model(cls):
"""获取模型实例,如果未加载则进行加载。"""
if cls._model is None:
cls.load_model()
return cls._model, cls._tokenizer
@classmethod
def load_model(cls, model_path: str = "./models/smallthinker-3b-preview"):
"""
加载模型和分词器。
在实际生产中,model_path可以配置为环境变量或从网络下载。
"""
logger.info(f"正在从 {model_path} 加载模型...")
try:
# 根据是否有GPU自动选择设备
cls._device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
logger.info(f"使用设备: {cls._device}")
# 加载分词器
cls._tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
# 加载模型,并指定设备
cls._model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype=torch.float16 if cls._device.type == "cuda" else torch.float32,
device_map="auto" if cls._device.type == "cuda" else None,
trust_remote_code=True
)
if cls._device.type == "cpu":
cls._model = cls._model.to(cls._device)
logger.info("模型加载完毕。")
except Exception as e:
logger.error(f"模型加载失败: {e}")
raise
@classmethod
def generate(cls, prompt: str, max_length: int = 512) -> str:
"""
根据提示词生成文本。
"""
model, tokenizer = cls.get_model()
inputs = tokenizer(prompt, return_tensors="pt").to(cls._device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_length,
do_sample=True,
temperature=0.7,
top_p=0.9
)
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 简单处理,返回生成的部分(去除输入提示)
return generated_text[len(prompt):] if generated_text.startswith(prompt) else generated_text
这段代码做了几件事:定义了一个类来管理模型,确保只在服务启动时加载一次(节省资源);自动检测并使用GPU;提供了文本生成的接口。
接下来,在 app/main.py 里创建FastAPI应用,并调用上面的模型类。
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from app.model_loader import SmallThinkerModel
import logging
app = FastAPI(title="SmallThinker-3B-Preview API", version="1.0.0")
logger = logging.getLogger(__name__)
# 定义请求体的数据模型
class GenerationRequest(BaseModel):
prompt: str
max_length: Optional[int] = 512
# 定义响应体的数据模型
class GenerationResponse(BaseModel):
generated_text: str
status: str = "success"
@app.on_event("startup")
async def startup_event():
"""
服务启动时自动加载模型。
"""
logger.info("正在启动服务,加载模型中...")
# 这里会触发模型加载
SmallThinkerModel.get_model()
logger.info("服务启动完成,模型已就绪。")
@app.get("/")
async def root():
"""健康检查端点。"""
return {"message": "SmallThinker-3B-Preview API is running"}
@app.get("/health")
async def health_check():
"""更详细的健康检查,可加入模型状态。"""
# 可以在这里添加模型是否加载成功的检查
return {"status": "healthy", "model_loaded": SmallThinkerModel._model is not None}
@app.post("/generate", response_model=GenerationResponse)
async def generate_text(request: GenerationRequest):
"""
文本生成主接口。
"""
try:
logger.info(f"收到生成请求,提示词长度: {len(request.prompt)}")
if not request.prompt.strip():
raise HTTPException(status_code=400, detail="提示词不能为空")
generated_text = SmallThinkerModel.generate(request.prompt, request.max_length)
return GenerationResponse(generated_text=generated_text)
except Exception as e:
logger.error(f"生成文本时出错: {e}")
raise HTTPException(status_code=500, detail=f"内部服务器错误: {str(e)}")
这个FastAPI应用提供了三个接口:根路径和/health用于健康检查,/generate是核心的文本生成接口。服务启动时会自动加载模型。
现在,你的核心应用代码就准备好了。可以再次构建Docker镜像并运行,然后使用curl或Postman测试一下/generate接口是否工作正常。
4. 配置GitHub Actions自动化流水线
手动构建和测试毕竟麻烦,我们的目标是自动化。GitHub Actions可以监听仓库的事件(比如推送代码、创建标签),然后自动执行我们定义好的工作流程。
我们在 .github/workflows/ci-cd.yml 文件里定义这个流程。这个流程一般会包含两个主要任务:持续集成(CI)和持续部署(CD)。
name: CI/CD Pipeline for SmallThinker Model
# 触发条件:当代码推送到main分支,或者手动触发时
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch: # 允许手动触发
# 环境变量,用于后续步骤
env:
REGISTRY: ghcr.io # 使用GitHub Container Registry
IMAGE_NAME: ${{ github.repository }} # 镜像名使用仓库名
jobs:
# 1. 测试任务 (CI)
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
# 安装测试需要的额外包
pip install pytest httpx
- name: Run linting (代码风格检查)
run: |
pip install black isort
black --check ./app
isort --check-only ./app
- name: Run unit tests
run: |
# 这里可以运行你的单元测试,例如:
# python -m pytest tests/ -v
echo "单元测试通过(示例,请替换为实际测试命令)"
# 2. 构建与推送镜像任务 (CD)
build-and-push:
# 这个任务依赖测试任务成功,并且只在推送到main分支时运行
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # 需要写权限来推送镜像到GHCR
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
这个工作流定义了两个任务(jobs):
- test任务:在每次推送或拉取请求时运行。它负责检查代码格式、运行单元测试(示例中为echo,你需要替换为真实的测试命令),确保新代码不会破坏现有功能。
- build-and-push任务:只在代码成功推送到main分支且测试任务通过后运行。它负责构建Docker镜像,并推送到GitHub Container Registry (GHCR)。它使用了元数据动作自动生成镜像标签(如基于分支名、提交SHA,并为main分支打上
latest标签),并配置了缓存以加速后续构建。
你需要将代码推送到GitHub仓库来触发这个工作流。在仓库的“Actions”标签页下,你可以看到工作流的运行状态和日志。
5. 集成星图GPU平台与部署
镜像已经自动构建好并推送到GHCR了,最后一步就是把它部署到能运行模型的GPU环境里。这里我们以星图GPU平台为例,展示如何完成这“最后一公里”。
星图平台通常提供了基于容器镜像的部署方式。我们需要做的是,让流水线在构建完镜像后,自动触发平台的部署更新。这可以通过调用平台的API来实现。
我们在之前的 ci-cd.yml 文件中,再增加一个部署任务。这个任务会在镜像推送成功后执行。
# 在 build-and-push job 之后,添加一个新的 job
deploy-to-startimes:
needs: build-and-push # 依赖构建任务成功
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy to StarTimes GPU Platform
env:
# 这些敏感信息务必保存在仓库的Settings -> Secrets and variables -> Actions 中
STAR_TIMES_API_TOKEN: ${{ secrets.START_TIMES_API_TOKEN }}
STAR_TIMES_DEPLOYMENT_ID: ${{ secrets.START_TIMES_DEPLOYMENT_ID }}
IMAGE_WITH_TAG: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # 使用latest标签或具体SHA
run: |
# 示例:使用curl调用星图平台的部署API
# 实际API端点、请求头和参数请查阅星图平台的官方文档
echo "正在触发星图平台部署,使用镜像: $IMAGE_WITH_TAG"
# 假设平台提供一个更新部署的REST API
curl -X POST \
-H "Authorization: Bearer $STAR_TIMES_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"deployment_id\": \"$STAR_TIMES_DEPLOYMENT_ID\", \"image_url\": \"$IMAGE_WITH_TAG\"}" \
"https://api.startimes.com/v1/deployments/update"
# 重要:这是一个示例命令,你需要替换为星图平台提供的真实API。
echo "部署触发指令已发送。请到星图平台控制台查看部署状态。"
这个部署任务的关键点在于:
- 使用Secrets管理密钥:平台的API Token和部署ID都属于敏感信息,绝不能直接写在代码里。你需要将它们保存在GitHub仓库的Settings -> Secrets and variables -> Actions中,然后在工作流中通过
${{ secrets.XXX }}引用。 - 调用部署API:通过HTTP请求(如curl)调用星图平台提供的API,告知平台:“请使用这个新的镜像版本更新我的服务。”具体的API地址、请求格式和参数,需要查阅星图平台的官方文档。
- 异步触发:通常这种API调用是异步的,即流水线发出指令后就结束了,实际的镜像拉取、服务重启等操作由平台在后台完成。你可以在脚本中添加一个循环,去轮询查询部署状态,直到成功或失败,但这会增加复杂度。对于入门,触发后让用户去平台控制台查看状态也是可行的。
将这部分代码补充到你的工作流文件后,一个完整的、从代码提交到模型服务上线的CI/CD流水线就搭建完成了。
6. 总结与后续优化建议
走完这一整套流程,你会发现自动化部署带来的最大好处就是“省心”。模型工程师可以更专注于模型本身的迭代和优化,代码提交后的一切都交给了流水线。这不仅能减少人为失误,还能让团队协作和发布节奏变得更加清晰和高效。
回顾一下,我们主要做了三件事:用Dockerfile把模型和环境打包成一个标准化的“包裹”;用GitHub Actions设置了一条自动化“流水线”,自动测试、打包这个“包裹”;最后,让这条流水线在打包完成后,自动把“包裹”寄到星图GPU平台这个“目的地”并启用。
当然,这只是一个起点。在实际项目中,你还可以根据需求对这套流水线进行增强。比如,可以在测试阶段加入更复杂的集成测试或压力测试;可以构建多架构的镜像(比如同时支持x86和ARM);可以在部署前增加一个人工审批环节;或者设置更复杂的发布策略,如蓝绿部署或金丝雀发布,来保证服务更新的平滑性。
最关键的是,这套模式是通用的。你完全可以把它应用到其他模型或AI应用的部署上,只需要调整Dockerfile里的依赖、应用代码以及平台API的调用方式。希望这个详细的步骤能帮你扫清模型部署自动化路上的障碍,让你能把更多时间花在更有创造性的工作上。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)