(image PullPolicy)
Pod 的核心是运行容器,必须指定容器引擎,比如 Docker,启动容器时,需要拉取镜像,k8s 的镜像拉取策略可以由用户指定:
1、IfNotPresent :优先使用本地已存在的镜像 ,若没有则去仓库拉取镜像,默认的镜像拉取策略
2、Always :每次创建 Pod 都会从仓库拉取镜像,无论本地是否已存在镜像;
3、Never :总是不去仓库拉取镜像,仅使用本地镜像。
注意:对于标签为“:latest”的镜像文件,其默认的镜像获取策略即为“Always”;而对于其他标签的镜像,其默认策略则为“IfNotPresent”。
举例:
--image=nginx:latest 或 nginx 镜像标签为 latest 或者无标签时,默认的拉取策略为 always
--image=nginx:1.14 镜像的标签为非latest 时,默认的拉取策略为IfNotPresent
此外:
在生产环境中部署容器时,你应该避免使用 :latest
标签,因为这使得正在运行的镜像的版本难以追踪,并且难以正确地回滚。(难以追溯版本,且latest一直会不断迭代更新,给版本维护照成困扰)
官方示例:Images | Kubernetes
- kubectl apply -f - <
- apiVersion: v1
- kind: Pod
- metadata:
- name: private-image-test-1
- spec:
- containers:
- - name: uses-private-image
- image: $PRIVATE_IMAGE_NAME
- imagePullPolicy: Always
- command: [ "echo", "SUCCESS" ]
- EOF
- kubectl edit deployment/nginx-deployment
- ......
- template:
- metadata:
- creationTimestamp: null
- labels:
- app: nginx
- spec:
- containers:
- - image: nginx:1.18
- imagePullPolicy: IfNotPresent #镜像拉取策略为 IfNotPresent
- name: nginx
- ports:
- - containerPort: 80
- protocol: TCP
- resources: {}
- terminationMessagePath: /dev/termination-log
- terminationMessagePolicy: File
- dnsPolicy: ClusterFirst
- restartPolicy: Always #Pod的重启策略为 Always,默认值
- schedulerName: default-scheduler
- securityContext: {}
- terminationGracePeriodSeconds: 30
- ......
创建测试案例
- mkdir /opt/demo
- cd /opt/demo
-
- vim pod1.yaml
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-test1
- spec:
- containers:
- - name: nginx
- image: nginx
- imagePullPolicy: Always
- command: [ "echo", "SUCCESS" ]
-
-
- kubectl apply -f pod1.yaml
-
- kubectl get pods -o wide
- pod-test1 0/1 CrashLoopBackOff 4 3m33s
- //此时 Pod 的状态异常,原因是 echo 执行完进程终止,容器生命周期也就结束了
-
- kubectl describe pod pod-test1
- ......
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal Scheduled 2m8s default-scheduler Successfully assigned default/pod-test1 to 192.168.80.12
- Normal Pulled 47s (x4 over 109s) kubelet, 192.168.80.12 Successfully pulled image "nginx"
- Normal Created 47s (x4 over 109s) kubelet, 192.168.80.12 Created container
- Normal Started 47s (x4 over 109s) kubelet, 192.168.80.12 Started container
- Warning BackOff 18s (x7 over 107s) kubelet, 192.168.80.12 Back-off restarting failed container
- Normal Pulling 5s (x5 over 2m8s) kubelet, 192.168.80.12 pulling image "nginx"
- //可以发现 Pod 中的容器在生命周期结束后,由于 Pod 的重启策略为 Always,容器再次重启了,并且又重新开始拉取镜像
修改 pod1.yaml 文件
- cd /opt/demo
- vim pod1.yaml
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-test1
- spec:
- containers:
- - name: nginx
- image: nginx:1.14 #修改 nginx 镜像版本
- imagePullPolicy: Always
- #command: [ "echo", "SUCCESS" ] #删除
-
- //删除原有的资源
- kubectl delete -f pod1.yaml
-
- //更新资源
- kubectl apply -f pod1.yaml
-
- //查看 Pod 状态
- kubectl get pods -o wide
- NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
- pod-test1 1/1 Running 0 33s 10.244.1.66 192.168.80.12 <none>
-
- 可以看到这次pod并没有进行重启。
二、pod的启动命令说明
在k8s的容器中也存在着和docker-compose类似的shell启动命令字段,用于pod容器启动后执行命令的操作。
该字段存在containers中:
kubectl explain pod.spec.containers
command,用于在 pod 中的容器初始化完毕之后运行一个命令
command: ["/bin/sh","-c","touch /tmp/wang.txt"]
- "/bin/sh","-c", 使用sh执行命令
- touch /tmp/hello.txt; 创建一个/tmp/hello.txt 文件
该字段还可以运用args进行编写(起到同样的效果):
args:
- /bin/bash
- touch /tmp/wang.txt
除了 command 参数外,还有一个 args 参数
command 已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个 args 选项,用于传递参数呢?这其实跟 docker 有点关系,kubernetes 中的 command、args 两项其实是实现覆盖 Dockerfile 中 ENTRYPOINT 的功能。
- 如果 command 和 args 均没有写,那么用 Dockerfile 的配置。
- 如果 command 写了,但 args 没有写,那么 Dockerfile 默认的配置会被忽略,执行输入的 command
- 如果 command 没写,但 args 写了,那么 Dockerfile 中配置的 ENTRYPOINT 的命令会被执行,使用当前 args 的参数
- 如果 command 和 args 都写了,那么 Dockerfile 的配置被忽略,执行 command 并追加上 args 参数
三、Pod 容器的 重启策略(restartPolicy)
k8s中重启策略适用于pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长为10s,20s,40s,80s,160s,300s, 300s是最大延迟时长
kubectl explain pod.spec.restartPolicy
(1)docker的重启策略
never 默认策略,在容器退出时不重启容器。 on-failure 在容器非正常退出时(退出状态非0),才会重启容器。此外on-failure还可以指定重启次数(on-failure:3,在容器非正常退出时重启容器,最多重启3次。) always 在容器退出时总是重启容器。 unless-stopped 在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器。
(2)k8s中pod的重启策略
Always 当Pod中的容器退出时,总是重启容器,无论容器退出状态码如何。默认的重启策略 OnFailure 当Pod中的容器异常退出(容器退出状态码非0)时,会重启容器,正常退出(容器退出状态码为0)时不重启容器 Never 当Pod中的容器退出时,总是不重启容器,无论容器退出状态码如何
#注意:K8S 中不支持重启 Pod 资源,只有删除重建
yaml方式创建Deployment和StatefulSet类型时,restartPolicy只能是Always,kubectl run 一个pod可以选择Always,OnFailure,Never三种策略
示例:
- vim pod3.yaml
- apiVersion: v1
- kind: Pod
- metadata:
- name: ppp
- spec:
- containers:
- - name: busybox
- image: busybox
- args:
- - /bin/sh
- - -c
- - sleep 30; exit 3
- #这个Pod配置会创建一个名为ppp的Pod,Pod内含一个名为busybox的容器。
- #该容器会启动一个Shell会话,执行命令先等待30秒,然后以退出码3结束。
-
-
-
- kubectl apply -f pod3.yaml
-
- //查看Pod状态,等容器启动后30秒后执行exit退出进程进入error状态,就会重启次数加1
- kubectl get pods
- NAME READY STATUS RESTARTS AGE
- ppp 1/1 Running 1 50s
修改重启策略为 never
- kubectl delete -f pod3.yaml
-
- vim pod3.yaml
- apiVersion: v1
- kind: Pod
- metadata:
- name: ppp
- spec:
- containers:
- - name: busybox
- image: busybox
- args:
- - /bin/sh
- - -c
- - sleep 30; exit 3
- restartPolicy: Never
- #注意:跟container同一个级别
-
-
-
- kubectl apply -f pod3.yaml
-
- //容器进入error状态不会进行重启
四、pod 的资源限制
4.1 资源限制的了解
在我前面Docker的Cgroup文章中,就提到过,为什么我们对容器进行资源限制。同理:首先K8s中pod使用宿主机的资源默认情况下是无节制的,但是当一个集群搭建成功后并投入生产环境中。
如果其中的某一个pod因为不明原因出现了bug,疯狂占用宿主机资源,抢占其他pod的资源。势必会导致整个集群的瘫痪,所以pod资源的限制是非常有必要的
在资源控制器中我们也可以准确的找到相应的资源控制字段:
kubectl explain deployment.spec.template.spec.containers.resources
kubectl explain statefulset.spec.template.spec.containers.resources
在官方文档中(资源配额 | Kubernetes) 我们可以得知:pod控制的资源总共为三大类:
cpu,memory,hugepages(巨页)。其中我们运用最多的限制还是cpu和memory。
当为Pod中的容器明确了所需的资源请求(request 预留资源限制),调度器会依据这些请求信息,选取一个资源充足且合适的节点来安置该Pod。这意味着,调度决策会确保目标节点拥有足够的资源来满足Pod运行的基本需求。
进一步地,若为容器设定了资源限制(limit),kubelet(负责管理节点上的容器)将扮演监督者的角色,严格监管容器的资源使用情况,确保其消耗的资源量不会超越预设的上限。同时,kubelet还会 预先为容器保留 所声明的资源请求量(也就是你设置的预留资源),确保这部分资源始终专用于该容器
如果 Pod 运行所在的节点具有足够的可用资源,容器可以使用超出所设置的 request 资源量。不过,容器不可以使用超出所设置的 limit 资源量。
如果给容器设置了内存的 limit 值,但未设置内存的 request 值,Kubernetes 会自动为其设置与内存 limit 相匹配的 request 值。 类似的,如果给容器设置了 CPU 的 limit 值但未设置 CPU 的 request 值,则 Kubernetes 自动为其设置 CPU 的 request 值 并使之与 CPU 的 limit 值匹配。
Pod 和 容器 的资源请求和限制:
spec.containers[].resources.requests.cpu //定义创建容器时预分配的CPU资源
spec.containers[].resources.requests.memory //定义创建容器时预分配的内存资源
spec.containers[].resources.limits.cpu //定义 cpu 的资源上限
spec.containers[].resources.limits.memory //定义内存的资源上限
4.2 资源的单位
CPU 资源单位
CPU 资源的 request 和 limit 以 cpu 为单位。Kubernetes 中的一个 cpu 相当于1个 vCPU(1个超线程)。
Kubernetes 也支持带小数 CPU 的请求。spec.containers[].resources.requests.cpu 为 0.5 的容器能够获得一个 cpu 的 、一半 CPU 资源(类似于Cgroup对CPU资源的时间分片)。表达式 0.1 等价于表达式 100m(毫核),表示每 1000 毫秒内容器可以使用的 CPU 时间总量为 0.1*1000 毫秒。
Kubernetes 不允许设置精度小于 1m 的 CPU 资源。
内存 资源单位
内存的 request 和 limit 以字节为单位。可以以整数表示,或者以10为底数的指数的单位(E、P、T、G、M、K)来表示, 或者以2为底数的指数的单位(Ei、Pi、Ti、Gi、Mi、Ki)来表示。
如:1KB=10^3=1000,1MB=10^6=1000000=1000KB,1GB=10^9=1000000000=1000MB
1KiB=2^10=1024,1MiB=2^20=1048576=1024KiB
PS:在买硬盘的时候,操作系统报的数量要比产品标出或商家号称的小一些,主要原因是标出的是以 MB、GB为单位的,1GB 就是1,000,000,000Byte,而操作系统是以2进制为处理单位的,因此检查硬盘容量时是以MiB、GiB为单位,1GiB=2^30=1,073,741,824,相比较而言,1GiB要比1GB多出1,073,741,824-1,000,000,000=73,741,824Byte,所以检测实际结果要比标出的少一些。
4.3 实例应用
示例1:
- apiVersion: v1
- kind: Pod
- metadata:
- name: frontend
- spec:
- containers:
- - name: app #应用容器
- image: images.my-company.example/app:v4
- env: #设置环境变量
- - name: MYSQL_ROOT_PASSWORD #变量名称
- value: "password" #变量的值
- resources:
- requests: #预留资源限制
- memory: "64Mi" #该容器至少需要64MiB的内存。
- cpu: "250m" #该容器至少需要0.25个CPU核心
- limits: #最大资源限制
- memory: "128Mi" #限制该容器最多使用128MiB的内存
- cpu: "500m" #限制该容器最多使用0.5个CPU核心
- - name: log-aggregator #日志聚合容器
- image: images.my-company.example/log-aggregator:v6
- resources:
- requests:
- memory: "64Mi"
- cpu: "250m"
- limits:
- memory: "128Mi"
- cpu: "500m"
-
- 此例子中的 Pod 有两个容器。每个容器的 request 值为 0.25 cpu 和 64MiB 内存,每个容器的 limit 值
- 为 0.5 cpu 和 128MiB 内存。那么可以认为该 Pod 的总的资源 request 为 0.5 cpu 和 128 MiB 内
- 存,总的资源 limit 为 1 cpu 和 256MiB 内存。
示例2:
- apiVersion: v1
- kind: Pod
- metadata:
- name: ceyan
- spec:
- containers:
- - name: nginx-web01
- image: nginx
- env:
- - name: WEB_ROOT_PASSWORD
- value: "password"
- resources:
- requests:
- memory: "64Mi"
- cpu: "250m"
- limits:
- memory: "128Mi"
- cpu: "500m"
- - name: db01
- image: mysql
- env:
- - name: MYSQL_ROOT_PASSWORD
- value: "abc666"
- #设置环境变量 MYSQL_ROOT_PASSWORD,值为 "abc123",这是MySQL的root用户的密码
- resources:
- requests:
- memory: "512Mi"
- cpu: "0.5"
- limits:
- memory: "1Gi"
- cpu: "1"
#查看pod的资源使用情况
kubectl describe pod ceyan
#查看node节点pod资源使用详情
kubectl describe nodes node01
五、Pod 容器的探针
5.1 探针的概念及其作用
探针是由 kubelet 对容器执行的定期诊断(pod中探针又分为三类):
存活探针(livenessProbe)探测容器是否运行正常。如果探测失败则kubelet杀掉容器(不是Pod),容器会根据重启策略决定是否重启。
如果容器不提供存活探针,则默认状态为Success。
就绪探针(readinessProbe)探测Pod是否能够进入READY状态,并做好接收请求的准备。如果探测失败Pod则会进入NOTREADY状态(READY为0/1)并且从所关联的service资源的端点(endpoints)中踢出,service将不会再把访问请求转发给这个Pod
如果容器不提供就绪探针,则默认状态为Success。
启动探针(startupProbe)探测容器内的应用是否启动成功,在启动探针探测成功之前,其它类型的探针都会暂时处于禁用状态
如果容器没有配置 startupProbe, 则默认状态为 Success。
注:以上规则可以同时定义。在readinessProbe检测成功之前,Pod的running状态是不会变成ready状态的。
注意:启动探针只是在容器启动后按照配置满足一次后就不再进行后续的探测了。存活探针和就绪探针会一直探测到Pod生命周期结束为止
5.2 探针的探测方式
●exec :在容器内执行指定命令。如果命令退出时返回码为0则认为诊断成功。
●tcpSocket :对指定端口上的容器的IP地址进行TCP检查(三次握手)。如果端口打开,则诊断被认为是成功的。
●httpGet :对指定的端口和路径上的容器的IP地址执行HTTPGet请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的
探针探测结果有以下值:
Success(成功):表示通过检测。
Failure(失败):表示未通过检测。
Unknown(未知):表示检测没有正常进行。(诊断失败,因此不会采取任何行动)
5.3 探针字段
探针(Probe)有许多可选字段,可以用来更加精确的控制Liveness(存活探针)和Readiness(就绪探针)两种探针的行为(Probe):
- initialDelaySeconds:容器启动后要等待多少秒后就探针开始工作,单位“秒”,默认是 0s,最小值是 0s;
- periodSeconds:执行探测的时间间隔(单位是秒),默认为 10s,最小值是 1s,
- timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为 1s,最小值是 1s,
- successThreshold:探针检测失败后认为成功的最小连接成功次数,默认为 1,在 Liveness和startup探针中必须为 1(为了避免不必要的等待),最小值为 1。
- failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认为 3,最小值为 1
5.4 探针实验测试
5.4.1 LivenessProbe 存活探针 使用
5.4.1.1 通过exec方式做健康探测
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- test: liveness
- name: liveness-exec
- spec:
- containers:
- - name: liveness
- image: k8s.gcr.io/busybox
- args:
- - /bin/sh
- - -c
- - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60
- livenessProbe: #存活探针
- exec:
- command:
- - cat
- - /tmp/healthy
- failureThreshold: 1 #探测失败的重试次数
- initialDelaySeconds: 5 #容器启动后 五秒后开启探测(第六秒开始执行)
- periodSeconds: 5 #每隔五秒探测一次
可以看到 Pod 中只有一个容器。kubelet 在执行第一次探测前需要等待 5 秒,kubelet 会每 5 秒执行一次存活探测。kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 当到达第 31 秒时,这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
#initialDelaySeconds:指定 kubelet 在执行第一次探测前应该等待5秒,即第一次探测是在容器启动后的第6秒才开始执行。默认是 0 秒,最小值是 0。
#periodSeconds:指定了 kubelet 应该每 5 秒执行一次存活探测。默认是 10 秒。最小值是 1。
#failureThreshold: 当探测失败时,Kubernetes 将在放弃之前重试的次数。 存活探测情况下的放弃就意味着重新启动容器。就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
#timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。(在 Kubernetes 1.20 版本之前,exec 探针会忽略 timeoutSeconds 探针会无限期地 持续运行,甚至可能超过所配置的限期,直到返回结果为止。)
- apiVersion: v1
- kind: Pod
- metadata:
- name: liveness-exec
- namespace: default
- spec:
- containers:
- - name: liveness-exec-container
- image: busybox
- imagePullPolicy: IfNotPresent
- command: ["/bin/sh","-c","touch /tmp/live ; sleep 30; rm -rf /tmp/live; sleep 666"]
- livenessProbe:
- exec:
- command: ["test","-e","/tmp/live"] #查看文件是否存在,若不存在则重启容器
- initialDelaySeconds: 1 #容器启动后一秒后开启探测
- periodSeconds: 3 #每隔三秒探测一次
容器在初始化后,执行(/bin/sh -c "touch /tmp/live; sleep 30; rm -rf /tmp/live; sleep 666")首先创建一个 /tmp/live 文件,然后执行睡眠命令,睡眠 30 秒,到时间后执行删除 /tmp/live 文件命令。
而设置的存活探针检检测方式为执行 shell 命令,用 test -e 命令检测 /tmp/live 文件是否存在,如果能成功执行这条命令一次(默认successThreshold:1),存活探针就认为探测成功,由于没有配置(failureThreshold、timeoutSeconds),所以执行(test -e /tmp/live)并只等待1s,如果1s内执行后返回失败,探测失败。
在前 30 秒内,由于文件存在,所以存活探针探测时执行 test -e /tmp/live 命令成功执行。30 秒后 live 文件被删除,所以执行命令失败,Kubernetes 会根据 Pod 设置的重启策略来判断,是否重启 Pod。
5.4.1.2 httpGet 方式
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- test: liveness
- name: liveness-http
- spec:
- containers:
- - name: liveness
- image: k8s.gcr.io/liveness
- args:
- - /server #容器启动时执行的命令行参数,这里假设启动一个web服务器
- livenessProbe:
- httpGet:
- path: /healthz #探针将向此路径发送HTTP GET 请求来检查容器健康状态
- port: 8080 #容器上监听的端口号,探针将通过此端口进行健康检查
- httpHeaders:
- - name: Custom-Header
- value: Awesome
- #名为 Custom-Header,值为 Awesome 的HTTP头,将在探针请求中附加此自定义头部。
- initialDelaySeconds: 3 #容器启动后,延迟3秒开始执行第一次探针检查
- periodSeconds: 3 #每隔3秒执行一次探针检查
在这个配置文件中,可以看到 Pod 也只有一个容器。initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。
任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。
- apiVersion: v1
- kind: Pod
- metadata:
- name: liveness-httpget
- namespace: default
- spec:
- containers:
- - name: liveness-httpget-container
- image: soscscs/myapp:v1
- imagePullPolicy: IfNotPresent
- ports:
- - name: http
- containerPort: 80
- livenessProbe:
- httpGet:
- port: http
- path: /index.html
- initialDelaySeconds: 1
- periodSeconds: 3
- timeoutSeconds: 10
-
- kubectl create -f httpget.yaml
-
- kubectl exec -it liveness-httpget -- rm -rf /usr/share/nginx/html/index.html
-
- kubectl get pods
- NAME READY STATUS RESTARTS AGE
- liveness-httpget 1/1 Running 1 2m44s
5.4.1.3 tcpSocket 方式
- apiVersion: v1
- kind: Pod
- metadata:
- name: goproxy
- labels:
- app: goproxy
- spec:
- containers:
- - name: goproxy
- image: k8s.gcr.io/goproxy:0.1
- ports:
- - containerPort: 8080
- readinessProbe: #就绪探针
- tcpSocket:
- port: 8080 #就绪探针通过TCP连接检查容器的8080端口是否就绪
- initialDelaySeconds: 5 #容器启动后等待5秒开始执行就绪探针
- periodSeconds: 10 #探针每10秒执行一次,以检查服务是否依然处于就绪状态
-
- livenessProbe: #存活探针
- tcpSocket:
- port: 8080 #存活探针同样通过TCP连接检查容器的8080端口
- initialDelaySeconds: 15
- periodSeconds: 20
这个例子同时使用 readinessProbe 和 livenessProbe 探测。kubelet 会在容器启动 5 秒后发送第一个 readinessProbe 探测。这会尝试连接 goproxy 容器的 8080 端口。如果探测成功,kubelet 将继续每隔 10 秒运行一次检测。除了 readinessProbe 探测,这个配置包括了一个 livenessProbe 探测。kubelet 会在容器启动 15 秒后进行第一次 livenessProbe 探测。就像 readinessProbe 探测一样,会尝试连接 goproxy 容器的 8080 端口。如果 livenessProbe 探测失败,这个容器会被重新启动。
- apiVersion: v1
- kind: Pod
- metadata:
- name: probe-tcp
- spec:
- containers:
- - name: nginx
- image: soscscs/myapp:v1
- livenessProbe:
- initialDelaySeconds: 5 #容器启动后等待5秒开始执行第一次探针检查
- timeoutSeconds: 1 #探针执行的超时时间
- tcpSocket:
- port: 8080
- periodSeconds: 10 #每隔10秒探测一次
- failureThreshold: 2 #连续失败2次探针检查后,才会认为该容器不健康并采取相应动作
该容器会在启动后的5秒开始被监控,通过周期性(每10秒)尝试与容器内的8080端口建立TCP连接来验证容器是否仍然运行并且能够响应。如果连续两次(在每次尝试间隔10秒的情况下,相当于最多20秒无响应)探测失败,Kubernetes将认定该容器不再健康,并依据Pod的重启策略采取行动,通常情况下这意味着容器会被重启
5.4.2 ReadinessProbe 就绪探针使用
livenessProbe+readinessProbe通过httpGet探测方法的实验过程:
本次过程我将nginx的网页目录中index.html修改为test.html。并写下了下面的资源yaml进行测试
- apiVersion: v1
- kind: Pod
- metadata:
- name: my-readiness
- spec:
- containers:
- - name: readiness-container
- image: soscscs/myapp:v1
- imagePullPolicy: IfNotPresent
- ports:
- - name: http #端口名称
- containerPort: 80 #容器内部监听的HTTP服务端口
- readinessProbe:
- httpGet:
- port: 80 #探针检查的端口
- path: /index01.html #检查的URL路径
- initialDelaySeconds: 1
- periodSeconds: 3
- livenessProbe:
- httpGet:
- port: http #使用端口名称http,映射到实际端口80
- path: /index.html #存活探针检查的URL路径
- initialDelaySeconds: 1 #容器启动后等待1秒开始执行首次存活探针
- periodSeconds: 3 #存活探针每3秒执行一次
- timeoutSeconds: 10 #探针请求的超时时间,如果在10秒内没有响应,则视为探针失败
kubectl exec -it my-readiness sh
/ # cd /usr/share/nginx/html
/usr/share/nginx/html # ls
50x.html index.html
/usr/share/nginx/html # echo "this is web" > index01.html
kubectl exec -it my-readiness -- rm -rf /usr/share/nginx/html/index.html
就绪探针案例2:
- apiVersion: v1
- kind: Pod
- metadata:
- name: myapp1
- labels:
- app: myapp
- spec:
- containers:
- - name: myapp
- image: soscscs/myapp:v1
- ports:
- - name: http
- containerPort: 80
- readinessProbe:
- httpGet:
- port: 80
- path: /index.html
- initialDelaySeconds: 5
- periodSeconds: 5
- timeoutSeconds: 10
- ---
- apiVersion: v1
- kind: Pod
- metadata:
- name: myapp2
- labels:
- app: myapp
- spec:
- containers:
- - name: myapp
- image: soscscs/myapp:v1
- ports:
- - name: http
- containerPort: 80
- readinessProbe:
- httpGet:
- port: 80
- path: /index.html
- initialDelaySeconds: 5
- periodSeconds: 5
- timeoutSeconds: 10
- ---
- apiVersion: v1
- kind: Pod
- metadata:
- name: myapp3
- labels:
- app: myapp
- spec:
- containers:
- - name: myapp
- image: soscscs/myapp:v1
- ports:
- - name: http
- containerPort: 80
- readinessProbe:
- httpGet:
- port: 80
- path: /index.html
- initialDelaySeconds: 5
- periodSeconds: 5
- timeoutSeconds: 10
- ---
- apiVersion: v1
- kind: Service
- metadata:
- name: myservice
- spec:
- selector:
- app: myapp
- type: ClusterIP
- ports:
- - name: http
- port: 80
- targetPort: 80
我们选择一个pod把其中的网页文件删除,看看endpoints中是否有该pod的IP地址。
关于Endpoints: 实质上是一个存储在 etcd 中的资源对象,记录了一个 Service 对应的所有后端 Pod 的访问地址(IP地址和端口)
kubectl exec -it pod/myapp1 -- rm -rf /usr/share/nginx/html/index.html
//readiness探测失败,Pod 无法进入READY状态,且端点控制器将从 endpoints 中剔除删除该 Pod 的 IP 地址
5.4.3 启动探针 startupProbe
启动探针(startupProbe)的设计目的正是为了处理那些启动时间较长的应用场景,确保在应用完全准备好服务请求之前,不会因为存活探针或就绪探针的误判而导致容器被错误地重启。
启动探针的作用:启动探针专门用于监控容器从启动到完全初始化完毕的过程,它允许容器有足够的时间进行初始化,而不会受到存活探针或就绪探针的干扰。正如您所说,一旦启动探针成功至少一次,存活探针和就绪探针才会开始它们的工作。
- ports:
- - name: liveness-port
- containerPort: 8666
- hostPort: 8666
-
- livenessProbe:
- httpGet:
- path: /healthz
- port: liveness-port
- failureThreshold: 1
- periodSeconds: 10
-
- startupProbe:
- httpGet:
- path: /healthz
- port: liveness-port
- failureThreshold: 30
- periodSeconds: 10
六、Pod 容器的启动和退出动作
- postStart 配置 exec.command 字段设置 Linux 命令,实现当应用容器启动时,会执行的额外操作
-
- preStop 配置 exec.command 字段设置 Linux 命令,实现当应用容器退出时,会执行的最后一个操作
- apiVersion: v1
- kind: Pod
- metadata:
- name: lifecycle-demo
- spec:
- containers:
- - name: lifecycle-demo-container
- image: soscscs/myapp:v1
- lifecycle: #此为关键字段
- postStart: #定义了容器启动后的执行命令
- exec:
- command: ["/bin/sh", "-c", "echo Hello from the postStart handler >> /var/log/nginx/message"]
- preStop: #定义了容器终止前的执行命令
- exec:
- command: ["/bin/sh", "-c", "echo Hello from the poststop handler >> /var/log/nginx/message"]
-
- volumeMounts:
- - name: message-log
- mountPath: /var/log/nginx/
- readOnly: false
- #在主容器和初始化容器中挂载了名为message-log的数据卷,挂载路径为/var/log/nginx/,
- 并且设置为可读写(readOnly: false),允许容器向该目录写入数据
-
- initContainers:
- - name: init-myservice #初始化容器的名称
- image: soscscs/myapp:v1
- command: ["/bin/sh", "-c", "echo 'Hello initContainers' >> /var/log/nginx/message"]
- #执行的shell命令,表明初始化过程已完成
-
- volumeMounts:
- - name: message-log
- mountPath: /var/log/nginx/
- readOnly: false
-
- volumes:
- - name: message-log #数据卷的名称
- hostPath:
- path: /data/volumes/nginx/log/ #指定数据卷类型为宿主机路径
- type: DirectoryOrCreate #目录不存在则创建
//在 node02 节点上查看
由上可知,init Container先执行,然后当一个主容器启动后,Kubernetes 将立即发送 postStart 事件。
删除 pod 后,再在 node02 节点上查看
kubectl delete pod lifecycle-demo
由上可知,当在容器被终结之前, Kubernetes 将发送一个 preStop 事件。
七、pod 的状态说明
1、pending:pod已经被系统认可了,但是内部的container还没有创建出来。这里包含调度到node上的时间以及下载镜像的时间,会持续一小段时间。
2、Running:pod已经与node绑定了(调度成功),而且pod中所有的container已经创建出来,至少有一个容器在运行中,或者容器的进程正在启动或者重启状态。--这里需要注意pod虽然已经Running了,但是内部的container不一定完全可用。因此需要进一步检测container的状态。
3、Succeeded:这个状态很少出现,表明pod中的所有container已经成功的terminated了,而且不会再被拉起了。
4、Failed:pod中的所有容器都被terminated,至少一个container是非正常终止的。(退出的时候返回了一个非0的值或者是被系统直接终止)
5、unknown:由于某些原因pod的状态获取不到,有可能是由于通信问题。 一般情况下pod最常见的就是前两种状态。而且当Running的时候,需要进一步关注container的状态
详细说明:
- (1)Pod 一直处于Pending状态
-
- Pending状态意味着Pod的YAML文件已经提交给Kubernetes,API对象已经被创建并保存在Etcd当中。但是,
- 这个Pod里有些容器因为某种原因而不能被顺利创建。比如,调度不成功(可以通过kubectl describe pod命
- 令查看到当前Pod的事件,进而判断为什么没有调度)。
-
- 可能原因:资源不足(集群内所有的Node都不满足该Pod请求的CPU、内存、GPU等资源); HostPort 已
- 被占用(通常推荐使用Service对外开放服务端口)。
-
-
- (2)Pod一直处于Waiting 或 ContainerCreating状态
- 首先还是通过 kubectl describe pod命令查看当前Pod的事件。可能的原因有:
- 1)镜像拉取失败,比如镜像地址配置错误、拉取不了国外镜像源(gcr.io)、私有镜像密钥配置错误、镜像太
- 大导致拉取超时 (可以适当调整kubelet的-image-pull-progress-deadline和-runtime-request-
- timeout选项)等。
- 2)CNI网络错误,一般需要检查CNI网络插件的配置,比如:无法配置Pod 网络、无法分配IP地址。
- 3)容器无法启动,需要检查是否打包了正确的镜像或者是否配置了正确的容器参数
- 4)Failed create pod sandbox,查看kubelet日志,原因可能是磁盘坏道(input/output error)。
-
-
- (3)Pod 一直处于ImagePullBackOff状态
-
- 通常是镜像名称配置错误或者私有镜像的密钥配置错误导致。
-
-
- (4)Pod 一直处于CrashLoopBackOff状态
-
- 此状态说明容器曾经启动了,但又异常退出。这时可以先查看一下容器的日志。
- 通过命令kubectl logs 和kubectl logs --previous 可以发下一些容器退出的原因,比如:容器进程退出、
- 健康检查失败退出;此时如果还未发现线索,还而已到容器内执行命令(kubectl exec cassandra - cat
- /var.log/cassandra/system.loq)来进一步查看退出原因;如果还是没有线索,那就需要SSH登录该Pod所在
- 的Node上,查看Kubelet或者Docker的日志进一步排查。
-
- (5) Pod处于Error状态
-
- 通常处于Error状态说明Pod启动过程中发生了错误。
-
- 常见的原因:依赖的ConfigMap、Secret或PV等不存在;请求的资源超过了管理员设置的限制,比如超过了
- LimitRange等;违反集群的安全策略,比如违反了PodSecurityPolicy.等;容器无法操作集群内的资源,
- 比如开启RDAC后,需要为ServiceAccount配置角色绑定。
-
-
- (6) Pod 处于Terminating或 Unknown状态
- 从v1.5开始,Kubernetes不会因为Node失联而删除其上正在运行的Pod,而是将其标记为Terminating 或
- Unknown 状态。想要删除这些状态的Pod有三种方法:
-
- 1)从集群中删除Node。使用公有云时,kube-controller-manager会在VM删除后自动删除对应的Node。而在
- 物理机部署的集群中,需要管理员手动删除Node(kubectl delete node)。
-
- 2)Node恢复正常。kubelet会重新跟kube-apiserver通信确认这些Pod的期待状态,进而再决定删除或者继
- 续运行这些Pod。用户强制删除,用户可以执行(kubectl delete pods pod-name --grace-period=0 --
- force)强制删除Pod。除非明确知道Pod的确处于停止状态(比如Node所在VM或物理机已经关机),否则不建
- 议使用该方法。特别是StatefulSet 管理的Pod,强制删除容易导致脑裂或数据丢失等问题。
-
-
- 3)Pod行为异常,这里所说的行为异常是指Pod没有按预期的行为执行,比如没有运行podSpec 里面设置的命
- 令行参数。这一般是podSpec yaml文件内容有误,可以尝试使用 --validate 参数重建容器,比如
- (kubectl delete pod mypod 和 kubectl create --validate -f mypod.yaml);也可以查看创建后的
- podSpec是否是对的,比如(kubectl get pod mypod -o yaml);修改静态Pod的Manifest后未自动重建,
- kubelet 使用inotify 机制检测 /etc/kubernetes/manifests 目录(可通过 kubelet 的
- -pod-manifest-path 选项指定)中静态Pod的变化,并在文件发生变化后重新创建相应的 Pod。
- 但有时也会发现修改静态Pod的 Manifest后未自动创建新 Pod的情景,此时已过简单的修复方法
- 是重启 Kubelet。
-
-
- Unknown 这个异常状态意味着Pod的状态不能持续地被 kubelet汇报给 kube-apiserver,这很有可能是
- 主从节点(Master 和 Kubelet)间的通信出现了问题。
-
-
-
- (7)pod从创建到成功或失败的事件
-
- PodScheduled
- pod正处于调度中,刚开始调度的时候,hostip还没绑定上,持续调度之后,有合适的节点就会绑定hostip,然后更新etcd数据
- Initialized
- pod中的所有初始化容器已经初启动完毕
-
- Ready
- pod中的容器可以提供服务了
-
- Unschedulable
- 不能调度,没有合适的节点
CrashLoopBackOff: 容器退出,kubelet正在将它重启
InvalidImageName: 无法解析镜像名称
ImageInspectError: 无法校验镜像
ErrImageNeverPull: 策略禁止拉取镜像
ImagePullBackOff: 正在重试拉取
RegistryUnavailable: 连接不到镜像中心
ErrImagePull: 通用的拉取镜像出错
CreateContainerConfigError: 不能创建kubelet使用的容器配置
CreateContainerError: 创建容器失败
m.internalLifecycle.PreStartContainer 执行hook报错
RunContainerError: 启动容器失败
PostStartHookError: 执行hook报错
ContainersNotInitialized: 容器没有初始化完毕
ContainersNotReady: 容器没有准备完毕
ContainerCreating: 容器创建中
PodInitializing:pod 初始化中
DockerDaemonNotReady: docker还没有完全启动
NetworkPluginNotReady: 网络插件还没有完全启动
Evicte: pod被驱赶
Container生命周期
1、Waiting:启动到运行中间的一个等待状态。
2、Running:运行状态。
3、Terminated:终止状态。 如果没有任何异常的情况下,container应该会从Waiting状态变为Running状态,这时容器可用。
但如果长时间处于Waiting状态,container会有一个字段reason表明它所处的状态和原因,如果这个原因很容易能标识这个容器再也无法启动起来时,例如ContainerCannotRun,整个服务启动就会迅速返回。(这里是一个失败状态返回的特性,不详细阐述)
pod 的生命周期
- 收到kubectl指令-->初始化容器(init container),生成文件(init可以有多个,但是不可以同时运行)-->
- 创建容器成功-->start容器-->readnessProbe(就绪性特征):就绪检测--> liveness Probe (存活性探针):
- 生存检测-->stop容器
-
-
-
-
- Pause 阶段:当 Pod 中的容器被暂停时,Pod 会进入 Pause 阶段。这通常发生在节点上发生调度变化
- 时,为了确保容器状态的一致性,Pod 会被暂停并且等待恢复。
-
- Init 阶段:如果 Pod 中定义了 Init 容器,那么在主容器启动之前,Init 容器会先启动并执行其初始化
- 任务。Pod 进入 Init 阶段直到所有 Init 容器都成功完成。
-
- 应用启动阶段:一旦所有的 Init 容器成功完成,主容器将会启动并开始执行应用程序。
-
- 存活阶段:Pod 中的容器正在运行,并且没有出现致命错误,此时 Pod 处于存活状态。
-
- 就绪阶段:Pod 中的容器已经准备好接收流量。如果 Pod 中的所有容器都已经就绪,那么整个 Pod 就处于
- 就绪状态,可以开始接收请求。