作者:王磊
更多精彩分享,欢迎访问和关注:https://www.zhihu.com/people/wldandan
在上一篇【MindSpore易点通机器人-03】迭代0的准备工作,我们从整体上概述了MindSpore易点通机器人项目开始前需要在迭代0的准备工作,本篇将会为大家讲述迭代0中具体的MLOps 环境搭建过程。整体的MLOps流水线设计如下图,包含持续训练流水线和CI/CD流水线。相关代码请参考MindSpore易点通机器人代码仓。
实际的技术选型上,我们基于Jenkins构建CI/CD流水线,基于Argo构建持续训练流水线。同时,我们把Jenkins和Argo都运行在K8S上来保证可用性和弹性。下文将为大家介绍如何在本地使用Minikube完成机器人项目的MLOps流水线的搭建,并在本地运行起来。
具体的过程如下:
- 安装配置WSL+Ubuntu+Docker;
- 基于Minikube运行K8S;
- 基于K8S+Jenkins构建CI/CD流水线;
- 基于K8S+Argo持续训练流水线。
1. 安装配置WSL+Ubuntu+Docker
因为团队大部分人的开发环境都是Windows,所以需要选择WSL+Linux+Docker的方式。这里我们没有采用Docker Desktop+WSL Backend,而是利用Distord让Docker在Ubuntu上直接运行,相关的安装配置文档可以参考如何不安装Docker Desktop在WSL下运行Docker这篇文章。
2. 基于Minikube运行K8S
Minikube是一个单机安装配置K8S集群的工具,它支持多平台(Mac/Linux/Windows)。Minikube可以将K8S集群安装配置在单个Docker容器或者VM(hyper-v/VMWare等)中,过程也非常简单。首先在Ubuntu中安装Minikube:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
然后执行minikube start
,因为在第一步中我们已经配置好了Docker,Minikube在启动时会默认使用Docker作为VM,然后在容器中启动K8S集群。启动完成后在Ubuntu上使用docker ps
只能看到一个容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
699a71fee349 kicbase/stable:v0.0.30 "/usr/local/bin/entr…" 2 weeks ago Up 2 hours 127.0.0.1:49157->22/tcp, 127.0.0.1:49156->2376/tcp, 127.0.0.1:49155->5000/tcp, 127.0.0.1:49154->8443/tcp, 127.0.0.1:49153->32443/tcp minikube
如果我们执行docker exec -it 699a71fee349
进入容器后再执行docker ps
,就能发现K8S集群的服务了:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
af45f3caa0d9 99a3486be4f2 "kube-scheduler --au…" 2 hours ago Up 2 hours k8s_kube-scheduler_kube-scheduler-minikube_kube-system_be132fe5c6572cb34d93f5e05ce2a540_1
e648e7d30a7d Error 404 (Not Found)!!1 "/pause" 2 hours ago Up 2 hours k8s_POD_kube-apiserver-minikube_kube-system_cd6e47233d36a9715b0ab9632f871843_1
e26d9e92c4e3 k8s.gcr.io/pause:3.6 "/pause" 2 hours ago Up 2 hours k8s_POD_kube-scheduler-minikube_kube-system_be132fe5c6572cb34d93f5e05ce2a540_1
e658bf17922d Error 404 (Not Found)!!1 "/pause" 2 hours ago Up 2 hours k8s_POD_kube-controller-manager-minikube_kube-system_b965983ec05322d0973594a01d5e8245_1
1f85a9bae877 Error 404 (Not Found)!!1 "/pause" 2 hours ago Up 2 hours k8s_POD_etcd-minikube_kube-system_9d3d310935e5fabe942511eec3e2cd0c_1
....
从容器中退出,安装kubectl之后就在Ubuntu上使用Kubectl管理集群了:
curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
执行kubectl get nodes
,查询K8S管理的节点。
~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 19d v1.23.3
如果关注Kubernetes的界面,使用minikube dashboard
就可以启动K8S的管理界面,并在Windows上通过浏览器访问 http://127.0.0.1:44185/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
。
使用Minikube可以让我们在本地拥有一个和生产环境一样功能的K8S集群,但这种方式同样带来了网络的复杂性,如下图,Ubuntu运行在Hyper-v的虚机中,在K8S部署的服务运行在Ubuntu的Docker容器的容器中(Docker in Docker)。所以,如果要在Windows的浏览器上访问K8S中运行的服务,需要先通过kubectl port-forward
完成Ubuntu VM和Minikube容器的端口映射,然后再使用minikube tunnel
完成VM到Windows的端口映射。
3. 基于K8S+Jenkins构建CI/CD流水线
Jenkins是一个经久不衰的持续集成工具,它的插件生态比较强大。我们构建基于Jenkins+Kubernetes的CI/CD流水线要达到的目的如下:
- 基于K8S实现Jenkins的弹性部署;
- 基于Jenkins插件实现CI任务在K8S上的运行;
- 基于Pipeline as Code实现Jenkins的流水线配置管理(持续集成任务+持续部署任务)。
基于K8S实现Jenkins的弹性部署
MindSpore易点通机器人的代码仓已经给出了在K8S上部署Jenkins的配置文件,这里要设置成LoadBalancer
类型。
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
spec:
type: LoadBalancer
selector:
name: jenkins
ports:
-
name: http
port: 8080
targetPort: 8080
protocol: TCP
再用kubectl create -n jenkins
创建Jenkins的namespace,通过kubectl apply -f jenkins.yaml -n jenkins
完成部署,然后用kubectl apply -f service-account.yaml -n jenkins
完成API调用的授权。
在配置文件中我们指定了对外暴露的端口是8080,所以可以用kubectl port-forward svc jenkins/jenkins 8080:8080 -n jenkins
完成端口映射,再执行minikube tunnel
就可以在浏览器中使用127.0.0.1:8080
打开Jenkins界面了,初始密码可以在pod启动的日志中获得。
基于Jenkins插件实现CI任务在K8S上的运行
完成Jenkins在K8S上的部署后,需要在Jenkins中安装kubernetes
插件,配置节点类型为K8S集群。从插件管理中先安装kubernetes
插件,然后在节点管理
中选择配置集群
。
首先配置K8S集群的信息,因为在一个K8S集群,服务间都可以通过主机名的方式相互访问,所以“Kubernetes地址”配置只需要输入https://kubernetes.defaults
,同时补充配置“Kubernetes命名空间”为jenkins
,如下图所示。
同理,对于“Jenkins地址”,只需要填入http://jenkins.jenkins:8080
。
另一个要配置是pod模板,既任务运行的pod的基础镜像及相关信息配置。要同时运行Java和Python,所以在dockerhub找了一个Java和Python都有的镜像,如下图所示,保存后即可完成配置。
基于Pipeline as Code实现Jenkins的流水线配置管理
我们的最后一步是基于Jenkins的流水线即代码功能完成CI/CD流水线搭建,按照设计,它应该包含如下任务:
- 持续集成流水线
- 代码检查:数据处理代码、模型代码、推理代码以及脚本代码规范检查任务
- 单元测试:数据处理逻辑、模型代码逻辑、推理代码逻辑的单元测试任务
- API测试:推理接口功能测试
- 训练触发:如果修改了训练代码,触发Argo的训练流水线
- 部署
使用Jenkins流水线即代码功能,对应的配置如下:
pipeline {
agent {
kubernetes {
containerTemplate {
name 'python'
image 'bitnami/java:1.8'
command 'sleep'
args 'infinity'
}
defaultContainer 'python'
}
}
stages {
stage('Code Check ') {
steps("Code Check") {
echo 'checking python code.'
}
}
stage('Unit Testing') {
steps("Unit Testing") {
echo "running unit tests"
}
}
stage('API Testing') {
steps {
echo 'running inference API Testing'
}
}
stage('Training Trigger') {
when {
changeset "src/train/*.py"
}
steps("trigger training") {
echo 'trigger new round of training'
}
}
}
}
部署流水线的pipeline脚本如下:
pipeline {
agent any
stages {
stage('Packaging') {
steps("Packaging") {
echo 'packaging with model and inference code'
}
}
stage('Continuous Deployment') {
steps("deploying") {
echo 'deploy new version of model'
}
}
}
}
配置文件中每个step先置空,是为了方便调试。在Jenkins基于上面的配置文件创建一个流水线后的测试结果如下:
4. 基于K8S+Argo持续训练流水线
Argo是一个基于K8S的开源的工作流管理工具,也支持机器学习的工作流。MindSpore DX Sig已经在先前的社区机器人项目中使用了该工具,所以这里我们复用了工具和配置。Argo Workflow的安装配置如下:
- 在K8S上安装Argo;
- 基于Argo Workflow 配置机器学习流水线;
- 运行机器学习流水线。
1. 在K8S上安装Argo
首先,我们执行kubectl create -n argo
为Argo创建新的命名空间。然后,基于配置文件,执行kubect apply -f install.yml -n argo
完成安装。最后,执行kubect apply -f manifests/create_serviceaccount.yaml -n argo
完成权限配置。
和前面的Jenkins配置类似,Argo Server需要设置为LoadBalancer
类型。
apiVersion: v1
kind: Service
metadata:
name: argo-server
spec:
ports:
- name: web
port: 2746
targetPort: 2746
type: LoadBalancer
sessionAffinity: None
externalTrafficPolicy: Cluster
selector:
app: argo-server
执行kubectl get svc -n argo
可以看到:
~$ kubectl get svc -n argo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
argo-server LoadBalancer 10.97.145.232 127.0.0.1 2746:30001/TCP 19d
workflow-controller-metrics ClusterIP 10.99.196.47 <none> 9090/TCP 19d
如果要在Windows的浏览器上访问Argo的Web界面,则还需要把argo-server的端口暴露出来,同上使用kubectl port-forward svc/argo-server 2746:2746 -n argo
完成端口映射。然后浏览器上访问https://127.0.0.1:2746
,通过下面的脚本获得密码,登录后就可以正常使用Argo的Web管理界面了。
#!/bin/bash
SECRET=$(kubectl get sa argo-server -n argo -o=jsonpath='{.secrets[0].name}')
ARGO_TOKEN="Bearer $(kubectl get secret $SECRET -n argo -o=jsonpath='{.data.token}' | base64 --decode)"
echo $ARGO_TOKEN
2. 基于Argo Workflow 配置机器学习流水线
我们期望训练的工作流可以完成以下任务:
- 数据处理
- 训练
- 评估
- 质量评估:基于测试集数据评估模型,预测性能需要高于基线值
- 可解释性评估
- 可靠性评估
- 总结
基于工作流的设计以及Argo工作流语法,可以得出基础的配置:
apiVersion: Page Not Found
kind: Workflow
metadata:
generateName: robot-train-eval-
spec:
serviceAccountName: robot-sa
entrypoint: robot-controller
onExit: summary
templates:
- name: robot-controller
steps:
- - name: data-process
template: process
- - name: robot-train
template: train
- - name: robot-eval
template: eval
- name: robot-interpretability
template: interpretability
- name: robot-reliability
template: reliability
- - name: summary
template: summary
而后,展开每个任务需要的配置,如训练的配置代码:
- name: train
container:
image: ubuntu
imagePullPolicy: Always
env:
- name: IS_TRAIN
value: "True"
- name: NUM_STEPS
value: "10"
command: ['echo']
args: ["trainning"]
...
把每个阶段的任务汇总在一起就完成了整个的工作流。接下来,我们尝试使用Argo运行下训练流水线。
3. 运行机器学习流水线
可以使用Argo CLI的客户端完成工作流任务的提交,首先安装客户端:
#!/bin/bash
# Download the binary
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.3.5/argo-linux-amd64.gz
# Unzip
gunzip argo-linux-amd64.gz
# Make binary executable
chmod +x argo-linux-amd64
# Move binary to path
mv ./argo-linux-amd64 /usr/local/bin/argo
然后通过argo cli提交工作流argo submit -n robot --watch robot-train-eval.yaml
,执行结果如下,和我们期望的流水线步骤一致。
总结
本篇文章总结了如何在本地完成MLOps环境的搭建,基于Minikube、Argo等工具可以让我们很好的在本地展开开发验证工作,不用依赖复杂的基础设施,也不用有额外的开销。在配置文件中和脚本中,我们没有加真实的实现,是为了先打通流程,然后再将调试好内容逐步补充进去,始终都可以从端到端的角度来完成验证。