本文回顾总结近一段时间网易云音乐机器学习平台(GoblinLab)在容器化实践的一些尝试。截止今日,音乐机器学习平台(GoblinLab)在容器化方面的尝试,已开展了一段时间,并且已经有了阶段性的成果。
布景
曩昔音乐算法的模型训练使命,是在物理机上进行开发、调试以及守时调度。每个算法团队运用归于自己的独立物理机,这种现状会形成一些问题。比方物理机的散布零星,缺少一致的办理,首要依赖于doc文档的表格记载机器的运用与归属;各事务间机器资源的分配,有时需求机器在不同机房的搬家,耗时耗力。别的,因为存在多人共用、开发与调度使命共用等状况,会形成环境的彼此影响,以及资源的抢夺。针对当时的状况,总结问题如下:
- 资源运用率低:部分机器资源运用率偏低;无法依据各个事务的不同阶段,在大局内快速、动态的完结扩缩容,以到达资源的合理装备,进步资源全体运用率;
- 环境彼此影响:存在多人共用、测验与调度混用同一开发机,未做任何的阻隔,形成或许的环境、同享资源的彼此影响与抢夺;
- 监控报警缺失:物理机形式,使命监控报警功用缺失,导致使命无法运维,或许功率低。
资源没有大局一致的合理分配,会呈现负载不均衡,资源不能最大化的运用。
Kubernetes的测验
在快速的扩缩容、环境阻隔、资源监控等方面,Kubernetes及其相关扩展,能够很好的处理问题。现将物理机会集起来,并构建成一个Kubernetes集群。经过剖析算法搭档以往的工作方法,机器学习渠道(GoblinLab)决议测验根据Kubernetes供给在线的开发调试容器环境以及使命的容器化调度两种计划,其别离针对使命开发和使命调度两种场景。
使命开发
为最大化的削减算法搭档由物理机迁移到容器化环境的学习本钱,GoblinLab体系中根本将Kubernetes的容器作为云主机运用。容器的镜像以各版别Tensoflow镜像为根底(底层是Ubuntu),集成了大数据开发环境(Hadoop、Hive、Spark等Client),安装了常用的软件。别的,为了方便运用,容器环境供给了Jupyter Lab、SSH登录、Code Server(VSCode)三种运用方法。
在GoblinLab中新建容器化开发环境比较简单,只需挑选镜像,填写所需的资源,以及需求挂载的外部存储即可(使命开发的环境下文简称开发实例)。
新建环境装备后,点击发动实例,容器初始化时,会主动发动Jupyter lab、SSH以及CodeServer。
Jupyter Lab:
Code Server:
SSH登录:
算法能够挑选以上恣意一种方法进行使命的开发,或许调试。因为供给了Code Server(VSCode),所以能够获得更好的体会。
使命开发所用的容器化环境,在底层Kubernetes上是经过StatefulSet类型完结,对应资源编列文件如下(已精简细节):
- kind:StatefulSet
- apiVersion:apps/v1
- metadata:
- name:${name}
- namespace:"${namespace}"
- spec:
- replicas:1
- selector:
- matchLabels:
- statefulset:${name}
- system/app:${name}
- template:
- spec:
- <#if(gpu>0)>
- tolerations:
- -effect:NoSchedule
- key:nvidia.com/gpu
- value:"true"
- </#if>
- <#ifusePrivateRepository=="true">
- imagePullSecrets:
- -name:registrykey-myhub
- </#if>
- volumes:
- -name:localtime
- hostPath:
- path:/etc/localtime
- <#ifMountPVCs??&&(MountPVCs?size>0)>
- <#listMountPVCs?keysaskey>
- -name:"${key}"
- persistentVolumeClaim:
- claimName:"${key}"
- </#list>
- containers:
- -name:notebook
- image:${image}
- imagePullPolicy:IfNotPresent
- volumeMounts:
- -name:localtime
- mountPath:/etc/localtime
- <#ifreadMountPVCs??&&(readMountPVCs?size>0)>
- <#listreadMountPVCs?keysaskey>
- -name:"${key}"
- mountPath:"${readMountPVCs[key]}"
- readOnly:true
- </#list>
- </#if>
- <#ifwriteMountPVCs??&&(writeMountPVCs?size>0)>
- <#listwriteMountPVCs?keysaskey>
- -name:"${key}"
- mountPath:"${writeMountPVCs[key]}"
- </#list>
- </#if>
- env:
- -name:NOTEBOOK_TAG
- value:"${name}"
- -name:HADOOP_USER
- value:"${hadoopUser}"
- -name:PASSWORD
- value:"${password}"
- resources:
- requests:
- cpu:${cpu}
- memory:${memory}Gi
- <#if(gpu>0)>
- nvidia.com/gpu:${gpu}
- </#if>
- limits:
- cpu:${cpu}
- memory:${memory}Gi
- <#if(gpu>0)>
- nvidia.com/gpu:${gpu}
- </#if>
现在GolbinLab已供给根据Tensoflow各版别的CPU与GPU通用镜像11个,以及多个定制化镜像。
使命调度
算法搭档在运用容器化环境之前,使命的开发调度都是在GPU物理机器上完结,调度一般都是经过守时器或crontab指令调度使命,使命无失利、超时等报警,以及也没有重试等机制,根本无相关的使命运维东西。
在介绍容器中开发的使命怎么上线调度之前,先扼要介绍一下GoblinLab的体系架构。
上图为GoblinLab简化的体系架构,其间首要分为四层,由上到下别离为:
- Application-应用层:供给直接面向用户的机器学习开发渠道(GoblinLab)
- Middle-中间层: 中间层,首要是接入了一致的调度、报警、以及装备等服务
- Wizard-履行服务: 其供给一致的履行服务,供给包括Kubernetes、Spark、Jar等各类使命的提交履行。插件式,支撑快速扩展
- Infrastructure-根底设施: 底层的根底设施,首要包括Kubernetes集群、Spark集群以及一般服务器等
GolbinLab为了保证调度使命的稳定性,将使命的开发与调度拆分,改动之前算法直接在物理机上开发完使命后,经过守时器或许crontab调度使命的方法。如上图所示,在开发完结后,使命的调度是经过使命流中的容器化使命调度组件完结,用户需填组件的相关参数(代码地点PVC及途径,装备镜像等),再经过使命流的调度功用完结使命调度。与使命开发不同,每个调度使命履行在独立的容器中,保证使命间彼此阻隔,一起经过后续介绍的资源阻隔计划,能够优先保证线上调度使命所需资源。
使命调度履行的一般流程如下:
使命调度履行时在Kubernetes上资源编列文件(已精简细节):
- apiVersion:batch/v1
- kind:Job
- metadata:
- name:${name}
- namespace:${namespace}
- spec:
- template:
- spec:
- containers:
- -name:jupyter-job
- image:${image}
- env:
- -name:ENV_TEST
- value:${envTest}
- command:["/bin/bash","-ic","cd${workDir}&&\
- ${execCommand}/root/${entryPath}${runArgs}"]
- volumeMounts:
- -mountPath:"/root"
- name:"root-dir"
- resources:
- requests:
- cpu:${cpu}
- memory:${memory}Gi
- <#if(gpu>0)>
- nvidia.com/gpu:${gpu}
- </#if>
- limits:
- cpu:${cpu}
- memory:${memory}Gi
- <#if(gpu>0)>
- nvidia.com/gpu:${gpu}
- </#if>
- volumes:
- -name:"root-dir"
- persistentVolumeClaim:
- claimName:"${pvc}"
- backoffLimit:0
权限操控
容器化开发环境装备发动后,用户能够经过SSH登录、CodeServer或JupyterLab等其间一种方法运用。为了防止容器化开发环境被其他人运用,GoblinLab给每种方法都设置了一致的密钥,而密钥在每次发动时随机生成。
1、随机生成暗码
2、设置账号暗码(SSH登录暗码)
- echo"root:${password}"|chpasswd
3、设置Code Server暗码 (VSCode)
- #设置环境变量PASSWORD即可
- env:
- -name:PASSWORD
- value:"${password}"
4、设置Jupyter Lab暗码
- jupyternotebook--generate-config,~/.jupyter目录下生成jupyter_notebook_config.py,并增加代码
- importos
- fromIPython.libimportpasswd
- c=c#pylint:disable=undefined-variable
- c.NotebookApp.ip='0.0.0.0'#https://github.com/jupyter/notebook/issues/3946c.NotebookApp.port=int(os.getenv('PORT',8888))c.NotebookApp.open_browser=False
- setsapasswordifPASSWORDissetintheenvironment
- if'PASSWORD'inos.environ:
- password=os.environ['PASSWORD']
- ifpassword:
- c.NotebookApp.password=passwd(password)
- else:
- c.NotebookApp.password=''
- c.NotebookApp.token=''
- delos.environ['PASSWORD']
数据耐久化
在Kubernetes容器中,如无特别装备,容器中的数据是没有进行耐久化,这意味着跟着容器的删去或许重启,数据就会丢掉。对应的处理方法比较简单,只需给需求耐久化的目录,挂载外部存储即可。在GoblinLab中,会给每个用户主动创立一个默许的外部存储PVC,并挂载到容器的/root目录。别的,用户也能够自定义外部存储的挂载。
除了主动创立的PVC外,用户也能够自己创立PVC,并支撑将创立的PVC只读或许读写同享给其他人。
别的,在Goblinlab上也能够对PVC里的数据进行办理。
服务露出
Kubernetes集群中创立的服务,在集群外无法直接拜访,GoblinLab运用Nginx Ingress + Gateway拜访,将集群内的服务露出到外部。
容器化开发环境的Service资源编列文件如下(已精简细节):
- apiVersion:v1
- kind:Service
- metadata:
- name:${name}
- namespace:${namespace}
- spec:
- clusterIP:None
- ports:
- -name:port-notebook
- port:8888
- protocol:TCP
- targetPort:8888
- -name:port-sshd
- port:22
- protocol:TCP
- targetPort:22
- -name:port-vscode
- port:8080
- protocol:TCP
- targetPort:8080
- -name:port-tensofboard
- port:6006
- protocol:TCP
- targetPort:6006
- <#ifports??&&(ports?size>0)>
- <#listportsasport>
- -name:port-${port}
- port:${port}
- targetPort:${port}
- </#list>
- </#if>
- selector:
- statefulset:${name}
- type:ClusterIP
每逢用户发动一个容器化开发环境,GoblinLab将经过接口主动修正Nginx Ingress装备,将服务露出出来,以供用户运用,Ingress转发装备如下:
- apiVersion:v1
- kind:ConfigMap
- metadata:
- name:tcp-services
- namespace:kube-system
- data:
- "20000":ns/notebook-test:8888
- "20001":ns/notebook-test:8080
- "20002":ns/notebook-test:22
资源管控
为进步资源的运用率,GoblinLab底层Kubernetes中的资源,根本都是以同享的方法运用,并进行必定份额的超售。可是当多个团队同享一个资源总量固定的集群时,为了保证每个团队公正的同享资源,此刻需求对资源进行办理和操控。在Kubernetes中,资源配额便是处理此问题的东西。现在GoblinLab需求管控的资源首要为CPU、内存、GPU以及存储等。渠道在考虑各个团队的实践需求后,将资源划分为多个行列(Kubernetes中的概念为namespace),供给给各个团队运用。
- apiVersion:v1
- kind:ResourceQuota
- metadata:
- name:skiff-quota
- namespace:test
- spec:
- hard:
- limits.cpu:"2"
- limits.memory:5Gi
- requests.cpu:"2"
- requests.memory:5Gi
- requests.nvidia.com/gpu:"1"
- requests.storage:10Gi
在集群中,最常见的资源为CPU与内存,因为能够超售(overcommit),所以存在limits与requests两个配额约束。除此以外,其他资源为扩展类型,因为不允许overcommit,所以只要requests配额约束。参数阐明:
- limits.cpu:Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value.
- limits.memory: Across all pods in a non-terminal state, the sum of memory limits cannot exceed this value.
- requests.cpu:Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value.
- requests.memory:Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value.
- http://requests.nvidia.com/gpu:Across all pods in a non-terminal state, the sum of gpu requests cannot exceed this value.
- requests.storage:Across all persistent volume claims, the sum of storage requests cannot exceed this value.
知优网 » 机器学习渠道在Kubernetes上的实践(kubernetes最佳实践)