k8s集群主要由master node和worker node两部分和五大组件(apiserver、schduler、controller、kubelet、kube proxy)组成。
K8S中的MasterNode是一个抽象的概念。MasterNode包含3个程序,分别是:
和MasterNode类似,WorkerNode本质也并不是一个独立的应用程序,它包含两个组件:
由于WorkNode也是抽象的概念,那么也可以在MasterNode上启动kube-proxy和kubelet进程,从而使MasterNode也拥有WorkNode的能力,双重角色。但生产环境不推荐这样搞。
APIServer有完备的集群安全验证机制,提供了对K8S中如Pod、Service等资源CRUD等HttpRest接口,是集群中各个组件之间数据交互的核心枢纽,其主要功能如下。
上图是通过kubectl命令执行创建kubectl apply -f deployment.yaml
创建pod时,经历的流程如上图,大概流程为:
1)apiserver接收kubectl的创建资源的请求
2)apiserver将创建请求写入ECTD
3)apiserver接收到etcd的回调事件
4)apiserver将回调事件发送给ControllerManager
5)controllerManager中的ReplicationController处理本次请求,创建RS,然后它会调控RS中的Pod的副本数量处于期望值,比期望值小就新创建Pod,于是它告诉ApiServer要创建Pod
6)apiserver将创建pod的请求写入etcd集群
7)apiserver接收etcd的创建pod的回调事件
8)apiserver将创建pod的回调事件发送给scheduler,由它为pod挑选一个合适的宿主node
9)scheduler告诉apiserver,这个pod可以调度到哪个node上
10)apiserver将scheduler告诉他的事件写入etcd
11)apiserver接收到etcd的回调,将更新pod的事件发送给对应node上的kubelet进程
12)kubelet通过CRI接口同容器运行时(Docker)交互,维护更新对应的容器。
ApiServer采用https+ca签名证书强制双向认证,即访问ApiServer的接口需要持有对应的证书才可。
外部程序不能直接后端pod,因为后端pod的ip是动态的,是经常变得。那么apiserver就提供了统一的访问入口提供给外部程序。
K8S中的Service占用单独的网段(启用ipvs的前提下),可通过ip addr命令查看。
当安装完K8S集群后,在default命名空间中会有个默认的叫kubernetes的Service,这个service使用的service网段的第一个地址,而且这个service是ApiServer的service,换句话说,通过这个service可以访问到apiserver 。
可以通过如下命令访问到本地的apiserver
curl --cert /etc/kubernetes/pki/admin.pem \
--key /etc/kubernetes/pki/admin-key.pem \
--cacert /etc/kubernetes/pki/ca.pem https://127..0.1:6443/api/v1/
将回环地址换成名为kubernetes的service的cluster-ip也能访问到api-server。
curl --cert /etc/kubernetes/pki/admin.pem --key /etc/kubernetes/pki/admin-key.pem --cacert /etc/kubernetes/pki/ca.pem https://192.168.0.1:6443/api/v1/
ApiServer提供了一种特殊的Restful接口,它本身不处理这些请求而将请求转发给对应的Kubelet处理。如:
1)关于Node相关的接口。
# 查询指定节点上所有的pod信息
/api/v1/nodes/{nodename}/proxy/pods
# 查询指定节点上物理资源的统计信息
/api/v1/nodes/{nodename}/proxy/stats
# 查询指定节点上的摘要信息
/api/v1/nodes/{nodename}/proxy/spec
如需更多接口请查询官网。
2)关于Pod的接口
# 访问pod
/api/v1/namespace/{namespace-name}/pods/{name}/proxy
# 访问pod指定路径
/api/v1/namespace/{namespace-name}/pods/{name}/{path:*}
Controller是K8S自动化管理各类资源的核心组件,所谓的自动化管理,其实就是实时监控集群中各类资源的状态的变化,不断的尝试将各类资源的副本数调整为期望值。
Controller Manager细分为8种Controller,分别如下:
1)确保任何时候ReplicaSet管理的Pod副本数量都为期望值
2)实际值高于期望值告诉apisever应该关闭多余的pod,反之亦然
3)只有当Pod的重启策略为Always,Replication Controller才会管理
4)Replication Controller通过标签管理Pod,若pod标签被改,pod将脱离管控
5)提供重新调度、弹性扩/缩容、滚动更新能力
通过kubectl工具请求K8S的ApiServer获取集群中的Node信息,如下:
可以看到集群中所有的节点以及节点状态都是Ready。
那么apiserver是如何知道集群中有哪些节点的呢?答案是kubelet上报给apiserver,再经由watch机制转发Node Controller,由Node Controller统一实现对集群中Node的管理。若节点长时间异常,NodeControler会删除该节点,节点上的资源重新调度到正常的节点中。
ResourceQuota Controller的功能是确保:指定资源对象数量在任何时候都不要超过指定的最大数量,进而保证由于业务设计的缺陷导致整个系统的紊乱甚至宕机。
使用示例:将ResourceQuota创建到哪个namespace中,便对哪个namespace生效。
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
# ==对象数量配额==
# 最多创建的cm数
configmaps: "10"
# 最多创建的pvc数
persistentvolumeclaims: "4"
# 最多启动的pod数
pods: "4"
# 在该命名空间中允许存在的 ReplicationController 总数上限。
replicationcontrollers: "20"
# 最多创建的secret数
secrets: "10"
# 最多创建的service数
services: "10"
# 在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。
services.loadbalancers: "2"
# 在该命名空间中允许存在的 NodePort 类型的 Service 总数上限。
services.nodeports: 2
# ==存储型==
# 所有 PVC,存储资源的需求总量不能超过该值。
requests.storage: 40Gi
# 在命名空间的所有 Pod 中,本地临时存储请求的总和不能超过此值。
requests.ephemeral-storage: 512Mi
# 在命名空间的所有 Pod 中,本地临时存储限制值的总和不能超过此值。
limits.ephemeral-storage: 40Gi
# ephemeral-storage 与 requests.ephemeral-storage 相同。
# ==cpu、memory==
# 所有非终止状态的 Pod,其 CPU 限额总量不能超过该值。
limits.cpu: 1
# 所有非终止状态的 Pod,其内存限额总量不能超过该值。
limits.memory: 16Gi
# 所有非终止状态的 Pod,其 CPU 需求总量(pod中所有容器的request cpu)不能超过该值。
requests.cpu: 0.5
# 所有非终止状态的 Pod,其内存需求总量(pod中所有容器的request cpu)不能超过该值。
requests.memory: 512Mi
# cpu: 0.5 和requests.cpu相同
# memory: 512Mi 和 requests.memory相同
目前支持的资源配额如下:
用户通过apiserver创建namespace,apiserver会将namespace的信息存储入etcd中。
namespaceController通过apiserver读取维护这些namespace的信息,若ns被标记为删除状态,namespaceController会将其状态改为Terminating,并且会删除它里面的ServiceAccount、RC、Pod等一切资源对象。
Service Controller 会监听维护Service的状态、变化。Service、Pod、Endpoint之间的关系如下图:Service通过标签选择器找到并代理对应的pod,pod的ip维护在和Service同名的Endpoint中。
Endpoint Controller的作用是:监听Service和他对应的pod的变化
endpoint对象由每个node上的kube-proxy的进程使用,后面会详细介绍。
Scheduler负责接收ApiServer创建Pod的请求,并为Pod选择一个合适的Node,由该Node上的kubelet启动Pod中的相应容器,其调度流程主要分为两大步:
通过设置启动参数register-node
参数开启kubelet向apiserver注册自己的信息,apiserver会将数据存储在etcd中,注册之后通过kubectl如下命令可用查看到集群中的所有node 的信息:
若Node有问题,可以看到Status被标记为NotReady。
最终管理员下发的创建Pod的请求由Kubelet转发给它所在节点的容器运行时(Docker)处理。
容器的livenessProbe探针(httpGet、tcpSocket、ExecAction)用于判断容器的健康状态,kubelet会定期调用这些探针来判断容器是否存活。
使用方式:
livenessProbe:
httpGet:
path: /app/healthz
port: 80
1)exec
在容器内执行某命令,命令执行成功(通过命令退出状态码为0判断)则确定Pod就绪;
使用方式:
livenessProbe:
exec:
command:
- cat
- /app/healthz
2)tcpSocket
打开一个TCP连接到容器的指定端口,连接成功建立则确定Pod就绪。
使用方式:
livenessProbe:
tcpSocket:
port: 80
一般就绪探针会在启动容器一段时间后才开始第一次的就绪探测,之后做周期性探测。所以在定义就绪指针时,会给以下几个参数:
K8S原生的支持通过Service的方式代理一组Pod暴露到集群外部对外提供服务,创建Service时会这个Service创建一个Cluster-IP。而kubeproxy本质上就是这个Service的cluster-ip的负载均衡器。
在k8s的不同版本中,kubeproxy负载均衡Service的ClusterIp的方式不尽相同,主要如下:
如上图这个版本的KubeProxy的更像是一个TCP/UDP代理,最明显的特征是:外部用户访问cluster:port过来的流量真实的经过了KubeProxy的处理和转发,即流量经过了userspace中。
在这个版本KubeProxy端明显特征是:用户的流量不再由kubeproxy负责转发负载均衡。kubeproxy仅负责通过apiserver获取service和它对应的endpoint,然后根据这些数据信息生成iptables的规则,用户的流量通过iptables找到最终的pod。
和上一个版本的区别是支持了ipvs模块。
两者的定位不同:虽然iptables和ipvs都是基于netfilter实现的,但是iptables为防火墙而设计,而ipvs的定位是高性能的负载均衡。
在数据结构层面实现不同:iptables是链式结构,流量流经iptables要过所谓的四表五链,当集群中中的pod数量剧增,iptables会变得十分臃肿,效率下降。而ipvs是基于hash表实现的,理论上支持无限扩张,且寻址快。
ipvs也有不足,比如不支持包过滤、地址伪装、SNAT,在使用Nodeport类型的Service时,依然需要和iptables搭配使用。
CoreDNS是K8S的服务发现机制(有DNS能力),实现了通过解析service-name解析出cluster-ip的能力。而且service被重新创建时cluter-ip是可能发生变化的,但是server-name不会改变。
以POD的形式将CoreDNS部署进K8S的,如下查看CoreDNS的service:
这里的kube-dns的ip地址通常可以在安装coredns的配置文件中指定:coredns.yaml
验证下kube-dns解析外网域名的DNS能力
[root@master01 ~]# yum -y install bind-utils
[root@master01 ~]# dig -t A www.baidu.com @192.168.0.10 +short
www.a.shifen.com.
39.156.66.18
39.156.66.14
验证下kube-dns解析service名的DNS能力
# ${serviceName}.${名称空间}.svc.cluster.local.
[root@master01 ~]# dig -t A kubernetes.default.svc.cluster.local. @192.168.0.10 +short
192.168.0.1
像Calico或者flanel等,都是符合CNI规范的网络插件,作用如下:
注意点:flanel不支持NetworkPolicy资源对象定义的网络策略,而Calico支持。
什么是网络策略?