本文演示了Gitlab上CI/CD的基本配置。持续集成(Continous Integration)和持续开发(Continous Development)是描述从开发到生产环境的几个变化的端到端过程的术语。CI/CD 自动执行代码集成的所有工作,例如集成测试、单元测试和回归测试,以及具有一组预定义标准的部署过程。因此,CI/CD 减少了人工工作量,以保持软件的质量。
本文重点介绍推理部分,你可能需要为模型开发部分添加更多层。但是,它几乎是相同的过程。
先决条件
请阅读这些资料以掌握 CI/CD 的概念
https://docs.gitlab.com/ee/ci/
https://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning
- 在 gitlab.com 创建 Gitlab 帐户
- 创建用于Web api部署的 fly.io帐户(http://fly.io/)
- 在 gitlab.com 上创建存储库
- 克隆存储库
运行git clone git@gitlab.com:<your-username>/iris-api.git
- 构建一个简单的 RESTFUL API
这是该项目的文件树
创建src/main.py
"""Iris Web API Service."""
from fastapi import FastAPI
from pydantic import BaseModel
import numpy as np
from src import distance, iris
dataset = iris.get_iris_data()
app = FastAPI()
class Item(BaseModel):
"""Input class for predict endpoint.
Args:
BaseModel (BaseModle): Inherited from pydantic
"""
sepal_length: float
sepal_width: float
petal_length: float
petal_width: float
@app.get("/")
def homepage():
"""Homepage for the web.
Returns:
str: Homepage
"""
return "Homepage Iris Flower - tags 0.0.2"
@app.post("/predict/")
async def predict(item: Item):
"""Predict function for inference.
Args:
item (Item): dictionary of sepal dan petal data
Returns:
str: predict the target
"""
sepal_length = item.sepal_length
sepal_width = item.sepal_width
petal_length = item.petal_length
petal_width = item.petal_width
data_input = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
result = distance.calculate_manhattan(dataset, data_input)
return result
创建src/iris.py
"""Load iris dataset from scikit-learn."""
from sklearn import datasets
def get_iris_data():
"""Load iris dataset.
Returns:
set: consists of X, y, feature names, and target_names
"""
iris = datasets.load_iris()
x_data = iris.data
y_label = iris.target
features_names = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
target_names = iris.target_names
return x_data, y_label, features_names, target_names
if __name__ == "__main__":
x_data, y_label, features, target_names = get_iris_data()
print("X", x_data)
print("y", y_label)
print("features", features)
print("target_names", target_names)
创建src/distance.py
"""Distance module for calculating distance between data input and dataset."""
import numpy as np
def calculate_manhattan(iris_data: np.ndarray, input_data: np.ndarray):
"""Calculate the distance between 2 vectors using manhattan distance.
Args:
dataset (np.ndarray): Iris dataset
input_data (np.ndarray): 1x4 matrix data input
Returns:
string: Return prediction
"""
x_data, y_label, _, target_names = iris_data
distance = np.sqrt(np.sum(np.abs(x_data - input_data), axis=1))
distance_index = np.argsort(distance)
y_pred = target_names[y_label[distance_index[0]]]
return y_pred
if __name__ == "__main__":
dataset = [
np.array([[4.9, 3.0, 1.4, 0.2], [4.9, 3.0, 1.4, 0.9]]),
[0, 0],
["sepal_length", "sepal_width", "petal_length", "petal_width"],
["setosa", "versicolor", "virginica"],
]
sample_data = np.array([[4.9, 3.0, 1.4, 0.2]])
print(calculate_manhattan(dataset, sample_data))
创建test/test_distance.py
import numpy as np
from src.iris import get_iris_data
from src.distance import calculate_manhattan
def test_calculate_manhattan():
dataset = get_iris_data()
input_data = np.array([[4.9, 3.0, 1.4, 0.2]])
result = calculate_manhattan(dataset, input_data)
assert result == 'setosa'
创建 Dockerfile
FROM python:3.10
EXPOSE 8000
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
ENTRYPOINT ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
设置requirements.txt
# python
pydoclint>=0.0.10
pylint>=2.17.0
black>=22.6.0
pydocstyle>=6.1.1
pytest>=7.1.2
# web app
fastapi>=0.98.0
uvicorn>=0.22.0
# models
numpy>=1.21.6
scikit-learn>=1.2.2
创建fly.toml
app = "iris-api-demo-stg"
primary_region = "sin"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 8000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
在 Fly.io 上设置 Web 应用程序
首先,请按照此链接在你的计算机上安装flyctl :https://fly.io/docs/hands-on/install-flyctl/
然后,进行身份验证flyctl auth login
然后在此处为 Gitlab 创建个人访问令牌:https://fly.io/user/personal_access_tokens
将令牌保存到记事本中,稍后我们会将令牌添加到 gitlab 环境中。
现在,你需要创建 2 个应用程序:staging 和 production
Staging app
flyctl launch --auto-confirm --copy-config --dockerfile Dockerfile --name iris-api-demo-stg --now --org personal --region sin
Production app
flyctl launch --auto-confirm --copy-config --dockerfile Dockerfile --name iris-api-demo--now --org personal --region sin
最终,你将在 Fly.io 仪表板中看到类似这样的内容
不要忘记将访问令牌 Fly.io 添加到 gitlab 环境以进行部署。添加变量并将其命名为 FLY_TOKEN。
设置 CI/CD
drum-roll
现在,我们重点关注这里的主要内容,配置 CI/CD pipline。
- 让我们创建一个名为gitlab-ci.ymlv1 的新文件
image: python:latest
docker-build:
stage: build
script:
- echo "Build Docker"
code-test:
stage: test
script:
- echo "Run Code Test"
production:
stage: deploy
environment: production
script:
- echo "Deploy to fly.io"
这是一个简单的 gitlab-ci,它运行你对远程存储库所做的每一次推送。它的作用是,当你推送一个更改时,将触发 3 个作业。docker 构建、代码测试和生产。
让我们深入了解一下运作原理。
image: python:latest:意味着所有这些作业都在 python 最新版本的 docker 镜像之上运行,你可以在 docker hub(https://hub.docker.com/_/python/tags)上找到该镜像。
docker-build:作业的名称。作业的名称可以是任何名称,你可以在单个 .yml 文件中创建多个作业。
stage表示该工作属于哪个阶段。CI/CD 管道中有 3 个常见阶段:构建、测试和部署。
environment用于指定该作业将运行的环境。你将获得具有特定环境的作业列表。这允许你部署要重新部署的提交。因此,如果在staging 或 production中出现问题,这将使问题更容易解决。
script允许你在容器中编写 shell 命令。想象一下一组脚本将在终端中运行。
完成后:
git add gitlab-ci.yml
git commit -m "add gitlab-ci.yml
git push
然后,你可以导航到管道选项卡
正如你所看到的,有 3 个绿色复选标记,表明作业已成功运行。如果失败,图标将为红叉。
管道详细页面
现在,你已经创建了一个简单的管道。
让我们创建一个通常用于 ML API 开发的管道。
image: python:latest
code-check:
stage: build
only:
- merge_requests
script:
- echo "Build Docker"
- pip install -r requirements.txt
- pylint src --rcfile=.pylintrc
- black src --check
- pydocstyle src
code-test:
stage: test
only:
- merge_requests
script:
- echo "Run Code Test"
- pip install -r requirements.txt
- pytest
staging:
stage: deploy
environment: staging
only:
- staging
script:
- echo "Deploy to fly.io in staging environment"
- curl -L https://fly.io/install.sh | sh
- bash
- /root/.fly/bin/flyctl deploy --app iris-api-demo-stg --access-token $FLY_TOKEN
production:
stage: deploy
environment: production
only:
- tags
script:
- echo "Deploy to fly.io in production environment"
- curl -L https://fly.io/install.sh | sh
- /root/.fly/bin/flyctl deploy --app iris-api-demo --access-token $FLY_TOKEN
我们有4个作业:
- code-check:此作业运行代码质量检查,例如使用pylint进行代码检查、使用black进行格式化和使用pydocstyle进行文档字符串检查。这旨在确保编写的代码符合指南。此作业仅在合并请求时运行。如果你只是推送到远程分支,它不会触发此作业。
- code-test:接下来是代码测试,我们已经在test_main.py中创建了一个简单的单元测试,以确保我们创建的模块按预期运行。
- staging:如果合并请求已被批准并合并到staging分支中,此作业将运行。它将自动部署到fly.io,使用staging应用程序。这允许你进行用户验收测试
- production:最后,我们有一个production作业。其目的与staging类似。如果你在存储库中创建了一个标签,将触发此作业。
创建用于部署到生产 Web 应用程序的标签
创建合并请求并合并到暂存分支后。它将部署到临时应用程序。如果符合预期,你可以继续将请求合并到主分支,然后批准。完成后,你可以创建标签以部署到生产 Web 应用程序中。
结论
这就是如何在Gitlab上设置CI/CD的指南。这可能看起来很简单,我将创建越来越复杂的管道,涉及 MLOps,例如模型跟踪、数据版本控制、模型注册、模型监控等。