关于 K8s 的一些基础概念整理
〇、前言
Kubernetes,将中间八个字母用数字 8 替换掉简称 k8s,是一个开源的容器集群管理系统,由谷歌开发并维护。它为跨主机的容器化应用提供资源调度、服务发现、高可用管理和弹性伸缩等功能。
下面简单列一下 k8s 的几个特性:
自动化部署:Kubernetes 可以根据应用程序计算资源需求自动分配到 node。
服务发现和负载均衡:Kubernetes 可以利用 DNS 名称或自己的 IP 地址暴露容器,如果到一个容器的流量过大,Kubernetes 能够负载均衡和分发网络流量,以保证部署稳定。
自动化容器扩容和缩容:根据 CPU 使用情况或其他选择的度量标准,Kubernetes 可以自动扩展或缩小运行的容器数量。
自我修复:当一个容器失败时,Kubernetes 会重新启动它;当节点失败时,它会替换和重新调度容器;当容器没有通过用户定义的健康检查时,它会杀死它。只有当容器准备好服务时,才会将其视为可用。
密钥和配置管理:Kubernetes Secrets 可以用来存储和管理敏感信息,如密码、OAuth 令牌和 SSH 密钥等。而 Kubernetes ConfigMaps 则可以用来存储和管理配置信息,如 Prometheus 配置文件、数据库连接字符串等。
一、关于 k8s 的一些概念解释
1.1 Container(Namespace、Cgroup)
容器,字面意思就是类似于锅碗瓢盆、瓶瓶罐罐等,总之就是一个装东西的目的。
IT 里的容器技术是英文单词 Linux Container 的直译。Container 这个单词有集装箱、容器的含义(主要偏集装箱意思)。在中文环境下,人们更喜欢用“容器”这个词。不过,如果要形象的理解 Linux Container 技术的话,还是得念成集装箱会比较好。例如,海边码头里的集装箱是运载货物用的,它是一种按规格标准化的钢制箱子。集装箱的特色,在于其格式划一,并可以层层重叠,所以可以大量规整地放置在特别设计的远洋轮船中,大大增加了出货和运输效率,从而更加快捷方便的为生产商提供廉价的运输服务。
在 Container 出现之前,普遍认为硬件抽象层基于 hypervisor 的虚拟化方式可以最大程度上提供虚拟化管理的灵活性。各种不同操作系统的虚拟机都能通过 hypervisor(KVM、XEN 等)来衍生、运行、销毁。然而,随着时间推移,用户发现 hypervisor 这种方式麻烦越来越多。因为对于 hypervisor 环境来说,每个虚拟机都需要运行一个完整的操作系统,以及其中安装好的大量应用程序。但实际生产开发环境里,我们更关注的是自己部署的应用程序,如果每次部署发布我都得搞一个完整操作系统和附带的依赖环境,那么这让任务和性能变得很重和很低下。
Linux Container 容器技术的诞生(2008 年)就解决了 IT 世界里“集装箱运输”的问题。Linux Container(简称 LXC)它是一种内核轻量级的操作系统层虚拟化技术。Linux Container 主要由 Namespace 和 Cgroup 两大机制来保证实现。那么 Namespace 和 Cgroup 是什么呢?
Namespace 的目的就是隔离。就如上面提到的集装箱,它的作用当然是可以对货物进行打包隔离了,不让 A 公司的货跟 B 公司的货混在一起,不然卸货就分不清楚了。一个资源只能在一个命名空间中,且资源的 Names 在 Namespace 中具有唯一性,但不同的 Namespace 中的资源可重名。
Cgroup 就负责资源管理控制作用。光有隔离还不够,我们还需要对货物进行资源的管理。同样的,航运码头也有这样的管理机制:货物用什么样规格大小的集装箱,货物用多少个集装箱,货物哪些优先运走,遇到极端天气怎么暂停运输服务怎么改航道等等。再比如进程组使用 CPU/MEM 的限制,进程组的优先级控制,进程组的挂起和恢复等等。
下边列一下容器技术的特点:
极其轻量:容器只打包了必要的二进制文件和库,不需要包含整个操作系统,这样使得容器更轻量;
秒级部署:根据镜像的不同,容器的部署可以是秒级,这比传统的虚拟机部署方式要快得多;
易于移植:一次构建,随处部署,从而极大减轻了开发和部署工作量,提高开发效率;
安全隔离:容器会在操作系统级别虚拟化 CPU、内存、存储和网络资源,为开发者提供在逻辑上与其他应用相隔离的沙盒化操作系统接口;
弹性伸缩:Kubernetes 这类开源、方便、好使的容器管理平台有着非常强大的弹性管理能力,同时减少资源的消耗冲突。
容器的应用:
持续集成和持续部署(CI/CD)
通过容器可以实现代码的快速构建、测试和部署,提高开发效率,交付速度可提高十几倍。通过持续集成(CI)和持续部署(CD),每次开发人员签入代码并顺利测试之后,IT 团队都能够集成新代码。作为开发运维方法的基础,CI/CD 创造了一种实时反馈回路机制,持续地传输小型迭代更改,从而加速更改过程,提高质量。CI 环境通常是完全自动化的,通过 git 推送命令触发测试,测试成功时自动构建新镜像,然后推送到 Docker 镜像库。通过后续的自动化和脚本,可以将新镜像的容器部署到预演环境,从而进行进一步测试。
微服务架构
在微服务架构中,应用程序被拆分成多个独立的、可伸缩的服务。容器可以帮助将这些服务打包成独立的运行环境,简化部署和管理过程。Docker 的端到端安全功能让团队能够构建和运行最低权限的微服务模型,服务所需的资源(其他应用、涉密信息、计算资源等)会适时被创建并被访问。
IT 基础设施优化,充分利用基础设施,节省资金
容器有助于优化 IT 基础设施的利用率和成本。优化不仅仅是指削减成本,还能确保在适当的时间有效地使用适当的资源。容器是一种轻量级的打包和隔离应用工作负载的方法,所以 Docker 允许在同一物理或虚拟服务器上毫不冲突地运行多项工作负载。企业可以整合数据中心,将并购而来的 IT 资源进行整合,从而获得向云端的可迁移性,同时减少操作系统和服务器的维护工作。
参考:https://www.cnblogs.com/qcloud1001/p/9273549.html
1.2 Pod
Pod 是 k8s 调度的最小单元,包含一个或者多个容器 Container。用户可以通过 k8s 的 Pod API 生产一个 Pod,让 Kubernetes 对这个 Pod 进行调度,也就是把它放在某一个 k8s 管理的节点上运行起来。
Pod 拥有一个唯一的 IP 地址,在包含多个容器的时候,依然是拥有一个 IP 地址。Pod 包含多个容器的时候,用到的就是共享 namespace,容器间就可以通过 localhost 通信了,就像两个进程一样。而 Pod 与 Pod 之间,是互相有 isolation 隔离的,保证其运行环境的一致性和稳定性。
针对 Pod 内部的多个容器,每个容器都需要有自己独立的端口号,以便外部流量能够正确地路由到相应的容器。如果它们都需要被外部访问,那么可以通过项目中的配置文件 service.yml 来实现。通过创建一个 service.yml,可以将 Pod 内部的多个容器的流量聚合到一个统一的 IP 地址和端口上,从而提供统一的访问入口。例如,假设一个 Pod 里面运行了两个容器,一个是 Web 应用,监听 80 端口;另一个是数据库,监听 3306 端口。此时,我们可以创建一个 service.yml,将这两个端口映射到一个指定的端口(比如 7777),然后通过访问 Pod 的 6666 端口,就可以同时访问到 Web 应用和数据库了。
另外,Pod 可以挂载多个共享的存储卷(Volume),这时内部的各个容器就可以访问共享的 Volume 进行数据的读写。例如,如果需要在一个 Pod 中运行两个容器,一个是应用容器,另一个是监控容器,则可以将这两个容器放在同一个 Pod 中。在这种情况下,监控容器可以通过共享内存机制访问应用容器的状态信息,从而减少了应用程序和监控系统之间的耦合度。
1.3 Volume(EmptyDir、HostPath)
先看下容器在 docker 中的情况。
创建容器时,如果没有指定容器的数据卷,容器中的文件在磁盘上通常是临时存放的,当容器崩溃时数据和文件就会丢失,然后 kubelet 会重新启动容器,但容器会以干净的状态重启,这样就造成了数据安全问题。
在 k8s 中,一个 Pod 中可以同时运行多个容器,常常需要在这些容器之间共享文件。
基于以上两个问题,k8s 中就出现了一个抽象概念“卷”(Volumn)这个组件。卷的核心是包含一些数据的目录,Pod 中的容器可以通过配置访问该目录。
以下是 Volume 的几个特点:
Volume 是 k8s 抽象出来的对象,它可以配置在 Pod 上,然后可以被一个 Pod 里的多个容器挂载到具体的文件目录下;
k8s 通过 Volume 实现同一个 Pod 中不同容器之间的数据共享以及数据的持久化存储;
Volume 的生命周期不与 Pod 中单个容器的生命周期相关,当容器终止或者重启时,Volume 中的数据也不会丢失;
k8s 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。
另外,k8s 中也提供了多种类型的 Volume:
常规存储:EmptyDir、HostPath;
高级存储:PV、PVC;
配置存储:ConfigMap、Secret;
其他还有网络存储系统 NFS、CIFS 等,包括云服务商提供的、本地、分布式。
下边详细介绍下两种常规存储:EmptyDir、HostPath。
EmptyDir 适用于临时缓存空间,存储一些运行过程中的中继日志。其特点如下:
当 Pod 指定到某个节点上时,首先创建的是一个 EmptyDir 卷,只要 Pod 在该节点上运行卷就一直存在;
当 Pod 因为某些原因被从节点上删除时,EmptyDir 卷中的数据也会永久删除;
容器崩溃并不会导致 Pod 被从节点上移除,所以容器崩溃时 EmptyDir 卷中的数据是安全的。
下边是一个绑定 EmptyDir 类型路径的 .yml 配置文件的示例:
由于 EmptyDir 创建的这个 Volume 是一个虚拟的路径,所以当其销毁后,Pod 中容器产生的数据也就随之销毁了,即无法真正实现数据的落盘持久化。
HostPath 即主机挂载目录,可以实现数据的落盘持久化。
HostPath 类型的磁盘就是挂在了主机的一个文件或者目录,即容器和宿主机之间的文件共享机制。
通过配置 Volume 的 HostPath 类型,我们可以指定一个位于宿主机上的文件或目录,然后在 Pod 的配置中,将这个目录以 Volume 的形式挂载到容器中的指定路径上。
根据使用场景的不同,HostPath 又可以细分成多个类型:
Directory 给定的目录路径必须存在;
DirectoryOrCreate 如果给定路径不存在,将根据需要在那里创建一个空目录;
File 给定路径上必须存在对应文件;
FileOrCreate 如果给定路径不存在,将根据需要在那里创建一个空文件。
下边是一个绑定 Hostpath 类型路径的 .yml 配置文件的示例:
主要的配置和上面的 EmptyDir 的案例中的差不多,最后的 volumes 类型那里改成 hostPath 相关的参数。
参考:https://www.yisu.com/zixun/772741.html
1.4 Depeloyment、RC、RS
RC(ReplicationController)、RS(ReplicaSet)。
在 k8s 中,RS 和 RC 都是用于实现 Pod 的自动化管理。它们的主要作用如下:
RC 是 k8s 集群中的核心概念之一,它定义了一个期望的场景,即确保指定数量的 Pod 副本始终在运行,并且正在运行的 Pod 副本的数量等于用户指定的数量。
RS 则是一种更高级的 API 对象,可视为是 RC 的增强版,它提供了更多的灵活性和功能。例如,RS 支持自动扩缩容、滚动更新等功能。
RS 和 RC 的主要区别在于选择器的支持。具体来说就是 RS 支持新的基于集合的选择器需求,而 RC 仅支持一个选择器。
官方建议,虽然 RS 可以独立使用,但现在它主要被 Deployments 用作协调 Pod 的创建、删除和更新的机制。
Deployment 是一个更高级的 API 对象,它与 RC 和 RS 的功能类似,但提供了更多的灵活性和功能。例如,Deployment 可以通过控制 RS 来实现自动扩缩容、滚动更新等功能。
当用户创建一个 Deployment 对象并描述一个期望的状态后,Deployment 控制器会生成一个唯一的 RS,让它负责管理 Pod 的生命周期。
虽然在实际的工作中,Deployment 并不是直接控制着 Pod 的,但是为了简化理解过程,我们通常会认为 Deployment 是直接管理 Pod 的。
总的来说就是,Deployment 是用于描述期望状态的对象,而 RC 和 RS 则是用于实现这些期望状态的实际控制器。
1.5 Service(kube-proxy、IPVS、Cluster-IP)
从前几节中介绍的可知,k8s 集群中的每一个 Pod 都有自己的 IP 地址,此时有同学可能会说,有 IP 访问起来不就简单了,其实不然。
因为在 k8s 中 Pod 不是持久性的,摧毁重建将获得新的 IP,客户端通过会变更 IP 来访问显然不合理。另外 Pod 还经常会通过多个副本来实现负载均衡,客户端如何高效的访问哪个副本的问题也显现出来了。那么本章节将要介绍的 Service 对象应运而生。
如下图,当我们通过 API 创建/修改 Service 对象时,Endpoints 控制器的 Informer 机制监听到 Service 对象,然后根据 Service 的配置的选择器创建一个 Endpoints 对象,此对象将 Pod 的 IP、容器端口做记录并存储到 etcd,这样 Service 只要看一下自己名下的 Endpoints 就可以知道所对应 Pod 信息了。
当一个服务的副本有多个时,请求的流向控制也非常重要,此时就用到 kube-proxy 对象了。
kube-proxy 是集群中每个 Node 上运行的网络代理,实现 k8s 服务(Service)概念的一部分。它是一个用于处理单个主机子网划分,并向外部世界公开的服务。它跨集群中的各种隔离网络将请求转发到正确的 Pod / 容器。kube-proxy 维护 Node 上的网络规则,这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。
如下图,kube-proxy 通过 Informer 知道了 Service、Endpoints 对象的创建,然后把 Service 身上的 Cluster-IP 和端口以及端点信息拿出来,创建 IPtable NAT 规则做转发或通过 IPVS 模块创建 VS 服务器,这样经过 Cluster-IP 的流量都被转发到后端 Pod。
kube-proxy 有三种运行模式:Userspace、IPtables 和 IPVS。
在 Userspace 模式下,kube-proxy 通过观察 k8s 中的 Service 和 Endpoint 对象的变化来实现负载均衡。当有新的 Service 创建时,kube-proxy 会创建相应的 NAT 规则并使用 IPtables 的 MARK/PREROUTING 链来实现对 Service 流量的转发。此模式不依赖于内核功能,因此可移植性较好,但是性能相对较差。
IPtables 模式中,kube-proxy 利用 IPtables 强大的数据包处理和过滤能力,实现 NAT 和负载均衡功能。IPtables 是 Linux 内核中的一个功能,它能在核心数据包处理管线中使用 Hook 挂接一系列的规则。然而,这种模式的主要问题是在服务多的时候会产生太多的 IPtables 规则,这会影响查询效率。
IPVS 模式是 kube-proxy 的另一种运行方式,它基于 Netfilter 实现传输层的负载均衡技术,通常被称为第四层 LAN 交换。IPVS 集成在 LVS(Linux Virtual Server)中,运行在主机上,并在真实服务器集群前充当负载均衡器。从 k8s 的 1.8 版本开始,kube-proxy 引入了 IPVS 模式。这种模式虽然与 IPtables 一样基于 Netfilter,但 IPVS 采用 Hash 表而 IPtables 采用一条条的规则列表。因此,当集群规模较大时,IPVS 模式相比 IPtables 模式有更高的查询效率。
k8s 支持四种类型的 Service,分别是 ClusterIP、NodePort、LoadBalancer 和 ExternalName。
ClusterIP 是默认和最常见的服务类型,k8s 会为 ClusterIP 服务分配一个集群内部 IP 地址,只能在集群内部访问,适用于集群内的服务间通信,比如应用程序的前端和后端组件之间的通信。
NodePort 则是将 Service 通过指定的 Node 上的端口暴露给外部,访问任意一个 NodeIP:NodePort 都将路由到 ClusterIP。这种方式适用于需要在集群外进行业务访问的场景。
LoadBalancer 类型是在 NodePort 的基础上,借助 CloudProvider 创建一个外部的负载均衡器,并将请求转发到 NodeIP:NodePort,此模式适用于云服务器上。
ExternalName 类型是将服务映射到 DNS 名称的方式转发到指定的域名,例如通过 spec.externlName 参数指定这些服务。
参考:https://zhuanlan.zhihu.com/p/454836610 https://zhuanlan.zhihu.com/p/157565821
1.6 Master、Node
k8s 里的 Master 指的是集群控制节点,每一个 k8s 集群里都必须要有一个 Master 节点,来负责整个集群的管理和控制,基本上 k8s 的所有控制命令都发给它,它来负责具体的执行过程。
通常,Master 部署在一个独立的服务器上,若想达到高可用性部署,建议用 2~3 台服务器,Master 也可以扩展副本数,来获取更好的可用性和冗余。它是整个集群的“首脑 brain”,如果宕机或者不可用,那么对集群内容器应用的管理都将失效。
Master 节点上运行着以下三个关键进程:
KubernetesAPIServer(kube-apiserver):提供了 HTTP Rest 接口的关键服务进程,是 k8s 里所有资源的增、删、改、查等操作的唯一入口,也是集群控制的入口进程。
KubernetesControllerManager(kube-controller-manager):k8s 里所有资源对象的自动化控制中心,可以理解为资源对象的“大总管”。
KubernetesScheduler(kube-scheduler):负责资源调度(Pod 调度)的进程,安排哪些服务的 Pod 运行在哪些节点上。
另外,在 Master 节点上还需要启动一个 etcd 服务,正如前面讲到的,k8s 里的所有资源对象的数据全部是保存在 etcd 中的。
除了 Master,k8s 集群中的其他机器被称为 Node 节点,在较早的版本中也被称为 Minion。
与 Master 一样,Node 节点可以是一台物理主机或者是虚拟机。Node 节点才是 k8s 集群中的工作负载节点,每个 Node 都会被 Master 分配一些应用程序服务以及云工作流,在有些时候,Master 节点上也会“安排”一些服务运行,或者说是一些 Docker 容器,当某个 Node 宕机时,其上的工作负载会被 Master 自动转移到其他节点上去。
每个 Node 节点上都运行着以下一组关键进程:
kubelet:负责 Pod 对应的容器的创建、启停等任务,同时与 Master 节点密切协作,实现集群管理的基本功能。
kube-proxy:实现 k8s 中 Service 的通信与负载均衡机制的重要组件。
DockerEngine(docker):Docker 引擎,负责本机的容器创建和管理工作。
Node 节点可以在运行期间动态增加到 k8s 集群中,前提是这个节点上已经正确安装、配置和启动了上述关键进程。
在默认情况下,kubelet 会向 Master 注册自己,这也是 k8s 推荐的 Node 管理方式。一旦 Node 被纳入集群管理范围,kubelet 进程就会定时向 Master 节点汇报自身的情报。例如操作系统、Docker 版本、机器的 CPU 和内存情况,以及当前有哪些 Pod 在运行等,这样 Master 可以获知每个 Node 的资源使用情况,并实现高效均衡等资源调度策略。而某个 Node 超过指定时间不上报信息时,会被 Master 判断为“失联”,Node 的状态被标记为不可用(Not Ready),随后 Master 会触发“工作负载大转移”的自动流程。
1.7 Ingress
在 k8s 中,服务和 Pod 的 IP 地址仅可以在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用能够访问集群内的服务,在 k8s 中目前提供了三种方案:NodePort、LoadBalancer、Ingress。
采用 NodePort 方式暴露服务面临问题是,由于 NodePort 在每个节点上开启的端口,服务一旦多起来,会造成暴露端口量极其庞大,而且难以维护。LoadBalancer 类型是在 NodePort 的基础上,借助 CloudProvider 创建一个外部的负载均衡器,并将请求转发到 NodeIP:NodePort,缺点依然存在。
那能否使用一个 Nginx 直接在集群内进行转发呢?众所周知,Pod 与 Pod 之间是可以互相通信的,而 Pod 是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod 上所监听的就是其上级 Node 的端口。简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了。
如上面的方法,采用 Nginx-Pod 似乎已经解决了问题,但是其实这里面有一个很大缺陷:当每次有新服务加入又该如何修改 Nginx 配置呢?虽然使用 Nginx 可以通过虚拟主机域名进行区分不同的服务,而每个服务通过 upstream 进行定义不同的负载均衡池,再加上 location 进行负载均衡的反向代理,在日常使用中只需要修改 nginx.conf 即可实现,但是在 k8s 中这种方式的调度又成了问题。
假设后端的服务初始服务只有 ECshop(B2C 开源商城系统),后面增加了 BBS(Bulletin Board System 论坛)和 Member(会员)服务,那么又该如何将这两个服务加入到 Nginx-Pod 进行调度呢?总不能每次手动改或者 Rolling Update 前端 Nginx Pod 吧!此时 Ingress 出现了,如果不算上面的 Nginx,Ingress 包含两大组件:Ingress Controller 和 Ingress。
Ingress 简单的理解就是你原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;那么问题来了:”Nginx 该怎么处理?”
Ingress Controller 这东西就是解决“Nginx 的处理方式” 的;Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取他,按照他自己模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下。
实际上,Ingress 也是 Kubernetes API 的标准资源类型之一,它其实就是一组基于 DNS 名称(host)或 URL 路径,把请求转发到指定的 Service 资源的规则。用于将集群外部的请求流量转发到集群内部完成的服务发布。
需要明白的是,Ingress 资源自身不能进行“流量穿透”,仅仅是一组规则的集合,这些集合规则还需要其他功能的辅助,比如监听某套接字,然后根据这些规则的匹配进行路由转发,这些能够为 Ingress 资源监听套接字并将流量转发的组件就是 Ingress Controller。PS:Ingress 控制器不同于 Deployment 控制器的是,Ingress 控制器不直接运行为 kube-controller-manager 的一部分,它仅仅是 Kubernetes 集群的一个附件,类似于 CoreDNS,需要在集群上单独部署。
文章转载自:橙子家
评论