AI工程必备的11个Docker镜像实战指南
1. 为什么这11个Docker镜像值得你花时间逐个试一遍
我带过三支AI工程团队,从零搭建过七套生产级大模型推理平台,也亲手把十几个学术项目从Jupyter Notebook拖进Kubernetes集群跑通全链路。最深的体会是:容器不是锦上添花的工具,而是AI项目能否活过三个月的生死线。你肯定遇到过这些场景——同事发来一个“跑通了”的PyTorch训练脚本,你本地pip install一堆包后报错说torch.compile不支持你的CUDA版本;或者好不容易调好的LoRA微调参数,在云服务器上加载模型时直接OOM;又或者团队里有人用conda装了旧版transformers,有人用pip装了nightly版,结果同一个pipeline()调用返回完全不同的输出。这些问题背后,根本不是代码问题,而是环境混沌。Docker解决的从来不是“能不能跑”,而是“为什么在你机器上能跑,在我机器上不能跑,在服务器上更不能跑”。
这11个镜像,不是随便凑数的清单。我按实际工程中的使用频次、踩坑深度和不可替代性重新排过序——前3个(Python数据科学栈、Hugging Face Transformers、NVIDIA CUDA运行时)是每天必开的“工作台”,中间5个(PyTorch、TensorFlow、Ollama、Qdrant、Jupyter)是具体任务的“专用工具箱”,最后3个(Airflow、MLflow、Kubeflow Notebooks)则是项目规模上量后的“指挥中枢”。比如Ollama镜像,很多人以为只是本地跑Llama3的玩具,但去年我们给某金融客户做合规文档摘要系统时,正是靠它封装的ollama serve+自定义modelfile机制,把模型权重、tokenizer、system prompt全部固化进镜像层,彻底规避了客户环境里Python包版本冲突导致的tokenization不一致问题。再比如Qdrant镜像,它的qdrant/qdrant官方镜像默认不启用gRPC,而我们的RAG服务必须用gRPC才能压测到2000 QPS,这个细节在官方文档里藏得极深,但镜像启动参数里加一行-p 6334:6334就能打开。这些不是教科书里的知识点,是我在凌晨三点调试失败的CI流水线时,用docker inspect一层层扒出来的。
如果你正在从单机实验转向团队协作,或者准备把模型服务部署到客户现场,又或者被“环境不一致”问题反复折磨——这篇就是为你写的。下面我会拆解每个镜像的真实用途、关键配置陷阱、以及我压测过的性能边界值。不讲虚的,只说你明天就能用上的东西。
2. 核心设计逻辑:为什么是这11个,而不是其他?
2.1 选型铁律:拒绝“看起来很美”的镜像
很多教程推荐镜像时只看Docker Hub下载量,这是大忌。我见过下载量百万级的TensorFlow镜像,基础层用的是ubuntu:20.04,结果客户内网安全策略要求所有镜像必须基于centos:8构建,光是重编译CUDA驱动就耗掉两天。真正的选型必须过三关:
第一关:基础镜像血统
必须明确知道它继承自哪个Linux发行版、哪个glibc版本、是否预装了musl libc。比如python:3.11-slim-bookworm(Debian 12)和python:3.11-slim-jammy(Ubuntu 22.04)看似相似,但前者默认禁用apt-get update的https源,后者默认启用。这个差异会导致你在离线环境中构建时,apt-get install直接卡死——因为内网镜像源只同步了http协议的包。我现在的标准是:所有生产环境镜像必须基于debian:bookworm-slim或ubuntu:22.04,原因很简单:这两个发行版的glibc ABI兼容性最好,且社区维护最活跃。
第二关:CUDA驱动绑定策略
这是GPU镜像的核心命门。nvidia/cuda:12.1.1-runtime-ubuntu22.04这类镜像,它的CUDA runtime版本(12.1.1)和宿主机NVIDIA驱动版本(如535.129.03)必须满足NVIDIA官方的 兼容矩阵 。很多人以为只要镜像里有CUDA就行,结果在A100服务器上跑nvidia-smi正常,但torch.cuda.is_available()返回False。根本原因是:该镜像要求宿主机驱动>=535,而客户服务器驱动是525。解决方案不是升级驱动(客户不允许),而是换用nvidia/cuda:11.8.0-runtime-ubuntu22.04镜像,它兼容驱动>=520。这个决策必须在项目启动第一天就定死,否则后期迁移成本极高。
第三关:二进制分发方式
Hugging Face的transformers镜像有两种主流构建方式:一种是pip install transformers,另一种是pip install git+https://github.com/huggingface/transformers.git@v4.41.0。前者安装快但无法控制commit hash,后者慢但可精确复现。我们团队的规则是:所有生产环境镜像必须用git commit hash安装,且在Dockerfile里写明# COMMIT: 7b4a9c2d1e8f...。这样当发现某个bug时,能立刻定位到是哪个PR引入的。
2.2 分层架构:从开发到生产的容器化路径
这11个镜像不是孤立存在的,它们构成了一条清晰的工程流水线。我画过无数遍的架构图,最终简化成三层:
第一层:开发沙盒(Dev Sandbox)
包含jupyter/scipy-notebook、python:3.11、huggingface/transformers。特点是体积大(>2GB)、预装大量调试工具(vim、htop、curl),但CPU/GPU资源不限制。这是数据科学家的游乐场,允许他们用!pip install随意折腾,因为每次重启容器就重置环境。
第二层:服务化中间件(Serving Middleware)
包含pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime、ollama/ollama、qdrant/qdrant。特点是精简(<800MB)、禁用交互式shell、只暴露必要端口。比如Ollama镜像,我们强制在Dockerfile里删掉/bin/bash,只保留/usr/bin/ollama,防止运维误操作进入容器修改模型文件。
第三层:编排中枢(Orchestration Core)
包含apache/airflow:2.8.1、mlflow/mlflow:2.14.0、kubeflow/jupyter-web-app:v1.8.2。特点是强依赖Kubernetes CRD,所有配置通过ConfigMap注入,镜像本身不存任何业务逻辑。比如Airflow镜像,我们禁用所有airflow connections的Web UI操作,所有数据库连接字符串必须通过AIRFLOW_CONN_*环境变量传入,确保密钥不落地。
这种分层不是理论设计,而是被客户审计逼出来的。某次金融客户安全扫描发现,我们的Jupyter镜像里存在ssh二进制文件,虽然没启用,但违反了“最小权限原则”。第二天我们就把所有开发镜像的基础层从ubuntu:22.04换成debian:bookworm-slim,并加入RUN apt-get purge -y openssh-client && rm -rf /var/lib/apt/lists/*。
2.3 镜像瘦身:为什么我的PyTorch镜像比官方小40%
官方pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime镜像大小是1.8GB,而我们团队维护的同版本镜像只有1.07GB。差距在哪?三个关键动作:
替换包管理器:官方镜像用
apt-get安装系统依赖,我们改用snap安装curl、wget等工具,因为snap包是压缩的,且卸载后不留缓存。实测节省120MB。删除文档和示例:
RUN find /usr/local/lib/python3.11/site-packages -name "*.md" -delete && find /usr/local/lib/python3.11/site-packages -name "examples" -type d -exec rm -rf {} +。这部分删掉86MB,且不影响运行时。多阶段构建的暴力优化:在构建阶段用
gcc-12编译PyTorch扩展,但最终镜像只COPY编译产物,不COPY整个/usr/lib/gcc目录。官方镜像保留了完整的GCC工具链(320MB),我们只留.so文件(<5MB)。
提示:镜像大小直接影响CI/CD速度。我们测试过,镜像从1.8GB降到1.07GB后,Kubernetes Pod启动时间从平均23秒降至14秒,CI流水线整体耗时减少17%。这不是玄学,是实实在在的工程收益。
3. 11个核心镜像深度解析与实操指南
3.1 Python数据科学栈:jupyter/scipy-notebook(ID: #1)
这个镜像常被当成“Jupyter笔记本”,但它真正的价值在于预集成的数据科学工具链。官方镜像jupyter/scipy-notebook:latest基于python:3.11,预装了numpy==1.26.4、pandas==2.2.2、scikit-learn==1.4.2等27个核心包,关键是它们的版本经过严格兼容性测试——比如pandas 2.2.2和scikit-learn 1.4.2共用同一套threadpoolctl底层线程库,避免多线程训练时出现core dump。
实操要点:
- 启动命令必须加
--NotebookApp.token='' --NotebookApp.password_required=False,否则每次重启都要输token。这是开发环境的合理妥协。 - 挂载数据卷时,用
-v $(pwd)/data:/home/jovyan/work/data:Z,末尾的:Z是SELinux标签,否则在CentOS服务器上会因权限拒绝写入。 - 禁用自动保存:在
~/.jupyter/jupyter_notebook_config.py中添加c.NotebookApp.autosave_interval = 0。实测发现,当笔记本超过50MB时,autosave会占用30% CPU,导致训练卡顿。
避坑经验:
去年有个项目,数据科学家在Jupyter里用!pip install torch==2.3.0覆盖了预装的torch==2.2.1,结果torch.compile功能失效。根源是torch==2.3.0需要CUDA 12.1,而镜像里预装的是CUDA 12.0。解决方案是:所有!pip install操作必须加--force-reinstall --no-deps,然后手动pip install依赖项。但我们后来彻底禁用了!pip install,改为在Dockerfile里用RUN pip install --no-cache-dir torch==2.3.0+cu121 -f https://download.pytorch.org/whl/torch_stable.html。
3.2 Hugging Face Transformers:huggingface/transformers-pytorch-gpu(ID: #3)
注意,这不是Hugging Face官方镜像,而是我们团队基于pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime二次构建的。官方huggingface/transformers镜像基于CPU,GPU版本需要自己构建。
关键配置:
- 必须指定
TRANSFORMERS_OFFLINE=1环境变量,否则首次加载模型时会尝试联网下载config.json,在离线环境直接超时。 HF_HOME环境变量要指向挂载卷,如-e HF_HOME=/workspace/hf_cache,否则模型权重会下到容器临时目录,重启即丢失。- 加载模型时,用
from_pretrained(..., local_files_only=True)强制走本地缓存,这是生产环境的黄金法则。
性能实测:
在A10G GPU上加载meta-llama/Llama-3-8b-chat-hf:
- 默认配置(
device_map="auto"):显存占用14.2GB,首token延迟842ms - 优化后(
device_map="balanced_low_0"+torch_dtype=torch.bfloat16):显存降至11.8GB,首token延迟压到521ms
这个优化来自Hugging Face文档里一句不起眼的话:“balanced_low_0将第一层放在GPU0,后续层均衡分配,避免单卡显存碎片化”。
3.3 NVIDIA CUDA运行时:nvidia/cuda:12.1.1-runtime-ubuntu22.04(ID: #4)
这是所有GPU镜像的地基。很多人忽略了一个致命细节:runtime镜像不包含nvcc编译器,只包含CUDA动态链接库。这意味着你不能在容器里编译CUDA kernel,但可以运行已编译的PyTorch/TensorFlow。
验证方法:
docker run --rm --gpus all nvidia/cuda:12.1.1-runtime-ubuntu22.04 nvidia-smi # 输出应显示GPU列表,且Driver Version与宿主机一致 docker run --rm --gpus all nvidia/cuda:12.1.1-runtime-ubuntu22.04 nvcc --version # 此命令应报错:command not found —— 这才是正确的!常见错误:
客户曾反馈“GPU识别失败”,排查发现是Docker daemon配置错误。在/etc/docker/daemon.json中必须有:
{ "default-runtime": "runc", "runtimes": { "nvidia": { "path": "nvidia-container-runtime", "runtimeArgs": [] } } }缺了"nvidia"runtime声明,--gpus all参数就无效。
3.4 PyTorch:pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime(ID: #5)
这个镜像的cudnn8后缀是重点。CuDNN 8.9.7比8.8.0在Transformer层加速12%,但要求CUDA driver >=535。我们做过对比测试:
| CuDNN版本 | A100 80GB吞吐(tokens/sec) | 显存占用 |
|---|---|---|
| 8.8.0 | 1842 | 15.3GB |
| 8.9.7 | 2065 | 15.1GB |
提升明显,但代价是驱动兼容性收窄。所以我们的策略是:新项目默认用8.9.7,老项目维持8.8.0。
关键环境变量:
PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128:防止CUDA内存碎片化,尤其在LoRA微调时避免OOM。TORCH_COMPILE_BACKEND=nvprune:启用NVIDIA的编译优化,比默认inductor快8%。
3.5 TensorFlow:tensorflow/tensorflow:2.16.1-gpu-jupyter(ID: #6)
注意,TensorFlow 2.16.1是最后一个支持CUDA 12.1的版本。2.17+已转向CUDA 12.4,而客户服务器驱动普遍停留在535系列,不支持12.4。所以2.16.1是当前生产环境的“甜蜜点”。
实操技巧:
TensorFlow的GPU内存增长策略默认是memory_growth=True,但实际效果不如PyTorch的max_split_size_mb。我们强制在代码开头加:
import tensorflow as tf gpus = tf.config.experimental.list_physical_devices('GPU') if gpus: for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True) # 关键:限制最大内存为总显存的80% tf.config.experimental.set_memory_limit(gpus[0], int(80 * 1024))3.6 Ollama:ollama/ollama:0.1.36(ID: #7)
Ollama镜像的精髓在于modelfile机制。很多人以为它只是个API wrapper,其实它是完整的模型打包方案。
构建自定义模型:
创建Modelfile:
FROM llama3:8b PARAMETER num_ctx 32768 PARAMETER stop "<|eot_id|>" SYSTEM """ 你是一个严谨的金融分析师,只回答与财报、估值、风险相关的问题。 """然后ollama create my-finance-model -f Modelfile。这个过程会把llama3:8b的权重、tokenizer、system prompt全部固化进镜像层,生成的my-finance-model可直接docker run启动。
网络配置陷阱:
默认Ollama监听127.0.0.1:11434,在Docker中必须改:
docker run -d -p 11434:11434 -v ollama:/root/.ollama ollama/ollama:0.1.36 \ ollama serve --host 0.0.0.0:11434否则宿主机curllocalhost:11434会超时。
3.7 Qdrant:qdrant/qdrant:v1.9.2(ID: #8)
Qdrant镜像的性能瓶颈往往不在向量搜索,而在存储层。官方镜像默认用RocksDB,但在高并发写入时会出现IO wait飙升。
优化方案:
在docker-compose.yml中:
qdrant: image: qdrant/qdrant:v1.9.2 command: ["--storage-type", "disk", "--cache-max-size", "2147483648"] volumes: - ./qdrant_data:/qdrant/storage environment: - QDRANT__STORAGE__MAX_MEMORY_MAP_SIZE=4294967296--cache-max-size 2G和QDRANT__STORAGE__MAX_MEMORY_MAP_SIZE=4G组合,让Qdrant用内存映射加速磁盘读取,实测1000 QPS下P99延迟从120ms降至45ms。
3.8 Airflow:apache/airflow:2.8.1(ID: #9)
Airflow镜像最大的坑是插件管理。官方镜像不预装apache-airflow-providers-docker,但你的DAG可能需要DockerOperator。
正确做法:
不要pip install,而是用Airflow的requirements.txt机制:
FROM apache/airflow:2.8.1 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txtrequirements.txt内容:
apache-airflow-providers-docker==4.10.0 apache-airflow-providers-postgres==6.2.0版本必须严格匹配Airflow主版本,否则airflow db upgrade会失败。
3.9 MLflow:mlflow/mlflow:2.14.0(ID: #10)
MLflow镜像的backend-store-uri必须用PostgreSQL,SQLite在多用户场景下会锁表。但官方镜像没预装psycopg2。
修复Dockerfile:
FROM mlflow/mlflow:2.14.0 RUN pip install --no-cache-dir psycopg2-binary==2.9.7 ENV MLFLOW_TRACKING_URI=http://mlflow:5000 ENV MLFLOW_BACKEND_STORE_URI=postgresql+psycopg2://user:pass@postgres:5432/mlflow注意psycopg2-binary必须指定2.9.7,因为2.10+需要编译,而MLflow镜像里没有gcc。
3.10 Kubeflow Notebooks:kubeflow/jupyter-web-app:v1.8.2(ID: #11)
这个镜像不是用来跑代码的,而是Kubeflow的前端控制器。它本身不包含Python环境,所有计算都在用户启动的独立Notebook Server中进行。
关键配置:
在Kubeflow的notebook-controllerConfigMap中,必须设置:
notebookControllers: defaultImages: - name: pytorch-cuda12.1 displayName: PyTorch with CUDA 12.1 description: PyTorch 2.3.0 + CUDA 12.1 + cuDNN 8.9.7 url: your-registry/pytorch-cuda121:2.3.0这样用户在Kubeflow UI里选择镜像时,看到的就是你定制的生产级镜像,而不是jupyter/scipy-notebook这种通用镜像。
3.11 补充说明:为什么没有LangChain、LlamaIndex等热门框架?
这是刻意为之。LangChain镜像在Docker Hub上有上百个,但90%都基于python:3.11-slim,而langchain==0.1.16依赖openai==1.35.0,后者又依赖httpx==0.27.0,这个组合在slim镜像里会因SSL证书问题导致requests.get失败。我们试过所有变体,最终结论是:LangChain这类胶水框架,应该作为应用层依赖,而不是基础镜像的一部分。正确的做法是在你的应用Dockerfile里:
FROM pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime RUN pip install --no-cache-dir langchain==0.1.16 openai==1.35.0 COPY . /app CMD ["python", "app.py"]把框架和业务代码一起打包,版本可控,问题可追溯。
4. 实操全流程:从本地开发到Kubernetes生产部署
4.1 本地开发:用docker-compose构建闭环环境
我们不用docker run单启容器,而是用docker-compose.yml模拟生产环境拓扑。以下是我们标准开发环境的docker-compose.yml:
version: '3.8' services: jupyter: image: jupyter/scipy-notebook:latest ports: - "8888:8888" volumes: - ./notebooks:/home/jovyan/work/notebooks - ./data:/home/jovyan/work/data environment: - JUPYTER_TOKEN= - JUPYTER_ENABLE_LAB=yes depends_on: - qdrant - ollama qdrant: image: qdrant/qdrant:v1.9.2 ports: - "6333:6333" - "6334:6334" # gRPC port volumes: - ./qdrant_data:/qdrant/storage command: ["--storage-type", "disk"] ollama: image: ollama/ollama:0.1.36 ports: - "11434:11434" volumes: - ./ollama_models:/root/.ollama command: ["ollama", "serve", "--host", "0.0.0.0:11434"]启动后验证:
- 访问
http://localhost:8888,新建notebook - 执行
!curl http://qdrant:6333/readyz,返回{"status":"ok"} - 执行
!curl http://ollama:11434/api/tags,查看已加载模型
这个compose环境完全复现了生产环境的服务发现逻辑(用service名当host),避免了“本地能跑,上K8s就挂”的经典问题。
4.2 CI/CD流水线:GitHub Actions自动化构建
我们用GitHub Actions实现镜像自动构建,关键是要解决多平台构建问题。客户环境有x86_64和ARM64两种服务器,所以镜像必须同时支持。
.github/workflows/build.yml核心步骤:
- name: Set up QEMU uses: docker/setup-qemu-action@v3 with: platforms: all - name: Login to Container Registry uses: docker/login-action@v3 with: registry: ${{ secrets.REGISTRY_URL }} username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build and push uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: | ${{ secrets.REGISTRY_URL }}/pytorch-cuda121:2.3.0 ${{ secrets.REGISTRY_URL }}/pytorch-cuda121:latest cache-from: type=gha cache-to: type=gha,mode=max为什么必须用QEMU?
GitHub Actions runner是x86_64,但要构建ARM64镜像,必须用QEMU模拟ARM环境。docker/setup-qemu-action会自动注册qemu-aarch64-static,让BuildKit能在x86机器上交叉编译ARM镜像。
4.3 Kubernetes部署:生产环境的最小可行配置
在K8s上部署,绝不能照搬docker-compose。以下是pytorch-inference服务的deployment.yaml精简版:
apiVersion: apps/v1 kind: Deployment metadata: name: pytorch-inference spec: replicas: 2 selector: matchLabels: app: pytorch-inference template: metadata: labels: app: pytorch-inference spec: containers: - name: inference image: your-registry/pytorch-cuda121:2.3.0 ports: - containerPort: 8000 env: - name: TORCH_COMPILE_BACKEND value: "nvprune" - name: PYTORCH_CUDA_ALLOC_CONF value: "max_split_size_mb:128" resources: limits: nvidia.com/gpu: 1 memory: 32Gi cpu: "4" requests: nvidia.com/gpu: 1 memory: 24Gi cpu: "2" securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"]关键安全配置解释:
readOnlyRootFilesystem: true:根文件系统只读,防止恶意代码写入/tmp执行提权capabilities: drop: ["ALL"]:删除所有Linux能力,只保留必需的NET_BIND_SERVICE(需在securityContext里显式添加)allowPrivilegeEscalation: false:禁止进程提权,这是PCI-DSS合规硬性要求
4.4 镜像扫描与合规:Trivy实战
所有镜像推送到私有仓库前,必须用Trivy扫描。我们在CI流水线末尾加入:
trivy image --severity CRITICAL,HIGH --format table \ --output trivy-report.txt \ your-registry/pytorch-cuda121:2.3.0典型修复案例:
Trivy报告openssl 3.0.12有CVE-2023-3817,但nvidia/cuda:12.1.1-runtime-ubuntu22.04基础镜像里openssl是系统包,无法单独升级。解决方案是:在Dockerfile里用apt-get install -y openssl=3.0.13强制覆盖,然后apt-mark hold openssl防止后续apt-get upgrade回滚。
5. 常见问题与排查技巧实录
5.1 GPU相关问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
nvidia-smi正常,但torch.cuda.is_available()为False | 宿主机驱动版本低于镜像要求 | nvidia-smi --query-gpu=driver_version --format=csv,noheader | 升级驱动或换低版本CUDA镜像 |
CUDA out of memory即使显存充足 | PyTorch内存碎片化 | nvidia-smi --query-compute-apps=pid,used_memory --format=csv | 设置PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 |
| 多卡训练时某卡显存占用为0 | device_map未正确分配 | watch -n 1 'nvidia-smi --query-compute-apps=gpu_uuid,pid,used_memory --format=csv' | 改用device_map="balanced"而非"auto" |
5.2 网络与服务发现故障
问题:Jupyter里requests.get("http://qdrant:6333")超时
排查链路:
- 进入Jupyter容器:
docker exec -it <jupyter-id> sh - 测试DNS:
nslookup qdrant→ 应返回Qdrant容器IP - 测试连通性:
telnet qdrant 6333→ 若失败,检查Qdrant是否监听0.0.0.0:6333而非127.0.0.1:6333 - 测试防火墙:
iptables -L -n \| grep 6333→ 确保无DROP规则
根本原因:Docker默认网络是bridge模式,服务名解析依赖embedded DNS,但某些企业网络会劫持DNS请求。解决方案是在docker-compose.yml中显式配置:
networks: default: driver: bridge ipam: config: - subnet: 172.20.0.0/165.3 模型加载失败的10种可能
模型加载失败是最高频问题,按发生概率排序:
- HF_HOME路径错误:
HF_HOME指向的目录没有写权限 →chmod -R 777 $HF_HOME - 网络超时:
TRANSFORMERS_OFFLINE=0且网络不通 → 设为1并手动下载模型到$HF_HOME - CUDA版本不匹配:模型用
torch==2.3.0+cu124训练,但镜像用cu121→ 重训或换镜像 - Tokenizer不一致:
AutoTokenizer.from_pretrained()加载的tokenizer与训练时不同 → 用from_pretrained(path, local_files_only=True) - 设备映射错误:
model.to("cuda:1")但容器只挂载了--gpus device=0→ 改为model.to("cuda:0") - 内存不足:8B模型至少需12GB显存,A10G只有24GB → 用
load_in_4bit=True量化 - 文件权限:模型文件属主是root,但容器以非root用户运行 →
chown -R 1001:1001 $HF_HOME - PyTorch版本冲突:
transformers要求torch>=2.0.0,但镜像里是1.13.1→ 升级PyTorch - SSL证书错误:内网环境证书不受信 →
export SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt - CUDA_VISIBLE_DEVICES未设置:容器内看到所有GPU,但只应访问指定卡 →
docker run --gpus '"device=0,1"'
5.4 我踩过的最深的坑:Jupyter的/tmp挂载
某次客户部署,Jupyter笔记本里!pip install总是失败,报错OSError: [Errno 28] No space left on device。df -h显示磁盘充足,df -i显示inode充足。最后发现是Docker默认的/tmp是tmpfs,大小只有64MB。解决方案是在docker-compose.yml中:
jupyter: volumes: - /tmp:/tmp:sharedshared标志让宿主机和容器共享tmpfs,避免空间隔离。
注意:这个配置在macOS上不生效,因为Docker Desktop的VM机制不同。macOS用户必须用
-v /private/tmp:/tmp。
6. 经验总结:三年容器化实践的三条铁律
第一条铁律:永远不要相信“latest”标签。我见过最惨的事故是:某天早上CI流水线突然全挂,查了一上午发现jupyter/scipy-notebook:latest悄悄升级了Python版本,从3.11变成3.12,而我们的某个依赖包还没适配3.12。从此我们团队所有Dockerfile都禁用latest,必须写死jupyter/scipy-notebook:2024-04-01这样的日期标签,且每周五下午专人检查Docker Hub更新日志。
第二条铁律:镜像构建必须和模型训练环境完全一致。我们曾用pytorch/pytorch:2.3.0-cuda12.1训练模型,但推理用pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime,结果torch.compile生成的kernel在runtime镜像里找不到对应CUDA函数。解决方案是:训练和推理用同一个基础镜像,推理镜像只是在这个基础上pip install额外依赖。
第三条铁
