文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

机器学习平台在Kubernetes上的实践

2024-12-03 16:31

关注

过去音乐算法的模型训练任务,是在物理机上进行开发、调试以及定时调度。每个算法团队使用属于自己的独立物理机,这种现状会造成一些问题。比如物理机的分布零散,缺乏统一的管理,主要依赖于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类型实现,对应资源编排文件如下(已精简细节): 

  1. kind: StatefulSet 
  2. apiVersion: apps/v1 
  3. metadata: 
  4. name: ${name
  5. namespace: "${namespace}" 
  6. spec: 
  7. replicas: 1 
  8. selector: 
  9. matchLabels: 
  10.   statefulset: ${name
  11.   system/app: ${name
  12. template: 
  13. spec: 
  14.   <#if (gpu > 0)> 
  15.   tolerations: 
  16.       - effect: NoSchedule 
  17.         key: nvidia.com/gpu 
  18.         value: "true" 
  19.    
  20.   <#if usePrivateRepository == "true"
  21.   imagePullSecrets: 
  22.     - name: registrykey-myhub 
  23.    
  24.   volumes: 
  25.     - name: localtime 
  26.       hostPath: 
  27.         path: /etc/localtime 
  28.     <#if MountPVCs?? && (MountPVCs?size > 0)> 
  29.     <#list MountPVCs?keys as key
  30.     - name"${key}" 
  31.       persistentVolumeClaim: 
  32.         claimName: "${key}" 
  33.      
  34.   containers: 
  35.     - name: notebook 
  36.       image: ${image} 
  37.       imagePullPolicy: IfNotPresent 
  38.       volumeMounts: 
  39.         - name: localtime 
  40.           mountPath: /etc/localtime 
  41.         <#if readMountPVCs?? && (readMountPVCs?size > 0)> 
  42.         <#list readMountPVCs?keys as key
  43.         - name"${key}" 
  44.           mountPath: "${readMountPVCs[key]}" 
  45.           readOnly: true 
  46.          
  47.          
  48.         <#if writeMountPVCs?? && (writeMountPVCs?size > 0)> 
  49.         <#list writeMountPVCs?keys as key
  50.         - name"${key}" 
  51.           mountPath: "${writeMountPVCs[key]}" 
  52.          
  53.          
  54.       env: 
  55.         - name: NOTEBOOK_TAG 
  56.           value: "${name}" 
  57.         - name: HADOOP_USER 
  58.           value: "${hadoopUser}" 
  59.         - namePASSWORD 
  60.           value: "${password}" 
  61.       resources: 
  62.         requests: 
  63.           cpu: ${cpu} 
  64.           memory: ${memory}Gi 
  65.           <#if (gpu > 0)> 
  66.           nvidia.com/gpu: ${gpu} 
  67.            
  68.         limits: 
  69.           cpu: ${cpu} 
  70.           memory: ${memory}Gi 
  71.           <#if (gpu > 0)> 
  72.           nvidia.com/gpu: ${gpu} 
  73.            

目前GolbinLab已提供基于Tensoflow各版本的CPU与GPU通用镜像11个,以及多个定制化镜像。

任务调度

算法同事在使用容器化环境之前,任务的开发调度都是在GPU物理机器上完成,调度一般都是通过定时器或crontab命令调度任务,任务无失败、超时等报警,以及也没有重试等机制,基本无相关的任务运维工具。

在介绍容器中开发的任务如何上线调度之前,先简要介绍一下GoblinLab的系统架构。

上图为GoblinLab简化的系统架构,其中主要分为四层,由上到下分别为:

GolbinLab为了保障调度任务的稳定性,将任务的开发与调度拆分,改变之前算法直接在物理机上开发完任务后,通过定时器或者crontab调度任务的方式。如上图所示,在开发完成后,任务的调度是通过任务流中的容器化任务调度组件实现,用户需填组件的相关参数(代码所在PVC及路径,配置镜像等),再通过任务流的调度功能实现任务调度。与任务开发不同,每个调度任务执行在独立的容器中,保证任务间相互隔离,同时通过后续介绍的资源隔离方案,可以优先保障线上调度任务所需资源。

任务调度执行的一般流程如下:

任务调度执行时在Kubernetes上资源编排文件(已精简细节): 

  1. apiVersion: batch/v1 
  2. kind: Job 
  3. metadata: 
  4. name: ${name
  5. namespace: ${namespace} 
  6. spec: 
  7. template: 
  8. spec: 
  9.   containers: 
  10.     - name: jupyter-job 
  11.       image: ${image} 
  12.       env: 
  13.         - name: ENV_TEST 
  14.           value: ${envTest} 
  15.       command: ["/bin/bash""-ic", "cd ${workDir} && \ 
  16.         ${execCommand} /root/${entryPath} ${runArgs}"] 
  17.       volumeMounts: 
  18.         - mountPath: "/root" 
  19.           name"root-dir" 
  20.       resources: 
  21.         requests: 
  22.           cpu: ${cpu} 
  23.           memory: ${memory}Gi 
  24.           <#if (gpu > 0)> 
  25.           nvidia.com/gpu: ${gpu} 
  26.            
  27.         limits: 
  28.           cpu: ${cpu} 
  29.           memory: ${memory}Gi 
  30.           <#if (gpu > 0)> 
  31.           nvidia.com/gpu: ${gpu} 
  32.            
  33.   volumes: 
  34.     - name"root-dir" 
  35.       persistentVolumeClaim: 
  36.         claimName: "${pvc}" 
  37. backoffLimit: 0 

权限控制

容器化开发环境配置启动后,用户可以通过SSH登录、CodeServer或JupyterLab等其中一种方式使用。为了避免容器化开发环境被其他人使用,GoblinLab给每种方式都设置了统一的密钥,而密钥在每次启动时随机生成。

1、随机生成密码

2、设置账号密码(SSH登录密码)

  1. echo "root:${password}" | chpasswd 

3、设置Code Server密码 (VSCode) 

  1. #设置环境变量PASSWORD即可 
  2. env:      
  3. namePASSWORD        
  4. value: "${password}"  

4、设置Jupyter Lab密码 

  1. jupyter notebook --generate-config,~/.jupyter 目录下生成jupyter_notebook_config.py,并添加代码 
  2. import os  
  3. from IPython.lib import passwd 
  4. c = c  # pylint:disable=undefined-variable  
  5. c.NotebookApp.ip = '0.0.0.0'  # https://github.com/jupyter/notebook/issues/3946 c.NotebookApp.port = int(os.getenv('PORT', 8888)) c.NotebookApp.open_browser = False 
  6. sets a password if PASSWORD is set in the environment 
  7. if 'PASSWORD' in os.environ:    
  8. password = os.environ['PASSWORD']    
  9. if password:      
  10.  c.NotebookApp.password = passwd(password)    
  11. else:      
  12.  c.NotebookApp.password = ''      
  13.  c.NotebookApp.token = ''    
  14. del os.environ['PASSWORD']  

数据持久化

在Kubernetes容器中,如无特殊配置,容器中的数据是没有进行持久化,这意味着随着容器的删除或者重启,数据就会丢失。对应的解决方法比较简单,只需给需要持久化的目录,挂载外部存储即可。在GoblinLab中,会给每个用户自动创建一个默认的外部存储PVC,并挂载到容器的/root目录。另外,用户也可以自定义外部存储的挂载。

除了自动创建的PVC外,用户也可以自己创建PVC,并支持将创建的PVC只读或者读写分享给其他人。

另外,在Goblinlab上也可以对PVC里的数据进行管理。

服务暴露

Kubernetes集群中创建的服务,在集群外无法直接访问,GoblinLab使用Nginx Ingress + Gateway访问,将集群内的服务暴露到外部。

容器化开发环境的Service资源编排文件如下(已精简细节): 

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4. name: ${name
  5. namespace: ${namespace} 
  6. spec: 
  7. clusterIP: None 
  8. ports: 
  9. name: port-notebook 
  10.   port: 8888 
  11.   protocol: TCP 
  12.   targetPort: 8888 
  13. name: port-sshd 
  14.   port: 22 
  15.   protocol: TCP 
  16.   targetPort: 22 
  17. name: port-vscode 
  18.   port: 8080 
  19.   protocol: TCP 
  20.   targetPort: 8080 
  21. name: port-tensofboard 
  22.   port: 6006 
  23.   protocol: TCP 
  24.   targetPort: 6006 
  25. <#if ports?? && (ports?size > 0)> 
  26. <#list ports as port> 
  27. name: port-${port} 
  28.   port: ${port} 
  29.   targetPort: ${port} 
  30.  
  31.  
  32. selector: 
  33. statefulset: ${name
  34. type: ClusterIP 

每当用户启动一个容器化开发环境,GoblinLab将通过接口自动修改Nginx Ingress配置,将服务暴露出来,以供用户使用,Ingress转发配置如下: 

  1. apiVersion: v1 
  2. kind: ConfigMap 
  3. metadata: 
  4. name: tcp-services 
  5. namespace: kube-system 
  6. data: 
  7. "20000": ns/notebook-test:8888 
  8. "20001": ns/notebook-test:8080 
  9. "20002": ns/notebook-test:22 

资源管控

为提高资源的利用率,GoblinLab底层Kubernetes中的资源,基本都是以共享的方式使用,并进行一定比例的超售。但是当多个团队共享一个资源总量固定的集群时,为了确保每个团队公平的共享资源,此时需要对资源进行管理和控制。在Kubernetes中,资源配额就是解决此问题的工具。目前GoblinLab需要管控的资源主要为CPU、内存、GPU以及存储等。平台在考虑各个团队的实际需求后,将资源划分为多个队列(Kubernetes中的概念为namespace),提供给各个团队使用。 

  1. apiVersion: v1 
  2. kind: ResourceQuota 
  3. metadata: 
  4. name: skiff-quota 
  5. namespace: test 
  6. spec: 
  7. hard: 
  8. limits.cpu: "2" 
  9. limits.memory: 5Gi 
  10. requests.cpu: "2" 
  11. requests.memory: 5Gi 
  12. requests.nvidia.com/gpu: "1" 
  13. requests.storage: 10Gi 

在集群中,最常见的资源为CPU与内存,由于可以超售(overcommit),所以存在limits与requests两个配额限制。除此以外,其他资源为扩展类型,由于不允许overcommit,所以只有requests配额限制。参数说明:

可以进行配额控制的资源不仅有CPU、内存、存储、GPU,其他类型参见官方文档:https://kubernetes.io/docs/con ... otas/

资源隔离

GoblinLab的资源隔离,指的是在同一Kubernetes集群中,资源在调度层面的相对隔离,其中包含GPU机器资源的隔离、线上与测试任务的隔离。

GPU机器资源的隔离

在Kubernetes集群中,相对于CPU机器,GPU机器资源较为珍贵,因此为了提供GPU的利用率,禁止CPU任务调度在GPU机器上。

GPU节点设置污点(Taint):禁止一般任务调度在GPU节点 

  1. key:    nvidia.com/gpu  
  2. value:  true  
  3. effect: NoSchedule   

Taint的effect可选配置:

GPU任务设置容忍(Toleration):让GPU任务可以调度在GPU节点 

  1. <#if (gpu > 0)>    
  2. tolerations:        
  3. - effect: NoSchedule          
  4. key: nvidia.com/gpu          
  5. value: "true"    
  6.  

线上与测试任务隔离

线上与测试任务(GolbinLab中线上任务与测试任务为业务层面的定义,指的是周期调度任务和开发测试任务)使用同一Kubernetes集群,但为了保障线上任务的资源,会特殊设置一些机器节点为线上任务的专有资源池。线上任务执行时优先调度在线上节点上,线上资源池无资源时,也可调度于非线上节点。

线上资源池节点设置污点(Taint):禁止一般任务调度在线上资源池 

  1. key:    node.netease.com/node-pool  
  2. value:  online  
  3. effect: NoSchedule  

线上任务添加容忍(Toleration):允许线上任务调度于线上资源池,但不是必须调度于线上资源池中 

  1. tolerations:        
  2. - effect: NoSchedule          
  3. key: node.netease.com/node-pool          
  4. value: "online"          
  5. operator: Equal 

线上资源池中机器节点设置标签 + 线上任务设置节点亲和性(nodeAffinity):优先将线上任务调度在线上资源池中,但如果线上资源池中无已资源,此时也可以调度在其他节点上

线上资源池中节点设置标签:为了方便,标签与污点同名:

  1. node.netease.com/node-pool: online 

线上任务设置节点亲和性(nodeAffinity):线上任务优先调度在线上资源池中 

  1. affinity:      
  2. nodeAffinity: 
  3. preferredDuringSchedulingIgnoredDuringExecution: 
  4.   nodeSelectorTerms: 
  5.     - matchExpressions: 
  6.     - key: node.netease.com/node-pool                
  7.     operator: In                
  8.    values:                  
  9.     - online 

目前Node affinity有以下几种策略,官方文档affinity-and-anti-affinity:

策略生效后效果如下图所示, 线上任务优先执行与线上资源池节点中,但当线上资源池没有空闲资源后,线上任务Job5也可以去使用普通节点的资源。

阶段性结果

截止今日,音乐机器学习平台(GoblinLab)在容器化方面的尝试,已开展了一段时间,并且已经有了阶段性的成果。

集群建设

经过近一段时间的尝试,目前音乐数据平台的Kubernetes,随着承载的业务越来越多,以及基于Kubernetes的大数据计算平台(Flink等)的落地,后续将有大量的CPU资源加入, 其稳定性将会成为比较大的挑战。

用户使用

任务开发

容器化的好处

对于算法同事,由物理机迁移到机器学习平台提供容器化的环境,能够带来的好处是:

后续规划

目前音乐机器学习平台已能提供完整的容器化开发基础能力,为进一步提高集群的资源利用率、提升运维效率,后续计划从资源调度策略优化(抢占等)、更丰富的资源监控等方面入手,进一步优化。

作者:网易云音乐数据智能部数据平台组王军正

 

来源:小石头的码疯窝内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯