在之前的文章《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
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 peri
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中的原子编排和调度对象。