kubernete编排技术一:pod
作者 | 朱晋君
来源 | 君哥聊技术(ID:gh_1f109b82d301)
在之前的文章《kubernete中的原子调度单位:pod》中提到过,如果把kubernete比作linux操作系统,那pod就是虚拟机,pod里面的容器就是虚拟机上的进程。这个类比可以说非常形象。在Linux上,进程并不是完全独立的,一些进程之间存在着一些关联,比如一个springboot应用和一个日志收集服务。pod正是使用了容器进程之间的这些关系,做的编排。
在kubernete上创建pod
下面的yaml文件springboot-mybatis.yaml定义了一个参数replicas: 2,这个参数的意思就是创建2个pod,image: zjj2006forever/springboot-mybatis:1.2则定义了pod中运行这容器的镜像。
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-mybatis-deployment
spec:
selector:
matchLabels:
app: springboot-mybatis
replicas: 2
template:
metadata:
labels:
app: springboot-mybatis
spec:
containers:
- name: spingboot-mybatis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-mybatis:1.2
ports:
- containerPort: 8300
执行下面命令这两个pod就被创建出来
kubectl apply -f springboot-mybatis.yaml
我们用命令可以查看到
[root@master k8s]# kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
default springboot-mybatis-deployment-5b78f66997-6fbqk 1/1 Running 0 63s
default springboot-mybatis-deployment-5b78f66997-qg5ng 1/1 Running 0 62s
pod的本质
pod是一个逻辑上的概念,不是一个物理存在。pod的本质是有关联关系的一组容器调度在一起,这些容器可以共享linux的networknamespace和volume。那为什么需要pod呢?
我们举一个例子,假如我们有3个容器,container1是一个springboot应用,container2负责收集container1的日志,container3负责对container2的日志进行分析处理。我们如果想把这3个容器部署在同一个node上,每个容器需要分配1G的内存。
但我们目前有2个node,node1有2.5G内存,node2有3G内存,如果没有pod的逻辑存在,container1和container2调度到了node1上,但是container3调度到node1上时发现内存不够而失败了。如果我们有了pod,我们把这3个容器编排在一个pod里面,pod会直接被调度到3G内存的node2上。
pod里面的容器是怎么共享namespace的?在之前的文章《浅谈kubernete中的flannel网络插件》文章中介绍过,pod中有一个infra的容器,在pod创建时这个容器总是第一个被创建。这个容器镜像使用的是k8s.gcr.io/pause,占用空间非常小,解压后也就100多k。在pod启动时,先创建出这个infra容器,用这个容器控制住networknamespace,这样其他容器启动时共享这个网络就可以了。
pod里面的容器是怎么共享volume?还是上面3个容器的例子,我们编写一个yaml文件,代码如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-mybatis-deployment
spec:
selector:
matchLabels:
app: springboot-mybatis
replicas: 2
template:
metadata:
labels:
app: springboot-mybatis
spec:
volumes:
- name: boot-log
hostPath:
path: /tmp/boot-log
containers:
- name: spingboot-mybatis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-mybatis:1.2
ports:
- containerPort: 8300
volumeMounts:
- name: boot-log
mountPath: /logs
- name: spingboot-log-accumulate
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-log-accumulate:v1
ports:
- containerPort: 8400
volumeMounts:
- name: boot-log
mountPath: /accumulate
- name: spingboot-log-analysis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-log-analysis:v1
ports:
- containerPort: 8500
volumeMounts:
- name: boot-log
mountPath: /analysis
上面的yaml文件中,我们定义了3个container,分别是应用容器spingboot-mybatis,日志收集spingboot-log-accumulate,日志分析spingboot-log-analysis,从上面的文件定义中我们可以看到,这3个容器都挂载了boot-log这个volum,对应宿主机目录/tmp/boot-log,本质上就是/tmp/boot-log这个目录被绑定在了上面3个容器中,这样上面3个容器就可以通过这个目录访问其他容器的绑定目录了。即spingboot-log-accumulate可以访问spingboot-mybatis的/logs,spingboot-log-analysis可以访问spingboot-log-accumulate的/accumulate。
所以:容器的本质其实是一组共享了networknamespace和volume的容器的集合。
pod的关键属性
pod是kubernete中最小的调度单位。在kubernete中,调度、网络、存储以及安全等属性,都是pod级别的。下面看一下pod的几个重要属性
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-mybatis-deployment
spec:
shareProcessNamespace: true
hostNetwork: true
nodeSelector:
hostname: worker1
selector:
matchLabels:
app: springboot-mybatis
hostAliases:
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
replicas: 2
template:
metadata:
labels:
app: springboot-mybatis
spec:
containers:
- name: spingboot-mybatis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-mybatis:1.2
ports:
- containerPort: 8300
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
1.shareProcessNamespace代表pod内容器共享pid namespace,hostNetwork代表共享宿主机网络
2.NodeSelector代表绑定宿主机标签,宿主机标签可以用下面命令查看
[root@master ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 17h v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
worker1 Ready <none> 17h v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker1,kubernetes.io/os=linux
beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker1,kubernetes.io/os=linux
3.hostAliases定义了host文件,上面yaml文件创建pod后,pod内的hosts文件会增加内容如下:
/ # cat etc/hosts
10.1.2.3 foo.remote
10.1.2.3 bar.remote
4.ImagePullPolicy
Always:默认,创建pod是总是拉取镜像
Never:从不拉取
IfNotPresent:宿主机上不存在镜像时拉取
5.lifecycle:它是一个hook,在容器发生变化是触发一个事件。
postStart表示容器启动时触发一个操作,容器启动时立即触发,不等容器启动完成
preStop表示在容器被杀死之前触发一个操作,触发这个操作结束后才执行杀死容器的动作,比如这里可以触发eureka优雅发布。
注:
pod的生命周期状态主要有以下几种
- Pending:创建不成功
- Running:pod创建成功,并且里面至少一个容器在运行中
- Succeeded:pod中容器运行完毕正常退出
- Failed:pod里至少一个容器创建不成功
- Unknown:异常
pod的健康检查
修改之前部署springboot的yaml文件如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-mybatis-deployment
spec:
selector:
matchLabels:
app: springboot-mybatis
replicas: 2
template:
metadata:
labels:
app: springboot-mybatis
spec:
containers:
- name: spingboot-mybatis
imagePullPolicy: IfNotPresent
image: zjj2006forever/springboot-mybatis:1.2
ports:
- containerPort: 8300
livenessProbe:
httpGet:
path: /actuator/info
port: 18082
initialDelaySeconds: 5
periodSeconds: 5
启动pod
kubectl apply -f springboot-mybatis.yaml
查看容器状态:
[root@master k8s]# kubectl get pod springboot-mybatis-deployment-dcd7f8bbf-9wfxl
NAME READY STATUS RESTARTS AGE
springboot-mybatis-deployment-dcd7f8bbf-9wfxl 1/1 Running 3 77s
[root@master k8s]# kubectl describe pod springboot-mybatis-deployment-dcd7f8bbf-9wfxl
Name: springboot-mybatis-deployment-dcd7f8bbf-9wfxl
Namespace: default
Priority: 0
Node: worker1/192.168.59.140
Start Time: Fri, 03 Jul 2020 03:56:44 -0400
Labels: app=springboot-mybatis
pod-template-hash=dcd7f8bbf
Annotations: <none>
Status: Running
IP: 10.244.1.19
IPs:
IP: 10.244.1.19
Controlled By: ReplicaSet/springboot-mybatis-deployment-dcd7f8bbf
Containers:
spingboot-mybatis:
Container ID: docker://19c4082ed706a80c308069ba3355d7192704ad518ced0e075d424bb6ebb16865
Image: zjj2006forever/springboot-mybatis:1.2
Image ID: docker-pullable://zjj2006forever/springboot-mybatis@sha256:bf43bc9d1d4bdb33d82a206282c8f82be3679847d1011806b72e3634ebe67564
Port: 8300/TCP
Host Port: 0/TCP
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: Error
Exit Code: 143
Started: Fri, 03 Jul 2020 03:57:42 -0400
Finished: Fri, 03 Jul 2020 03:58:02 -0400
Ready: False
Restart Count: 3
Liveness: http-get http://:18082/actuator/info delay=5s timeout=1s period=5s #success=1 #failure=3
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-qk5nc (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
default-token-qk5nc:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-qk5nc
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 85s default-scheduler Successfully assigned default/springboot-mybatis-deployment-dcd7f8bbf-9wfxl to worker1
Normal Killing 28s (x3 over 68s) kubelet, worker1 Container spingboot-mybatis failed liveness probe, will be restarted
Normal Pulled 27s (x4 over 84s) kubelet, worker1 Container image "zjj2006forever/springboot-mybatis:1.2" already present on machine
Normal Created 27s (x4 over 84s) kubelet, worker1 Created container spingboot-mybatis
Normal Started 26s (x4 over 84s) kubelet, worker1 Started container spingboot-mybatis
Warning Unhealthy 18s (x10 over 78s) kubelet, worker1 Liveness probe failed: Get http://10.244.1.19:18082/actuator/info: dial tcp 10.244.1.19:18082: connect: connection refused
下面这句可以看出,失败了3次,从上面的Events中也可以看出这个被删除重新创建的过程
Liveness: http-get http://:18082/actuator/info delay=5s timeout=1s period=5s #success=1 #failure=3
pod健康检查失败后默认会重启pod,所以这时查看pod状态,发现重启了4次
[root@master k8s]# kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
default springboot-mybatis-deployment-dcd7f8bbf-9wfxl 1/1 Running 4 7m6s
default springboot-mybatis-deployment-dcd7f8bbf-q7nhp 1/1 Running 4 7m6s
注意:
1.kubernete中pod的restartPolicy默认是Always,即只要容器status不是RUNNING,就自动重启容器。除此之外,还有OnFailure(只有容器异常时才自动重启)和Never(从来不重启容器)。
2.在一个有多个容器的pod中,只有所有的容器都不是RUNNING状态时,pod才会执行restartPolicy,否则pod状态一直是RUNNING
3.livenessProbe有多种方式,比如http,tcp,也可以在容器中执行命令来判断
4.在我们定义的yaml文件中,我们定义了kind: Deployment,如果我们定义了kind: pod,那就只能在当前宿主机重启pod,这样如果当前宿主机故障,就会一直调度失败。但是kind: Deployment这种方式是可以调度到其他宿主机节点上的。
总结
pod是kubernete中最重要的概念,把pod类比成操作系统上的虚拟机、pod中的容器类比成虚拟机上运行的进程,是非常恰当的。kubernete强大的编排功能,原子单位是pod,pod是kubernete中的原子编排和调度对象。