kubernetes简介

kubernetes简介

2017.10 Docker宣布将在新版本加入对Kubernetes的原生支持 Kubernets(k8s)是google在2014年发布的一个开源项目
据说google的数据中心里运行着20多亿个容器,而且google十年前就开始使用容器技术

最初 google开发了一个叫Borg的系统(现在命名为Omega)来调度如此庞大数量的容器和工作负载,
在积累了多年的经验后,google决定重写这个容器管理系统,并将其贡献到开源社区,让全世界都能受益 这个项目

Kubernetes是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务。
通过Kubernetes能够进行应用的自动化部署和扩缩容。在Kubernetes中,会将组成应用的容器组合成一个逻辑单元以更易管理和发现。
Kubernetes积累了作为Google生产环境运行工作负载15年的经验,并吸收了来自于社区的最佳想法和实践。
Kubernetes经过这几年的快速发展,形成了一个大的生态环境,Google在2014年将Kubernetes作为开源项目。Kubernetes的关键特性包括:

自动化装箱:在不牺牲可用性的条件下,基于容器对资源的要求和约束自动部署容器。同时,为了提高利用率和节省更多资源,将关键和最佳工作量结合在一起。
自愈能力:当容器失败时,会对容器进行重启;当所部署的Node节点有问题时,会对容器进行重新部署和重新调度;当容器未通过监控检查时,会关闭此容器;直到容器正常运行时,才会对外提供服务。
水平扩容:通过简单的命令、用户界面或基于CPU的使用情况,能够对应用进行扩容和缩容。
服务发现和负载均衡:开发者不需要使用额外的服务发现机制,就能够基于Kubernetes进行服务发现和负载均衡。
自动发布和回滚:Kubernetes能够程序化的发布应用和相关的配置。如果发布有问题,Kubernetes将能够回归发生的变更。
保密和配置管理:在不需要重新构建镜像的情况下,可以部署和更新保密和应用配置。
存储编排:自动挂接存储系统,这些存储系统可以来自于本地、公共云提供商(例如:GCP和AWS)、网络存储(例如:NFS、iSCSI、Gluster、Ceph、Cinder和Floker等)

Kubernetes及容器生态系统
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
kubernetes特性

1.自我修复
在节点故障时重新启动失败的容器,替换和重新部署,保证预期的副本数量;
杀死健康检查失败的容器,并且健康检查失败的容器,并且在未准备好之前不会处理客户端请求,确保线上服务不中断。

2.弹性伸缩
使用命令、UI或者基于CPU使用情况自动快速扩容和缩容应用程序实例,保证应用业务高峰并发时的高可用性;业务低峰时回收资源,以最小成本运行服务。

3.自动部署和回滚
K8S采用滚动更新策略更新应用,一次更新一个Pod,而不是同时删除所有Pod,如果更新过程中出现问题,将回滚更改,确保升级不受影响业务。

4.服务发现和负载均衡
K8S为多个容器提供一个统一访问入口(内部IP地址和一个DNS名称),并且负载均衡关联的所有容器,使得用户无需考虑容器IP问题。

5.机密和配置管理
管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性。并可以将一些常用的配置存储在K8S中,方便应用程序使用。

6.存储编排
挂载外部存储系统,无论是来自本地存储,公有云(如AWS),还是网络存储(如NFS、GlusterFS、Ceph)都作为集群资源的一部分使用,极大提高存储使用灵活性。

7.批处理
提供一次性任务,定时任务;满足批量数据处理和分析的场景。

Kubernetes的整体架构

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

kubernets组件介绍

Master节点组件提供整个集群的控制面板:
kube-apiserver: 暴露API操作接口,是kubernetes里面所有资源增,删,改,查等操作的唯一入口;也是集群控制的入口。
etcd: 集群的主数据库,集群里面的所有数据都存储于此。
kube-controller-manager: kubernetes里所有资源对象的自动化控制中心, 控制器的大管家。
kube-scheduler: 负责资源调度(Pod调度)的进程,为新创建的Pod分配Node节点去运行。相当于公交公司的“调度室”。
Node节点组件维持Pods的运行:
kubelet:负责Pod对应容器的创建,启动,停止等任务; 同时与Master节点密切协作,实现集群管理的基本功能。
kube-proxy:实现Service的通信和负载均衡机制的重要组件。
docker: docker引擎,负责本节点的容器创建和管理工作。
Flannel:是Overlay网络的一种,也是将源数据包封装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VXLAN、AWS VPC和GCE路由等数据转发方式。
多主机容器网络通信其他主流方案:隧道方案( Weave、OpenvSwitch ),路由方案(Calico)等。

Kubernetes属于主从分布式架构,主要由Master Node和Worker
Node组成,以及包括客户端命令行工具kubectl和其它附加项。

Master Node:作为控制节点,对集群进行调度管理;
Worker Node:作为真正的工作节点,运行业务应用的容器;Worker Node包含kubelet、kube proxy和Container Runtime;
kubectl:用于通过命令行与API Server进行交互,而对Kubernetes进行操作,实现在集群中进行各种资源的增删改查等操作;
Add-on:是对Kubernetes核心功能的扩展,例如增加网络和网络策略等能力。
repliceation 用于伸缩副本数量
endpoint 用于管理网络请求
scheduler 调度器
etcd保存了整个集群的状态;
apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;
除了核心组件,还有一些推荐的Add-ons:

kube-dns负责为整个集群提供DNS服务
Ingress Controller为服务提供外网入口
Heapster提供资源监控
Dashboard提供GUI
Federation提供跨可用区的集群
Fluentd-elasticsearch提供集群日志采集、存储与查询

组件功能介绍

在这里插入图片描述

Master Node(主节点)

API Server(API服务器)
API Server主要用来处理REST的操作,确保它们生效,并执行相关业务逻辑,以及更新etcd(或者其他存储)中的相关对象。API Server是所有REST命令的入口,它的相关结果状态将被保存在etcd(或其他存储)中。

API Server的基本功能包括:
REST语义,监控,持久化和一致性保证,API 版本控制,放弃和生效
内置准入控制语义,同步准入控制钩子,以及异步资源初始化

API注册和发现
另外,API Server也作为集群的网关。默认情况,客户端通过API Server对集群进行访问,客户端需要通过认证,并使用API Server作为访问Node和Pod(以及service)的堡垒和代理/通道。
Cluster state store(集群状态存储)
Kubernetes默认使用etcd作为集群整体存储,当然也可以使用其它的技术。
etcd是一个简单的、分布式的、一致的key-value存储,主要被用来共享配置和服务发现。
etcd提供了一个CRUD操作的REST API,以及提供了作为注册的接口,以监控指定的Node。
集群的所有状态都存储在etcd实例中,并具有监控的能力,因此当etcd中的信息发生变化时,就能够快速的通知集群中相关的组件。
Controller-Manager Server(控制管理服务器)
Controller-Manager Serve用于执行大部分的集群层次的功能,它既执行生命周期功能
(例如:命名空间创建和生命周期、事件垃圾收集、已终止垃圾收集、级联删除垃圾收集、node垃圾收集),也执行API业务逻辑(例如:pod的弹性扩容)。
控制管理提供自愈能力、扩容、应用生命周期管理、服务发现、路由、服务绑定和提供。
Kubernetes默认提供Replication Controller、Node Controller、Namespace Controller、Service Controller、Endpoints Controller、Persistent Controller、DaemonSet Controller等控制器。
Scheduler(调度器)
scheduler组件为容器自动选择运行的主机。依据请求资源的可用性,服务请求的质量等约束条件,scheduler监控未绑定的pod,并将其绑定至特定的node节点。
Kubernetes也支持用户自己提供的调度器,Scheduler负责根据调度策略自动将Pod部署到合适Node中,调度策略分为预选策略和优选策略,Pod的整个调度过程分为两步:

1)预选Node:遍历集群中所有的Node,按照具体的预选策略筛选出符合要求的Node列表。如没有Node符合预选策略规则,该Pod就会被挂起,直到集群中出现符合要求的Node。

2)优选Node:预选Node列表的基础上,按照优选策略为待选的Node进行打分和排序,从中获取最优Node。

Worker Node(从节点)

Kubelet
Kubelet是Kubernetes中最主要的控制器,它是Pod和Node API的主要实现者,Kubelet负责驱动容器执行层。
在Kubernetes中,应用容器彼此是隔离的,并且与运行其的主机也是隔离的,这是对应用进行独立解耦管理的关键点。

在Kubernets中,Pod作为基本的执行单元,它可以拥有多个容器和存储数据卷,
能够方便在每个容器中打包一个单一的应用,从而解耦了应用构建时和部署时的所关心的事项,已经能够方便在物理机/虚拟机之间进行迁移。
API准入控制可以拒绝或者Pod,或者为Pod添加额外的调度约束,但是Kubelet才是Pod是否能够运行在特定Node上的最终裁决者,而不是scheduler或者DaemonSet。
kubelet默认情况使用cAdvisor进行资源监控。
负责管理Pod、容器、镜像、数据卷等,实现集群对节点的管理,并将容器的运行状态汇报给Kubernetes API Server。
Container Runtime(容器运行时)
每一个Node都会运行一个Container Runtime,其负责下载镜像和运行容器。Kubernetes本身并不停容器运行时环境,但提供了接口,可以插入所选择的容器运行时环境。
kubelet使用Unix socket之上的gRPC框架与容器运行时进行通信,kubelet作为客户端,而CRI shim作为服务器。
protocol buffers API提供两个gRPC服务,ImageService和RuntimeService。
ImageService提供拉取、查看、和移除镜像的RPC。
RuntimeSerivce则提供管理Pods和容器生命周期管理的RPC,以及与容器进行交互(exec/attach/port-forward)。
容器运行时能够同时管理镜像和容器(例如:Docker和Rkt),并且可以通过同一个套接字提供这两种服务。在Kubelet中,这个套接字通过–container-runtime-endpoint和–image-service-endpoint字段进行设置。
Kubernetes CRI支持的容器运行时包括docker、rkt、cri-o、frankti、kata-containers和clear-containers等。

在这里插入图片描述

kube proxy
基于一种公共访问策略(例如:负载均衡),服务提供了一种访问一群pod的途径。
此方式通过创建一个虚拟的IP来实现,客户端能够访问此IP,并能够将服务透明的代理至Pod。
每一个Node都会运行一个kube-proxy,kube proxy通过iptables规则引导访问至服务IP,并将重定向至正确的后端应用,通过这种方式kube-proxy提供了一个高可用的负载均衡解决方案。
服务发现主要通过DNS实现。

在Kubernetes中,kube proxy负责为Pod创建代理服务;引到访问至服务;并实现服务到Pod的路由和转发,以及通过应用的负载均衡。

运行流程

1、准备好一个包含应用程序的Deployment的yml文件,然后通过kubectl客户端工具发送给ApiServer。
2、ApiServer接收到客户端的请求并将资源内容存储到数据库(etcd)中。
3、Controller组件(包括scheduler、replication、endpoint)监控资源变化并作出反应。
4、ReplicaSet检查数据库变化,创建期望数量的pod实例。
5、Scheduler再次检查数据库变化,发现尚未被分配到具体执行节点(node)的Pod,然后根据一组相关规则将pod分配到可以运行它们的节点上,并更新数据库,记录pod分配情况。
6、Kubelete监控数据库变化,管理后续pod的生命周期,发现被分配到它所在的节点上运行的那些pod。如果找到新pod,则会在该节点上运行这个新pod。
7、kuberproxy运行在集群各个主机上,管理网络通信,如服务发现、负载均衡。例如当有数据发送到主机时,将其路由到正确的pod或容器。
	对于从主机上发出的数据,它可以基于请求地址发现远程服务器,并将数据正确路由,在某些情况下会使用轮训调度算法(Round-robin)将请求发送到集群中的多个实例。
 

创建Pod的整个流程,时序图如下:

在这里插入图片描述

1. 用户提交创建Pod的请求,可以通过API Server的REST API ,也可用Kubectl命令行工具,支持Json和Yaml两种格式;
2. API Server 处理用户请求,存储Pod数据到Etcd;
3. Schedule通过和 API Server的watch机制,查看到新的pod,尝试为Pod绑定Node;
4. 过滤主机:调度器用一组规则过滤掉不符合要求的主机,比如Pod指定了所需要的资源,那么就要过滤掉资源不够的主机;
5. 主机打分:对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,
   比如把一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等;
7. 选择主机:选择打分最高的主机,进行binding操作,结果存储到Etcd中;
8. kubelet根据调度结果执行Pod创建操作: 绑定成功后,会启动container, docker run, scheduler会调用API Server的API在etcd中创建一个bound pod对象,
	描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步bound pod信息,
	一旦发现应该在该工作节点上运行的bound pod对象没有更新,则调用Docker API创建并启动pod内的容器。

其他附加项

kubectl

kubectl是Kubernetes集群的命令行接口。运行kubectl命令的语法如下所示:

$ kubectl [command] [TYPE] [NAME] [flags]
这里的command,TYPE、NAME和flags为:

comand:指定要对资源执行的操作,例如create、get、describe和delete
TYPE:指定资源类型,资源类型是大小学敏感的,开发者能够以单数、复数和缩略的形式。例如:
$ kubectl get pod pod1 
$ kubectl get pods pod1 
$ kubectl get po pod1
NAME:指定资源的名称,名称也大小写敏感的。如果省略名称,则会显示所有的资源,例如:
 $kubectl get pods
flags:指定可选的参数。例如,可以使用-s或者–server参数指定Kubernetes API server的地址和端口。
另外,可以通过运行kubectl help命令获取更多的信息。

在Kunbernetes中可以以附加项的方式扩展Kubernetes的功能,目前主要有网络、服务发现和可视化这三大类的附加项,下面是可用的一些附加项:

网络和网络策略

ACI 通过与Cisco ACI集成的容器网络和网络安全。
Calico 是一个安全的3层网络和网络策略提供者。
Canal 联合Fannel和Calico,通过网络和网络侧。
Cilium 是一个3层网络和网络侧插件,它能够透明的加强HTTP/API/L7 策略。其即支持路由,也支持overlay/encapsultion模式。
Flannel 是一个overlay的网络提供者

流程图

服务发现

CoreDNS 是一个灵活的,可扩展的DNS服务器,它能够作为Pod集群内的DNS进行安装。
Ingress 提供基于Http协议的路由转发机制。

可视化&控制

Dashboard 是Kubernetes的web用户界面。

kubernetes集群中的对象或资源
在这里插入图片描述

分层架构

Kubernetes设计理念和功能其实就是一个类似Linux的分层架构,如下图所示

在这里插入图片描述

核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
接口层:kubectl命令行工具、客户端SDK以及集群联邦
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
kubelet
kubelet负责管理pods和它们上面的容器,images镜像、volumes、etc。

kube-proxy
每一个节点也运行一个简单的网络代理和负载均衡(详见services FAQ )PS:官方 英文)。 正如Kubernetes API里面定义的这些服务(详见the services doc)(PS:官方 英文)也可以在各种终端中以轮询的方式做一些简单的TCP和UDP传输。
服务端点目前是通过DNS或者环境变量( Docker-links-compatible 和 Kubernetes{FOO}_SERVICE_HOST 及 {FOO}_SERVICE_PORT 变量都支持)。这些变量由服务代理所管理的端口来解析。

Kubernetes控制面板
Kubernetes控制面板可以分为多个部分。目前它们都运行在一个master 节点,然而为了达到高可用性,这需要改变。不同部分一起协作提供一个统一的关于集群的视图。
etcd
所有master的持续状态都存在etcd的一个实例中。这可以很好地存储配置数据。因为有watch(观察者)的支持,各部件协调中的改变可以很快被察觉。

Kubernetes API Server
API服务提供Kubernetes API (PS:官方 英文)的服务。这个服务试图通过把所有或者大部分的业务逻辑放到不两只的部件中从而使其具有CRUD特性。它主要处理REST操作,在etcd中验证更新这些对象(并最终存储)。

Scheduler
调度器把未调度的pod通过binding api绑定到节点上。调度器是可插拔的,并且我们期待支持多集群的调度,未来甚至希望可以支持用户自定义的调度器。

Kubernetes控制管理服务器
所有其它的集群级别的功能目前都是由控制管理器所负责。例如,端点对象是被端点控制器来创建和更新。这些最终可以被分隔成不同的部件来让它们独自的可插拔。
replicationcontroller(PS:官方 英文)是一种建立于简单的 pod API之上的一种机制。一旦实现,我们最终计划把这变成一种通用的插件机制。

Label

Label是Kubernetes系列中另外一个核心概念。是一组绑定到K8s资源对象上的key/value对。同一个对象的labels属性的key必须唯一。label可以附加到各种资源对象上,如Node,Pod,Service,RC等。

通过给指定的资源对象捆绑一个或多个不用的label来实现多维度的资源分组管理功能,以便于灵活,方便地进行资源分配,调度,配置,部署等管理工作。

版本标签:"release" : "stable" , "release" : "canary"...
环境标签:"environment" : "dev" , "environment" : "production"
架构标签:"tier" : "frontend" , "tier" : "backend" , "tier" : "middleware"
分区标签:"partition" : "customerA" , "partition" : "customerB"...
质量管控标签:"track" : "daily" , "track" : "weekly"

什么是Label selector(标签选择器)

Label selector是Kubernetes核心的分组机制,通过label
selector客户端/用户能够识别一组有共同特征或属性的资源对象。

Label selector的查询条件

基于值相等的查询条件: 类似于SQL语句中的=或!=;  
例如:select * from pod where name=(!=)'redis-slave';

基于子集的查询条件: 类似于SQL语句中的in或 not in; 
例如:select * from pod where name in(或not in) ('redis-slave','redis-master');

两种查询条件也可以组合在一起使用。

新出现的管理对象如Deploment、ReplicaSet、DaemonSet和Job则可以在Selector中使用基于集合的筛选条件定义,例如:

selector:
  matchLabels:
    app: myweb
  matchExpressions:
    - {key: tier, operator: In, values: [frontend]}
    - {key: environment, operator: NorIn, values: [dev]}
matchLabels用于定义一组Label,与直接写在Selector中作用相同:matchExpression用于定义一组基于集合的筛选条件,可用的条件运算符包括:In、NotIn、Exists和DoesNotExist。

如果同时设置了matchLabels和matchExpression,则两组条件为“AND”关系,即所有条件需要满足才能完成Selector的筛选。

Label selector的使用场景

1.kube-controller进程通过资源对象RC上定义的Label Selector来筛选要监控的Pod副本的数量,从而实现Pod副本的数量始终符合预期设定的全自动控制流程

2.kupe-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立器每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制

3.通过对某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,Kube-scheduler进程可以实现Pod定向调度的特性

在前面的留言板例子中,我们只使用了一个name=XXX的Label Selector。
假设为Pod定义了Label:release、env和role,不同的Pod定义了不同的Label值,
如果我们设置了“role=frontend”的Label Selector,则会选取到Node 1和Node 2上到Pod。
而设置“release=beta”的Label Selector,则会选取到Node 2和Node 3上的Pod

总结:使用Label可以給对象创建多组标签,Label和Label
Selector共同构成了Kubernetes系统中最核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。

Kubernetes所提供的微服务网格架构

  • 既然每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,现在多个Pod副本组成了一个集群来提供服务,那么客户端如何来访问它们呢?一般的做法是部署一个负载均衡器(软件或硬件),为这组Pod开启一个对外的服务端口如8000端口,并且将这些Pod的Endpoint列表加入8000端口的转发列表中,客户端就可以通过负载均衡器的对外IP地址+服务端口来访问服务,而客户端的请求最后会被zhaun fa转发到哪个Pod,则由负载均衡器的算法所决定。

  • Kubernetes也遵循了上述常规做法,运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话机制。Kubernetes发明了一种很巧妙又影响深远的设计:Service不是共用一个负载均衡的IP地址,而是每个Service分配了全局唯一的虚拟IP地址,这个虚拟IP地址被称为Cluster IP。这样一来,每个服务就变成了具备唯一IP地址的“通信节点”,服务调用就变成了最基础的TCP网络通信问题。

  • 我们知道,Pod的Endpoint地址会随着Pod的销毁和重新创建而发生改变,因为新Pod的IP地址与之前旧Pod的不同。而Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个生命周期内。它的Cluster IP不会发生改变。于是,服务发现这个棘手的问题在Kubernetes的架构里也得到轻松解决:只要用Service的Name与Service的Cluster IP地址做一个DNS域名映射即可完美解决问题。现在想想,这真是一个很棒的设计。

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 8080
  selector:
    tier: frontend
#上述内容定义了一个名为“tomcat-service”的Service,它的服务端口为8080,拥有“tier-frontend”这个Label的所有Pod实例都属于它,运行下面的命令进行创建:

kubectl create -f tomcat-service.yaml

#注意到我们之前在tomcat-deployment.yaml里定义的Tomcat的Pod刚好拥有这个标签,所以我们刚才创建的tomcat-service已经对应到了一个Pod实例,
#运行下面的命令可以查看tomcat-service的Endpoint列表,其中172.17.1.3是Pod的IP地址,端口8080是Container暴露的端口:

kubectl get endpoints
运行下面的命令即可看到tomcat-service被分配的Cluster IP及更多的信息:

kubectl get svc tomcat-service -o yaml

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2018-10-17T10:04:21Z
  name: tomcat-service
  namespace: default
  resourceVersion: "10169415"
  selfLink: /api/v1/namespaces/default/services/tomcat-service
  uid: 04caf53f-d1f4-11e8-83a3-5254008f2a0b
spec:
  clusterIP: 10.254.169.39
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    tier: frontend
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}
在spec.ports的定义中,targetPort属性用来确定提供该服务的容器所暴露(EXPOSE)的端口号,即具体业务进程在容器内的targetPort上提供TCP/IP接入;而port属性则定义了Service的虚拟端口。
前面我们定义Tomcat服务时,没有指targetPort,则默认targetPort与port相同。

Service的多端口问题

很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如Mycat、Codis等常见中间件。Kubernetes Service支持多个Endpoint,在存在多个Endpoint的情况下,要求每个Endpoint定义一个名字区分。下面是Tomcat多端口的Service定义样例:

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 8080
   name: service-port
  - port: 8005
   name: shutdown-port
  selector:
    tier: frontend

Kubernetes的服务发现机制

任何分布式系统都会涉及“服务发现”这个基础问题,大部分分布式系统通过提供特定的API接口来实现服务发现的功能,但这样做会导致平台的入侵性比较强,也增加了开发测试的困难。Kubernetes则采用了直观朴素的思路去解决这个棘手的问题。

首先,每个Kubernetes中的Service都有一个唯一的Cluster IP及唯一的名字,而名字是由开发者自己定义的,部署时也没有改变,所以完全可以固定在配置中。接下来的问题就是如何通过Service的名字找到对应的Cluster IP?

最早时Kubernetes采用了Linux环境变量的方式解决这个问题,即每个Service生成一些对应的Linux环境变量(ENV),并在每个Pod的容器在启动时,自动注入这些环境变量,以下是tomcat-service产生的环境变量条目:

TOMCAT_SERVICE_SERVICE_HOST=10.254.93.4
TOMCAT_SERVICE_SERVICE_PORT_SERVICE_PORT=8080
TOMCAT_SERVICE_SERVICE_PORT_SHUTDOWN_PORT=8005
TOMCAT_SERVICE_SERVICE_PORT=8080
TOMCAT_SERVICE_PORT=tcp://10.254.93.4:8080
TOMCAT_SERVICE_PORT_8080_TCP_ADDR=10.254.93.4
TOMCAT_SERVICE_PORT_8080_TCP=tcp://10.254.93.4:8080
TOMCAT_SERVICE_PORT_8080_TCP_PROTO=tcp
TOMCAT_SERVICE_PORT_8080_TCP_PORT=8080
TOMCAT_SERVICE_PORT_8005_TCP=tcp://10.254.93.4:8005
TOMCAT_SERVICE_PORT_8005_TCP_ADDR=10.254.93.4
TOMCAT_SERVICE_PORT_8005_TCP_PROTO=tcp
TOMCAT_SERVICE_PORT_8005_TCP_PORT=8005

上述环境变量中,比较重要的是前3条环境变量,我们可以看到,每个Service的IP地址及端口都是有标准的命名规范,就可以通过代码访问系统环境变量的方式得到所需的信息,实现服务调用。

考虑到环境变量的方式获取Service的IP与端口的方式仍然不太方便,不够直观,后来Kubernetes通过Add-On增值包的方式引入了DNS系统,把服务名作为dns域名,这样一来,程序就可以直接使用服务名来建立通信连接了。目前Kubernetes上的大部分应用都已经采用了DNS这些新型的服务发现机制,后面的章节中我们会讲述如何部署这套DNS系统。

外部系统访问Service的问题

为了更好深入地理解和掌握Kubernetes,我们需要弄明白Kubernetes里的“三种IP”这个关键问题,这三种分别如下。

Node IP:Node节点的IP地址。
Pod IP:Pod的IP地址。
Cluster IP:Service的IP地址。

首先,Node IP是Kubernetes集群中每个节点的物理网卡的IP地址,这是一个真实存在的物理网络,所有属于这个网络的服务器之间都能通过这个网络直接通信,不管它们中是否有部分节点不属于这个Kubernetes集群。这也表明了Kubernetes集群之外的节点访问Kubernetes集群之内的某个节点或者TCP/IP服务时,必须要通过Node
IP进行通信。

其次,Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络,前面我们说过,Kubernetes里一个Pod里的容器访问另外一个Pod里的容器,就是通过Pod IP所在的虚拟二层网络进行通信的,而真实的TCP/IP流量则是通过Node IP所在的物理网卡流出的。

最后,我们说说Service的Cluster IP,它也是一个虚拟的IP,但更像是一个“伪造”的IP网络,原因有以下几点。

Cluster IP仅仅作用于Kubernetes Service这个对象,并由Kubernetes管理和分配IP地址(来源于Cluster IP地址池)。 Cluster IP无法被Ping,因为没有一个“实体网络对象”来响应。 Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备TCP/IP通信的基础,并且它们属于Kubernetes集群这样一个封闭的空间,集群之外的节点如果要访问这个通信端口,则需要做一些额外的工作。> 在Kubernetes集群之内,Node IP网、Pod IP网与Clsuter IP之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊的路由规则,与我们所熟知的IP路由有很大的不同。

根据上面的分析和总结,我们基本明白了:Service的Cluster IP属于Kubernetes集群内部的地址,无法在集群外部直接使用这个地址。那么矛盾来了:实际上我们开发的业务系统中肯定多少由一部分服务是要提供Kubernetes集群外部的应用或者用户来使用的,典型的例子就是Web端的服务模块,比如上面的tomcat-service,那么用户怎么访问它?

采用NodePort是解决上述问题的最直接、最常用的做法。具体做法如下,以tomcat-service为例,我们在Service的定义里做如下扩展即可(黑体字部分):

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  type: NodePort
  ports:
   - port: 8080
     nodePort: 31002
  selector:
    tier: frontend

#其中,nodePort:31002这个属性表明我们手动指定tomcat-service的NodePort为31002,否则Kubernetes会自动分配一个可用的端口。

通过NodePort访问Service

NodePort的实现方式是在Kubernetes集群里的每个Node上为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务,在任意Node上运行netstat命令,我们就可以看到有NodePort端口被监听:

 netstat -tlp|grep 31002
tcp6       0      0 [::]:31002              [::]:*                  LISTEN      19043/kube-proxy

但NodePort还没有完全解决外部访问Service的所有问题,比如负载均衡问题,假如我们的集群中有10个Node,则此时最好有一个负载均衡器,外部的请求只需要访问此负载均衡器的IP地址,由负载均衡负责转发流量到后面某个Node的NodePort上。

NodePort与Load balancer

图中的Load balancer组件独立于Kubernetes集群之外,通常是一个硬件的负载均衡器,或者是以软件方式实现的,例如HAProxy或者Nginx。对于每个Service,我们通常需要配置一个对应的Load balancer实例来转发流量到后端的Node上,这的确增加了工作量及出错的概率。于是Kubernetes提供了自动化的解决方案,如果我们的集群运行在谷歌的GCE公有云上,那么只要我们把Service的type=NodePort改为type=LoadBalancer,此时Kubernetes会自动创建一个对应的Load balancer实例并返回它的IP地址供外部客户端使用。其他公有云提供商只要实现了支持此特性的驱动,则也可以达到上述目的。此外,裸机上的类似机制(Bare Metal Service Load Balancers)也正在被开发。

参考链接
k8s网络之Flannel网络
Kubernetes-基于Flannel的网络原理

Flannel

Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。

在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相互ping通。

Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。

Flannel实质上是一种“覆盖网络(overlaynetwork)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等数据转发方式,默认的节点间数据通信方式是UDP转发。

flannel的特点

1.使集群中的不同Node主机创建的Docker容器都具有全集群唯一的虚拟IP地址。

2.建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖网络是建立在另一个网络之上并由其基础设施支持的虚拟网络。
  覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础设施分离。在将封装的数据包转发到端点后,将其解封装。

3.创建一个新的虚拟网卡flannel0接收docker网桥的数据,通过维护路由表,对接收到的数据进行封包和转发(vxlan)。

4.etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化。

flannel对网络要求提出的解决方法

一、互相不冲突的IP
1.flannel利用kubernetes Api通过ETCD存储整个集群的网络配置,根据配置记录集群使用的网段
2.flannel在每个主机上英雄flanneld作为agent,它会为所在主机从集群的网络地址空间中获取一个小网段subnet,
  本主机内所有容器IP将从中分配在flannel network中,每个Pod都会分配一个唯一的IP地址,且每个k8s Node的subnet不重叠

二、Pod之间互相访问
1.flanneld将本机获取的subnet以及主机之间通信的public ip通过etcd存储起来,需要时发给相应模块
2.flanneld通过各种backend mechanism,如vxlan、udp等跨主机转发容器之间的网络流量,完成容器之间的跨主机通信

Flannel架构原理
在这里插入图片描述

各个组件的解释:

Cni0:网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡)。Pod中从网卡eth0发出的流量都会发送到Cni0网桥设备的端口(网卡)上。
Flannel.1: overlay网络的设备,用来进行 vxlan 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。

Flanneld:flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac,ip等网络数据信息。
不同node上的pod的通信流程:

pod中产生数据,根据pod的路由信息,将数据发送到Cni0
Cni0 根据节点的路由表,将数据发送到隧道设备flannel.1
Flannel.1查看数据包的目的ip,从flanneld获得对端隧道设备的必要信息,封装数据包。
Flannel.1将数据包发送到对端设备。对端节点的网卡接收到数据包,发现数据包为overlay数据包,解开外层封装,并发送内层封装到flannel.1设备。
Flannel.1设备查看数据包,根据路由表匹配,将数据发送给Cni0设备。
Cni0匹配路由表,发送数据给网桥上对应的端口。
具体的通信流程

Pod通信

flannel是Overlay网络的一种,也是将源数据包封装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VXLAN、AWS, VPC和GCE路由等数据转发方式。

Flannel通过给每台宿主机分配一个子网的方式为容器提供虚拟网络,它基于Linux TUN/TAP,使用UDP封装IP包来创建overlay网络,并借助etcd维护网络的分配情况。

Flannel是CoreOS团队针对Kubernates设计的跨主机容器网络解决方案, 它可以使集群中不同节点上运行的docker容器都具有全集群唯一的虚拟IP地址。其模型为全部的宿主机使用一个network,然后在每个宿主机上从network中划分一个子网subnet。为宿主机上的容器创建网络时,从subnet中划分一个ip给容器。

数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。
Flannel通过Etcd服务维护了一张节点间的路由表,详细记录了各节点子网网段 。
源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一样的有docker0路由到达目标容器。

同一个Node上的pod之间的通信
每个pod都有唯一一个ip(属于docker0网桥上的网段),每个pod的网络基础设施容器都是通过veth设备对直接连在docker0网桥上的
不同Node上的pod之间的通信
要支持不同Node节点上的pod通信需要满足的要求:
1)、整个kubernetes集群中对每个POD分配一个唯一的IP:在部署Kubernetes时,对每个Node节点的docker0网桥的网段重新划分,
用户设定一个大的网段(eg:10.20.0.0/16),存在etcd中,每个节点的flanneld会去etcd中查找这个值,然后,flanneld随机生成一个属于大网段的,
且不冲突的子网(eg: 10.20.37.0/24; 10.20.92.0/24; 10.20.0.53/24)并将该值写回etcd中,这样就保证了每个pod的IP都会在10.20.0.0/16这个网段内;

2)、Node节点之间需要架设一个overlay网络(一般通过flannel实现),保证pod可以互相访问。
Pod与Service之间的通信
为了支持水平扩展和高可用,实际使用中并不会直接使用POD的IP来访问POD中的服务,
而是定义service来访问一组POD中的服务。service是对一组POD的抽象,会根据访问策略(负载均衡)来访问这组POD。

Kubernetes会在创建service时随机分配一个ClusterIP,这是一个虚IP(VIP),不能与docker0网桥的网段冲突,
比如:10.10.0.0/16这个网段(在 kube-apiserver的--service-cluster-ip-range=10.10.0.0/16配置项中指定)。

访问POD中服务是只需要访问这个VIP就好了,而service会将请求经过负载均衡之后转发到后端POD中,这些工作都是运行在每个Node节点的kube-proxy进程管理的,
它的核心功能是:将对某个Service的访问经过负载均衡(Round Robin算法)之后转发到某一个后端POD:

1、Service的类型
1、ClusterIP:默认类型,自动分配一个仅cluster内部可以访问的虚拟IP;
2、NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过<NodeIP>:NodePort来访问改服务;
3、LoadBalancer:在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到<NodeIP>:NodePort
无论是ClusterIP+TargetPort的方式,还是NodeIP+NodePort的方式访问服务,都会被节点的iptables规则重定向到kube-proxy监听Service的服务代理端口,
kube-proxy接受到Service请求之后,会使用Round-Robin负载均衡算法按照Service维护的Endpoint对象中选择一个endpoint(其实就是POD)。

ClusterIP类型的service需要的iptables规则
任何数据包进入宿主机后,首先进入PREROUTING链:
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER

通过服务访问POD时,进入KUBE-SERVICES链,-m comment --comment "kubernetes service portals" 表示匹配规则使用iptables的显示扩展的注释功能(使用额外的匹配机制)
kube-proxy进程在启动时或者监听到Service和Endpoint发生变化之后,会为每个endpoint修改本机Iptables的NAT表中的4条规则链

k8s负载均衡

目前,kubernetes中的负载均衡?致可以分为以下几种机制:

1.Service(NodePort): 直接用Service提供cluster内部的负载均衡,通过在集群的每个node上暴露一个端口,然后将这个端口映射到某个具体的service来实现的,虽然每个node的端口有很多(默认的取值范围是 30000-32767),但是由于安全性和易用性(服务多了就乱了,还有端口冲突问题)实际使用可能并不多。

2.Ingress Controller: 还是用Service提供cluster内部的负载均衡,但是通过自定义LB提供外部访问。

3.Service Load Balancer:通过向底层云平台申请创建一个负载均衡器来向外暴露服务;

4.Custom Load Balancer:自定义负载均衡,并替代kube-proxy,一般在物理部署Kubernetes时使用,方便接入公司已有的外部服务。

Service

Service是对一组提供相同功能的Pods的抽象,并为它们提供一个统一的入口。借助Service,应用可以方便的实现服务发现与负载均衡,并实现应用的零宕机升级。
Service通过标签来选取服务后端,一般配合Replication Controller或者Deployment 来保证后端容器的正常运行。
Service有三种类型:

1.Cluster Ip: 默认类型,自动分配一个仅cluster内部可以访问的虚拟IP
2.NodePort : 在ClusterIp基础上为Service在每台机器上绑定一个端口,这样可以通过<NodeIP>:NodePort来访问该服务。
3.LoadBalancer:在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到<NodeIP>:NodePort

Ingress Controller

Service虽然解决了服务发现和负载均衡的问题,但对外访问的时候,NodePort类型需要在外部搭建额外的负载均衡,而LoadBalancer需要Kubernetes必须跑在支持的cloud provider上面。 Ingress就是为了解决这些限制的。

Ingress 是一个规则的集合,它允许集群外的流量通过一定的规则到达集群内的 Service 。 request--->ingress--->service.

Ingress由三个组件组成:

1.反向代理负载均衡器:即常见的负载均衡软件,如 nginx、Haproxy 等

2.Ingress Controller: kubernetes API 进行交互,实时的感知后端 service、pod 等变化, Ingress Controller 再结合下文的 Ingress 生成配置,
  然后更新反向代理负载均衡器,并刷新其配置,实现动态服务发现与更新

3.Ingress:规则集合;定义了域名与Kubernetes的service的对应关系;这个规则将与 Ingress Controller 结合, 
  Ingress Controller 将其动态写入到负载均衡器配置中,从而实现整体的服务发现和负载均衡。

Service Load Balancer

在Ingress出现以前,Service Load Balancer谁推荐的解决Service局限性的方式。Service Load Balancer将haproxy跑在容器中,并监控service和endpoint的变化,通过容器IP对外提供4层和7层负载均衡服务。

Custom Load Balancer

自定义组件,代替kube-proxy来做负载均衡。如nginx plus, kube2haproxy等。

Endpoints

有几种情况需要用到没有selector的service
1.使用kubernetes集群外部的数据库时。
2.service中用到了其它namespace或kubernetes集群中的service
3.在kubernetes的工作负载与集群外的后端之间互相迁移。

Traefik

Traefik是款开源的反向代理与负载均衡具。它最大的优点是能够与常用的微服务系统直接整合,可以实现自动化动态配置。
目前持有Docker,Swarm, Mesos/Marathon, Mesos, Kubernetes, Consul, Etcd, Zookeeper,BoltDB, Rest API等等后端模型。

在这里插入图片描述

ClusterIP Service(内部)
在这里插入图片描述

NodePort Service(由内而外)
在这里插入图片描述

LoadBalance Service(外部)
在这里插入图片描述

Ingress(外部)

在这里插入图片描述

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值