文档章节

使用Dubbo+Kubernetes部署线上的TensorFlow Serving服务

WaltonWang
 WaltonWang
发布于 2018/06/10 13:07
字数 3516
阅读 668
收藏 2

Author: xidianwangtao@gmail.com

摘要:本文介绍了在Kubernetes集群中,使用Dubbo+Zookeeper来完成TensorFlow Serving服务的注册与发现、负载均衡的方案,以及使用KubeDNS+Kube2LVS的方案。

背景

TensorFlow Serving服务在Kubernetes集群中的部署方案,如果是从零开始建设,那么可以通过Kubernetes原生的Service+KubeDNS实现服务的注册与发现,并通过对接LVS集群进行负载均衡。因此我们在TaaS中开发了Kube2LVS模块,负责对TensorFlow Serving服务进行ListAndWatch,实现TensorFlow Serving Service Info动态reload到LVS config中。

但是在TensorFlow Serving on Kubernetes发布之前,用户已经通过裸机部署的方式在线上部署了Serving服务,用户采用Dubbo框架来进行Serving服务的注册与发现、LB,因此为了兼容已有的架构,我们最终选择使用Dubbo+Zookeeper的方式来取代前面基于KubeDNS+Kube2LVS的方案。

需要说明:

  • 我们为TensorFlow Serving服务单独提供了一个CaaS集群,目前并没有和训练集群混合部署。
  • CaaS集群的网络方案要求Pod IP对集群外可见,Cisco Contiv(OVS + Vlan)和Calico(BGP)均可。

KubeDNS + Kube2LVS

Architecture

Overview

  • 在IDC部署基于OSPF + Quagga + Tunnel的LVS集群;
  • 开发LVS配置热更新的HTTP API,提供给Kube2LVS调用;
  • 为了方便管理和部署,我们的线上TensorFlow Serving使用原则:一个TensorFlow Serving实例只加载一个Model,暴露一个Port;
  • 上线初期,为了保证(验证)Serve Model的高可用,同一个Model需要一部分副本部署在物理服务器上,另外一部分副本部署在CaaS集群中。业务请求经过LVS分发到对应的物理服务器和CaaS集群节点,均提供IP + Port给LVS集群;
  • LVS集群中,会给每个Model从VIP Pool中分配一个对应的VIP;
  • TaaS平台开发Kube2LVS模块,负责ListAndWatch CaaS集群中TensorFlow Serving Service的CUD事件,然后调用LVS HTTP接口更新LVS配置;
  • TensorFlow Serving Services以NodePort方式暴露到集群外部,外部访问TensorFlow Serving服务只能通过CaaS集群中的Edge Node,在Edge Node通过kube-proxy经过iptables 4层路由转发到后端真正的TensorFlow Serving容器。Edge Node是Kubernetes节点,但是不部署任何业务容器,只做流量入口及流量分发,通过Node Taint和Node Label的方式实现。
  • Edge Node流量过大,可以通过Ansible分钟级扩容(事先准备好服务器);
  • 通过TaaS中对每个Serving服务的监控,如果发现某个Model的副本数不够,可以通过在TaaS平台上秒级手动扩容到期望的副本数;
  • TensorFlow Serving部署的CaaS集群需独立部署(与TensorFlow Training的CaaS集群物理隔离);

Request & Response路径

  • Request的路径:
Client ——> LVS ——> Edge Node ——> TensorFlow Serving Instance(Model);
  • Response路径:
TensorFlow Serving Instance(Model)——> Client

高可用

方案保证了请求的全链路高可用,包括以下三个方面:

  • LVS的高可用

LVS集群通过OSPF + Quagga来部署,每个LVS集群部署两个LVS实例来保证LVS的高可用。

  • Edge Node的高可用

在LVS集群中,给每个Model分配一个VIP,并4层负载到后端至少2个Edge Node上,这样保证Edge Node这一层的高可用;

  • TensorFlow Serving 4 Model_N的高可用

    • 每个Model会通过至少2个TensorFlow Serving实例来加载并提供服务,防止单点。
    • 每个TensorFlow Serving实例都能接受和处理请求,具备负载均衡的能力。
    • 同一个Model的不同TensorFlow Serving实例会由CaaS自动调度到不同的物理服务器或者机架,防止物理服务器或者机架掉电等引发的单点故障。
    • 如果CaaS中的某个TensorFlow Serving实例down了,那么CaaS会自动发现这一事件,并会自动再重启一个TensorFlow Serving实例。
    • TensorFlow Serving实例只有部分部署在CaaS集群中,还有部分部署在CaaS集群之外的物理服务器上(由用户自己部署),在LVS层面配置好负载均衡,防止不可预知的整个CaaS集群故障引发单点故障;
    • 待稳定运行一段时间后,将所有的TensorFlow Serving实例部署到CaaS集群中;

资源隔离和稳定性

通过裸机在线上部署的TensorFlow Serving实例目前都是单独占用一台物理服务器,如果该Model的负载不高,则会造成一定的资源浪费。部署到CaaS集群后,可以支持单台服务器启动多个TensorFlow Serving实例。Kubernetes提供以下集中资源隔离机制,来保证单个TensorFlow Serving实例资源的同时,也能做好各个实例之间的资源隔离,防止某个Model完全抢占了其他Model Server的资源,也达到了提供资源使用率的目的。

  • 通过kubernetes limitrange(足够大)默认使得单台服务器只能部署一个TensorFlow Serving;
  • 通过单独给需要的TensorFlow Serving容器配置resource requests(cpu & memory)来保证当服务器资源资源不够用出现争抢时,这个TensorFlow Serving最少可用的资源;
  • 如果服务器资源有空闲,则它上面的任何TensorFlow Serving实例都能尽量去利用空闲的资源,提高资源使用率。也就是给容器配置resource limits,否则可能会出现被Linux Kernel OOM Killer杀死的风险。
  • CaaS集群中的每台服务器,都会设置给linux系统进程和kubernetes组件预留的资源,以此保证其上面的TensorFlow Serving再怎么榨取服务器资源,也不会影响linux系统和关键组件的运行,防止服务器被TensorFlow Serving搞垮和集群雪崩。关于这部分的详细内容,请参考从一次集群雪崩看Kubelet资源预留的正确姿势

弹性伸缩

项目初期,只提供用户手动干预的方式进行Scale:

  • Edge Node的Scale up/down

需要对Edge Node的网络IO进行监控和告警,当网络IO遇到瓶颈时,准备好物理服务器(两个万兆网卡做Bond),然后通过Ansible自动化部署CaaS相关组件,组件启动后就能作为Edge Node提供流量入口服务和分发的能力了,之后就能添加到LVS配置中作为LVS后端服务。

  • TensorFlow Serving实例的Scale up/down

当某个Model Serve的请求量太大,通过监控发现后端的TensorFlow Serving Replicas的负载过高产生告警。用户收到告警后,登录TaaS平台进行扩容操作,增加Replicas数,CaaS会自动创建对应TensorFlow Serving容器并加载Model对外提供服务,以此降低每个实例的负载并提升了处理能力。

线上运行成熟后,根据经验我们可以实现基于定制化的HPA(对接Prometheus)TensorFlow Serving实例的 Auto Scale up/down,全程自动化处理,无需人为干预。

Dubbo + Zookeeper

方案

方案注意事项

  • 使用Kubernetes Deployment(replicas=1)来管理一个模型的Serving实例,同一个模型的副本数用户可以在TaaS上配置,注意:

    • 每个副本都对应一个Deployment和Service。Deployment的replicas设置为1,TaaS按照创建顺序,给同一个模型的多个Serving副本的Deployments、Services和Pods打上对应的Label:Index:$N, Model:$Name
    • Deployment和Service的命名规则为:$ModelName-$Index
    • 每个Serving实例的创建,都是按照先创建Service,再创建Deployment的顺序。先创建Service是为了先拿到Kubernetes自动分配的NodePort。
    • 如此,每个Pod对应一个Deployment和Service,Deployment负责该Pod的Self-Healing,Service类型为NodePort,负责NodePort的自动分配和代理。(注意,这里不能使用Headless Service,因为Headless Service不支持NodePort类型。)
  • 每个Pod内两个业务容器,一个是TensorFlow Serving容器,负责加载HDFS上的Model并提供grpc接口调用,TaaS上提供用户配置TensorFlow Serving的模型加载策略,默认加载lastest模型;另外一个是Tomcat业务容器,业务jar包在这里启动并进行热更新,jar包实现不同的特征抽取组合进行预测,启动时向集群外的Zookeeper集群注册自己所在节点NodeIP和NodePort。

    • 通过downward-api的方式向Pod内注入NodeIP的env。由于先创建Service拿到NodePort,通过给Pod注入env的方式将NodePort注入到Pod内。如此,tomcat容器内就能拿到对应的NodeIP和NodePort,从而启动前去更新dubbo的配置文件。
    • 为了兼容一机多实例的场景,不能使用hostNetwork:true共享Host网络命名空间,否则必然会导致tomcat和Serving无法启动的问题。
  • 如何进行一机单实例部署?

    • 上线初期,按照一机单实例进行部署,通过给Pod内的container设置resource.request接近Node Allocatable,使得Kubernetes调度时一个宿主机只能容下一个Pod。
  • 如何进行一机多实例部署?

    • 稳定运行一段时间后,如果发现集群的资源利用率较低,那么考虑一机多实例的方式进行部署。只需要将Pod对应的resource.request减小到合理的值,使得Kubernetes调度时一个宿主机能容下多个Pod。

完整流程

TaaS实现的流程如下:

  1. 页面上提供用户配置预测模型的相关信息,包括:
    • 模型名称
    • 模型的HDFS路径
    • TensorFlow Serving模型加载策略相关配置(默认latest)
    • 期望实例个数N
    • 每个实例的请求资源值(默认为24cpu,128GB,也就是一机单实例)
  2. TaaS顺序(index从1到N)的为每个实例按照如下逻辑进行实现:
    • 先创建一个NodePort类型的Service(加上对应的Label:Index:$N, Model:$Name),注意不要指定nodePort的值。
    • 然后查询这个Service的nodePort值。
    • 再封装Deployment对象,把获取到的nodePort注入到pod的env中。通过downward-api事先注入不确定的NodeIP,调用Kubernetes接口创建该Deployment。
  3. 接着Kubernetes会调度到合适的节点,将Pod内的容器启动。tomcat启动前会获取NodeIP和NodePort,并更新到dubbo配置文件中,并自动上报到集群外的Zookeeper集群。
  4. Zookeeper会将新的或者发生变更的服务信息自动通知client,client根据负载均衡策略选择其中一个RealServer。
  5. client选择某个RealServer的NodeIP和NodePort后,发起预测请求。请求到对应的节点后,经过节点的iptables(kube-proxy根据Service自动维护)转发到后端的Pod。
  6. Pod内的tomcat处理后,对TensorFlow Serving发出grpc请求进行预测。整个请求的数据原路返回。

注意:pod中tomcat和serving两个容器启动的顺序是有要求的:先启动serving容器,再启动tomcat容器。tomcat容器启动前,先去检测localhost中serving服务是否启动成功,如果未启动,则循环等待。

健康检查及流量自动接入与摘除

  • tomcat服务启动时会自动往ZK注册服务,通过Session长连接的方式来维护ZK的服务列表。如果长连接断了,那么ZK会自动从服务列表中删除这个实例的信息。通过这种方式完成服务的自动接入与摘除。
  • 给tomcat容器配置Kubernetes Liveness Probe,通过模拟用户的请求(会调用tensorflow serving服务)来判断服务是否可用。如果探针失败,则kubelet会自动重启tomcat容器,重启过程中,与ZK的Session长连接会断开,ZK就会自动摘除这个实例。重启后,会重新注册服务,完成自动接入。通过这种方式来防止服务Hang住假死的问题。
  • 给tomcat容器配置Readiness Probe吗,如果探针失败,会从Service中摘除这个实例,client的请求即使到了所在的节点,iptables也不会转发这个请求到对应的Pod。
  • tensorflow serving容器的健康检查,配置和tomcat一样的liveness probe。如果探测失败,kubelet自动重启serving容器。注意探测的周期不要太短,建议分钟级别。

高可用

  • tomcat服务down了或者Hang住的情况。

    • tomcat服务down了,与ZK的长连接就断了,ZK会摘除这个实例,ZK接着通知client,client之后就不会将请求发到这个实例了,直到重新注册成功。
    • tomcat服务down了,那么liveness probe就会失败,kubelet会重启tomcat,触发重新注册。
    • tomcat服务Hang住的情况,Session没断的话,ZK是无法感知的。但是不要紧,liveness probe会失败,kubelet会重启tomcat,触发重新注册。
  • tensorflow serving服务down了或者Hang住的情况。

    • tensorflow serving容器配置了liveness probe的话,如果探测失败,kubelet会重启这个容器。
  • 实例所在的服务器down了的情况下。

    • 实例所在的节点down了,会导致Session断开,ZK感知到这一事件并自动摘除对应实例。
    • 节点down了后大概5min时间,会在其他节点重新启动一个实例,新实例启动后往ZK中注册服务。由于线上都是多副本部署的,这个实例5min内不可用不要紧,其他副本能正常提供服务即可。
  • 实例所在节点与ZK的网络挂了的情况下。

    • 网络挂了,Session就断了,ZK感知到这一事件并自动摘除对应实例。

总结

本文介绍了两种使用Kubernetes部署TensorFlow Serving服务,并完成服务发现与负载均衡的方案。基于KubeDNS+Kube2LVS的方案使用Kubernetes原生的特性,基于Dubbo+Zookeeper的方案则使用Dubbo的服务发现与软负载特性。当然,还有很多的实现细节需要读者自己思考,有需求的同学可以找我讨论。

© 著作权归作者所有

共有 人打赏支持
WaltonWang
粉丝 210
博文 104
码字总数 220998
作品 0
深圳
程序员
私信 提问
加载中

评论(4)

1
1231414124

引用来自“1231414124”的评论

遇到了一点问题想请教一下,docker内部署dubbo-provider遇到往zookeeper注册的是docker内的IP,于是修改配置+service的NodePort 但是启动运用的使用,遇到netty服务绑定ip失败,应用容器和宿主机不通,请问您是如何解决的呢

引用来自“WaltonWang”的评论

需要将容器内的/etc/hosts中删除自动添加的容器IP,改成NodeIP
thanks~
WaltonWang
WaltonWang

引用来自“1231414124”的评论

遇到了一点问题想请教一下,docker内部署dubbo-provider遇到往zookeeper注册的是docker内的IP,于是修改配置+service的NodePort 但是启动运用的使用,遇到netty服务绑定ip失败,应用容器和宿主机不通,请问您是如何解决的呢
需要将容器内的/etc/hosts中删除自动添加的容器IP,改成NodeIP
1
1231414124
com.alibaba.dubbo.rpc.RpcException: Fail to start server(url: dubbo://10.83.3.76:8888/com.xmcares.framework.dubbo.sample.HelloService?anyhost=false&application=dubbo-provider&bind.ip=10.83.3.76&bind.port=8888&channel.readonly.sent=true&codec=dubbo&dubbo=2.5.7&generic=false&heartbeat=60000&interface=com.xmcares.framework.dubbo.sample.HelloService&methods=sayHello,test,sayNice&module=dubbo-provider&pid=7&revision=1.0.0-SNAPSHOT&side=provider×tamp=1533524522795) Failed to bind NettyServer on /10.83.3.76:8888, cause: Failed to bind to: /10.83.3.76:8888
1
1231414124
遇到了一点问题想请教一下,docker内部署dubbo-provider遇到往zookeeper注册的是docker内的IP,于是修改配置+service的NodePort 但是启动运用的使用,遇到netty服务绑定ip失败,应用容器和宿主机不通,请问您是如何解决的呢
tensorflow serving docker

背景介绍  TensorFlow Serving 是一个用于机器学习模型 serving 的高性能开源库。它可以将训练好的机器学习模型部署到线上,使用 gRPC 作为接口接受外部调用。更加让人眼前一亮的是,它支持...

晴天哥
03/03
0
0
如何部署tensorflow训练的模型

最近深度学习算法被广泛研究和应用,而tensorflow则是被应用最为广泛的工具。tensorflow训练的模型被应用在线上时,主要有3种方式(本文主要讨论java方向的应用): 1:java代码重写预测代码(...

lirainbow0
2018/05/29
0
0
基于TensorFlow Serving的深度学习在线预估

一、前言 随着深度学习在图像、语言、广告点击率预估等各个领域不断发展,很多团队开始探索深度学习技术在业务层面的实践与应用。而在广告CTR预估方面,新模型也是层出不穷: Wide and Deep[...

美团技术团队
2018/10/12
0
0
Kubeflow实战系列:利用TensorFlow Serving进行模型预测

介绍 本系列将介绍如何在阿里云容器服务上运行Kubeflow, 本文介绍如何使用加载训练模型并且进行模型预测。 第一篇:阿里云上使用JupyterHub 第二篇:阿里云上小试TFJob 第三篇:利用TFJob运行...

必嘫
2018/06/23
0
0
谷歌发布 TensorFlow Serving

TensorFlow服务是一个灵活的,高性能的机器学习模型的服务系统,专为生产环境而设计。 TensorFlow服务可以轻松部署新的算法和实验,同时保持相同的服务器体系结构和API。 TensorFlow服务提供...

磐石001
2017/08/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

容器服务

简介 容器服务提供高性能可伸缩的容器应用管理服务,支持用 Docker 和 Kubernetes 进行容器化应用的生命周期管理,提供多种应用发布方式和持续交付能力并支持微服务架构。 产品架构 容器服务...

狼王黄师傅
昨天
3
0
高性能应用缓存设计方案

为什么 不管是刻意或者偶尔看其他大神或者大师在讨论高性能架构时,自己都是认真的去看缓存是怎么用呢?认认真真的看完发现缓存这一块他们说的都是一个WebApp或者服务的缓存结构或者缓存实现...

呼呼南风
昨天
12
0
寻找一种易于理解的一致性算法(扩展版)

摘要 Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可...

Tiny熊
昨天
2
0
聊聊GarbageCollectionNotificationInfo

序 本文主要研究一下GarbageCollectionNotificationInfo CompositeData java.management/javax/management/openmbean/CompositeData.java public interface CompositeData { public Co......

go4it
昨天
3
0
阿里云ECS的1M带宽理解

本文就给大家科普下阿里云ECS的固定1M带宽的含义。 “下行带宽”和“上行带宽” 为了更好的理解,需要先给大家解释个词“下行带宽”和“上行带宽”: 下行带宽:粗略的解释就是下载数据的最大...

echojson
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部