Kubernetes in Action—kubernetes 介绍

微服务相较于大型单体应用,彼此之间解耦,因此可以被独立开发,部署,升级,伸缩,使得我们可以对每一个微服务实现快速迭代,并且迭代的速度可以和市场需求变化的速度保持一致。

但随着部署组件的增多和数据中心的增长,配置、管理并保持系统正常运行变得越来越困难,因此我们需要一些自动化的措施,包括自动调度、配置、监管和故障处理,Kubernetes 可以很好地完成了这些工作。

Kubernetes 系统需求

从单体应用到微服务

单体应用由多个组件组成,这些组件紧密耦合在一起并在同一个系统进程中运行,因此带来了 3 个问题:

  • 开发、部署、管理的时候必须同一个实体进行,即使是小的修改,也需要重新部署应用
  • 组件间缺乏严格定义,相互依赖,日积月累导致系统复杂度提升,整体质量急剧恶化
  • 通常需要为整个应用提供足够资源的高性能服务器,通过垂直扩展(增加 CPU、内存或其他系统资源,成本将越来越高)或者水平扩展(增加更多应用程序,代码需要做较大改动,有时甚至不可行)来应对系统负荷

将应用拆解为多个微服务

以上几个问题迫使我们将复杂大型单体应用拆分为小的可独立部署的微服务组件,每个服务以独立进程运行,并通过简单且定义良好的接口(API)与其他微服务通信,因为微服务都是独立进程,因此只要 API 不变或向前兼容,改动一个微服务,并不会要求对其他微服务进行改动或者重新部署。

image-20191214125601404

微服务的扩容

相比于单体应用扩容针对的是整个系统,微服务的扩容只需要针对单个服务,这意味着可以选择扩容那些需要更多资源的服务而保持其他服务仍然维持在原来的规模。当单体应用因为其中一部分无法扩容而整体被限制扩容时,则可以把应用拆分为多个微服务,将能进行扩容的组件进行水平扩展,不能进行扩容的组件垂直扩展。

部署微服务

当组件数量增加时,部署相关的决定就变得越来越困难,因为不仅组件部署的组合数在增加,而且组件间依赖的组合数也在以更大的因素增加。

部署微服务时,部署者需要正确配置所有服务来使其作为单一系统能正确工作,随着微服务的数量不断增加,配置工作变得冗杂且易错。除此之外,微服务还带来了其他诸多问题,如跨进程使代码调试和异常定位变得困难,当然,我们有各种各样的解决方案。

环境需求的差异

在微服务架构下,不同团队开发不同的组件是正常的事实,但每个团队都有可能使用不同的库并在需求升级时替换它们,因为组件之间以来的差异性,因此应用程序需要同一个库的不同版本也是不可避免的,因此需要在同一个主机上部署的组件数量越大,满足不同组件的需求就越难,这经常会成为运维团队的噩梦。

image-20191214134357391

为应用程序提供一个一致的环境

开发团队和运维团队需要解决的一个最大问题是程序运行环境的差异性,这种差异来自于开发环境和生产环境之间,甚至存在于各个生产机器之间,还有随着时间的推移环境也会发生变化。从硬件到操作系统再到每台机器的可用库上,运维团队和开发者对自己所处系统环境的理解程度是不同的。

为了减少仅在生产环境才暴露的问题,最理想的做法是让应用在开发和生产阶段可以运行在完成一样的环境下,它们有着一样的操作系统、库、系统配置、网络环境和其他所有的条件。如果可能,在一台服务器上部署新的应用时,不会影响到机器上已有的应用

迈向持续交付:DevOps 和无运维

在过去,开发团队的任务是创建应用并交付给运维团队,然后运维团队部署应用并使它运行。

现在,公司都意识到,让同一个团队参与应用的开发、部署、运维的整个生命周期更好,这意味着,开发者、QA 和运维团队彼此之间的合作需要贯穿整个流程,这种实践被称为 DevOps

带来的优点

开发者能够更多的在生产环境中运行应用,使他们对用户的需求和问题,以及运维团队维护应用所面临的困难,有更好的理解。应用开发者更趋向于将应用尽快地发布上线,然后通过收集用户反馈对应用进一步开发优化。

为了频繁地发布应用,就需要简化部署流程。理想的状态是开发人员能够自己部署应用上线,而不需要交付给运维人员操作

让开发者和系统管理员做他们最擅长的

开发者和系统管理员共同的目标都是成功运行一个应用并服务于客户,但开发者更热衷于创造新功能和提升用户体验,而系统管理员关系系统安全、资源利用率等对于开发者来说优先级不高的东西。

理想情况是,开发者部署程序本身,不需要知道硬件基础设施的任何情况,也不需要和运维团队交涉,这称为 NoOps。Kubernetes 通过对实际硬件做抽象,然后将自身暴露为一个平台,用于部署和运行应用程序。这样,就可以解放系统管理员,让他们不需要关心组件间暗含的依赖关系,只要保持底层基础设施正常运转即可。

介绍容器技术

以上的问题都可以通过 Kubernetes 解决,其使用 Linux 容器技术来提供应用的隔离,因此,需要通过熟悉容器的基本知识来深入理解 Kubernetes,包括认识存在的容器技术分支,如 Docker 或者 rkt 等。

什么是容器

面对不同的环境要求,针对不同组件需要不同的、可能存在冲突的依赖库版本或者其他不同环境需要的问题,提供不同的操作系统实例或增加配置虚拟机,都增加了系统管理员的工作负担,间接导致了人力资源的浪费。

用 Linux 容器技术隔离组件

开发者通过 Linux 容器技术隔离微服务环境,容器允许在同一台机器上允许多个服务,不仅提供不同的环境给不同服务,而且将它们互相隔离,相较于虚拟机,容器的开销小很多。

容器运行的进程实际上是运行在宿主机的操作系统上的,但又与其他进程隔离,对于容器进程自身而言,就好像是机器和操作系统上允许的唯一一个进程。

比较虚拟机和容器

容器相较于虚拟机更加轻量级,容器允许在相同的硬件上允许更多数量的组件,究其原因是因为每个虚拟机需要允许自己的一组系统进程,而容器仅仅是运行在宿主机上被隔离的的那个进程

image-20191214152050048

虚拟机里的应用程序执行虚拟机操作系统的系统调用,虚拟机内核会通过管理程序在宿主机上的物理 CPU 执行 x86 指令,下图是运行在多个虚拟机上的应用:

image-20191214152611561

多个容器则会完全执行在宿主机上的同一个内核的系统调用,此内核是唯一一个宿主机操作系统上执行 x86 指令的内核,因此 CPU 也不需要做对虚拟机那样的虚拟化,下图是运行在独立容器中的应用:

image-20191214152652276

容器实现隔离机制介绍

容器隔离的机制有二:

  • Linux 命令空间,使每个进程只看到自己的系统视图(文件、进程、网络接口、主机名等)
  • Linux 控制组(cgroups),限制了进程使用的资源量(CPU、内存、网络带宽等)

用 Linux 命名空间隔离进程

默认情况下,每个 Linux 系统最初仅有一个命名空间,所有系统资源(诸如文件系统、用户 ID、网络接口等)属于这一个命名空间,但是你能创建额外的命名空间,以及在它们之间组织资源。对于一个进程,可以在任何一个命名空间中运行它,进程只能看到同一个命名空间下的资源

存在以下类型的命名空间:

  • Mount(mnt)
  • ProcessId(pid)
  • Network(net)
  • Inter-process communicaion(ipd)
  • UTS
  • User Id(user)

每种命名空间被用来隔离一组特定的资源。如 UTS 命名空间决定了运行在命名空间里的进程能看见哪些主机名和域名,通过分派不同 UTS 命名空间给不同进程,就能使它们看到不同的本地主机名,这时候这些进程就好像运行在不同机器一样。

限制进程的可用资源

另外的隔离性就是限制容器能使用的系统资源,其通过 cgroup 实现。

cgroup 是一个 Linux 内核功能,其被用来限制一个进程或者一组进程的资源(CPU、内存、网络带宽等)使用,进程的资源使用量不能超出被分配的量,这和进程运行在不同机器是类似的。

Docker 容器平台介绍

Docker 简化了打包应用的流程,也简化了打包应用的库和以依赖,甚至整个操作系统的文件系统也能被打包成一个简单的可移植的包,该包可以在任何其他运行 Docker 的机器上被使用。

Docker 的概念

Docker 是一个打包、分发和运行应用程序的平台,它允许将应用程序和应用程序依赖的整个环境打包在一起。以下是 Docker 中主要的三个概念:

  • 镜像:包含了打包的应用程序及其所依赖的环境
  • 镜像仓库:存放 Docker 镜像,促进不同人和不同电脑间共享镜像
  • 容器:通常为一个 Linux 容器,其基于 Docker 镜像被创建,一个运行中的容器是一个运行在 Docker 主机上的进程

构建、分发和运行 Docker 镜像

下图显示了以上三个概念及它们的关系:

image-20191214161936240

对比虚拟机与 Docker 容器

运行多个虚拟机的宿主机:

image-20191214162124696

运行多个 Docker 容器的宿主机:

image-20191214162154534

镜像层

Docker 镜像由多层构成,不同镜像可能包含完全相同的层,因为它们都是基于另一个 Docker 镜像之上构建的,不同镜像都能使用相同的父镜像作为其基础镜像,这提升了镜像在网络上的分发效率以及减少镜像的存储空间。容器运行时,一个新的可写层在镜像层之上被创建,容器进程写入位于底层的一个文件时,该文件的一个 copy 在顶层被创建,进程写入该 copy 文件。

容器镜像可移植性的限制

理论上,一个容器镜像能运行在一个运行 Docker 的机器上。但如果一个容器化的应用需要一个特定的内核版本,那它可能不能在每台机器上都工作,如果一台机器上运行了一个不匹配的 Linux 内核版本,或者没有相同内核模块可用,那么此应用就不能在其上运行。一个在特定硬件架构上编译的容器化应用,只能在有相同硬件架构的机器上运行

rkt — 一个 Docker 的替代方案

开放容器计划(OCI)围绕容器格式和运行时创建了开放工业标准。

rkt 和 Docker 一样是一个运行容器的平台,其强调安全性,可构建性并遵从开放标准。我们不应该错误地认为 Kubernetes 是一个专为 Docker 容器设计的容器编排系统,容器恰好是在不同集群节点上运行应用的最佳方式,而 Kubernetes 的核心远不止是编排容器。

Kubernetes 介绍

初衷

Kubernetes 是谷歌基于 Borg 和 Omega 及其他谷歌内部系统实践的开源系统。

深入浅出地了解 Kubernetes

Kubernetes 是一个软件系统,其允许你在其上很容易地部署和管理容器化的应用。它依赖于 Linux 容器特效来运行异构应用,而无须知道应用的内部详情,也不需要手动将这些应用部署到每台机器。使用 Kubernetes,可以使开发人员在数以千计的电脑节点上运行软件就像在单个大节点一样,它将底层基础设施抽象,这样做简化了应用的开发、部署以及对开发和运维团队的管理。

Kubernetes 的核心功能

下图是最简单的 Kubernetes 系统图:

image-20191214222644941

整个系统由一个主节点和若干个工作节点组成,开发者把应用列表提交到主节点,Kubernetes 会将它们部署到集群的工作节点,而组件被部署在哪个节点对于开发者和系统管理员来说不用关心。

帮助开发者聚焦核心应用功能

Kubernetes 可以被当作集群的一个操作系统来看待,其提供了包括服务发现、扩容、负载均衡、自恢复,甚至领导者选举等服务,使应用程序开发者能集中精力实现应用本身功能而不用思考和基础设施相关的服务和问题。

帮助运维团队获取更高的资源利用率

Kubernetes 将应用容器化并运行在集群的某个地方,并提供信息给应用组件发现彼此,因此 Kubernetes 能在任何时间迁移应用并通过混合和匹配来获得比手动调度高很多的资源利用率。

Kubernetes 集群架构

一个 Kubernetes 集群由很多节点组成,节点分为两种类型:

  • 主节点,承载着 Kubernetes 控制和管理整个集群系统的控制面板
  • 工作节点,运行用户实际部署的应用

image-20191214223417378

控制面板

控制面板用于控制集群并使它工作。其包含多个组件,组件可以运行在单个主节点或者通过副本分别部署在多个主节点以确保高可用性,这些组件是:

  • Kubernetes API 服务器,你和其他控制面板组件都要和它通信
  • Scheculer,调用你的应用(为应用的每个部署组件分配一个工作节点)
  • Controller Manager,执行集群级别的功能,如复制组件、持续跟踪工作节点、处理节点失败等
  • etcd,一个可靠的分布式数据存储,它能持久化存储集群配置

工作节点

工作节点是运行容器化应用的容器,运行、监控和管理应用服务的任务是由以下组件完成的:

  • Docker、rtk 或其他的容器类型
  • Kubelet,它与 API 服务器通信,并管理它所在节点的容器
  • Kubernetes Servie Proxy(kube-proxy),负责组件之间的负载均衡网络流量

在 Kubernetes 中运行应用

为了在 Kubernetes 运行应用,首先需要将应用打包进一个或多个容器镜像,再将镜像推送到镜像仓库,然后将应用的描述发布到 Kubernetes API 服务器。

描述信息怎样成为一个运行的容器

当 API 服务器处理应用的描述时,调度器调度(基于每组所需的计算资源,以及调度时每个节点未分配的资源)指定组的容器到可用的工作节点上,之后节点上的 Kubelet 指示容器运行时(如 Docker)拉取所需的镜像并运行容器。

保持容器运行

一旦应用程序运行起来, Kubernetes 会不断确认应用程序的部署状态始终与提供的描述相匹配。如描述指出需要运行五个 web 服务器实例,因此 Kubernetes 总是正好运行五个实例,当实例之一停止了工作后,Kubernetes 将自动重启它。同理当整个工作节点死亡或者无法访问时, Kubernetes 将为在故障节点上运行的所有容器选择新节点,并在新节点上运行它们。

扩展副本数量

当应用程序运行时,可以决定要增加或减少副本量,而 Kubernetes 将分别增加附加的或停止多余的副本,当然, Kubernetes 也可以决定最佳副本数目,其根据实时指标(如 CPU 负载、内存消耗、每秒查询次数等)自动调整副本数。

命中移动目标

为了让客户能够轻松地找到提供特定服务的容器,可以告诉 Kubernetes 哪些容器提供相同的服务,而 Kubernetes 将通过一个静态 IP 暴露所有容器,并将该地址暴露给集群中运行的所有应用程序,这是通过环境变量完成的,客户端也可以通过良好的 DNS 查找服务 IP。kube-proxy 确保到提供服务的连接提供负载均衡,只要 IP 地址不变,客户端始终可以连接到它的容器。

使用 Kubernetes 的好处

简化应用程序部署

由于 Kubernetes 将其所有工作节点公开为一个部署平台,因此应用程序开发人员可以自己部署应用程序而无须了解组成集群的服务器。实际上,所有节点都是一组等待应用程序使用它们的计算资源

更好地利用硬件

通过在服务器上装配 Kubernetes,并使用其运行应用程序而不是手动运行,你已经将应用程序与基础设施分离开来。只需要告诉 Kubernetes 运行应用程序,它就可以根据应用程序的资源描述和每个节点的可用资源选择合适的节点运行应用程序。计算机可以比人更好、更快地完成寻找最优组合,特别是当选项数量特别大的时候,因此 Kubernetes 可以比人工更好地利用基础设施。

健康检查和自修复

随着集群大小的增加,将更频繁地处理出现故障的计算机组件,Kubernetes 监控应用程序组件及其运行的节点,并在节点故障时自动调度到其它节点,使运维团队不必手动迁移应用程序组件,且允许团队立即专注于修复节点本身

自动扩容

Kubernetes 通过监控每个应用程序使用的资源,不断调整每个应用程序的运行实例数量,这在如应对突发负载峰值等情况时十分有用。在基础设施中,添加额外的节点就像通过云供应商的 API 请求一样简单,因此 Kubernetes 甚至可以根据部署的应用程序将整个集群规模扩大或者缩小。