pod分类
自主式Pod
自我管理。scheduler调度到某台node,pod启动后,如果有容器出现故障,需要重启容器,是由k8s来完成的。但是如果节点故障了,pod就消失了,没法进行全局调度。
控制器管理端的Pod
Pod控制器,有很多种。
- ReplicationController:最早的一种,早期只有这一个控制器。比如启动一个Nginx pod,控制器控制了pod副本,一旦副本数量少了,立马补齐。多了就终止掉多余的Pod。ReplicationController支持滚动更新,比如一开始pod里的容器基于1.0版本的镜像启的,但是现在有新版本了,1.1版本的镜像,可以滚动更新。
- ReplicaSet:副本集控制器。但是ReplicaSet不直接使用,它有个申明是更新的控制器Deployment。
- Deployment:只能管理那些无状态的应用。
- StatefulSet:有状态副本集,管理有状态应用。
- DaemonSet:如果我们需要在每一个node上运行一个副本,而不是随意运行。
- Job,CronJob:作业,周期性作业。Job:比如备份操作,或者一些临时操作,比如现在要处理数据集,临时启用一个pod,清理完数据这个Pod就结束了。这种不需要一直运行着。
在k8s上创建Pod,只需要定义控制器,控制器创建好就会帮我们创建Pod。
Deployment控制器还支持二级控制器,叫HPA,称为水平Pod自动升缩控制器。比如一开始运行着两个pod,但是某个时间段用户访问增加,需要增加pod。到底应该加几个呢?HPA控制可以自动进行扩展。比如:判断一下,现在的CPU、内存资源利用率有多高,我们必须要确保平均低于60%,计算后发现需要加两个,那么HPA就扩展了两个。一旦访问量小了,还可以自动减,当然Deployment可以确保至少有几个存在。
Service
Pod有生命周期,万一Pod所在的节点宕机了,Pod有可能需要在其他节点上重建的,而重建完的Pod和之前的Pod不是同一个,只不过里面运行的是同一种服务。而且每一个容器都有ip地址,新Pod中的容器的ip地址可能和之前损坏的Pod中容器的ip地址也是不一样的。这样一来就有问题了,客户端怎么访问这些Pod呢?服务发现。客户端在访问后端服务时,应该不是直接访问后端服务的,这就需要服务发现机制了。
为了尽可能降低二者之间协调的复杂度,k8s为每一组提供同类服务的Pod和它的客户端之间添加了一个中间层,这个中间层是固定的,就叫service。service只要不删除,它的地址就是固定的,名称也是固定的。而后当客户端需要写在配置文件中访问某个服务时,它也不用再去自动发现某个功能,它只需要在配置文件中写明service地址或者名称就行。这个Service是一个调度器,不但能提供一个稳定的访问入口,还能将客户端带领到对应的Pod之上。一旦Pod因为什么原因消失了,新创建的Pod会立即被service关联进来,怎么实现这个关联的呢?客户端访问服务都是靠ip:port或者主机名:port来访问,service关联后端的pod不是靠IP地址来实现的,而是靠Pod上的元数据Label,标签。service靠标签选择器来关联Pod。关联进来后,service在动态探测这个新Pod的ip地址是什么,端口是什么,并作为自己调度的后端服务器的可用对象。
在k8s上,service不是应用程序,也不是实体组件,它只不过是iptables的DNAT规则。我们在创建DNAT规则时,所有到达某地址的都通通被目标地址转换成某某某地址。DNAT规则只是规则,其中的service地址并没有配置到任何一张网卡上,是不存在的,它仅仅出现在规则里,所以service的IP是ping不通的。但是的确可以请求作为服务端。能ping通是因为有TCP/IP协议栈支持这个IP地址,所以它能够在协议栈上进行响应,而这里是没有的,地址仅出现在规则里。
service作为k8s中对象来讲,service有自己的名称,相当于这个服务的名称,而名称可以被解析。你可以把service名称解析成service的IP。名称解析得靠dns来实现,装完k8s集群,第一件事就是在k8s集群上部署一个dns pod,以确保各service名称能被解析。像这种Pod是k8s自身服务就需要的Pod,所以称为基础性的Pod,而且也称为集群的附件(附加组件),Add-ons。可有可无,不作为程序本身的一部分组成。
k8s的附件有很多,dns只是其中一个。而且这个dns会动态改变,动态创建,动态删除,动态变动。怎么变动呢?比如说你把service的名称改一改,这会自动触发dns解析记录中对应的名称也改了。还有其他附件,比如监控,promethesure和grafana。
service后端可能是多个Pod,DNAT多目标要注意,对Linux来讲,iptables已经将负载均衡的功能主要交由ipvs来实现,因此如果后端是多个Pod时,使用DNAT来实现,在调度效果上可能不太尽如人意。因此在目前最新的1.11版本上,已经把iptables规则进一步改成了ipvs规则。也就意味着当你生成每一个service,就相当于一条ipvs规则,只不过是NAT模型的ipvs。因此支持用户指定的各种调度算法。service的地址是在iptables或ipvs规则中。
k8s三种网络
- 第一个网络:Pod网络。各Pod运行在一个网络中。Pod地址是配置在Pod内部网络名称空间之上的,是可以ping通的。
- 第二个网络:集群网络。各service运行在一个网络中。service地址是虚拟的,是假的,只存在于iptables或ipvs的规则当中
- 第三个网络:节点网络。各节点运行在一个网络中。节点网络在构建k8s之前就设置好了
外部访问时先到达节点网络,由节点网络代理至集群网络,在由集群网络代理至Pod网络。
k8s三类通信
一个Pod内的多个容器通信
lo,本地通信。
###各Pod间通信
无论两个Pod运行在一个节点上还是两个节点上,两个Pod之间的地址不会冲突,大家在同一个网段,而且可以直接通信。是通过物理桥通信或者Overay Network方案(叠加网络)。叠加网络是通过隧道的方式来转发二层报文,使得两个不同节点上的Pod,虽然跨主机,但好像工作在同一个二层网络中一样。叠加器有二层叠加,也有三层叠加。我们可以转发对方的二层报文,或隧道转发对方的三层报文,从而实现叠加网络。使用叠加网络解决docker容器跨主机容器通信,设置每台服务器使用docker0网络要不一样,比如第一台设为172.17.0.xxx,第二台使用172.17.1.xxx,这样所有容器地址都不会冲突,然后通过隧道转发以后,可以直接通信,就相当于在同一个二层网络中一样。云计算中理解最难之一就在网络上。
Pod和service之间的通信
两者都在各自的网络中,不是同一个网络,怎么通信?service地址只不过是节点主机中iptables或ipvs中的规则中的地址,所以只需要把容器中报文地址指向网关,网关假如是docker0桥,iptables在当前宿主机上就有规则,当容器需要访问service地址时,会把报文给网关,一般是docker0桥的地址,docker0桥收到以后,通过iptables或ipvs规则表一检查,就知道在哪了。但是service也是有可能变化的,比如被删除,被修改,这时候是如何触发修改iptables或ipvs规则的呢?在每个node上有一个守护进程,叫kube-proxy。负责随时与API server通信,Pod变化后是需要保存在API server中的。API server会生成一个通知事件,这个事件可以被任何关联的组件所接收到,一旦发现某个service背后的pod发生改变,对应的由kube-proxy将变化反应在iptables或ipvs的规则中。service的管理是由kube-proxy实现的。每一个service变动也要靠kube-proxy反应到规则上。
etcd和证书
API Server要存储大量的信息,如果某个master挂了,则需要切换到另外一台master,那么master之间需要共享存储,也就是etcd。
etcd是键值存储的数据库系统,跟redis很相像。但是etcd本身有很多协调功能是redis不具备的。更像zk。如果etcd宕机了,整个集群就完了。所以etcd要做成至少3节点。etcd本身也是restful风格的集群,也就是通过http/https通讯。如果是http通信,数据就有可能被拿走,所以要配置成https通信。
etcd一个端口用于集群通讯,一个端口用于像客户端提供服务。其内部通讯需要一个专门的证书,叫点对点通信的证书。而后像客户端提供服务的时候,如果需要https,得靠另外一套证书。同样的道理,k8s的apiserver,http不可靠,加密https,另外一套证书,因为这套证书是服务于k8s的客户端与服务端之间通信。而且最好不要与etcd属于同一个CA来签署。还有apiserver对外提供服务,一套证书。apiserver与kube-proxy通讯,一套证书。apiserver与kubelet通信,一套证书。一共5个CA,5套证书,为了足够安全。不光是加密,还有认证,所以CA也得是不同的,签证机构得不同。
CNI
k8s通过CNI插件体系来接入外部的网络服务解决方案,所以叫容器网络接口。只要网络服务提供商,能遵循CNI开发这个服务,那么这个网络服务商就可以作为k8s的网络解决方案来使用。这些网络解决方案可以以附件的形式托管在集群之上。常见的网络解决方案如下:
- flannel:网络配置,不支持网络策略,纯粹的叠加网络
- calico:网络配置,同时支持网络策略。但是这个部署和使用比较难,能基于bgp协议实现。。。
- canel:用第一种方式提供网络,用第二个提供网络策略
- …
以上解决方案都是第三方公司提供的,这些可以作为附件来运行,也可以作为节点上守护进程来运行。
在容器网络的构建中,CNI和kube-proxy两者都有参与。CNI插件的任务是在容器开启时为容器分配IP,并为这个IP构建虚拟设备,另外,CNI插件也负责将容器间通信的协议包(如TCP/UDP等)从K8s集群中的任意一台机器转发到指定容器所在的机器上,再转发到指定容器上。
k8s的名称空间
这个名称空间和docker里所用到的名称空间的不一样。
整个k8s是作为一个集群存在的,我们可以在里面运行两万个pod,可能会互相干扰。可以把它切割成多个空间,一类pod只运行在一个空间中,这个空间提供的不是真正意义上的网络边界,只是管理边界。比如说第一个空间叫开发空间,所有开发相关的pod都放在这个空间中,第二个是生产环境,所有生产环境的pod都放到这个空间里。将来我们删除这个网络名称空间,可以把这个环境通通移除掉。所以这个名称空间给我们提供了管理的边界。但是第一个名称空间里的pod访问第二个名称空间的pod是没有问题的,所以CNI实现还要支持网络策略定义,可以定义名称空间和名称空间之间,甚至同一个名称空间里的各pod之间能不能互相访问。可以生成iptables规则来隔离他们之间的访问。
对k8s来讲网络功能和网络策略是两个维度的内容,flannel只实现网络功能配置。