1664607516509.png

一、Pod简述

Pod是k8s(下文Kubernetes简称k8s)的一个抽象概念,Pod本身不是容器。但Pod是由一个或多个容器组成。是 k8s 项目中最小原子调度单位。如果把k8s比作操作系统,那么容器就是进程,而Pod的话就是进程组的概念。

图片来源https://kuboard.cn/learning/k8s-basics/explore.html

二、Namespace共享原理

同一个Pod里的所有容器,共享的是同一个Nettwork Namespace,并且可以声明同一个Volume

在 Kubernetes 项目里,Pod 的实现需要使用一个中间容器,这个容器叫作 Infra 容器。在这个 Pod 中,Infra 容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra 容器关联在一起。这样的组织关系,可以用下面这样一个示意图来表达:

在这里插入图片描述

Infra容器 全称infrastucture container(又叫Pause)基础容器,作为init pod存在,所有当我们创建Pod是都会看到一个Pause容器被创建。现在我们终于知道pause类型镜像(k8s.gcr.io/pause:3.2)的作用是什么。

三、Init容器

1.理解

Init 容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。

**每个Pod中可以包含很多容器,但是有的容器可能依赖某一个功能或特性,需要提前启动一个容器为它提供服务。就像我们使用yum安装一些服务时,会有很多依赖项,如果我们没有安装这个依赖,这个服务会始终安装失败。我们的pod也是,如果Pod的Init容器启动失败,kubelet会不断重启Init容器直到成功,并且Pod的Init容器失败,整个Pod状态也会设置为失败,就像安装依赖一样。**

2.示例

例子定义了一个具有 1 个 Init 容器的简单 Pod。 init容器拉去k8s页面存放到nginx默认路径,当Init容器运行过程中,Pod的状态为Init:0/1 ,init容器启动成功后,pod状态变为Running

1>创建nginx-init.yaml文件

---
apiVersion: v1
kind: Pod
metadata:
    name: nginx-init
spec:
   initContainers:
   - name: install
     image: busybox
     command:
     -  wget
     - "-O"
     - "/work-dir/index.html"
     -  http://kubernetes.io
     volumeMounts:
     -  name: workdir
        mountPath: "/work-dir"
   containers:
   -  name: nginx
      image: nginx
      ports:
      -  containerPort: 80
      volumeMounts:
      -  name: workdir
         mountPath: /usr/share/nginx/html
   volumes:
   -  name: workdir
      emptyDir: {}

2>通过运行下面命令启动Pod:

[root@ycloud ~]# kubectl apply -f nginx-init.yaml 
pod/myapp-pod created

3>验证

[root@ycloud ~]# kubectl get po -owide
NAME                              READY   STATUS             RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
nginx-init                        1/1     Running            0          13m    100.111.212.6    ycloud   <none>           <none>
[root@ycloud ~]# curl 100.111.212.6
<!doctype html><html lang=en class=no-js><head class=live-site><meta name=ROBOTS content="INDEX, FOLLOW">
.....

四、生命周期

Pod的生命周期变化主要体现在status变化,最初状态为Pending阶段,至少要有一个应用容器启动成功后,则进入Running,之后取决于 Pod 中是否有容器以 失败状态结束而进入 Succeeded 或者 Failed 阶段。

  • Pending。这个状态意味着,Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中。但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
  • Running。这个状态下,Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
  • Succeeded。这个状态意味着,Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
  • Failed。这个状态下,Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
  • Unknown。这是一个异常状态,意味着 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题。

五、容器状态

Kubernetes 会跟踪 Pod 中每个容器的状态,就像它跟踪 Pod 总体上的阶段一样。 你可以使用容器生命周期回调 来在容器生命周期中的特定时间点触发事件。

一旦调度器将 Pod 分派给某个节点,kubelet 就通过 容器运行时 开始为 Pod 创建容器。 容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)。

六、Pod使用过程中的重要字段:

凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的。

1.NodeSelector:是一个供用户将 Pod 与 Node 进行绑定的字段.

apiVersion: v1
kind: Pod
...
spec:
 nodeSelector:
   disktype: ssd

指明Pod永远只能运行在携带“disktype: ssd”标签的节点上;否则会调度失败

2.HostAliases:定义了 Pod 的 hosts 文件(比如 /etc/hosts)的内容

apiVersion: v1
kind: Pod
...
spec:
  hostAliases:
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
...

在Pod的YAML文件中,我们添加了一组映射关系。

如果我们咋pod启动之后进入pod内去手动添加hosts文件,这样有一个弊端就是当pod重启后,文件将重载。

cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
...
10.244.135.10 hostaliases-pod
10.1.2.3 foo.remote
10.1.2.3 bar.remote

3.凡是跟容器的 Linux Namespace 相关的属性,也一定是 Pod 级别的

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true

shareProcessNamespace: true

意味着这个Pod里的容器要共享PID Namespace

[root@ycloud kubernetes]# kubectl apply -f shareproccessnamespace.yaml 
pod/nginx created
---
[root@ycloud kubernetes]# kubectl exec -it nginx -c shell -- /bin/sh
/ # ps ax
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    8 root      0:00 nginx: master process nginx -g daemon off;
   39 101       0:00 nginx: worker process
   40 101       0:00 nginx: worker process
   41 101       0:00 nginx: worker process
   42 101       0:00 nginx: worker process
   43 root      0:00 sh
   57 root      0:00 /bin/sh
   64 root      0:00 ps ax
/ # 

可以看到,在这个容器里,我们不仅可以看到它本身的 ps ax 指令,还可以看到 nginx 容器的进程,以及 Infra 容器的 /pause 进程。这就意味着,整个 Pod 里的每个容器的进程,对于所有容器来说都是可见的:它们共享了同一个 PID Namespace。

4.Pod中的容器共享主机的Namespace

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  hostNetwork: true
  hostIPC: true
  hostPID: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true

在这个 Pod 中,我定义了共享宿主机的 Network、IPC 和 PID Namespace。这就意味着,这个 Pod 里的所有容器,会直接使用宿主机的网络、直接与宿主机进行 IPC 通信、看到宿主机里正在运行的所有进程。

5.Lifecycle处理

k8s 支持 postStart 和 preStop 事件。 当一个容器启动后,k8s 将立即发送 postStart 事件;在容器被终结之前, k8s 将发送一个 preStop 事件。容器可以为每个事件指定一个处理程序。

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]

你可以看到 postStart 命令在容器的 /usr/share 目录下写入文件 message。 命令 preStop 负责终止 nginx 服务。当因为失效而导致容器终止

[root@ycloud ~]# kubectl exec -it lifecycle-demo -- cat /usr/share/message
Hello from the postStart handler

k8s 在容器结束前立即发送 preStop 事件。除非 Pod 宽限期限超时,k8s 的容器管理逻辑 会一直阻塞等待 preStop 处理函数执行完毕。

总结

这篇文章中,主要讲解了pod对象,一些重要字段,分析了Pod Namespace共享原理以及Pod的生命周期。

Pod对象是k8s中比较核心的一个概念,在后面的控制器学习中都会用到。

参考文献

https://time.geekbang.org/column/article/40366

https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/

https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/