如何在 Kubernetes 部署应用

部署的难题

  对开发人员来说,在部署程序的新版本之前,需要考虑很多事情:

  • 如何在多台机器实现自动部署?
  • 在部署的过程,如何保证服务的高可用?
  • 如何知道新版本是否出现问题,健康检查要怎么做?
  • 如果新版本出现问题,能够做到回滚?

  很幸运,Kubernetes 提供了 Deployment 功能,帮助开发人员解决这些问题。

如何部署应用

  下面的配置文件定义了一个 Deployment,其中包含两个 Nginx Pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ cat nginx-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx-server # 这个 Deployment 管理着那些拥有这个标签的 Pod
template:
metadata:
labels:
app: nginx-server # 为所有 Pod 都打上这个标签
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

  接着,创建这个 Deployment:

1
2
3
4
$ kubectl apply -f nginx-deployment.yaml
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 2 2 2 2 5s

  可以观察到,这个 Deployment 管理着两个 Nginx Pod:

1
2
3
4
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-2569060349-2ph84 1/1 Running 0 2m
nginx-deployment-2569060349-4wdsd 1/1 Running 0 2m

比较 Deployment 与 ReplicaSet

  在 Kubernetes 入门指南 我们说到了 ReplicaSet,ReplicaSet 也是用来管理多个 Pod 的副本,那么 Deployment 和 ReplicaSet 的区别在哪里呢?
  当我们创建了 Deployment 之后,实际上也创建了 ReplicaSet,所以说 Deployment 管理着 ReplicaSet(实际上 Deployment 比 ReplicaSet 有着更多功能)。
  这里可以验证一下:

1
2
3
$ kubectl get rs --selector=app=nginx-server
NAME DESIRED CURRENT READY AGE
nginx-deployment-2569060349 2 2 2 26m

  由于 ReplicaSet 可以伸缩 Pod 的数量,因此 Deployment 也可以做到:

1
2
$ kubectl scale deployments nginx-deployment --replicas=3
deployment "nginx-deployment" scaled

  Deployment 管理着 ReplicaSet,因此当 Deployment 伸缩时,由它管理的 ReplicaSet 也会发生伸缩:

1
2
3
$ kubectl get rs --selector=app=nginx-server
NAME DESIRED CURRENT READY AGE
nginx-deployment-2569060349 3 3 3 30m

  但反过来,如果我们直接伸缩 ReplicaSet,那么 Deployment 也会相应发生伸缩吗?答案是不会的

1
2
3
4
5
$ kubectl scale rs nginx-deployment-2569060349 --replicas=1
replicaset "nginx-deployment-2569060349" scaled
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 38m

滚动更新

  更新版本时,如何保证服务的高可用?可以借助 Deployment 的滚动更新功能,例如下面的例子中,我们将 Nginx 从1.7.9升级到1.9.0版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ cat nginx-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
kubernetes.io/change-cause: "Update nginx from 1.7.9 to 1.9.0" # 注释
spec:
replicas: 2
selector:
matchLabels:
app: nginx-server
strategy: # 更新策略
rollingUpdate:
maxSurge: 0
maxUnavailable: 1 # 更新过程中,最多有一个 Pod 不可用
type: RollingUpdate # 策略类型: 滚动更新
template:
metadata:
labels:
app: nginx-server
spec:
containers:
- name: nginx
image: nginx:1.9.0
ports:
- containerPort: 80

  上述的配置文件中,我们指定更新策略为RollingUpdate(即滚动更新)。在这种策略下,更新版本的过程是渐进性的,分多次进行,每次都只更新少数几个 Pod,直到所有 Pod 都更新完毕。
  有两个参数可用用来配置滚动更新策略:

  • maxUnavailable表示在更新过程中,最多有多少个 Pod 不可用(这个数值也可以是百分比,例如 20% 表示最多有 20% 的 Pod 不可用)。譬如说,如果设置maxUnavailable为 1,那么更新过程会分多次进行,每次都是先销毁 1 个旧的 Pod,然后创建 1 个新的 Pod 替换它。
  • 在更新的过程,如果需要保证服务的 100% 可用,可以使用maxSurge,同时将maxUnavailable设置为 0。譬如说,将maxSurge设置为 1,同时将maxUnavailable设置为 0,那么更新过程会分多次进行,每次都是先创建 1 个新的 Pod,如果新的 Pod 可用了,这时才会替换掉旧的 Pod。

  使用下面的命令开始滚动升级:

1
2
$ kubectl apply -f nginx-deployment.yaml
deployment "nginx-deployment" configured

  查看升级过程的状态:

1
2
$ kubectl rollout status deployments nginx-deployment
deployment "nginx-deployment" successfully rolled out

回滚

  如果新版本出现问题,如何回滚呢?首先,先查看更新的历史:

1
2
3
4
5
$ kubectl rollout history deployment nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 <none>
2 Update nginx from 1.7.9 to 1.9.0

  可以看到,有两个版本号,可以查看某个版本的具体细节:

1
2
3
4
5
6
7
8
9
10
11
12
$ kubectl rollout history deployment nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
Labels: app=nginx-server
pod-template-hash=2000470006
Annotations: kubernetes.io/change-cause=Update nginx from 1.7.9 to 1.9.0
Containers:
nginx:
Image: nginx:1.9.0
Port: 80/TCP
Volume Mounts: <none>
Environment Variables: <none>
No volumes.

  假设我们想回滚到上一个版本,上一个版本的版本号是 1,那么可以执行:

1
2
$ kubectl rollout undo deployments nginx-deployment --to-revision=1
deployment "nginx-deployment" rolled back

  接着,查看更新的历史,可以看到已经成功回滚了:

1
2
3
4
5
$ kubectl rollout history deployment nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
2 Update nginx from 1.7.9 to 1.9.0
3 <none>

  版本号为 1 的那个版本不见了,说明被版本 3 替换掉了。

参考资料