一、kubernetes架构
kube-apiserver : Kubernetes API,集群的统一入口,各组件协调者,以RESTful API提供接口服务,所有对象资源的增删改查和监听操作都交给APIServer处理后再提交给Etcd存储
kube-controller-manager:处理集群中常规后台任务,一个资源对应一个控制器,ControllerManager负责管理这些控制器
kube-scheduler:根据调度算法为新创建的Pod选择一个Node节点,可以任意部署, 可以部署在同一个节点上,也可以部署在不同的节点上
Etcd:分布式键值存储系统,用于保存集群状态数据,比如Pod、Service 等对象信息
Kubelet: kubelet是Master在Node节点上的Agent,管理本机运行容器的生命周期,比如创建容器、 Pod挂载数据卷、下载secret、获取容器和节点状态 等工作。kubelet将每个Pod转换成一组容器。
kube-proxy:在Node节点上实现Pod网络代理,维护网络规则和四层负载均衡工作
docker:容器引擎,运行容器
二、pod创建以及通信
1、Pod phase状态
- 挂起(Pending):API Server创建了Pod资源对象并已经存入了etcd中,但是并未被调度完成。
- 运行中(Running):Pod已经被调度到某节点之上,并且所有容器都已经被kubelet创建完成。
- 成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
- 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。
- 未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。
2、pod创建
3、pod通信
一个服务经常会起多个pod,到底访问那个pod的ip呢?
pod经常会因为各种原因被调度,调度后一个pod的ip会发生变化
pod的ip是虚拟的且局域的,在集群内部访问没有问题,但是从k8s集群的外部如何访问
4、services服务发现
对Pod资源对象进行打标签,用于Pod的辨识,而Servcie通过标签选择器,关联至同一标签类型的Pod资源对象。这样就实现了从service→pod→container的一个过程。
Type
的取值:
ClusterIP
:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的ServiceType
。NodePort
:通过每个 Node 上的 IP 和静态端口(NodePort
)暴露服务。NodePort
服务会路由到ClusterIP
服务,这个ClusterIP
服务会自动创建。通过请求<NodeIP>:<NodePort>
,可以从集群的外部访问一个NodePort
服务。LoadBalancer
:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort
服务和ClusterIP
服务。ExternalName
:通过返回CNAME
和它的值,可以将服务映射到externalName
字段的内容(例如,example.com
)。 没有任何类型代理被创建,Kubernetes 1.7 或更高版本的kube-dns
支持。
Service资源基于标签选择器将一组Pod定义成一个逻辑组合,并通过自己的IP地址和端口调度代理请求到组内的Pod对象,类似于负载均衡器功能,并且是基于TCP/IP 协议栈的四层负载
4.1 Service实现模型
Kubernetes v1.0 版本,默认 userspace代理模式。
Kubernetes v1.2 版本,默认 iptables 代理模式
Kubernetes v1.11版本,默认ipvs代理模式
1、userspace代理模式
kube-proxy 组件始终监视着api server中有关service的变动信息,通过watch监视,一旦有service资源相关的变动和创建,kube-proxy就会转换为当前节点上的能够实现资源调度规则
(1)客户端Pod请求内核空间的service iptables,
(2)请求转到给用户空间监听的kube-proxy 的端口,
(3)由kube-proxy将请求转给内核空间的 service ip,
(4)再由service iptalbes根据请求转给各节点中的的service pod。
由此可见这个模式有很大的问题,由客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。这样流量从用户空间进出内核带来的性能损耗是不可接受的
2、iptables代理模式
(1)客户端Pod请求内核空间的service iptables
(2)根据iptables的规则直接将请求转发到到各pod上
如果集群中存在上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能会打折扣。
3、ipvs代理模式
(1)客户端Pod请求内核空间的ipvs
(2)根据ipvs的规则直接分发到各pod上
kube-proxy会监视Kubernetes Service
对象和Endpoints
,调用netlink
接口创建ipvs规则并定期与Kubernetes Service
对象和Endpoints
对象同步ipvs规则,访问服务时流量将被重定向到其中一个后端Pod。
ipvs使用哈希表作为底层数据结构并在内核空间中工作,可以更快地重定向流量,并且在同步代理规则时具有更好的性能。
此外,ipvs的负载均衡算法比iptables更多,例如:
- rr:
轮询调度
- lc:最小连接数
dh
:目标哈希sh
:源哈希sed
:最短期望延迟nq
:不排队调度
注意: kube-proxy将验证节点上是否安装了IPVS模块,如果未安装,则kube-proxy将回退到iptables代理模式。
如果服务后端pod发生变化,增加或者删除,controller-manage会立即反映到apiserver上,而kube-proxy一直watch api server,感知到变化后转为ipvs或者iptables中的规则,这一切都是动态和实时的。
同时使用kube-dns可以解决Service的发现问题,k8s将Service的名称当做域名注册到kube-dns中,通过Service的名称就可以访问其提供的服务。
4.2 向外界暴露服务
1、pod漂移问题
Pod 可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上;那么自然随着 Pod 的创建和销毁,Pod IP 肯定会动态变化;
那么如何把这个动态的 Pod IP 暴露出去?借助Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外暴露只暴露 Service IP 就行了;
这就是 NodePort 模式:即在每个节点上开起一个端口,然后转发到内部 Pod IP 上
2、端口管理问题
采用 NodePort 方式暴露服务面临问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且难以维护;
这时,我们可以使用一个Nginx直接对内进行转发,Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间的,当在共享网络名称空间时,Pod上所监听的就是Node上的端口。简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了
3、域名分配及动态更新问题
当每次有新服务加入又该如何修改 Nginx 配置呢?总不能每次手动改或者Rolling Update 前端 Nginx Pod 吧,此时 Ingress 出现了,Ingress 包含两大组件:Ingress Controller 和 Ingress。
Ingress 简单的理解就是原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;
Ingress Controller 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取规则,按照自己模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,Ingress Controller需要单独部署
yaml配置
Ingress 中的spec字段是Ingress资源的核心组成部分:
rules: 用于定义当前Ingress资源的转发规则列表;由rules定义规则,或没有匹配到规则时,所有的流量会转发到由backend定义的默认后端
backend: 默认的后端用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,必须要定义backend或rules两者之一,该字段用于让负载均衡器指定一个全局默认的后端。
backend对象的定义由2个必要的字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标Service资源名称和端口。
部署的流程如下:
①下载Ingress-controller相关的YAML文件,并给Ingress-controller创建独立的名称空间;
②部署后端的服务,如rtaas,并通过service进行暴露;
③部署Ingress-controller的service,以实现接入集群外部流量;
④部署Ingress,进行定义规则,使Ingress-controller和后端服务的Pod组进行关联。
参考文献:
https://kubernetes.io/zh/docs/concepts/services-networking/
http://docs.kubernetes.org.cn/230.html