Docker容器的本质:
Namespace做隔离,Cgroups做限制,rootfs做文件系统,Pod是Kubernetes中的最小调度单位。
Kubernets是未来云计算系统中的操作系统,而容器是其中的进程。
Pod是一组共享了某些资源的容器,Pod里的所有容器,共享的是同一个Network Namespace,并且可以声明同一个Volume。
凡是调度、网络、存储以及安全相关的属性,基本上是Pod级别的。凡是与容器的Linux Namespace相关的属性,也一定是Pod级别的。
NodeSelector:是一个供用户将Pod与Node进行绑定的字段。
Pod的设计就是要让它里面的内容尽可能多地共享Linux Namespace,仅保留必要地隔离和限制能力。
ImagePullPolicy 的值默认是 Always,即每次创建 Pod 都重新拉取一次镜像。另外,当容器的镜像是类似于 nginx 或者 nginx:latest 这样的名字时,ImagePullPolicy 也会被认为 Always。而如果它的值被定义为 Never 或者 IfNotPresent,则意味着 Pod 永远不会主动拉取这个镜像,或者只在宿主机上不存在这个镜像时才拉取。
Lifecycle 字段。它定义的是 Container Lifecycle Hooks。顾名思义,Container Lifecycle Hooks 的作用,是在容器状态发生变化时触发一系列“钩子”。
Pod对象在Kubernetes中的生命周期:
- Pending:这个状态意味着,Pod的YAML文件已经提交给了Kubernetes,API对象已经被创建并保存在etcd中,但是,这个Pod里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
- Running:这个状态下,Pod已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个在运行中
- Succeed:这个状态下,Pod里的所有容器都正常运行完毕,并且已经退出了。这种情况在与运行一次性任务时最为常见
- Unknow:这是一个异常状态,意味着Pod的状态不饿能持续地被kubelet汇报给kube-apiserver,这很有可能是主从节点(Master和kubelet)间的通信出现了问题。
Project Volume(投射数据卷):它们不是为了存在容器中的数据,也不是用来进行容器和宿主机之间的数据交换,这种特殊的Volume的作用是为容器提供预先定义好的数据。从容器的角度来看,这些Volume里的信息就是仿佛是被Kubernetes“投射”(Project)进容器当中的。
目前Kubernetes支持的Projected Volume一共有四种:
- Secret:将Pod想要访问的加密数据,存放到Etcd中。然后,你可以通过再Pod中的容器挂载Volume的方式,访问到这些Secret里保存的信息,一旦对应的etcd里的数据被更新,这些Volume里的文件内容,同样也会被更新,这是因为kubelet组件在定期维护这些Volume。
- ConfigMap:与Secret的区别仅仅是保存不需要加密的配置信息
- Downward API: 让Pod里的容器能够直接获取到这个Pod API对象本身的信息
- ServiceAccountToken:Kubernetes系统内置的一种“服务账户”,它是Kubernetes进行权限分配的对象。比如你想在你的容器里面访问kube-apiserver,就必须拥有这个secrete对象。
容器重启策略:
- 只要Pod的restartPolicy指定的策略允许重启异常的容器(比如:Always),那么这个Pod就会保持Running状态,并进行容器重启。否则,Pod就会进入Failed状态
- 对于包含多个容器的Pod,只有它里面所有的容器都进入异常状态后,Pod才会进入Failed状态。在此之前,Pod都是Running状态。此时,Pod的READY字段会显示正常的容器个数
restartPolicy,Pod的恢复策略:
- Always:在任何情况下,只要容器不在运行状态,就会自动重启容器
- OnFailure:只在容器异常时才自动重启容器
- Never:从来不重启容器
滚动更新:将一个集群中正在运行的多个Pod版本,交替地逐一升级的过程
StatefulSet:(有状态应用)可以帮助维持拓扑状态和存储状态
状态的分类:
- 拓扑状态:应用的多个实例之前不是完全对等的关系。这些应用实例,必须按照某些顺序启动,比如应用的主节点A要先于从节点B启动。而如果你把A和B两个Pod删除,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的Pod,必须和原来Pod的网络标识一样,这样原先的访问者才能使用同样的方法,访问这个新Pod
- 存储状态:应用的多个实例分别绑定了不同的存储数据。对于这些应用实例来说,Pod A第一次读取到的数据,和隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间Pod A被重新创建过。这种情况最典型的例子,就是一个数据库应用的多个存储实例。
StatefulSet的核心功能,就是通过某种方式记录这些状态,然后再Pod被创建时,能够为新Pod恢复这些状态。
Headless Service不需要分配一个VIP,而是可以直接以DNS记录的方式解析出被代理Pod的IP地址
DNS规则:
...svc.cluster.local
StatefulSet控制器的主要作用之一就是使用Pod模板创建Pod的时候,对它们进行编号,并且按照编号顺序逐一完成创建工作。而当StatefulSet的“控制循环”发现Pod的“实际状态”与“期望状态”不一致,需要新建或者删除Pod进行“调谐”的时候,它会严格按照这些Pod编号的顺序,逐一完成这些操作。
DaemonSet
- 这个Pod运行再Kubernetes集群里的每一个节点(Node)上
- 每个节点上只有一个这样的Pod实例
- 当有新的节点加入Kubernetes集群后,该Pod会自动地在新节点上被创建出来;而当旧节点被删除后,它上面的Pod也相应地会被回收掉
运行Daemon Set的例子:
- 各种网络插件的Agent组件,都必须运行在每一个节点上,用来处理这个节点上的容器网络;
- 各种存储插件的Agent组件,也必须运行在每一个节点上,用来在这个节点上挂载远程存储目录,操作容器的Volume目录
- 各种监控组件和日志组件,也必须运行在每一个节点上,负责这个节点上的监控信息和日志收集
在Kubernetes项目中,ControllerRevision其实是一个通用的版本管理对象。
声明式API---kubectl apply
Istio:一个基于Kubernetes羡慕的微服务治理框架:
Istio最根本的组件是一个运行在每一个应用Pod里的Envoy容器,Envoy就是一个高性能C++网络代理。
在Istio项目中,把这个代理服务以sidecar容器的方式,运行在了每一个被治理的应用Pod中。Pod里的所有容器都共享同一个Network Namespace。Envoy容器就能够通过配置Pod里的iptables规则,把整个Pod的进出流量接管下来。
Istio的控制层(Control Plane)里的Pilot组件,就能够通过每个Envoy容器的API,对这个Envoy代理进行配置,从而实现微服务治理。
假设这个Istio架构图左边的Pod是已经在运行的应用,而右边的Pod则是我们刚刚上线的应用的新版本。这时候,Pilot通过调节这两个Pod里的Envoy容器的配置,从而将90%的流量分配给旧版本的应用,将10%的流量分配给新版本的应用,并且,还可以在后续的过程中随时调整。这样,一个典型的“灰度发布”的场景就完成了。比如,Istio可以这个流量从90%-10%,改到80%-20%...再到0%-100%,就完成了一个灰度发布的过程。
一个Kubernetes的控制器,实际上就是一个“死循环”,它不断地获取“实际状态”,然后与“期望状态”作对比,并以此为依据决定下一步地操作。
Service Mesh:Istio项目的核心,就是由无数个运行在应用Pod中的Envoy容器组成的服务代理网格。
Kubernetes:声明式API才是Kubernetes项目编排能力”赖以生存“的核心所在:
其独特之处主要体现在:
- ”声明式“指的就是我只需要提交一个定义好的API对象来”声明“,我所期望的状态是什么样子
- ”声明式API”允许有多个API写端,以PATCH的方式对API对象进行修改,而不需关心本地原始YAML文件的内容
- 有了上述两个能力,Kubernetes项目才可以基于API对象的增、删、改、查,在完全无需外界干预的情况下,完成对“实际状态”和“期望状态”的调谐(Reconcile)过程。
Kubernetes中所有的API对象:
APIServer创建一个CronJob对象的过程:
24 | 深入解析声明式API(一):API对象的奥秘 (geekbang.org)
声明式API往往需要通过控制器模式来“监视”API对象的变化,(比如,创建或者删除Network),然后以此来决定实际要执行的具体工作。
自定义控制器:
RBAC:(ROLE-Based Access Control)基于角色的访问控制:负责完成授权工作的机制。
所谓角色:就是一组权限规则列表,而我们分配这些权限的方式,就是通过创建RoleBinding对象,将被作用者(subject)和权限列表进行绑定。
Operator的工作原理:利用Kubernetes自定义API资源(CRD),来描述想要部署的“有状态应用”;然后在自定义控制器里,根据自定义API对象的变化,来完成具体的部署和运维工作。