环境:centos7.9 docker-ce-20.10.9 kubernetes-version v1.22.6
作为一个初学者,我们应该首先了解为什么需要k8s,以及从历史到现当下,软件开发部署是如何演变发展的。本篇就来详细讲解这些知识内容。
单体应用在过去,多数的应用都是大型单体应用,以单个进程或几个进程的方式,运行于几台服务器之上,这些应用的发布周期长,而且迭代也不频繁,每个发布周期结束前,开发者会把应用程序打包后交给运维团队,运维人员再进行处理部署、监控事宜,并且在硬件发生故障时手动前移应用。
单体应用的特点 1、由很多组件组成,组件之间紧密地耦合在一起,在同一个操作系统进程中运行,开发、部署、管理的时候必须以同一个实体进行;
2、对于单体应用而言,即使是某个组件中的一个小修改,都要重新部署整个应用;
3、组件之间缺乏严格的边界定义,相互依赖,日积月累,系统复杂度提升,质量恶化;
垂直扩展:增加增加CPU、内存或其他服务器系统资源;
水平扩展:增加更多的服务器来跑单体应用;
缺点:成本很高
微服务大型单体应用被分解为小的、可独立运行的组件,我们称之为微服务,每个微服务以独立的进程运行,并通过简单且定义良好的api接口与其他微服务同行,微服务彼此之间解耦,所以它们可以被独立开发、部署、升级、伸缩。
微服务的缺点 1、当组件数量增加时,决定将组件部署在哪里就显得困难,因为不仅组件的部署的组合数在增加,组件间依赖的组合数也在增加;
2、运维工程师需要正确配置所有的微服务来使其称为一个单一系统能正常工作,随着微服务数量的增加,配置工作变得冗余且易错;
3、由于微服务跨了多个进程和机器,使得调试代码和定位异常调用变得困难。
4、由于微服务组件既可以被独立开发又可以被独立部署,由此组件之间依赖的差异性,应用程序需要同一个依赖库的不同版本,这种问题不可避免。特别是,程序运行环境的差异性,这是开发和运维团队要解决的一个最大问题。
为应用程序提供一个一致的环境
为解决程序运行环境的差异性:最理想的做法是让应用在开发环境和生产阶段可以运行在一个完全一样的环境下、一样的操作系统、库、系统配置、网络环境和其他所有的资源条件。
DevOps是一种实践,让一个团队参与应用的开发、部署、运维的整个生命周期,这意味着开发者、QA和运维团队彼此之间的合作需要贯穿整个软件流程。
容器技术 容器允许你在同一台服务器上运行多个服务,容器不仅提供不同的环境给每个服务,而且还将服务相互隔离。
一个容器里运行的进程实际上运行在宿主机上的操作系统,就像所以其它进程一样(不像虚拟机,进程是运行在不同的操作系统上的),但在容器里的进程仍然是和其他进程隔离的,对于容器内进程本身而言,就好像是在机器和操作系统上运行的唯一一个进程。
1、和虚拟机相比,容器更加轻量级,它允许在相同的硬件上运行更多的组件,主要是因为每个虚拟机都需要运行着自己的一组系统进程(因为虚拟机都要安装操作系统),这就产生了除组件进程之外的额外资源消耗。从另一个方面来说,一个容器仅仅是运行在宿主机上被隔离的单个进程,仅消耗应用容器消耗的资源,不会有其他进程的开销。
2、因为虚拟机的额外开销,导致没有足够的资源给每个应用开一个专用的虚拟机,最终会导致多个应用分组塞进每个虚拟机;当使用容器时,能够也应该让每个应用有一个容器,最终的结果就是可以在同一台裸机上运行更多的应用。
3、虚拟机都有自己的操作系统,虚拟机当然也提供完全隔离的环境,在虚拟机上的应用程序都调用自己的操作系统内核;容器是运行在宿主机上的,所以多个容器完全执行宿主机上的同一个内核的系统调用,此内核是唯一一个在宿主机操作系统上执行x86指令的内核,CPU也不需要做任何虚拟化。
4、容器因它的低消耗而成为一个更好的选择,记住,每个虚拟机运行它自己的一组系统服务,而容器不会,因为容器都都运行在同一个宿主机操作系统上,这样也就以为着一个容器不用像虚拟机那样要开机,容器的进程就可以很快的被启动。
也许你会好奇,如果多个进程都运行在同一个操作系统上,那容器是如何实现隔离的呢,有2个机制:第一个是Linux命名空间,它使每个进程只看到它自己的系统视图(文件、进程、网络接口、主机名等);第二个是Linux控制组(cgroups),它限制了进程能使用的资源(cpu、内存、网络资源等)。
用 Linux 命名空间隔离进程
默认情况下,每个 Linux 系统最初仅有一个命名空间,所有系统资源(文件系统、用户 ID 、网络接口等)属于这一个命名空间。你能创建额外等命名空间,以及在它们之间组织资源。对于一个进程,可以在其中一个命名空间中运行它,进程将只能看到同一个命名空间下的资源。存在多种类型的多个命名空间,所以一个进程不仅只属于某一个命名空间,而属于每个类型的一个命名空间。
限制进程的可用资源
cgroups 是一个 Linux 内核功能,它被用来限制一个进程或者一组进程的资源使用。一个进程的资源( CPU 、内存、网络带宽等)使用量不能超过被分配的量。
镜像: Docker 镜像里包含了打包的应用程序及其所依赖的环境;
镜像仓库: Docker 镜像仓库用于存放 Docker 镜像,以及促进不同人和不同电脑之间共享这些镜像;
容器: Docker 容器通常是一个 Linux 容器,基于 Docker 镜像被创建。一个运行中的容器是一个运行在 Docker 主机上的进程,但它和主机,以及所有运行在主机上的其他进程都是隔离的。这个进程也是资源受限的,仅能访问和使用分配给它的资源(CPU 、内存等)。
开发人员首先构建一个镜像,然后把镜像推到镜像仓库中,任何可以访问镜像仓库的人都可以使用该镜像。然后,他们可以将镜像拉取到任何运行着 Docker 的机器上并运行镜像。 Docker 会基于镜像创建一个独立的容器,并运行镜像中指定的可执行二进制文件。
Kubernetes 是一个容器编排集群系统,它允许你在其上很容易地部署和管理容器化的应用。它依赖于 Linux 容器的特性来运行异构应用,而无须知道这些应用的内部详情,也不需要手动将这些应用部署到每台机器。因为这些应用运行在容器里,它们不会影响运行在同一台服务器上的其他应用。
Kubernetes 使你在数以千计的电脑节点上运行软件时就像所有的节点是单个大节点已有。它将底层基础设施抽象,这样做同时简化了应用的开发、部署,以及对开发和运维团队对管理。
通过 Kubernetes 部署应用程序时,你的集群包含多少节点都是一样的。集群规模不会造成什么差异性,额外的集群节点只是表示了一些额外的可用来部署应用的资源。
上图展示了一副最简单的 Kubernetes 系统图。整个系统由一个主节点和若干个工作节点组成。开发者把一个应用列表提交到主节点, Kubernetes 会将它们部署到集群的工作节点。组件被部署在哪个节点对于开发者和系统管理员来说都不用关心。
开发者也可以指定一些应用必须一起运行, Kubernetes 将会在某个工作节点上部署它们。其他的将被分散部署到集群中,但是不管部署在哪儿,它们都能以相同的方式互相通信。
在硬件级别,一个 Kubernetes 集群由很多节点组成,这些节点被分成以下两种类型:
主节点:即master节点,承载着 Kubernetes 控制和管理整个集群系统的控制面板;
工作节点:,即node节点,顾名思义,正在干活的节点,运行着实际部署的应用;
控制面板(master节点)用于控制集群并使它工作。它包含多个组件,组件可以运行在单个主节点上或者也可以通过副本分别部署在多个主节点以确保master节点的高可用性。
Kubernetes API 服务器:客户端和其他控制面板组件都要和它通信;
Scheduler :调度应用,即通过内部计算,为应用的部署组件计算出一个合适的工作节点;
Controller Manager :执行集群级别的功能,如复制组件、持续跟踪工作节点、处理节点失败等;
etcd :一个分布式存储数据库,用来持久化存储集群配置;
工作节点是运行容器化应用的真正节点服务器。运行、监控和管理应用服务的任务是由以下组件完成的:
Docker: docker是一个容器化平台,k8s与docker是市场上应用最广泛的,但是k8s也支持他的容器类型,如rkt;
Kubelet :与 API 服务器通信,并管理它所在节点的容器;
Kubernetes Services Proxy (kube-proxy) :负责组件之间的负载均衡网络流量;
在 Kubernetes 中运行应用的步骤:
1、将应用打包成为一个或多个容器镜像;
2、将制作好的镜像推送到镜像仓库;
3、将应用的描述发布到 Kubernetes API 服务器。
应用的描述包括但不限于以下几点:
1、容器镜像或者包含应用程序组件的容器镜像
2、这些组件如何相互关联
3、哪些组件需要同时运行在同一个节点上
4、哪些组件不需要同时运行
5、哪些组件为内部或外部客户提供服务且应该通过单个 IP 地址暴露,并使其他组件可以发现
当 API 服务器处理应用的描述时,调度器调度指定的容器组到可用的工作节点上,调度是基于每组所需的计算资源,以及调度时每个节点未分配的资源。然后,那些节点上的 Kubelet 指示容器运行时(例如 Docker)拉取所需的镜像并运行容器。
上图可以帮助更好地理解如何在 Kubernetes 中部署应用程序。应用描述符列出了四个容器,并将它们分为三组(这些集合被称为 pod )。前两个 pod 只包含一个容器,而最后一个包含两个容器。这意味着两个容器都需要协作运行,不应该相互隔离。在每个 pod 旁边,还可以看到一个数字,表示需要并行运行的每个 pod 的副本数量。在向 Kubernetes 提交描述符之后,它将把每个 pod 的指定副本数量调度到可用的工作节点上。节点上的 Kubelets将告知 Docker 从镜像仓库中拉取容器镜像并运行容器。
保持容器运行一旦应用程序运行起来, Kubernetes 就会不断地确认应用程序的部署状态始终与你提供的描述相匹配。它会自动重启崩溃或停止响应的进程,并且能自动将故障节点上运行的所有容器迁移到新节点运行。
扩展副本数量Kubernetes 可以根据指示增加附加的副本或者停止多余的副本,并且能根据实时指标(如 CPU 负载、内存消耗、每秒查询或应用程序公开的任何其他指标)自动调整副本数。
命中移动目标可以告诉 Kubernetes 哪些容器提供相同的服务,而 Kubernetes 将通过一个静态 IP 地址暴露所有容器,并将该地址暴露给集群中运行的所有应用程序。这是通过环境变量完成的,但是客户端也可以通过良好的 DNS 查找服务 IP 。 kube-proxy 将确保到服务的连接可在提供服务的所有容器中实现负载均衡。服务的 IP 地址保持不变,因此客户端始终可以连接到它的容器,即使它们在集群中移动
使用 Kubernetes 的好处 简化应用程序部署:开发人员可以自己开始部署应用程序,基本不需要了解组成集群的服务器;
更好地利用硬件: Kubernetes 根据应用程序的资源需求描述和每个节点上的可用资源选择最合适的节点来运行应用程序;
健康检查和自修复: Kubernetes 监控应用程序组件和它们运行的节点,并在节点出现故障时自动将它们重新调度到其他节点;
自动扩容: Kubernetes 可以监视每个应用程序使用的资源,并不断调整每个应用程序的运行实例数量;
简化应用开发;开发环境和生产环境一样有助于快速发现 bug,开发人员不需要实现他们通常会实现的特性,如:服务发现、扩容、负载均衡、自恢复,甚至集群的 leader 选举,Kubernetes 可以自动检测一个应用的新版本是否有问题,如果有问题则立即停止其滚动更新。