来自 电脑知识 2019-09-20 23:05 的文章
当前位置: 威尼斯国际官方网站 > 电脑知识 > 正文

威尼斯国际官方网站docker容器技术,Kubernetes架构

  大家都知道历史上有段佳话叫“司马相如和卓文君”。“皑如山上雪,皎若云间月”。卓文君这么美,却也抵不过多情女儿薄情郎。

Kubernetes是Google开源的容器集群管理系统,其提供应用部署、维护、 扩展机制等功能,利用Kubernetes能方便地管理跨机器运行容器化的应用,是Docker分布式系统的解决方案。k8s里所有的资源都可以用yaml或Json定义。

  司马相如因一首《子虚赋》得汉武帝赏识,飞黄腾达之后便要与卓文君“故来相决绝”,寄来给家乡留守的妻子一封《两地书》,上面只有一行数字:“一二三四五六七八九十百千万。”意义是:无亿,我已经无意于你啦。

1 K8s基本概念

  卓文君看了这封信也不示弱,回了一首《怨郎诗》,司马相如看了发现虽然我是靠写诗吃饭的。要说写诗还是我媳妇厉害,于是亲自将卓文君迎回长安。

1.1 Master

Master节点负责整个集群的控制和管理,所有的控制命令都是发给它,上面运行着一组关键进程:

  • kube-apiserver:提供了HTTP REST接口,是k8s所有资源增删改查等操作的唯一入口,也是集群控制的入口。
  • kube-controller-manager:所有资源的自动化控制中心。当集群状态与期望不同时,kcm会努力让集群恢复期望状态,比如:当一个pod死掉,kcm会努力新建一个pod来恢复对应replicas set期望的状态。
  • kube-scheduler:负责Pod的调度。

实际上,Master只是一个名义上的概念,三个关键的服务不一定需要运行在一个节点上。


  卓文君其实是个二婚。头婚的丈夫结婚不久就死了。这估计也是司马相如后来想对她始乱终弃的原因之一。但是文君老奶奶是有实力的。人家第一敢连夜私奔,第二还hold得住老公。就我这文采,你想不要我,你大Boss汉武帝都会不高兴的。

1.1.1 API Server的原理

集群中的各个功能模块通过 apiserver将信息存储在Etcd,当需要修改这些信息的时候通过其REST接口来实现。


  正是卓文君的才智,不仅成就了她自己,更成就了他老公。诗圣杜甫都用“茂陵多病后,尚爱卓文君”来赞美他们的爱情。如果司马相如娶了别人,那曲《凤求凰》就可以看出,骨子里胡兰成一样的人物。

1.1.2 Controller Manager的原理

内部包含:

  • Replication Controller
  • Node Controller
  • ResourceQuota Controller
  • Namespace Controller
  • ServiceAccount Controller
  • Token Controller
  • Service Controller
  • Endpoint Controller等

这些Controller通过API Server实时监控各个资源的状态,当有资源因为故障导致状态变化,Controller就会尝试将系统由“现有状态”恢复到“期待状态”。


  今天的主题不是爱情不是诗,咱们用《两地书》来谈谈K8s基础知识关键词:

1.1.3 Scheduler的原理

作用是将apiserver或controller manager创建的Pod调度和绑定到具体的Node上,一旦绑定,就由Node上的kubelet接手Pod的接下来的生命周期管理。


  一个目标:容器操作;两地三中心;四层服务发现;五种Pod共享资源;六个CNI常用插件;七层负载均衡;八种隔离维度;九个网络模型原则;十类IP地址;百级产品线;千级物理机;万级容器;相如无亿,K8s有亿:亿级日服务人次。

1.2 Node

Node是工作负载节点,运行着Master分配的负载(Pod),但一个Node宕机时,其上的负载会被自动转移到其他Node上。其上运行的关键组件是:

  • kubelet:负责Pod的生命周期管理,同时与Master密切协作,实现集群管理的基本功能。
  • kube-proxy:实现Service的通信与负载均衡机制的重要组件,老版本主要通过设置iptables规则实现,新版1.9基于kube-proxy-lvs 实现。
  • Docker Engine:Docker引擎,负责Docker的生命周期管理。

威尼斯国际官方网站 1

2018030420563054.jpg

一个目标:容器操作
Kubernetes(k8s)是自动化容器操作的开源平台。这些容器操作包括:部署,调度和节点集群间扩展。
具体功能:
自动化容器部署和复制。
实时弹性收缩容器规模。
容器编排成组,并提供容器间的负载均衡。
调度:容器在哪个机器上运行。
组成:
kubectl:客户端命令行工具,作为整个系统的操作入口。
kube-apiserver:以REST API服务形式提供接口,作为整个系统的控制入口。
kube-controller-manager:执行整个系统的后台任务,包括节点状态状况、Pod个数、Pods和Service的关联等。
kube-scheduler:负责节点资源管理,接收来自kube-apiserver创建Pods任务,并分配到某个节点。
etcd:负责节点间的服务发现和配置共享。
kube-proxy:运行在每个计算节点上,负责Pod网络代理。定时从etcd获取到service信息来做相应的策略。
kubelet:运行在每个计算节点上,作为agent,接收分配该节点的Pods任务及管理容器,周期性获取容器状态,反馈给kube-apiserver。
DNS:一个可选的DNS服务,用于为每个Service对象创建DNS记录,这样所有的Pod就可以通过DNS访问服务了。
下面是K8s的架构拓扑图:
威尼斯国际官方网站 2

1.2.1 kube-proxy的原理

每个Node上都运行着一个kube-proxy进程,它在本地建立一个SocketServer接收和转发请求,可以看作是Service的透明代理和负载均衡器,负载均衡策略模式是Round Robin也可以设置会话保持,策略使用的是“ClientIP”,将同一个ClientIP的请求转发同一个Endpoint上。

Service的Cluster IP和NodePort等概念都是kube-proxy服务通过Iptables的NAT转换实现,Iptables机制针对的是kube-proxy监听的端口,所以每个Node上都要有kube-proxy。

威尼斯国际官方网站 3

20180304205645562.png


两地三中心
两地三中心包括本地生产中心、本地灾备中心、异地灾备中心。
威尼斯国际官方网站 4
两地三中心要解决的一个重要问题就是数据一致性问题。k8s使用etcd组件作为一个高可用、强一致性的服务发现存储仓库。用于配置共享和服务发现。
它作为一个受到Zookeeper和doozer启发而催生的项目。除了拥有他们的所有功能之外,还拥有以下4个特点:
简单:基于http+json的api让你用curl命令就可以轻松使用。
安全:可选SSL客户认证机制。
快速:每个实例每秒支持一千次写操作。
可信:使用Raft算法充分实现了分布式。

1.2.2 kubelet原理

每个Node都会启动一个kubelet,主要作用有:

(1)Node管理

  • 注册节点信息;
  • 通过cAdvisor监控容器和节点的资源;
  • 定期向Master(实际上是apiserver)汇报本节点资源消耗情况

(2)Pod管理

所以非通过apiserver方式创建的Pod叫Static Pod,这里我们讨论的都是通过apiserver创建的普通Pod。kubelet通过apiserver监听etcd,所有针对Pod的操作都会被监听到,如果其中有涉及到本节点的Pod,则按照要求进行创建、修改、删除等操作。

(3)容器健康检查

kubelet通过两类探针检查容器的状态:

LivenessProbe:判断一个容器是否健康,如果不健康则会删除这个容器,并按照restartPolicy看是否重启这个容器。实现的方式有ExecAction(在容器内部执行一个命令)、TCPSocketAction(如果端口可以被访问,则健康)、HttpGetAction(如果返回200则健康)。

ReadinessProbe:用于判断容器是否启动完全。如果返回的是失败,则Endpoint Controller会将这个Pod的Endpoint从Service的Endpoint列表中删除。也就是,不会有请求转发给它。


四层服务发现
先一张图解释一下网络七层协议:
威尼斯国际官方网站 5
k8s提供了两种方式进行服务发现:
环境变量:当创建一个Pod的时候,kubelet会在该Pod中注入集群内所有Service的相关环境变量。需要注意的是,要想一个Pod中注入某个Service的环境变量,则必须Service要先比该Pod创建。这一点,几乎使得这种方式进行服务发现不可用。
比如,一个ServiceName为redis-master的Service,对应的ClusterIP:Port为10.0.0.11:6379,则对应的环境变量为:
威尼斯国际官方网站 6
DNS:可以通过cluster add-on的方式轻松的创建KubeDNS来对集群内的Service进行服务发现。
以上两种方式,一个是基于tcp,众所周知,DNS是基于UDP的,它们都是建立在四层协议之上。

1.3 Pod

Pod是k8s进行资源调度的最小单位,每个Pod中运行着一个或多个密切相关的业务容器这些业务容器共享这个Pause容器的IP和Volume,我们以这个不易死亡的Pause容器作为Pod的根容器,以它的状态表示整个容器组的状态。一个Pod一旦被创建就会放到Etcd中存储,然后由Master调度到一个Node绑定,由这个Node上的Kubelet进行实例化。

每个Pod会被分配一个单独的Pod IP,Pod IP + ContainerPort 组成了一个Endpoint


五种Pod共享资源
Pod是K8s最基本的操作单元,包含一个或多个紧密相关的容器,一个Pod可以被一个容器化的环境看作应用层的“逻辑宿主机”;一个Pod中的多个容器应用通常是紧密耦合的,Pod在Node上被创建、启动或者销毁;每个Pod里运行着一个特殊的被称之为Volume挂载卷,因此他们之间通信和数据交换更为高效,在设计时我们可以充分利用这一特性将一组密切相关的服务进程放入同一个Pod中。
威尼斯国际官方网站 7
同一个Pod里的容器之间仅需通过localhost就能互相通信。一个Pod中的应用容器共享五种资源:
PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。
网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。
IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。
UTS命名空间:Pod中的多个容器共享一个主机名。
Volumes(共享存储卷):Pod中的各个容器可以访问在Pod级别定义的Volumes。
Pod的生命周期通过Replication Controller来管理;通过模板进行定义,然后分配到一个Node上运行,在Pod所包含容器运行结束后,Pod结束。
Kubernetes为Pod设计了一套独特的网络配置,包括:为每个Pod分配一个IP地址,使用Pod名作为荣期间通信的主机名等。

1.4 Service

K8s中一个Service相当于一个微服务的概念,一个Service对应后端多个Pod计算实例,使用LabelSelector将一类Pod都绑定到自己上来。一般还会需要一个Deployment或者RC来帮助这个Service来保证这个Service的服务能力和质量。

威尼斯国际官方网站 8

20180304205656579.png

六个CNI常用插件
CNI(Container Network Interface)容器网络接口,是Linux容器网络配置的一组标准和库,用户需要根据这些标准和库来开发自己的容器网络插件。CNI只专注解决容器网络连接和容器销毁时的资源释放,提供一套框架,所以CNI可以支持大量不同的网络模式,并且容易实现。
下面用一张图表示六个CNI常用插件:
威尼斯国际官方网站 9

1.4.1 kube-proxy负载均衡

运行在每个Node上的kube-proxy其实就是一个智能的软件负载均衡器,它负载将发给Service的请求转发到后端对应的Pod,也就是说它负责会话保持和负责均衡。


七层负载均衡
提负载均衡就不得不先提服务器之间的通信。
IDC(Internet Data Center),也可称 数据中心、机房,用来放置服务器。IDC网络是服务器间通信的桥梁。
威尼斯国际官方网站 10
上图里画了很多网络设备,它们都是干啥用的呢?
路由器、交换机、MGW/NAT都是网络设备,按照性能、内外网划分不同的角色。
内网接入交换机:也称为TOR(top of rack),是服务器接入网络的设备。每台内网接入交换机下联40-48台服务器,使用一个掩码为/24的网段作为服务器内网网段。
内网核心交换机:负责IDC内各内网接入交换机的流量转发及跨IDC流量转发。
MGW/NAT:MGW即LVS用来做负载均衡,NAT用于内网设备访问外网时做地址转换。
外网核心路由器:通过静态互联运营商或BGP互联美团统一外网平台。
先说说各层负载均衡:
二层负载均衡:基于MAC地址的二层负载均衡。
三层负载均衡:基于IP地址的负载均衡。
四层负载均衡:基于IP+端口的负载均衡。
七层负载均衡:基于URL等应用层信息的负载均衡。
这里用一张图来说说四层和七层负载均衡的区别:
威尼斯国际官方网站 11
上面四层服务发现讲的主要是k8s原生的kube-proxy方式。K8s关于服务的暴露主要是通过NodePort方式,通过绑定minion主机的某个端口,然后进行pod的请求转发和负载均衡,但这种方式有下面的缺陷:
Service可能有很多个,如果每个都绑定一个node主机端口的话,主机需要开放外围的端口进行服务调用,管理混乱。
无法应用很多公司要求的防火墙规则。
理想的方式是通过一个外部的负载均衡器,绑定固定的端口,比如80,然后根据域名或者服务名向后面的Service ip转发,Nginx很好的解决了这个需求,但问题是如果有的心得服务加入,如何去修改Nginx的配置,并且加载这些配置?Kubernetes给出的方案就是Ingress。这是一个基于7层的方案。

1.4.2 Cluster IP

负载均衡的基础是负载均衡器要维护一个后端Endpoint列表,但是Pod的Endpoint会随着Pod的销毁和重建而改变,k8s使这个问题透明化。一旦Service被创建,就会立刻分配给它一个Cluster IP,在Service的整个生命周期内,这个Cluster IP不会改变。于是,服务发现的问题也解决了:只要用Service Name和Service Cluster IP做一个DNS域名映射就可以了。


八种隔离维度
威尼斯国际官方网站 12
K8s集群调度这边需要对上面从上到下从粗粒度到细粒度的隔离做相应的调度策略。

1.4.3 DNS

从Kubernetes 1.3开始,DNS通过使用插件管理系统cluster add-on,成为了一个内建的自启动服务。Kubernetes DNS在Kubernetes集群上调度了一个DNS PodService,并配置kubelet,使其告诉每个容器使用DNS Service的IP来解析DNS名称。

(1)Service

集群中定义的每个Service(包括DNS Service它自己)都被分配了一个DNS名称。默认的,Pod的DNS搜索列表中会包含Pod自己的命名空间和集群的默认域,下面我们用示例来解释以下。 假设有一个名为foo的Service,位于命名空间bar中。运行在bar命名空间中的Pod可以通过DNS查找foo关键字来查找到这个服务,而运行在命名空间quux中的Pod可以通过关键字foo.bar来查找到这个服务。

普通(非headless)的Service都被分配了一个DNS记录,该记录的名称格式为my-svc.my-namespace.svc.cluster.local,通过该记录可以解析出服务的集群IP。 Headless(没有集群IP)的Service也被分配了一个DNS记录,名称格式为my-svc.my-namespace.svc.cluster.local。与普通Service不同的是,它会解析出Service选择的Pod的IP列表。

(2)Pod

Pod也可以使用DNS服务。pod会被分配一个DNS记录,名称格式为pod-ip-address.my-namespace.pod.cluster.local。 比如,一个pod,它的IP地址为1.2.3.4,命名空间为default,DNS名称为cluster.local,那么它的记录就是:1-2-3-4.default.pod.cluster.local。 当pod被创建时,它的hostname设置在Pod的metadata.name中。

在v1.2版本中,用户可以指定一个Pod注解,pod.beta.kubernetes.io/hostname,用于指定Pod的hostname。这个Pod注解,一旦被指定,就将优先于Pod的名称,成为pod的hostname。比如,一个Pod,其注解为pod.beta.kubernetes.io/hostname: my-pod-name,那么该Pod的hostname会被设置为my-pod-name。 v1.2中还引入了一个beta特性,用户指定Pod注解,pod.beta.kubernetes.io/subdomain,来指定Pod的subdomain。比如,一个Pod,其hostname注解设置为“foo”,subdomain注解为“bar”,命名空间为“my-namespace”,那么它最终的FQDN就是“foo.bar.my-namespace.svc.cluster.local”。 在v1.3版本中,PodSpec有了hostnamesubdomain字段,用于指定Pod的hostname和subdomain。它的优先级则高于上面提到的pod.beta.kubernetes.io/hostnamepod.beta.kubernetes.io/subdomain


九个网络模型原则
K8s网络模型要符合4个基础原则,3个网络要求原则,1个架构原则,1个IP原则。
每个Pod都拥有一个独立的IP地址,而且假定所有Pod都在一个可以直接连通的、扁平的网络空间中,不管是否运行在同一Node上都可以通过Pod的IP来访问。
K8s中的Pod的IP是最小粒度IP。同一个Pod内所有的容器共享一个网络堆栈,该模型称为IP-per-Pod模型。
Pod由docker0实际分配的IP,Pod内部看到的IP地址和端口与外部保持一致。同一个Pod内的不同容器共享网络,可以通过localhost来访问对方的端口,类似同一个VM内不同的进程。
IP-per-Pod模型从端口分配、域名解析、服务发现、负载均衡、应用配置等角度看,Pod可以看做是一台独立的VM或物理机。
所有容器都可以不用NAT的方式同别的容器通信。
所有节点都可以在不同NAT方式下同所有容器心痛,反之亦然。
容器的地址和别人看到的地址是同一个地址。
要符合下面的架构:
威尼斯国际官方网站 13

1.4.4 外部访问Service的问题

先明确这样几个IP:

  • Node IP:Node主机的IP,与它是否属于K8s无关。
  • Pod IP:是Dokcer Engine通过docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络。k8s中一个Pod访问另一个Pod就是通过Pod IP。
  • Cluster IP:仅用于Service对象,属于k8s的内部IP,外界无法直接访问。

(1)NodePort

在Service的yaml中定义NodePort,k8s为集群中每个Node都增加对这个端口的监听,使用这种方式往往需要一个独立与k8s之外的负载均衡器作为流量的入口。

(2)使用External IP

  • 运行Hello World应用程序的五个实例。
  • 创建一个暴露外部IP地址的Service对象。
  • 使用Service对象访问正在运行的应用程序。

使用deployment创建暴露的Service对象:

~ kubectl expose deployment hello-world --type=LoadBalancer --name=my-service

显示关于Service的信息:

~ kubectl get services my-service

 NAME         CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
 my-service   10.3.245.137   104.198.205.71   8080/TCP   54s

~  kubectl describe services my-service
 Name:           my-service
 Namespace:      default
 Labels:         run=load-balancer-example
 Selector:       run=load-balancer-example
 Type:           LoadBalancer
 IP:             10.3.245.137
 LoadBalancer Ingress:   104.198.205.71
 Port:           <unset> 8080/TCP
 NodePort:       <unset> 32377/TCP
 Endpoints:      10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080 + 2 more...
 Session Affinity:   None
 Events:

在此例子中,外部IP地址为104.198.205.71。还要注意Port的值。在这个例子中,端口是8080。在上面的输出中,您可以看到该服务有多个端点:10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080

  • 2 more…。这些是运行Hello World应用程序的pod的内部地址。

使用外部IP地址访问Hello World应用程序:

~  curl http://<external-ip>:<port>
 Hello Kubernetes!

删除服务

~ kubectl delete services my-service
~ kubectl delete deployment hello-world

本文由威尼斯国际官方网站发布于电脑知识,转载请注明出处:威尼斯国际官方网站docker容器技术,Kubernetes架构

关键词: