深入剖析kubernetes 1-4章阅读总结

容器的来龙去脉

PaaS

Paas 解决的是应用程序的托管打包分发问题,强调零停机时间部署、自动规模伸缩与负载均衡等功能。用户可以在云服务商租一批虚拟机,然后可以用脚本或手动的方式在上面部署应用。

以 Cloud Foundry 这个 PaaS 项目为例,它的核心组件就是一套应用的打包和分发机制。

  • 用户把应用、启动脚本等打包到一个压缩包中,上传后 Cloud Foundry 会通过调度器选择一个合适的虚拟机,然后通知这个机器的 Agent 下载应用压缩包并启动
  • 由于需要在一个虚拟机上启动多个应用,Cloud Foundry 会调用操作系统的 Cgroups 和 Namespace 机制为每个应用单独创建一个隔离环境唤做“沙盒”,然后在里面启动应用进程。
这样就把多个应用在虚拟机中互不干扰的自动运行了起来,**这些“隔离环境”,就是“容器”,**后文具体介绍。

Docker

Docker 容器和 Cloud Foundry “沙箱”没有本质区别,它主要解决了 PaaS 中“应用打包难”的问题。
Docker 之前应用打包难:

  • 用户必须为每种语言、每种框架,甚至每个版本为一个一个打好的包。
  • 打包过程没有章法可循,且在本地良好运行的应用,需要做很多配置和修改才能在 Paas 中运行。

Docker 的解决方案:

  • 镜像,就是打包了应用程序 + 需要的整个操作系统,从而保证了本地环境和云端环境的高度一致避免了用户通过“试错”来匹配不同运行环境之间差异的过程。

Docker 只是解决了打包部分的问题,但是并不负责“分发”、“应用部署”。
因此,Docker 提供了 Swarm 工具来做集群管理,swarm 集群由管理节点(manager)和工作节点(work node)构成。

  • swarm mananger:负责整个集群的管理工作包括集群配置、服务管理等所有跟集群有关的工作。
  • work node:即图中的 available node,主要负责运行相应的服务来执行任务(task)。

Compose(Fig)项目来做容器编排。

  • 容器编排:指用户通过某些工具或配置来完成一组虚拟机以及关联资源的定义、配置、创建、删除等工作。
  • 对 docker 而言,编排就是对 docker 容器的一系列定义、配置和创建动作的管理。比如定义多个容器之间的依赖关系[2]

K8s

“容器的价值非常有限,真正有价值的是容器编排”,很多项目(Yarn,Mesos,Docker Swarm)擅长的是把一个容器按照某种规则放置到某个最佳节点上执行,这种功能成为“调度”。K8s 擅长的是编排,即按照用户的意愿和整个系统的规则,完全自动化地处理好容器之间的各种关系。

[14]

容器技术基础

容器技术的核心功能,就是通过约束和修改进程的“动态表现”,为其创造一个“边界”。以 Linux 容器为例

  1. NameSpace 技术是用来进行隔离资源的主要方法。
  2. Cgroups 技术是用来限制(限制 cpu、内存、存储等资源)的主要手段

Namespace[3]

Linux Namespace 提供了一种内核级别隔离系统资源的方法,通过将系统的全局资源放在不同的 Namespace 中,来实现资源隔离的目的。 目前提供了 6 种系统资源的隔离机制

  • Mount:隔离文件系统
  • UTS:隔离主机名和域名信息
  • IOC:隔离进程间通信
  • PID:隔离进程的 ID
  • Network:隔离网络资源
  • User:隔离用户和用户组的 ID

以 PID 隔离[7]为例,举个例子:
1)在宿主机启动一个 docker 容器

1
docker run -it busybox /bin/sh

2)查看进程号
左边是容器(隔离出的空间)的中的进程号为 1,对应到右边宿主机中真实的进程好为 30789

3)实际上,docker 利用 Namespace 机制,施了一个障眼法,对被隔离的进程空间动了手脚,使得这些进程看到的是“重新计算过的 PID”

实际上,在创建进程时候传递对应的 NameSpace 参数,来对进程上下文施展各种障眼法,这样进程就只能看到所属 NameSpace 下的资源信息。容器就使用了这种技术,Docker 启动的还是原来的应用进程,只不过在创建这些进程时,Docker 为他们加上了各种各样的 Namespace 参数。所以:容器其实只是一种特殊的进程,用户应用实际上就是容器中 PID=1 的进程。

Cgroups

Linux Cggroups(Linux control groups) 最主要的作用就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。cgroup 通过以下组件来抽象进程和资源[9]

  • task:任务,对应进程
  • subsystem:子系统,具体的资源控制器,控制某个特定的资源使用,如 cpu 子系统控制 cpu 使用时间
  • cgroup:控制组,一组任务和子系统的关联关系,标识对这些任务进行怎样的资源控制策略
  • hierarchy:层级树,一些列 cgroup 组成的树形结构。

进程和 cgroup 是多对多的关系[10]:

Cgroup 使用示例:
1)Linux 下 cgroups 以文件和目录方式组织在 /sys/fs/cgroup 下:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@VM-16-5-centos ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)

2)以 cpu 使用限制为例,在 cpu 下创建 container 目录(控制组)

1
2
3
[root@VM-16-5-centos container]# ls
cgroup.clone_children cpuacct.stat cpuacct.usage_all cpuacct.usage_percpu_sys cpuacct.usage_sys cpu.cfs_period_us cpu.shares notify_on_release
cgroup.procs cpuacct.usage cpuacct.usage_percpu cpuacct.usage_percpu_user cpuacct.usage_user cpu.cfs_quota_us cpu.stat tasks

3)限制 cpu 的使用,表示在 100ms 的时间内,被该控制组限制的进程只能使用 20ms 的 cpu 时间,可以将需要限制的进程 id 写入到 tasks 文件中

1
2
3
echo 100000 > /sys/fs/cgroup/cpu/container/cpu.cfs_period_us
echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
echo $(pid) > /sys/fs/cgroup/cpu/container/tasks

Docker 创建 linux 容器时,只需要在每个子系统下创建对应的控制组(新建目录),然后在启动容器进程后,把整个进程的 PID 天蝎到对应控制组的 tasks 文件中就可以完成资源的限制。如:

1
2
3
4
5
6
7
8
9
10
11
[root@VM-16-5-centos container]docker run -it --cpu-period=100000 --cpu-quota=20000 busybox /bin/sh
[root@VM-16-5-centos docker-4cd72e800ed85e9502818dfc1bec238f653da27e10ce61f74d50fb501f819200.scope]# ls
cgroup.clone_children cpuacct.usage_all cpuacct.usage_sys cpu.shares
cgroup.procs cpuacct.usage_percpu cpuacct.usage_user cpu.stat
cpuacct.stat cpuacct.usage_percpu_sys cpu.cfs_period_us notify_on_release
cpuacct.usage cpuacct.usage_percpu_user cpu.cfs_quota_us tasks
[root@VM-16-5-centos docker-4cd72e800ed85e9502818dfc1bec238f653da27e10ce61f74d50fb501f819200.scope]# cat cpu.cfs_quota_us
20000
[root@VM-16-5-centos docker-4cd72e800ed85e9502818dfc1bec238f653da27e10ce61f74d50fb501f819200.scope]# cat cpu.cfs_period_us
100000
[root@VM-16-5-centos docker-4cd72e800ed85e9502818dfc1bec238f653da27e10ce61f74d50fb501f819200.scope]#

Rootfs[13]

容器进程应该“看到”,一个完全隔离、独立的文件环境,而不是继承自宿主机的文件系统。在 Linux 中要实现这一点,要借助 chroot(change root file system) 命令,可以将指定目录挂载为指定容器进程的根目录。

一般会在容器的根目录下“挂在一个完整的操作系统的文件系统目录”,这个用来为容器进程提供隔离后执行环境的文件系统,就是“容器镜像”,即 rootrs(根文件系统);

  • 这些文件不包含操作系统内核,所以区别于虚拟机同一台机器上所有容器都是共享操作系统内核的。

(容器和虚拟机的对比)

  • 打包操作系统文件目录,赋予了容器的一致性:无论在本地、云端或其他机器上,只需解压打包好的镜像,这个应用运行所需要的完整的执行环境就能重现。

总结

容器实际是由 Linux Namespace,Linux Cgroup 和 Rootfs 3 种技术构建出来的进程的隔离环境,一个运行的 Linux 容器,可以被一分为二的看待:

  1. 容器镜像:一组联合挂载在 /var/lib/docker/aufs/mnt 上的 rootfs,是容器的静态视图
  2. 容器运行时:一个由 Namespace + Cgroups 构成的隔离环境,是容器的动态视图

K8s 的设计

核心能力与定位

在大规模集群中的各种任务之间运行,实际存在各种各样的关系。这些关系的处理才是作业编排和管理系统最困难的地方。K8s 的核心能力就是要解决这个问题,而不是拉取和部署镜像。
K8s 的主要设计思路是:

  • 以统一的方式抽象底层基础设施能力(计算、存储、网络等);定义任务编排的各种关系(如亲密关系、访问关系、代理关系)
  • 以声明式 api(yaml)的方式对外暴露,从而允许用户基于这些抽象构建自己的上层 平台(如自己的 paas)
  • 所以,K8s 的本质是 “平台的平台”,一个帮助用户构建上层平台的基础平台。

如图,K8s 是如何定义任务编排的各种关系的

  • Pod 中的容器共享同一个 Network Namespace、同组 Volume,从而实现高效交换信息。

架构设计[11]

分为 Master 控制节点和 Node 计算节点

  • Master:Controller Manager 负责容器编排;整个集群的数据,由 Api Server 处理后存储在 etcd 中
  • Node:
    • kubelet 主要负责和容器运行时(如 Docker)交互,交互依赖 CRI(container runtime interface)远程调用接口。如 Docker 通过 OCI 容器运行时规范同 Linux 交互,把 CRI 请求翻译为对 Linux 系统的调用(操作 Namespace,Cgroups 等)
    • Device Plugin:是 k8s 用来管理 GPU 等宿主机无力设备的主要组件,kubelet 通过 grpc 和其交互
    • CNI(container networking interface):kubelet 调用其为容器配置网络
    • CSI(container storage interface):kubelet 调用其为容器配置持久化存储

Pod[12]

容器是一个单进程模型,容器不具备进程管理能力,所以一个容器中只包含一个应用进程是合理的;当几个应用(容器)之间具有协作关系,需要共享某些资源,必须放在一起去管理(如必须运行在同一台机器上)要怎么办,而 pod 就是 k8s 给出的解决方案。

Pod 实际上是个逻辑概念,无力上对应的是一组容器;除此之外,Pod 是 Kubernetes 分配资源的一个单位,因为里面的容器要共享某些资源,所以 Pod 也是 Kubernetes 的原子调度单位。

总结

PaaS 解决应用:打包、分发部署、托管执行方面的问题

  • 打包:缺乏标准,打包难问题 -> docker 容器镜像(增量 rootfs、layer)解决了这个问题,成为事实上的打包标准;
  • 执行:依赖 linux os 自身的 namespace、cgroups、chroot 实现的容器技术解决这方面的问题;
    • namespace:隔离进程资源视图
    • cgroups:限制进程的资源使用

PaaS 的 3 个问题解决好后,K8s 解决更上层的问题:容器、应用编排。从而成为平台的平台;如 cloud foundry 和 k8s 的结合[15]:

引用

  1. https://www.spiceworks.com/tech/cloud/articles/what-is-platform-as-a-service/
  2. https://medium.com/@krishnakummar/creating-block-diagrams-from-your-docker-compose-yml-da9d5a2450b4
  3. https://techtutorialsite.com/docker-vs-virtual-machines/
  4. https://www.slideshare.net/AdamFitzGerald/cf-overview-gitpro
  5. https://www.slideshare.net/jaxLondonConference/run-your-java-apps-on-cloud-foundry-andy-piper-pivotal
  6. https://blogs.sap.com/2018/06/05/cloud-native-with-containers-and-kubernetes-part-2/
  7. https://zhuanlan.zhihu.com/p/73248894
  8. https://www.nginx.com/blog/what-are-namespaces-cgroups-how-do-they-work/
  9. https://juejin.cn/post/6921299245685276686
  10. https://tech.meituan.com/2015/03/31/cgroups.html
  11. https://www.cnblogs.com/luoahong/p/12330735.html
  12. http://dockone.io/article/9290
  13. https://www.feiyiblog.com/2020/03/27/%E9%95%9C%E5%83%8F%E5%88%86%E5%B1%82%E7%BB%93%E6%9E%84%E7%90%86%E8%AE%BA%E8%AF%A6%E8%A7%A31/
  14. https://blogs.sap.com/2018/10/19/cloud-foundry-and-kubernetes-where-do-they-differ-how-do-they-fit-together/
  15. https://blogs.sap.com/2021/01/15/back-to-the-future-cloud-foundry-on-kubernetes/