kubernete编排技术一:pod

gq_design
发布于 2022-5-1 09:39
浏览
0收藏

作者 |  朱晋君
来源 | 君哥聊技术(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中的原子编排和调度对象。

分类
收藏
回复
举报
回复
    相关推荐