Kubernetes编排原理
Kubernetes
编排原理
为什么我们需要Pod
Pod
是Kubernetes
项目中最小的API
对象. 更专业的表述是: Pod
是Kubernetes
项目的原子调度单位.
容器的”单进程模型”并不是指容器里只能运行”一个”进程, 而是指容器无法管理多个进程. 这是因为容器里PID=1
的进程就是应用本身, 其他进程都是这个PID=1
进程的子进程. 可是, 用户编写的应用并不像正常操作系统里的init
进程或者systemd
那样拥有进程管理的功能.
在Kubernetes
项目里, Pod
的实现需要使用一个中间容器, 这个容器叫作Infra
容器. 在这个Pod
中, Infra
容器永远是第一个被创建的容器, 用户定义的其他容器则通过Join Network Namespace
的方式与Infra
容器关联在一起.
Pod
对象使用进阶
在Kubernetes
中有几种特殊的Volume
, 它们存在的意义不是为了存放容器里的数据, 也不是用于容器和宿主机之间的数据交换, 而是为容器提供预先定义好的数据.
Secret
Secret
的作用是帮你把Pod
想要访问的加密数据存放在etcd
中, 你就可以通过在Pod
的容器里挂载Volume
的方式访问这些Secret
里保存的信息了.
像这样通过挂载方式进入容器里的Secret
, 一旦其对应的etcd
里的数据更新, 这些Volume
里的文件内容也会更新. 其实, 这是kubelet
组件在定时维护这些Volume
.
需要注意的是, 这个更新可能会有一定的延时. 所以在编写应用程序时, 在发起数据库连接的代码处写好重试和超时的逻辑, 绝对是个好习惯.
ConfigMap
ConfigMap
与Secret
类似, 区别在于ConfigMap
保存的是无需加密的, 应用所需要的配置信息.
DownwardAPI
DownwardAPI
的作用是让Pod
里的容器能够直接获取这个PodAPI
对象本身的信息.
不过, 需要注意的是, DownwardAPI
能够获取的信息一定是Pod
里的容器进程启动之前就能确定下来的信息. 如果你想要获得Pod
容器运行后才会出现的信息, 比如容器进程的PID
, 就肯定不能使用DownwardAPI
了, 而应该考虑在Pod
里定义一个sidecar
容器.
ServiceAccountToken
Service Account
对象的作用就是Kubernetes
系统内置的一种”服务账户”, 它是Kubernetes
进行权限分配的对象. 比如, Service Account A
可以只被允许对Kubernetes
进行GET
操作, 而Service Account B
可以有Kubernetes API
的所有操作的权限.
像这样的Service Account
的授权信息和文件, 实际上保存在它所绑定的一个特殊的Secret
对象里. 这个特殊的Secret
对象叫作ServiceAccountToken
. 任何在Kubernetes
集群上运行的应用, 都必须使用ServiceAccountToken
里保存的授权信息(也就是token
), 才可以合法访问API Server
.
容器健康检查和恢复机制
Kubernetes
中的重启, 实际上却是重新创建了容器. 这个功能就是Kubernetes
里的Pod
恢复机制, 也叫restartPolicy
.
一定要强调的是, Pod
的恢复过程永远发生在当前节点上, 而不会跑到别的节点上. 事实上, 一旦一个Pod
与一个节点绑定, 除非这个绑定关系发生了变化pod.spec.node
字段被修改, 否则它永远不会离开这个节点.
如果你想让Pod
出现在其他的可用节点上, 就必须使用Deployment
这样的”控制器”来管理Pod
, 哪怕你只需要一个Pod
副本.
如果你关心这个容器退出后的上下文环境, 比如容器退出后的日志, 文件和目录, 就需要将restartPolicy
设置为Never
. 这是因为一旦容器被自动重新创建, 这些内容就有可能丢失(被垃圾回收了).
只要Pod
的restartPolicy
指定的策略允许重启异常的容器, 那么这个Pod
就会保持Running
状态并重启容器, 否则Pod
会进入Failed
状态.
对于包含多个容器的Pod
, 只有其中所有的容器都进入异常状态后, Pod
才会进入Failed
状态.
PodPreset
PodPreset
里定义的内容只会在Pod API
对象被创建之前追加在这个对象上, 而不会影响任何Pod
的控制器的定义. 比如, 现在提交的是一个nginx-deployment
, 那么这个Deployment
对象永远不会被PodPreset
改变, 被修改的只是这个Deployment
创建出来的所有Pod
.
如果你定义了同时作用于一个Pod
对象的多个PodPreset
, 会发生什么呢?
实际上, Kubernetes
项目会帮你合并这两个PodPreset
要做的修改, 而如果它们要做的有冲突的话, 这些冲突字段就不会被修改.
谈谈”控制器”思想
像Deployment
定义的template
字段, 在Kubernetes
项目中有一个专属的名字, 叫作PodTemplate
(Pod
模版).
作业副本与水平扩展
如果你更新了Deployment
的Pod
模版, 那么Deployment
就需要遵循一种叫作滚动更新的方式, 来升级现有容器.
创建Deployment
时建议指定--record
参数, 因此创建回滚版本的时候执行的kubectl
命令都会被记录下来, 否则通过kubectl rollout history
命令查看历史版本时, CHANGE-CAUSE
这一栏会是空.
深入理解StatefulSet(一)
: 拓扑状态
这种实例之间有不对等关系, 以及实例对外部是数据有依赖关系的应用, 就称为有状态应用.
StatefulSet
的设计其实非常容易理解, 它把现实世界里的应用状态抽象为了两种情况.
拓扑状态
应用的多个实例之间不是完全对等的. 这些应用实例必须按照某种顺序启动, 比如应用的主节点A要先于从节点B启动. 而如果删除A和B两个Pod
, 它们再次被创建出来时也必须严格按照这个顺序运行. 并且, 新创建出来的Pod
必须和原来Pod
的网络标识一样, 这丫原先的访问者才能使用同样的方法访问到这个新的Pod
.
存储状态
应用的多个实际分别绑定了不同的存储数据. 对于这些应用实例来说, Pod A
第一次读取到的数据和隔了10分钟之后再此读取到数据应该是同一份, 哪怕在此期间Pod A
被创建过.
StatefulSet
的核心功能, 就是通过某种方式记录这些状态, 然后在Pod
被重新创建时, 能够为新的Pod
恢复这些状态.
Service如何被访问的
第一种是以Service
的VIP
(virtual IP
, 虚拟IP)方式.
第二种是以Service
的DNS
方式.
在第二种Service DNS
的方式下, 具体又可以分为两种处理方法.
第一种处理方法是Normal Service
.
第二种处理方法是Headless Service
. Headless Service
中cluster IP
字段是的值是None
, 即这个Service
没有一个VIP
作为”头”. 这个Service
被创建后并不会被分配一个VIP
, 而是会以DNS
记录的方式暴露出它所代理的Pod
.
通过这种方法, Kubernetes
就成功地将Pod
的拓扑状态(比如哪个节点先启动, 哪个节点后启动), 按照Pod
的”名字+编号”的方式固定下来. 此外, Kubernetes
还为每个Pod
提供了一个固定且唯一的访问入口, 即这个Pod
对应的DNS
记录. 这些状态在StatefulSet
的整个生命周期里都保持不变, 绝不会因为对应Pod
的删除或者重新创建而失败.
DNS
记录本身不会变, 但它解析到的Pod
的IP
地址并不是固定的. 这就意味着, 对于有状态应用实例的访问, 必须使用DNS
记录或者hostname
的方式, 而绝不应该直接访问这些Pod
的IP
地址.
深入理解StatefulSet(二)
: 存储状态
StatefulSet
的控制器直接管理的是Pod
. Kubernetes
通过Headless Service
为这些有编号的Pod
, 在DNS
服务器中生成带有相同编号的DNS
编号. StatefulSet
还为每一个Pod
分配并创建了一个相同编号的PVC
.
容器化守护进程: DaemonSet
跟其他编排对象不同, DaemonSet
开始运行的时候很多时候要比整个Kubernetes
集群出现的时机要早.
在Kubernetes
项目中, 当一个节点的网络插件尚未安装时, 该节点就会被自动加上名为node.kubernetes.io/network-unavailable
的”污点”. 而通过这样一个Toleration
, 调度器在调度这个Pod
时就会忽略当前节点上的”污点”, 从而成功地将网络插件的Agent
组件调度到这台机器上启动起来.
撬动离线业务: Job
和CronJob
事实上, restartPolicy
在Job
对象里只允许设置为Never
和OnFailure
; 而在Deployment
对象里, restartPolicy
只允许设置为Always
.
如果restartPolicy=Never
, 那么离线业务失败后Job Controller
就会不断尝试创建一个新Pod
. 如果restartPolicy=OnFailure
, 那么离线业务失败后, Job Controller
就不会尝试创建新的Pod
, 而是不断尝试重启Pod
里的容器.
CronJob
是一个专门用来管理Job
对象的控制器.
基于角色的权限控制: RBAC
我们知道, Kubernetes
中所有API
对象都保存在etcd
里. 可是, 对这些API
对象的操作一定都是通过访问kube-apiserver
实现的. 其中一个非常重要的原因是, 需要API Server
来帮忙做授权工作. 而在Kubernetes
项目中, 负责完成授权工作的机制是RBAC
.