深入剖析Kubernetes
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入剖析Kubernetes相关的知识,希望对你有一定的参考价值。
参考技术A 容器,其实是一种特殊的进程而已。很多人会把Docker项目称为轻量级虚拟化技术的原因,实际上就是把虚拟机的概念套用在了容器上。Linux 容器中用来实现“隔离”的技术手段:Namespace。Namespace 技术实际上修改了应用进程看待整个计算机“视图”,即它的“视线”被操作系统做了限制,只能“看到”某些指定的内容。“敏捷”和“高性能”是容器相较于虚拟机最大的优势,也是它能够在 PaaS 这种更细粒度的资源管理平台上大行其道的重要原因。
基于 Linux Namespace 的隔离机制相比于虚拟化技术也有很多不足之处,其中最主要的问题就是:隔离得不彻底。
首先,既然容器只是运行在宿主机上的一种特殊的进程,那么多个容器之间使用的就还是同一个宿主机的操作系统内核。其次,在 Linux 内核中,有很多资源和对象是不能被 Namespace 化的,最典型的例子就是:时间。
容器的“限制”问题
Linux Cgroups 就是 Linux 内核中用来为进程设置资源限制的一个重要功能。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs。
通过“分层镜像”的设计,以 Docker 镜像为核心,来自不同公司、不同团队的技术人员被紧密地联系在了一起。而且,由于容器镜像的操作是增量式的,这样每次镜像拉取、推送的内容,比原本多个完整的操作系统的大小要小得多;而共享层的存在,可以使得所有这些容器镜像需要的总空间,也比每个镜像的总和要小。这样就使得基于容器镜像的团队协作,要比基于动则几个 GB 的虚拟机磁盘镜像的协作要敏捷得多。
一个进程,可以选择加入到某个进程已有的 Namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理。
Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。
一个正确的时机,进行一次绑定挂载,Docker 就可以成功地将一个宿主机上的目录或文件,不动声色地挂载到容器中。
在整个“开发 - 测试 - 发布”的流程中,真正承载着容器信息进行传递的,是容器镜像,而不是容器运行时。这个重要假设,正是容器技术圈在 Docker 项目成功后不久,就迅速走向了“容器编排”这个“上层建筑”的主要原因:作为一家云服务商或者基础设施提供商,我只要能够将用户提交的 Docker 镜像以容器的方式运行起来,就能成为这个非常热闹的容器生态图上的一个承载点,从而将整个容器技术栈上的价值,沉淀在我的这个节点上。
从一个开发者和单一的容器镜像,到无数开发者和庞大的容器集群,容器技术实现了从“容器”到“容器云”的飞跃,标志着它真正得到了市场和生态的认可。
容器就从一个开发者手里的小工具,一跃成为了云计算领域的绝对主角;而能够定义容器组织和管理规范的“容器编排”技术,则当仁不让地坐上了容器技术领域的“头把交椅”。
Kubernetes 项目依托着 Borg 项目的理论优势,才在短短几个月内迅速站稳了脚跟,进而确定了一个如下图所示的全局架构:
在 Kubernetes 项目中,kubelet 主要负责同容器运行时(比如 Docker 项目)打交道。
此外,kubelet 还通过 gRPC 协议同一个叫作 Device Plugin 的插件进行交互。
而 kubelet 的另一个重要功能,则是调用网络插件和存储插件为容器配置网络和持久化存储
从一开始,Kubernetes 项目就没有像同时期的各种“容器云”项目那样,把 Docker 作为整个架构的核心,而仅仅把它作为最底层的一个容器运行时实现。
Kubernetes 项目最主要的设计思想是,从更宏观的角度,以统一的方式来定义任务之间的各种关系,并且为将来支持更多种类的关系留有余地。
在 Kubernetes 项目中,这些容器则会被划分为一个“Pod”,Pod 里的容器共享同一个 Network Namespace、同一组数据卷,从而达到高效率交换信息的目的。Pod 是 Kubernetes 项目中最基础的一个对象。Kubernetes 项目的做法是给 Pod 绑定一个 Service 服务,而 Service 服务声明的 IP 地址等信息是“终生不变”的。这个 Service 服务的主要作用,就是作为 Pod 的代理入口(Portal),从而代替 Pod 对外暴露一个固定的网络地址。
围绕着容器和 Pod 不断向真实的技术场景扩展,我们就能够摸索出一幅如下所示的 Kubernetes 项目核心功能的“全景图”。
在 Kubernetes 项目中,我们所推崇的使用方法是:首先,通过一个“编排对象”,比如 Pod、Job、CronJob 等,来描述你试图管理的应用;然后,再为它定义一些“服务对象”,比如 Service、Secret、Horizontal Pod Autoscaler(自动水平扩展器)等。这些对象,会负责具体的平台级功能。这种使用方法,就是所谓的“声明式 API”。这种 API 对应的“编排对象”和“服务对象”,都是 Kubernetes 项目中的 API 对象(API Object)。
Kubernetes——Service(SVC)服务
Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector
Service能够提供负载均衡的能力,但是在使用上有以下限制:
Service 在 K8s 中有以下四种类型
svc基础导论
在 Kubernetes 集群中,每个 Node 运行一个kube-proxy进程。kube-proxy负责为Service实现了一种VIP(虚拟 IP)的形式,而不是ExternalName的形式。在 Kubernetes v1.0 版本,代理完全在 userspace。在Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。从 Kubernetes v1.2 起,默认就是iptables 代理。在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理
在 Kubernetes 1.14 版本开始默认使用ipvs 代理
在 Kubernetes v1.0 版本,Service是 “4层”(TCP/UDP over IP)概念。在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务
为何不使用 round-robin DNS?
DNS会在很多的客户端里进行缓存,很多服务在访问DNS进行域名解析完成、得到地址后不会对DNS的解析进行清除缓存的操作,所以一旦有他的地址信息后,不管访问几次还是原来的地址信息,导致负载均衡无效。
这种模式,kube-proxy 会监视 Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs 规则并定期与 Kubernetes Service对象和Endpoints对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod
与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
注意: ipvs模式假定在运行kube-proxy之前在节点上都已经安装了IPVS内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点上是否安装了IPVS模块,如果未安装,则kube-proxy将回退到iptables代理模式
clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口
为了实现图上的功能,主要需要以下几个组件的协同工作:
创建 myapp-deploy.yaml 文件
创建 Service 信息
有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定 ClusterIP(spec.clusterIP)的值为“None”来创建 Headless Service。这类Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由
主要的特点是通过无头服务的方式去解决hostname和portname的变化问题,也就是通过它去进行绑定
对于svc,一旦创建成功以后,它会写入到coreDNS中去,我们的svc的创建会有一个主机名会被写入到coreDNS,写入的格式体就是 svc的名称+命名空间的名称+当前集群的域名
意味着在无头服务中,虽然它没有ip了,但可以通过访问域名的方案依然可以访问服务下的pod
nodePort的原理在于在node上开了一个端口,将向该端口的流量导入到kube-proxy,然后由 kube-proxy进一步到给对应的pod
loadBalancer和nodePort 其实是同一种方式。区别在于 loadBalancer 比nodePort多了一步,就是可以调用cloud provider【云供应商】 去创建LB【负载均衡】来向节点导流
这种类型的 Service 通过返回 CNAME和它的值,可以将服务映射到externalName字段的内容(例如:hub.yibo.cn)。ExternalName Service 是Service的特例,它没有 selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
当查询主机 my-service.defalut.svc.cluster.local(SVC_NAME.NAMESPACE.svc.cluster.local)时,集群的DNS 服务将返回一个值my.database.example.com的CNAME记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在DNS层,而且不会进行代理或转发。
iptables详解: http://www.zsythink.net/archives/1199/
ipvs详解: https://www.cnblogs.com/hongdada/p/9758939.html
LVS详解: https://blog.csdn.net/Ki8Qzvka6Gz4n450m/article/details/79119665
参考:
https://www.cnblogs.com/LiuQizhong/p/11573173.html
https://www.cnblogs.com/fanqisoft/p/11579061.html
参考技术ASVC主要有以下两个作用:
一、服务发现
现在工作当中都将微服务项目部署到K8S上,因为每个项目都是很多个服务的集合,每个服务一般又都是由很多个pod组成的,那么当请求想要访问这个服务的时候如何将请求能够很好地找到这些POD并将请求分发给他们呢?
即使是同一组服务他们的pod是在集群的不同位置的,Ip也就各不相同,SVC就可以有效地将同一组服务绑定在一起,也就是提供了一个统一的服务访问的入口,无论他们分发到哪个节点,也无论他们被分发了多少个不同的IP,SVC都可以做到将请求转发到这组服务的其中一个POD中进行处理,k8s在创建SVC时候,会根据标签选择器selector(Lable selector)来查找pod,据此创建与SVC同名的endpoint对象,当pod地址发生变化时,endpoint也会随之发生变化,SVC接收到前端client请求的时候,就会通过endpoint,找到要转发到哪个Pod进行访问网站的地址。
二、负载均衡
因为每个SVC都是通过Label绑定微服务当中其中一个服务的一组POD,当外部或者集群其他服务发来请求时,SVC会通过负载均衡,将请求分发到这一组POD当中的其中一个。
以上是关于深入剖析Kubernetes的主要内容,如果未能解决你的问题,请参考以下文章
深入YARN系列3:剖析NodeManager架构,组件与生产应用
深入YARN系列3:剖析NodeManager架构,组件与生产应用