本文由网易云音乐数据智能部数据平台组撰写,主要回顾了音乐机器学习平台(GoblinLab)在容器化方面的一些尝试,欢迎提出宝贵意见。
在过去,音乐算法模型的训练工作主要在物理机上进行,每个算法团队都有自己的独立物理机。然而,这种方式存在一些问题,如物理机分布零散、资源管理不便、资源分配困难等。此外,多人共用物理机可能导致环境冲突和资源竞争。具体问题包括:
Kubernetes及其相关扩展在资源管理、环境隔离和监控报警方面表现出色,因此决定将物理机集中构建为Kubernetes集群。平台尝试提供两种方案:一种是基于Kubernetes的在线开发调试容器环境,另一种是基于Kubernetes的容器化任务调度。
为了降低算法人员迁移到容器化环境的学习成本,GoblinLab系统将K8S容器视为云主机使用。容器镜像以TensorFlow为基础,集成大数据开发环境(如Hadoop、Hive、Spark等),并安装常用软件。容器环境提供了Jupyter Lab、SSH登录和Code Server三种使用方式。
新建容器化开发环境的操作相对简单,只需选择镜像、填写所需资源及挂载外部存储即可。容器初始化时会自动启动Jupyter Lab、SSH和Code Server。
算法人员可以选择任意一种方式进行开发或调试,特别是Code Server(VSCode)提供了更好的开发体验。
任务开发使用的容器化环境在底层Kubernetes上通过StatefulSet类型实现,相关资源编排文件如下(已简化细节):
yaml
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: ${name}
namespace: "${namespace}"
spec:
replicas: 1
selector:
matchLabels:
statefulset: ${name}
system/app: ${name}
template:
spec:
tolerations:
- effect: NoSchedule
key: nvidia.com/gpu
value: "true"
imagePullSecrets:
- name: registrykey-myhub
volumes:
- name: localtime
hostPath:
path: /etc/localtime
- name: "${key}"
persistentVolumeClaim:
claimName: "${key}"
containers:
- name: notebook
image: ${image}
imagePullPolicy: IfNotPresent
volumeMounts:
- name: localtime
mountPath: /etc/localtime
- name: "${key}"
mountPath: "${readMountPVCs[key]}"
readOnly: true
- name: "${key}"
mountPath: "${writeMountPVCs[key]}"
env:
- name: NOTEBOOK_TAG
value: "${name}"
- name: HADOOP_USER
value: "${hadoopUser}"
- name: PASSWORD
value: "${password}"
resources:
requests:
cpu: ${cpu}
memory: ${memory}Gi
nvidia.com/gpu: ${gpu}
limits:
cpu: ${cpu}
memory: ${memory}Gi
nvidia.com/gpu: ${gpu}
目前,GoblinLab已提供基于TensorFlow各版本的CPU和GPU通用镜像11个,以及多个定制化镜像。
在引入容器化环境之前,算法人员的任务开发和调度都是在GPU物理机上进行,通常使用定时器或crontab命令进行调度,缺乏报警和重试机制。GoblinLab为了保障任务调度的稳定性,将任务开发和调度分开,改变算法人员直接在物理机上开发任务后,通过定时器或crontab调度任务的方式。开发完成后,任务调度通过任务流中的容器化任务调度组件完成,用户需填写相关参数(代码所在PVC路径、配置镜像等),再通过任务流的调度功能完成任务调度。每个调度任务在独立的容器中运行,确保任务间的隔离,并通过资源隔离方案优先保障线上任务所需资源。
任务调度执行的通用流程如下:
任务调度执行时在Kubernetes上的资源编排文件(已简化细节)如下:
yaml
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
nvidia.com/gpu: ${gpu}
limits:
cpu: ${cpu}
memory: ${memory}Gi
nvidia.com/gpu: ${gpu}
volumes:
- name: "root-dir"
persistentVolumeClaim:
claimName: "${pvc}"
backoffLimit: 0
容器化开发环境启动后,用户可以通过SSH登录、CodeServer或JupyterLab等方式使用。为了避免容器化开发环境被他人使用,GoblinLab为每种方式设置了统一的密钥,每次启动时随机生成。
在Kubernetes容器中,数据默认不会持久化,这意味着容器删除或重启后数据会丢失。解决方法很简单,只需挂载外部存储即可。GoblinLab为每个用户自动创建默认的外部存储PVC,并挂载到容器的/root目录。用户还可以自定义外部存储的挂载。
Kubernetes集群中的服务在集群外无法直接访问,GoblinLab使用Nginx Ingress + Gateway将集群内的服务暴露到外部。
容器化开发环境的Service资源编排文件如下(已简化细节):
yaml
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-tensorboard
port: 6006
protocol: TCP
targetPort: 6006
selector:
statefulset: ${name}
type: ClusterIP
每当用户启动一个容器化开发环境,GoblinLab将通过接口自动修改Nginx Ingress配置,将服务暴露出来,Ingress配置如下:
yaml
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中以共享方式使用资源,并进行一定比例的超售。但为了确保每个团队公平共享资源,需要对资源进行管理和控制。GoblinLab需要管控的主要资源包括CPU、内存、GPU和存储等。平台根据各个团队的实际需求,将资源划分为多个队列(K8S中的概念为namespace),提供给各个团队使用。
资源配额控制的资源不仅限于CPU、内存、存储、GPU,还包括其他类型,具体参数如下:
GoblinLab的资源隔离包括GPU机器资源的隔离和线上与测试任务的隔离。
在K8S集群中,GPU机器资源较为珍贵,因此为了提高GPU资源利用率,防止CPU任务调度在GPU机器上,设置了污点(Taint)来禁止普通任务调度在GPU节点。
nvidia.com/gpu,value: true,effect: NoSchedule线上与测试任务使用同一K8S集群,但为了保障线上任务的资源,设置了一些机器节点为线上任务的专用资源池。线上任务优先调度在这些线上节点上,如果没有资源,也可以调度在非线上节点上。
node.netease.com/node-pool,value: online,effect: NoSchedule截至现在,音乐机器学习平台(GoblinLab)在容器化方面已经取得了一些阶段性成果。
经过一段时间的尝试,音乐数据平台的Kubernetes集群随着承载业务的增多,以及基于Kubernetes的大数据计算平台(如Flink等)的落地,大量CPU资源将加入,这将带来较大的挑战。
对于算法人员,由物理机迁移到机器学习平台提供的容器化环境,可以带来以下好处:
目前,音乐机器学习平台已经提供了完整的容器化开发基础能力,为进一步提高集群的资源利用率、提升运维效率,后续计划从资源调度策略优化(抢占等)、更丰富的资源监控等方面入手,进一步优化。