本文参考相关书籍和相关博客对kubernetes中的一些重要的组件和使用方法进行简单介绍,并对一些注意事项进行说明,最后会整理一些kubernetes系统的一些运维小技巧。
APIServer 负责对外提供kubernetes API服务,它运行在master节点上,任何对资源的增删改查的操作都要经过APIServer的处理后才能提交给etcd,也就是说APIServer负责直接和etcd交互(其他的组件都不会直接操作etcd)。APIServer大致主要负责以下几个方面的工作:
①对外提供基于RESTful的管理接口,支持对kubernetes的资源对象如:pod、service、node等进行增删改查和监听操作。例如:Get
表示查询默认namespace中所有的pod信息。Get
表示监听默认namespace中所有pod的状态变化信息,返回pod的创建、更新和删除事件。(这样的get请求可以保持TCP长连接,持续监听pod的变化事件)。
②配置kubernetes的资源对象,并将这些对象的期望状态和当前的实际状态存储在etcd中供其他组件读取、分析。
③提供可定制的功能性插件(用户可以自定义),完善对集群的管理。例如,调用内部或外部的用户认证与授权机制保证集群的安全。
④系统日志收集功能,暴露在/logs API中。
APIServer将集群中的资源都存储在etcd中,默认路径都由/register开始,可以传入etcd-prefix参数来修改该值。当用户向APIServer发起请求后,APIServer会借助一个register实体完成对etcd的所有操作(所以etcd资源的存储路径都是以register开始的)。
另外APIServer在响应多个请求的时候是如何避免竞争和冲突的问题呢?
是这样的,kubernetes的资源对象都有一个resourceVersion作为其元数据的一部分,这个resourceVersion是用来标识资源对象内部版本的字符串,每次资源对象的更新,APIServer都会修改此字符串的值,然后客户端就可以通过它来判断该对象是否被修改过(注意,这个resourceVersion版本仅对当前资源对象和namespace限定域内有效)。
调度器scheduler的作用是根据特定的调度算法将pod调度到指定的工作节点上。为了避免轮询APIServer获取需要的数据,scheduler为这些需要的资源和数据(如已调度、待调度的pod信息,node状态列表等等)设置了本地缓存机制,分为两类:简单的cache对象,用来缓存无序数据,如当前可用的工作节点;另一种是先进先出的队列,用来缓存有序数据,如下一个到达的pod。在没有通过Pod的nodeSelector和node的label标签进行匹配的时候,将根据调度策略随机选择一个最合适的node节点。
kubernetes中的调度策略分为两个阶段:Predicates和Priorities,其中前者决定能否将pod调度到某个工作节点上,后者在前者的决定为是的基础上通过为候选节点设置优先级来选出最适合的节点。所以只有通过几个Predicates条件的检验后才有被评优先级的资格,检验条件如:检查宿主机上的端口是否冲突;node上的资源是否够用;容器挂载的卷(volume)是否有冲突;node能否被pod的NodeSelector选中等。另外如果待调度的pod指定了pod.Spec.Host的值为hostname,则将他调度到主机名为指定hostname的工作节点上运行。通过一系列的筛选,通过后工作节点就可以被打分来评优先级了,如尽量将pod调度到资源占用比较小的工作节点上;cpu和内存利用率尽量相近;节点上属于同一个service的后端pod尽量少;主机上的镜像大小等等。
Controller Manager 是集群内部的管理控制中心,用来管理node节点,pod副本,命名空间等。每个Controller通过API Server提供的接口实时监控整个集群的每个资源对象的当前状态,当发生故障导致系统状态发生变化时,会尝试将系统状态修复到“期望状态”。下面对副本管理控制器、节点控制器、命名空间控制器做一些介绍:
1.副本管理控制器(Replication Controller)
Replication Controller 是副本控制器,简称RC,它能够保证集群中RC所关联的pod副本数始终保持预设值,可以通过调整RC中的spec.replicas属性值来实现系统的扩容或缩容,还可以改变RC中的pod模板来实现滚动升级。
主要注意的是:
①只有当Pod的重启策略是Always的时候(RestartPolicy=Always),副本控制器才会管理该Pod的操作(创建、销毁、重启等)。
②Pod可以通过修改label来脱离RC的管控。
③删除一个RC不会影响它所创建的Pod,但是如果要删除Pod需要将RC的副本数属性设置为0。
下面说一下如何弹性伸缩和滚动升级:
假设名为XXX的RC,其pod副本数量初始为2,现在将其更新为3:
$ kubectl scale rc XXX --replicas=3
然后可以通过以下命令来查看pod副本目前的数量
$ kubectl get pods
滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建了一个新的RC,然后自动控制旧的RC中的Pod副本数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,最终实现了Pod的升级。
假设XXXpod是1.0版本,现在将其升级为2.0版本:
创建XXX-v2.yaml文件如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: XXX-v2
labels:
name: XXX
version: v2
spec:
replicas: 1
selector:
name: XXX
version: v2
template:
metadata:
labels:
name: XXX
version: v2
spec:
containers:
- name: master
image: kubeguide/XXX:2.0
ports:
- containerPort: 6666
然后运行以下命令完成升级:
kubectl rolling-update XXX -f XXX-v2.yaml
使用配置文件时需要注意:
①RC的name不能与旧的相同
②在selector中至少有一个Label与旧的RC的Label不同,来标识其为新的RC。
还可以不实用配置文件,直接通过命令并加上–image参数指定新版镜像名称来完成Pod的滚动升级:
kubectl rolling-update XXX --image=XXX:2.0
如果在更新过程中发现配置或其他地方有误,可以中断更新操作,并通过执行Kubectl rolling-update –rollback完成Pod版本的回滚:
$ kubectl rolling-update XXX --image=kubeguide/XXX:2.0 --rollback
2.节点控制器(Node Controller)
node controller 主要用来检查kubernetes的工作节点是否可用,他会定期检查所有在运行的工作节点上的kubelet进程来获取节点信息,如果kubelet在规定时间里没有推送该工作节点的状态,则将其NodeCondition为Ready的状态置为UnKnown并写入etcd中。工作节点的动态维护过程是依靠node controller来完成的,controller manager中一直运行着一个循环,负责集群内各个工作节点的同步及健康检查,在每个循环周期内,node controller不断地检测当前kubernetes的每台工作节点是否正常工作,如果一个之前已经失败的工作节点在这个检测中变成了“可以工作”的状态,那么node controller就要把这个机器添加为工作节点的一员;反之,node controller会把已有的工作节点删除。被删除的只是etcd中的一个对象,因为与pod和service不同的是工作节点并不是由kubernetes创建的,要么是由IaaS平台,要么就是用户管理的物理机或者虚拟机,所以当kubernetes创建了一个node时只是创建了一个node节点的“描述”。
3.Namespace Controller
用户通过API Server可以创建新的Namespace并保存在etcd中,Namespace Controller定时通过API Server读取这些Namespace信息。如果Namespace被API标记为优雅删除(即设置删除期限,DeletionTimestamp),则将该Namespace状态设置为“Terminating”,并保存到etcd中。同时Namespace Controller删除该Namespace下的ServiceAccount、RC、Pod等资源对象。
在kubernetes集群中,每一个node节点上都会起一个kubelet进程,来处理master节点下发的任务并管理pod和其上的容器。
节点管理:
节点可以将kubelet的启动参数“–register-node”的值设置为true,这样kubelet将向API Server注册自己,Kubelet在启动时通过API Server注册节点信息,并定时向API Server发送节点新消息,API Server在接收到这些信息后,将这些信息写入etcd。通过Kubelet的启动参数“–node-status-update-frequency”设置Kubelet每隔多少时间向API Server报告节点状态,默认为10秒。
默认配置文件在/etc/kubernetes/kubelet中,其中
–api-servers:用来配置Master节点的IP和端口。
–kubeconfig:用来配置kubeconfig的路径,kubeconfig文件常用来指定证书。
–hostname-override:用来配置该节点在集群中显示的主机名。
–node-status-update-frequency:配置kubelet向Master心跳上报的频率,默认为10s。
pod管理:
kubelet可以通过API Server来监听etcd目录,并将获取的信息同步到本地缓存中,具体流程:
1.为该Pod创建一个数据目录。
2.从API Server读取该Pod清单。
3.为该Pod挂载外部卷(External Volume)
4.下载Pod用到的Secret。
5.检查运行的Pod,执行Pod中未完成的任务。
6.先创建一个Pause容器,该容器接管Pod的网络,再创建其他容器。
7.Pod中容器的处理流程:
1)比较容器hash值并做相应处理。
2)如果容器被终止了且没有指定重启策略,则不做任何处理。
3)调用Docker Client下载容器镜像,调用Docker Client运行容器。
cAdvisor资源监控:
cAdvisor(Container Advisor)是一个开源的分析容器资源使用率和性能特性的代理工具。它是因为容器而产生的,因此自然支持Docker容器。在Kubernetes项目中,cAdvisor被集成到Kubernetes代码中。cAdvisor自动查找所有在其所在节点上的容器,自动采集CPU、内存、文件系统和网络使用的统计信息。cAdvisor通过它所在节点机的Root容器,采集并分析该节点机的全面的使用情况。运行在宿主机上的cAdvisor通过暴露一个TCP端口对外提供一台REST API,客户端可以发起HTTP请求,如:http://
。
获取容器信息:
获取容器信息的URL形如:/api/{api version}/containers/
。绝对容器名(absolute container name)/下包含整个宿主机上所有容器(包含Docker容器)的资源信息,所以如果要获取特定Docker容器的资源信息,绝对容器名字段需要填入/docker/{container ID}。例如:
client, err := client.NewClient("http://127.0.0.1:4194/")
request :=info.ContainerInfoRequest{NumStats:10}
sInfo, err := client,ContainerInfo("/docker/d1g5eb8q179e6...", $request)
其中NumStats字段指定cAdvisor对容器资源信息的取样次数。
获取宿主机信息:
可以访问/api/{api version}/machine
来获取宿主机的资源信息。如
client, err := client.NewClient("http://127.0.0.1:4194/")client.MachineInfo()
另外kubelet还负责着Docker容器与镜像的垃圾回收。
Node的隔离
在特定的情况下,有需要将node隔离起来,脱离kubernetes的调度范围,在需要的时候再重新将其纳入调度范围。首先可以通过yaml文件的方式,在spec部分指定unschedulable为true,如下:
apiVersion: v1
kind: Node
metadata:
name: kubernetes-minion1
labels:
kubernetes.io/hostname: kubernetes-minion1
spec:
unschedulable: true
然后,再通过kubectl replace命令完成对Node状态的修改:
$ kubectl replace -f XXX.yaml
nodes/kubernetes-minion1
除此之外还可以直接使用kubectl patch命令完成这个操作,如下:
$ kubectl patch node kubernetes-minion1 -p '{"spec":{"unschedulable":true}}'
这样一来,在后续的调度中系统将不会把pod调度到此node节点上。在需要将此node重新纳入调度范围的时候,再次将unschedulable设置为false即可。
Node的扩容
在kubernetes集群中对node扩容是很简单的,首先在node节点上安装Docker、Kubelet和kube-proxy服务,然后将Kubelet和kube-proxy的启动参数中的Master URL指定为当前Kubernetes集群Master的地址,然后启动这些服务,前文也提到过:kubelet会向Api Server注册自己,这样一来新的node就会加入到现有的kubernetes集群中了。
更新资源对象的Label
通过kubectl label命令可以对对象上的label进行操作:
如,为pod A添加一个标签 region=north:
$ kubectl label pod A region=north
修改label的值,加上–overwrite参数即可:
$ kubectl label pod A region=south --overwrite
删除label,只需在命令行最后指定label的key名并与一个减号相连即可:
$ kubectl label pod A region-
将Pod调度到指定Node节点
上文在讲scheduler调度的时候已经说了,调度算法将随机的选择一个合适的node节点进行pod的调度,但是如果需要将pod调度到指定的node节点上,可以通过Node的Label标签和Pod的nodeSelector属性相匹配来实现。
首先通过kubectl label命令为目标node加上特定的标签,如为name为mynode的node节点加上region=north的标签:
$ kubectl label nodes mynode region=north
然后,在Pod的配置文件中加入nodeSelector定义:
apiVersion: v1
kind: ReplicationController
metadata:
name: XXX
labels:
name: XXX
spec:
replicas: 1
selector:
name: XXX
template:
metadata:
labels:
name: XXX
spec:
containers:
- name: XXX
image: XXX
ports:
- containerPort: XXX
nodeSelector:
region: north
运行kubectl create -f命令创建Pod,scheduler就会将该Pod调度到拥有region=north标签的Node上去。
我们可以通过kubectl get pods -o wide命令来验证pod是否在具体的node上面。
注:
如果给多个Node都定义了相同的标签,则scheduler将会根据调度算法从这组Node中挑选一个可用的Node进行Pod调度。 如果我们指定了Pod的nodeSelector条件,但集群中不存在包含相应标签的Node时,即使还有其他可供调度的Node,这个Pod也会调度失败。