K8S Pod
概念:
Pod 扮演的是传统部署环境里“虚拟机”的角色。
如果你把 Pod 看成传统环境里的“机器”、把容器看作是运行在这个“机器”里的“用户程序”,那么很多关于 Pod 对象的设计就非常容易理解了。
比如,凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的。
重要字段:
NodeSelector:
是一个供用户将 Pod 与 Node 进行绑定的字段
1 | apiVersion: v1 |
这样的一个配置,意味着这个 Pod 永远只能运行在携带了“disktype: ssd”标签(Label)的节点上;否则,它将调度失败。
NodeName:
一旦 Pod 的这个字段被赋值,Kubernetes 项目就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。所以,这个字段一般由调度器负责设置,但用户也可以设置它来“骗过”调度器,当然这个做法一般是在测试或者调试的时候才会用到。
(比如有A、B两台node,如果是调度器来调度,两台机器均有可能被调度,手动设置到A机器,“骗过”调度器的调度操作,pod就直接部署到A机器了。)
HostAliases:
定义了 Pod 的 hosts 文件(比如 /etc/hosts)里的内容
1 | apiVersion: v1 |
如果直接修改hosts内容,那么pod被删除重建之后,kubelet会覆盖修改的内容
凡是跟容器的 Linux Namespace 相关的属性,也一定是 Pod 级别的
1 | apiVersion: v1 |
shareProcessNamespace: true → 这个 Pod 里的容器要共享 PID Namespace。
凡是 Pod 中的容器要共享宿主机的 Namespace,也一定是 Pod 级别的定义
1 | apiVersion: v1 |
在这个 Pod 中,定义了共享宿主机的 Network、IPC 和 PID Namespace。这就意味着,这个 Pod 里的所有容器,会直接使用宿主机的网络、直接与宿主机进行 IPC 通信、看到宿主机里正在运行的所有进程。
Container的主要字段:
Image(镜像)、 Command(启动命令)、 workingDir(容器的工作目录)、 Ports(容器的端口), volumeMounts(容器要挂载的Volume)
ImagePullPolicy 的值默认是 Always,即每次创建 Pod 都重新拉取一次镜像。而如果它的值被定义为 Never 或者 IfNotPresent,则意味着 Pod 永远不会主动拉取这个镜像,或者只在宿主机上不存在这个镜像时才拉取。
Lifecycle 字段,它定义的是 Container Lifecycle Hooks。Container Lifecycle Hooks 的作用,是在容器状态发生变化时触发一系列“钩子”。
1 | apiVersion: v1 |
postStart: 它指的是,在容器启动后,立刻执行一个指定的操作。需要明确的是,postStart 定义的操作,虽然是在 Docker 容器 ENTRYPOINT 执行之后,但它并不严格保证顺序。也就是说,在 postStart 启动时,ENTRYPOINT 有可能还没有结束。当然,如果 postStart 执行超时或者错误,Kubernetes 会在该 Pod 的 Events 中报出该容器启动失败的错误信息,导致 Pod 也处于失败的状态。(postStart 和ENTRYPOINT异步并发执行)
preStop 发生的时机,则是容器被杀死之前(比如,收到了 SIGKILL 信号)。而需要明确的是,preStop 操作的执行,是同步的。所以,它会阻塞当前的容器杀死流程,直到这个 Hook 定义操作完成之后,才允许容器被杀死,这跟 postStart 不一样。
Pod生命周期
Pod 生命周期的变化,主要体现在 Pod API 对象的 Status 部分,这是它除了 Metadata 和 Spec 之外的第三个重要字段。其中,pod.status.phase,就是 Pod 的当前状态,它有如下几种可能的情况:
Pending。这个状态意味着,Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中。但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
Running。这个状态下,Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
Succeeded。这个状态意味着,Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
Failed。这个状态下,Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
Unknown。这是一个异常状态,意味着 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题
更进一步地,Pod 对象的 Status 字段,还可以再细分出一组 Conditions。这些细分状态的值包括:PodScheduled、Ready、Initialized,以及 Unschedulable。它们主要用于描述造成当前 Status 的具体原因是什么。
对于 Pod 状态是 Ready,实际上不能提供服务的情况的几个例子:
- 程序本身有 bug,本来应该返回 200,但因为代码问题,返回的是500;
- 程序因为内存问题,已经僵死,但进程还在,但无响应;
- Dockerfile 写的不规范,应用程序不是主进程,那么主进程出了什么问题都无法发现;
- 程序出现死循环。
Projected Volume
在 Kubernetes 中,有几种特殊的 Volume,它们存在的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。这些特殊 Volume 的作用,是为容器提供预先定义好的数据。所以,从容器的角度来看,这些 Volume 里的信息就是仿佛是被 Kubernetes“投射”(Project)进入容器当中的
四种类型:
Secret, ConfigMap, Downward API, ServiceAccountToken.
Secret:
它的作用,是帮你把 Pod 想要访问的加密数据,存放到 Etcd 中。然后,你就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret 里保存的信息了.
1 | apiVersion: v1 |
user和pass 是secret中的key
1 | $ cat ./username.txt |
像这样通过挂载方式进入到容器里的 Secret,一旦其对应的 Etcd 里的数据被更新,这些 Volume 里的文件内容,同样也会被更新。其实,这是 kubelet 组件在定时维护这些 Volume。需要注意的是,这个更新可能会有一定的延时。所以在编写应用程序时,在发起数据库连接的代码处写好重试和超时的逻辑,绝对是个好习惯。
ConfigMap:
用法和secret几乎完全相同.
Downward API:
让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
1 | apiVersion: v1 |
不过,需要注意的是,Downward API 能够获取到的信息,一定是 Pod 里的容器进程启动之前就能够确定下来的信息。而如果你想要获取 Pod 容器运行后才会出现的信息,比如,容器进程的 PID,那就肯定不能使用 Downward API 了,而应该考虑在 Pod 里定义一个 sidecar 容器。
ServiceAccountToken
Service Account 对象的作用,就是 Kubernetes 系统内置的一种“服务账户”,它是 Kubernetes 进行权限分配的对象。比如,Service Account A,可以只被允许对 Kubernetes API 进行 GET 操作,而 Service Account B,则可以有 Kubernetes API 的所有操作权限。
ServiceAccountToken是一个特殊的Secret对象 , 保存着Service Account的授权信息和文件.
所以Kubernetes 项目的 Projected Volume 其实只有三种,因为第四种 ServiceAccountToken,只是一种特殊的 Secret 而已。
容器健康检查和恢复机制:
1 | apiVersion: v1 |
基本设计原则:
1、只要 Pod 的 restartPolicy 指定的策略允许重启异常的容器(比如:Always),那么这个 Pod 就会保持 Running 状态,并进行容器重启。否则,Pod 就会进入 Failed 状态 。
2、对于包含多个容器的 Pod,只有它里面所有的容器都进入异常状态后,Pod 才会进入 Failed 状态。
除了在容器中执行命令外,livenessProbe 也可以定义为发起 HTTP 或者 TCP 请求的方式,定义格式如下:
1 | ... |




