November 22, 2020
所有 Kubernetes 集群都有两类用户:由 Kubernetes 管理的 ServiceAccount 和普通用户。
Kubernetes 中的用户 # 对于与普通用户,Kuernetes 使用以下方式管理:
负责分发私钥的管理员 类似 Keystone 或者 Google Accounts 这类用户数据库 包含用户名和密码列表的文件 因此,kubernetes 并不提供普通用户的定义,普通用户是无法通过 API 调用写入到集群中的。
尽管如此,通过集群的证书机构签名的合法证书的用户,kubernetes 依旧可以认为是合法用户。基于此,kubernetes 使用证书中的 subject.CommonName 字段来确定用户名,接下来,通过 RBAC 确认用户对某资源是否存在要求的操作权限。
与此不同的 ServiceAccount,与 Namespace 绑定,与一组 Secret 所包含的凭据有关。这些凭据会挂载到 Pod 中,从而允许访问 kubernetes 的 API。
API 请求要么与普通用户相关,要么与 ServiceAccount 相关,其他的视为匿名请求。这意味着集群内和集群外的每个进程向 kube-apiserver 发起请求时,都必须通过身份认证,否则会被视为匿名用户。
认证机制 # 目前 kubernetes 提供的认证机制丰富多样,尤其是身份验证,更是五花八门:
身份验证 X509 Client Cert Static Token File Bootstrap Tokens Static Password File(deprecated in v1.16) ServiceAccount Token OpenID Connect Token Webhook Token Authentication Proxy 匿名请求 用户伪装 client-go 凭据插件 身份验证策略 # X509 Client Cert # X509 客户端证书认证,也被称为 TLS 双向认证,即为服务端和客户端互相验证证书的正确性。使用此认证方式,只要是 CA 签名过的证书都能通过认证。
...
December 29, 2020
在客户端请求通过认证后,会进入鉴权阶段,kube-apiserver 同样支持多种鉴权机制,并支持同时开启多个鉴权模块。
如果开启多个鉴权模块,则按照顺序执行鉴权模块,排在前面的鉴权模块有较高的优先级来允许或者拒绝请求。
只要有一个鉴权模块通过,则鉴权成功。
...
January 31, 2023
Service 与 Endpoints、EndpointSlice 那些事 # Service 资源 # k8s Service 定义了这样一种抽象:逻辑上的一组 Pod,一种可以访问它们的策略 —— 通常称为微服务。 Service 所针对的 Pod 集合通常是通过 LabelSelector 来确定的。 LabelSelector 选中的 Pod,将会自动创建与 Service 同名且同 Namespace 的 Endpoint 对象,将 Pod 的 IP 写入其中。
如果没有 LabelSelector,用户可能是希望 Service 关联的后端是集群外的服务,或者是其他 Namespace; 如果用户还想要使用负载均衡服务,需要自行创建与 Service 同名的 Endpoints 对象。
Service 类型 # k8s Service 分为 4 种类型(svc.spec.type):
ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType。 NodePort:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <node ip>:<node port>,你可以从集群的外部访问一个 NodePort 服务。 LoadBalance:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。 ExternalName:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容; 例如,foo.
...
October 25, 2020
序言 # 垃圾回收相关,可参考 这里
源码解析 # GarbageCollectorController 负责回收集群中的资源对象,要做到这一点,首先得监控所有资源。 gc controller 会监听集群中所有可删除资源的事件,这些事件会放到一个队列中,然后启动多个 worker 协程处理。 对于删除事件,则根据删除策略删除对象;其他事件,更新对象之间的依赖关系。
startGarbageCollectorController() # 首先来看 gc controller 的入口方法,也就是 kube-controller-manager 是如何启动它的。它的主要逻辑:
判断是否启用 gc controller,默认是 true 初始化 clientset,使用 discoveryClient 获取集群中所有资源 注册不考虑 gc 的资源,默认为空 调用 garbagecollector.NewGarbageCollector() 方法 初始化 gc controller 对象 调用 garbageCollector.Run() 启动 gc controller,workers 默认是 20 调用 garbageCollector.Sync() 监听集群中的资源,当出现新的资源时,同步到 minitors 中 调用 garbagecollector.NewDebugHandler() 注册 debug 接口,用来提供集群内所有对象的关联关系; // cmd/kube-controller-manager/app/core.go:538 func startGarbageCollectorController(ctx ControllerContext) (http.Handler, bool, error) { // 1. 判断是否启用 gc controller,默认是 true if !
...
October 21, 2020
DaemonSet 简介 # 我们知道,Deployment 是用来部署一定数量的 Pod。但是,当你希望 Pod 在集群中的每个节点上运行,并且每个节点上都需要一个 Pod 实例时,Deployment 就无法满足需求。
这类需求包括 Pod 执行系统级别与基础结构相关的操作,比如:希望在每个节点上运行日志收集器和资源监控组件。另一个典型的例子,就是 Kubernetes 自己的 kube-proxy 进程,它需要在所有节点上都运行,才能使得 Service 正常工作。
如此,DaemonSet 应运而生。它能确保集群中每个节点或者是满足某些特性的一组节点都运行一个 Pod 副本。当有新节点加入时,也会立即为它部署一个 Pod;当有节点从集群中删除时,Pod 也会被回收。删除 DaemonSet,也会删除所有关联的 Pod。
应用场景 # 在每个节点上运行集群存守护进程 在每个节点上运行日志收集守护进程 在每个节点上运行监控守护进程 一种简单的用法是为每种类型的守护进程在所有的节点上都启动一个 DaemonSet。 一个稍微复杂的用法是为同一种守护进程部署多个 DaemonSet;每个具有不同的标志,并且对不同硬件类型具有不同的内存、CPU 等要求。
基本功能 # 创建 删除 级联删除:kubectl delete ds/nginx-ds 非级联删除:kubectl delete ds/nginx-ds --cascade=false 更新 RollingUpdate OnDelete 回滚 示例 # apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: # this toleration is to have the daemonset runnable on master nodes # remove it if your masters can't run pods - key: node-role.
...
October 14, 2020
序言 # 什么是垃圾回收 # 参考 Java 中的概念,垃圾回收(Garbage Collection)是 JVM 垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。 垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身。换言之,垃圾回收只会负责释放那些对象占有的内存。 对象是个抽象的词,包括引用和其占据的内存空间。当对象没有任何引用时其占据的内存空间随即被收回备用,此时对象也就被销毁。
因此,垃圾回收关注的是无任何引用的对象。在 kubernetes 中,对象的引用关系又是怎样的呢?
k8s 中的对象引用 # 某些 kubernetes 对象是其他一些对象的属主。例如一个 ReplicaSet 是一组 Pod 的属主;反之这组 Pod 就是此 ReplicaSet 的附属。 每个附属对象具有一个指向属主对象的 metadata.ownerReference 字段。
Kubernetes 会自动为 ReplicationController、ReplicaSet、StatefulSet、DaemonSet、Deployment、Job 和 CronJob 自动设置 ownerReference 的值。 也可以通过手动设置 ownerReference 的值,来指定属主和附属之间的关系。
先看一个 Pod 的详细信息,例如下面的配置显示 Pod 的属主是名为 my-replicaset 的 ReplicaSet:
apiVersion: v1 kind: Pod metadata: ... ownerReferences: - apiVersion: apps/v1 controller: true blockOwnerDeletion: true kind: ReplicaSet name: my-rs uid: d9607e19-f88f-11e6-a518-42010a800195 .
...
October 12, 2020
StatefulSet 简介 # Statefulset 是为了解决有状态服务的问题,而产生的一种资源类型(Deployment 和 ReplicaSet 是解决无状态服务而设计的)。
这里可能有人说,MySQL 是有状态服务吧,但我使用的是 Deploment 资源类型, MySQL 的数据通过 PV 的方式存储在第三方文件系统中,也能解决 MySQL 数据存储问题。
是的,如果你的 MySQL 是单节点,使用 Deployment 类型确实可以解决数据存储问题。 但是如果你的有状态服务是集群,且每个节点分片存储的情况下,Deployment 则不适用这种场景, 因为 Deployment 不会保证 Pod 的有序性,集群通常需要主节点先启动, 从节点在加入集群,Statefulset 则可以保证,其次 Deployment 资源的 Pod 内的 PVC 是共享存储的, 而 Statefulset 下的 Pod 内 PVC 是不共享存储的,每个 Pod 拥有自己的独立存储空间, 正好满足了分片的需求,实现分片的需求的前提是 Statefulset 可以保证 Pod 重新调度后还是能访问到相同的持久化数据。
适用 Statefulset 常用的服务有 Elasticsearch 集群,Mogodb 集群,Redis 集群等等。
特点 # 稳定、唯一的网络标识符
如:Redis 集群,在 Redis 集群中,它是通过槽位来存储数据的,假如:第一个节点是 0~1000,第二个节点是 1001~2000,第三个节点 2001~3000……,这就使得 Redis 集群中每个节点要通过 ID 来标识自己,如:第二个节点宕机了,重建后它必须还叫第二个节点,或者说第二个节点叫 R2,它必须还叫 R2,这样在获取 1001~2000 槽位的数据时,才能找到数据,否则 Redis 集群将无法找到这段数据。
...
October 12, 2020
概念 # 滚动更新,通常出现在软件或者是系统中。滚动更新与传统更新的不同之处在于: 滚动更新不但提供了更新服务,而且通常还提供了滚动进度查询,滚动历史记录, 以及最重要的回滚等能力。通俗地说,就是具有系统或是软件的主动降级的能力。
Deployment 滚动更新 # Deployment 更新方式有 2 种:
RollingUpdate Recreate 其中,滚动更新是最常见的,阅读代码 pkg/controller/deployment/deployment_controller.go:648, 可以看到 2 种方式分别对应的业务逻辑:
func (dc *DeploymentController) syncDeployment(key string) error { ... switch d.Spec.Strategy.Type { case apps.RecreateDeploymentStrategyType: return dc.rolloutRecreate(d, rsList, podMap) case apps.RollingUpdateDeploymentStrategyType: return dc.rolloutRolling(d, rsList) } ... } 根据 d.Spec.Strategy.Type,若更新策略为 RollingUpdate, 则执行 dc.rolloutRecreate() 方法,具体逻辑如下:
func (dc *DeploymentController) rolloutRolling(d *apps.Deployment, rsList []*apps.ReplicaSet) error { // 1、获取所有的 rs,若没有 newRS 则创建 newRS, oldRSs, err := dc.getAllReplicaSetsAndSyncRevision(d, rsList, true) if err !
...
September 24, 2020
注:本文转自
图解 kubernetes 中基于 etcd 的 watch 关键设计
本文介绍了 kubernetes 针对 etcd 的 watch 场景,k8s 在性能优化上面的一些设计,
逐个介绍缓存、定时器、序列化缓存、bookmark 机制、forget 机制、
针对数据的索引与 ringbuffer 等组件的场景以及解决的问题,
希望能帮助到那些对 apiserver 中的 watch 机制实现感兴趣的朋友。
...
September 10, 2020
注:本文翻译自 Node Topology Manager
概要 # 越来越多的系统将 CPU 和硬件加速器组合使用,以支撑高延迟和搞吞吐量的并行计算。 包括电信、科学计算、机器学习、金融服务和数据分析等领域的工作。这种的混血儿构成了一个高性能环境。
为了达到最优性能,需要对 CPU 隔离、内存和设备的物理位置进行优化。 然而,在 kubernetes 中,这些优化没有一个统一的组件管理。
本次建议提供一个新机制,可以协同 kubernetes 各个组件,对硬件资源的分配可以有不同的细粒度。
启发 # 当前,kubelet 中多个组件决定系统拓扑相关的分配:
CPU 管理器 CPU 管理器限制容器可以使用的 CPU。该能力,在 1.8 只实现了一种策略——静态分配。 该策略不支持在容器的生命周期内,动态上下线 CPU。 设备管理器 设备管理器将某个具体的设备分配给有该设备需求的容器。设备通常是在外围互连线上。 如果设备管理器和 CPU 管理器策略不一致,那么 CPU 和设备之间的所有通信都可能导致处理器互连结构上的额外跳转。 容器运行时(CNI) 网络接口控制器,包括 SR-IOV 虚拟功能(VF)和套接字有亲和关系,socket 不同,性能不同。 相关问题:
节点层级的硬件拓扑感知(包括 NUMA) 发现节点的 NUMA 架构 绑定特定 CPU 支持虚拟函数 提议:CPU 亲和与 NUMA 拓扑感知 注意,以上所有的关注点都只适用于多套接字系统。 内核能从底层硬件接收精确的拓扑信息(通常是通过 SLIT 表),是正确操作的前提。 更多信息请参考 ACPI 规范的 5.2.16 和 5.2.17 节。
目标 # 根据 CPU 管理器和设备管理器的输入,给容器选择最优的 NUMA 亲和节点。 集成 kubelet 中其他支持拓扑感知的组件,提供一个统一的内部接口。 非目标 # 设备间连接:根据直接设备互连来决定设备分配。此问题不同于套接字局部性。设备间的拓扑关系, 可以都在设备管理器中考虑,可以做到套接字的亲和性。实现这一策略,可以从逐渐支持任何设备之间的拓扑关系。 大页:本次提议有 2 个前提,一是集群中的节点已经预分配了大页; 二是操作系统能给容器做好本地页分配(只需要本地内存节点上有空闲的大页即可)。 容器网络接口:本次提议不包含修改 CNI。但是,如果 CNI 后续支持拓扑管理, 此次提出的方案应该具有良好的扩展性,以适配网络接口的局部性。对于特殊的网络需求, 可以使用设备插件 API 作为临时方案,以减少网络接口的局限性。 用户故事 # 故事 1: 快速虚拟化的网络功能
...