跳至主要内容

如何使用 Docker 将应用容器化

学习如何使用 Docker 和 Kubernetes 将机器学习应用容器化。本指南适合初学者,带您在生产环境中构建、部署和扩展容器化的机器学习模型。
· 15分钟 阅读

使用 AI 探索

随着机器学习应用日益复杂,将模型从开发推向生产不再只是写好代码那么简单。关键在于确保应用能在不同环境中一致运行。这正是容器化大显身手的地方。但究竟什么是容器化?为什么它能为机器学习工作流带来颠覆性改变?

容器化是一种轻量、可移植的方式,它将应用及其所有依赖、库与配置打包到称为“容器”的单一单元中。与传统虚拟机(VM)不同,容器共享主机系统的操作系统,从而更快、更高效,也更易扩展。 

对数据科学家和机器学习工程师而言,这意味着您可以在本地机器上构建模型,并将其部署到任意位置——无论是云平台、服务器,还是同事的笔记本电脑——而无需担心兼容性问题。

在本教程中,我们将使用 Docker 和 Kubernetes 将一个机器学习应用容器化。完成后,您将掌握:

  1. 为您的 ML 应用构建 Docker 容器。
  2. 使用 Kubernetes 部署并管理容器化应用。
  3. 在生产环境中扩展并监控 ML 应用。

如果您是 Docker 新手,请查看我们的指南:从零开始学习 Docker。您也可以学习我们的课程:使用 Docker 和 Kubernetes 的容器化与虚拟化。  

先决条件

在开始之前,您应具备 Python、机器学习工作流以及命令行的基础知识。建议熟悉Docker 概念(如镜像、容器、基础命令),而Kubernetes 的基础知识可选,但对部署与扩展很有帮助。 

什么是容器化应用?

容器化应用是将软件应用与其所有依赖、库和配置一起打包到一个轻量的单元(容器)中。对于机器学习应用,这意味着将从已训练的模型到所需的 Python 库全部打包,确保您的应用在任何地方都以相同方式运行。

Traditional application versus Containerized application

传统应用 vs 容器化应用

为什么容器化对机器学习应用很重要

在开发机器学习应用时,您很可能遇到过“只在我机器上能跑”的问题。也许您的模型在笔记本上运行完美,但部署到云端就失败;或者同事因安装了不同版本的 scikit-learn 或 TensorFlow 而无法复现您的结果。 

容器化通过为您的应用创建一致且隔离的环境来解决这些问题。以下是容器化应用在机器学习中的优势:

  1. 一致性:从开发到测试再到生产,应用在不同环境中的行为保持一致。
  2. 隔离性:每个容器在其独立环境中运行,避免不同应用或依赖之间的冲突。
  3. 可移植性:容器可在任何支持容器化的平台上运行,无论是本地机器、云服务还是本地服务器。
  4. 可扩展性:需要处理更多请求?通过运行多个实例,容器可轻松实现水平扩展。

容器 vs 虚拟机

容器和虚拟机(VM)都用于隔离应用,但在运行方式与适用场景上有显著差异。以下是关键区别与优势概览:

方面

容器

虚拟机(VM)

架构

共享主机 OS 内核;轻量且相互隔离。

包含完整 OS;运行在虚拟机监控器之上。

性能

轻量、启动快、资源开销低。

更重、启动慢,因完整 OS 带来更高资源占用。

可移植性

高度可移植;跨环境表现一致。

受 OS 依赖影响,移植性较差,可能遇到兼容性问题。

可扩展性

可借助 Kubernetes 等编排工具轻松扩展。

可扩展,但需要更多资源与管理投入。

安全性

依赖主机 OS 安全性;隔离度低于 VM。

因独立 OS 带来更强隔离;更适合敏感应用。

对于机器学习应用,这些差异尤为重要。当您需要扩展模型以处理更多预测请求,或想部署多个版本进行 A/B 测试时,容器能提供所需的敏捷性与资源效率。

想进一步了解容器化与虚拟化,请查看该学习路径:使用 Docker 和 Kubernetes 的容器化与虚拟化。

如何将机器学习应用容器化 

在下文中,我们将按步骤介绍如何将应用容器化: 

1. 环境准备

在开始将机器学习应用容器化之前,需要安装必要工具并准备环境。本节将指导您安装 Docker,并准备一个简单的机器学习应用以进行容器化。

所需工具

要将机器学习应用容器化,您需要以下工具:

  1. Docker:用于构建、分发和运行容器的平台。
  2. Kubernetes (可选):用于大规模管理容器的编排工具。(我们将在后文介绍。)

先从安装 Docker 开始。

步骤 1:安装 Docker

Docker 可用于 Windows、macOS 和 Linux。以下是在您的系统上安装 Docker 的步骤。

Windows/macOS:

  1. 前往Docker 官网。
  2. 下载并安装 Docker Desktop。
  3. 按照安装向导的提示完成安装。

Linux(Ubuntu/Debian):

打开终端并运行以下命令安装 Docker:

# Update your package list
sudo apt-get update

# Install required packages to allow apt to use a repository over HTTPS
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common

# Add Docker’s official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Add the Docker repository to your system
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Update your package list again
sudo apt-get update

# Install Docker
sudo apt-get install -y docker-ce docker-ce-cli containerd.io

# Verify Docker installation
sudo docker --version

该安装过程会将 Docker 软件源添加到系统,使用 GPG 密钥验证其真实性,并安装 Docker 引擎及其命令行工具。

步骤 2:验证 Docker 安装 

安装完成后,在命令行中运行以下命令验证是否正常工作:

# Check Docker version
docker --version

# Run hello-world container
docker run hello-world

这将拉取一个轻量级测试镜像并以容器形式运行。如果成功,您会看到一条欢迎信息,确认安装正常,如下所示:

Hello from Docker!

2. 准备您的机器学习应用

现在已安装 Docker,接下来准备一个简单的机器学习应用进行容器化。本文将使用一个基础的 Python 脚本,它加载一个预训练模型并通过 Flask API 提供预测服务。

步骤 1:创建一个简单的 ML 应用

为您的项目创建一个新目录,并添加以下文件:

  1. app.py:通过 Flask API 提供预测的 Python 脚本。
  2. requirements.txt:列出 Python 依赖的文件。

以下是 app.py 的代码:

# Import required libraries
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from flask import Flask, request, jsonify
import numpy as np

# Initialize Flask app
app = Flask(__name__)

# Load and prepare the model
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)

@app.route('/predict', methods=['POST'])
def predict():
    # Get features from request
    features = request.json['features']
    
    # Make prediction
    prediction = model.predict([features])
    
    return jsonify({'prediction': int(prediction[0]),
                   'class_name': iris.target_names[prediction[0]]})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

该代码加载 Iris 数据集,训练随机森林分类器,并设置一个 Flask API 端点来接收特征值并返回预测。我们将其构建为 Web 服务,以便更适合容器化。

步骤 2:创建requirements.txt

requirements.txt 文件列出脚本运行所需的 Python 库。与 app.py 位于同一目录下创建该文件:

# requirements.txt
scikit-learn==1.3.0
numpy==1.24.3
flask==2.0.1

这指定了我们的应用所需的 Python 包的确切版本。固定版本可确保容器化应用的一致性与可复现性。

现在环境已就绪,ML 应用也准备完毕,我们可以开始构建第一个 Docker 容器。下一节将编写Dockerfile,并为基于 Flask 的 ML 应用构建 Docker 镜像。

3. 构建您的第一个 Docker 容器

现在机器学习应用已就绪,下一步是使用 Docker 将其容器化。这需要创建一个 Dockerfile,它是一份包含构建 Docker 镜像指令的脚本。镜像构建完成后,我们即可将其作为容器运行。

Dockerfile 示例:编写 Dockerfile

Dockerfile 是一份文本文件,包含组装镜像所需的全部命令。以下是为我们的 Flask 型 ML 应用创建 Dockerfile 的方法。

  1. 在与 app.py 和 requirements.txt 同一目录下,创建名为 Dockerfile 的新文件(无扩展名)。
  2. 将以下内容添加到 Dockerfile 中:
# Use an official Python runtime as the base image
FROM python:3.9-slim

# Set the working directory in the container
WORKDIR /app

# Copy the requirements file into the container
COPY requirements.txt .

# Install the Python dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code into the container
COPY . .

# Expose port 5000 for the Flask app
EXPOSE 5000

# Define the command to run the Flask app
CMD ["python", "app.py"]

说明:

  • FROM python:3.9-slim:使用官方 Python 3.9 slim 镜像作为基础镜像。slim 版本轻量,适合容器化应用。
  • WORKDIR /app:将容器内的工作目录设为/app。后续命令均在该目录下运行。
  • COPY requirements.txt .:将本地的 requirements.txt 复制到容器中。
  • RUN pip install --no-cache-dir -r requirements.txt:安装 requirements.txt 中列出的 Python 依赖。使用 --no-cache-dir 可避免缓存从而减小镜像体积。
  • COPY . .:将本地当前目录的所有文件复制到容器的 /app 目录。
  • EXPOSE 5000:暴露 Flask 应用监听的 5000 端口。
  • CMD ["python", "app.py"]:定义容器启动时运行 Flask 应用的命令。

构建并测试 Docker 镜像

编写好 Dockerfile 后,我们就可以构建镜像并以容器形式运行。 

步骤 1:构建 Docker 镜像

在终端运行以下命令构建 Docker 镜像:

docker build -t ml-flask-app .

该命令会基于当前目录下的 Dockerfile 构建镜像。标志 -t ml-flask-app 为镜像打上 ml-flask-app 的标签,便于后续引用。

步骤 2:运行 Docker 容器

镜像构建完成后,使用以下命令将其作为容器运行:

docker run -p 5000:5000 ml-flask-app

该命令基于 ml-flask-app 镜像启动一个容器。标志 -p 5000:5000 将本机 5000 端口映射到容器内 5000 端口,便于通过浏览器或 curl 等工具访问 Flask 应用。

步骤 3:测试容器化应用

打开新的终端窗口,向容器内运行的 Flask 应用发送测试请求:

curl -X POST -H "Content-Type: application/json" -d '{"input": [5.1, 3.5, 1.4, 0.2]}' http://localhost:5000/predict

您将看到类似如下的响应:

{
  "prediction": 0
}

这意味着容器化的 Flask 应用已成功运行,可对外提供预测服务。

调试常见问题

如果在构建或运行容器时遇到问题,以下建议可帮助您排查:

查看 Docker 日志:使用以下命令查看运行中容器的日志:

docker logs <container_id>

验证 Dockerfile 语法:仔细检查 Dockerfile 是否有拼写或语法错误。

检查端口冲突:确保本机 5000 端口未被占用。如已占用,可将容器映射到其他端口,例如:

docker run -p 5001:5000 ml-flask-app

重新构建镜像:

如修改了 Dockerfile 或应用代码,请使用以下命令重新构建:

docker build -t ml-flask-app .

在成功构建并测试 Docker 容器后,下一步是学习如何管理和扩展容器化应用。接下来我们将探索如何使用 Kubernetes 部署并扩展 ML 应用。

想获取更多 Docker 项目,请查看我们的指南:10 个 Docker 项目灵感:从入门到进阶。

运行您的容器化应用

既然我们已构建好 Docker 容器,接下来看看如何有效管理它。本节将带您了解启动、停止和检查容器的常用 Docker 命令,以及如何处理日志和端口。

使用 Docker 管理容器

Docker 提供了一组强大的命令来管理容器。让我们了解最常用的命令。

启动容器

要从镜像启动一个容器,使用 docker run 命令。例如:

docker run -p 5000:5000 ml-flask-app

该命令基于 ml-flask-app 镜像启动容器,并将本机 5000 端口映射到容器 5000 端口。

如果希望在后台运行容器(分离模式),请添加 -d 标志:

docker run -d -p 5000:5000 ml-flask-app

这样容器会在后台运行,释放您的终端。

停止容器

要停止一个运行中的容器,先使用以下命令获取容器 ID:

docker ps

该命令会列出所有运行中的容器及其 ID、名称和其他信息。获得容器 ID 后,使用如下命令停止容器:

docker stop <container_id>

例如:

docker stop a1b2c3d4e5f6

检查容器

要查看容器的详细信息,使用 docker inspect 命令:

docker inspect <container_id>

它将输出 JSON 格式的详情,如容器的网络设置、挂载卷和环境变量等。

列出容器

要查看所有容器(包括运行中与已停止),使用:

docker ps -a

这有助于检查容器状态并查找容器 ID。

处理日志和端口

日志与端口对调试和访问容器化应用至关重要。让我们看看如何使用它们。

查看容器日志

要查看运行中容器的日志,使用:

docker logs <container_id>

例如:

docker logs a1b2c3d4e5f6

该命令会显示应用的输出,包括任何错误或控制台信息。若希望实时跟随日志(类似于 tail -f),可添加 -f 标志:

docker logs -f <container_id>

端口映射

运行容器时,可以使用 -p 标志将容器端口映射至本机。例如:

docker run -p 5000:5000 ml-flask-app

该命令将容器内的 5000 端口映射到本机 5000 端口。如果 5000 端口已被占用,可映射到其他端口,例如:

docker run -p 5001:5000 ml-flask-app

现在,您的 Flask 应用可通过 http://localhost:5001 访问。

检查端口绑定

要查看运行中容器的端口映射,使用:

docker port <container_id>

该命令会列出容器的端口映射,有助于排查连通性问题。下一节我们将介绍如何在 Kubernetes 上部署容器化的 ML 应用并实现规模化管理。

借助 Kubernetes 扩展

应用容器化后,我们可以使用Kubernetes 进行部署。Kubernetes 能自动化容器化应用的部署、扩展与管理,使处理大规模工作负载更轻松。本节我们将在 Kubernetes 集群上部署我们的 ML 应用。

在 Kubernetes 上部署容器

Kubernetes 使用 YAML 文件来定义应用的期望状态。这些文件描述容器应如何部署、扩展与管理。先为基于 Flask 的 ML 应用创建一个 Kubernetes 部署。

步骤 1:编写 Kubernetes 部署 YAML 文件

创建名为 ml-flask-app-deployment.yaml 的文件,并填入以下内容:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ml-flask-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ml-flask-app
  template:
    metadata:
      labels:
        app: ml-flask-app
    spec:
      containers:
      - name: ml-flask-app
        image: ml-flask-app
        ports:
        - containerPort: 5000

该 YAML 文件定义了:

  • 一个名为 ml-flask-app 的 Deployment。
  • 三个副本,确保高可用性。
  • 使用我们之前构建的 ml-flask-app 镜像的容器。
  • 容器端口 5000,即 Flask 应用监听的端口。

步骤 2:部署应用

在 Kubernetes 上部署应用,使用 kubectl apply 命令:

kubectl apply -f ml-flask-app-deployment.yaml

该命令会指示 Kubernetes 创建 YAML 文件中定义的资源。您可以使用以下命令查看部署状态:

kubectl get deployments

该命令会显示部署状态,包括正在运行的副本数。

步骤 3:暴露应用

要使应用可从 Kubernetes 集群外部访问,需要创建 Service。创建名为 ml-flask-app-service.yaml 的文件,并填入以下内容:

apiVersion: v1
kind: Service
metadata:
  name: ml-flask-app-service
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 5000
  selector:
    app: ml-flask-app

该 YAML 文件定义了:

  • 名为 ml-flask-app-service 的 Service。
  • LoadBalancer 类型,用于将应用暴露到互联网。
  • 将外部 80 端口映射到内部 5000 端口。

使用以下命令应用 Service:

kubectl apply -f ml-flask-app-service.yaml

您可以用以下命令查看 Service 状态:

kubectl get services

该命令会显示应用可访问的外部 IP 地址。

扩展与监控

Kubernetes 让应用扩展和性能监控变得轻松。让我们来看具体做法。

扩展应用

要扩展应用,可更新部署中的副本数量。例如将副本数扩展到 5,运行:

kubectl scale deployment ml-flask-app --replicas=5

您也可以在 YAML 文件中更新 replicas 字段并重新应用:

spec:
  replicas: 5

然后运行:

kubectl apply -f ml-flask-app-deployment.yaml

监控应用

Kubernetes 提供多种工具监控我们的应用。下面看看常用工具。

使用 kubectl logs 查看特定 Pod 的日志:

kubectl logs <pod_name>
 

使用 kubectl get pods 检查 Pod 状态:

kubectl get pods

使用 kubectl top 查看 CPU 与内存使用:

kubectl top pods

自动扩缩容

Kubernetes 还支持 Horizontal Pod Autoscaler(HPA),可基于 CPU 或内存使用率自动调整 Pod 数量。启用自动扩缩容:

kubectl autoscale deployment ml-flask-app --cpu-percent=50 --min=3 --max=10

这将确保部署根据 CPU 使用率在 3 到 10 个副本间自动伸缩。

Kubernetes 部署最佳实践

在 Kubernetes 上部署应用时,建议遵循以下最佳实践。

使用资源限额

为容器定义 CPU 和内存限制,防止资源耗尽:

resources:
   limits:
     cpu: "1"
     memory: "512Mi"

使用存活与就绪探针

添加健康检查,确保应用正确运行:

livenessProbe:
  httpGet:
    path: /health
    port: 5000
  initialDelaySeconds: 5
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /ready
    port: 5000
  initialDelaySeconds: 5
  periodSeconds: 10
   

使用命名空间

使用命名空间组织资源,避免冲突:

kubectl create namespace ml-app
kubectl apply -f ml-flask-app-deployment.yaml -n ml-app

下一节我们将介绍优化 Dockerfile、管理依赖以及确保安全性的技巧。

容器化 ML 应用的最佳实践

对机器学习应用进行容器化会面临一些挑战,比如大型依赖的管理、可复现性的保证以及安全维护。以下最佳实践可帮助您应对这些挑战,构建健壮的容器化 ML 应用。

优化 Dockerfile

优化良好的 Dockerfile 能显著缩短构建时间并减小镜像体积。以下是一些优化技巧:

使用多阶段构建

多阶段构建允许我们在单个 Dockerfile 中使用多个 FROM 语句。它通过丢弃中间阶段中不必要的文件和依赖来减小最终镜像体积。例如:

# Stage 1: Build the application
FROM python:3.9-slim as builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt

COPY . .

# Stage 2: Create the final image
FROM python:3.9-slim

WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .

ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["python", "app.py"]

该 Dockerfile:

  • 使用 builder 阶段安装依赖并构建应用。
  • 仅将必要文件(如已安装依赖和应用代码)复制到最终镜像,从而减小体积。

减少层数

Dockerfile 中的每条指令都会创建一个新层。要减少层数并减小镜像体积:

  • 将多个 RUN 命令用 && 合并为一个命令。
  • 优先使用 COPY 而非 ADD,除非确实需要 ADD 的额外功能。

例如:

RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential && \
    rm -rf /var/lib/apt/lists/*

使用轻量级基础镜像

选择轻量级基础镜像,如 python:3.9-slim,而非更大的 python:3.9。slim 镜像仅包含必要包,可有效减小体积。

管理依赖与可复现性

可复现性对机器学习工作流至关重要。以下是管理依赖并确保可复现性的方法。

使用 requirements.txt 或 pipen

将所有 Python 依赖列在 requirements.txt 文件中,或使用 pipenv 进行管理。例如:

# requirements.txt
scikit-learn==1.3.0
numpy==1.24.3
Flask==2.3.2

在 Dockerfile 中安装依赖:

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

固定依赖版本

始终固定依赖版本以避免意外变动。例如:

scikit-learn==1.3.0
numpy==1.24.3
Flask==2.3.2

使用环境变量进行配置

用环境变量进行应用配置,而非硬编码。这样能让容器在不同环境中更灵活、更易配置。例如:

ENV MODEL_PATH=/app/models/model.pkl

然后在应用中访问该环境变量:

import os
model_path = os.getenv('MODEL_PATH')

安全性考量

安全是容器化应用的关键。以下是保护 ML 应用的最佳实践:

避免以 root 身份运行

以 root 用户运行容器会带来安全风险。应创建非 root 用户,并以该用户运行容器。例如:

# Create a non-root user
RUN useradd -m myuser

# Switch to the non-root user
USER myuser

# Set the working directory
WORKDIR /home/myuser/app

# Copy the application code
COPY --chown=myuser:myuser . .

# Run the application
CMD ["python", "app.py"]

扫描镜像漏洞

使用 Trivy 或 Clair 等工具扫描 Docker 镜像中的漏洞。例如使用 Trivy 扫描镜像:

trivy image ml-flask-app

安全地管理机密

切勿在 Dockerfile 或应用代码中硬编码敏感信息(如 API 密钥或数据库凭据)。应使用 Kubernetes Secrets 或 Docker Secrets 管理敏感数据。例如,在 Kubernetes 中:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  api_key: <base64-encoded-api-key>

然后在部署中将该机密作为环境变量挂载:

env:
- name: API_KEY
  valueFrom:
    secretKeyRef:
      name: my-secret
      key: api_key

测试与调试

测试与调试对确保容器化应用按预期运行至关重要。以下是一些建议:

本地测试

在部署到 Kubernetes 之前,使用 Docker 在本地测试容器。例如:

docker run -p 5000:5000 ml-flask-app

使用日志进行调试

使用 docker logs 或 kubectl logs 调试容器化应用中的问题。例如:

docker logs <container_id>
kubectl logs <pod_name>

自动化测试

使用 CI/CD 流水线自动化容器化应用的测试与部署。GitHub Actions、GitLab CI 或 Jenkins 等工具可帮助我们搭建自动化工作流。

遵循这些最佳实践,我们可以构建高效、可复现且安全的容器化机器学习应用。从优化 Dockerfile 到管理依赖与确保安全,这些策略将帮助我们简化 ML 工作流,并自信地部署模型。

结语

使用 Docker 和 Kubernetes 对机器学习应用进行容器化,可简化部署、确保可复现性并增强可扩展性。通过遵循优化 Dockerfile、管理依赖与重视安全等最佳实践,我们能够构建健壮、高效且安全的 ML 工作流。

若想继续学习容器化并强化您的机器学习工作流,请查看以下课程:

容器化 ML 应用常见问答

机器学习模型必须容器化吗?

不是必须,但它能让部署、扩缩容和版本控制更简单、更可靠。

我可以在不停止容器的情况下更新 ML 模型吗?

 可以。您可以实施滚动更新,在不停机的情况下用新模型版本替换容器。

多个 ML 模型可以在同一个容器中运行吗?

可以,但更好的做法是让每个模型在独立容器中运行,以便更好地管理资源。

Docker 和容器有什么区别?

Docker 是创建和管理容器的平台;容器则是实际打包好的应用。

运行容器化应用一定要用 Kubernetes 吗?

不需要。仅运行容器用 Docker 就足够;只有在需要管理多容器扩展时才需要 Kubernetes。

作者

Rajesh Kumar

我是一名数据科学内容写作者,热衷于创作与 AI/ML/DS 相关的内容。我也会探索新的 AI 工具并撰写相关文章。

主题