为什么写这个指南?
我一直在创业公司 understand.ai 实习,在那里我留意到了一个麻烦:首先得在本地设计一个机器学习算法,然后将其放到云上用不同的参数和数据集来训练模型。这第二步,将算法放到云上进行全面的训练,所耗费的时间要比想象的更长,通常让人很沮丧而且涉及到很多陷阱。
因此,我决定解决这个问题,让第二步变得容易、简单和快捷。
我写了这篇很有用的指南,描述了每个人如何设置他们自己的 Kubernetes GPU 集群来加速他们的工作。
给深度学习研究者的新处理过程:
用 Kubernetes GPU 集群进行自动化的深度学习训练,它能极大地改进在云上训练模型的过程。
(点击放大图像)
声明
下面的章节可能有些武断。Kubernetes 是一个进化的、快节奏的环境,这就意味着这个指南很可能会在某个时间过时,这取决于作者的空余时间和个人贡献。因此,非常感谢对此的贡献。
如果你需要重温下 Kubernetes 知识,下面这些文章很有用。
核心想法是用一个很小的只有 CPU 的 master 节点来控制一组 GPU worker 节点。
(点击放大图像)
在我们使用集群之前,要先对其初始化,这一点很重要。因此,要手动地初始化每个节点,随后将它加入集群中。
该配置非常适合这里所描述的用例。对其他用例、操作系统等,需要进一步地调整配置。
Master
Worker
关于安全:当然,如果你想在产品中使用,就应该启用某些防火墙;这里出于简单考虑我停用了 ufw。设置 Kubernetes 以用于实际的产品中,理所当然应该启用某些防火墙如 ufw、iptables 或者你的云提供商的防火墙。在云上设置集群会更加复杂。通常云提供商的防火墙与主机级别的防火墙是分开的。你可能需要停用 ufw、并开启云提供商防火墙的规则来让这篇文档里的步骤生效。
这些指南涵盖了我在 Ubuntu 16.04 的经验,对其他操作系统可能适用,也可能不适用。
我创建了两个脚本对 master 和 worker 节点完全初始化。脚本如下所示。如果你想用快速通道,可以直接使用这些脚本。否则,我建议你阅读一步一步的指南。
将下面相应的脚本拷贝到你的 master 和 worker 上。
MASTER 节点
运行初始化脚本,记下 token。
Token 的格式类似于 --token f38242.e7f3XXXXXXXXe231e。
- chmod + x init - master.sh sudo. / init - master.sh < IP - of - master >
WORKER 节点
运行初始化脚本,使用正确的 token 和 master IP。
端口通常是 6443。
- chmod + x init - worker.sh sudo. / init - worker.sh < Token - of - Master > <IP - of - master > :<Port >
MASTER 节点
1. 将 Kubernetes Repository 加入 packagemanager
- sudo bash - c 'apt-get update && apt-get install -y apt-transport-https
- curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
- cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
- deb http://apt.kubernetes.io/ kubernetes-xenial main
- EOF
- apt-get update'
2. 安装 docker-engine、kubeadm、kubectl 和 kubernetes-cni
- sudo apt - get install - y docker - engine sudo apt - get install - y kubelet kubeadm kubectl kubernetes - cni sudo groupadd docker sudo usermod - aG docker $USER echo 'You might need to reboot / relogin to make docker work correctly'
3. 由于我们想创建一个使用 GPU 的集群,我们要在 master 节点上启用 GPU 加速功能。记住,在 Kubernetes 之后的版本中,这一步可能被淘汰了或者完全更改了。
3.I 在集群初始化之前,添加 GPU 支持到 Kubeadm 配置。
这一步必须对集群中的每一个节点进行,即使某些节点没有 GPU。
- sudo vim / etc / systemd / system / kubelet.service.d / <<Number >> -kubeadm.conf
因此,在 ExecStart 后追加上标签 --feature-gates="Accelerators=true",最终格式如下:
- ExecStart = /usr/bin / kubelet $KUBELET_KUBECONFIG_ARGS[...]--feature - gates = "Accelerators=true"
3.II 重启 kubelet
- sudo systemctl daemon - reload sudo systemctl restart kubelet
4. 现在初始化 master 节点。
你需要 master 节点的 IP。而且,这一步会向你提供认证信息,用于添加 worker 节点,因为要记住你的 token。
Token 的格式类似于 --token f38242.e7f3XXXXXXXXe231e 130.211.XXX.XXX:6443。
- sudo kubeadm init--apiserver - advertise - address = <ip - address >
5. 由于 Kubernetes 1.6 从 ABAC 卷管理变成了 RBAC 式,因此我们需要向用户公布认证信息。每一次登录机器,都需要执行这一步。
- sudo cp / etc / kubernetes / admin.conf $HOME / sudo chown $(id - u) : $(id - g) $HOME / admin.conf export KUBECONFIG = $HOME / admin.conf
6. 安装网络插件让节点能相互通信。Kubernetes 1.6 对网络插件有一些要求,如:
这篇 GoogleSheet 文档包含很多合适的网络插件。链接: GoogleSheet Network Add-on comparison。
出于个人的偏好,我会使用 wave-works。
- kubectl apply - f https: //git.io/weave-kube-1.6
7. 现在都设置好了。检查所有的 pod 都在线上,确认一切运转顺利。
- kubectl get pods--all - namespaces
N. 如果你想撤掉 master 节点,你需要重置它。
- sudo kubeadm reset
WORKER NODE
前面几步对你而言应该很熟悉了,能让进程加快一些。
1. 将 Kubernetes Repository 加入 packagemanager
- sudo bash - c 'apt-get update && apt-get install -y apt-transport-https
- curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
- cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
- deb http://apt.kubernetes.io/ kubernetes-xenial main
- EOF
- apt-get update'
2. 安装 docker-engine、kubeadm、kubectl 和 kubernetes-cni
- sudo apt - get install - y docker - engine sudo apt - get install - y kubelet kubeadm kubectl kubernetes - cni sudo groupadd docker sudo usermod - aG docker $USER echo 'You might need to reboot / relogin to make docker work correctly'
3. 由于我们想创建一个使用 GPU 的集群,我们要在 master 节点上启用 GPU 加速功能。记住,在 Kubernetes 之后的版本中,这一步可能被淘汰了或者完全更改了。
3.I 在集群初始化之前,添加 GPU 支持到 Kubeadm 配置。
这一步必须对集群中的每一个节点进行,即使某些节点没有 GPU。
- sudo vim / etc / systemd / system / kubelet.service.d / <<Number >> -kubeadm.conf
因此,在 ExecStart 后追加上标签 --feature-gates="Accelerators=true",最终格式如下:
- ExecStart = /usr/bin / kubelet $KUBELET_KUBECONFIG_ARGS[...]--feature - gates = "Accelerators=true"
3.II 重启 kubelet
- sudo systemctl daemon - reload sudo systemctl restart kubelet
4. 现在我们将 worker 加入到集群中。
你需要记住 master 节点的 token,所以查看下节点。
- sudo kubeadm join--token f38242.e7f3XXXXXXe231e 130.211.XXX.XXX: 6443
5. 完成。在 master 上检查节点,看看是否一切运转顺利。
- kubectl get nodes
N. 如果你想撤掉 worker 节点,你需要将该节点从集群中移除,然后重置该节点。从集群中移除 worker 节点是很有帮助的。
在 master 节点上:
- kubectl delete node < worker node name >
在 worker 节点上
- sudo kubeadm reset
客户端
为了控制你的集群,如从客户端控制 master,你需要对客户端的正确用户进行认证。该指南中并没有为客户端创建一个单独的用户,我们只是从 master 节点复制用户。相信我,这样做会更简单。
[会在将来加入如何添加用户的指导]
1. 在客户端安装 kubectl。我只在 mac 上测试过,但应该也适用 linux。我不知道是否适用于 windows,但又有谁关心 windows 呢。
Mac
- brew install kubectl
Ubuntu 你要么遵循官方指南 https://kubernetes.io/docs/tasks/tools/install-kubectl/,要么从上面的 worker 指南中提取需要的步骤(可能只在 Ubuntu 上可行)。
2. 将 master 的 admin 认证拷贝到客户端。
- scp uai@130.211.XXX.64 : ~ / admin.conf~ / .kube /
3. 将 admin.conf 配置和认证信息添加到 Kubernetes 配置中。你需要对每个代理都执行该步骤。
- export KUBECONFIG = ~ / .kube / admin.conf
你可以在本地的客户端上使用 kubectl 了。
4. 你可以测试列出所有的 pod
- kubectl get pods--all - namespaces
安装 Kubernetes dashboard
Kubernetes dashboard 非常的漂亮,它向那些跟我一样喜欢摆弄脚本的人提供了很多功能。要使用 dashboard 的话,你需要让客户端运行,RBAC 会确保这一点。
你可以直接在 master 节点上或者从客户端运行下面的步骤。
1. 检查已经安装了 dashboard: kubectl get pods --all-namespaces | grep dashboard
2. 如果没有安装,则安装它
- kubectl create - f https: //git.io/kube-dashboard
如果不成功,检查. yaml 中定义的容器 git.io/kube-dashboard 是否存在。(这个软件故障曾让我花费了很多时间)
为了能访问你的 dashboard,你需要在客户端进行认证。
3. 添加 dashboard 代理到客户端。
在客户端运行:
- kubectl proxy
4. 用浏览器访问 dashboard,访问 127.0.0.1:8001/ui。
这里帮助你运行一个需要 GPU 权限的 Docker 容器。
在该指南中,我选择创建一个 Docker 容器的例子,它用 TensorFlow GPU 二进制文件,并能在 Jupyter 笔记本中运行 TensorFlow 程序。
请记住,该指南适用于 Kubernetes 1.6,因此可能不适用于今后的变化。
为了能够让你的带 CUDA 的 Nvidia GPU 运行,你需要将 Nvidia 驱动和 CUDA 库文件传给容器。因此,我们将使用 hostPath,让 Kubernetes pod 能访问它们。实际的路径因机器而异,因为它们是由 Nvidia 驱动和 CUDA 安装程序来设置的。
- volumes: -hostPath: path: /usr/lib / nvidia - 375 / bin name: bin - hostPath: path: /usr/lib / nvidia - 375 name: lib
将包含有驱动和 CUDA 的卷加载到容器中的正确目录下。根据你的容器的具体要求,这些设置可能有所不同。
- volumeMounts: -mountPath: /usr/local / nvidia / bin name: bin - mountPath: /usr/local / nvidia / lib name: lib
因为你要告诉 Kubernetes 你需要 n 个 GPU,所以你可以在这里定义这些需求。
- resources: limits: alpha.kubernetes.io / nvidia - gpu: 1
这就是你需要创建 Kuberntes 1.6 容器的所有东西。
这是我的所有经验:
Kubernetes + Docker + Machine Learning + GPUs = 顶呱呱
文件 example-gpu-deployment.yaml 描述了两部分,部署和服务,因为我想让 juptyer 笔记本能被外部访问。
运行 kubectl 来让其对外可见。
- kubectl create - f deployment.yaml
文件 deployment.yaml 内容如下:
- ---apiVersion: extensions / v1beta1 kind: Deployment metadata: name: tf - jupyter spec: replicas: 1 template: metadata: labels: app: tf - jupyter spec: volumes: -hostPath: path: /usr/lib / nvidia - 375 / bin name: bin - hostPath: path: /usr/lib / nvidia - 375 name: lib containers: -name: tensorflow image: tensorflow / tensorflow: 0.11.0rc0 - gpu ports: -containerPort: 8888 resources: limits: alpha.kubernetes.io / nvidia - gpu: 1 volumeMounts: -mountPath: /usr/local / nvidia / bin name: bin - mountPath: /usr/local / nvidia / lib name: lib---apiVersion: v1 kind: Service metadata: name: tf - jupyter - service labels: app: tf - jupyter spec: selector: app: tf - jupyter ports: -port: 8888 protocol: TCP nodePort: 30061 type: LoadBalancer---
为了验证这些设置是正确的,你可以访问 JupyterNotebook 实例,链接是 http://<IP-of-master>:30061。
现在我们要验证你的 JupyterNotebook 实例可以访问 GPU。因此,在一个新的笔记本中运行下面的程序。它将列出 tensorflow 可用的所有服务。
- from tensorflow.python.client import device_lib
- def get_available_devices() : local_device_protos = device_lib.list_local_devices() return [x.name
- for x in local_device_protos]
- print(get_available_devices())
输出结果应该类似于 [u'/cpu:0', u'/gpu:0']。
Get 命令,输出基本信息
- kubectl get services#列出该命名空间的所有服务kubectl get pods--all - namespaces#列出所有命名空间的所有pod kubectl get pods - o wide#列出该命名空间的所有pod,给出细节kubectl get deployments#列出所有部署kubectl get deployment my - dep#列出给定部署
Describe 命令,输出冗长的信息
- kubectl describe nodes < node - name > kubectl describe pods < pod - name >
删除资源
- kubectl delete - f. / pod.yaml#删除pod,其类型和名字被定义在pod.yaml中kubectl delete pod,
- service baz foo#删除名字为"baz"和"foo"的pod和服务kubectl delete pods,
- services - l name = <myLabel > #删除标签名为myLabel的pod和服务kubectl - n < namespace > delete po,
- svc--all#删除命名空间my - ns的所有pod和服务
进入某个 pod 的 bash 控制台:
- kubectl exec - it < pod - name > --/bin/bash
有很多指南、github 仓库、问题和帮助过我的人们。
我想感谢每一个人的帮助。
特别感谢创业公司 understand.ai 的支持。
Frederic Tausch - 原创 - Langhalsdino
该项目采用 MIT 许可,具体细节请查看 LICENSE.md 文件。
来源: http://www.infoq.com/cn/articles/kubernetes-gpu-cluster-to-automate-deep-learning-training