k8s学习笔记(二、k8s组成架构及容器基本原理)

1.Kubernetes 架构

典型的k8s中包含两类节点:

  • Master-Node:主要负责对集群进行调度,管理容器的创建、销毁等。
  • Worker-Node:主要负责对运行容器化应用

一套典型的高可用k8s集群架构如下图所示:

img

由于已经安装好了生产环境,因此对照生产环境中的docker镜像对比学习系统组件。

1.1 Master节点组件

使用kubeadm安装集群时,master节点中的docker环境下会下载如上图所示对应的docker-image。

image-20210729151325418

可以看到,所下载的docker image与架构图中各部分基本上一一对应(多出来了flannel、pause、coredns),各组成部分功能角色如下:

  • kube-apiserver:通过调用kube-apiserver提供的api接口,用户可以对集群中的node、deployments、pods、service进行管理。我们使用的kubectl工具就是通过调用api对集群进行的管理。apiserver是唯一一个会和etcd集群进行交互的组件,它会在etcd集群中存取数据。
  • kube-scheduler:对pods的部署调度就是这个组件负责的,该组件会对Worker-Node的资源进行统计,然后决策应该将pod部署在哪一台Worker-Node中。
  • kube-controller-manager:这个里面包含了许多控制程序,这些控制程序会对集群中部署内容的状态进行监控,例如当某个deployment的image变化时,该组件负责对pods进行更新。
  • kube-proxy:为pods和外部用户创建代理网络,使得外部用户能够访问到pods中提供的服务。
  • flannel:是一种网络规划服务,简单来说,通过该服务能够让集群中的各个docker容器处于同一个虚拟内网之中。
  • pause:pause容器主要用于给pod中的各个业务容器共享网络和挂载卷,例如一个php容器和一个mysql容器,可以同时将自己的网络和一个pause容器进行共享。此时,若mysql通过localhost的3306端口对外提供服务,则php容器可以通过localhost:3306访问到该mysql提供的服务。
  • coredns:集群内部的dns服务器。
  • etcd:键值对数据库,官方提供了两种master集群的部署方式,etcd既可以和master节点安装在一起,也可以单独做成一个集群。

1.2 Worker节点组件

Worker节点上运行的组件如下:

kube-proxy:pods内部的容器和外部用户通信就是靠这个proxy代理

flannel:和master节点里的用途一样。

pause:和master节点里的用途一样。

kubelet:通过kubadm安装的worker-node中这个服务是直接本机运行的,主要负责跟master节点汇报、获取工作内容。

2.容器的基本原理

容器技术需要解决几个主要的技术问题,即:

  • 限制每一个容器能够使用资源的数量,不能让单一容器大量消耗cpu、内存等资源
  • 容器间隔离,不能让一个容器无限制的访问其余容器的网络或进程内存
  • 文件系统隔离,使用某个镜像建立一个容器,对该容器的文件系统中的文件修改,不能影响其他使用该镜像创建的容器
  • 特权分配问题,容器技术本质上是对进程进行权限、资源访问限制。而进程中有时会需要一定的特权保证程序正常运行。

以下针对上述技术问题分别学习docker中的解决方案。

2.1 cgroup

2.1.1 cgroup简介

cgroup全称control group,是linux操作系统提供的一个特性,用于解决第一个问题即资源限制问题。有点类似于组策略对于组内成员的管理,cgroup也是以分组的方式对进程能够访问的资源进行集中管控。在容器技术中,主要用于限制容器能够使用的资源数量。例如,我们在执行创建docker容器的时候,就可以指定参数,要求容器最多只能使用多少cpu或者内存。

docker-limit-resource

比如我们可以限制某个docker容器最多只能使用128M内存,

root@worker-node:~# docker run -d --memory 128MB testapp:v1.0
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1

root@worker-node:~# docker exec f5eabd cat /proc/1/cgroup
12:devices:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
11:freezer:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
10:pids:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
9:memory:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
8:blkio:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
7:cpuset:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
6:perf_event:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
5:rdma:/
4:net_cls,net_prio:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
3:cpu,cpuacct:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
2:hugetlb:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
1:name=systemd:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope
0::/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope

root@worker-node:~# cat /sys/fs/cgroup/memory/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope/memory.limit_in_bytes 
134217728

当拿到某台主机shell时,可以先看/proc/1/cgroup文件来确定是否处于容器环境中,一般的docker容器下的内容如下图所示,会出现明显的docker字样:

image-20210729203219831

而身处k8s的pods中时,则会看到明显的kube字样,如下图所示,也会存在明显的kubepods字样:

cgroupInK8s

cgroup V1共包含13个子系统, 13个子系统概述如下:

子系统 功能
cpu 主要用于限制进程的cpu使用率
cpuacct 提供进程对于cpu使用情况的统计能力限制
cpuset 可以帮助cgroup内的进程绑定一组cpu或者非统一内存访问节点
freezer 可以挂起或者恢复cgroup内的进程
hugt1b 限制组内进程使用大页内存
blkio 用于限制组内进程对指定块存储设备的I/O使用速率
memory 用于限制组内进程内存、内核内存、swap空间使用
perf_event 允许组内进程执行perf(性能)监控
pids 限制组内进程最大创建进程的数量
rdma 限制组内进程是否允许使用RDMA(Remote Direct Memory Access)
net_cls 给由cgroup内进程创建的网络数据包添加classsid
net_prio 用于设置网络流量优先级
devices 限制进程能够创建(mknod)或者读写devices

2.2 Namespace

容器间隔离依靠操作系统所提供的namespace(命名空间)机制。linux命名空间包含了以下6种不同的命名空间:

  • Mount namespace:功能类似于chroot。隔离mount操作,子进程中所有的mount操作都不会对父进程产生影响
  • UTS(UNIX Time-sharing System) namespace:隔离主机名和网络信息服务
  • IPC namespace:隔离进程间通信
  • PID namespace:隔离PID
  • Network namespace:隔离网络
  • User namespace: 隔离用户和组

参考https://www.cnblogs.com/linhaifeng/p/6657119.html中的代码进行学习效果及佳!

2.3 rootfs

镜像的本质就是一个rootfs,简单来说就是docker容器的根目录。docker容器启动后会以镜像为基础创建一个只读的rootfs,然后挂载一个可写的层用于记录修改。通过这种方式,即可对于同一镜像产生的不同容器实现文件系统的隔离。

2.4 capabilities

capabilities是对linux操作系统中root用户所拥有特权的细化,早期使用suid对某个文件赋予执行时具备root用户全部权限,例如passwd修改用户密码的命令就是这样。但一次性赋予用户全部root特权较为危险,因此linux操作系统对root用户特权进行了拆分,也就是capabilities(kernel2.2之后才有的)。

可以使用capsh命令查看或编辑capabilities,例如可以使用capsh --print查看当前特权列表:

image-20210808154804696

容器中可能没有该命令,可以读取/proc/1/status查看当前容器所具有的特权列表:

root@161f90c0aab4:/# cat /proc/1/status 
...省略...
CapInh: 0000003fffffffff
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
...省略...

上面命令执行结果包含了五个Capabilities集合,各集合含义如下:

名称 说明
CapInh Inheritable集合,可继承特权集合。
CapPrm Permitted集合,允许申请的特权集合。该集合中没有的特权是不允许线程申请的
CapEff Effective集合,有效特权结合,内核在检查的时候检查的就是这个集合,也是我们重点关注的集合。
CapBnd Bounding集合,边界特权集合,如果某个特权不在该集合中,即使在Permitted集合里该线程也不能添加到Bounding集合
CapAmb Ambient集合,linux4.3新增的一个集合。具体可以参考Linux-capabilities

对于当前容器所拥有的权限,可以查看CapEff中的值,然后使用capsh --decode来显示特权:

image-20210808162555684

现有的特权详情如下:

特权名 说明 备注
CAP_AUDIT_CONTROL 进行开启和关闭内核审计,是否允许修改审计规则
CAP_AUDIT_READ 通过socket读取审计日志
CAP_AUDIT_WRITE 向审计日志写入记录 默认就有
CAP_BLOCK_SUSPEND 阻止系统挂起
CAP_BPF 使用BPF包过滤器
CAP_CHECKPOINT_RESTORE 修改/proc/sys/kernel/ns_last_pid,能否使用set_tid,能否读取其他进程的/proc/[pid]/map_files
CAP_CHOWN 修改文件的UIDs和GIDs 默认就有
CAP_DAC_OVERRIDE 绕过文件的读写执行权限检查、执行open_by_handle_at 默认就有,配合CAP_DAC_READ_SEARCH可以向宿主机内写入文件进行逃逸
CAP_DAC_READ_SEARCH 绕过文件读执行检查、执行open_by_handle_at、使用AT_EMPTY_PATH flag创建到文件的链接 可以读取宿主机文件
CAP_FOWNER 忽略文件UID和进程UID必须相同的限制 默认就有
CAP_FSETID 文件被修改时不清除set-user-ID 和 set-group-ID 默认就有
CAP_IPC_LOCK 锁定内存或使用大页
CAP_IPC_OWNER 绕过System V IPC object的权限检查
CAP_KILL 对不属于自己的进程发送Signal 默认就有
CAP_LEASE 修改文件锁的FL_LEASE标志
CAP_LINUX_IMMUTABLE 设置FS_APPEND_FL and FS_IMMUTABLE_FL标志
CAP_MAC_ADMIN 修改MAC配置
CAP_MAC_OVERRIDE 覆盖强制访问控制(Mandatory Access Control)
CAP_MKNOD 允许使用mknod 默认就有,容器逃逸时有时会使用该命令挂载宿主机的硬盘
CAP_NET_ADMIN 网卡配置,ip修改,修改route等
CAP_NET_BIND_SERVICE 绑定服务在1024号以下端口 默认就有
CAP_NET_BROADCAST Socket广播或混杂模式监听
CAP_NET_RAW 使用原始套接字 默认就有
CAP_PERFMON 多种性能监控
CAP_SETGID 允许改变进程GID 默认就有
CAP_SETFCAP 为文件设置任意特权 默认就有
CAP_SETPCAP 将当前集合全部转移到另一PID 默认就有
CAP_SETUID 改变进程UID
CAP_SYS_ADMIN 可以挂载文件系统、设置磁盘配额等 可用于逃逸,在宿主机执行命令
CAP_SYS_BOOT 可以重启系统
CAP_SYS_CHROOT 使用chroot 默认就有
CAP_SYS_MODULE 允许加载内核模块 可以通过加载内核模块逃逸
CAP_SYS_NICE 设置进程优先级
CAP_SYS_PACCT 允许使用acct(切换进程统计开关)
CAP_SYS_PTRACE 利用ptrace调试任意进程 配合与宿主机pid namespace共享,可注入进程逃逸
CAP_SYS_RAWIO 访问 /devport、/dev/mem、/dev/kmem 及原始块设备
CAP_SYS_RESOURCE 忽略资源限制
CAP_SYS_TIME 设置系统时间
CAP_SYS_TTY_CONFIG 配置TTY设备
CAP_SYSLOG 使用syslog读取内核消息
CAP_WAKE_ALARM 捕捉唤醒系统的消息

参考文献:

https://www.aquasec.com/cloud-native-academy/kubernetes-101/kubernetes-complete-guide/

http://www.dockone.io/article/9004

https://www.cnblogs.com/linhaifeng/p/6657119.html

https://man7.org/linux/man-pages/man7/cgroups.7.html

https://blog.csdn.net/alex_yangchuansheng/article/details/102796001

https://man7.org/linux/man-pages/man7/capabilities.7.html

https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities