forked from yangpeng14/DevOps
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
yangpeng
committed
Apr 15, 2020
1 parent
b758df3
commit 993e22c
Showing
2 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
> - 作者:jasonminghao | ||
> - 链接:https://www.cnblogs.com/jasonminghao/p/12681352.html?utm_source=tuicool&utm_medium=referral | ||
## 目录导航 | ||
|
||
- 1、Pod容器钩子最终目的 | ||
- 2、何为Pod容器钩子 | ||
- 3、基于PostStart演示 | ||
- 4、基于PreStop演示 | ||
- 5、优雅停止Java应用 | ||
|
||
## 1、Pod容器钩子最终目的 | ||
|
||
之前在生产环境中使用`dubbo框架`,由于服务更新的过程中,容器直接被停止了,部分请求仍会被分发到终止的容器,导致有用户会访问服务出现`500错误`,这部分错误请求数据占用的比较少,因为Pod是滚动一对一更新。由于这个问题出现了,考虑使用优雅的终止方式,将错误请求将至到最低,直至滚动更新完全不会影响到用户。 | ||
|
||
### 简单分析一下 `优雅的停止Pod` | ||
|
||
微服务中,网关会把流量分配给每个Pod节点,如:我们线上更新Pod的时候 | ||
|
||
1、如果我们直接把Pod给杀死,那这部分流量就无法得到正确的处理,会影响到部分用户访问,一般来说网关或者注册中心会将我们的服务保持一个心跳,过了心跳超时后就会自动摘除我们的服务,但是有一个问题就是超时时间可能是10s、30s、甚至是60s,虽然不会大规模的影响我们业务系统,但是一定会对用户产生轻微的抖动。 | ||
|
||
2、如果我们在停止服务前执行一条命令,通知网关或注册中心摘掉这台Pod,即服务进行下线,那么注册中心就会标记这个Pod服务已经下线,不进行流量转发,用户也就不会有任何的影响,这就是优雅停止,将滚动更新的影响最小化。 | ||
|
||
## 2、何为Pod容器钩子 | ||
|
||
Kubernetes 最小调度单位为 `Pod`,它为Pod中的容器提供了生命周期钩子,钩子能够使得容器感知其生命周期内的所有事件,并且当相应的生命周期的钩子被调用时运行执行的代码,而Pod 钩子是由Kubelet发起的。 | ||
|
||
容器钩子两类触发点: | ||
|
||
- `PostStart`:容器创建后 | ||
- `PreStop`:容器终止前 | ||
|
||
### PostStart | ||
|
||
这个钩子在容器创建后立即执行。但是,并不能保证钩子将在容器 `ENTRYPOINT` 之前运行。没有参数传递给处理程序。 | ||
|
||
容器 `ENTRYPOINT` 和 `钩子` 执行是`异步操作`。如果钩子花费太长时间以至于容器不能运行或者挂起,容器将不能达到running状态。 | ||
|
||
### PreStop | ||
|
||
这个钩子在容器终止之前立即被调用。它是`阻塞的`,意味着它是同步的,所以它必须在删除容器调用发出之前完成。 | ||
|
||
如果钩子在执行期间挂起,Pod阶段将停留在running状态并且永不会达到failed状态。 | ||
|
||
如果 `PostStart` 或者 `PreStop` 钩子失败,容器将会被 `kill`。 | ||
|
||
用户应该使它们的钩子处理程序尽可能的`轻量`。 | ||
|
||
## 3、基于PostStart演示 | ||
|
||
如果 `PostStart` 或者 `PreStop` 钩子失败,它会杀死容器。所以我们应该让钩子函数尽可能的轻量。当然有些情况下,长时间运行命令是合理的,比如在停止容器之前预先保留状态。 | ||
|
||
1、我们echo一段话追加到/tmp/message,在Pod启动前操作 | ||
|
||
```bash | ||
$ cat >>hook_test.yaml<<EOF | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: hook-demo1 | ||
spec: | ||
containers: | ||
- name: hook-demo1 | ||
image: nginx | ||
lifecycle: | ||
postStart: | ||
exec: | ||
command: ["/bin/sh", "-c", "echo 1 > /tmp/message"] | ||
EOF | ||
``` | ||
|
||
2、应用 hook_test.yaml | ||
|
||
```bash | ||
$ kubectl apply -f hook_test.yaml | ||
``` | ||
|
||
3、可以通过下面查看结果 | ||
|
||
```bash | ||
$ kubectl get pods | grep hook-demo1 | ||
|
||
hook-demo1 1/1 Running 0 49s | ||
|
||
$ kubectl exec -it hook-demo1 /bin/bash | ||
root@hook-demo1:/# cat /tmp/message | ||
|
||
1 | ||
``` | ||
|
||
## 4、基于PreStop演示 | ||
|
||
下面示例中,定义一个Nginx Pod,设置了PreStop钩子函数,即在容器退出之前,优雅的关闭Nginx。 | ||
|
||
```bash | ||
$ cat >>hook_test.yaml<<EOF | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: hook-demo2 | ||
spec: | ||
containers: | ||
- name: hook-demo2 | ||
image: nginx | ||
lifecycle: | ||
preStop: | ||
exec: | ||
command: ["/usr/sbin/nginx","-s","quit"] | ||
EOF | ||
``` | ||
|
||
## 5、优雅停止Java应用 | ||
|
||
我们都知道java应用的启动和停止都需要时间,为了更加优雅的停止,可以通过 `pidof` 获取到java进程ID,循环通过kill命令往PID发送 `SIGTERM` 信号。 | ||
|
||
```yaml | ||
lifecycle: | ||
preStop: | ||
exec: | ||
command: ["/bin/bash","-c","PID=`pidof java` && kill -SIGTERM $PID && while ps -p $PID > /dev/null;do sleep 1; done;"] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
## 背景 | ||
|
||
最近接到任务,要使k8s集群支持调度`GPU`,我对硬件资源不是很懂,大概看了看官方,简单梳理了一下思路,便开始了踩坑之路(本片文章是无坑文档,请放心使用/参考) | ||
|
||
## 前提条件: | ||
|
||
对于实验学习而言,在k8s集群中,至少保证1台node节点是有显卡的(本文是`NVIDIA`),其他品牌显卡请出门右转自行Google | ||
|
||
## 集群环境说明: | ||
|
||
> 本文是学习实验环境,所以只有 `ai-gpu-flask2.novalocal` 支持 `GPU`,其余的节点都是虚拟机。最终验证也是在 `ai-gpu-flask2.novalocal` 上进行。 | ||
|系统|名称|角色|k8s版本| | ||
|:--:|:--:|:--:|:--:| | ||
|centos7.6|ai-gpu-flask2.novalocal|worker|kubeadm v1.16.8| | ||
|centos7.6|master|etcd,master|kubeadm v1.16.8| | ||
|centos7.6|node1|worker|kubeadm v1.16.8| | ||
|centos7.6|node2|worker|kubeadm v1.16.8| | ||
|
||
## 预安装 | ||
|
||
- 本步骤在支持`GPU`节点上执行 | ||
- 参考链接:https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#introduction | ||
|
||
要验证您的 `GPU` 是否具有 `CUDA` 功能,请转至与您的发行版等效的“系统属性”,或者在命令行中输入: | ||
|
||
```bash | ||
$ lspci | grep -i nvidia | ||
|
||
00:06.0 3D controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1) | ||
``` | ||
|
||
要确定您正在运行的发行版和发行版号,请在命令行中键入以下内容: | ||
```bash | ||
$ uname -m && cat /etc/*release | ||
``` | ||
|
||
要验证系统上安装的`gcc`版本,请在命令行中键入以下内容: | ||
```bash | ||
$ gcc --version | ||
``` | ||
|
||
可以通过运行以下命令找到系统正在运行的内核版本: | ||
```bash | ||
$ uname -r | ||
``` | ||
|
||
当前运行的内核的内核头文件和开发包可以通过以下方式安装: | ||
```bash | ||
$ sudo yum install kernel-devel-$(uname -r) kernel-headers-$(uname -r) | ||
``` | ||
|
||
选择您正在使用的平台并下载 `NVIDIA CUDA Toolkit` | ||
|
||
> 下载链接:http://developer.nvidia.com/cuda-downloads | ||
``` | ||
$ wget http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda-repo-rhel7-10-2-local-10.2.89-440.33.01-1.0-1.x86_64.rpm | ||
$ sudo rpm -i cuda-repo-rhel7-10-2-local-10.2.89-440.33.01-1.0-1.x86_64.rpm | ||
$ sudo yum clean all | ||
$ sudo yum -y install nvidia-driver-latest-dkms cuda | ||
$ sudo yum -y install cuda-drivers | ||
``` | ||
|
||
要安装显示驱动程序,必须首先禁用 `Nouveau` 驱动程序: | ||
在以下位置创建文件 | ||
```bash | ||
$ vim /etc/modprobe.d/blacklist-nouveau.conf | ||
|
||
# 具有以下内容: | ||
$ blacklist nouveau | ||
$ options nouveau modeset=0 | ||
``` | ||
|
||
重新生成内核initramfs: | ||
```bash | ||
$ sudo dracut --force | ||
``` | ||
|
||
## 验证 | ||
|
||
```bash | ||
# 正常来说会输出已经安装过的程序 | ||
$ rpm -qa | grep nvidia | ||
|
||
# 正常来说会输出已经安装过的程序 | ||
$ rpm -qa | grep cuda | ||
``` | ||
## 满足先决条件 | ||
|
||
- 阅读文档:https://github.com/NVIDIA/k8s-device-plugin#prerequisites | ||
- 说明:该文章中描述了安装`k8s-device-plugin`的先决条件 | ||
- 条件一 NVIDIA drivers ~= 384.81 已经满足 | ||
- 条件四 Kubernetes version >= 1.10 已经满足 | ||
|
||
## 先决条件准备 | ||
|
||
- 满足`条件二` nvidia-docker version > 2.0 | ||
- 参考链接:https://github.com/NVIDIA/nvidia-docker#centos-7-docker-ce-rhel-7475-docker-ce-amazon-linux-12 | ||
|
||
```bash | ||
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) | ||
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo | ||
|
||
$ sudo yum install -y nvidia-container-toolkit | ||
$ sudo systemctl restart docker | ||
``` | ||
- 满足`条件三` docker configured with nvidia as the default runtime. | ||
- 参考链接:https://github.com/NVIDIA/k8s-device-plugin#preparing-your-gpu-nodes | ||
|
||
```bash | ||
$ vim /etc/docker/daemon.json | ||
``` | ||
|
||
```json | ||
{ | ||
"default-runtime": "nvidia", | ||
"runtimes": { | ||
"nvidia": { | ||
"path": "/usr/bin/nvidia-container-runtime", | ||
"runtimeArgs": [] | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## 验证 | ||
|
||
> 参考链接:https://github.com/NVIDIA/nvidia-docker#usage | ||
```bash | ||
$ docker run --gpus all nvidia/cuda:10.0-base nvidia-smi | ||
|
||
# 报错如下: | ||
docker: Error response from daemon: OCI runtime create failed: unable to retrieve OCI runtime error (open /run/containerd/io.containerd.runtime.v1.linux/moby/36cd5b1383a3ca5d2196d05e3655122ea4703af69bdfedfdfc6657f/log.json: no such file or directory): fork/exec /usr/bin/nvidia-container-runtime: no such file or directory: unknown. | ||
ERRO[0000] error waiting for container: context canceled | ||
``` | ||
- 解决办法 | ||
|
||
```bash | ||
$ yum search nvidia-container-runtime | ||
$ yum -y install nvidia-container-runtime.x86_64 nvidia-container-runtime-hook.x86_64 | ||
``` | ||
|
||
## 再次验证 | ||
|
||
```bash | ||
$ docker run --gpus all nvidia/cuda:10.0-base nvidia-smi | ||
``` | ||
![](/img/fc46531a-b6fa-408f-8577-9521c97f5fda.png) | ||
|
||
### 至此,docker环境下使用`GPU`已经完成,接下来部署`k8s-device-plugin`使k8s也支持`GPU` | ||
|
||
> 参考链接:https://github.com/NVIDIA/k8s-device-plugin#enabling-gpu-support-in-kubernetes | ||
```bash | ||
$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/1.0.0-beta5/nvidia-device-plugin.yml | ||
``` | ||
|
||
### 验证 | ||
|
||
通过命令`kubectl describe node ai-gpu-flask2.novalocal`可以看到node节点有`ncidia.com/gpu`就正常了 | ||
|
||
![](/img/b5fc1888-c27a-4732-9df4-84805641c62a.png) | ||
|
||
### 运行一个pod | ||
|
||
- 参考链接:https://github.com/NVIDIA/k8s-device-plugin#running-gpu-jobs | ||
|
||
```yaml | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: gpu-pod | ||
spec: | ||
containers: | ||
- name: cuda-container | ||
image: nvidia/cuda:9.0-devel | ||
resources: | ||
limits: | ||
nvidia.com/gpu: 2 # requesting 2 GPUs | ||
- name: digits-container | ||
image: nvidia/digits:6.0 | ||
resources: | ||
limits: | ||
nvidia.com/gpu: 2 # requesting 2 GPUs | ||
``` | ||
## 结束语 | ||
经过两天踩坑到填坑的魔鬼历程,才有了这篇无坑文章,大家且用且珍惜。 | ||
GPU的学习成本较高,普通的虚拟机是不可能完成这个实验操作的,如果你看到了这篇文档,不妨先收藏一下,日后如果有需求,可以第一时间拿出来参考。 |