作者:张强
爱可生研发核心成员,后端研发工程师,目前负责 DMP 产品 Redis 相干业务开发。
本文起源:原创投稿
* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。
一. 什么是 Kubernetes ?
Kubernetes,又称为 k8s(首字母为 k、首字母与尾字母之间有 8 个字符、尾字母为 s,所以简称 k8s)或者简称为「kube」,是一个可移植的、可扩大的开源平台,用于治理容器化的工作负载和服务。相比与传统部署以及虚拟化部署形式而言,具备如下特点:
- 麻利应用程序的创立和部署:与应用 VM 镜像相比,进步了容器镜像创立的简便性和效率。
- 继续开发、集成和部署:通过疾速简略的回滚(因为镜像不可变性),反对牢靠且频繁的 容器镜像构建和部署。
- 关注开发与运维的拆散:在构建 / 公布时而不是在部署时创立应用程序容器镜像,从而将应用程序与基础架构拆散。
- 可察看性:不仅能够显示操作系统级别的信息和指标,还能够显示应用程序的运行状况和其余指标信号。
- 跨开发、测试和生产的环境一致性:在便携式计算机上与在云中雷同地运行。
- 跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、Google Kubernetes Engine 和其余任何中央运行。
- 以应用程序为核心的治理:进步形象级别,从在虚构硬件上运行 OS 到应用逻辑资源在 OS 上运行应用程序。
- 涣散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立局部,并且能够动静部署和治理 – 而不是在一台大型单机上整体运行。
- 资源隔离:可预测的应用程序性能。
- 资源利用:高效率和高密度。
1. Docker 的衰亡
要说 Kubernetes,还要从 docker 容器化技术谈起。
2013 年,Docker 公司(彼时还称之为 dotCloud Inc.)在 PyCon 大会上首次公开介绍了 Docker 这一产品。过后最热门的 PaaS 我的项目是 Cloud Foundary,然而 Docker 我的项目在 docker 镜像上的优良设计,解决了过后其余 PaaS 技术未能解决的利用打包和利用公布的繁琐步骤问题。大多数 docker 镜像是间接由一个残缺操作系统的所有文件和目录形成的,所以这个压缩包里的内容跟你本地开发和测试环境用的操作系统是齐全一样的 —— 正是这一优良的个性使得 docker 我的项目在泛滥 Pass 技术迅速崛起。
2. Docker 编排的进化
Swarm 的最大特点,是齐全应用 Docker 我的项目本来的容器治理 API 来实现集群治理。在部署了 Swarm 的多机环境下,用户只须要应用原先的 Docker 指令创立一个容器,这个申请就会被 Swarm 拦挡下来解决,而后通过具体的调度算法找到一个适合的 Docker Daemon 运行起来。
2014 年,docker 并购 Fig 我的项目,将其倒退为当初的 Compose,使得容器的部署更加的便当 —— 能够应用配置文件就能够实现较为简单的容器的编排参数的配置。同年 6 月,Google 公司公布了 Kubernetes。
2015 年 6 月 22 日,由 Docker 公司牵头,CoreOS、Google、RedHat 等公司独特发表,Docker 公司将 Libcontainer 捐出,并改名为 RunC 我的项目,交由一个齐全中立的基金会治理,而后以 RunC 为根据,大家独特制订一套容器和镜像的规范和标准。这套规范和标准,就是 OCI(Open Container Initiative)。OCI 的提出,意在将容器运行时和镜像的实现从 Docker 我的项目中齐全剥离进去。从 API 到容器运行时的每一层,Kubernetes 我的项目都为开发者暴露出了能够扩大的插件机制,激励用户通过代码的形式染指到 Kubernetes 我的项目的每一个阶段。Kubernetes 我的项目的这个改革的成果空谷传声,很快在整个容器社区中催生出了大量的、基于 Kubernetes API 和扩大接口的二次翻新工作,比方:
- 微服务治理我的项目 Istio;
- 有状态利用部署框架 Operator;
- 还有像 Rook 这样的开源创业项目,它通过 Kubernetes 的可扩大接口,把 Ceph 这样的重量级产品封装成了简略易用的容器存储插件。
二. Docker 容器的技术根底
1. 过程与 Linux Namespaces
为了达到在容器中排除其余过程,docker 利用了 Linux 的 Namespace 机制。在 Linux 零碎中创立线程的零碎调用是 clone(),比方:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
此时,这个零碎调用就会为咱们创立一个新的过程,并且返回它的过程号 pid。而如果在用 clone() 零碎调用创立一个新过程时,退出 CLONE_NEWPID 参数,比方:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
此时这个过程就会在一个全新的过程空间里,在这个过程空间里,它的 pid 是 1。除去 PID Namespace,Linux 零碎还提供了 Mount、UTS、IPC、Newwork 和 User 等 Namespace。这就是为什么容器中只能「看到」本身容器内的过程的起因了。
2. 资源限度与 Linux Cgroups
尽管在容器中 PID 为 1 的过程只能看到容器里的状况。然而从宿主机角度来看,它作为一个一般过程与其余过程仍然是平等关系,也就说它可能应用的资源(CPU、内存等),仍旧能够被宿主机上的其余过程占用。这种状况导致了容器内的过程「隔离了但没有齐全隔离」的难堪,是不合乎容器作为一个「沙盒」的个性的。
Linux Cgroups 的全称是 Linux Control Groups。它的最次要作用,就是限度一个过程组可能应用的资源下限,包含 CPU、内存、磁盘、网络带宽等。比方:
- blkio,为块设施设定 I/O 限度,个别用于磁盘等设施;
- cpuset,为过程调配独自的 CPU 核和对应的内存节点;
- memory,为过程设定内存应用的限度。等等
凭借 Cgroups 的机制,容器化技术能够实现对容器内过程的资源限度。
3. 容器镜像和文件系统
Linux 操作系统中有一个名为 chroot
的命令,作用就是扭转过程的根目录到指定的地位。比方执行 chroot $HOME/test /bin/bash
之后,如果执行ls /
,就会看到命令返回的内容都是 $HOME/test 目录下的内容。Linux 操作系统的第一个 Namespace 是 Mount Namespace,正是基于 chroot 的一直改进而来的。
为了让容器中的目录看起来更「实在」,个别会在容器内的根目录下挂载一个残缺操作系统的文件系统。比方 Ubuntu 16.04 的 ISO。这样在启动容器后,在容器内执行 ls /
查看根目录下的内容,就是 Ubuntu 16.04 的所有目录和文件。这个挂载在容器根目录上、用来为容器过程提供隔离后执行环境的文件系统,就是所谓的「容器镜像」,也称之为 rootfs(根文件系统)。
所以,对于 Docker 来说,外围流程即:
- 启用 Linux Namespace 配置;
- 设置指定的 Cgroups 参数;
- 切换过程的根目录(pivot_root 或 chroot)。
同时须要留神:rootfs 只是一个操作系统所蕴含的文件、配置和目录,并不蕴含 Linux 操作系统内核。所有的容器,都共享宿主机操作系统的内核。
综上,一个「容器」,实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建进去的过程的隔离环境。能够分为两局部:
- 容器镜像,即联结挂载在 /var/lib/docker/aufs/mnt 上的 rootfs。
- 容器运行时,即一个由 Namespace + Cgroups 形成的隔离环境。
三. Kubernetes 架构简介
从一个开发者的角度来说,真正须要关注的是容器镜像。从容器集群治理角度来说,而可能定义容器组织和治理标准的「容器编排」技术是最重要的。其中最具代表性的容器编排工具,当属 Docker 公司的 Compose+Swarm 组合,以及 Google 与 RedHat 公司独特主导的 Kubernetes。
Kubernetes 脱胎与 Google 外部的 Borg 我的项目。而 Borg,是承载 Google 公司整个基础设施的外围依赖。在 Google 公司曾经公开发表的基础设施体系论文中,Borg 我的项目当仁不让地位居整个基础设施技术栈的最底层。同时在开源社区的致力下,Kubernetes 又修复了原来 Borg 体系中的缺点和问题。借着 Borg 我的项目的实践劣势,逐渐确定了一个如下图所示的全局架构:
Kubernetes 我的项目的架构,都由 Master 和 Node 两种节点组成,别离对应着管制节点和计算节点。其中:
-
Master 管制节点
- 负责 API 服务的 kube-apiserver;
- 负责调度的 kube-scheduler;
- 负责内容编排的 kube-controller-manager;
- 负责集群长久化数据的 etcd。
-
Node 计算节点
- 负责与容器运行时交互的 kubelet 组件。另外,还为容器配置网络和长久化存储;
- 负责保护节点上的网络规定 kube-proxy。
- 容器运行时,Kubernetes 反对 Docker、containerd、CRI-O 等多个容器运行环境。
以上设计的考量是 Kubernetes 没有把 Docker 作为架构的外围,而仅仅把 Docker 作为一个底层的容器运行时实现。Kubernetes 最外围的问题是:运行在大规模集群中的各种工作之间,实际上存在着各种各样的关系。这些关系的解决,才是作业编排和管理系统最艰难的中央。
在一个稍简单的集群零碎中,会有各种不同的服务关系存在,比方:一个 Web 利用与数据库之间的拜访关系,一个计算服务和监控套件之间的拜访关系等。在容器技术遍及之前,处于部署上的便当,这些利用可能会被部署在同一台虚拟机中。在容器技术呈现后,本来的各个利用、组件、守护过程,都能够被别离做成镜像并且运行在每个专属的容器中。它们之间互不干涉,领有各自的资源配额,能够被调度在整个集群的任何一台机器上。
为了更好地解决容器间利用的拜访关系,连贯严密的容器会被划分为一个「Pod」,Pod 里的容器共享同一个 Network Namespace、同一组数据卷等。同时 Kubernetes 会给 Pod 绑定一个 Service 服务(kube-proxy),次要作用是作为 Pod 的代理入口,从而代替 Pod 对外裸露一个固定的网络地址。
Kubernetes 还定义了基于 Pod 改良后的对象。比方用 Job 来形容一次性运行的 Pod;用 DaemonSet 来形容有且只有一个正本的守护过程;又比方 CronJob,用于形容定时工作。
Kubernetes 应用申明式 API 形式来编排利用。所谓申明式,就是提交一个定义好的 API 对象来「申明」,示意所冀望的最终状态即可。如果提交的是一个个命令,去一步一步达到冀望状态,这就是「指令式」。比方,应用 kubernetes 来启动一个 Nginx 容器镜像,具体步骤为:
- 编写一个名为 nginx-deployment.yaml 文件,定义一个 Depoloyment 对象。主体是一个应用 Nginx 镜像的 Pod,能够定义正本数为 2(replicas=2);
- 执行命令
kubectl create -f nginx-deployment.yaml
来启动容器。
从设计指标来看,Kubernetes 提供了一套基于容器的、可能便当地构建分布式系统的根底依赖,可能实现容器利用的部署以及利用的弹性治理。它所善于的,就是依照用户的定义和零碎规定,来主动解决容器(利用 / 服务)之间的各种关系。而这所有,都是基于容器化技术来实现的。至于更进一步的学习 Kubernetes 架构相干常识,以及 Kubernetes 环境下的环境开发,请见后续相干文章更新。
四. 参考
[1] 维基百科:Kubernetes 词条 (https://zh.wikipedia.org/wiki…)
[2] Kubernetes 官网文档 (https://kubernetes.io/)
[3] 深刻分析 Kubernetes (https://time.geekbang.org/col…)
[4] 维基百科:Linux namespaces 词条 (https://en.wikipedia.org/wiki…)
[5] 维基百科:Cgroups 词条 (https://zh.wikipedia.org/wiki…)