容器技术(六):K8S容器编排基础 |
您所在的位置:网站首页 › 怎么加入相关微信群 › 容器技术(六):K8S容器编排基础 |
K8S是如何实现容器编排的?本文介绍了K8S的基本架构,常见的资源对象,让读者能够理解K8S容器编排的基本原理。 K8S基本架构目前主流的集群资源管理与使用框架大多都是主从(Master/Worker)模式,即一个Master管理一堆Worker去执行任务,对使用者屏蔽集群中结点之前相互通信的复杂细节,可以使用户像操作单机一样去操控整个集群。 K8S也不例外,在K8S中由master负责集群中应用的调度、更新、扩缩容等操作。 K8S中的执行角色为Node,一个Node一般是一个虚拟机或物理机,它上面事先运行着 docker 服务和 kubelet 服务( Kubernetes 的一个组件), 当接收到 master 下发的"任务"后,Node 就要去完成任务(用 docker 运行一个指定的应用)。 Master 上的主要组件有: apiserver:提供了集群资源操作的统一接口,它通过与每个节点上的kubelet组件通信来控制节点的运行。kubectl客户端也是通过与apiserver交互来操作集群资源的。此外它还提供了认证、授权、访问控制、API注册和发现等机制。scheduler:负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上,比如运维通过kubectl客户端命令根据推荐算法的Deployment配置文件与镜像启动100个Pod,master接受到请求后,scheduler就会根据每个节点的负载信息,决定将这100个Pod分配到哪些节点。controller manager:master上运行着很多控制器,如节点控制器、副本控制器、端点控制器等,用于故障检测、自动扩展、滚动更新等以保证集群的稳定运行,controller manager为这些控制器提供了一个统一的运行组件跟master密切相关的还有两个组件 etcd:它是一个高性能的分布式数据库,master用它来存储所有node上报的状态信息,实际也可以用其它DB代替。kubectl:它是一个客户端工具,运维通过它可以使用命shell命令来操作K8S集群资源。每个Node主要包含下列一些组件 kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理,它会周期性的向apiserver上报节点信息,使得master可以得知每个节点状态,合理的操作每一个节点。kube-proxy负责为Service提供cluster内部的服务发现和负载均衡,使得可以通过Service的name来访问服务。针对不在同一个节点的pod之间的通信,需要在本地配置路由转发信息,将pod之间的通信转为节点之间的通信,这些配置信息也是由kube-proxy来维护。Kubernetes有一个基于web的用户界面,它可以图表化显示当前集群状态。 K8S网络模型三种IP在K8S的网络中主要会涉及三种IP: Node IP:容器宿主机也就是节点的ip,由路由器分配。Pod IP:K8s在每个Node里虚拟出的局域网,为Node上每个Pod分配一个IP,且同一宿主机下Pod位于相同网段,该地址是实际存在于某个网卡(可以是虚拟设备)上的。Service Cluster IP:由k8s分配给每个service的全局唯一的虚拟ip(VIP),service一般包含若干Pod,可以理解为这些Pod的反向代理。其原理是由kube-proxy通过Iptables规则重新定向到其本地端口,再均衡到后端Pod的。但VIP没有挂接到网络设备,外部不能直接访问。设计原则每个Pod都拥有一个独立的IP地址(IPper Pod),而且假定所有的pod都在一个可以直接连通的、扁平的网络空间中,无论这些Pod是否位于同一个宿主机。并且所有容器之间都是以非NAT(网络地址转换)的方式通信的,即容器的真实地址与看到的地址一致。 Linux网络名词基础网络的命名空间(namespace):Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;docker利用这一特性,实现不容器间的网络隔离。Veth设备对:Veth设备对的引入是为了实现在不同网络命名空间的通信。Iptables/Netfilter:Netfilter负责在内核中执行各种挂接的规则(过滤、修改、丢弃等),运行在内核模式中;Iptables模式是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表;通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。网桥:网桥是一个二层网络设备,通过网桥可以将linux支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信。路由:Linux系统包含一个完整的路由功能,当IP层在处理数据发送或转发的时候,会使用路由表来决定发往哪里。同一Pod内容器间通信同一Pod内的容器共享同一个网络命名空间,它们之间的访问可以用localhost地址 + 容器端口就可以访问。 同一Node中Pod的默认路由都是docker0的地址,由于它们关联在同一个docker0网桥上,地址网段相同,所有它们之间应当是能直接通信的。 不同Node中Pod间通信要满足2个条件: Pod的IP不能冲突; 将Pod的IP和所在的Node的IP关联起来。通过Node IP网络的转发,从而可以让Pod之间可以互相访问。这种转发时通过iptables实现的。 在K8S中,服务一般时以Service的形式对外服务的,它是一组Pod的服务抽象,相当于一组Pod的代理或负载均衡器(Load Balancer),负责将请求分发给某个具体的Pod去处理;Service可以通过IP或域名的方式被其他应用访问,具体暴露方式有四种。 Cluster IP:默认采用的是这种方式,仅限于集群内部访问,外部访问。IP可以自己手动指定,不指定的话服务启动时系统会动态分配。spec: clusterIP: 10.0.0.1 ports: - name: httpNodePort:建立在Cluster IP类型之上,将服务暴露到所有节点的指定端口,访问任意一个node ip加端口号都可以访问到(前提没有指定node调度策略)服务,可以通过apiserver的配置文件查看端口暴露范围,类似于Docker Swarm中的Routing Mesh。这种模式K8S依然会为service分配集群IP地址,并将此作为NodePort的路由目标。配置示例如下,targetPort即为对外暴露的端口。apiVersion: v1 kind: Service metadata: name: test-nodportsvc spec: type: NodePort selector: app: test-deploy ports: - protocol: TCP port: 80 targetPort: 80LoadBalancer:建立在NodePort类型之上,其通过cloud provider提供的负载均衡器将服务暴露到集群外部。与NodePort类型的service的使用方法基本类似,需要指定外部的负载均衡器,示例如下。kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 clusterIP: 10.0.171.239 loadBalancerIP: 78.11.24.19 type: LoadBalancer status: loadBalancer: ingress: - ip: 146.148.47.155ExternalIPs:用于将集群外部的服务以自定义名称的形式发布到集群中以供Pod中的应用程序访问,不需要使用标签选择器关联任何的pod对象,但必须要用spec.externalName属性定义一个CNAME记录用于返回外部真正提供服务的主机的别名,而后通过CHAME记录获取到相关主机的IP地址。配置示例如下,当查询主机 my-service.prod.svc.CLUSTER时,集群的DNS服务将返回一个值为http://my.database.example.com 的 CNAME记录。kind: Service apiVersion: v1 metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.comKube-proxy原理那么Service是如何实现的呢?这就用到了Kube-proxy。它是一个简单的网络代理和负载均衡器,具体来说,是实现了内部从Pod到Service和外部的从NodePort向Service的访问。它有两种实现方式: 用户空间代理:直接通过kuber-proxy实现LB的代理服务,这个是kube-proxy的最初的版本,较为稳定,但是效率也自然不太高。通过采用iptables来实现LB,这是目前kube-proxy默认的方式。其原理如下:Kube-proxy是实现了流量从ClusterIp到pod的转发,然而在实际编码的时候,我们更希望能通过固定的域名来访问服务。Kube-dns就是用来为kubernetes service分配子域名的,在集群中可以直接通过名称访问service;通常kube-dns会为service赋予一个名为“service名称.namespace.svc.cluster.local”的A记录,用来解析service的clusterip。 Kube-dns主要(1.4版本之后)由“Kubedns、dnsmasq、exechealthz”三个组件组成,它们的功能如下。 Kubedns 服务端口是10053,主要作用有: 接入SkyDNS,为dnsmasq提供查询服务。替换etcd容器,使用树形结构在内存中保存DNS记录。通过K8S API监视Service资源变化并更新DNS记录。Dnsmasq Dnsmasq是一款小巧的DNS配置工具,其Dockerfile在GitHub上Kubernetes组织的contrib仓库中,位于dnsmasq目录下。它在kube-dns中的作用是: 通过kubedns容器获取DNS规则,在集群中提供DNS查询服务提供DNS缓存,提高查询性能降低kubedns容器的压力、提高稳定性在kube-dns插件的编排文件中可以看到,dnsmasq通过参数–server=127.0.0.1:10053指定upstream为kubedns。 Exechealthz 源码同样在contrib仓库中,位于exec-healthz目录下,主要作用是提供健康检查功能。 K8S常见资源对象极其使用方式Pod:Pod是一组容器(当然也可以只有一个),是K8S最小调度单位。容器本身就是一个小盒子了,Pod相当于在容器上又包了一层小盒子。这个盒子里面的容器: 可以直接通过volume共享存储。有相同的网络空间,通俗点说就是有一样的ip地址,有一样的网卡和网络设置。 多个容器之间可以“了解”对方,比如知道其他人的镜像,知道别人定义的端口等。那么Pod是如何被创建的了?这就需要Deployment配置文件。 Deployment是一个抽象的概念,可以说是应用管理者。 K8S是对容器进行编排的,如果使用它我们要事先准备好docker镜像。有了镜像之后,一般我们会通过Kubernetes的Deployment的配置文件去描述应用,比如应用叫什么名字、使用的镜像名字、要运行几个实例、需要多少内存、cpu资源等。示例如下 apiVersion: apps/v1 kind: Deployment metadata: name: web spec: selector: matchLabels: app: web replicas: 2 template: metadata: labels: app: web spec: containers: - name: web image: registry.cn-hangzhou.aliyuncs.com/liuyi01/tomcat:8.0.51-alpine ports: - containerPort: 8080有了配置文件就可以通过Kubernetes提供的命令行客户端-kubectl去创建这个应用的pod实例了。比如应用的Deployment配置文件叫app.yaml,我们就可以通过命令 kubectl create -f app.yaml来启动这个应用的pod实例,之后就由Kubernetes来保证我们的应用处于预期的工作状态,当某个pod实例运行失败了或者运行着应用的Node突然宕机了,Kubernetes会根据节点心跳信息发现这个问题,并在新的可用Node上调度一个新的实例。 Service:这个我们前面已经介绍了,它是一组pod的代理。那么Service是如何知道它负责哪些Pod呢?是如何跟踪这些Pod变化的? 最直接的方法是使用Deployment的名字。一个Service对应一个Deployment。当然这样确实可以实现。也可以使用了Label标签,再Deployment配置文件中给Pod打上标签,然后再Service的配置文件中指定其负责的pod的标签,这样Service不仅可以只负责一个Deployment的Pod也可以负责多个Deployment的Pod。 例如前面的Deployment配置文件中指定了其创建的pod的标签为app=web,现在我们创建一个Service,并指定其负责的pod的标签为app=web。相应的配置文件如下 apiVersion: v1 kind: Service metadata: name: web spec: ports: - port: 80 # 服务端口 protocol: TCP targetPort: 8080 # 容器端口 selector: app: web # 标签选择器,这里选择app=web的pod作为这个Service的代理对象假设配置文件的名称是app.yaml,通过下列kubectl命令就可以启动服务。 kubectl create -f app.yaml此外,对于需要升级的服务,K8S可以提供RollingUpdate(滚动升级)的解决方案,即一边增加新版本应用的实例数,一边减少旧版本应用的实例数,直到新版本的实例数达到预期,旧版本的实例数减少为0。在整个升级过程中,服务一直处于可用状态。并且可以在任意时刻回滚到旧版本。 Job:Service主要用于一些长期运行的任务,如web应用,数据库服务等。而Job则相反,它适合于一些非长期运行的任务,比如数据处理之类,运行完成后对应的pod会被停止,释放硬件资源。 |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |