本文总计 2700 字,预计阅读需要 9 分钟
介绍
本篇文章将介绍 Docker 和 Kubernetes 的基础知识和要点。并以MLops的方式介绍如何 dockerize 一个简单的 ML python 应用程序,并将它部署到 Kubernetes 集群中。Docker 和 Kubernetes 是一个很大的话题所以这里只是简单的介绍。我们这里的目标是:作为数据科学家,已经创建了一个机器学习模型,使用 Streamlit 或 Python Flask 框架创建了一个应用程序,现在想要部署机器学习应用程序。所以我们将只关注 dockerfile和 Kubernetes 部署/服务 YAML 文件。并将其部署在 GCP-GKE 中。
docker 和 kubernetes 部署的工作流程:
在此之前我们先简单介绍一下Docker。
Docker 基础知识
Docker 文档中介绍:Docker 提供了在称为容器的松散隔离环境中打包和运行应用程序的能力。隔离和安全性允许在给定主机上同时运行多个容器。虽然容器是轻量级的但是容器中包含运行应用程序所需的一切,不需要依赖主机上当前安装的内容。容器是可以共享的,并共享的容器都是以相同方式工作。
Docker和虚拟机有什么区别?
Docker守护进程可以直接与主操作系统进行通信,为各个Docker容器分配资源;它还可以将容器与主操作系统隔离,并将各个容器互相隔离。由于没有臃肿的从操作系统,Docker可以节省大量的磁盘空间以及其他系统资源。虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而Docker通常用于隔离不同的应用,例如前端,后端以及数据库。
在Docker中最重要的就是dockerfile了,这里我们以构建Python应用为例做个简单的介绍:
- dockerfile是一个包含构建镜像文件分布操作和说明的文本文件。
- Docker 使用这些指令来构建这个镜像。dockerfile中可以使用 COPY、FROM、CMD 等的常用指令
- 每个 Dockerfile 都必须以 FROM 指令开头并指定一个基础镜像。
- COPY 命令将文件从本地系统复制到 Docker 映像。
- ENV 用于定义环境变量。
- RUN 告诉 Docker 要执行哪些附加命令。例如 RUN pip install -r requirements.txt - 此命令安装我们的 requirements.txt 文件中定义的所有依赖包。
- WORKDIR /mlapp - 类似于 cd 命令。它将我们的工作目录设置为 /mlapp。Expose命令指定应用程序正在侦听的端口。
- ENTRYPOINT ["python"] - 当镜像作为容器运行时执行此命令。
- CMD ["app.py"] - 此命令指定容器初始化时将执行的程序或文件。
我们会将dockerfile进行构建后的结果称为镜像,此时应用还没有运行,可以理解为我们已经将所有的环境配置好了,下一步就是通过Docker 容器运行了。
容器是 docker 镜像的实例,并且可以基于同一个 docker 镜像创建多个容器。通过使用 docker run 命令创建容器,容器就相当于是一个虚拟的系统(VM),是可读写的。
如何 dockerize 一个 PythonML 应用程序?
以Python 图像分类 ML 应用程序为例,我们要 dockerize 一个 python 图像分类机器学习应用程序并将其部署在 Kubernetes 集群中。
首先我们肯定是有一个训练好的分类模型,然后创建一个 Streamlit 应用程序。我们的目标是 使用Docker + Kubernetes 并部署在 GCP Kubernetes 集群中。这里将重点介绍创建 Streamlit 应用程序后的步骤也就是部署的步骤,流程如下:
主要的步骤如下:
- 编写 dockerfile。
- 创建docker镜像。
- 将镜像推送到存储库。
- 在 Kubernetes 中拉取镜像。
- 使用服务 yaml 配置公开端口。
- 检查 pod(Container) 是否已部署。
我们的dockerfile内容如下
# lightweight pythonFROM python:3.7-slim
RUN apt-get update
# 复制本地代码到镜像ENV APP_HOME /appWORKDIR $APP_HOMECOPY . ./
RUN ls -la $APP_HOME/
# 安装依赖RUN pip install -r requirements.txt
# 运行streamlitCMD [ "streamlit", "run","--server.enableCORS","false","app.py" ]
对于来自 Dockerfile 的每条指令或命令,Docker 都会生成一个镜像层并将其堆叠在之前的层上。因此会产生很多只读的的 Docker 镜像,这样设计的原因很多这里就不详细说了,我们只要记住他的工作流程就可以了。
这里我们选择了python:3.7-slim作为基础镜像的原因是基础镜像的大小会影响最后生成镜像的大小,因为需要构建Python应用程序,所以这个镜像就够用了,我们只要使用pip安装需要的依赖就可以了。并且他是官方镜像。
使用pip install -r requirements.txt安装python的依赖,内容如下
pytorchstreamlitpillownumpyrequests
构建镜像时不执行 CMD 行。这条命令会在容器启动时才执行。
Kubernetes
Kubernetes 是一个很大的话题, 这里只介绍最需要的。
以前所有的应用程序用于在物理服务器上运行。后来物理机被分割成了虚拟机,每一个应用都会分配到不同的虚拟机中。但是现在应用程序可以在容器中运行。
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化工作负载和服务。Kubernetes 由 Google 开发,用于管理其内部应用程序,并于 2014 年将其作为开源项目提供给 Cloud Native Computing Foundation (CNCF)。Kubernetes 是用 golang 开发的。我们可以直接将docker 创建镜像部署在 Kubernetes 容器中。
Kubernetes 集群是物理服务器、虚拟机等机器的集合。Kubernetes 集群中的机器通常称为节点。节点有两种类型——主节点和工作节点。
主节点Kubernetes集群的大脑,运行着的Daemon服务包括kube-apiserver、kube-scheduler、kube-controller-manager、etcd和Pod网络(例如flannel)。kube-apiserver这是集群中唯一跟我们交互的部分,而kube-scheduler决定在哪些节点上使用 pod。
根据谷歌文档,Pods是Kubernetes中最小、最基本的可部署对象。Pod表示集群中正在运行的进程的单个实例。pod包含一个或多个容器,例如Docker容器。当一个Pod运行多个容器时,容器被管理为一个单独的实体并共享Pod的资源。通常情况下,在单个Pod中运行多个容器是一种高级用例所以我们可以只关注一个Docker容器,这样就可以直接理解为,docker中的容器就是Kubernetes中的pod。
将MLapp部署到Kubernetes集群
在Kubernetes集群中部署pod需要创建YAML配置文件,并使用命令行将它提交给kube-apiserver,kube-scheduler会根据我们配置集群中申请资源部署POD。
我们需要将上一步dockerfile创建的镜像部署到集群中,这里使用GCP,YAML文件内容如下:
apiVersion: apps/v1kind: Deploymentmetadata:name: clsappspec:replicas: 2selector:matchLabels:app: imageclassifiertemplate:metadata:labels:app: imageclassifierspec:containers:-name: cv-appimage: gcr.io/project-11111/app:v1ports:-containerPort: 8501
第1行apiVersion使用的是apps/V1。
第2行kind我们使用Deployment,后面还会有不同的类型。
第3行和第4行这里填写app的名称。
第5行就是配置的细节:部署完全围绕着需要多少pod(副本)以及如何部署pod。这里我配置2个副本。
第16行指定容器名字。
第17行容器镜像的位置。因为使用GCP所以地址也是gcp的。
第19行docker容器的对外服务端口8501。
最后使用Kubectl命令提交YAML文件
kubectlapply-fdeploy.yml
通过以下命令检查部署状态:
kubectlget deploy imgclass
kubectldescribe deploy imgclass
kubectlget rs
在部署完成后还需要进行最后一步,配置服务并暴露对外的端口
apiVersion: v1kind: Servicemetadata:name: imageclassifierspec:type: LoadBalancerselector:app: imageclassifierports:-port: 80targetPort: 8501
这里的kind我们使用Service就是告诉Kubernetes,新建了一个对外的服务,这个服务监听80端口,并将80端口的访问发送到端口8501的Pod,可以理解为将Docker的8501端口映射到Kubernetes集群的80端口。
#部署服务kubectlapply -f service.yml#检查kubectlget svc imageclassfier
说明:Deployment 为部署无状态应用,service ,endpoint, ingress 为服务类型。一次deployment为一次部署,当外部访问服务器时,首先访问Kubernetes服务对外提供的接口端口,通过这一接口再将请求workload到每台主机上,每台主机上都有相对应的NodePort端口来接受这一请求,每个service服务上都有一个ClusterIP,通过ClusterIP和每台主机上的IPTables表,service将请求workload到具体的每个pod上。
总结
Kubernetes越来越受欢迎并已经成为大多数公司使用的默认工具。如果你想成为一名数据科学家或机器学习工程师,学习docker和Kubernetes对工作是很有帮助的,我们不需要知道如何安装他们,只需要知道一些常用的命令,并且知道如何使用它部署我们的应用就可以了。