Kubernetes 学习实践
参考书籍
Kubernetes修炼手册 - Nigel Poulton 人民邮电出版社
基础概念
编排器
Kubernetes 是一个应用编排器(orchestrator),Kubernetes 包括但不仅限于以下功能。
- 部署应用程序。
- 根据需要动态扩缩容。
- 当出现故障时自愈。
- 进行不停机的滚动升级和回滚。
Kubernetes 最突出的优点是无须人工干预决策的情况下自动完成以上所有任务。
容器化应用
容器相比传统方式更加轻量、快速、便宜。
2000年以前:应用运行在物理机上;2000-2010年:应用运行在虚拟机上;2010年以后:应用运行在容器中。
Kubernetes 普遍用于容器的编排。目前k8s主流容器是 Containerd 。
云操作系统
物理机上的操作系统(Linux、Windows)会将硬件资源(比如网卡、硬盘)统一抽象,并对进程进行调度。
云上安装k8s时,它会将云上的资源统一抽象,并对云上的应用统一调度。 (可以横跨不同的云包括自建机房,不被某一云厂商绑定,自由负载切换)
主节点 / 控制平面 Master Node / Control Plane
工作节点 Work Node
工作节点主要由3个组件构成: Kubelet
节点代理、CRI
容器运行时接口(Container Runtime Interface)、Kube-proxy
网络代理。
- Kubelet
- 在工作节点加入集群时,Kubelet 负责将当前节点注册到集群中,并将当前节点的Cpu、内存以及存储信息加入到集群资源池
- Kubelet 的一个重要职责是监听 Api Server 分配的新任务,维护与控制平面的通信频道,并将执行结果返回。
- CRI
- 容器运行时接口(Container Runtime Interface) CRI是一个插件接口,它使 Kubelet 能够使用各种容器运行时,无需重新编译集群组件,例如摘取镜像、启动停止或重启容器。CRI 屏蔽了k8s内部运行机制,并向第三方容器提供了接口来支持接入。
- containerd 「发音如“container-dee”」已替代 Docker 成为k8s最流行的容器。
- Kube-proxy
- 是集群中每个节点上所运行的网络代理,维护节点上的一些网络规则,这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。例如 保证每个工作节点都可以获取到唯一的 IP 地址,并实现了本地 IPTABLE 以及 IPVS 来保障 Pod 间的网络路由与负载均衡。
工作节点是k8s集群中的工作者,主要负责以下事情:
- 监听 Api Server 分派的新任务
- 执行新分派的任务
- 向控制平面回复任务执行的结果(通过 Api Server )
Controller管理器
Controller管理器 对集群监控并响应事件,它负责创建 Controller 并监控他们的执行。每个 Controller 都在后台启动了独立的循环监听功能,负责监听 API Server 的变更,这样做的目标是保证集群的当前状态可以与期望状态匹配。
- 每个控制循环实现上述功能的基础逻辑如下:
- 获取期望状态
- 观察当前状态
- 判断两者差异
- 变更当前状态来消除差异点
- 常用的 Controller 包括
Deployment
、DaemonSet
、StatefulSet
Kubernetes DNS
尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该有集群 DNS, 因为很多示例都需要 DNS 服务。
集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。
Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中
Pod
- 在k8s中,调度的原子单位是 Pod , Pod 本身并不会运行任何东西,只是作为一个承载容器的沙箱而存在。
- 在英语中,会将
a group of whales
(一群鲸鱼)称作 aPod
of whale,因为 Docker 的Logo是鲸鱼,所以在k8s中会将包含了一组容器的事物称作Pod。 - 如果在 Pod 中运行多个容器,那么多个容器间是共享相同的Pod环境的。共享环境中包括了IPC命名空间、内存、磁盘、网络以及其他资源等。举一个具体的例子,运行在相同 Pod 中的所有容器,都有相同的IP地址( Pod 的IP地址)。
- 一般来说,用户通过更上层的控制器来完成Pod部署。上层的控制器包括
Deployment
、DaemonSet
、StatefulSet
。Deployment
提供了如扩缩容管理、不停机更新以及版本控制等其他特性。
服务注册与发现
Service
由于 Pod 是不可靠的,上层控制器如 Deployment 会自动替换掉有故障的 Pod ,每个 Pod 的IP都是不同的,Service
组件实现了一组Pod的动态负载均衡,提供了请求路由到Pod的能力。
Kubernetes 存储
卷 Volume
容器中的文件在磁盘上是临时存放的,当容器崩溃或停止时,容器生命周期内创建或修改的所有文件都将丢失;当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题。 跨所有容器设置和访问共享文件系统具有一定的挑战性。卷(Volume) 这一抽象概念为解决这两个问题而出现。
Kubernetes 支持来自多种途径(如云上、自建机房,本地磁盘等)多种类型的存储。无论来自何处、无论什么软硬件的存储,在其对接到k8s时,都被统称为卷
。卷的核心是一个目录,其中可能存有数据, Pod 中的容器可以访问该目录中的数据。卷所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。
Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同, 但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
- 持久卷 (Persistent Volume,PV) 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。
- 临时卷 (Ephemeral Volume) 有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。
容器存储接口 CSI
就像CRI是容器运行时抽象化的接口一样,容器存储接口(Container Storage Interface) CSI将存储驱动的实现与k8s的集成简洁抽象化,不过日常管理一般不会与CSI接触。
k8s持久化系统
概括地说,PV 代表的是 Kubernetes 中的存储; PVC 就像许可证,赋 予 Pod 访问 PV 的权限; CS 则使分配过程是动态的。
- 持久卷(Persistent Volume,PV)
- 持久卷申请 (Persistent Volume Claim,PVC)
- 持久类(Storage Class,SC)
集群配置 ConfigMap
ConfigMap
ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中,将你的配置数据和应用程序代码分开。ConfigMap 在设计上不是用来保存大量数据的。在 ConfigMap 中保存的数据不可超过 1 MiB。如果你需要保存超出此尺寸限制的数据,你可能希望考虑挂载存储卷 或者使用独立的数据库或者文件服务。 ConfigMap 同样支持声明式与命令式配置,配置参数参考 官方文档-ConfigMap
ConfigMap 的使用
通过环境变量使用 ConfigMap
将 ConfigMap 作为环境变量来使用是有缺点的,所有 ConfigMap 的变化事件对运行中的容器是感知不了的。
将 ConfigMap 作为数据卷挂载
将ConfigMap作为数据卷挂载是最灵活的方式,当卷中使用的 ConfigMap 被更新时,所投射的键最终也会被更新。
其他使用方式
- 在容器命令和参数内使用 ConfigMap
- 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap
StatefulSet 与 Deployment
StatefulSet 与 Deployment 的相同点
- 都是 Kubernetes API 中的一等对象,并且遵循典型的 Kubernetes 控制器架构。这些控制器都通过启动对 API Server 的监听循环来观察集群状态,以便能够及时将当前状态调整至与期望状态保持一致。
- Deployment和StatefulSet都支持自愈、自动扩缩容、滚动更新等特性。
StatefulSet 与 Deployment 的差异点
- 名字
StatefulSet 中的 Pod遵循StatefulSetName-Integer
的规则,后面的数字是自增的;而 Deployment 则是随机名字的Pod。事实上,StatefulSet Pod 的 名字与扩缩容操作和关联存储卷操作是息息相关的。 - 部署、扩展、更新、删除顺序
StatefulSet 部署、扩展、更新、删除都要有逐一顺序操作,而 Deployment 中的 Pod 都是无序且并行的。 - 存储
StatefulSet 给定 Pod 的存储必须基于所请求的 storage class 来制备,删除或者扩缩 StatefulSet 并不会删除它关联的存储卷。 - 网络
StatefulSet 只能使用无头服务
headlessService 它没有ClusterIP,无法负载均衡,返回的都是 pod 的 Dns 子域名(默认 <object-name>.<Service-name>.<namespace>.svc.cluster.local
k8s 安全模型
STRIDE 模型分析
- 伪装(Spoofing)
- 防止 k8s 组件的伪装
- k8s 组件与 Api Server的通信均通过 mutual TLS
mTLS
(https 双向认证) - k8s 默认会生成一个证书颁发接口(Certificate Authority, CA) ,CA 一般情况下只在集群内部使用以确保其安全。
- k8s 组件与 Api Server的通信均通过 mutual TLS
- 防止 Pod 的伪装
- 每一个Pod都有一个关联的ServiceAccount,它用于在集群内部为该Pod提供身份证明。Kubernetes会自动将一个服务账号令牌(Service account token)作为Secret挂载到每一个Pod中。
- 防止 k8s 组件的伪装
- 篡改(Tampering)
- 防止 k8s 组件的篡改
- 严格限制对运行有Kubernetes的服务器的访问,尤其是部署了 Kubernetes控制层组件的节点。
- 严格限制对保存有Kubernetes配置文件的库的访问。
- 仅在最初部署Kubernetes时进行远程SSH访问(记得安全保管SSH密钥)。
- 对于下载的二进制文件一定进行SHA-2校验和的查验。
- 严格限制对镜像仓库及相关库的访问。
- 防止 Pod 中应用的篡改
- 通过配置 Pod 描述文件中
spec.securityContext
,能限制容器中的文件系统为只读属性
- 通过配置 Pod 描述文件中
- 防止 k8s 组件的篡改
- 抵赖(Repudiation)
- 可以借住 DaemonSet 在所有节点部署代理程序,收集日志并发送到一个安全的中心日志库并加以审计。
- 信息泄露(Information Disclosure)
- k8s 通过提供 Secret 以加密敏感数据。
- 拒绝服务(Denial of Service)
- 借助防火墙,在内网控制对集群的访问
- 保证集群资源 Contorll Panel、 Work Node、etcd 多活高可用
- 对访问进行监控与预警
- 提升权限(Elevation of Privilege)
- 保护 Api Server
- 基于角色的访问控制(Role-based Access Control, RBAC) 限制仅对指定的用户开放某API操作的权限,此处的 “用户”既可以是普通的用户账号,也包括系统服务。
- 保护 Pod
- 通过配置 Pod 描述文件中
spec.securityContext
避免进程以root身份运行、启用用户命名空间(user namespace)、维护一个map来记录UID的使用情况、禁止容器中的权限扩大。 - 通过配置 Pod 描述文件中
spec.securityContext.capabilities
或者 赋予应用系统权限。 - 使用 seccomp 限制容器的系统调用
- 通过配置 Pod 描述文件中
- 保护 Api Server