Kubernetes 如何打赢容器之战?

阿里妹导读:Kubernetes 近几年很热门,在各大技术论坛上被炒的很火。它提供了强大的容器编排能力,与此同时 DevOps 的概念也来到你们身边,广大的开发同窗也能简单地运维复杂的商业化分布式系统,打破了传统开发和运维之间的界限。数据库

本文会以初学者的视角,但愿能让读者更好地理解 Kubernetes 出现的背景、超前的设计理念和优秀的技术架构。编程

背景

PaaS后端

PaaS 技术,一句话归纳就是:它提供了“应用托管”的能力。api

早期的主流作法基本上是租 AWS 或者 OpenStack 的虚拟机,而后把这些虚拟机看成物理机同样,用脚本或者手工的方式在上面部署应用。这个过程当中如何保证本地环境和云端环境的一致性是一个很大的课题,而提供云计算服务的公司的核心竞争力就是比拼谁作的更好。从某种意义上来讲 PaaS 的出现,算是一个比较好的解决方案。服务器

以 Cloud Foundry 为例,在虚拟机上部署上 Cloud Foundry 项目后,用户能够很方便地把本身的应用上云。以上帝视角来看这个过程:Cloud Foundry 最核心的是提供了一套应用的打包和分发机制,它为不一样的编程语言定义了不一样的打包格式,它能把可执行文件、启动参数等等一块儿打包成压缩包而后上传至 Cloud Foundry 存储中心,最后由调度器选择虚拟机,由虚拟机上的 Agent 下载并启动应用。网络

分布式系统架构

随着软件的规模愈来愈大,业务模式愈来愈复杂,用户量的上升、地区的分布、系统性能的苛刻要求都促成服务架构从最初的单体变成 SOA 再到现在的微服务,将来还可能演变为 Service Mesh ,Serverless 等等。app

现在,一个完整的后端系统再也不是单体应用架构了,多年前的 DDD 概念从新回到你们的视线中。如今的系统被不一样的职责和功能拆成多个服务,服务之间复杂的关系以及单机的单点性能瓶颈让部署和运维变得很复杂,因此部署和运维大型分布式系统的需求急迫待解决。负载均衡

容器技术框架

前面提到诸如 Cloud Foundry 的 PaaS,用户必须为不一样语言、不一样框架区分不一样的打包方式,这个打包过程是很是具备灾难性的。而现实每每更糟糕,当在本地跑的好好的应用,因为和远端环境的不一致,在打包后却须要在云端各类调试,最终才能让应用“平稳”运行。

而 Docker 的出现改变了一切,它凭借镜像解决了这个问题。Docker 一不作二不休,干脆把完整的操做系统目录也打包进去,如此高的集成度,保证了云端和本地环境的高度一致,而且随时随地轻易地移植。

谁也不知道就由于“镜像”这个简单的功能,Docker 完成了对 PaaS 的降维打击,占有了市场。此时,一些聪明的技术公司纷纷跟进 Docker,推出了自家的容器集群管理项目,而且称之为 CaaS。

容器技术利用 Namespace 实现隔离,利用 Cgroups 实现限制;在 Docker 实现上,经过镜像,为容器提供完整的系统执行环境,而且经过 UnionFS 实现 Layer 的设计。

Docker 容器是彻底使用沙箱机制,相互之间不会有任何接口。经过 Docker,实现进程、网络、挂载点和文件隔离,更好地利用宿主机资源。Docker 强大到不须要关心宿主机的依赖,全部的一切均可以在镜像构建时完成,这也是 Docker 目前成为容器技术标准的缘由。因此咱们能看到在 Kubernetes 中默认使用 Docker 做为容器(也支持 rkt)。

Kubernetes

铺垫了这么多,终于说到本文的主角了。说 Kubernetes 以前,不得不提 Compose、Swarm、Machine 三剑客,其实在 Kubernetes 还未一统江湖以前,它们已经能实现大部分容器编排的能力了。可是在真正的大型系统上,它们却远远不如 Mesosphere 公司出品的大型集群管理系统,更别说以后的 Kubernetes 了。

在容器化和微服务时代,服务愈来愈多,容器个数也愈来愈多。Docker 如它 Logo 所示同样,一只只鲸鱼在大海里自由地游荡,而 Kubernetes 就像一个掌舵的船长,带着它们,有序的管理它们,这个过程其实就是容器编排。

Kubernetes 起源于 Google,不少设计都是源自于 Borg,是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化的应用简单而且高效,而且提供了应用部署,规划,更新,维护的一种机制。

小结

至此,读者了解了 Kubernetes 的前世此生,由 PaaS 的火热,引爆了容器技术的战争,而赢得这场战争中最关键的便是拥有强大的容器编排的能力,而 Kubernetes 无疑是这场战争的胜利者。

设计理念

这一部分,咱们会围绕 Kubernetes 的四个设计理念看看这些作法能给咱们带来什么。

声明式 VS 命令式

声明式和命令式是大相径庭的两种编程方式,在命令式 API 中,咱们能够直接发出服务器要执行的命令,例如: “运行容器”、“中止容器”等;在声明式 API 中,咱们声明系统要执行的操做,系统将不断向该状态驱动。

咱们经常使用的 SQL 就是一种声明式语言,告诉数据库想要的结果集,数据库会帮咱们设计获取这个结果集的执行路径,并返回结果集。众所周知,使用 SQL 语言获取数据,要比自行编写处理过程去获取数据容易的多。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: etcd-operator
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: etcd-operator
    spec:
      containers:
      - name: etcd-operator
        image: quay.io/coreos/etcd-operator:v0.2.1
        env:
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name

咱们来看看相同设计的 YAML,利用它,咱们能够告诉 Kubernetes 最终想要的是什么,而后 Kubernetes 会完成目标。

声明式 API 使系统更加健壮,在分布式系统中,任何组件均可能随时出现故障。当组件恢复时,须要弄清楚要作什么,使用命令式 API 时,处理起来就很棘手。可是使用声明式 API ,组件只需查看 API 服务器的当前状态,便可肯定它须要执行的操做。

显式的 API

Kubernetes 是透明的,它没有隐藏的内部 API。换句话说 Kubernetes 系统内部用来交互的 API 和咱们用来与 Kubernetes 交互的 API 相同。

这样作的好处是,当 Kubernetes 默认的组件没法知足咱们的需求时,咱们能够利用已有的 API 实现咱们自定义的特性。

无侵入性

感谢 Docker 容器技术的流行,使得 Kubernetes 为你们提供了无缝的使用方式。在容器化的时代,咱们的应用达到镜像后,不须要改动就能够遨游在 Kubernetes 集群中。

Kubernetes 还提供存储 Secret、Configuration 等包含但不局限于密码、证书、容器镜像信息、应用启动参数能力。如此,Kubernetes 以一种友好的方式将这些东西注入 Pod,减小了你们的工做量,而无需重写或者很大幅度改变原有的应用代码。

有状态的移植

在有状态的存储场景下,Kubernetes 如何作到对于服务和存储的分离呢?假设一个大型分布式系统使用了多家云厂商的存储方案,如何作到开发者无感于底层的存储技术体系,而且作到方便的移植?

为了实现这一目标,Kubernetes 引入了 PersistentVolumeClaim(PVC)和 PersistentVolume(PV)API 对象。这些对象将存储实现与存储使用分离。

PersistentVolumeClaim 对象用做用户以与实现无关的方式请求存储的方法,经过它来抹除对底层 PersistentVolume 的差别性。这样就使 Kubernetes 拥有了跨集群的移植能力。

架构

首先要说起的是 Kubernetes 使用很具表明性的 C/S 架构方式,Client 可使用 kubectl 命令行或者 RESTful 接口与 Kubernetes 集群进行交互。下面这张图是从宏观上看 Kubernetes 的总体架构,每个 Kubernetes 集群都由 Master 节点 和 不少的 Node 节点组成。

Master

Master 是 Kubernetes 集群的管理节点,负责管理集群,提供集群的资源数据访问入口。拥有 Etcd 存储服务,运行 API Server 进程,Controller Manager 服务进程及 Scheduler 服务进程,关联工做节点 Node。

Kubernetes API Server 提供 HTTP Rest 接口的关键服务进程,是 Kubernetes 里全部资源的增、删、改、查等操做的惟一入口。也是集群控制的入口进程; Kubernetes Controller Manager 是 Kubernetes 全部资源对象的自动化控制中心,它驱使集群向着咱们所须要的最终目的状态; Kubernetes Schedule 是负责 Pod 调度的进程。

Node

Node 是 Kubernetes 集群架构中运行 Pod 的服务节点。Node 是 Kubernetes 集群操做的单元,用来承载被分配 Pod 的运行,是 Pod 运行的宿主机。关联 Master 管理节点,拥有名称和 IP、系统资源信息。运行 Docker Runtime、kubelet 和 kube-proxy。

kubelet 负责对 Pod 对于的容器的建立、启停等任务,发送宿主机当前状态; kube-proxy 实现 Kubernetes Service 的通讯与负载均衡机制的重要组件; Docker Runtime 负责本机容器的建立和管理工做。

实现原理

为了尽量地让读者能明白 Kubernetes 是如何运做的,这里不会涉及到具体的细节实现,若有读者感兴趣能够自行参阅官网文档。这里以一个简单的应用部署示例来阐述一些概念和原理。

建立 Kubernetes 集群

介绍架构的时候咱们知道,Kubernetes 集群由 Master 和 Node 组成。

Master 管理集群的全部行为例如:应用调度、改变应用的状态,扩缩容,更新/降级应用等。

Node 能够是是一个虚拟机或者物理机,它是应用的“逻辑主机”,每个 Node 拥有一个 Kubelet,Kubelet 负责管理 Node 节点与 Master 节点的交互,同时 Node 还须要有容器操做的能力,好比 Docker 或者 rkt。理论上来讲,一个 Kubernetes 为了应对生产环境的流量,最少部署3个 Node 节点。

当咱们须要在 Kubernetes 上部署应用时,咱们告诉 Master 节点,Master 会调度容器跑在合适的 Node 节点上。

咱们可使用 Minikube 在本地搭一个单 Node 的 Kubernetes 集群。

部署应用

当建立好一个 Kubernetes 集群后,就能够把容器化的应用跑在上面了。咱们须要建立一个 Deployment,它会告诉 Kubernetes Master 如何去建立应用,也能够来更新应用。

当应用实例建立后,Deployment 会不断地观察这些实例,若是 Node 上的 Pod 挂了,Deployment 会自动建立新的实例而且替换它。相比传统脚本运维的方式,这种方式更加优雅。

咱们能经过 kubectl 命令或者 YAML 文件来建立 Deployment,在建立的时候须要指定应用镜像和要跑的实例个数,以后 Kubernetes 会自动帮咱们处理。

查看 Pods 和 Nodes

下面来介绍下 Pod 和 Node:

当咱们建立好 Deployment 的时候,Kubernetes 会自动建立 Pod 来承载应用实例。Pod 是一个抽象的概念,像一个“逻辑主机”,它表明一组应用容器的集合,这些应用容器共享资源,包括存储,网络和相同的内部集群 IP。

任何一个 Pod 都须要跑在一个 Node 节点上。Node 是一个“虚拟机器”,它能够是虚拟机也能够是物理机,一个 Node 能够有多个 Pods,Kubernetes 会自动调度 Pod 到合适的 Node 上。

Service 与 LabelSelector

Pods 终有一死,也就是说 Pods 也有本身的生命周期,当一个 Pod 挂了的时候,ReplicaSet 会建立新的,而且调度到合适的 Node 节点上。考虑下访问的问题,Pod 替换伴随着 IP 的变化,对于访问者来讲,变化的 IP 是合理的;而且当有多个 Pod 节点时,如何 SLB 访问也是个问题,Service 就是为了解决这些问题的。

Service 是一个抽象的概念,它定义了一组逻辑 Pods,而且提供访问它们的策略。和其余对象同样,Service 也能经过 kubectl 或者 YAML 建立。Service 定义的 Pod 能够写在 LabelSelector 选项中(下文会介绍),也存在不指定 Pods 的状况,这种比较复杂,感兴趣的读者能够自行查阅资料。

Service 有如下几种类型:

  • ClusterIP(默认):在集群中内部IP上暴露服务,此类型使Service只能从群集中访问;
  • NodePort:经过每一个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动建立。经过请求 :,能够从集群的外部访问一个 NodePort 服务;
  • LoadBalancer:使用云提供商的负载均衡器,能够向外部暴露服务。外部的负载均衡器能够路由到 NodePort 服务和 ClusterIP 服务;
  • ExternalName:经过返回 CNAME 和它的值,(适用于外部 DNS 的场景)

Labels 和 Selectors 可以让 Kubernetes 拥有逻辑运算的能力,有点像 SQL。举个例子:能够查找 app=hello_word 的全部对象,也能够查找 app in (a,b,c) abc的全部对象。

Labels是一个绑定在对象上的 K/V 结构,它能够在建立或者以后的时候的定义,在任什么时候候均可以改变。

扩容应用

前文提到咱们可使用 Deployment 增长实例个数,下图是原始的集群状态:

咱们能够随意的更改 replicas (实例个数)来扩容,当咱们更改了 Deployment 中的 replicas 值时,Kubernetes 会自动帮咱们达到想要的目标实例个数,以下图:

更新应用

更新应用和扩容相似,咱们能够更改 Deployment 中的容器镜像,而后 Kubernetes 会帮住咱们应用更新(蓝绿、金丝雀等方式),经过此功能,咱们还能够实现切换应用环境、回滚、不停机 CI/CD。下面是部署的过程,须要注意的是咱们能够指定新建立的 Pod 最大个数和不可用 Pod 最大个数:

总结

到了最后,你们对 Kubernetes 有个大概的了解了,但 Kubernetes 远远不止本文所介绍的这些内容。在云原生概念逐渐清晰的今天,Kubernetes 做为 CNCF 中一个接地气的落地项目,其重要性不言而喻。



本文做者: 淘敏

阅读原文

本文来自云栖社区合做伙伴“ 阿里技术”,如需转载请联系原做者。

相关文章
相关标签/搜索