共计 7248 个字符,预计需要花费 19 分钟才能阅读完成。
这篇文章主要讲解了“Kubernetes 的架构怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着丸趣 TV 小编的思路慢慢深入,一起来研究和学习“Kubernetes 的架构怎么使用”吧!
分布式 TensorFlow
TensorFlow 是一个使用数据流图进行数值计算的开源软件库。图中的节点代表数学运算,而图中的边则代表在这些节点之间传递的多维数组(张量)。这种灵活的架构可让您使用一个 API 将计算工作部署到桌面设备、服务器或者移动设备中的一个或多个 CPU 或 GPU。关于 TensorFlow 的基础概念,我就不多介绍了。
单机 TensorFlow
下面是一个单机式 TensorFlow 训练示意图,通过 Client 提交 Session,定义这个 worker 要用哪个 cpu/gpu 做什么事。
分布式 TensorFlow
2016 年 4 月 TensorFlow 发布了 0.8 版本宣布支持分布式计算,我们称之为 Distributed TensorFlow。这是非常重要的一个特性,因为在 AI 的世界里,训练的数据量和模型参数通常会非常大。比如 Google Brain 实验室今年发表的论文 OUTRAGEOUSLY LARGE NEURAL NETWORKS: THE SPARSELY-GATED MIXTURE-OF-EXPERTS LAYER 中提到一个 680 亿个 Parameters 的模型,如果只能单机训练,那耗时难于接受。通过 Distributed TensorFlow,可以利用大量服务器构建分布式 TensorFlow 集群来提高训练效率,减少训练时间。
通过 TensorFlow Replcation 机制,用户可以将 SubGraph 分布到不同的服务器中进行分布式计算。TensorFlow 的副本机制又分为两种,In-graph 和 Between-graph。
In-graph Replication 简单来讲,就是通过单个 client session 定义这个 TensorFlow 集群的所有 task 的工作。
与之相对地,Between-graph Replication 就是每个 worker 都有独立的 client 来定义自己的工作。
下面是抽象出来的分布式 TensorFlow Framework 如下:
我们先来了解里面的几个概念:
Cluster
一个 TensorFlow Cluster 有一个或多个 jobs 组成,每个 job 又由一个或多个 tasks 构成。Cluster 的定义是通过 tf.train.ClusterSpec 来定义的。比如,定义一个由 3 个 worker 和 2 个 ps 的 TensorFlow Cluster 的 ClusterSpec 如下:
tf.train.ClusterSpec({
worker : [
worker0.example.com:2222 , // 主机名也可以使用 IP
worker1.example.com:2222 ,
worker2.example.com:2222
],
ps : [
ps0.example.com:2222 ,
ps1.example.com:2222
]})
Client
Client 用来 build 一个 TensorFlow Graph,并构建一个 tensorflow::Session 用来与集群通信。一个 Client 可以与多个 TensorFlow Server 交互,一个 Server 能服务多个 Client。
Job
一个 Job 由 tasks list 组成,Job 分 ps 和 worker 两种类型。ps 即 parameter server,用来存储和更新 variables 的,而 worker 可以认为是无状态的,用来作为计算任务的。workers 中,一般都会选择一个 chief worker(通常是 worker0),用来做训练状态的 checkpoint,如果有 worker 故障,那么可以从最新 checkpoint 中 restore。
Task
每个 Task 对应一个 TensorFlow Server,对应一个单独的进程。一个 Task 属于某个 Job,通过一个 index 来标记它在对应 Job 的 tasks 中的位置。每个 TensorFlow 均实现了 Master service 和 Worker service。Master service 用来与集群内的 worker services 进行 grpc 交互。Worker service 则是用 local device 来计算 subgraph。
关于 Distributed TensorFlow 的更多内容,请参考官方内容 www.tensorflow.org/deplopy/distributed
分布式 TensorFlow 的缺陷
分布式 TensorFlow 能利用数据中心所有服务器构成的资源池,让大量 ps 和 worker 能分布在不同的服务器进行参数存储和训练,这无疑是 TensorFlow 能否在企业落地的关键点。然而,这还不够,它还存在一下先天不足:
训练时 TensorFlow 各个 Task 资源无法隔离,很有可能会导致任务间因资源抢占互相影响。
缺乏调度能力,需要用户手动配置和管理任务的计算资源。
集群规模大时,训练任务的管理很麻烦,要跟踪和管理每个任务的状态,需要在上层做大量开发。
用户要查看各个 Task 的训练日志需要找出对应的服务器,并 ssh 过去,非常不方便。
TensorFlow 原生支持的后端文件系统只支持:标准 Posix 文件系统(比如 NFS)、HDFS、GCS、memory-mapped-file。大多数企业中数据都是存在大数据平台,因此以 HDFS 为主。然而,HDFS 的 Read 性能并不是很好。
当你试着去创建一个大规模 TensorFlow 集群时,发现并不轻松;
TensorFlow on Kubernetes 架构与原理
TensorFlow 的这些不足,正好是 Kubernetes 的强项:
提供 ResourceQuota, LimitRanger 等多种资源管理机制,能做到任务之间很好的资源隔离。
支持任务的计算资源的配置和调度。
训练任务以容器方式运行,Kubernetes 提供全套的容器 PLEG 接口,因此任务状态的管理很方便。
轻松对接 EFK/ELK 等日志方案,用户能方便的查看任务日志。
支持 Read 性能更优秀的分布式存储(Glusterfs),但目前我们也还没对接 Glusterfs,有计划但没人力。
通过声明式文件实现轻松快捷的创建一个大规模 TensorFlow 集群。
TensorFlow on Kubernetes 架构
TensorFlow on Kubernetes 原理
在我们的 TensorFlow on Kubernetes 方案中,主要用到以下的 Kubernetes 对象:
Kubernetes Job
我们用 Kubernetes Job 来部署 TensorFlow Worker,Worker 训练正常完成退出,就不会再重启容器了。注意 Job 中的 Pod Template restartPolicy 只能为 Never 或者 OnFailure,不能为 Always,这里我们设定 restartPolicy 为 OnFailure,worker 一旦异常退出,都会自动重启。但是要注意,要保证 worker 重启后训练能从 checkpoint restore,不然 worker 重启后又从 step 0 开始,可能跑了几天的训练就白费了。如果你使用 TensorFlow 高级 API 写的算法,默认都实现了这点,但是如果你是使用底层 core API,一定要注意自己实现。
kind: Job
apiVersion: batch/v1
metadata:
name: {{ name }}-{{ task_type }}-{{ i }}
namespace: {{ name }}
spec:
template:
metadata:
labels:
name: {{ name }}
job: {{ task_type }}
task: {{ i }}
spec:
imagePullSecrets:
- name: harborsecret
containers:
- name: {{ name }}-{{ task_type }}-{{ i }}
image: {{ image }}
resources:
requests:
memory: 4Gi
cpu: 500m
ports:
- containerPort: 2222
command: [/bin/sh , -c , export CLASSPATH=.:/usr/lib/jvm/java-1.8.0/lib/tools.jar:$(/usr/lib/hadoop-2.6.1/bin/hadoop classpath --glob); wget -r -nH -np --cut-dir=1 -R index.html*,*gif {{ script }}; cd ./{{ name }}; sh ./run.sh {{ ps_hosts() }} {{ worker_hosts() }} {{ task_type }} {{ i }} {{ ps_replicas }} {{ worker_replicas }} ]
restartPolicy: OnFailure
Kubernetes Deployment
TensorFlow PS 用 Kubernetes Deployment 来部署。为什么不像 worker 一样,也使用 Job 来部署呢?其实也未尝不可,但是考虑到 PS 进程并不会等所有 worker 训练完成时自动退出 (一直挂起),所以使用 Job 部署没什么意义。
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: {{ name }}-{{ task_type }}-{{ i }}
namespace: {{ name }}
spec:
replicas: 1
template:
metadata:
labels:
name: {{ name }}
job: {{ task_type }}
task: {{ i }}
spec:
imagePullSecrets:
- name: harborsecret
containers:
- name: {{ name }}-{{ task_type }}-{{ i }}
image: {{ image }}
resources:
requests:
memory: 4Gi
cpu: 500m
ports:
- containerPort: 2222
command: [/bin/sh , -c , export CLASSPATH=.:/usr/lib/jvm/java-1.8.0/lib/tools.jar:$(/usr/lib/hadoop-2.6.1/bin/hadoop classpath --glob); wget -r -nH -np --cut-dir=1 -R index.html*,*gif {{ script }}; cd ./{{ name }}; sh ./run.sh {{ ps_hosts() }} {{ worker_hosts() }} {{ task_type }} {{ i }} {{ ps_replicas }} {{ worker_replicas }} ]
restartPolicy: Always
关于 TensorFlow PS 进程挂起的问题,请参考 https://github.com/tensorflow/tensorflow/issues/4713. 我们是这么解决的,开发了一个模块,watch 每个 TensorFlow 集群的所有 worker 状态,当所有 worker 对应 Job 都 Completed 时,就会自动去删除 PS 对应的 Deployment,从而 kill PS 进程释放资源。
Kubernetes Headless Service
Headless Service 通常用来解决 Kubernetes 里面部署的应用集群之间的内部通信。在这里,我们也是这么用的,我们会为每个 TensorFlow 对应的 Job 和 Deployment 对象都创建一个 Headless Service 作为 worker 和 ps 的通信代理。
kind: Service
apiVersion: v1
metadata:
name: {{ name }}-{{ task_type }}-{{ i }}
namespace: {{ name }}
spec:
clusterIP: None
selector:
name: {{ name }}
job: {{ task_type }}
task: {{ i }}
ports:
- port: {{ port }}
targetPort: 2222
用 Headless Service 的好处,就是在 KubeDNS 中,Service Name 的域名解析直接对应到 PodIp,而没有 service VIP 这一层,这就不依赖于 kube-proxy 去创建 iptables 规则了。少了 kube-proxy 的 iptables 这一层,带来的是性能的提升。
在 TensorFlow 场景中,这是不可小觑的,因为一个 TensorFlow Task 都会创建一个 service,几万个 service 是很正常的事,如果使用 Normal Service,iptables 规则就几十万上百万条了,增删一条 iptabels 规则耗时几个小时甚至几天,集群早已奔溃。关于 kube-proxy iptables 模式的性能测试数据,请参考华为 PaaS 团队的相关分享。
KubeDNS Autoscaler
前面提到,每个 TensorFlow Task 都会创建一个 service,都会在 KubeDNS 中有一条对应的解析规则,但 service 数量太多的时候,我们发现有些 worker 的域名解析失败概率很大,十几次才能成功解析一次。这样会影响 TensorFlow 集群内各个 task 的 session 建立,可能导致 TensorFlow 集群起不来。
为了解决这个问题,我们引入了 Kubernetes 的孵化项目 kubernetes-incubator/cluster-proportional-autoscaler 来对 KubeDNS 进行动态伸缩。关于这个问题的具体的细节,有兴趣的同学可以查看我的博文 https://my.oschina.net/jxcdwangtao/blog/1581879。
TensorFlow on Kubernetes 实践
基于上面的方案,我们开发一个 TaaS 平台,已经实现了基本的功能,包括算法管理、训练集群的创建和管理、模型的管理、模型上线 (TensorFlow Serving)、一键创建 TensorBoard 服务、任务资源监控、集群资源监控、定时训练管理、任务日志在线查看和批量打包下载等等,这部分内容可以参考之前在 DockOne 上分享的文章 http://dockone.io/article/3036。
这只是刚开始,我正在做下面的特性:
支持基于训练优先级的任务抢占式调度: 用户在 TaaS 上创建 TensorFlow 训练项目时,可以指定项目的优先级为生产 (Production)、迭代 (Iteration)、调研 (PTR),默认为迭代。优先级从高到低依次为 **Production — Iteration — PTR**。但集群资源不足时,按照任务优先级进行抢占式调度。
提供像 Yarn 形式的资源分配视图,让用户对自己的所有训练项目的资源占用情况变得清晰。
训练和预测的混合部署,提供数据中心资源利用率。
…
经验和坑
整个过程中,遇到了很多坑,有 TensorFlow 的,也有 Kubernetes 的,不过问题最多的还是我们用的 CNI 网络插件 contiv netplugin,每次大问题基本都是这个网络插件造成的。Kubernetes 是问题最少的,它的稳定性比我预期还要好。
contiv netplugin 的问题,在 DevOps 环境中还是稳定的,在大规模高并发的 AI 场景,问题就层出不穷了,产生大量垃圾 IP 和 Openflow 流表,直接把 Node 都成 NotReady 了,具体的不多说,因为据我了解,现在用这个插件的公司已经很少了,想了解的私下找我。
在我们的方案中,一个 TensorFlow 训练集群就对应一个 Kubernetes Namespace,项目初期我们并没有对及时清理垃圾 Namespace,到后来集群里上万 Namespace 的时候,整个 Kubernetes 集群的相关 API 性能非常差了,导致 TaaS 的用户体验非常差。
TensorFlow grpc 性能差,上千个 worker 的训练集群,概率性的出现这样的报错 grpc_chttp2_stream request on server; last grpc_chttp2_stream id=xxx, new grpc_chttp2_stream id=xxx,这是 TensorFlow 底层 grpc 的性能问题,低版本的 grpc 的 Handlergrpc 还是单线程的,只能尝试通过升级 TensorFlow 来升级 grpc 了,或者编译 TensorFlow 时单独升级 grpc 版本。如果升级 TensorFlow 版本的话,你的算法可能还要做 API 适配。目前我们通过增加单个 worker 的计算负载来减少 worker 数量的方法,减少 grpc 压力。
还有 TensorFlow 自身 OOM 机制的问题等等
感谢各位的阅读,以上就是“Kubernetes 的架构怎么使用”的内容了,经过本文的学习后,相信大家对 Kubernetes 的架构怎么使用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是丸趣 TV,丸趣 TV 小编将为大家推送更多相关知识点的文章,欢迎关注!