深入掌握Pod(上)
深入掌握Pod
对Kubernetes如何发布与管理容器应用进行详细说明和示例,主要包括Pod和容器的使用、应用配置管理、Pod的控制和调度管理、Pod的升级和回滚,以及Pod的扩缩容机制等内容。
| 静态Pod
- 静态 Pod 直接由特定节点上的kubelet进程来管理,不通过 master 节点上的apiserver。无法与我们常用的控制器Deployment或者DaemonSet进行关联,它由kubelet进程自己来监控,当pod崩溃时重启该pod,kubelete也无法对他们进行健康检查。静态 pod 始终绑定在某一个kubelet,并且始终运行在同一个节点上。 kubelet会自动为每一个静态 pod 在 Kubernetes 的 apiserver 上创建一个镜像 Pod(Mirror Pod),因此我们可以在 apiserver 中查询到该 pod,但是不能通过 apiserver 进行控制(例如不能删除)。
- 创建静态 Pod 有两种方式:配置文件和 HTTP 两种方式。这里采用配置文件的方式创建
查看配置文件路径
- kubelet通过 kubelet --pod-manifest-path=<路径>来启动kubelet进程,kubelet 定期的去扫描这个目录,根据这个目录下出现或消失的 YAML/JSON 文件来创建或删除静态 pod。
如果你的 kubelet 启动参数中没有配置上面的–pod-manifest-path参数的话,那么添加上这个参数然后重启 kubelet 即可。
[root@localhost ~]# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Mon 2021-07-26 09:55:46 CST; 7h ago
Docs: https://kubernetes.io/docs/
Main PID: 22913 (kubelet)
Tasks: 17
Memory: 34.5M
CGroup: /system.slice/kubelet.service
└─22913 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig...
Jul 26 16:22:40 localhost.localdomain kubelet[22913]: W0726 16:22:40.758730 22913 conversion.go:110] Coul...ase
Jul 26 16:32:42 localhost.localdomain kubelet[22913]: W0726 16:32:42.195526 22913 conversion.go:110] Coul...ase
Jul 26 16:42:43 localhost.localdomain kubelet[22913]: W0726 16:42:43.687560 22913 conversion.go:110] Coul...ase
Jul 26 16:55:05 localhost.localdomain kubelet[22913]: W0726 16:55:05.486097 22913 conversion.go:110] Coul...ase
Jul 26 16:55:15 localhost.localdomain kubelet[22913]: W0726 16:55:15.517377 22913 conversion.go:110] Coul...ase
Jul 26 17:05:27 localhost.localdomain kubelet[22913]: W0726 17:05:27.041500 22913 conversion.go:110] Coul...ase
Jul 26 17:15:08 localhost.localdomain kubelet[22913]: W0726 17:15:08.447827 22913 conversion.go:110] Coul...ase
Jul 26 17:15:18 localhost.localdomain kubelet[22913]: W0726 17:15:18.469033 22913 conversion.go:110] Coul...ase
Jul 26 17:18:07 localhost.localdomain kubelet[22913]: I0726 17:18:07.183772 22913 topology_manager.go:233...ler
Jul 26 17:18:07 localhost.localdomain kubelet[22913]: E0726 17:18:07.619942 22913 kuberuntime_manager.go:937...
Hint: Some lines were ellipsized, use -l to show in full.
找到Drop-In 获取其中的环境变量
Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path
如果没有添加一下
Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"
注意的是所以如果我们通过kubeadm
的方式来安装的集群环境,对应的kubelet已经配置了我们的静态 Pod 文件的路径,那就是/etc/kubernetes/manifests
,所以我们只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可。
编辑一个Pod nginx.yaml
[root@localhost ~]# cd /etc/kubernetes/manifests/
[root@localhost manifests]# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: web
name: web
spec:
containers:
- image: nginx
name: web
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
检查Pod状态
查看Pod情况
[root@localhost manifests]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-localhost.localdomain 1/1 Running 0 30m 10.244.0.6 localhost.localdomain <none> <none>
运行Curl
[root@localhost manifests]# curl 10.244.0.6
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
静态pod的删除
- 无法使用kubectl delete删除pods,删掉会马上重启。通过删除/etc/kubernetes/manifests下对应的yaml或JSON文件删除静态POD
补充HTTP方式创建Pods
- kubelet 周期地从–manifest-url=参数指定的地址下载文件,并且把它翻译成 JSON/YAML 格式的 pod 定义。此后的操作方式与–pod-manifest-path=相同,kubelet 会不时地重新下载该文件,当文件变化时对应地终止或启动静态 pod。
| Pod容器共享Volume
同一个Pod中的多个容器能够共享Pod级别的存储卷Volume。Volume可以被定义为各种类型,多个容器各自进行挂载操作,将一个Volume挂载为容器内部需要的目录。
在下面的例子中,在Pod内包含两个容器:tomcat和busybox,在Pod级别设置Volume“app-logs”,用于tomcat向其中写日志文件,busybox读日志文件。
创建Yaml文件
[root@localhost manifests]# vim pod-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: tomact
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
volumeMounts:
- name: app-logs
mountPath: /usr/local/tomcat/logs
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command: ["sh","-c","tail -f /logs/catalina*.log"]
volumeMounts: #容器内挂载点
- name: app-logs #必须有名称
mountPath: /logs #容器内挂在路径
volumes: #跟上面的名称对应
- name: app-logs #跟上面的名称对应
emptyDir: {} #宿主机挂载点
这里设置的Volume名为app-logs,类型为emptyDir,挂载到tomcat容器内的/usr/local/tomcat/logs目录,同时挂载到logreader容器内的/logs目录。tomcat容器在启动后会向/usr/local/tomcat/logs目录写文件,logreader容器就可以读取其中的文件了。
检查Pod
有两个在运行的Pod
[root@localhost test]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
volume-pod-localhost.localdomain 2/2 Running 1 4m48s 10.244.0.9 localhost.localdomain <none> <none>
web-localhost.localdomain 1/1 Running 0 68m 10.244.0.6 localhost.localdomain <none> <none>
| ConfigMap概述
ConfigMap供容器使用的典型用法如下。
- 生成为容器内的环境变量。
- 设置容器启动命令的启动参数(需设置为环境变量)。
- 以Volume的形式挂载为容器内部的文件或目录。
ConfigMap以一个或多个key:value的形式保存在Kubernetes系统中供应用使用,既可以用于表示一个变量的值(例如apploglevel=info),也可以用于表示一个完整配置文件的内容(例如server.xml=<?xml...>...)
可以通过YAML配置文件或者直接使用kubectl create configmap命令行的方式来创建ConfigMap。
示例
根据文件创建一个名为my-config的configmap
$ kubectl create configmap my-config --from-file=path/to/bar
使用指定的keys创建一个名为my-config的configmap
$ kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
使用key1 = config1和key2 = config2创建一个名为my-config的configmap
$ kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
从文件中的key = value对创建一个名为my-config的configmap
$ kubectl create configmap my-config --from-file=path/to/bar
从env文件创建一个名为my-config的configmap
$ kubectl create configmap my-config --from-env-file=path/to/bar.env
通过YAML配置文件方式创建
直接通过kubectl create configmap也可以创建ConfigMap,可以使用参数--from-file或--from-literal指定内容,并且可以在一行命令中指定多个参数。然后再转换成yaml文件,
[root@master ~]# kubectl create configmap my-config --from-literal=apploglevel=info --from-literal=appdatadir=/var/data -o yaml --dry-run > configmap.yaml
W0727 06:29:13.460284 83913 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client.
[root@master ~]# cat configmap.yaml
apiVersion: v1
data:
appdatadir: /var/data
apploglevel: info
kind: ConfigMap
metadata:
creationTimestamp: null
name: my-config
#检查是否正常
[root@master ~]# kubectl apply -f configmap.yaml
configmap/my-config created
[root@master ~]# kubectl get configmap
NAME DATA AGE
my-config 2 5s
容器应用对ConfigMap的使用有以下两种方法。
- 通过环境变量获取ConfigMap中的内容。
- 通过Volume挂载的方式将ConfigMap中的内容挂载为容器内部的文件或目录。
| 使用ConfigMap的限制条件
使用ConfigMap的限制条件如下。
- ConfigMap必须在Pod之前创建。
- ConfigMap受Namespace限制,只有处于相同Namespace中的Pod才可以引用它。
- ConfigMap中的配额管理还未能实现。
- kubelet只支持可以被API Server管理的Pod使用ConfigMap。kubelet在本Node上通过 --manifest-url或--config自动创建的静态Pod将无法引用ConfigMap。
- 在Pod对ConfigMap进行挂载(volumeMount)操作时,在容器内部只能挂载为“目录”,无法挂载为“文件”。在挂载到容器内部后,在目录下将包含ConfigMap定义的每个item,如果在该目录下原来还有其他文件,则容器内的该目录将被挂载的ConfigMap覆盖。如果应用程序需要保留原来的其他文件,则需要进行额外的处理。可以将ConfigMap挂载到容器内部的临时目录,再通过启动脚本将配置文件复制或者链接到(cp或link命令)应用所用的实际配置目录下。
| 在Pod中使用ConfigMap
通过环境变量方式使用ConfigMap
在Pod“cm-test-pod”的定义中,将ConfigMap“cm-appvars”中的内容以环境变量(APPLOGLEVEL和APPDATADIR)方式设置为容器内部的环境变量,容器的启动命令将显示这两个环境变量的值("env | grep APP")
使用kubectl create -f命令创建该Pod,由于是测试Pod,所以该Pod在执行完启动命
令后将会退出,并且不会被系统自动重启(restartPolicy=Never)
使用kubectl get pods --show-all查看已经停止的Pod:
查看该Pod的日志,可以看到启动命令“env | grep APP”的执行结果如下:
说明容器内部的环境变量使用ConfigMap cm-appvars中的值进行了正确设置。
Kubernetes从1.6版本开始,引入了一个新的字段envFrom,实现了在Pod环境中将ConfigMap(也可用于Secret资源对象)中所有定义的key=value自动生成为环境变量:
| Pod生命周期和重启策略
Pod在整个生命周期中被系统定义为各种状态,熟悉Pod的各种状态对于理解如何设置Pod的调度策略、重启策略是很有必要的。
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查(详见下节)失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。
Pod的重启策略包括Always、OnFailure和Never,默认值为Always。
- 🥩 Always:当容器失效时,由kubelet自动重启该容器。
- 🥩 OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
- 🥩 Never:不论容器运行状态如何,kubelet都不会重启该容器。
Pod的重启策略与控制方式息息相关,当前可用于管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod)。每种控制器对Pod的重启策略要求如下。
- RC和DaemonSet:必须设置为Always,需要保证该容器持续运行。
- Job:OnFailure或Never,确保容器执行完成后不再重启。
- kubelet:在Pod失效时自动重启它,不论将RestartPolicy设置为什么值,也不会对Pod进行健康检查。
在Pod的yaml编排使用重启策略。
spec:
containers:
- image: nginx
name: web
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always #重启策略
Pod健康检查
Kubernetes 对 Pod 的健康状态可以通过两类探针来检查:LivenessProbe 和ReadinessProbe,kubelet定期执行这两类探针来诊断容器的健康状况。
- LivenessProbe探针:用于判断容器是否存活(Running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。
- ReadinessProbe探针:用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod Endpoint的关联关系也将基于Pod是否Ready进行设置。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回后端Endpoint列表。这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod实例上。
LivenessProbe和ReadinessProbe均可配置以下三种实现方式。
ExecAction
- ExecAction:在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。
通过执行“cat /tmp/health”命令来判断一个容器运行是否正常。在该Pod运行后,将在创建/tmp/health文件10s后删除该文件,而LivenessProbe健康检查的初始探测时间(initialDelaySeconds)为15s,探测结果是Fail,将导致kubelet杀掉该容器并重启它
[root@master ~]# vim pod.yaml
dd resources: {}
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
name: web
spec:
containers:
- image: nginx
name: web
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
livenessProbe: "健康检查"
exec:
command:
- cat
- /tmp/health
initialDelaySeconds: 15 "初始探测时间"
timeoutSeconds: 1 "超时时间"
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
查看状态正常
[root@master ~]# kubectl apply -f pod.yaml
pod/web created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
web 1/1 Running 0 4s
TCPSocketAction
- TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
在下面的例子中,通过与容器内的localhost:80建立TCP连接进行健康检查:
[root@master ~]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
name: web
spec:
containers:
- image: nginx
name: web
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
livenessProbe: "健康检查"
tcpSocket: "TCP连接进行健康检查"
port: 80
initialDelaySeconds: 30
timeoutSeconds: 1
dnsPolicy: ClusterFirst
restartPolicy: Always
[root@master ~]# kubectl apply -f pod.yaml
pod/web created
status: {}
HTTPGetAction
- HTTPGetAction:通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。
在下面的例子中,kubelet定时发送HTTP请求到localhost:80/_status/healthz来进行容器应用的健康检查:
[root@master ~]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
name: web
spec:
containers:
- image: nginx
name: web
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
livenessProbe: "健康检查"
httpGet: "HTTP Get方法"
path: /_status/healthz
port: 80
initialDelaySeconds: 30
timeoutSeconds: 1
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
对于每种探测方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们的含义分别如下。
- initialDelaySeconds:启动容器后进行首次健康检查的等待时间,单位为s。
- timeoutSeconds:健康检查发送请求后等待响应的超时时间,单位为s。当超时发生时,kubelet会认为容器已经无法提供服务,将会重启该容器。