基本使用
1 简单的yaml文件
在K8s集群上可使用Kubectl命令以指定文件方式创建一个kind=Deployment的资源对象
$ kubectl create -f nginx.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.9.1
下图分别为 在终端查看生成的DeployMent, ReplicaSet, pod资源,以及他们之前的拓扑关系图(可以先忽略oldReplicaSet)
如图,k8s根据yaml中指定的spec.replicas值为我们创建3个pod,并在deployment整个运行周期中维护这个数量,然后根据spec.template.spec中的container数组配置,将容器组启动在每个pod中。
这是一个简单创建deployment任务过程。
2 更新及回滚
Deployment作为一个大数据结构(yaml文件)控制维护我们的业务,我们通过更新这个yaml文件来更新业务部署。
上节提到spec.template下是具体pod要启动的业务及配置,只有对spec.template进行更新才会触发pod重新部署(横向扩缩容不触发重新部署)
命令行支持两种更新方式,更新后自动触发deployment更新
更新结果是根据deployment中配置的replicas数和spec.template中定义的模板,生成pod。然后清理上版本的旧pod。
//直接使用 kubectl set 更新对象
$ kubectl set image deployment/nginx nginx=nginx:1.9.1
//直接更新nginx yaml文件
$ kubectl edit deployment/nginx
此时观察到系统中存在两个replicaSet,如果正常发布,Dp拥有两个Rs,新版Rs下维护3个pod,旧版下0个,k8s默认为我们保留更新过的版本,方便我们回滚版本使用。
以下是一个更新及回滚过程中Rs的状态
(初次发布后Rs状态 -> set修改镜像触发更新 -> 新pod生成旧版本下pod被清理 -> 回滚 -> "旧"版本pod被重建,"新"版本pod被清理)
以上为基本的更新/回滚流程。两个问题:
- 过程描述中,回滚后的新旧版本被我加了双引号
- 倒数第2次get Rs信息,发现版本之间数量的变化并非单独的清理旧版,发起新版。
(后面读源码将讲到)
暂停与恢复
暂停态时,对spec.template资源的更新都不会生效。恢复状态后,再执行更新操作。官方现在给的解释为:暂停态为支持多次更新配置而不用触发更新。
命令:
$ kubectl rollout pause deployment/nginx //暂停
$ kubectl rollout resume deployment/nginx //恢复
因为不会触发更新,所以理论上也不支持回滚。在暂停态时,发起回滚属于非法操作。
STATUS
Dp结构体主要包含3个部分:
* ObjectMeta 元数据
* DeploymentSpec Dp任务期望状态
* Status 处理状态
其中Meta由用户指定一部分,另一部分系统维护。DpSpec基本由用户指定,Status完全基本由系统控制,在同步过程中对此状态进行参考修改。
我目前根据Dp配置中的condition判断k8s在处理过程中的状态:
以上表示两项结果:
- Available 服务是否达到可用状态(可自定义livenessProbe、readinessProbe等来指定服务可用标志,默认pod内容器正常启动即为可用),图中此项status为True,原因为满足用户期望的最小可用实例数
- Progressing 指Dp收到的最近一个更新请求是否完成。例如回滚操作,指定时间内达到用户预期结果status将置为true,否则为False并设置错误原因。指定时间由spec.progressDeadlineSecond参数指定,Pause状态时此值不定义超时。 (此处更新指所有对Dp的更新,包括水平扩容操作)
概念
Label、Seletor and OwnerReference
观察本文图1,发现资源名的特点:
创建Dp时,我们定义nginx为name;Dp生成的Rs名均为nginx-hashstr;Rs又创建多个pod,pod名为rs-hashstr
假定Dp->Rs->Pod是一个从上到下的关系,那k8s通过上层selector和下层labels来确认下属于上,同时下层会保存上层的metaUid信息,用于所属确认和垃圾回收。
我通过–show-labels 来查看三项资源的lebels信息:
如上,Dp通过 app=roll
来确定Rs,但是不同版本Rs之间必须有差别,所以创建Rs时引入pod-template-hash作为selector 和 labels,并将其复制给pod.labels,这样在dp下同时存在两个版本时,多个Rs可以接管各自的pod
Rs和Pod中,都保存了ownerReferences信息。uid为所属Dp.uid。有两项用处(以获取dp下拥有的rs为例):
- 遍历检查rs.labels,首先检查并确认dp.selector需要是它的子集。然后检查rs.ownerReferences,确认为Dp信息时,表示此Rs属于Dp
- 删除Deployment时,仅操作Dp资源。检查Rs时,通过确认其uid标识的owner已被删除,确认是不是清理当前Rs资源
ControllerManager源码阅读
简单介绍一下事件处理前的如何获取事件集:
为了减轻对apiserver的压力,客户端存在一个Informer,它负责从apiserver端同步发生变更的数据到store,然后从store中读取需要处理的事件调用相应的Handler。
deploymentController会启动多个worker去接收store中的deployment-key,Handler处理函数为syncDeployment
syncDeployment
func (dc *DeploymentController) syncDeployment(key string) error {
//由key值获取Dp的namespaces和name
namespace, name, err := cache.SplitMetaNamespaceKey(key)
//根据ns、name从系统中获取deployment当前信息(此时有可能已被delete,在处理同步中会不断检查删除状态)
deployment, err := dc.dLister.Deployments(namespace).Get(name)
//deepcopy信息,更新状态时更新拷贝信息然后将副本更新到server
d := deployment.DeepCopy()
//行为:获取属于当前Dp的所有Rs,同时进行一些adopt和release操作
rsList, err := dc.getReplicaSetsForDeployment(d)
//获取rs列表下的所有pod,返回Map(key为Rs.UID value为PodList)
podMap, err := dc.getPodMapForDeployment(d, rsList)
//如果Pod已经被delete,调用getAllReplicaSetsAndSyncRevision更新版本信息,并同步状态信息。不明白这里,为什么已删除还要同步状态
if d.DeletionTimestamp != nil {
return dc.syncStatusOnly(d, rsList)
}
//检查是否为暂停或恢复事件。
//暂停时将condition中Progressing中 status=Unknown reason=DpPaused,此时不对其进行处理超时等检查
//检查为恢复请求并且当前为暂停时,更新Progressing为 status=Unknown reason=DpResume
if err = dc.checkPausedConditions(d)
//暂停态时,执行sync同步状态(本节会单独分析函数)
if d.Spec.Paused {
return dc.sync(d, rsList)
}
//检查有回滚事件时,回滚版本(下节会分析此函数)
if getRollbackTo(d) != nil {
return dc.rollback(d, rsList)
}
//发现desire与dp.replicas不符时,确定为正在进行扩缩容事件,调用sync同步
scalingEvent, err := dc.isScalingEvent(d, rsList)
if scalingEvent {
return dc.sync(d, rsList)
}
//根据两种发布策略检查并更新deployment到最新状态(下节会分析处理函数)
switch d