K8s 入门笔记
本文第一章来源自 Weekend read, Serverless, Docker, Kubernetes,及文中涉及的五篇 tutorials。主要针对以下问题:
- 什么是 K8s?
- Pods、Nodes、Services,又是什么?
- Serverless 又是什么?
- Cluster 该如何开始,又该怎么维护?
本文第二章开始,就是对极客时间出品的《深入剖析Kubernetes》,进行概括总结。
一、基本概念
k8s 的使用背景:需要多台机器分担负载并可能随时发生弹性缩放的场景下,k8s 是处理 containers 的好的实践方案。
- 使用 containers 可实现环境的复用,让 dev/pre/Online 环境都表现得一致。
- 使用 container API 可一次管理多个 containers,适用于任务调度、负载均衡、分布式等场景。
k8s 是一个开源系统,能够自动进行部署、缩放、管理容器内的 app 等。
Minikube 是一个工具,能够让k8s 跑在本地。
- 另外有一个开源的 kubernetes-client,不知道跟 Minukube 相比孰优孰劣。
Nodes——作为 Pod 运行的地方。通常是一个 worker machine(可以是物理机或者虚拟机)。
- Node :pods = 1 :N
- 在集群中,Master 通过 Nodes 管理 pods。
- 每个 Node 都有至少一个Kubelet,至少一个container runtime
- Kubelet,负责 k8s master 跟 node 之间交流的进程, 负责管理 pods,是 node 节点的核心。kubelet 主要负责同容器运行时(比如 Docker 项目)打交道。而这个交互所依赖的,是一个称作 CRI(Container Runtime Interface)的远程调用接口,这个接口定义了容器运行时的各项核心操作,比如:启动一个容器需要的所有参数。
- 所以 K8s 不关心部署的是什么容器运行时,只要能够运行标准的容器镜像,就可以通过 CRI 接入到 K8s 中。
- kubelet 还通过 gRPC 协议同一个叫作 Device Plugin 的插件进行交互,用在对 GPU 等硬件使用上。
- kubelet通过 CNI 和 CSI 两个接口调用网络插件和存储插件为容器配置网络和持久化存储。
- A container runtime(如 docker),负责从 registry 中拉 image,开启 container,并运行 app。
- Kubelet,负责 k8s master 跟 node 之间交流的进程, 负责管理 pods,是 node 节点的核心。kubelet 主要负责同容器运行时(比如 Docker 项目)打交道。而这个交互所依赖的,是一个称作 CRI(Container Runtime Interface)的远程调用接口,这个接口定义了容器运行时的各项核心操作,比如:启动一个容器需要的所有参数。
Pods——k8s平台上的原子单位,是部署环境的最小单元。
最精辟的定义:Pod,其实是一组共享了某些资源的容器。详细解释:Pod 里的所有容器,共享的是同一个 Network Namespace,并且可以声明共享同一个 Volume。
通常是一个或一些 containers。常见的有 docker、containers 之间的共享资源(如共享存储、网络、镜像信息等)。
pod 里的containers,共享同一个 ip 和端口。就算是同一个 Node 里的 Pod,也有唯一独立的 IP 地址。
Pods 是具有生命周期的,当一个 Pod 死亡时,K8s 会通过一个叫做 ReplicaSet 的机制开启新的 Pods 来保证 app 的正常运行。
- 使用 Service 来实现 Pods 间的同步机制。
- 死亡后 ip 地址回收,重启 Pods 时,会发放新的 ip 地址。所以会出现同一个 Pod,经过短暂下线又上线后,换了个 ip 的情况。
K8s 中的 Service 维护了一系列 Pods 的集合,并提供访问它们的方式。
- 用 Label 和 Selector 来实现 Service 跟 Pods 之间的沟通,通常用 Label+logicalName 来指代某个Pod,而不是 ip 地址。Label 是 K-V 格式的。
- 有依赖关系的 Pods 间使用 yaml/json 进行记录。
Service 有多种分类(角色):
- ClusterIP(default):仅允许集群访问的内部 IP。
- NodePort:让集群中的某一部分 Node使用同一个端口暴露服务,用 NAT 来实现。
- 外部使用”:”访问。
- 是 ClusterIP 的父集合。
- LoadBalancer:一个外部的负载均衡器,通常分配一个固定的外部 IP。
- 是 NodePort 的父集合。
- ExternalName:相当于取一个别名,然后返回一个 CNAME 记录。
- 不使用代理,需要 v1.7 或更高版本的 kube-dns。
像上文提到的 Docker,其实就是一种 container runtime,帮助用户创建可复制的环境(指定 os、lib 的版本、环境变量等等)。Docker 负责创建 container,这个 container 中会包含 app 运行需要的所有东西(含源码)。
- 注意 Docker 跟虚拟机的区别。Docker 使用 layered file system(分层文件系统),让containers 之间共享部分资源,相比虚拟机,更少出现独占资源的情况。
- Docker containers 间相互独立,而且是轻量级的、安全的系统单元。
- 欲开启一个 Docker。首先需要定义一个
Dockerfile
,文件中需要指定 os、环境变量等信息。然后创建 image(或者从别的地方拉)。最后运行一个container。 - Docker 采用 volume (或称为 Docker volume 数据卷)的方式向 containers 提供持久化存储,比如存储 log、json、SQLite 等,就算 containers 生命周期结束了,存在 volume 中的数据也不会被删除。
- 使用 Docker Compose 实现组件复用,具备了微服务的思想。使用了 Compose 后,开启 Docker时,可能仅需要定义 Dockerfile了,后续的image 和 containers 的管理都可以交给compose 来完成。
- 使用 docker-compose.yaml 进行配置,配置内容为:Build、Environment、Image、Networks、Ports、Volumes 等。
- 更多 Docker 内容参考:Docker 菜鸟教程
Serverless,服务端逻辑运行在无状态的计算容器中,通常是 Event-driven。
并非不需要服务器作为计算资源,而是程序员无需理会计算资源的申请和维护,而交由平台来管理。
二、极客时间《深入剖析Kubernetes》 学习笔记
docker 最简介绍,源自 PaaS,但因为 DockerImage 的存在,使得程序在打包时附带上了操作系统的文件系统,使得程序在多服务器上实现近似的”一键部署”的功能。
沙盒技术,容器本质是一种具有边界的沙盒技术,沙盒中盛装的就是外部应用。
- 边界:容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”。
- 回顾进程的概念:一个程序运行起来后的计算机执行环境的总和。
- Docker 容器属于单进程模型,是指只有一个进程时 Docker 可控的。所以Docker 容器是允许多个进程的,区别在于其他进程不为 Docker 容器控制。
Docker 最核心两个技术:Cgroups 和 Namespace。
1. Groups
Cgroups,或叫 Linux Control Groups:制造约束(边界)的主要手段,多是指资源限制。
限制 docker 控制的进程对 host os (宿主机)的资源使用情况,避免此进程饥饿或占据过多资源(CPU、内存等)。
Cgroups 就是用来限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等的工具。
具体过程是:使用一个子系统目录加上一组资源限制文件完成资源限制的意图。
1 | # cgroup 挂载的资源(cpuset(单独 cpu 核与内存结点),cpu,cpuacct,blkio(IO 限制),memory等) |
2. Namespace
也可以在 Docker run
命令执行时带上一些参数。
Namespace:修改进程视图的手段,包括 PID Namespace、Mount/UTS/UTS/Network/User 等Namespace。
PID Namespace 举例:一个 docker 在 run 了某个应用之后,此应用只能看到一个全新的进程空间(其实是 mock 空间),此空间里一般只有自己应用的进程(pid 被修改为 1,也是后续创建进程的父进程),看不到其他进程。
- 为什么这么设计?因为这么做之后,容器应用就可以跟应用同生命周期,对后续的容器编排非常重要。
Mount Namespace:只让当前应用看到此 namespace 下的挂载点信息。
挂载:将新的文件系统关联至当前根文件系统(一般在分区后使用,举例:外置硬盘都被当成 file,但需要挂载后才能被 linux 识别)。参考:Linux 文件系统挂载, Linux中挂载详解以及mount命令用法,一文看懂linux的挂载原理和流程。
Mount Namespace 对容器进程视图的改变,一定是伴随着挂载操作(mount)才能生效。
- 完整操作系统文件一般是root 开始,然后
/etc,/dev,…
文件,挂载时,将一套完整的 os 文件挂载到某个子目录下,这样一来,docker 中的进程只能看到此子目录下的文件系统,实现管窥而不得全貌。这一套完成的 os 文件系统也是俗称的容器镜像,官方名称—— rootfs(根文件系统,撑死几百兆,比虚拟机 OS 占用空间小太多了)。
1
2
3
4# rootfs 包含的一级目录:
ls /
bin dev etc home lib lib64 mnt opt proc root run sbin sys tmp usr var- 完整操作系统文件一般是root 开始,然后
docker 是使用命令
pivot_root 系统调用
来实现以上文件挂载过程的(此命令借鉴并升级 自 linux 系统中的chroot
命令)。用户只需要解压打包好的容器镜像,那么这个应用运行所需要的完整的执行环境就被重现出来了。
不过这里的容器镜像其实是增量镜像(笔者自己编的词),因为一个 rootfs 可能是经过多次多人修改后的,每次修改都保存全量并不现实,所以会带上一些增量记录(专业名词:lay),将多个增量 lay 进行文件合并(AuFS命令,联合文件系统),最终组成一个完成的 os 文件系统。
rootfs 一般分成三成:只读层、可读写层、init 层。只读层对应的正是 ubuntu:latest 镜像的五层。可读写层默认为空,一般增删改都发生在这一层,如果是删除操作,会通过创建
whiteout
文件,遮挡住只读层的文件来实现。init 层是容器启动时用户想设置的 hosts、resolv.conf 等信息,此信息一般不会再 Docker Hub 间流通,所以放在 init 层中。
- 上面的读写层通常也称为容器层,下面的只读层称为镜像层,所有的增删查改操作都只会作用在容器层,相同的文件上层会覆盖掉下层。知道这一点,就不难理解镜像文件的修改,比如修改一个文件的时候,首先会从上到下查找有没有这个文件,找到,就复制到容器层中,修改,修改的结果就会作用到下层的文件,这种方式也被称为copy-on-write。
- 查了一下,包括但不限于以下这几种:aufs, device mapper, btrfs, overlayfs, vfs, zfs。aufs是ubuntu 常用的,device mapper 是 centos,btrfs 是 SUSE,overlayfs ubuntu 和 centos 都会使用,现在最新的 docker 版本中默认两个系统都是使用的 overlayfs,vfs 和 zfs 常用在 solaris 系统。
- 镜像的多层结构使得它可以在多个镜像之间共享和征用。
需要明确的是,挂载了文件系统,但内核仍然是宿主机内核,而且无解。
Network Namespace:只让当前应用看到此 namespace 下的网络设置和配置。
但是,如果 docker 中的 app 想修改时间,然后影响到了 host os 的时间,这种情况是不能被允许的。所以对于 docker 来说,有一些资源和对象(比如时间),是不能被 Namespace 化的。
一个进程的每种 Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上。
3. Docker 技术与虚拟机比较
![](1.jpg)
左虚拟机,右 docker。
虚拟机需要 Hypervisor 对应用进程的隔离负责,需要 Guest OS 作为真实的虚拟机提供一个各种虚拟化软件,占用了百兆的内存资源。而且多经过虚拟化软件这一层对 IO、网络等性能损耗也大。
docker 不需要 Hypervisor 和 Guest OS ,更加敏捷、更高性能。
但是就是因为没有 GuestOS,那么也就不存在自己专属的 OS 内核。
举例:在左图中,hostOS 可以是 win,但 app 是运行在 linux 环境下,所以GuestOS 安装上linux后,app 就可以运行。
但在右图,如果是同一个 hostOS 跟 app,那么因为内核是 win,所以 app 无法正常运行。即使可以挂载不同 OS 的操作系统文件,但内核没变,也变不了。
结论:如果应用依赖内核版本,那么 docker 也无法解决,除非再使用虚拟机。
数据卷 Volume
- Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。
- 挂载技术:Linux 的绑定挂载(bind mount)机制。挂载的本质就是修改 inode,实现了重定向。