Kubernetes进阶实战(第2版)
上QQ阅读APP看书,第一时间看更新

2.4 命令式应用编排

本节使用示例镜像ikubernetes/demoapp:v1.0演示容器应用编排的基础操作:应用部署、访问、查看,服务暴露和应用扩缩容等。一般说来,Kubernetes之上应用程序的基础管理操作由如下几个部分组成。

1)通过合用的控制器类的资源(例如Deployment或ReplicationController)创建并管控Pod对象以运行特定的应用程序:无状态(stateless)应用的部署和控制通常使用Deployment控制器,而有状态应用则需要使用StatefulSet控制器或扩展的Operator。

2)为Pod对象创建Service对象,以便向客户端提供固定的访问端点,并能够借助KubeDNS进行服务发现。

3)随时按需获取各资源对象的简要或详细信息,以了解其运行状态。

4)如有需要,对支持扩缩容的应用按需进行扩容或缩容;或者,为支持HPA的控制器组件(例如Deployment或ReplicationController)创建HPA资源对象,以实现Pod副本数目的自动伸缩。

5)应用程序的镜像出现新版本时,对其执行更新操作,若相应的控制器支持,修改指定的控制器资源中Pod模板的容器镜像为指定的新版本即可自动触发更新过程。

本节中的操作示例仅演示的部分功能,即应用部署、访问、查看,以及服务暴露。应用的扩缩容、升级及回滚等操作会在后面章节中详细介绍。

提示

以下操作命令均可在任何部署了kubectl并能正常访问Kubernetes集群的主机上执行,包括集群外的主机。复制Master主机上的/etc/kubernetes/admin.conf至相关用户主目录下的.kube/config文件即可正常执行,具体方法请参考kubeadm init命令结果中的提示。

2.4.1 应用编排

在Kubernetes集群上自主运行的Pod对象在非计划内终止后,其生命周期也随之终结,用户需要再次手动创建类似的Pod对象才能确保其再次可用。对于Pod数量众多的场景,尤其是对微服务业务来说,用户必将疲于应付此类需求。自动化应用编排是Kubernetes的核心价值之一,将应用托管给控制器编排才是发挥Kubernetes作用的根本所在。

1. 创建Deployment控制器对象

kubectl create deployment能够以“命令式命令”直接创建Deployment控制器对象,经该对象编排的Pod对象将由该命令生成的Pod模板自动创建,但需要用户以--image选项指定要使用的容器镜像。该命令的--dry-run={none|client|server}选项可用于测试运行,并不真正执行资源对象的创建过程,因而可用于在真正运行之前测试其是否能成功创建出指定的Deployment资源。例如,下面的命令会创建一个名为demoapp的Deployment控制器对象,它使用镜像ikubernetes/demoapp:v1.0创建Pod对象,但仅用于测试,运行后即退出。


~$ kubectl create deployment demoapp --image="ikubernetes/demoapp:v1.0" --dry-run=client
deployment.apps/demoapp created (dry run)

确认测试命令无误后,可在移除--dry-run选项后再次执行命令以完成资源对象的创建。


~$ kubectl create deployment demoapp --image="ikubernetes/demoapp:v1.0" 
deployment.apps/demoapp created

该命令创建的Deployment/demoapp对象会借助指定的镜像生成一个Pod,并自动为其添加app=demoapp标签,而控制器对象自身也将使用该标签作为标签选择器。镜像ikubernetes/demoapp:v1.0中定义的容器主进程为默认监听于80端口的Web应用程序demoapp。命令执行完成后,其运行效果示意图如图2-11所示,default名称空间中的Deployment/demoapp对象基于默认的标签选择器编排根据指定的镜像文件创建的一个Pod对象。

图2-11 Deployment对象demoapp及其创建的Pod对象

资源对象创建完成后,通常需要了解其当前状态是否正常,以及是否符合用户期望的目标状态,相关的操作一般使用kubectl get、kubectl describe等命令进行。

2. 打印资源对象的相关信息

如前所述,kubectl get命令可用于获取各种资源对象的相关信息,它能显示对象类型特有格式的简要信息,也能打印出YAML或JSON格式的详细信息,还能使用Go模板自定义要显示的属性及信息等。例如,下面是查看前面创建的Deployment对象的相关运行状态的命令及其输出结果:


~$ kubectl get deployments/demoapp
NAME      READY    UP-TO-DATE   AVAILABLE       AGE
demoapp    1/1         1            1           1m7s

get命令的默认输出格式是以指定的字段返回资源对象的简要信息,例如上面命令结果中的NAME和READY等,不同资源类型的输出结果也会有所区别。在上面显示Deployment资源简要信息的命令执行结果中,各字段基本可以做到见名知义。

▪NAME:Deployment资源对象的名称。

▪READY:以类似m/n格式返回两个数字,m代表就绪的Pod数量,n表示期望的总的Pod数量。

▪UP-TO-DATE:更新到最新版本定义的Pod对象副本数量,在控制器的滚动更新模式下,它表示已经完成版本更新的Pod对象副本数量。

▪AVAILABLE:当前处于可用状态的Pod对象副本数量,即可正常提供服务的副本数。

▪AGE:该资源的存在时长。

提示

Deployment资源对象是通过ReplicaSet控制器对象作为中间层实例完成对Pod对象的控制,各Pod的名称也是由ReplicaSet对象名称后跟几个随机字符构成。

Deployment/demoapp创建的唯一Pod对象运行正常与否、该对象被调度至哪个节点运行,以及当前是否就绪等也是用户在创建完成后应该关注的重点信息。由控制器创建的Pod对象的名称通常是以其隶属的ReplicaSet对象的名称为前缀,以随机字符为后缀,例如下面命令以app=demoapp为标签选择器打印筛选出的Pod对象的相关信息。


~$ kubectl get pods -l app=demoapp -o wide
NAME                      READY  STATUS   RESTARTS   AGE   IP    NODE  …
demoapp-6c5d545684-4t9kr   1/1   Running   0   7m  10.244.2.4 k8s-node02.ilinux.io…

在上面命令的执行结果中,每个字段代表Pod资源对象一个方面的属性,下面仅说明几个主要字段的功用。

▪READY:m/n格式,m表示Pod中就绪状态的容器数量,n表示Pod中总的容器数量。

▪STATUS:Pod的当前状态,其值可能是Pending、Running、Succeeded、Failed和Unknown等其中之一,并存在某些类型的中间状态(容器状态)。

▪RESTARTS:Pod对象可能会因容器进程崩溃、超出资源限额等故障而被重启,此字段记录了它重启的次数。

▪IP:Pod的IP地址,通常由网络插件自动分配。

▪NODE:该Pod对象绑定的Node,目标Node由Scheduler负责挑选。

确认Pod对象转为Running状态后,即可在集群中任一节点(或其他Pod对象)直接访问容器化应用的服务,如图2-12中节点Node X上的客户端程序Client,或集群上运行在Pod中的客户端程序。

图2-12 访问Pod中容器化应用服务程序

接下来,我们可在集群中任意一个节点上使用curl命令对地址为10.244.1.5的Pod对象demoapp-6c5d545684-4t9kr的80端口发起服务请求。例如,将下面的命令运行在k8s-node03节点上。


~$ POD_IP=$(kubectl get pods -l app=demoapp -o jsonpath={.items[0].status.podIP})
~$ curl http://${POD_IP}/
iKubernetes demoapp v1.0 !! ClientIP: 172.29.9.13, ServerName: demoapp-…, ServerIP: 10.244.1.5!

2.4.2 部署Service对象

简单来说,Service对象就是一组Pod的逻辑组合,它通过称为ClusterIP的地址和服务端口接收客户端请求,并将这些请求代理至使用标签选择器来过滤一个符合条件的Pod对象。

kubectl create service命令可创建Service对象以将应用程序“暴露”于网络中,它使用的标签选择器为app=SVC_NAME。例如,下面的命令使用app=demoapp为标签选择器创建了Service/demoapp资源对象。


~$ kubectl create service nodeport demoapp --tcp=80
service/demoapp created

在上面的命令中,nodeport是指Service对象的类型,它会在集群中各节点上随机选择一个节点端口(hostPort)为该Service对象接入集群外部的访问流量,集群内部流量则由Service资源通过ClusterIP直接接入。命令选项--tcp=<port>[:<targetPort>]用于指定Servcie端口及容器上要暴露的端口,省略容器端口时表示与Service端口相同。创建完成后,default名称空间中的对象及其通信示意图如图2-13所示。

图2-13 Service对象在Pod对象前端添加了一个固定访问层

我们仍然可以在集群节点上或者通过任意一个Pod对象来模拟集群内部客户端对Service/demoapp的访问,为了便于测试,Kubernetes为各Service对象自动生成DNS记录及相应的名称解析功能。下面,在同一集群中的Pod对象中通过客户端程序发起访问测试来模拟图2-12中源自Pod对象Client的访问请求。

首先,使用kubectl run命令创建一个自主式Pod对象并直接接入其交互式接口,如下面命令的-it组合选项即用于交互式打开并保持其shell命令行接口。


~$ kubectl run client-pod --image="ikubernetes/admin-toolbox:v1.0" --rm -it --command -- /bin/sh
[root@client-pod /]#

接下来,在client-pod中测试Service对象的名称解析功能,如下面命令中的demoapp是指Service对象名称,而default是该Service所属的名称空间,svc代表Service资源,而cluster.local是集群域名。其中的svc.cluster.local通常可以省略,如果客户端与目标Service对象在同一名称空间中,则名称空间也可省略。


[root@client-pod /]# nslookup -query=A demoapp.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   demoapp.default.svc.cluster.local
Address: 10.102.6.223

随后,可在client-pod通过crul命令对此前创建的Service对象的名称发起访问请求,下面的命令使用了短格式的服务名称。


[root@client-pod /]# curl http://demoapp.default
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.2, ServerName: demoapp-…, ServerIP: 10.244.1.5!

类似于列出Deployment控制器及Pod资源对象的方式,kubectl get services命令能够列出Service对象的相关信息,例如下面的命令显示了Service对象demoapp的简要状态信息。


~$ kubectl get service/demoapp 
NAME      TYPE       CLUSTER-IP        EXTERNAL-IP      PORT(S)     AGE
demoapp   NodePort    10.102.6.223     <none>        80:32687/TCP   58s

其中,PORT(s)字段中表明,集群中各工作节点会捕获发往本地的目标端口为32687的流量,并将其代理至当前Service对象的80端口,于是,集群外部的用户可以使用当前集群中任一节点的此端口来请求Service对象上的服务。CLUSTER-IP字段为当前Service的IP地址,它是一个虚拟IP,并没有配置在集群中任何主机的任何接口之上,但每个Node上的kube-proxy都会为CLUSTER-IP所在的网络创建用于转发的iptables或ipvs规则。此时,用户可于集群外部任一浏览器请求Kubernetes集群任意一个节点的相关端口来进行访问测试。

2.4.3 扩容与缩容

前面示例中创建的Deployment对象demoapp仅创建了一个Pod对象,其所能够承载的访问请求数量受限于这个Pod对象的服务能力。请求流量上升到接近或超出其承载上限之前,用户可以通过Kubernetes的应用“扩容”(scaling up)机制来增加Pod副本数量,从而提升其服务容量。相应地,“缩容”是指缩减Pod副本数量,只不过这通常缘于与扩容操作相反的原因。

kubectl scale命令就是专用于变动控制器应用规模的命令,它支持对Deployment、ReplicaSet、StatefulSet等类型资源对象的扩容和缩容操作。例如,如果要将Deployment/demoapp中的Pod副本数量扩展为3个,可以使用如下命令完成。


~$ kubectl scale deployment/demoapp --replicas=3
deployment.apps/demoapp scaled

而后列出由demoapp创建的Pod副本,便可确认其扩展操作的完成状态。如下命令显示出其Pod副本数量已经扩增至3个,其中包括此前的demoapp-6c5d545684-4t9kr。


~$ kubectl get pods -l app=demoapp
NAME                      READY    STATUS    RESTARTS   AGE
demoapp-6c5d545684-4t9kr   1/1     Running   0          115m
demoapp-6c5d545684-6tth5   1/1     Running   0          32s
demoapp-6c5d545684-z4tgh   1/1     Running   0          32s

Service对象内置的负载均衡机制可在其后端副本数量不止一个时自动进行流量分发,它还会自动监控关联到的Pod的健康状态,以确保仅将请求流量分发至可用的后端Pod对象。因此,Deployment对象demoapp规模扩展完成后,default名称空间中的资源对象及其关联关系就变成了如图2-14所示的情形。

图2-14 Deployment对象规模扩增完成

而后由kubectl describe deployment命令打印Deployment对象demoapp的详细信息,了解其应用规模的变动及当前Pod副本的状态等相关信息。由命令结果可以看出,其Pod副本数量的各项指标都已经转换为新的目标数量,而其事件信息中也有相应事件显示其扩增操作已成功完成。


~$ kubectl describe deployment/demoapp 
Name:                   demoapp
……
Replicas:          3 desired | 3 updated | 3 total | 3 available | 0 unavailable
……
NewReplicaSet:   demoapp-6c5d545684 (3/3 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  2m16s  deployment-controller  Scaled up replica set   demoapp-6c5d545684 to 3

Service对象demoapp的标签选择器动态纳入的新Pod副本也将成为该Service对象可用的代理后端,所有流量会被调度至其后端的所有Pod对象之上,如图2-13所示。每个能够接收流量的后端称为一个端点,它通常表现为相应主机或容器上可接收特定流量的访问入口(套接字),如下面命令结果中的Endpoints字段所示。


~$ kubectl describe service/demoapp
Name:                     demoapp
……
IP:                       10.102.6.223
Port:                     80  80/TCP
TargetPort:               80/TCP
NodePort:                 80  32687/TCP
Endpoints:                10.244.1.5:80,10.244.2.5:80,10.244.3.3:80
……

我们可以通过任何客户端对Service/demoapp的服务发起访问请求进行测试,这次我们在集群外的主机172.29.0.1上通过NodePort对该服务发起持续访问,以测试Service对象的流量调度机制是否能够正常工作。由命令的响应结果显示,Service/demoapp已然将请求调度至3个不同的后端Pod之上。


~$ while true; do curl http://172.29.9.12:32687/; sleep 0.2; done
……, ServerName: demoapp-6c5d545684-z4tgh, ServerIP: 10.244.3.3!
……, ServerName: demoapp-6c5d545684-6tth5, ServerIP: 10.244.2.5!
……, ServerName: demoapp-6c5d545684-4t9kr, ServerIP: 10.244.1.5!

应用规模缩容的方式和扩容相似,只不过是将Pod副本的数量调至比原来小的数字。例如,将demoapp的Pod副本缩减至2个,可以使用类似如下命令进行。


~$ kubectl scale deployment/demoapp --replicas=2
deployment.apps/demoapp scaled

缩容完成后,Service/demoapp的后端端点数量也会随之动态变动。至此,功能基本完整的容器化应用在Kubernetes上已经部署完成,即便是部署一个略复杂的分层应用,也只需要通过合适的镜像以类似的方式就能完成。

2.4.4 修改与删除对象

成功创建于Kubernetes之上的资源对象也称为活动对象(live object),其配置规范(live object configuration)由API Server保存在集群状态存储系统etcd中。kubectl edit命令可调用默认编辑器对活动对象的可配置属性进行编辑。例如,若需要将此前创建的Service/demoapp对象的类型修改为ClusterIP,可以使用kubectl edit service demoapp命令打开编辑界面后,通过将type属性的值修改为ClusterIP使其实时生效。

提示

不同资源类型的资源规范相差很大,而且未必所有字段都支持运行时修改。

有些命令是kubectl edit命令某一部分功能的二次封装,例如kubectl scale命令不过是专用于修改资源对象的replicas属性值而已,而kubectl set image通常用于修改工作负载型控制器资源规范中Pod模板的容器镜像,这两个命令同样直接作用于活动对象。

不再有价值的活动对象可使用kubectl delete命令予以删除。例如,下面的命令能够删除service/demoapp资源对象:


~$ kubectl delete service/demoapp
service "demoapp" deleted

有时候需要清空某一类型下的所有对象,此时只需要将上面命令对象名称换成--all选项便能实现。例如,删除dafault名称空间中所有的Deployment控制器:


~$ kubectl delete deployment --all
deployment.apps "demoapp" deleted

需要注意的是,受控于控制器的Pod对象在删除后会被重建,因而删除此类对象需要直接删除其控制器对象。默认情况下,删除Deployment一类的工作负载型控制器资源会级联删除相关的所有Pod对象,若要禁用该功能,需要在删除命令中使用--cascade=false选项。

显然,直接命令式管理资源对象存在较大的局限性,它们在设置资源对象属性方面提供的配置能力相当有限,而且有不少资源并不支持通过命令操作进行创建。例如,用户无法创建带有多个容器的Pod对象,也无法为Pod对象创建存储卷。因此,资源对象的更有效管理方式是使用保存有对象配置信息的配置清单。