在移动互联网时代,消费者的消费行为已经 "全天候化",为此,商家的业务系统也要保持 7×24 小时不间断地提供服务以满足消费者的需求。很难想像如今还会有以 "中断业务" 为前提的服务系统更新升级。如果 官方发布公告说:每周六晚 23:00~ 次日凌晨 2:00 进行例行系统升级,不能提供服务,作为用户的你会怎么想、怎么做呢?因此,各个平台在最初设计时就要考虑到服务的更新升级问题,部署在 Kubernetes 集群中的 Service 也不例外。
传统的升级更新,是先将服务全部下线,业务停止后再更新版本和配置,然后重新启动并提供服务。这样的模式已经完全不能满足 "时代的需要" 了。在并发化、高可用系统普及的今天,服务的升级更新至少要做到 "业务不中断"。而滚动更新 (Rolling-update) 恰是满足这一需求的一种系统更新升级方案。
简单来说,滚动更新就是针对多实例服务的一种不中断服务的更新升级方式。一般情况,对于多实例服务,滚动更新采用对各个实例逐个进行单独更新而非同一时刻对所有实例进行全部更新的方式。"滚动更新" 的先进之处在于 "滚动" 这个概念的引入,笔者觉得它至少有以下两点含义:
a) "滚动" 给人一种 "圆" 的映像,表意:持续,不中断。"滚动" 的理念是一种趋势,我们常见的 ""、" "都是" 滚动 "理念的应用。与传统的大版本周期性发布 / 更新相比," 滚动 " 可以让用户更快、更及时地使用上新 Feature,缩短市场反馈周期,同时滚动式的发布和更新又会将对用户体验的影响降到最小化。
b) "滚动" 可向前,也可向后。我们可以在更新过程中及时发现 "更新" 存在的问题,并 "向后滚动",实现更新的回退,可以最大程度上降低每次更新升级的风险。
对于在 Kubernetes 集群部署的 Service 来说,Rolling update 就是指一次仅更新一个 Pod,并逐个进行更新,而不是在同一时刻将该 Service 下面的所有 Pod shutdown,避免将业务中断的尴尬。
对于我们要部署的 Application 来说,一般是由多个抽象的 Service 组成。在 Kubernetes 中,一个 通过 match 出一个 Pods 集合,这些 Pods 作为 Service 的 endpoint,是真正承载业务的实体。而 Pod 在集群内的部署、调度、副本数保持则是通过 或 这些高 level 的抽象来管理的,下面是一幅示意图:
新版本的 Kubernetes 推荐用 替代 ReplicationController,在 Deployment 这个概念下在保持 Pod 副本数上实际发挥作用的是隐藏在背后的 。
因此,我们可以看到 Kubernetes 上 Service 的 实质上是对 Service 所 match 出来的 Pod 集合的 Rolling update,而控制 Pod 部署、调度和副本调度的却又恰恰是 Deployment 和 replication controller,因此后两者才是 kubernetes service rolling update 真正要面对的实体。
kubernetes 在 kubectl cli 工具中仅提供了对 Replication Controller 的 rolling-update 支持,通过 kubectl -help,我们可以查看到下面的命令 usage 描述:
- # kubectl -help
- ... ...
- Deploy Commands:
- rollout Manage a deployment rollout
- rolling-update Perform a rolling update of the given ReplicationController
- scale Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job
- autoscale Auto-scale a Deployment, ReplicaSet, or ReplicationController
- ... ...
- # kubectl help rolling-update
- ... ...
- Usage:
- kubectl rolling-update OLD_CONTROLLER_NAME ([NEW_CONTROLLER_NAME] --image=NEW_CONTAINER_IMAGE | -f
- NEW_CONTROLLER_SPEC) [options]
- ... ...
我们现在来看一个例子,看一下 kubectl rolling-update 是如何对 service 下的 Pods 进行滚动更新的。我们的 kubernetes 集群有两个版本的 :
- # docker images|grep nginx
- nginx 1.11.9 cc1b61406712 2 weeks ago 181.8 MB
- nginx 1.10.1 bf2b4c2d7bf5 4 months ago 180.7 MB
在例子中我们将 Service 的 Pod 从 nginx 1.10.1 版本滚动升级到 1.11.9 版本。
我们的 rc-demo-v0.1.yaml 文件内容如下:
- apiVersion: v1
- kind: ReplicationController
- metadata:
- name: rc-demo-nginx-v0.1
- spec:
- replicas: 4
- selector:
- app: rc-demo-nginx
- ver: v0.1
- template:
- metadata:
- labels:
- app: rc-demo-nginx
- ver: v0.1
- spec:
- containers:
- - name: rc-demo-nginx
- image: nginx:1.10.1
- ports:
- - containerPort: 80
- protocol: TCP
- env:
- - name: RC_DEMO_VER
- value: v0.1
创建这个 replication controller:
- # kubectl create -f rc-demo-v0.1.yaml
- replicationcontroller "rc-demo-nginx-v0.1" created
- # kubectl get pods -o wide
- NAME READY STATUS RESTARTS AGE IP NODE
- rc-demo-nginx-v0.1-2p7v0 1/1 Running 0 1m 172.30.192.9 iz2ze39jeyizepdxhwqci6z
- rc-demo-nginx-v0.1-9pk3t 1/1 Running 0 1m 172.30.192.8 iz2ze39jeyizepdxhwqci6z
- rc-demo-nginx-v0.1-hm6b9 1/1 Running 0 1m 172.30.0.9 iz25beglnhtz
- rc-demo-nginx-v0.1-vbxpl 1/1 Running 0 1m 172.30.0.10 iz25beglnhtz
Service manifest 文件 rc-demo-svc.yaml 的内容如下:
- apiVersion: v1
- kind: Service
- metadata:
- name: rc-demo-svc
- spec:
- ports:
- - port: 80
- protocol: TCP
- selector:
- app: rc-demo-nginx
创建这个 service:
- # kubectl create -f rc-demo-svc.yaml
- service "rc-demo-svc" created
- # kubectl describe svc/rc-demo-svc
- Name: rc-demo-svc
- Namespace: default
- Labels: <none>
- Selector: app=rc-demo-nginx
- Type: ClusterIP
- IP: 10.96.172.246
- Port: <unset> 80/TCP
- Endpoints: 172.30.0.10:80,172.30.0.9:80,172.30.192.8:80 + 1 more...
- Session Affinity: None
- No events.
可以看到之前 replication controller 创建的 4 个 Pod 都被置于 rc-demo-svc 这个 service 的下面了,我们来访问一下该服务:
- # curl -I http://10.96.172.246:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- Date: Wed, 08 Feb 2017 08:45:19 GMT
- Content-Type: text/html
- Content-Length: 612
- Last-Modified: Tue, 31 May 2016 14:17:02 GMT
- Connection: keep-alive
- ETag: "574d9cde-264"
- Accept-Ranges: bytes
- # kubectl exec rc-demo-nginx-v0.1-2p7v0 env
- ... ...
- RC_DEMO_VER=v0.1
- ... ...
通过 Response Header 中的 Server 字段,我们可以看到当前 Service pods 中的 nginx 版本为 1.10.1;通过打印 Pod 中环境变量,得到 RC_DEMO_VER=v0.1。
接下来,我们来 rolling-update rc-demo-nginx-v0.1 这个 rc,我们的新 rc manifest 文件 rc-demo-v0.2.yaml 内容如下:
- apiVersion: v1
- kind: ReplicationController
- metadata:
- name: rc-demo-nginx-v0.2
- spec:
- replicas: 4
- selector:
- app: rc-demo-nginx
- ver: v0.2
- template:
- metadata:
- labels:
- app: rc-demo-nginx
- ver: v0.2
- spec:
- containers:
- - name: rc-demo-nginx
- image: nginx:1.11.9
- ports:
- - containerPort: 80
- protocol: TCP
- env:
- - name: RC_DEMO_VER
- value: v0.2
rc-demo-new.yaml 与 rc-demo-old.yaml 有几点不同:rc 的 name、image 的版本以及 RC_DEMO_VER 这个环境变量的值:
- # diff rc-demo-v0.2.yaml rc-demo-v0.1.yaml
- 4c4
- < name: rc-demo-nginx-v0.2
- ---
- > name: rc-demo-nginx-v0.1
- 9c9
- < ver: v0.2
- ---
- > ver: v0.1
- 14c14
- < ver: v0.2
- ---
- > ver: v0.1
- 18c18
- < image: nginx:1.11.9
- ---
- > image: nginx:1.10.1
- 24c24
- < value: v0.2
- ---
- > value: v0.1
我们开始 rolling-update,为了便于跟踪 update 过程,这里将 update-period 设为 10s,即每隔 10s 更新一个 Pod:
- # kubectl rolling-update rc-demo-nginx-v0.1 --update-period=10s -f rc-demo-v0.2.yaml
- Created rc-demo-nginx-v0.2
- Scaling up rc-demo-nginx-v0.2 from 0 to 4, scaling down rc-demo-nginx-v0.1 from 4 to 0 (keep 4 pods available, don't exceed 5 pods)
- Scaling rc-demo-nginx-v0.2 up to 1
- Scaling rc-demo-nginx-v0.1 down to 3
- Scaling rc-demo-nginx-v0.2 up to 2
- Scaling rc-demo-nginx-v0.1 down to 2
- Scaling rc-demo-nginx-v0.2 up to 3
- Scaling rc-demo-nginx-v0.1 down to 1
- Scaling rc-demo-nginx-v0.2 up to 4
- Scaling rc-demo-nginx-v0.1 down to 0
- Update succeeded. Deleting rc-demo-nginx-v0.1
- replicationcontroller "rc-demo-nginx-v0.1" rolling updated to "rc-demo-nginx-v0.2"
从日志可以看出:kubectl rolling-update 逐渐增加 rc-demo-nginx-v0.2 的 scale 并同时逐渐减小 rc-demo-nginx-v0.1 的 scale 值直至减到 0。
在升级过程中,我们不断访问 rc-demo-svc,可以看到新旧 Pod 版本共存的状态,服务并未中断:
- # curl -I http://10.96.172.246:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
- # curl -I http://10.96.172.246:80
- HTTP/1.1 200 OK
- Server: nginx/1.11.9
- ... ...
- # curl -I http://10.96.172.246:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
更新后的一些状态信息:
- # kubectl get rc
- NAME DESIRED CURRENT READY AGE
- rc-demo-nginx-v0.2 4 4 4 5m
- # kubectl get pods
- NAME READY STATUS RESTARTS AGE
- rc-demo-nginx-v0.2-25b15 1/1 Running 0 5m
- rc-demo-nginx-v0.2-3jlpk 1/1 Running 0 5m
- rc-demo-nginx-v0.2-lcnf9 1/1 Running 0 6m
- rc-demo-nginx-v0.2-s7pkc 1/1 Running 0 5m
- # kubectl exec rc-demo-nginx-v0.2-25b15 env
- ... ...
- RC_DEMO_VER=v0.2
- ... ...
官方文档说 kubectl rolling-update 是由 client side 实现的 rolling-update,这是因为 roll-update 的逻辑都是由 kubectl 发出 N 条命令到 APIServer 完成的,在 kubectl 的代码中我们可以看到这点:
- //https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/rollingupdate.go
- ... ...
- func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
- ... ...
- err = updater.Update(config)
- if err != nil {
- return err
- }
- ... ...
- }
- //https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/rolling_updater.go
- func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
- ... ...
- // Scale newRc and oldRc until newRc has the desired number of replicas and
- // oldRc has 0 replicas.
- progressDeadline := time.Now().UnixNano() + config.Timeout.Nanoseconds()
- for newRc.Spec.Replicas != desired || oldRc.Spec.Replicas != 0 {
- // Store the existing replica counts for progress timeout tracking.
- newReplicas := newRc.Spec.Replicas
- oldReplicas := oldRc.Spec.Replicas
- // Scale up as much as possible.
- scaledRc, err := r.scaleUp(newRc, oldRc, desired, maxSurge, maxUnavailable, scaleRetryParams, config)
- if err != nil {
- return err
- }
- newRc = scaledRc
- ... ...
- }
在 rolling_updater.go 中 Update 方法使用一个 for 循环完成了逐步减少 old rc 的 replicas 和增加 new rc 的 replicas 的工作,直到 new rc 到达期望值,old rc 的 replicas 变为 0。
通过 kubectl rolling-update 实现的滚动更新有很多不足:
- 由 kubectl 实现,很可能因为网络原因导致 update 中断;
- 需要创建一个新的 rc,名字与要更新的 rc 不能一样;虽然这个问题不大,但实施起来也蛮别扭的;
- 回滚还需要执行 rolling-update,只是用的老版本的 rc manifest 文件;
- service 执行的 rolling-update 在集群中没有记录,后续无法跟踪 rolling-update 历史。
不过,由于 Replication Controller 已被 Deployment 这个抽象概念所逐渐代替,下面我们来考虑如何实现 Deployment 的滚动更新以及 deployment 滚动更新的优势。
kubernetes Deployment 是一个更高级别的抽象,就像文章开头那幅示意图那样,Deployment 会创建一个 Replica Set,用来保证 Deployment 中 Pod 的副本数。由于 kubectl rolling-update 仅支持 replication controllers,因此要想 rolling-updata deployment 中的 Pod,你需要修改 Deployment 自己的 manifest 文件并应用。这个修改会创建一个新的 Replica Set,在 scale up 这个 Replica Set 的 Pod 数的同时,减少原先的 Replica Set 的 Pod 数,直至 zero。而这一切都发生在 Server 端,并不需要 kubectl 参与。
我们同样来看一个例子。我们建立第一个版本的 deployment manifest 文件:deployment-demo-v0.1.yaml。
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- name: deployment-demo
- spec:
- replicas: 4
- selector:
- matchLabels:
- app: deployment-demo-nginx
- minReadySeconds: 10
- template:
- metadata:
- labels:
- app: deployment-demo-nginx
- version: v0.1
- spec:
- containers:
- - name: deployment-demo
- image: nginx:1.10.1
- ports:
- - containerPort: 80
- protocol: TCP
- env:
- - name: DEPLOYMENT_DEMO_VER
- value: v0.1
创建该 deployment:
- # kubectl create -f deployment-demo-v0.1.yaml --record
- deployment "deployment-demo" created
- # kubectl get deployments
- NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
- deployment-demo 4 4 4 0 10s
- # kubectl get rs
- NAME DESIRED CURRENT READY AGE
- deployment-demo-1818355944 4 4 4 13s
- # kubectl get pods -o wide
- NAME READY STATUS RESTARTS AGE IP NODE
- deployment-demo-1818355944-78spp 1/1 Running 0 24s 172.30.0.10 iz25beglnhtz
- deployment-demo-1818355944-7wvxk 1/1 Running 0 24s 172.30.0.9 iz25beglnhtz
- deployment-demo-1818355944-hb8tt 1/1 Running 0 24s 172.30.192.9 iz2ze39jeyizepdxhwqci6z
- deployment-demo-1818355944-jtxs2 1/1 Running 0 24s 172.30.192.8 iz2ze39jeyizepdxhwqci6z
- # kubectl exec deployment-demo-1818355944-78spp env
- ... ...
- DEPLOYMENT_DEMO_VER=v0.1
- ... ...
deployment-demo 创建了 ReplicaSet:deployment-demo-1818355944,用于保证 Pod 的副本数。
我们再来创建使用了该 deployment 中 Pods 的 Service:
- # kubectl create -f deployment-demo-svc.yaml
- service "deployment-demo-svc" created
- # kubectl get service
- NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- deployment-demo-svc 10.109.173.225 <none> 80/TCP 5s
- kubernetes 10.96.0.1 <none> 443/TCP 42d
- # kubectl describe service/deployment-demo-svc
- Name: deployment-demo-svc
- Namespace: default
- Labels: <none>
- Selector: app=deployment-demo-nginx
- Type: ClusterIP
- IP: 10.109.173.225
- Port: <unset> 80/TCP
- Endpoints: 172.30.0.10:80,172.30.0.9:80,172.30.192.8:80 + 1 more...
- Session Affinity: None
- No events.
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
好了,我们看到该 service 下有四个 pods,Service 提供的服务也运行正常。
接下来,我们对该 Service 进行更新。为了方便说明,我们建立了 deployment-demo-v0.2.yaml 文件,其实你也大可不必另创建文件,直接再上面的 deployment-demo-v0.1.yaml 文件中修改也行:
- # diff deployment-demo-v0.2.yaml deployment-demo-v0.1.yaml
- 15c15
- < version: v0.2
- ---
- > version: v0.1
- 19c19
- < image: nginx:1.11.9
- ---
- > image: nginx:1.10.1
- 25c25
- < value: v0.2
- ---
- > value: v0.1
我们用 deployment-demo-v0.2.yaml 文件来更新之前创建的 deployments 中的 Pods:
- # kubectl apply -f deployment-demo-v0.2.yaml --record
- deployment "deployment-demo" configured
apply 命令是瞬间接收到 apiserver 返回的 Response 并结束的。但 deployment 的 rolling-update 过程还在进行:
- # kubectl describe deployment deployment-demo
- Name: deployment-demo
- ... ...
- Replicas: 2 updated | 4 total | 3 available | 2 unavailable
- StrategyType: RollingUpdate
- MinReadySeconds: 10
- RollingUpdateStrategy: 1 max unavailable, 1 max surge
- Conditions:
- Type Status Reason
- ---- ------ ------
- Available True MinimumReplicasAvailable
- OldReplicaSets: deployment-demo-1818355944 (3/3 replicas created)
- NewReplicaSet: deployment-demo-2775967987 (2/2 replicas created)
- Events:
- FirstSeen LastSeen Count From SubObjectPath Type Reason Message
- --------- -------- ----- ---- ------------- -------- ------ -------
- 12m 12m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set deployment-demo-1818355944 to 4
- 11s 11s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set deployment-demo-2775967987 to 1
- 11s 11s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set deployment-demo-1818355944 to 3
- 11s 11s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set deployment-demo-2775967987 to 2
- # kubectl get pods
- NAME READY STATUS RESTARTS AGE
- deployment-demo-1818355944-78spp 1/1 Terminating 0 12m
- deployment-demo-1818355944-hb8tt 1/1 Terminating 0 12m
- deployment-demo-1818355944-jtxs2 1/1 Running 0 12m
- deployment-demo-2775967987-5s9qx 0/1 ContainerCreating 0 0s
- deployment-demo-2775967987-lf5gw 1/1 Running 0 12s
- deployment-demo-2775967987-lxbx8 1/1 Running 0 12s
- deployment-demo-2775967987-pr0hl 0/1 ContainerCreating 0 0s
- # kubectl get rs
- NAME DESIRED CURRENT READY AGE
- deployment-demo-1818355944 1 1 1 12m
- deployment-demo-2775967987 4 4 4 17s
我们可以看到这个 update 过程中 ReplicaSet 的变化,同时这个过程中服务并未中断,只是新旧版本短暂地交错提供服务:
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.11.9
- ... ...
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
最终所有 Pod 被替换为了 v0.2 版本:
- kubectl exec deployment-demo-2775967987-5s9qx env
- ... ...
- DEPLOYMENT_DEMO_VER=v0.2
- ... ...
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.11.9
- ... ...
我们发现 deployment 的 create 和 apply 命令都带有一个–record 参数,这是告诉 apiserver 记录 update 的历史。通过 kubectl rollout history 可以查看 deployment 的 update history:
- # kubectl rollout history deployment deployment-demo
- deployments "deployment-demo"
- REVISION CHANGE-CAUSE
- 1 kubectl create -f deployment-demo-v0.1.yaml --record
- 2 kubectl apply -f deployment-demo-v0.2.yaml --record
如果没有加 "–record",那么你得到的历史将会类似这样的结果:
- # kubectl rollout history deployment deployment-demo
- deployments "deployment-demo"
- REVISION CHANGE-CAUSE
- 1 <none>
同时,我们会看到 old ReplicaSet 并未被删除:
- # kubectl get rs
- NAME DESIRED CURRENT READY AGE
- deployment-demo-1818355944 0 0 0 25m
- deployment-demo-2775967987 4 4 4 13m
这些信息都存储在 server 端,方便回退!
Deployment 下 Pod 的回退操作异常简单,通过 rollout undo 即可完成。rollout undo 会将 Deployment 回退到 record 中的上一个 revision(见上面 rollout history 的输出中有 revision 列):
- # kubectl rollout undo deployment deployment-demo
- deployment "deployment-demo" rolled back
rs 的状态又颠倒回来:
- # kubectl get rs
- NAME DESIRED CURRENT READY AGE
- deployment-demo-1818355944 4 4 4 28m
- deployment-demo-2775967987 0 0 0 15m
查看 update 历史:
- # kubectl rollout history deployment deployment-demo
- deployments "deployment-demo"
- REVISION CHANGE-CAUSE
- 2 kubectl apply -f deployment-demo-v0.2.yaml --record
- 3 kubectl create -f deployment-demo-v0.1.yaml --record
可以看到 history 中最多保存了两个 revision 记录(这个 Revision 保存的数量应该可以设置)。
我们的最终目标是通过 API 来实现 service 的 rolling-update。Kubernetes 提供了针对 ,包括:create、read、replace、delete、patch、rollback 等。从这些 API 的字面意义上看,patch 和 rollback 很可能符合我们的需要,我们需要验证一下。
我们将 deployment 置为 v0.1 版本,即:image: nginx:1.10.1,DEPLOYMENT_DEMO_VER=v0.1。然后我们尝试通过 patch API 将 deployment 升级为 v0.2 版本,由于 patch API 仅接收 json 格式的 body 内容,我们将 deployment-demo-v0.2.yaml 转换为 json 格式:deployment-demo-v0.2.json。patch 是局部更新,这里偷个懒儿,直接将全部 deployment manifest 内容发给了 APIServer,让 server 自己做 merge^0^。
执行下面 curl 命令:
- #curl - H 'Content-Type:application/strategic-merge-patch+json' - X PATCH--data@deployment - demo - v0.2.json http: //localhost:8080/apis/extensions/v1beta1/namespaces/default/deployments/deployment-demo
这个命令输出一个 merge 后的 ,由于内容太多,这里就不贴出来了,内容参见: 。
跟踪命令执行时的 deployment 状态,我们可以看到该命令生效了:新旧两个 rs 的 Scale 值在此消彼长,两个版本的 Pod 在交替提供服务。
- # kubectl get rs
- NAME DESIRED CURRENT READY AGE
- deployment-demo-1818355944 3 3 3 12h
- deployment-demo-2775967987 2 2 2 12h
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.11.9
- ... ...
- # curl -I http://10.109.173.225:80
- HTTP/1.1 200 OK
- Server: nginx/1.10.1
- ... ...
不过通过这种方式 update 后,通过 rollout history 查看到的历史就有些 "不那么精确了":
- #kubectl rollout history deployment deployment-demo
- deployments "deployment-demo"
- REVISION CHANGE-CAUSE
- 8 kubectl create -f deployment-demo-v0.1.yaml --record
- 9 kubectl create -f deployment-demo-v0.1.yaml --record
目前尚无好的方法。但 rolling update 的确是 ok 了。
Patch API 支持三种类型的 Content-type:json-patch+json、strategic-merge-patch+json 和 merge-patch+json。对于后面两种,从测试效果来看,都一样。但 json-patch+json 这种类型在测试的时候一直报错:
- # curl -H 'Content-Type:application/json-patch+json' -X PATCH --data @deployment-demo-v0.2.json http://localhost:8080/apis/extensions/v1beta1/namespaces/default/deployments/deployment-demo
- {
- "kind": "Status",
- "apiVersion": "v1",
- "metadata": {},
- "status": "Failure",
- "message": "json: cannot unmarshal object into Go value of type jsonpatch.Patch",
- "code": 500
- }
kubectl patch 子命令似乎使用的是 strategic-merge-patch+json。源码中也没有过多说明三种方式的差别:
- //pkg/kubectl/cmd/patch.go
- func getPatchedJSON(patchType api.PatchType, originalJS, patchJS[] byte, obj runtime.Object)([] byte, error) {
- switch patchType {
- case api.JSONPatchType:
- patchObj,
- err: =jsonpatch.DecodePatch(patchJS) if err != nil {
- return nil,
- err
- }
- return patchObj.Apply(originalJS)
- case api.MergePatchType:
- return jsonpatch.MergePatch(originalJS, patchJS)
- case api.StrategicMergePatchType:
- return strategicpatch.StrategicMergePatchData(originalJS, patchJS, obj)
- default:
- // only here as a safety net - go-restful filters content-type
- return nil,
- fmt.Errorf("unknown Content-Type header for patch: %v", patchType)
- }
- }
- // DecodePatch decodes the passed JSON document as an RFC 6902 patch.
- // MergePatch merges the patchData into the docData.
- // StrategicMergePatch applies a strategic merge patch. The patch and the original document
- // must be json encoded content. A patch can be created from an original and a modified document
- // by calling CreateStrategicMergePatch.
接下来,我们使用 deployment rollback API 实现 deployment 的 rollback。我们创建一个 deployment-demo-rollback.json 文件作为请求的内容:
- //deployment-demo-rollback.json
- {
- "name" : "deployment-demo",
- "rollbackTo" : {
- "revision" : 0
- }
- }
revision:0 表示回退到上一个 revision。执行下面命令实现 rollback:
- # curl -H 'Content-Type:application/json' -X POST --data @deployment-demo-rollback.json http://localhost:8080/apis/extensions/v1beta1/namespaces/default/deployments/deployment-demo/rollback
- {
- "kind": "Status",
- "apiVersion": "v1",
- "metadata": {},
- "status": "Failure",
- "message": "rollback request for deployment \"deployment-demo\" succeeded",
- "code": 200
- }
- # kubectl describe deployment/deployment-demo
- ... ...
- Events:
- FirstSeen LastSeen Count From SubObjectPath Type Reason Message
- --------- -------- ----- ---- ------------- -------- ------ -------
- ... ...
- 27s 27s 1 {deployment-controller } Normal DeploymentRollback Rolled back deployment "deployment-demo" to revision 1
- ... ...
通过查看 deployment 状态可以看出 rollback 成功了。但这个 API 的 response 似乎有些 bug,明明是 succeeded 了 (code:200),但 status 却是 "Failure"。
如果你在 patch 或 rollback 过程中还遇到什么其他问题,可以通过 kubectl describe deployment/deployment-demo 查看输出的 Events 中是否有异常提示。
从上面的实验来看,通过 Kubernetes 提供的 API 是可以实现 Service 中 Pods 的 rolling-update 的,但这更适用于无状态的 Service。对于那些有状态的 Service(通过 或是 1.5 版本后的 实现的),这么做是否还能满足要求还不能确定。由于暂时没有环境,这方面尚未测试。
上述各个 manifest 的源码可以在 下载到。
© 2017,bigwhite. 版权所有.
来源: