k8s中pod详解
文章目录
一、Pod的详解
1. Pod基础概念
Pod 是 Kubernetes 中的最小部署单元,它代表集群中的一个运行进程。一个 Pod 可以包含一个或多个紧密耦合的容器,这些容器共享网络和存储资源。在 Kubernetes 中,Pod 是管理容器的基础结构,其他如 Deployment、StatefulSet、DaemonSet 等控制器对象都围绕 Pod 进行扩展。
2. Kubrenetes集群中Pod有如下两种使用方式
-
单容器 Pod:
最常见的用法,一个 Pod 只包含一个容器。这种情况下,Pod 就相当于容器的封装,Kubernetes 管理的是 Pod,而不是容器。apiVersion: v1 kind: Pod metadata: name: single-container-pod spec: containers: - name: nginx image: nginx:1.14 -
多容器 Pod:
一个 Pod 可以包含多个容器,这些容器通常有共同的资源需求,如共享网络和存储,彼此间可以通过localhost进行通信。apiVersion: v1 kind: Pod metadata: name: multi-container-pod spec: containers: - name: app image: my-app:latest - name: sidecar image: log-collector:latest
3. Pause 容器(基础容器)
在每个 Pod 中,都会有一个特殊的容器被称为 Pause 容器,它的主要职责是提供 Pod 的 Linux 命名空间基础。它使得 Pod 内的所有容器共享网络和存储资源。Pause 容器运行时,它会创建一个 Linux 命名空间,其他容器将共享该命名空间来进行网络通信和存储操作。
4. Pod 中的共享资源
-
网络共享:
每个 Pod 分配一个唯一的 IP 地址,Pod 内的所有容器共享该 IP 和端口。它们能够通过localhost进行通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)① 每个pod 分配一个独特IP地址
② pod 内部所有容器共享这个IP和端口 ,能通过 localhost直接通信
③ 与外界 通信时, 必须使用宿主机的端口映射 -
存储共享:
Pod 可以指定多个共享的 Volume,这些 Volume 可以被 Pod 内的所有容器访问,确保容器重启后数据不会丢失。① Pod可以指定多个共享的Volume,所有容器都可以访问
② Volume可以用来持久化存储 ,确保容器重启后数据不会丢失
5. 总结
每个Pod都有一个特殊的被称为“基础容器”的Pause容器。Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或者多个紧密相关的用户应用容器。
Pod 包含一个特殊的“基础容器”的Pause容器 ,它负责 pod网络 和存储资源,除了pauser容器 pod还包含多个应用容器,他们可以共同工作
6. Pod 的使用场景
- 单一进程应用:最常见的用法,一个 Pod 用于运行一个容器。
- 多进程协作:多个容器在同一个 Pod 内共享网络和存储资源,适用于紧密耦合的服务。
7. Pod 的类型
- 自主式 Pod:
这种 Pod 不具备自我修复的能力。如果它所在的节点故障,Pod 会被删除,且不会自动恢复。 - 控制器管理的 Pod:
Kubernetes 中通常通过控制器(如 Deployment、StatefulSet)来管理 Pod。控制器提供副本管理、滚动升级、自动修复等功能。
8. Pod容器的分类
8.1 基础容器(infrastructure container)
维护整个 Pod 网络和存储空间
node 节点中操作
启动一个Pod时,k8s会自动启动一个基础容器
cat /opt/kubernetes/cfg/kubelet
......
--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0"
每次创建 Pod 时候就会创建,运行的每一个Pod都有一个 pause-amd64 的基础容器自动会运行,对于用户是透明的
docker ps -a
registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 "/pause"
8.2 初始化容器(initcontainers)
8.2.1 Init容器启动过程
Init容器必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以Init容器能够提供了一种简单的阻塞或延迟应用容器的启动的方法。
Init 容器与普通的容器非常像,除了以下两点:
-
Init 容器总是运行到成功完成为止
启动 --》运行 --》 结束 --》 退出 成功 再运行下一个INit
-
每个 Init 容器都必须在下一个 Init 容器启动之前成功完成启动和退出
如果 Pod 的 Init 容器失败,k8s 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的重启策略(restartPolicy)为 Never,它不会重新启动。
8.2.2 Init 的容器作用
因为init容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:
-
Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。例如,没有必要仅为了在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具而去FROM 一个镜像来生成一个新的镜像。
独立使用的工具 -
Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
-
应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
-
Init 容器能以不同于Pod内应用容器的文件系统视图运行。因此,Init容器可具有访问 Secrets 的权限,而应用容器不能够访问。
-
由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,
直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动。
9、应用容器(Maincontainer)
9.1 并行启动
这个例子先运行两个init容器再运行myapp-container
官网示例:
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/init-containers/
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
这个例子是定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待 myservice 启动, 第二个等待 mydb 启动。 一旦这两个 Init容器都启动完成,Pod 将启动 spec 中的应用容器。
先去查看 找不到错误信息
kubectl describe pod myapp-pod
使用logs查看 制定好 init容器名称
kubectl logs myapp-pod -c init-myservice
查到是因为没有myservice
添加service
vim myservice.yaml
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
kubectl create -f myservice.yaml
apply
kubectl get svc
kubectl get pods -n kube-system
kubectl get pods
然后使用相同方式查看,可以看到第一个init执行成功,第二个错误的问题是没有mydb
vim mydb.yaml
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
kubectl create -f mydb.yaml
kubectl get pods
9.2 特别说明(总结)
- 在Pod启动过程中,Init容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。
- 如果由于运行时或失败退出,将导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试。然而,如果Pod的restartPolicy设置为Always,Init容器失败时会使用RestartPolicy策略。
- 在所有的Init容器没有成功之前,Pod将不会变成Ready状态。Init容器的端口将不会在Service中进行聚集。正在初始化中的Pod处于Pending状态,但应该会将Initializing状态设置为true。
- 如果Pod重启,所有Init容器必须重新执行。
- 对Init容器spec的修改被限制在容器image字段,修改其他字段都不会生效。更改Init容器的image字段,等价于重启该Pod。
- Init容器具有应用容器的所有字段。除了readinessProbe,因为Init容器无法定义不同于完成(completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行。
- 在Pod中的每个app和Init容器的名称必须唯一;与任何其它容器共享同一个名称,会在验证时抛出错误
10. 配置的核心部分
10.1 Pod 的镜像拉取策略
Pod 中的容器需要通过指定镜像来启动,镜像拉取策略有以下几种:
IfNotPresent(默认):如果本地已有镜像,不会拉取新的镜像,仅在本地缺失时拉取。Always:每次启动 Pod 时都会拉取镜像。Never:不拉取镜像,仅使用本地镜像。
imagePullPolicy: Always
官方示例: https://kubernetes.io/docs/concepts/containers/images
kubectl apply -f - <<EOF
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
//master01 上操作
kubectl edit deployment/nginx-deployment
......
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.15.4
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
......
案列1
1)创建测试案例
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 create -f pod1.yaml
kubectl get pods -o wide
pod-test1 0/1 CrashLoopBackOff 4 3m33s
kubectl edit pod pod-test1
//此时 Pod 的状态异常,原因是 echo 执行完进程终止,容器生命周期也就结束了
kubectl describe pod pod-test1
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m10s default-scheduler Successfully assigned default/pod-test1 to 192.168.80.11
Normal Pulled 46s (x4 over 119s) kubelet, 192.168.80.11 Successfully pulled image "nginx"
Normal Created 46s (x4 over 119s) kubelet, 192.168.80.11 Created container
Normal Started 46s (x4 over 119s) kubelet, 192.168.80.11 Started container
Warning BackOff 19s (x7 over 107s) kubelet, 192.168.80.11 Back-off restarting failed container
Normal Pulling 5s (x5 over 2m8s) kubelet, 192.168.80.11 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 READINESS
pod-test1 1/1 Running 0 10m 10.244.2.24 node02 <none> <none>
//在任意 node 节点上使用 curl 查看头部信息
curl -I http:// 10.244.2.24
HTTP/1.1 200 OK
Server: nginx/1.14.2
......
10.2 Pod 的重启策略(restartPolicy)
Pod 的重启策略定义了容器退出后 Kubernetes 如何处理容器的重启。主要有以下三种策略:
Always:无论容器的退出状态如何,都会重新启动。OnFailure:只有容器非正常退出(即退出码非 0)时,才会重启容器。Never:容器退出后不会重启。
restartPolicy: Always
#注意:K8S 中不支持重启 Pod 资源,只有删除重建
kubectl edit deployment nginx-deployment
......
restartPolicy: Always
案列1
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep 30; exit 3
kubectl apply -f pod3.yaml
//查看Pod状态,等容器启动后30秒后执行exit退出进程进入error状态,就会重启次数加1
kubectl get pods
NAME READY STATUS RESTARTS AGE
foo 1/1 Running 1 50s
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep 30; exit 3
restartPolicy: Never
#注意:跟container同一个级别
kubectl apply -f pod3.yaml
//容器进入error状态不会进行重启
kubectl get pods -w
11.端口数据流向
外部客户端 → 节点 IP:NodePort(如 192.168.10.10:30080) → 节点 iptables(由 kube-proxy 配置) → Service ClusterIP:port(如 10.96.0.100:80) → 后端 Pod IP:targetPort(如 10.244.1.5:80)→到具体容器container port → 容器处理请求后,响应按原路返回
到service之后会通过标签(因为pod重启后可能ip会变但是标签不会变)绑定,可以通过service的endpoints查看绑定的pod的ip。
二、Pod 的进阶
1、Pod 资源限制
在 Kubernetes 中,为了合理管理集群中的资源,容器的 CPU 和内存资源都可以设置请求值(requests)和限制值(limits)。这些设置确保了容器的资源分配和限制,避免资源争用和过度使用。
资源请求与限制(Request & Limit)
- 请求(request):当容器启动时,Kubernetes 会根据容器的资源请求来决定容器应该调度到哪个节点上。这个值表示容器在运行时最少需要的资源。
- 限制(limit):容器在运行时可以使用的最大资源量。如果容器超过了这个限制,Kubernetes 会采取措施来控制容器的资源使用,防止过度消耗。
官网示例:
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
//Pod 和 容器 的资源请求和限制:
spec.containers[].resources.requests.cpu //定义创建容器时预分配的CPU资源
spec.containers[].resources.requests.memory //定义创建容器时预分配的内存资源
spec.containers[].resources.limits.cpu //定义 cpu 的资源上限
spec.containers[].resources.limits.memory //定义内存的资源上限
2、资源单位
2.1 CPU 资源单位
在 Kubernetes 中,CPU 的单位表示方式如下:
- 1 CPU = 1 vCPU(或 1 核心超线程)
- CPU 请求和限制使用 “m”(毫核)作为单位。
- 例如:
500m表示半个 CPU,100m表示 0.1 个 CPU,1表示一个完整的 CPU。
- 例如:
- 带小数的 CPU 也是支持的,表示容器能获得的 CPU 时间片。例如,
0.25表示该容器最多可以使用一个 CPU的四分之一。
2.2 内存资源单位
内存的单位有两种表示方式:
- **十进制单位:**如
1Gi,1Mi,1Ki,分别为1024Mi,1024Ki。Kubernetes 的内存限制通常使用这些基于 2 的指数单位。 - **十进制单位:**如
1GB,1MB等,表示为以 10 为底的单位。
需要注意的是,在存储设备中,标示的单位(如GB)是基于十进制,而操作系统通常使用二进制单位(如GiB)。因此,1 GiB 的内存比 1 GB 多出大约 73MB。
https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/
2.3 Pod 案列
以下是两个常见的 Kubernetes Pod 配置示例,展示了如何为容器指定资源请求和限制:
示例 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" # 最少 64Mi 内存
cpu: "250m" # 最少 0.25 CPU
limits:
memory: "128Mi" # 最大 128Mi 内存
cpu: "500m" # 最大 0.5 CPU
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi" # 最少 64Mi 内存
cpu: "250m" # 最少 0.25 CPU
limits:
memory: "128Mi" # 最大 128Mi 内存
cpu: "500m" # 最大 0.5 CPU
分析:
- 每个容器的 请求资源 是:
0.25 CPU和64Mi内存。 - 每个容器的 限制资源 是:
0.5 CPU和128Mi内存。 - 该 Pod 的 总请求资源 为
0.5 CPU和128Mi内存。 - 该 Pod 的 总限制资源 为
1 CPU和256Mi内存。
案列1:不同容器资源配置
vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: web
image: nginx
env:
- name: WEB_ROOT_PASSWORD
value: "password"
resources:
requests:
memory: "64Mi" # 最少 64Mi 内存
cpu: "250m" # 最少 0.25 CPU
limits:
memory: "128Mi" # 最大 128Mi 内存
cpu: "500m" # 最大 0.5 CPU
- name: db
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "abc123"
resources:
requests:
memory: "512Mi" # 最少 512Mi 内存
cpu: "0.5" # 最少 0.5 CPU
limits:
memory: "1Gi" # 最大 1Gi 内存
cpu: "1" # 最大 1 CPU
分析:
- Web 容器:
- 请求:
0.25 CPU和64Mi内存 - 限制:
0.5 CPU和128Mi内存
- 请求:
- DB 容器:
- 请求:
0.5 CPU和512Mi内存 - 限制:
1 CPU和1Gi内存
- 请求:
该 Pod 的总请求资源为:
0.75 CPU和576Mi内存
该 Pod 的总限制资源为:
1.5 CPU和1.128Gi内存
2.4 调度与资源分配
当 Kubernetes 调度 Pod 时,它会根据容器的资源请求(requests)来选择适合的节点。调度器会尝试确保节点有足够的资源来满足这些请求。如果节点上的资源不够,调度器会将 Pod 调度到其他可用节点。
Pod 调度实例:
kubectl apply -f pod2.yaml
kubectl describe pod frontend
查看 Pod 分配资源情况:
kubectl get pods -o wide
输出结果将显示 Pod 是否成功启动,以及它的运行状态。
查看节点资源:
kubectl describe nodes node02
此命令展示了节点的资源使用情况。你可以看到该节点上各个 Pod 的请求与限制占用了多少资源,以及节点剩余的资源。
CPU 与内存资源分配的实际情况
例如,在你的虚拟机中有 2 个 CPU 核心,如果你为 Pod 设置了总共 1 个 CPU 的请求和 2 个 CPU 的限制,那么该 Pod 将占用节点上 50% 的 CPU 资源。
节点资源的分配情况可以通过以下命令进行查看:
kubectl describe nodes node02
输出的资源分配详细信息:
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
default frontend 500m (25%) 1 (50%) 128Mi (3%) 256Mi (6%)
kube-system kube-flannel-ds-amd64-f4pbp 100m (5%) 100m (5%) 50Mi (1%) 50Mi (1%)
2.5 总结
-
资源请求与限制:
requests是容器启动时最少需要的资源,调度器依据该值选择节点。limits是容器能够使用的最大资源值,超出该值的资源请求会被限制。
-
自动匹配:
- 如果未设置
requests,Kubernetes 会自动将其设置为与limits相同。
- 如果未设置
-
资源的分配:
- Pod 中多个容器的资源请求与限制会被加总,以便监控和调整节点的资源分配。
-
资源单位:
- CPU 使用
m(毫核)表示,例如:500m表示 0.5 个 CPU。 - 内存 使用标准的字节单位表示,通常推荐使用基于 2 的指数单位,如
Gi,Mi等。
- CPU 使用
-
分配大小:
- 如果因为分配内存或者cpu不够可能会导致安装失败或者运行问题,通过kubectl describe pod frontend 可以进行查看。
2.6 健康检查:又称为探针(Probe)
探针是由kubelet对容器执行的定期诊断。
2.6.1 探针的三种规则
Init 容器 → 应用容器启动 → 启动探针(若配置)→ 存活探针和就绪探针(启动探针成功后)
-
livenessProbe :判断容器是否正在运行。如果探测失败,则kubelet会杀死容器,并且容器将根据 restartPolicy 来设置 Pod 状态。 如果容器不提供存活探针,则默认状态为Success。
如果失败,直接重启容器,如果成功显示running
-
readinessProbe :判断容器是否准备好接受请求。如果探测失败,端点控制器将从与 Pod 匹配的所有 service 址endpoints 中剔除删除该Pod的IP地。 初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为Success。
如果失败,在ready处会显示0/1,不会影响running状态
-
startupProbe(这个1.17版本增加的):判断容器内的应用程序是否已启动,主要针对于不能确定具体启动时间的应用。如果配置了 startupProbe 探测,在则在 startupProbe 状态为 Success 之前,其他所有探针都处于无效状态,直到它成功后其他探针才起作用。 如果 startupProbe 失败,kubelet 将杀死容器,容器将根据 restartPolicy 来重启。如果容器没有配置 startupProbe, 则默认状态为 Success。
#注:以上规则可以同时定义。在readinessProbe检测成功之前,Pod的running状态是不会变成ready状态的。
2.6.2 Probe支持三种检查方法
-
exec :在容器内执行指定命令。如果命令退出时返回码为0则认为诊断成功。
-
tcpSocket :对指定端口上的容器的IP地址进行TCP检查(三次握手)。如果端口打开,则诊断被认为是成功的。
-
httpGet :对指定的端口和路径上的容器的IP地址执行HTTPGet请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的
2.6.3 每次探测都将获得以下三种结果之一
- 成功:容器通过了诊断。
- 失败:容器未通过诊断。
- 未知:诊断失败,因此不会采取任何行动
官网示例:
https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
实例 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
#initialDelaySeconds:指定 kubelet 在执行第一次探测前应该等待5秒,即第一次探测是在容器启动后的第6秒才开始执行。默认是 0 秒,最小值是 0。
#periodSeconds:指定了 kubelet 应该每 5 秒执行一次存活探测。默认是 10 秒。最小值是 1。
#failureThreshold: 当探测失败时,Kubernetes 将在放弃之前重试的次数。 存活探测情况下的放弃就意味着重新启动容器。就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
#timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。(在 Kubernetes 1.20 版本之前,exec 探针会忽略 timeoutSeconds 探针会无限期地 持续运行,甚至可能超过所配置的限期,直到返回结果为止。)
可以看到 Pod 中只有一个容器。kubelet 在执行第一次探测前需要等待 5 秒,kubelet 会每 5 秒执行一次存活探测。kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 当到达第 31 秒时,这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
案列1
vim exec.yaml
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 3600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
kubectl create -f exec.yaml
kubectl describe pods liveness-exec
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 51s default-scheduler Successfully assigned default/liveness-exec-pod to node02
Normal Pulled 46s kubelet, node02 Container image "busybox" already present on machine
Normal Created 46s kubelet, node02 Created container liveness-exec-container
Normal Started 45s kubelet, node02 Started container liveness-exec-container
Warning Unhealthy 8s (x3 over 14s) kubelet, node02 Liveness probe failed:
Normal Killing 8s kubelet, node02 Container liveness-exec-container failed liveness probe,will be restarted
kubectl get pods -w
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 85s
示例2:httpGet方式
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
在这个配置文件中,可以看到 Pod 也只有一个容器。initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。
任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。
案列2 httpGet方式
vim httpget.yaml
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
可以看到执行了一次重启,不会执行第二次重启是因为再次启动的时候index已经恢复
httpget http://IP:80/index.html delay 延迟 =3 tomout=10s period(频率)=3s succes(成功)=1 faulure(失败)=3 机会 杀死容器
示例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
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
这个例子同时使用 readinessProbe 和 livenessProbe 探测。kubelet 会在容器启动 5 秒后发送第一个 readinessProbe 探测。这会尝试连接 goproxy 容器的 8080 端口。如果探测成功,kubelet 将继续每隔 10 秒运行一次检测。除了 readinessProbe 探测,这个配置包括了一个 livenessProbe 探测。kubelet 会在容器启动 15 秒后进行第一次 livenessProbe 探测。就像 readinessProbe 探测一样,会尝试连接 goproxy 容器的 8080 端口。如果 livenessProbe 探测失败,这个容器会被重新启动。
案列3 tcpsocket
vim tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
spec:
containers:
- name: nginx
image: soscscs/myapp:v1
livenessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
tcpSocket:
port: 8080
periodSeconds: 10
failureThreshold: 2
kubectl create -f tcpsocket.yaml
kubectl exec -it probe-tcp -- netstat -natp
这里可以看到开放的是80端口,但是tcpsocket监听的却是8080,所以会失败重启
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1/nginx: master pro
kubectl get pods -w
NAME READY STATUS RESTARTS AGE
probe-tcp 1/1 Running 0 1s
probe-tcp 1/1 Running 1 25s #第一次是 init(5秒) + period(10秒) * 2
probe-tcp 1/1 Running 2 45s #第二次是 period(10秒) + period(10秒) 重试了两次
probe-tcp 1/1 Running 3 65s
案列4:就绪检测
vim readiness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: soscscs/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 10
kubectl create -f readiness-httpget.yaml
[root@master01 opt]# kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend 2/2 Running 0 40m
liveness-httpget 1/1 Running 1 23m
nginx-a-5cf6f5b85c-65zsd 1/1 Running 0 47h
nginx-a-5cf6f5b85c-7hblm 1/1 Running 0 47h
readiness-httpget 0/1 Running 0 12s
可以看到running但是没有ready因为live存活探针成功所以status为running,但是就绪探针失败
kubectl exec -it readiness-httpget sh
cd /usr/share/nginx/html/
ls
50x.html index.html
echo 123 > index1.html
exit
创建index1之后就可以访问到了,所以ready变为1/1
kubectl get pods
NAME READY STATUS RESTARTS AGE
readiness-httpget 1/1 Running 0 2m31s
kubectl exec -it readiness-httpget -- rm -rf /usr/share/nginx/html/index.html
kubectl get pods -w
NAME READY STATUS RESTARTS AGE
readiness-httpget 1/1 Running 0 4m10s
readiness-httpget 0/1 Running 1 4m15s
案列5:就绪检测2
vim readiness-myapp.yaml
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: myapp
spec:
selector:
app: myapp
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
kubectl create -f readiness-myapp.yaml
kubectl get pods,svc,endpoints -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/myapp1 1/1 Running 0 3m42s 10.244.2.13 node02 <none> <none>
pod/myapp2 1/1 Running 0 3m42s 10.244.1.15 node01 <none> <none>
pod/myapp3 1/1 Running 0 3m42s 10.244.2.14 node02 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
......
service/myapp ClusterIP 10.96.138.13 <none> 80/TCP 3m42s app=myapp
NAME ENDPOINTS AGE
......
endpoints/myapp 10.244.1.15:80,10.244.2.13:80,10.244.2.14:80 3m42s
可以看到三个endpoint
kubectl exec -it pod/myapp1 -- rm -rf /usr/share/nginx/html/index.html
//readiness探测失败,Pod 无法进入READY状态,且端点控制器将从 endpoints 中剔除删除该 Pod 的 IP 地址
kubectl get pods,svc,endpoints -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/myapp1 0/1 Running 0 5m17s 10.244.2.13 node02 <none> <none>
pod/myapp2 1/1 Running 0 5m17s 10.244.1.15 node01 <none> <none>
pod/myapp3 1/1 Running 0 5m17s 10.244.2.14 node02 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
......
service/myapp ClusterIP 10.96.138.13 <none> 80/TCP 5m17s app=myapp
NAME ENDPOINTS AGE
......
endpoints/myapp 10.244.1.15:80,10.244.2.14:80 5m17s
案列6 启动、退出动作
vim post.yaml
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
initContainers:
- name: init-myservice
image: soscscs/myapp:v1
command: ["/bin/sh", "-c", "echo 'Hello initContainers' >> /var/log/nginx/message"]
volumeMounts:
- name: message-log
mountPath: /var/log/nginx/
readOnly: false
volumes:
- name: message-log
hostPath:
path: /data/volumes/nginx/log/
type: DirectoryOrCreate
kubectl create -f post.yaml
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
lifecycle-demo 1/1 Running 0 2m8s 10.244.2.28 node02 <none> <none>
kubectl exec -it lifecycle-demo -- cat /var/log/nginx/message
Hello initContainers
Hello from the postStart handler
//在 node02 节点上查看
[root@node02 ~]# cd /data/volumes/nginx/log/
[root@node02 log]# ls
access.log error.log message
[root@node02 log]# cat message
Hello initContainers
Hello from the postStart handler
#由上可知,init Container先执行,然后当一个主容器启动后,Kubernetes 将立即发送 postStart 事件。
//删除 pod 后,再在 node02 节点上查看
kubectl delete pod lifecycle-demo
[root@node02 log]# cat message
Hello initContainers
Hello from the postStart handler
Hello from the poststop handler
#由上可知,当在容器被终结之前, Kubernetes 将发送一个 preStop 事件。
apiVersion: v1 # API版本,此处为v1(Pod属于核心v1组)
kind: Pod # 资源类型,此处定义一个Pod
metadata:
name: lifecycle-demo # Pod的名称,用于标识该Pod
spec: # Pod的规格配置
containers: # 容器列表,定义Pod中的应用容器
- name: lifecycle-demo-container # 容器名称
image: soscscs/myapp:v1 # 容器使用的镜像(此处为自定义的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: # 容器挂载卷配置,将Pod的存储卷挂载到容器内
- name: message-log # 挂载的卷名称,需与spec.volumes中的名称一致
mountPath: /var/log/nginx/ # 容器内的挂载路径(日志文件存放目录)
readOnly: false # 挂载路径是否为只读(此处为可写,允许写入日志)
initContainers: # 初始化容器列表,在应用容器启动前执行(用于前置初始化)
- name: init-myservice # 初始化容器名称
image: soscscs/myapp:v1 # 初始化容器使用的镜像
command: ["/bin/sh", "-c", "echo 'Hello initContainers' >> /var/log/nginx/message"] # 初始化命令:向日志文件写入初始化信息
volumeMounts: # 初始化容器挂载卷(与应用容器共享同一存储卷,确保日志写入同一路径)
- name: message-log # 挂载的卷名称(与spec.volumes一致)
mountPath: /var/log/nginx/ # 容器内的挂载路径(与应用容器相同,共享日志目录)
readOnly: false # 可写,允许初始化容器写入日志
volumes: # Pod的存储卷配置,定义可被容器共享的存储
- name: message-log # 卷名称,用于容器挂载时引用
hostPath: # 卷类型为hostPath,将宿主机的目录挂载到Pod中
path: /data/volumes/nginx/log/ # 宿主机上的实际路径(日志会持久化到宿主机此目录)
type: DirectoryOrCreate # 若宿主机路径不存在,则自动创建该目录(确保挂载成功)
Init 容器 → 应用容器启动 → 启动探针(若配置)→ 存活探针和就绪探针(启动探针成功后)
扩展
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的状态
####Container生命周期
1、Waiting:启动到运行中间的一个等待状态。
2、Running:运行状态。
3、Terminated:终止状态。 如果没有任何异常的情况下,container应该会从Waiting状态变为Running状态,这时容器可用。
但如果长时间处于Waiting状态,container会有一个字段reason表明它所处的状态和原因,如果这个原因很容易能标识这个容器再也无法启动起来时,例如ContainerCannotRun,整个服务启动就会迅速返回。(这里是一个失败状态返回的特性,不详细阐述)
更多推荐



所有评论(0)