概述
kubernetes 检查恢复机制 -PodPreset
一:检查恢复机制
容器健康检查和恢复机制
在 k8s 中,可以为 Pod 里的容器定义一个健康检查"探针"。kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器是否运行作为依据。这种机制,是生产环境中保证应用健康存活的重要手段.
命令模式探针
Kubernetes 文档中的例子:
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
- name: liveness
image: daocloud.io/library/nginx
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
它在启动之后做的第一件事是在 /tmp 目录下创建了一个 healthy 文件,以此作为自己已经正常运行的标志。而 30 s 过后,它会把这个文件删除掉。
与此同时,定义了一个这样的 livenessProbe(健康检查)。它的类型是 exec,它会在容器启动后,在容器里面执行一句我们指定的命令,比如:“cat /tmp/healthy”。这时,如果这个文件存在,这条命令的返回值就是 0,Pod 就会认为这个容器不仅已经启动,而且是健康的。这个健康检查,在容器启动 5 s 后开始执行(initialDelaySeconds: 5),每 5 s 执行一次(periodSeconds: 5)。
创建Pod:
[root@master diandian]# kubectl create -f test-liveness-exec.yaml
查看 Pod 的状态:
[root@master diandian]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test-liveness-exec 1/1 Running 0 10s
由于已经通过了健康检查,这个 Pod 就进入了 Running 状态。
30 s 之后,再查看一下 Pod 的 Events:
[root@master diandian]# kubectl describe pod test-liveness-exec
发现,这个 Pod 在 Events 报告了一个异常:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
显然,这个健康检查探查到 /tmp/healthy 已经不存在了,所以它报告容器是不健康的。那么接下来会发生什么呢?
再次查看一下这个 Pod 的状态:
# kubectl get pod test-liveness-exec
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
这时发现,Pod 并没有进入 Failed 状态,而是保持了 Running 状态。这是为什么呢?
RESTARTS 字段从 0 到 1 的变化,就明白原因了:这个异常的容器已经被 Kubernetes 重启了。在这个过程中,Pod 保持 Running 状态不变。
注意:Kubernetes 中并没有 Docker 的 Stop 语义。所以虽然是 Restart(重启),但实际却是重新创建了容器。
这个功能就是 Kubernetes 里的Pod 恢复机制,也叫 restartPolicy。它是 Pod 的 Spec 部分的一个标准字段(pod.spec.restartPolicy),默认值是 Always,即:任何时候这个容器发生了异常,它一定会被重新创建。
小提示:
Pod 的恢复过程,永远都是发生在当前节点上,而不会跑到别的节点上去。事实上,一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定发生了变化(pod.spec.node 字段被修改),否则它永远都不会离开这个节点。这也就意味着,如果这个宿主机宕机了,这个 Pod 也不会主动迁移到其他节点上去。
而如果你想让 Pod 出现在其他的可用节点上,就必须使用 Deployment 这样的"控制器"来管理 Pod,哪怕你只需要一个 Pod 副本。这就是一个单 Pod 的 Deployment 与一个 Pod 最主要的区别。
http get方式探针
[root@master diandian]# vim liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
创建该pod
[root@master diandian]# kubectl create -f liveness-httpget.yaml
pod/liveness-httpget-pod created
查看当前pod的状态
[root@master diandian]# kubectl describe pod liveness-httpget-pod
...
Liveness: http-get http://:http/index.html delay=1s timeout=1s period=3s #success=1 #failure=3
...
测试将容器内的index.html删除掉
登陆容器
[root@master diandian]# kubectl exec liveness-httpget-pod -c liveness-exec-container -it -- /bin/sh
/ # ls
bin dev etc home lib media mnt proc root run sbin srv sys tmp usr var
/ # mv /usr/share/nginx/html/index.html index.html
/ # command terminated with exit code 137
可以看到,当把index.html移走后,这个容器立马就退出了。
此时,查看pod的信息
[root@master diandian]# kubectl describe pod liveness-httpget-pod
...
Normal Killing 1m kubelet, node02 Killing container with id docker://liveness-exec-container:Container failed liveness probe.. Container will be killed and recreated.
...
看输出,容器由于健康检查未通过,pod会被杀掉,并重新创建
[root@master diandian]# kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 1/1 Running 1 33m
restarts 为 1
重新登陆容器查看
重新登陆容器,发现index.html又出现了,证明容器是被重拉了。
[root@master diandian]# kubectl exec liveness-httpget-pod -c liveness-exec-container -it -- /bin/sh
/ # cat /usr/share/nginx/html/index.html
Pod 的恢复策略
可以通过设置 restartPolicy,改变 Pod 的恢复策略。一共有3种:
- Always:在任何情况下,只要容器不在运行状态,就自动重启容器;
- OnFailure:只在容器异常时才自动重启容器;
- Never: 从来不重启容器。
实际使用时,需要根据应用运行的特性,合理设置这三种恢复策略。
比如,一个 Pod,它只计算 1+1=2,计算完成输出结果后退出,变成 Succeeded 状态。这时,你如果再用 restartPolicy=Always 强制重启这个 Pod 的容器,就没有任何意义了。
而如果要关心这个容器退出后的上下文环境,比如容器退出后的日志,文件和目录,就需要将 restartPolicy 设置为 Never。因为一旦容器被自动重新创建,这些内容就有可能丢失掉了(被垃圾回收了)。
官方文档把 restartPolicy 和 Pod 里容器的状态,以及 Pod 状态的对应关系,总结了非常复杂的一大堆情况。实际上,你根本不需要死记硬背这些对应关系,只要记住如下两个基本的设计原理即可:
- 只要 Pod 的 restartPolicy 指定的策略允许重启异常的容器(比如:Always),那么这个 Pod 就会保持 Running 状态,并进行容器重启。否则,Pod 就会进入 Failed 状态 。
- 对于包含多个容器的 Pod,只有它里面所有的容器都进入异常状态后,Pod 才会进入 Failed 状态。在此之前,Pod 都是 Running 状态。此时,Pod 的 READY 字段会显示正常容器的个数,比如:
[root@master diandian]# kubectl get pod test-liveness-exec
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 Running 1 1m
二:kubernetes-PodPreset (1.11-1.20 可用 1.20取消了此功能)
PodPreset详解
Pod 的字段那么多,不可能全记住,Kubernetes 能不能自动给 Pod 填充某些字段呢?
比如,开发人员只需要提交一个基本的、非常简单的 Pod YAML,Kubernetes 就可以自动给对应的 Pod 对象加上其他必要的信息,比如 labels,annotations,volumes 等等。而这些信息,可以是运维人员事先定义好的。这样,开发人员编写 Pod YAML 的门槛,就被大大降低了。
一个叫作 PodPreset(Pod 预设置)的功能 已经出现在了 v1.11 版本的 Kubernetes 中。
理解 Pod Preset
Pod Preset 是一种 API 资源,在 pod 创建时,用户可以用它将额外的运行时需求信息注入 pod。 使用标签选择器(label selector)来指定 Pod Preset 所适用的 pod。是专门用来对 Pod 进行批量化、自动化修改的一种工具对象,使用 Pod Preset 使得 pod 模板编写者不必显式地为每个 pod 设置信息。这样,使用特定服务的 pod 模板编写者不需要了解该服务的所有细节。
PodPreset 如何工作
Kubernetes 提供了准入控制器 (PodPreset),该控制器被启用时,会将 Pod Preset 应用于接收到的 pod 创建请求中。 当出现 pod 创建请求时,系统会执行以下操作:
- 检索所有可用 PodPresets 。
- 检查 PodPreset 的标签选择器与要创建的 pod 的标签是否匹配。
- 尝试合并 PodPreset 中定义的各种资源,并注入要创建的 pod。
- 发生错误时抛出事件,该事件记录了 pod 信息合并错误,同时在 不注入 PodPreset 信息的情况下创建 pod。
- 为改动的 pod spec 添加注解,来表明它被 PodPreset 所修改。 注解形如: podpreset.admission.kubernetes.io/podpreset-": “”。
- 一个 Pod 可能不与任何 Pod Preset 匹配,也可能匹配多个 Pod Preset。 同时,一个 PodPreset 可能不应用于任何 Pod,也可能应用于多个 Pod。 当 PodPreset 应用于一个或多个 Pod 时,Kubernetes 修改 pod spec。 对于 Env、 EnvFrom 和 VolumeMounts 的改动, Kubernetes 修改 pod 中所有容器的规格,对于卷的改动,Kubernetes 修改 Pod spec。
启用 Pod Preset
为了在集群中使用 Pod Preset,必须确保以下几点:
- 已启用 api 类型 settings.k8s.io/v1alpha1/podpreset。 (kubeadm方式安装的kubernets集群)
这可以通过在 API 服务器的 --runtime-config 配置项中包含 settings.k8s.io/v1alpha1=true 来实现。
api-server配置文件添加如下配置:重启api-server服务
[root@k8s-master ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
command:
- --runtime-config=settings.k8s.io/v1alpha1=true
[root@master manifests]# systemctl restart kubelet
[root@k8s-master ~]# kubectl api-versions
admissionregistration.k8s.io/v1beta1
apiextensions.k8s.io/v1beta1
apiregistration.k8s.io/v1
apiregistration.k8s.io/v1beta1
apps/v1
apps/v1beta1
apps/v1beta2
authentication.k8s.io/v1
authentication.k8s.io/v1beta1
authorization.k8s.io/v1
authorization.k8s.io/v1beta1
autoscaling/v1
autoscaling/v2beta1
batch/v1
batch/v1beta1
ceph.rook.io/v1
certificates.k8s.io/v1beta1
events.k8s.io/v1beta1
extensions/v1beta1
networking.k8s.io/v1
policy/v1beta1
rbac.authorization.k8s.io/v1
rbac.authorization.k8s.io/v1beta1
rook.io/v1alpha2
scheduling.k8s.io/v1beta1
settings.k8s.io/v1alpha1
storage.k8s.io/v1
storage.k8s.io/v1beta1
v1
- 已启用准入控制器 PodPreset。 (二进制安装)
启用的一种方式是在 API 服务器的 --enable-admission-plugins 配置项中包含 PodPreset 。
[root@master ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
--enable-admission-plugins=PodPreset,NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction
[root@master ~]# systemctl restart kubelet
- 已经通过在相应的名字空间中创建 PodPreset 对象,定义了 Pod preset。
案例
例:现开发人员编写了如下一个 pod.yaml 文件:
# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: daocloud.io/library/nginx
ports:
- containerPort: 80
如果运维人员看到了这个 Pod,他一定会连连摇头:这种 Pod 在生产环境里根本不能用啊!
运维人员就可以定义一个 PodPreset 对象。在这个对象中,凡是他想在开发人员编写的 Pod 里追加的字段,都可以预先定义好。
# vim preset.yaml
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: allow-database
spec:
selector:
matchLabels:
role: frontend
env:
- name: DB_PORT
value: "6379"
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
selector:
这里的selector表示后面这些追加的定义,只会作用于 selector 所定义的、带有"role: frontend"标签的 Pod 对象,这就可以防止"误伤"。
然后定义了一组 Pod 的 Spec 里的标准字段,以及对应的值。
比如:
env 里定义了 DB_PORT 这个环境变量
volumeMounts 定义了容器 Volume 的挂载目录
volumes 定义了一个 emptyDir 的 Volume。
接下来,假定运维人员先创建了这个 PodPreset,然后开发人员才创建 Pod:
# kubectl create -f preset.yaml
# kubectl create -f pod.yaml
Pod 运行之后,查看这个 Pod 的 API 对象:
# kubectl get pod website -o yaml
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
annotations:
podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
containers:
- name: website
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
ports:
- containerPort: 80
env:
- name: DB_PORT
value: "6379"
volumes:
- name: cache-volume
emptyDir: {}
清楚地看到,这个 Pod 里多了新添加的 labels、env、volumes 和 volumeMount 的定义,它们的配置跟 PodPreset 的内容一样。此外,这个 Pod 还被自动加上了一个 annotation 表示这个 Pod 对象被 PodPreset 改动过。
注意:
PodPreset 里定义的内容,只会在 Pod API 对象被创建之前追加在这个对象本身上,而不会影响任何 Pod 的控制器的定义。
比如,现在提交的是一个 nginx-deployment,那么这个 Deployment 对象本身是永远不会被 PodPreset 改变的,被修改只是这个Deployment创建出来的所有 Pod。
这里有一个问题:如果你定义了同时作用于一个 Pod 对象的多个 PodPreset,会发生什么呢?
Kubernetes 项目会帮你合并(Merge)这两个 PodPreset 要做的修改。而如果它们要做的修改有冲突的话,这些冲突字段就不会被修改。
三:使用yaml创建Deployment
k8s deployment资源创建流程
- 用户通过 kubectl 创建 Deployment。
- Deployment 创建 ReplicaSet。
- ReplicaSet 创建 Pod。
简介
Deployment是一个定义及管理多副本应用(即多个副本 Pod)的新一代对象,与Replication Controller相比,它提供了更加完善的功能,使用起来更加简单方便。
如果Pod出现故障,对应的服务也会挂掉,所以Kubernetes提供了一个Deployment的概念 ,目的是让Kubernetes去管理一组Pod的副本,也就是副本集 ,这样就能够保证一定数量的副本一直可用,不会因为某一个Pod挂掉导致整个服务挂掉。
Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。
这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 k8s 中,叫作"控制器"模式(controller pattern)。Deployment 扮演的正是 Pod 的控制器的角色。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-vol
volumes:
- name: nginx-vol
emptyDir: {}
END
最后
以上就是甜美电脑为你收集整理的k8s——kubernetes 检查恢复机制--PodPreset的全部内容,希望文章能够帮你解决k8s——kubernetes 检查恢复机制--PodPreset所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复