Deployment
提供了一种更加简单的更新
Replication Controller
和
Pod
的机制,更好地解决了
Pod
的编排问题。本节将详细介绍如何通过
Deployment
实现
Pod
的管理。
15.1.1 什么是Deployment
Deployment 的中文意思为部署、调集,它是在Kubernetes 的1.2 版本中新增加的一个核心概念。Deployment 的实现为用户管理Pod 提供了一种更为便捷的方式。用户可以通过在Deployment 中描述所期望的集群状态,将现在的集群状态在一个可控的速度下逐步更新成所期望的集群状态。与Replication Controller (复制控制器)基本一样,Deployment 的主要职责同样是保证Pod 的数量和健康。Deployment 的绝大部分功能与Replication Controller 完全一样,因此,用户可以将Deployment 看作是升级版的Replication Controller 。
与Replication Controller 相比,除了继承Replication Controller 的全部功能之外,Deployment 还有以下新的特性。
事件和状态查看:可以查看Deployment 升级的详细进度和状态。
回滚:当升级Pod 镜像或者相关参数的时候发现问题,可以使用回滚操作回退到上一个稳定的版本或者指定的版本。
版本记录:每次对Deployment 的操作都能保存下来,以备后续可能的回滚操作。
暂停和启动:对于每次升级,都能够随时暂停和启动。
多种升级方案: 主要包括重建,即删除所有已存在的Pod ,重新创建新的Pod ;滚动升级,即采用逐步替换的策略,同时滚动升级时支持更多的附加参数。
15.1.2 Deployment 与ReplicaSet
讲到ReplicaSet 对象,不得不提到Replication Controller 。在旧版本的Kubernetes 中,只有Replication Controller 对象,它的主要作用是确保Pod 以用户指定的副本数运行,即如果有容器异常退出,Replication Controller 会自动创建新的Pod 来替代,因异常情况而多出来的容器也会自动回收。可以说,通过Replication Controller ,Kubernetes 实现了集群的高可用性。
在Kubernetes 1.15 后续版本中,建议使用ReplicaSet 来取代Replication Controller 。ReplicaSet 跟Replication Controller 没有本质的不同,只是名字不一样,并且ReplicaSet 支持集合式的选择器,而Replication Controller 只支持等式选择器。
虽然ReplicaSet 也可以独立使用,但是Kubernetes 并不建议用户直接操作ReplicaSet 对象。而是通过更高层次的对象Deployment 来自动管理ReplicaSet ,这样就无须担心与其他机制不兼容的问题,比如ReplicaSet 不支持滚动更新,但Deployment 支持,并且Deployment 还支持版本记录、回滚、暂停升级等高=级特性。这意味着用户几乎不会有机会直接管理ReplicaSet 。
15.1.3 运行Deployment
我们先从一个简单的例子开始介绍如何运行Deployment ,执行如下命令,结果如下:
$ kubectl run nginx-deployment --image=nginx:1.7.9 --replicas=3
deployment "nginx-deployment" created
在上面的命令中,nginx-deployment 是指要创建的Deployment 的名称,--image 选项用来指定容器所使用的镜像,其中nginx 为镜像名称,1.7.9 为版本号。replicas 选项用来指定Pod 的副本数为3 ,即当前集群中在任何时候都要保证有3 个Nginx 的副本在运行。
执行完命令之后,用户可以通过get 命令查看刚才运行的Deployment ,如下所示:
$ kubectl get deployment nginx-deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 10s
在上面的输出结果中,DESIRED 表示预期的副本数,即我们在命令中通过--replicas 参数指定的数量。CURRENT 表示当前的副本数。实际上,CURRENT 是Deployment 所创建的ReplicaSet 中Replica 的值,在部署的过程中,这个数值会不断地增加,一直增加到DESIRED 所指定的值为止。UP-TO-DATE 表示当前已经处于新版本的Pod 的副本数,主要用于在滚动升级的过程中,表示当前已经有多少个副本已经成功升级。AVAILABLE 表示当前集群中属于当前Deployment 的、可用的Pod 的副本数量,实际上,就是当前Deployment 所产生的、在当前集群中存活的Pod 的数量。AGE 表示当前Deployment 的年龄,即从创建到现在的时间差。
接下来,我们通过describe deployment 命令来查看当前Deployment 更加详细的信息,如下所示:
$ kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Sun, 24 Mar 2023 06:57:18 +0800
Labels: run=nginx-deployment
Selector: run=nginx-deployment
Replicas: 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets:
NewReplicaSet: nginx-deployment-3954615459 (3/3 replicas created)
No events.
上面的输出信息的含义大部分都非常明确,我们重点关注几点即可。首先Namespace 表示当前Deployment 所属的命名空间为default ,即默认的命名空间。Replicas 表示当前Pod 的副本信息。NewReplicaSet 表示由Deployment 自动创建的ReplicaSet ,其名称为nginx-deployment-3954615459 ,可以发现ReplicaSet 的名称使用了所属的Deployment 的名称作为前缀,这样可以非常容易地分辨这个ReplicaSet 是由哪个Deployment 创建的。
前面已经介绍过ReplicaSet 是一个非常重要的概念,而且用户几乎不会直接操作ReplicaSet ,而是通过Deployment 间接地管理。下面我们通过get replicaset 命令来查看ReplicaSet 的信息,如下所示:
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
nginx-deployment-3954615459 3 3 3 10s
在上面的输出信息中,ReplicaSet 的名称为nginx-deployment-3954615459 。READY 表示当前已经就绪的副本数。
为了了解更多关于nginx-deployment-3954615459 的信息,可以通过describe replicaset 命令来查看,如下所示:
$ kubectl describe replicaset nginx-deployment-3954615459
Name: nginx-deployment-3954615459
Namespace: default
Image(s): nginx:1.7.9
Selector: pod-template-hash=3954615459,run=nginx-deployment
Labels: pod-template-hash=3954615459
run=nginx-deployment
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
No events.
Image 表示容器所使用的镜像为nginx:1.7.9 ,Replicas 表示副本数,Pods Status 表示当前Deployment 所创建的Pod 的状态,可以看到有3 个副本处于运行(Running )状态,没有处于等待(Waiting )、结束(Succeeded )或者失败(Failed )状态的Pod 。
接下来,我们继续探讨Deployment 所创建的Pod 。执行get pod 命令,获取当前集群中的Pod 信息,如下所示:
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
...
nginx-deployment-3954615459-4zp22 1/1 Running 0 1h 172.17.0.5 192.168.1.122
nginx-deployment-3954615459-b2jvj 1/1 Running 0 1h 172.17.0.3 127.0.0.1
nginx-deployment-3954615459-rpjcp 1/1 Running 0 1h 172.17.0.4 127.0.0.1
...
可以得知,3 个Pod 副本都处于运行状态。另外,Kubernetes 还为每个Pod 自动分配了IP 地址。其中1 个副本运行在名称为192.168.1.122 的节点上,2 个副本运行在名称为127.0.0.1 的节点上。为了便于维护,Pod 的命名规则也是以Deployment 的名称以及ReplicaSet 的名称作为前缀的。由此可以推断出,在同一个节点上,可以同时运行一个Pod 的多个副本。
前面已经介绍过,Kubernetes 中的各种服务最终是由Pod 中的容器提供的。因此,了解Pod 及其容器是我们的最终目标。所以,下面通过kubectl describe pod 命令来查看其中某个Pod 副本的详细信息,如下所示:
$ kubectl describe pod nginx-deployment-3954615459-4zp22
Name: nginx-deployment-3954615459-4zp22
Namespace: default
Node: 192.168.1.122/192.168.1.122
Start Time: Sun, 24 Mar 2019 06:57:19 +0800
Labels: pod-template-hash=3954615459
run=nginx-deployment
Status: Running
IP: 172.17.0.2
Controllers: ReplicaSet/nginx-deployment-3954615459
Containers:
nginx-deployment:
Container ID: docker://df381a4ea070522af06d897d0c49a253def23ac7264a8b5bc3b50fbe67e86b6a
Image: nginx:1.7.9
Image ID: docker-pullable://docker.io/nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451
Port:
State: Running
Started: Sun, 24 Mar 2019 17:02:43 +0800
Last State: Terminated
Reason: Completed
Exit Code: 0
Started: Sun, 24 Mar 2019 07:59:14 +0800
Finished: Sun, 24 Mar 2019 08:03:36 +0800
Ready: True
Restart Count: 1
Volume Mounts:
Environment Variables:
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
No volumes.
QoS Class: BestEffort
Tolerations:
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
56m 56m 2 {kubelet 192.168.1.122} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy.
56m 56m 1 {kubelet 192.168.1.122} spec.containers{nginx-deployment} Normal Pulled Container image "nginx:1.7.9" already present on machine
56m 56m 1 {kubelet 192.168.1.122} spec.containers{nginx-deployment} Normal Created Created container with docker id df381a4ea070; Security:[seccomp=unconfined]
56m 56m 1 {kubelet 192.168.1.122} spec.containers{nginx-deployment} Normal Started Started container with docker id df381a4ea070
该命令的输出信息非常多,我们只要关注其中重要的部分即可。Node 表示当前Pod 所处的节点。IP 是Kubernetes 为当前Pod 副本分配的IP 地址,用户可以通过该IP 地址与Pod 通信。Controllers 表示当前Pod 由哪个Deployment 和ReplicaSet 创建。在本例中,Deployment 名称为nginx-deployment ,ReplicaSet 名称为nginx-deployment-3954615459 。Containers 表示当前Pod 中的容器的列表,在本例中,只有一个用户容器,其容器ID 为docker://df381a4ea070522af06d897d0c49a253def23ac7264a8b5bc3b50fbe67e86b6a 。Events 为当前Pod 副本的日志信息,便于用户调试。
最后,我们的关注点落在了容器上。由于nginx-deployment-3954615459-4zp22 当前运行在名称为192.168.1.122 的节点上,因此我们登录到192.168.1.122 ,然后使用前面介绍的容器管理命令docker ps 来查看当前节点的容器列表,如下所示:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
df381a4ea070 nginx:1.7.9 "nginx -g 'daemon ..." About an hour ago Up About an hour k8s
_nginx-deployment.a8dee3c8
_nginx-deployment-3954615459-4zp22
_default
_0271d1ca-4dbf-11e9-867a-000c2994a2b7
_20f4bc78
fb16d8356f3c registry.access.redhat.com/rhel7/pod-infrastructure:latest "/usr/bin/pod" About an hour ago Up About an hour k8s _POD.ae8ee9ac _nginx-deployment-3954615459-4zp22 _default _0271d1ca-4dbf-11e9-867a-000c2994a2b7 _5927077c
...
可以看到,在192.168.1.122 节点上有2 个容器,这2 个容器都是由nginx-deployment 创建的。还记得前面介绍的Pod 的结构吗?在每个Pod 中都存在着一个名称为Pause 的根容器,这个根容器是Kubernetes 系统的一部分。在上面的容器列表中,ID 为df381a4ea070 的是用户容器,ID 为fb16d8356f3c 的即为当前Pod 的根容器。
用户可以通过docker inspect 命令查看容器的详细信息,如下所示:
$ docker inspect df381a4ea070
[
{
"Id": "df381a4ea070522af06d897d0c49a253def23ac7264a8b5bc3b50fbe67e86b6a",
"Created": "2019-03-24T09:02:43.579970795Z",
"Path": "nginx",
"Args": [
"-g",
"daemon off;"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 13830,
"ExitCode": 0,
"Error": "",
"StartedAt": "2019-03-24T09:02:43.808003Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:84581e99d807a703c9c03bd1a31cd9621815155ac72a7365fd02311264512656",
"ResolvConfPath": "/var/lib/docker/containers/fb16d8356f3c7e2f630e9f7349791369cbbbfd813a77072e6ac1c3780f2ac710/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/fb16d8356f3c7e2f630e9f7349791369cbbbfd813a77072e6ac1c3780f2ac710/hostname",
"HostsPath": "/var/lib/kubelet/pods/0271d1ca-4dbf-11e9-867a-000c2994a2b7/etc-hosts",
"LogPath": "",
"Name": "/k8s _nginx-deployment.a8dee3c8 _nginx-deployment-3954615459-4zp22 _default _0271d1ca-4dbf-11e9-867a-000c2994a2b7 _20f4bc78",
"RestartCount": 0,
"Driver": "overlay2",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": [
...
由于这部分内容前面已经详细介绍过,因此此处不再重复讲解。
至此,我们已经成功地运行了一个Deployment 。
