kubernetes 基础知识
Kubernetes
Kubernetes 是 Google 基于 Borg 开源的容器编排调度,用于管理容器集群自动化部署、扩容以及运维的开源平台。作为云原生计算基金会 CNCF(Cloud Native Computing Foundation)最重要的组件之一(CNCF 另一个毕业项目 Prometheus ),它的目标不仅仅是一个编排系统,而是提供一个规范,可以让你来描述集群的架构,定义服务的最终状态,Kubernetes 可以帮你将系统自动地达到和维持在这个状态,Kubernetes 也可以对容器(Docker)进行集群管理和服务编排(Docker Swarm 类似的功能),对于大多开发者来说,以容器的方式运行一个程序是一个最基本的需求,跟多的是 Kubernetes 能够提供路由网关、水平扩展、监控、备份、灾难恢复等一系列运维能力。
- 本文用于科普
Kubernetes
的一些基础知识以及概念。
Kubernetes 核心组件
Kubernetes
集群由两种角色组成:Master
集群调度节点, 管理集群。(kube-apiserver, kube-controller-manager, kube-scheduler, etcd)Node
引用程序实际运行的工作节点。(kubelet, kube-proxy)
Master 端
kube-apiserver
集群控制入口, 提供了HTTP Rest
接口的关键服务进程,是Kubernetes
里所有资源的增删改查等操作的唯一入口, 也是集群控制的入口。提供集群管理的 REST API 接口,包括认证授权、数据校验以及集群状态变更等。
提供其他模块之间的数据交互和通信的枢纽(其他模块通过 API Server 查询或修改数据,只有 API Server 才直接操作 etcd)
kube-controller-manager
服务运行控制器, 处理常规任务的后台线程 比如故障检测、自动扩展、滚动更新等。kube-controller-manager 由一系列的控制器组成包括如下控制器Replication Controller
Node Controller
CronJob Controller
Daemon Controller
Deployment Controller
Endpoint Controller
Garbage Collector
Namespace Controller
Job Controller
Pod AutoScaler
RelicaSet
Service Controller
ServiceAccount Controller
StatefulSet Controller
Volume Controller
Resource quota Controller
kube-scheduler
负责 Pod 资源调度。监视新创建没有分配到Node的Pod, 为Pod选择一个Node。调度方式:公平调度
资源高效利用
QoS
affinity 和 anti-affinity (约束)
数据本地化(data locality)
内部负载干扰(inter-workload interference)
deadlines
etcd
用于共享配置和服务发现,存储 Kubernetes 集群所有的网络配置和对象的状态信息。
Node 端
kubelet
负责 Pod 对应的容器的创建,启动等任务,同时与Master节点密切协作, kubelet 提供了一系列PodSpecs
集合规范,并确保这些PodSpecs
中描述的容器运行正常。 kubelet是主要的节点代理,它会监视已分配给节点的pod, 具体功能安装 Pod 所需的volume。
下载 Pod 的Secrets。
监控 Pod 中运行的 docker(或experimentally,rkt)容器。
定期执行容器健康检查。
回传 pod 的状态到其他 kubernetes 服务中。
回传 节点 的状态到其他 kubernetes 服务中。
kube-proxy
实现 Kubernetes SVC 的通信与负载均衡机制的重要组件。通过在主机上维护网络规则并执行连接转发来实现Kubernetes服务抽象。
Kubernetes 基本对象与术语
Pod
- Pod 是一组紧密关联的容器集合,它们共享
PID
、IPC
、Network
和UTS namespace
,是 Kubernetes 调度的基本单位。Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
Label
Label 是识别 Kubernetes 对象的标签,以
key/value
的方式附加到对象上(key最长不能超过63字节,value 可以为空,也可以是不超过253字节的字符串)。 Label 不提供唯一性,并且实际上经常是很多对象(如 Pods)都使用相同的 label 来标志具体的应用。 Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 label 的对象(比如 Service 用 label 来选择一组 Pod)。Label Selector 支持以下几种方式:等于和不等于, 如app=nginx和env!=production
集合,如env in (production, test)
多个label(它们之间是AND关系),如app=nginx, env=test
Namespace
- Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 pods, services, deployments 等都是属于某一个 namespace 的(默认是default),而 Node, PersistentVolumes 等则不属于任何 namespace。
ReplicaSet
- ReplicaSet 它的主要作用是确保
Pod
以你指定的副本数运行, 如果有容器异常退出, 会自动创建新的Pod
来替代, 而异常多出来的容器也会自动回收。 支持集合式的selector
。ReplicaSet 可以独立使用, 但建议使用Deployment
来自动管理 ReplicaSet, 这样就无需担心跟其他机制的不兼容问题(比如 ReplicaSet 不支持 rolling-update 但 Deployment 支持), 并且Deployment
还支持版本记录、回滚、暂停升级等高级特性。
Deployment
Deployment 确保任意时间都有指定数量的
Pod
在运行。如果为某个Pod
创建了 Deployment 并且指定3个副本,它会创建3个 Pod,并且持续监控它们。如果某个 Pod 不响应,那么 Deployment 会替换它,保持总数为3。如果之前不响应的 Pod 恢复了,现在就有4个 Pod 了,那么 Deployment 会将其中一个终止保持总数为3。如果在运行中将副本总数改为5,Deployment 会立刻启动2个新 Pod,保证总数为5。Deployment 还支持回滚和滚动升级。创建 Deployment 需要指定:
Pod 模板 用于配置 pod 生成的属性和副本。
Label 标签 Deployment 需要监控的 Pod 的标签。
StatefulSet
StatefulSet 是为了解决有状态服务的问题 (对应 Deployments 和 ReplicaSets是为无状态服务而设计), 其应用场景包括
稳定的持久化存储,即 Pod 重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
稳定的网络标志,即 Pod 重新调度后其
PodName
和HostName
不变,基于 Headless Service(即没有Cluster IP的Service)来实现有序部署,有序扩展,即 Pod 是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的 Pod 必须都是 Running 和 Ready 状态),基于 init containers来实现。
有序收缩,有序删除(即从N-1到0)
DaemonSet
- DaemonSet 保证在每个Node上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用。
Service
- Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的Pod IP 和端口列表组成 endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些endpoints 上。每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。
Job
Job 负责批量处理短暂的一次性任务 (short lived one-off tasks),即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。
Kubernetes 支持以下几种 Job
非并行 Job:通常创建一个 Pod 直至其成功结束
固定结束次数的 Job:设置
.spec.completions
, 创建多个 Pod,直到.spec.completions
个 Pod 成功结束带有工作队列的并行 Job:设置
.spec.Parallelism
但不设置.spec.completions
,当所有Pod结束并且至少一个成功时,Job 就认为是成功
CronJob
- CronJob 即定时任务,就类似于 Linux 系统的 crontab,在指定的时间周期运行指定的任务。
Horizontal Pod Autoscaler(HPA)
- Horizontal Pod Autoscaling 可以根据 CPU 使用率或应用自定义 metrics 自动扩展 Pod 数量(支持replication controller、deployment和replica set), 从而合理的扩展性能与使用资源。
PersistentVolume (PV)
PersistentVolume(PV)是集群中由管理员配置的一段网络存储。 它是集群中的资源,就像节点是集群资源一样。 PV是容量插件,如Volumes,但其生命周期独立于使用PV的任何单个pod。 此API对象捕获存储实现的详细信息,包括NFS,iSCSI或特定于云提供程序的存储系统。
PersistentVolume 支持的类型:
GCEPersistentDisk
AWSElasticBlockStore
AzureFile
AzureDisk
FC (Fibre Channel)
Flexvolume
Flocker
NFS
iSCSI
RBD (Ceph Block Device)
CephFS
Cinder (OpenStack block storage)
Glusterfs
VsphereVolume
Quobyte Volumes
HostPath
Portworx Volumes
ScaleIO Volumes
StorageOS
PersistentVolume 服务状态
Available
资源尚未被claim使用Bound
卷已经被绑定到claim了Released
claim被删除,卷处于释放状态,但未被集群回收。Failed
卷自动回收失败
PersistentVolumeClaim(PVC)
PersistentVolumeClaim(PVC)是由用户进行存储的请求。 它类似于pod。 Pod消耗节点资源,PVC消耗PV资源。Pod可以请求特定级别的资源(CPU和内存)。声明可以请求特定的大小和访问模式(例如,可以一次读/写或多次只读)。
PVC和PV是一一对应的。
PVC 与 PV 的生命周期
PV是群集中的资源。PVC是对这些资源的请求,并且还充当对资源的检查。
PV和PVC之间的相互作用遵循生命周期:
Provisioning
——->Binding
——–>Using
-——>Releasing
-——>Recycling
Provisioning (准备) 通过集群外的存储系统或者云平台来提供存储持久化支持。
静态提供Static:集群管理员创建多个PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费。
动态提供Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,集群可能会尝试为PVC动态配置卷。 此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已创建并配置该类才能进行动态配置。 要求该类的声明有效地为自己禁用动态配置。
Binding (绑定) 用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态。
Using (使用) 用户可在pod中像volume一样使用pvc。
Releasing (释放) 用户删除pvc来回收存储资源,pv将变成"released"状态。由于还保留着之前的数据,这些数据需要根据不同的策略来处理,否则这些存储资源无法被其他pvc使用。
Recycling (回收) pv 可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。
保留策略 - 允许人工处理保留的数据。
删除策略 - 将删除pv和外部关联的存储资源,需要插件支持。
回收策略 - 将执行清除操作,之后可以被新的pvc使用,需要插件支持。
StorageClass
- StorageClass为管理员提供了一种描述他们提供的存储的"类"的方法。 不同的类可能映射到服务质量级别,或备份策略,或者由群集管理员确定的任意策略。 Kubernetes本身对于什么类别代表是不言而喻的。 这个概念有时在其他存储系统中称为"配置文件”。
Command、Args、Entrypoint、Cmd
Command
、Args
是属于kubernetes
的命令Entrypoint
、Cmd
是属于Docker
的命令在
kubernetes
于Docker image
中都分别包含命令的情况下:如果
command
和args
均没有写, 那么用Docker image
默认的配置。如果
command
写了, 但args
没有写, 那么Docker
默认的配置会被忽略而且仅仅执行kubernetes
的command
(不带任何参数的)。如果
command
没写, 但args
写了, 那么Docker
默认配置的Entrypoint
命令行会被执行, 但是调用的参数是kubernetes
的args
。如果
command
和args
都写了, 那么Docker
默认的配置会被忽略, 而使用kubernetes
的配置。
Pod 的生命周期
Pod对象自从其创建开始至其终止退出的时间范围称为其生命周期。
创建主容器(main container)为
必需
的操作。初始化容器(init container)。
容器启动后钩子(post start hook)。
容器的存活性探测(liveness probe)。
就绪性探测(readiness probe)。
容器终止前钩子(pre stop hook)
其他Pod的定义操作。
初始化容器(init container)
初始化容器(init container)即应用程序的主容器启动之前要运行的容器,常用于为主容器执行一些预置操作,它们具有两 种典型特征。
初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么kubernetes需要重启它直到成功完成。(注意:如果pod的
spec.restartPolicy
字段值为Never
,那么运行失败的初始化容器不会被重启。)每个初始化容器都必须按定义的顺序串行运行。
pod phase
- Pod 的 status 属性是一个
PodStatus
对象,拥有一个 phase 字段。它简单描述了 Pod 在其生命周期的阶段。
pod phase | 描述 |
---|---|
挂起(Pending) | kubernetes 通过apiserver 创建了pod 资源对象并存入etcd中, 但它尚未被调度完成, 或者仍处于从仓库下载镜像的过程中 |
运行中(Running) | Pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成,至少一个容器正在运行。 |
成功(Succeeded) | Pod中的所有容器都已经成功并且不会被重启。 |
失败(Failed) | Pod中的所有容器都已终止了,并且至少有一个容器是因为失败终止。即容器以非0状态退出或者被系统禁止。 |
未知(Unknown) | ApiServer 无法正常获取到Pod对象的状态信息,通常是由于无法与所在工作节点的kubelet通信所致。 |
Pod conditions
Pod 的 status 属性是一个
PodStatus
对象, 里面包含 PodConditions 数组,代表 Condition 是否通过。PodCondition 属性描述
字段 | 描述 |
---|---|
lastProbeTime | 最后一次探测 Pod Condition 的时间戳。 |
lastTransitionTime | 上次 Condition 从一种状态转换到另一种状态的时间。 |
message | 上次 Condition 状态转换的详细描述。 |
reason | Condition 最后一次转换的原因。 |
status | Condition 状态类型,可以为 True False Unknown |
type | Condition类型(PodScheduled, Ready, Initialized, Unschedulable, ContainersReady) |
Condition Type 说明
PodScheduled
Pod 已被调度到一个节点Ready
Pod 能够提供请求,应该被添加到负载均衡池中以提供服务Initialized
所有 init containers 成功启动Unschedulable
调度器不能正常调度容器,例如缺乏资源或其他限制ContainersReady
Pod 中所有容器全部就绪
Container probes
Probe 是在容器上 kubelet 的定期执行的诊断,kubelet 通过调用容器实现的 Handler 来诊断。
Success
容器诊断通过。Failure
容器诊断失败。Unknown
诊断失败,因此不应采取任何措施。
Handlers 包含如下三种
ExecAction
在容器内部执行指定的命令,如果命令以状态代码 0 退出,则认为诊断成功。TCPSocketAction
对指定 IP 和端口的容器执行 TCP 检查,如果端口打开,则认为诊断成功。HTTPGetAction
对指定 IP + port + path 路径上的容器的执行 HTTP Get 请求。如果响应的状态代码大于或等于 200 且小于 400,则认为诊断成功。
kubelet 可以选择性地对运行中的容器进行两种探测器执行和响应。
livenessProbe
存活性探测, 探测容器是否正在运行,如果活动探测失败,则 kubelet 会杀死容器,并且容器将受其 重启策略 的约束。如果不指定活动探测, 默认状态是 Success。readinessProbe
就绪性探测, 探测容器是否已准备好为请求提供服务,如果准备情况探测失败,则控制器会从与 Pod 匹配的所有服务的端点中删除 Pod 的 IP 地址。初始化延迟之前的默认准备状态是Failure
, 如果容器未提供准备情况探测,则默认状态为 Success。
以下为 Nginx 应用的两种探针配置示例
|
|
Pod RestartPolicy (重启策略)
PodSpec 中有一个
restartPolicy
字段,可能的值为Always、OnFailure和Never。默认为Always。restartPolicy适用于Pod中的所有容器。而且它仅用于控制在同一节点上重新启动Pod对象的相关容器。首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长依次为10秒、20秒、40秒… 300秒是最大延迟时长。
Pod,一旦绑定到一个节点,Pod对象将永远不会被重新绑定到另一个节点,它要么被重启,要么终止,直到节点发生故障或被删除。
Pod 创建过程
创建一个 pod 的过程
用户通过kubectl或其他API客户端提交了Pod Spec给API Server。
API Server尝试着将Pod对象的相关信息存入etcd中,待写入操作执行完成,API Server即会返回确认信息至客户端。
API Server开始检测etcd中的状态变化。
所有的kubernetes组件均使用
watch
机制来跟踪检查API Server上的相关的变动。kube-scheduler(调度器)通过其
watcher
觉察到API Server创建了新的Pod对象但尚未绑定至任何工作节点。kube-scheduler(调度器)为Pod对象挑选一个工作节点并将结果信息更新至API Server。
调度结果信息由API Server更新至etcd存储系统,而且API Server也开始反映此Pod对象的调度结果。
Pod被调度到的目标工作节点上的kubelet尝试在当前节点上调用Docker启动容器,并将容器的结果状态返回送至API Server。
API Server将Pod状态信息存入etcd系统中。
在etcd确认写入操作成功完成后,API Server将确认信息发送至相关的kubelet,事件将通过它被接受。
Kubernetes 资源调度与限制
在 Kubernetes 体系中,资源默认是被多租户多应用共享使用的,应用与租户间不可避免地存在资源竞争问题。
在 Kubernetes 中支持分别从
Namespace
、Pod
和Container
三个级别对资源进行管理。在 Kubernetes 将 cpu 1 Core (核) = 1000m,
m
这个单位表示 千分之一核, 2000m 表示 两个完整的核心, 也可以写成2
或者2.0
。
ResourceQuota
Namespace
级别, 可以通过创建ResourceQuota
对象对Namespace
进行绑定, 提供一个总体资源使用量限制。可以设置该命名空间中 Pod 可以使用到的计算资源(CPU、内存)、存储资源总量上限。
可以限制该 Namespace 中某种类型对象(如 Pod、RC、Service、Secret、ConfigMap、PVC 等)的总量上限。
ResourceQuota 示例
|
|
requests
kubernetes会根据Request的值去查找有足够资源的node来调度此pod, 既超过了 Request 限制的值, Pod 将不会被调度到此node中。limits
对应资源量的上限, 既最多允许使用这个上限的资源量, 由于cpu是可压缩的, 进程是无法突破上限的, 而memory是不可压缩资源, 当进程试图请求超过limit限制时的memory, 此进程就会被kubernetes杀掉。
LimitRange
- LimitRange 对象设置 Namespace 中 Pod 及 Container 的默认资源配额和资源限制。
|
|
pod
与Container
以及pvc
类型可分开定义LimitRange
分配资源。limits
字段下面的default
字段表示每个 Pod 的默认的 limits 配置,所以任何没有分配资源的 limits 的 Pod 都会被自动分配 200Mi limits 的内存和 300m limits 的 CPU。defaultRequest
字段表示每个 Pod 的默认 requests 配置,所以任何没有分配资源的 requests 的 Pod 都会被自动分配 100Mi requests 的内存和 200m requests 的 CPU。max
与min
字段分别限制 type 类型下的 服务最大与最小的限制值。
ResourceRequests/ResourceLimits
在 Container 级别可以对两种计算资源进行管理
CPU
和内存
。ResourceRequests
表示容器希望被分配到的可完全保证的资源量,Requests 的值会被提供给 Kubernetes 调度器,以便优化基于资源请求的容器调度。ResourceLimits
表示容器能用的资源上限,这个上限的值会影响在节点上发生资源竞争时的解决策略。
|
|
kubernetes 与 Cgroup
Kubernetes 对内存资源的限制实际上是通过 cgroup 来控制的,cgroup 是容器的一组用来控制内核如何运行进程的相关属性集合。针对内存、CPU 和各种设备都有对应的 cgroup。cgroup 是具有层级的,这意味着每个 cgroup 拥有一个它可以继承属性的父亲,往上一直直到系统启动时创建的 root cgroup。
内存 限制
- Kubernetes 通过 cgroup 和 OOM killer 来限制 Pod 的内存资源,当超过内存限制值以后, Kubernetes 会选择好几个进程作为
OOM killer
候选人, 其中最重要的进程是标注为pause
的进程, 用来为业务容器创建共享的network
和namespace
, 其oom_score_adj
值为-998
,可以确保不被杀死。oom_score_adj
值越低就越不容易被杀死, 因为业务容器内pause
之外的所有其他进程的oom_score_adj
值都相同,所以谁的内存使用量最多,oom_score
值就越高,也就越容易被杀死。
- Kubernetes 通过 cgroup 和 OOM killer 来限制 Pod 的内存资源,当超过内存限制值以后, Kubernetes 会选择好几个进程作为
CPU 限制
在 Kubernetes 中设置的 cpu requests 最终会被 cgroup 设置为
cpu.shares
属性的值, cpu limits 会被带宽控制组设置为cpu.cfs_period_us
和cpu.cfs_quota_us
属性的值。与内存一样,cpu requests 主要用于在调度时通知调度器节点上至少需要多少个 cpu shares 才可以被调度。cpu requests 与 内存 requests 不同,设置了 cpu requests 会在 cgroup 中设置一个属性,以确保内核会将该数量的 shares 分配给进程。
cpu limits 与 内存 limits 也有所不同。如果容器进程使用的内存资源超过了内存使用限制,那么该进程将会成为 oom-killing 的候选者。但是容器进程基本上永远不能超过设置的 CPU 配额,所以容器永远不会因为尝试使用比分配的更多的 CPU 时间而被驱逐。系统会在调度程序中强制进行 CPU 资源限制,以确保进程不会超过这个限制。
Kubernetes 网络模型
Kubernetes 中每个Pod 都拥有一个独立的IP地址,而且假定所有Pod 都在一个可以直接连通的、扁平的网络空间中,不管是否运行在同一Node上都可以通过Pod的IP来访问。
Kubernetes 中Pod的IP是最小粒度IP。同一个Pod内所有的容器共享一个网络堆栈,该模型称为IP-per-Pod模型。
Kubernetes 通信
同一个 Node 下, 同一个 pod 内
同一个Pod的容器共享同一个网络命名空间, 它们之间的访问可以用 Pod IP 地址 + 容器端口就可以访问。
- 同一个 Node 下, 不同的 pod 之间通信
- 不同的 Node 之间, pod 与 pod 使用 网络组件进行通信
Kubernetes RBAC
- 基于角色的访问控制(Role-Based Access Control, 即 “RBAC”): 使用 “rbac.authorization.k8s.io” API Group 实现授权决策,允许管理员通过 Kubernetes API 动态配置策略。
角色 ( ClusterRole 与 Role )
Role(角色)
是一系列权限的集合,例如一个角色可以包含读取 Pod 的权限和列出 Pod 的权限。- Role 只能用来给某个特定 namespace 中的资源作鉴权。
如下是 role 对象的定义的一个 demo 授予 default 这个 namespaces 下的 pods 的权限
|
|
大多数资源由代表其名字的字符串表示,例如 ”pods”,就像它们出现在相关API endpoint 的URL中一样。然而,有一些Kubernetes API还 包含了”子资源”,比如 pod 的 logs。
在Kubernetes中,pod logs endpoint的URL格式为:
GET /api/v1/namespaces/{namespace}/pods/{name}/log
|
|
通过
resourceNames
列表,角色可以针对不同种类的请求根据资源名引用资源实例。当指定了
resourceNames
列表时,不同动作 种类的请求的权限,如使用 ”get”、”delete”、”update”以及”patch”等动词的请求,将被限定到资源列表中所包含的资源实例上。值得注意的是,如果设置了 resourceNames,则请求所使用的动词不能是 list、watch、create或者delete collection。由于资源名不会出现在 create、list、watch和delete collection 等API请求的URL中,所以这些请求动词不会被设置了resourceNames 的规则所允许,因为规则中的 resourceNames 部分不会匹配这些请求。
如下 如果需要限定一个角色绑定主体只能 ”get” 或者 ”update” 指定的一个 configmap
|
|
ClusterRole
对象可以授予与 Role 对象相同的权限,但由于它们属于集群范围对象,也可以使用它们授予对以下几种资源的访问权限集群范围资源(例如节点,即 node)
非资源类型 endpoint(例如 /healthz)
授权多个 Namespace
如下是 ClusterRole 定义可用于授予用户对某一个 namespace,或者 所有 namespace 的 secret(取决于其绑定方式)的读访问权限
|
|
角色绑定 ( ClusterRoleBinding 与 RoleBinding )
RoleBinding
把 Role 或 ClusterRole 中定义的各种权限映射到User
,Service Account
或者Group
,从而让这些用户继承角色在 namespace 中的权限。如下是 RoleBinding 引用在同一命名空间内定义的Role对象。
|
|
RoleBinding
对象也可以引用一个 ClusterRole 对象用于在 RoleBinding 所在的命名空间内授予用户对所引用的ClusterRole 中定义的命名空间资源的访问权限。这一点允许管理员在整个集群范围内首先定义一组通用的角色,然后再在不同的命名空间中复用这些角色。如下 RoleBinding 引用的是一个 ClusterRole 对象,但是用户”dave”(即角色绑定主体)还是只能读取”development” 命名空间中的 secret(即RoleBinding所在的命名空间)
|
|
ClusterRoleBinding
让用户继承 ClusterRole 在整个集群中的权限。如下使用 ClusterRoleBinding 在集群级别和所有命名空间中授予权限。下面示例中所定义的 ClusterRoleBinding 允许在用户组 ”manager” 中的任何用户都可以读取集群中任何命名空间中的 secret 。
|
|
默认角色 与 默认角色绑定
API Server 会创建一组默认的 ClusterRole 和 ClusterRoleBinding 对象。这些默认对象中有许多包含 system: 前缀,表明这些资源由 Kubernetes基础组件”拥有”。对这些资源的修改可能导致非功能性集群(non-functional cluster)。
system:node
ClusterRole 对象。这个角色定义了 kubelet 的权限。如果这个角色被修改,可能会导致kubelet 无法正常工作。所有默认的 ClusterRole 和 ClusterRoleBinding 对象都会被标记为 kubernetes.io/bootstrapping=rbac-defaults。
用户角色
通过命令
kubectl get clusterrole
查看到并不是所有都是以 system:前缀,它们是面向用户的角色。这些角色包含超级用户角色(cluster-admin),即旨在利用 ClusterRoleBinding(cluster-status)在集群范围内授权的角色, 以及那些使用 RoleBinding(admin、edit和view)在特定命名空间中授权的角色。cluster-admin
超级用户权限,允许对任何资源执行任何操作。在 ClusterRoleBinding 中使用时,可以完全控制集群和所有命名空间中的所有资源。在 RoleBinding 中使用时,可以完全控制 RoleBinding 所在命名空间中的所有资源,包括命名空间自己。admin
管理员权限,利用 RoleBinding 在某一命名空间内部授予。在 RoleBinding 中使用时,允许针对命名空间内大部分资源的读写访问, 包括在命名空间内创建角色与角色绑定的能力。但不允许对资源配额(resource quota)或者命名空间本身的写访问。edit
允许对某一个命名空间内大部分对象的读写访问,但不允许查看或者修改角色或者角色绑定。view
允许对某一个命名空间内大部分对象的只读访问。不允许查看角色或者角色绑定。由于可扩散性等原因,不允许查看 secret 资源。
API Server 认证机制
通常我们使用客户端
kubectl
来与Kubernetes API Server
交互, 它们之间的接口是REST调用。为了确保
Kubernetes
集群的安全,API Server
都会对客户端进行身份认证,认证失败则无法调用API。Pod中访问
Kubernetes API Server
服务的时候,是以Service
方式访问服务名为kubernetes
的这个服务,而kubernetes
服务又只在HTTPS 443上提供服务,那么如何进行身份认证呢?Service Account Token
Kubernetes
有两套账户系统分别是1.
User Account
是给用户使用的, 是全局性的, 主要用于 后端的用户数据库同步。2.
Service Account
是给Pod
内的进程使用的, 是属于具体的Namespace
的。
Service Account
每个Namespace下有一个名为default的默认的Service Account对象,这个Service Account里面有一个名为Tokens的可以当作Volume一样被Mount到Podcast里的Secret,当Pod 启动时,这个Secret会自动被Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权过程。
kubernetes
开启了Service Account
会在每个Namespace
下面都会创建一个默认的default
的Service Account
。
|
|
- 每个
Service Account
下面都会拥有一个加密过的secret
作为Token
。 这个Token
就是Service Account Token
。这个Token
才是真正在API Server
验证(authentication)环节起作用的。
|
|
- 当用户在该
Namespace
下创建pod
的时候都会默认使用这个Service Account
。
|
|
查看
secret
的具体内容ca.crt
这个是API Server
的CA
公钥证书, 用于Pod
中的Process
对API Server
的服务端数字证书进行校验时使用Namespace
和Token
被放到了容器内, 这样容器内就可以通过https
的方式通过 SVC -kubernetes
请求访问API Server
。
|
|
Secret
Kubernetes
提供了Secret
来处理敏感信息, 目前Secret
的类型有3种1.
Opaque
(default): 任意字符串。2.
kubernetes.io/service-account-token
: 作用于Service Account
。3.
kubernetes.io/dockercfg
: 作用于Docker registry
, 用于下载Docker镜像认证使用。
Kubernetes 存储
- 在 Kubernetes 中后端存储是有状态服务最重要的系统, kubernetes 中的存储涉及到不同的组件, 多个控制器的协作。
原理分析
volume
的生命周期, 从Pods
创建开始volume
会经历如下几个阶段.volume
创建 –> 使用provision
阶段,volume
创建/分配 成功.attach
阶段,volume
被挂载到pod
所在的node
节点中.先挂载到
node
节点中的全局路径中, 如:/var/lib/kubelet/plugins/kubernetes.io/csi/....
然后映射到
Pod
对应路径, 如:/var/lib/kubelet/pods/49a5fede-b811-11e8-844f-fa7378845e00/volumes/kubernetes.io~csi/...
mount
阶段,volume
被挂载为容器文件系统 并且 映射到对应的pod
里. 相当于docker run -v
volume
卸载 –> 回收umount
阶段,volume
和对应的pod
解除映射, 并且从文件系统中umount
. 既/var/lib/kubelet/pods/49a5fede-b811-11e8-844f-fa7378845e00/volumes/kubernetes.io~csi/...
此类目录.detach
阶段,volume
从对应的node
中卸载.recycle
阶段,volume
被后端文件系统回收.
volume
生命周期管理, 需要不同的控制器处理.PV Controller
- 负责创建和回收volume
.attach detach Controller
- 负责 挂载和卸载volume
.volume Manager
- 负责 mount 和 umountvolume
.
Pod 创建过程
Pod
绑定volume
的过程.- pvc 绑定 pv 由
PV Controller
负责完成. 顺利完成可以查看到 pvc 状态为Bound
.
- pvc 绑定 pv 由
将
volume
挂载到对应的 Pod 中, 是由attach detach Controller
负责完成.如果 pod 分配到
Node
节点,并且对应volume
已经创建, 则将volume
挂载到对应node
节点中,并且给node
节点资源增加volume
已经挂载的状态信息.通过查看
kubectl get nodes k8s-node-1 -o yaml
中的 volumesAttached 与 volumesInUse 字段, 可以查看节点中volume
的已挂载的状态信息.
将
volume
从Node
映射到对应的容器文件系统中, 是由volume manager
负责完成的.pod 分配到
Node
节点后, 获取 Pod 需要的volume
,通过对比Node
节点状态中的volumesAttached
,确认volume
是否已经 attach 到Node
节点, 如果 attach 到Node
节点中, 则将自身actualStateOfWorld
中的volume
状态设置成attached
.attached
状态 – 先将volume
挂载到Node
节点的全局路径中, 然后映射到Pod
对应的路径中. 最后将actualStateOfWorld
的状态设置为 成功.Pod Controller
确认卷已经映射成功, 启动 Pod.
Pod 删除过程
Pod Controller
watch 到Pod
处于被删除状态, 执行killPod
操作, 删除 Pod.volume Manager
获取到 Pod 被删除的信息, 会执行如下几步:将 Pod 从
desiredStateOfWorld
的缓存信息中清除.首先查找到
Node
节点中移除掉的Pod
.将
Pod
从关联的volume
列表中清除.如果发现
volume
未关联Pod
, 则将volume
从desiredStateOfWorld
清除.
actualStateOfWorld
中已经挂载的volume
和desiredStateOfWorld
发现 Pod 不应该挂载, 执行UmountVolume
操作, 将 Pod 和volume
映射关系解除, 并将 Pod 从actualStateOfWorld
的volume
信息中剔除.首先找到实际挂载但是期望不挂载的
volume
.将
volume
从 Pod 映射中umount
掉, 注: 此时 全局路径 不会被umount
.设置
actualStateOfWorld
缓存信息volume
已从 Pod 中umount
.将 Pod 从
volume
关联的 Pod 集合中剔除.
此时如果实际状态中
volume
没有关联任何 Pod, 则说明volume
可以完全与节点分离.- 则先执行
UnmountDevice
将volume
的全局路径umount
, 等到下次reconcile
时执行MarkVolumeAsDetached
将volume
完全从实际状态中删除掉.
- 则先执行
attach detach Controller
发现挂载到Node
节点的volume
没有被 Pod 使用, 执行detach
操作, 将卷从Node
节点detach
, 此时volume
完全处于集群中未被使用的状态.
Kubernetes 存储特点总结
不同组件通过资源状态协作.
attach detach Controller
- 需要 PVC 绑定 PV 的状态信息.volume Manager
需要Node
节点 status 中 volume attached 状态信息.
组件通过
reconcile
方式达到期望状态, 并且状态可能需要多次reconcile
中完成, 如 Pod 清除掉后,volume
最终和Node
节点分离.