使用Docker和Kubernetes托管机器学习模型

Docker是在云中部署ML模型的绝佳工具。如果你想在云中创建一套生产级部署,那么AWS和GCP上有很多选项可用。我正在撰写一本书,其中的第4章重点介绍用于模型服务的ECS,同时还会深入探索谷歌Kubernetes引擎。这篇文章省略了第4章关于AWS的容器部署部分,其余部分则没有删减。

可复制模型的容器

部署数据科学模型时,重点在于复现培训时的环境和用于服务的环境。容器的理念是形成一个隔离的环境,你可以在其中设置执行任务所需的依赖项。任务可以是执行ETL工作、服务ML模型、支持API或托管交互式Web应用程序。容器框架的目标是在实例之间提供隔离,同时只占用很少的资源。使用容器框架,你可以指定代码所需的依赖项,并让该框架管理不同的执行环境。Docker是容器的事实标准,人们围绕这一平台构建了大量工具。

弹性容器环境(例如弹性容器服务器——ECS)提供了与无服务器函数类似的功能,这种环境将服务器的概念从数据科学模型托管中抽象了出来。关键区别在于无服务器生态系统仅限于特定的运行时,通常具有内存限制,很难使用深度学习框架,并且是特定于云的。使用ECS,你可以设置用于服务模型的实例类型,可以使用服务模型所需的任何语言,并且想用多少内存都没问题。ECS也还是AWS的专有工具,但是诸如EKS之类的较新选项建立在开源和可移植的Kubernetes之上。

以下是我见过的一些容器数据科学用例:

  • 可重现分析:容器是打包分析的好办法,分析被打包后其他团队成员就可以在几个月或几年后重新启动你的工作。
  • Web应用程序:在第2章中,我们使用Dash构建了一个交互式Web应用程序。部署应用时,容器提供了一种很好的方式来抽象出托管事宜。
  • 模型部署:如果要将模型公开为端点,容器提供了一种很好的方法来分离模型应用程序代码与模型服务基础架构。

本章的重点是最后一个用例。我们会使用第二章中的Web端点,并将应用程序包装在一个Docker容器中。我们首先会在EC2实例上本地运行容器,然后试着使用ECS创建可扩展、支持负载平衡和容错的模型部署。接下来,我们将展示如何使用Kubernetes在GCP上获得类似的结果。

既然我们正在探索可扩展的计算环境,那么使用ECS和GKE时务必要注意云成本。对于AWS,我们应该密切关注服务了多少EC2实例;在GCP上,计费工具可很好地跟踪成本。关于编排的部分是专门针对AWS的,并且使用的方法不能迁移到其他云环境中。如果AWS环境不适合你的模型部署,请直接跳到Kubernetes的部分。

Docker

Docker和其他平台即服务工具提供了称为容器的虚拟化概念。容器在主机操作系统之上运行,但是为容器内运行的代码提供了标准化的环境。这种虚拟化方法的主要目标之一是你可以为目标环境编写代码,然后任何运行Docker的系统都可以运行你的容器。

容器是提供类似功能的虚拟机的轻量级替代方案。关键区别在于容器的启动速度要快得多,同时提供与虚拟机相同的隔离级别。另一个好处是,容器可以复用其他容器中的层,这样就可以更快地构建和共享容器。当你需要在一台机器上运行相冲突版本的Python运行时或库时,容器是一个很好的解决方案。

使用docker,你可以编写一个名为Dockerfile的文件,该文件用于定义容器的依赖项。构建Dockerfile的结果是一个Docker镜像,该镜像打包了运行应用程序所需的所有运行时、库和代码。Docker容器是运行应用程序的实例化镜像。Docker的一个很有用的功能是新镜像可以在已有镜像的基础上构建。对于我们的模型部署,我们将扩展ubuntu:latest镜像。

本节将展示:如何在EC2实例上设置Docker;编写Dockerfile来构建第2章中echo服务的镜像;使用Docker生成镜像及运行容器。要在EC2实例上安装Docker,可以使用amazon-linux-extras工具简化过程。下面的命令将安装Docker,在EC2实例上启动服务,并列出正在运行的容器,这里将返回一个空列表。

sudo yum install -y python3-pip python3 python3-setuptools
sudo yum update -y
sudo amazon-linux-extras install docker
sudo service docker start
sudo docker ps

我们要部署的应用程序是第2章中的echo服务。这个服务是Flask应用程序,它可以解析GET或POST中的msg属性,并返回一个JSON负载来回显所提供的消息。这里与之前应用程序的唯一区别是Flask应用现在运行在80端口上,如下面的echo.py代码片段的最后一行所示。

# load Flask 
import flask
app = flask.Flask(__name__)
# define a predict function as an endpoint 
@app.route("/predict", methods=["GET","POST"])
def predict():
    data = {"success": False}
    # get the request parameters
    params = flask.request.json
    if (params == None):
        params = flask.request.args
    # if parameters are found, echo the msg parameter 
    if (params != None):
        data["response"] = params.get("msg")
        data["success"] = True
    # return a response in json format 
    return flask.jsonify(data)
  
# start the flask app, allow remote connections
app.run(host='0.0.0.0', port = 80)

现在我们已经安装了Docker和要容器化的应用程序,我们需要编写一个Dockerfile来描述如何构建镜像。下面的代码片段展示了用于执行此任务的Dockerfile。第一步是使用FROM命令来标识要使用的基本镜像。ubuntu镜像提供了一个支持apt-get命令的linux环境。MAINTAINER命令将镜像维护者的名称添加到与镜像关联的元数据信息中。接下来使用RUN命令安装python,设置符号链接并安装Flask。对于具有许多Python库的容器,也可以使用requirements.txt文件。Copy命令将脚本插入镜像,并将文件放置在根目录中。最后的命令指定要执行应用程序时要运行的参数。

FROM ubuntu:latest
MAINTAINER Ben Weber  
RUN apt-get update \  
  && apt-get install -y python3-pip python3-dev \  
  && cd /usr/local/bin \  
  && ln -s /usr/bin/python3 python \  
  && pip3 install flask  
  
COPY echo.py echo.py 
ENTRYPOINT ["python3","echo.py"]

编写Dockerfile之后,你可以使用Docker提供的build命令来创建镜像。下面的代码段中展示的第一个命令,显示了如何使用文件./Dockerfile构建标记为echo_service的镜像。第二个命令显示了实例上可用的Docker镜像列表。输出将显示我们用作镜像基础的ubuntu镜像以及新创建的镜像。

sudo docker image build -t "echo_service" .
sudo docker images

要将镜像作为容器运行,我们可以使用下面的代码段中所示的run命令。-d标志指定容器应作为守护进程运行,即使终端关闭,这个守护进程仍将继续运行。-p标志会将主机上的端口映射到容器用来通信的端口上。没有这个设置,我们的容器就无法接收外部连接。ps命令显示正在运行的容器列表,这个列表现在应包括echo服务。

sudo docker run -d -p 80:80 echo_service
sudo docker ps

为了测试容器,我们可以使用之前的一套流程,当时我们在Web浏览器中使用了EC2实例的外部IP,并将msg参数传递给/predict端点。由于我们设置了从主机端口80到容器端口80的端口映射,因此我们可以在开放的Web上直接调用容器。下面的例子显示了来自echo服务容器的调用和结果。

http://34.237.242.46/predict?msg=Hi_from_docker
{"response":"Hi_from_docker","success":true}

现在,我们完成了Docker镜像的构建,并将该镜像作为容器运行在EC2实例上。尽管这一方法确实提供了隔离机器上不同服务的解决方案,但它没有提供扩展和容错功能,而这通常是生产级模型部署所必需的。

GCP上的Kubernetes

谷歌云平台提供了一项称为谷歌Kubernetes引擎(GKE)的服务,用于服务Docker容器。Kubernetes是最初由谷歌开发的容器编排系统,现已开源。这个平台的用例覆盖很大的范围,但我们将重点关注使用托管版Kubernetes托管我们echo服务的任务。

使用Kubernetes托管Docker容器的流程很像ECS,第一步是将镜像保存到可以与编排系统对接的Docker私有仓库(Registry)中。这个私有仓库服务的GCP版本称为容器仓库(Container Registry)。要将镜像从AWS上的EC2实例获取到GCP容器仓库,我们将再次使用docker login命令。为使此过程正常进行,你将需要我们在第1章中设置的GCP凭证json文件。下面的代码段显示了如何将json文件传递给docker login命令,标记该镜像以将其上传到仓库, 并将镜像推送到容器仓库。

cat dsdemo.json | sudo docker login -u _json_key 
                               --password-stdin https://us.gcr.io
sudo docker tag echo_service us.gcr.io/[gcp_account]/echo_service 
sudo docker push us.gcr.io/[gcp_account]/echo_service

你需要使用完整的谷歌帐户ID替换此脚本中的gcp_acount参数。执行完这些步骤后,echo服务镜像应显示在GCP控制台的Registry视图下,如图4.8所示。通常来说,如果你使用GCP来服务模型,则可能会使用谷歌Compute实例而不是EC2,但是最好练习在不同云平台的组件之间对接。

图4.8:GCP容器仓库上的echo镜像

与使用ECS所需的一大堆步骤相比,使用GKE托管容器的过程简化了许多。我们首先使用GCP控制台在Kubernetes上设置一个容器,然后将服务公开到开放的Web上。要部署echo服务容器,请从GCP控制台执行以下步骤:

  1. 找到并选择“Kubernetes Engine”
  2. 点击“Deploy Container”
  3. 选择“Existing Container Image”
  4. 选择“echo_service:latest”
  5. 分配应用程序名称“echo-kge”
  6. 点击“Deploy”

现在我们已经部署了Kubernetes集群,并准备提供echo服务。在GKE上部署Kubernetes集群可能需要几分钟来设置。部署完成后,你应该能在集群列表下方看到echo集群,如图4.9所示。

图4.9:通过Kubernetes部署的echo镜像

要使用该服务,我们需要通过GCP控制台执行以下步骤,将集群公开到开放的Web上:

  1. 从GKE菜单中选择你的集群
  2. 点击“Workloads”
  3. 选择“echo-gke”负载
  4. 选择“Actions”标签,然后选择“Expose”
  5. 对于服务类型,选择“load balancer”

执行完这些步骤后,集群将配置一个可用于调用服务的外部IP,如图4.10所示。GKE将根据需要自动进行负载平衡并扩展服务以匹配负载。

图4.10:echo服务已部署到开放的Web

http://35.238.43.63/predict?msg=Hi_from_GKE
{"response":"Hi_from_GKE","success":true}

上面的代码段显示了使用服务的示例。我们能够使用GKE快速获取一个Docker镜像并将其部署在Kubernetes生态系统中。用Kubernetes托管Docker镜像的体验非常好,因为它是一种可移植的解决方案,可跨多个云环境运行,并且已被许多开源项目采用。

小结

容器非常适合用来在不同环境下重现你的分析和模型。尽管容器可以在单台机器上保持依赖项整洁有序,但它的主要好处是让数据科学家能够编写模型端点,而不必担心容器是怎样托管的。关注点分离后,我们就更容易与工程团队合作将模型部署到生产环境中;或者使用本章中展示的方法,数据和应用科学团队也可以自行将模型部署到生产环境中。

用来服务模型的最佳方法取决于你的部署环境和预期的负载。通常,在一家公司工作时,你只能使用特定的云平台,因为模型服务可能需要与云中的其他组件(例如数据库或云存储)交互。在AWS中有多种托管容器的选项,而GCP在GKE上整合为单个解决方案。你主要关心的一个问题是使用无服务器函数技术或弹性容器技术服务模型是否更具成本效益。正确的答案将取决于你需要处理的流量、最终用户可以忍受的延迟以及需要托管的模型的复杂程度。容器化解决方案非常适合用来服务复杂的模型,并确保你可以满足延迟要求,但是与无服务器函数方案相比,前者可能需要更多的DevOps开销。

原文链接https://towardsdatascience.com/using-docker-kubernetes-to-host-machine-learning-models-780a501fda49

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/krbEmrwZjmSkVPgvbtwd

扫码关注云+社区

领取腾讯云代金券