值得收藏!Kubernetes 常见运维技巧总结

注:节选自《Kubernetes权威指南》,主要对经常使用的Kubernetes系统运维操做和技巧进行详细说明。

一、Node的隔离和恢复node


apiVersion: v1
kind: Node
metadata:  
  name: kubernetes-minion1  
  labels:    
   kubernetes.io/hostname: kubernetes-minion1
spec:  
   unschedulable: true

而后,经过kubectl replace命令完成对Node状态的修改:redis

$ kubectl replace -f unschedule_node.yaml 
nodes/kubernetes-minion1

查看Node的状态,能够观察到在Node的状态中增长了一项SchedulingDisabled:算法

$ kubectl get nodes
NAME                 LABELS                                      STATUS
kubernetes-minion1   kubernetes.io/hostname=kubernetes-minion1   Ready, SchedulingDisabled

对于后续建立的Pod,系统将不会再向该Node进行调度。数据库

另外一种方法是不使用配置文件,直接使用kubectl patch命令完成:api

$ kubectl patch node kubernetes-minion1 -p '{"spec":{"unschedulable":true}}'

须要注意的是,将某个Node脱离调度范围时,在其上运行的Pod并不会自动中止,管理员须要手动中止在该Node上运行的Pod。 服务器

一样,若是须要将某个Node从新归入集群调度范围,则将unschedulable设置为false,再次执行kubectl replace或kubectl patch命令就能恢复系统对该Node的调度。网络

二、Node的扩容

在实际生产系统中会常常遇到服务器容量不足的状况,这时就须要购买新的服务器,而后将应用系统进行水平扩展来完成对系统的扩容。架构

在Kubernetes集群中,对于一个新Node的加入是很是简单的。能够在Node节点上安装Docker、Kubelet和kube-proxy服务,而后将Kubelet和kube-proxy的启动参数中的Master URL指定为当前Kubernetes集群Master的地址,最后启动这些服务。基于Kubelet的自动注册机制,新的Node将会自动加入现有的Kubernetes集群中,如图1所示。 运维


图1 新节点自动注册完成扩容ide

Kubernetes Master在接受了新Node的注册以后,会自动将其归入当前集群的调度范围内,在以后建立容器时,就能够向新的Node进行调度了。

经过这种机制,Kubernetes实现了集群的扩容。

三、Pod动态扩容和缩放

在实际生产系统中,咱们常常会遇到某个服务须要扩容的场景,也可能会遇到因为资源紧张或者工做负载下降而须要减小服务实例数的场景。此时咱们能够利用命令kubectl scale rc来完成这些任务。以redis-slave RC为例,已定义的最初副本数量为2,经过执行下面的命令将redis-slave RC控制的Pod副本数量从初始的2更新为3:

$ kubectl scale rc redis-slave --replicas=3
scaled

执行kubectl get pods命令来验证Pod的副本数量增长到3:

$ kubectl get pods
NAME                READY     STATUS    RESTARTS AGE
redis-slave-4na2n    1/1     Running      0      1h
redis-slave-92u3k    1/1       Running    0      1h
redis-slave-palab    1/1       Running    0      2m

将--replicas设置为比当前Pod副本数量更小的数字,系统将会“杀掉”一些运行中的Pod,便可实现应用集群缩容:

$ kubectl scale rc redis-slave --replicas=1
scaled

$ kubectl get pods
NAME               READY         STATUS    RESTARTS   AGE
redis-slave-4na2n    1/1           Running   0          1h

四、更新资源对象的Label

Label(标签)做为用户可灵活定义的对象属性,在已建立的对象上,仍然能够随时经过kubectl label命令对其进行增长、修改、删除等操做。

例如,咱们要给已建立的Pod“redis-master-bobr0”添加一个标签role=backend:

$ kubectl label pod redis-master-bobr0 role=backend

查看该Pod的Label:

$ kubectl get pods -Lrole
NAME               READY     STATUS    RESTARTS   AGE  ROLE
redis-master-bobr0   1/1       Running   0       3m        backend

删除一个Label,只需在命令行最后指定Label的key名并与一个减号相连便可:

$ kubectl label pod redis-master-bobr0 role-

修改一个Label的值,须要加上--overwrite参数:

$ kubectl label pod redis-master-bobr0 role=master --overwrite

五、将Pod调度到指定的Node

咱们知道,Kubernetes的Scheduler服务(kube-scheduler进程)负责实现Pod的调度,整个调度过程经过执行一系列复杂的算法最终为每一个Pod计算出一个最佳的目标节点,这一过程是自动完成的,咱们没法知道Pod最终会被调度到哪一个节点上。有时咱们可能须要将Pod调度到一个指定的Node上,此时,咱们能够经过Node的标签(Label)和Pod的nodeSelector属性相匹配,来达到上述目的。

首先,咱们能够经过kubectl label命令给目标Node打上一个特定的标签,下面是此命令的完整用法:

kubectl label nodes <node-name> <label-key>=<label-value>

这里,咱们为kubernetes-minion1节点打上一个zone=north的标签,代表它是“北方”的一个节点:

$ kubectl label nodes kubernetes-minion1 zone=north
NAME                 LABELS                                                 STATUS
kubernetes-minion1   kubernetes.io/hostname=kubernetes-minion1,zone=north   Ready

上述命令行操做也能够经过修改资源定义文件的方式,并执行kubectl replace -f xxx.yaml命令来完成。

而后,在Pod的配置文件中加入nodeSelector定义,以redis-master-controller.yaml为例:

apiVersion: v1
kind: ReplicationController
metadata:  
name: redis-master  
labels:    
   name: redis-master
spec:  
   replicas: 1  
   selector:    
     name: redis-master  
     template:    
       metadata:      
       labels:        
       name: redis-master    
  spec:      
     containers:      
        - name: master        
     image: kubeguide/redis-master        
     ports:        
       - containerPort: 6379      
       nodeSelector:        
          zone: north

运行kubectl create -f命令建立Pod,scheduler就会将该Pod调度到拥有zone=north标签的Node上去。

使用kubectl get pods -o wide命令能够验证Pod所在的Node:

# kubectl get pods -o wide
NAME                 READY     STATUS    RESTARTS   AGE       NODE
redis-master-f0rqj   1/1       Running   0          19s       kubernetes-minion1

若是咱们给多个Node都定义了相同的标签(例如zone=north),则scheduler将会根据调度算法从这组Node中挑选一个可用的Node进行Pod调度。

这种基于Node标签的调度方式灵活性很高,好比咱们能够把一组Node分别贴上“开发环境”“测试验证环境”“用户验收环境”这三组标签中的一种,此时一个Kubernetes集群就承载了3个环境,这将大大提升开发效率。

须要注意的是,若是咱们指定了Pod的nodeSelector条件,且集群中不存在包含相应标签的Node时,即便还有其余可供调度的Node,这个Pod也最终会调度失败。

六、应用的滚动升级

当集群中的某个服务须要升级时,咱们须要中止目前与该服务相关的全部Pod,而后从新拉取镜像并启动。若是集群规模比较大,则这个工做就变成了一个挑战,并且先所有中止而后逐步升级的方式会致使较长时间的服务不可用。Kubernetes提供了rolling-update(滚动升级)功能来解决上述问题。

滚动升级经过执行kubectl rolling-update命令一键完成,该命令建立了一个新的RC,而后自动控制旧的RC中的Pod副本数量逐渐减小到0,同时新的RC中的Pod副本数量从0逐步增长到目标值,最终实现了Pod的升级。须要注意的是,系统要求新的RC须要与旧的RC在相同的命名空间(Namespace)内,即不能把别人的资产偷偷转移到自家名下。

以redis-master为例,假设当前运行的redis-master Pod是1.0版本,则如今须要升级到2.0版本。

建立redis-master-controller-v2.yaml的配置文件以下:

apiVersion: v1  
kind: ReplicationController  
metadata:  
  name: redis-master-v2  
  labels:  
   name: redis-master  
version: v2  
spec:  
  replicas: 1  
  selector:  
    name: redis-master  
    version: v2  
template:  
  metadata:  
  labels:  
    name: redis-master  
    version: v2  
spec:  
containers:  
- name: master  
  image: kubeguide/redis-master:2.0  
ports:  
- containerPort: 6379

在配置文件中有几处须要注意:
(1)RC的名字(name)不能与旧的RC的名字相同;
(2)在selector中应至少有一个Label与旧的RC的Label不一样,以标识其为新的RC。

本例中新增了一个名为version的Label,以与旧的RC进行区分。

运行kubectl rolling-update命令完成Pod的滚动升级:

kubectl rolling-update redis-master -f redis-master-controller-v2.yaml

Kubectl的执行过程以下:

Creating redis-master-v2  
At beginning of loop: redis-master replicas: 2, redis-master-v2 replicas: 1  
Updating redis-master replicas: 2, redis-master-v2 replicas: 1  
At end of loop: redis-master replicas: 2, redis-master-v2 replicas: 1  
At beginning of loop: redis-master replicas: 1, redis-master-v2 replicas: 2  
Updating redis-master replicas: 1, redis-master-v2 replicas: 2  
At end of loop: redis-master replicas: 1, redis-master-v2 replicas: 2  
At beginning of loop: redis-master replicas: 0, redis-master-v2 replicas: 3  
Updating redis-master replicas: 0, redis-master-v2 replicas: 3  
At end of loop: redis-master replicas: 0, redis-master-v2 replicas: 3  
Update succeeded. Deleting redis-master  
redis-master-v2

等全部新的Pod启动完成后,旧的Pod也被所有销毁,这样就完成了容器集群的更新。

另外一种方法是不使用配置文件,直接用kubectl rolling-update命令,加上--image参数指定新版镜像名称来完成Pod的滚动升级:

kubectl rolling-update redis-master --image=redis-master:2.0

与使用配置文件的方式不一样,执行的结果是旧的RC被删除,新的RC仍将使用旧的RC的名字。

Kubectl的执行过程以下:

Creating redis-master-ea866a5d2c08588c3375b86fb253db75  
At beginning of loop: redis-master replicas: 2, redis-master-ea866a5d2c08588c 3375b86fb253db75 replicas: 1  
Updating redis-master replicas: 2, redis-master-ea866a5d2c08588c3375b86fb253db 75 replicas: 1  
At end of loop: redis-master replicas: 2, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 1  
At beginning of loop: redis-master replicas: 1, redis-master-ea866a5d2c08588c 3375b86fb253db75 replicas: 2  
Updating redis-master replicas: 1, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 2  
At end of loop: redis-master replicas: 1, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 2  
At beginning of loop: redis-master replicas: 0, redis-master-ea866a5d2c08588c 3375b86fb253db75 replicas: 3  
Updating redis-master replicas: 0, redis-master-ea866a5d2c08588c3375b86fb253db 75 replicas: 3  
At end of loop: redis-master replicas: 0, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 3  
Update succeeded. Deleting old controller: redis-master  
Renaming redis-master-ea866a5d2c08588c3375b86fb253db75 to redis-master  
redis-master

能够看到,Kubectl经过新建一个新版本Pod,停掉一个旧版本Pod,逐步迭代来完成整个RC的更新。

更新完成后,查看RC:

$ kubectl get rc    
CONTROLLER     CONTAINER(S)   IMAGE(S)            SELECTOR        REPLICAS
redis-master   master         kubeguide/redis-master:2.0              deployment= ea866a5d2c08588c3375b86fb253db75,name=redis-master,version=v1   3

能够看到,Kubectl给RC增长了一个key为“deployment”的Label(这个key的名字可经过--deployment-label-key参数进行修改),Label的值是RC的内容进行Hash计算后的值,至关于签名,这样就能很方便地比较RC里的Image名字及其余信息是否发生了变化,它的具体做用能够参见第6章的源码分析。

若是在更新过程当中发现配置有误,则用户能够中断更新操做,并经过执行Kubectl rolling-update –rollback完成Pod版本的回滚:

$ kubectl rolling-update redis-master --image=kubeguide/redis-master:2.0 --rollback  
Found existing update in progress (redis-master-fefd9752aa5883ca4d53013a7b 583967), resuming.  
Found desired replicas.Continuing update with existing controller redis-master.  
At beginning of loop: redis-master-fefd9752aa5883ca4d53013a7b583967 replicas: 0, redis-master replicas: 3  
Updating redis-master-fefd9752aa5883ca4d53013a7b583967 replicas: 0, redis-master replicas: 3  
At end of loop: redis-master-fefd9752aa5883ca4d53013a7b583967 replicas: 0, redis-master replicas: 3  
Update succeeded. Deleting redis-master-fefd9752aa5883ca4d53013a7b583967  
redis-master

到此,能够看到Pod恢复到更新前的版本了。

七、Kubernetes集群高可用方案

Kubernetes做为容器应用的管理中心,经过对Pod的数量进行监控,而且根据主机或容器失效的状态将新的Pod调度到其余Node上,实现了应用层的高可用性。针对Kubernetes集群,高可用性还应包含如下两个层面的考虑:etcd数据存储的高可用性和Kubernetes Master组件的高可用性。

**7.1 etcd高可用性方案
**

etcd在整个Kubernetes集群中处于中心数据库的地位,为保证Kubernetes集群的高可用性,首先须要保证数据库不是单故障点。一方面,etcd须要以集群的方式进行部署,以实现etcd数据存储的冗余、备份与高可用性;另外一方面,etcd存储的数据自己也应考虑使用可靠的存储设备。

etcd集群的部署可使用静态配置,也能够经过etcd提供的REST API在运行时动态添加、修改或删除集群中的成员。本节将对etcd集群的静态配置进行说明。关于动态修改的操做方法请参考etcd官方文档的说明。

首先,规划一个至少3台服务器(节点)的etcd集群,在每台服务器上安装好etcd。

部署一个由3台服务器组成的etcd集群,其配置如表1所示,其集群部署实例如图2所示。
表1 etcd集群的配置

而后修改每台服务器上etcd的配置文件/etc/etcd/etcd.conf。

以etcd1为建立集群的实例,须要将其ETCD_INITIAL_CLUSTER_STATE设置为“new”。etcd1的完整配置以下:

# [member]  
ETCD_NAME=etcd1 #etcd实例名称  
ETCD_DATA_DIR="/var/lib/etcd/etcd1" #etcd数据保存目录  
ETCD_LISTEN_PEER_URLS="http://10.0.0.1:2380" #集群内部通讯使用的URL  
ETCD_LISTEN_CLIENT_URLS="http://10.0.0.1:2379" #供外部客户端使用的URL  
……  
#[cluster]  
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.1:2380" #广播给集群内其余成员使用的URL  
ETCD_INITIAL_CLUSTER="etcd1=http://10.0.0.1:2380,etcd2=http://10.0.0.2:2380, etcd3=http://10.0.0.3:2380" #初始集群成员列表  
ETCD_INITIAL_CLUSTER_STATE="new" #初始集群状态,new为新建集群  
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #集群名称  
ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.1:2379" #广播给外部客户端使用的URL

启动etcd1服务器上的etcd服务:

$ systemctl restart etcd

启动完成后,就建立了一个名为etcd-cluster的集群。

etcd2和etcd3为加入etcd-cluster集群的实例,须要将其ETCD_INITIAL_CLUSTER_STATE设置为“exist”。etcd2的完整配置以下(etcd3的配置略):

# [member]  
ETCD_NAME=etcd2 #etcd实例名称  
ETCD_DATA_DIR="/var/lib/etcd/etcd2" #etcd数据保存目录  
ETCD_LISTEN_PEER_URLS="http://10.0.0.2:2380" #集群内部通讯使用的URL  
ETCD_LISTEN_CLIENT_URLS="http://10.0.0.2:2379" #供外部客户端使用的URL  
……  
#[cluster]  
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.2:2380" #广播给集群内其余成员使用的URL  
ETCD_INITIAL_CLUSTER="etcd1=http://10.0.0.1:2380,etcd2=http://10.0.0.2:2380,etcd3=http://10.0.0.3:2380" #初始集群成员列表  
ETCD_INITIAL_CLUSTER_STATE="exist" # existing表示加入已存在的集群  
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #集群名称  
ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.2:2379" #广播给外部客户端使用的URL

启动etcd2和etcd3服务器上的etcd服务:

$ systemctl restart etcd

启动完成后,在任意etcd节点执行etcdctl cluster-health命令来查询集群的运行状态:

$ etcdctl cluster-health
cluster is healthy
member ce2a822cea30bfca is healthy
member acda82ba1cf790fc is healthy
member eba209cd0012cd2 is healthy

在任意etcd节点上执行etcdctl member list命令来查询集群的成员列表:

$ etcdctl member list
ce2a822cea30bfca: name=default peerURLs=http://10.0.0.1:2380,http://10.0.0.1: 7001 clientURLs=http://10.0.0.1:2379,http://10.0.0.1:4001
acda82ba1cf790fc: name=default peerURLs=http://10.0.0.2:2380,http://10.0.0.2: 7001 clientURLs=http://10.0.0.2:2379,http://10.0.0.2:4001
eba209cd40012cd2: name=default peerURLs=http://10.0.0.3:2380,http://10.0.0.3: 7001 clientURLs=http://10.0.0.3:2379,http://10.0.0.3:4001

至此,一个etcd集群就建立成功了。

以kube-apiserver为例,将访问etcd集群的参数设置为:

--etcd-servers=http://10.0.0.1:4001,http://10.0.0.2:4001,http://10.0.0.3:4001

在etcd集群成功启动以后,若是须要对集群成员进行修改,则请参考官方文档的详细说明:点击此处6

对于etcd中须要保存的数据的可靠性,能够考虑使用RAID磁盘阵列、高性能存储设备、NFS网络文件系统,或者使用云服务商提供的网盘系统等来实现。

7.2 Kubernetes Master组件的高可用性方案

在Kubernetes体系中,Master服务扮演着总控中心的角色,主要的三个服务kube-apiserver、kube-controller-mansger和kube-scheduler经过不断与工做节点上的Kubelet和kube-proxy进行通讯来维护整个集群的健康工做状态。若是Master的服务没法访问到某个Node,则会将该Node标记为不可用,再也不向其调度新建的Pod。但对Master自身则须要进行额外的监控,使Master不成为集群的单故障点,因此对Master服务也须要进行高可用方式的部署。

以Master的kube-apiserver、kube-controller-mansger和kube-scheduler三个服务做为一个部署单元,相似于etcd集群的典型部署配置。使用至少三台服务器安装Master服务,而且使用Active-Standby-Standby模式,保证任什么时候候总有一套Master可以正常工做。

全部工做节点上的Kubelet和kube-proxy服务则须要访问Master集群的统一访问入口地址,例如可使用pacemaker等工具来实现。图3展现了一种典型的部署方式。

图3 Kubernetes Master高可用部署架构

相关文章
相关标签/搜索