简介
-
Kubernetes 能自动调度、配置、监管和故障处理,使开发者可以自主部署应用,并且控制部署的频率,完全脱离运维团队的帮助。
-
Kubernetes 同时能让运维团队监控整个系统,并且在硬件故障时重新调度应用。 P2
-
Kubernetes 抽象了数据中心的硬件基础设施,使得对外暴露的只是一个巨大的资源池。 在部署多组件应用时, -
-
Kubernetes 会为每个组件都选择一个合适的服务器,部署之后它能够保证每个组件可以轻易地发现其他组件,并彼此之间实现通信。
Kubernetes 系统的需求
近年来,应用程序的开发部署的变化原因:
大型单体应用被拆解为更多的小型微服务
应用运行所依赖的基础架构的变化
从单体应用到微服务
单体应用由很多个组件组成,这些组件紧密地耦合在一起,并且在同一个操作系统进程中运行,所以在开发、部署、管理的时候必须以同一个实体进行。
单体应用通常需要一台能为整个应用提供足够资源的高性能服务器,有两种方法可以应对不断增长的系统负荷: P3
垂直扩展:提升单机性能 —— 增加 CPU 、内存或其他系统资源
-
优点:不需要应用程序做任何变化
-
缺点:成本很快会越来越高,并且通常会有瓶颈
水平扩展:增加服务器数量
- 优点:能线性扩充系统性能
- 缺点:需要在架构层面支持水平扩展,部分组件难于甚至不太可能去做水平扩展(像关系型数据库)
垂直扩展总会达到单机性能的极限,所以终极解决方案是水平扩展,同时也可以通过垂直扩展进行辅助。
仔细回想一下,发现我们平时也是这样处理的。由于历史原因,我们项目核心功能的大部分代码在同一个应用中,导致启动就会占用大量资源,单机处理能力较差。在经历各种配置下压测后,选择了合适的配置,然后就直接水平扩展,并且逐渐将一些压力大的接口拆成微服务提供接口或者直接处理各种请求。
如果单体应用的任何一个部分不能扩展,整个应用就不能扩展,除非我们想办法把它拆分开。
将应用拆解为多个微服务
服务之间可以通过类似 HTTP 这样的同步协议通信,也可以通过像 AMQP 这样的异步协议通信,并且微服务也可以选用最适合的开发语言来实现。
每个微服务都是独立的,可以独立开发和部署。只要 API 不变或者向前兼容,改动一个微服务,并不会要求对其他微服务进行改动或者重新部署。
微服务的扩容
单体系统必须要对整个系统扩容,而微服务只需针对单个服务扩容。因此,我们可以选择仅扩容那些需要更多资源的服务而保持其他的服务仍然维持在原来的规模。当单体应用因为其中一部分无法扩容而整体被限制扩容时,可以把应用拆分成多个微服务,将能扩容的服务进行水平扩展,不能进行扩容的组件进行垂直扩展。
部署微服务
当组件数量增加时,部署相关的决定就变得越来越困难。因为不仅组件部署的组合数在增加,而且组件间依赖的组合数也在以更大的因素增加,并且配置工作变得冗杂易错,同时因为跨了多个进程和机器,调试代码和定位异常调用变得困难。
环境需求的差异
因为组件之间依赖的差异性,应用程序需要同一个库的不同版本是不可避免的。当多个应用在同一个主机上运行就有可能会有依赖冲突。
为应用程序提供一个一致的环境
开发和运维团队需要解决的一个最大的问题是程序运行环境的差异性:
- 开发环境和生产环境之间
- 各个生产机器之间
- 生产机器环境随时间的推移而变化
为了减少会在生产环境才暴露的问题,最理想的做法就是让应用在开发和生产阶段可以运行在完全一样的环境下,它们有完全一样的操作系统、库、系统配置、网络环境和其他所有条件。这个环境不会随着时间的推移而变化,并且在一台服务器上部署新的应用时,不会影响机器上已有的应用。
迈向持续交付: DevOps 和无运维
在过去,开发团队的任务是创建应用并交付给运维团队,然后运维团队部署应用并使它运行。
而现在,让一个团队参与应用的开发、部署、运维的整个生命周期更好。这意味着开发者、 QA 和运维团队彼此之间的合作需要贯穿整个流程。这种实践被称为 DevOps 。
带来的优点
开发者更多地在生产环境中运行应用,能更好地理解用户的需求和问题、运维团队维护应用所面临的困难
开发者更趋向于尽快发布上线,能进行快速迭代
简化部署流程,开发者自己部署应用上线
让开发者和系统管理员做他们最擅长的
Kubernetes 通过对实际硬件做抽象,然后将自身暴露成一个平台,用于部署和运行应用程序。它允许开发者自己配置和部署应用程序,而不需要系统管理员的任何帮助,让系统管理员聚焦于保持底层基础设施运转正常的同时,不需要关注实际运行在平台上的应用程序。
介绍容器技术
Kubernetes 使用 Linux 容器技术来提供应用的隔离,需要先通过熟悉容器的基本知识来更深入地理解 Kubernetes 。 P7
什么是容器
用 Linux 容器技术隔离组件
容器允许你在同一台机器上运行多个服务,不仅提供不同的环境给每个服务,而且将它们相互隔离。容器类似虚拟机,但开销小很多。
一个容器里运行但进程实际上运行在宿主机的操作系统上,但容器里的进程仍然是和宿主机的其他进程隔离的。对容器内的进程本身而言,就好像是在机器和操作系统上运行的唯一一个进程。
比较虚拟机和容器
容器更加轻量级,它允许在相同的硬件上运行更多数量的组件。一个容器仅仅是运行在宿主机上被隔离的单个进程,仅消耗应用容器消耗的资源,不会有其他进程的开销。虚拟机则需要运行自己的一组系统进程,会产生除了组件进程消耗以外的额外计算资源损耗。
因为虚拟机有额外开销,所以没有足够的资源给每个应用开一个专用的虚拟机,最终会将多个应用程序分组塞进每个虚拟机。而容器能够(也应该)让每个应用有一个容器,最终可以让同一台裸机上运行更多的应用程序。
运行在虚拟机里的应用程序会执行虚拟机操作系统的系统调用,然后虚拟机内核会通过管理程序在宿主机上的物理 CPU 来执行 x86 指令。而运行在容器内部的应用程序执行的系统调用都会由宿主机上的同一个内核执行,此内核是唯一一个在宿主机操作系统上执行 x86 指令的内核, CPU 也不需要做任何虚拟机所做的虚拟化
虚拟机提供完全隔离的环境,因为每个虚拟机运行在自己的 Linux 内核上,而容器都调用同一个内核,存在安全隐患。
每个虚拟机运行它自己的一组系统服务,而容器则不会,因为它们都运行在同一个操作系统上。因此运行一个容器不用像虚拟机那样要开机,它的进程可以很快被启动。
//加入Java开发交流君样:756584822一起吹水聊天
容器实现隔离机制介绍
Linux 命名空间:使每个进程只看到它自己的系统视图(文件、进程、网络接口、主机名等)
Linux 控制组 (cgroups) :限制了进程能使用的资源量( CPU 、内存、网络带宽等)
用 Linux 命名空间隔离进程
默认情况下,每个 Linux 系统最初仅有一个命名空间,所有系统资源(文件系统、用户 ID 、网络接口等)属于这一个命名空间。你能创建额外等命名空间,以及在它们之间组织资源。对于一个进程,可以在其中一个命名空间中运行它,进程将只能看到同一个命名空间下的资源
存在多种类型的多个命名空间,所以一个进程不仅只属于某一个命名空间,而属于每个类型的一个命名空间。
限制进程的可用资源
cgroups 是一个 Linux 内核功能,它被用来限制一个进程或者一组进程的资源使用。一个进程的资源( CPU 、内存、网络带宽等)使用量不能超过被分配的量。
Docker 容器平台介绍
Docker 的概念
Docker 是一个打包、分发和运行应用程序的平台,它允许将应用程序和应用程序所依赖的整个环境打包在一起。 P11
三个主要概念组成了这种情形:
镜像: Docker 镜像里包含了打包的应用程序及其所依赖的环境
镜像仓库: Docker 镜像仓库用于存放 Docker 镜像,以及促进不同人和不同电脑之间共享这些镜像
容器: Docker 容器通常是一个 Linux 容器,基于 Docker 镜像被创建。一个运行中的容器是一个运行在 Docker 主机上的进程,但它和主机,以及所有运行在主机上的其他进程都是隔离的。这个进程也是资源受限的,仅能访问和使用分配给它的资源(CPU 、内存等)
构建、分发和运行 Docker 镜像
开发人员首先构建一个镜像,然后把镜像推到镜像仓库中,任何可以访问镜像仓库的人都可以使用该镜像。然后,他们可以将镜像拉取到任何运行着 Docker 的机器上并运行镜像。 Docker 会基于镜像创建一个独立的容器,并运行镜像中指定的可执行二进制文件。
对比虚拟机和 Docker 容器
可以发现应用 A 和应用 B 无论是运行在虚拟机上还是作为两个分离容器运行时都可以访问相同的二进制和库。 P13
应用 A 和应用 B 能访问相同的文件是因为它们的容器是基于相同基础层的镜像被创建的,原理将在镜像层中会介绍。
镜像层
-
Docker 镜像由多层构成,不同镜像可能包含完全相同的层,因为这些 Docker 镜像都是基于另一个镜像之上构建的,不同的镜像都能使用相同的父镜像作为它们的基础镜像。
-
镜像层不仅使分发高效,也有助于减少镜像的存储空间。 每一层仅被存一次,当基于相同基础层的镜像被创建成两个容器时,它们就能够读相同的文件。但是如果其中一个容器写入某些文件,另外一个是无法看见文件变更的。因此,即使它们共享文件,仍然彼此隔离。
-
容器镜像层是只读的。容器运行时,一个新的可写层在镜像层之上被创建。容器中进程写入位于底层的一个文件时,此文件的一个拷贝在顶层被创建,进程写的是此拷贝。
容器镜像可移植性的限制
- 如果一个容器化的应用需要一个特定的内核版本,那么它可能不能在每台机器上都工作
- 如果一台机器上运行了一个不匹配的 Linux 内核版本,或者没有相同内核模块可用,那么此应用就不能在其上运行
一个在特定硬件架构之上编译的容器化应用,只能在有相同硬件架构的机器上运行(例如:不能将一个 x86 架构编译的应用容器化后,又期望它能运行在 ARM 架构的机器上)
rkt —— 一个 Docker 的替代方案
Docker 本身并不提供进程隔离,实际上容器是在 Linux 内核之上使用诸如 Linux 命名空间和 cgroups 之类的内核特性完成的, Docker 仅简化了这些特性的使用。
rkt (发音为 “rock-it”) 是另外一个 Linux 容器引擎,强调安全性、可构建性并遵从开放标准,目前已经停止维护。
这里提到 rkt 的原因是,不应该错误地认为 Kubernetes 是一个专门为 Docker 容器设计的容器编排系统。实际上, Kubernetes 的核心远不止编排容器。容器恰好是在不同集群结点上运行应用的最佳方式。
Kubernetes 介绍
Kubernetes 是一个软件系统,它允许你在其上很容易地部署和管理容器化的应用。它依赖于 Linux 容器的特性来运行异构应用,而无须知道这些应用的内部详情,也不需要手动将这些应用部署到每台机器。因为这些应用运行在容器里,它们不会影响运行在同一台服务器上的其他应用。
Kubernetes 使你在数以千计的电脑节点上运行软件时就像所有的节点是单个大节点已有。它将底层基础设施抽象,这样做同时简化了应用的开发、部署,以及对开发和运维团队对管理。
通过 Kubernetes 部署应用程序时,你对集群包含多少节点都是一样的。集群规模不会造成什么差异性,额外的集群节点只是代表一些额外的可用来部署应用的资源。
Kubernetes 的核心功能
上图展示了一副最简单的 Kubernetes 系统图。整个系统由一个主节点和若干个工作节点组成。开发者把一个应用列表提交到主节点, Kubernetes 会将它们部署到集群的工作节点。组件被部署在哪个节点对于开发者和系统管理员来说都不用关心。 P16
开发者能制定一些应用必须一起运行, Kubernetes 将会在一个工作节点上部署它们。其他的将被分散部署到集群中,但是不管部署在哪儿,它们都能以相同的方式互相通信。
帮助开发者聚焦核心应用功能
Kubernetes 可以被当作集群的一个操作系统来看待。开发者可以依赖 Kubernetes 提供一些和基础相关的服务,包括服务发现、扩容、负载均衡、自恢复,甚至集群的 leader 选举。
帮助运维团队获取更高的资源利用率
Kubernetes 能在任何时间迁移应用并通过混合和匹配应用来获得比手动调度高很多的资源利用率。
Kubernetes 集群架构
在硬件级别,一个 Kubernetes 集群由很多节点组成,这些节点被分成以下两种类型:
主节点:承载着 Kubernetes 控制和管理整个集群系统的控制面板
工作节点:运行用户实际部署的应用
控制面板
控制面板用于控制集群并使它工作,控制面板的组件持有并控制集群状态。它包含多个组件,组件可以运行在单个主节点上或者通过副本分别部署在多个主节点以确保高可用性。
- Kubernetes API 服务器:你和其他控制面板组件都要和它通信
- Scheduler :调度你的应用(为应用的每个可部署组件分配一个工作节点)
- Controller Manager :执行集群级别的功能,如复制组件、持续跟踪工作节点、处理节点失败等
- etcd :一个可靠的分布式数据存储,能持久化存储集群配置
工作节点
工作节点是运行容器化应用的机器。运行、监控和管理应用服务的任务是由以下组件完成的:
- Docker, rkt 或其他的容器类型
- Kubelet :与 API 服务器通信,并管理它所在节点的容器
- Kubernetes Services Proxy (kube-proxy) :负责组件之间的负载均衡网络流量
- 在 Kubernetes 中运行应用
在 Kubernetes 中运行应用的步骤:
//加入Java开发交流君样:756584822一起吹水聊天
将应用打包进一个或多个容器镜像
将那些镜像推送到镜像仓库
将应用的描述发布到 Kubernetes API 服务器
应用的描述包括但不限于以下几点:
容器镜像或者包含应用程序组件的容器镜像
这些组件如何相互关联
哪些组件需要同时运行在同一个节点上
哪些组件不需要同时运行
哪些组件为内部或外部客户提供服务且应该通过单个 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 可以自动检测一个应用的新版本是否有问题,如果有问题则立即停止其滚动更新