Kubernetes基本入门-集群资源(二)

资源

在kubernetes中包含很多中类型的资源,所有内容都可以抽象为资源,资源实例化之后称为对象。资源按照不同的类型可以划分为集群类、名称空间类、元数据类资源。

集群级资源

集群级别的资源是所有名称空间之间可以共享的资源。

Namespace

Kubernetes中,名称空间(Namespace)提供一种机制,将同一集群中的资源划分为相互隔离的组。 同一名称空间内的资源名称要唯一。名称空间作用域仅针对带有名称空间的对象,例如Deployment、Service等, 这种作用域对集群访问的对象不适用,例如StorageClass、Node、PersistentVolume等。

如何使用

名称空间属于Kubernetes中一种重量级隔离方案,是在多个用户之间划分集群资源的一种方法(通过资源配额)适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名称空间。
使用kubectl可以对名称空间进行操作,查看当前集群所包含的名称空间信息

[root@master ~]# kubectl get ns

# 输出结果
NAME              STATUS   AGE
default           Active   9d
ingress-nginx     Active   9d
kube-node-lease   Active   9d
kube-public       Active   9d
kube-system       Active   9d
kuboard           Active   9d
lens-metrics      Active   7d19h

也可以使用describe命令来查看某个名称空间的详细信息

[root@master ~]# kubectl describe ns kube-public

# 输出结果
Name:         kube-public
Labels:       kubernetes.io/metadata.name=kube-public
Annotations:  <none>
Status:       Active
No resource quota.
No LimitRange resource.

在使用kubectl发送请求时也可以使用--namespace参数来进行某个名称空间的操作。

[root@master ~]# kubectl get pods

# 输出结果
NAME                                   READY   STATUS    RESTARTS     AGE
docker-learn-server-7597c4ddf6-ctvq6   1/1     Running   0            28h
docker-learn-server-7597c4ddf6-d99wk   1/1     Running   0            28h
docker-learn-server-7597c4ddf6-dpm4f   1/1     Running   0            31h
docker-learn-server-7597c4ddf6-nwl8j   1/1     Running   0            28h
docker-learn-server-7597c4ddf6-sp4ph   1/1     Running   0            28h
netchecker-agent-b2fx8                 1/1     Running   0            9d
netchecker-agent-hostnet-6vgg5         1/1     Running   0            9d
netchecker-agent-hostnet-clvhd         1/1     Running   0            9d
netchecker-agent-hostnet-rh5x6         1/1     Running   0            9d
netchecker-agent-l6stj                 1/1     Running   0            9d
netchecker-agent-mh4wh                 1/1     Running   0            9d
netchecker-server-59fcd6bf86-8hj27     2/2     Running   1 (9d ago)   9d

# 指定--namespace=ingress-nginx即可只查看此名称空间下的pods
[root@master ~]# kubectl get pods --namespace=ingress-nginx

# 输出结果
NAME                                                      READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-ingress-nginx-j675b        0/1     Completed   0          9d
ingress-nginx-admission-patch-ingress-nginx-jtfsv         0/1     Completed   0          9d
ingress-nginx-controller-ingress-nginx-6cc7cfdbff-6cb47   1/1     Running     0          9d
ingress-nginx-controller-ingress-nginx-6cc7cfdbff-tlk29   1/1     Running     0          9d

使用kubecel创建名称空间test-ns

[root@master ~]# kubectl create namespace test-ns
namespace/test-ns created

删除某个名称空间

[root@master ~]# kubectl delete ns test-ns
namespace "test-ns" deleted

并非所有的对象都在名称空间内,大多数 kubernetes 资源(例如 Pod、Service、副本控制器等)都位于某个名称空间中,但名称空间资源本身并不在名称空间中,而且底层资源,例如节点(Node)和持久化卷(Volume)也不属于任何名字空间。
查看哪些Kubernetes资源在名称空间中,哪些不在名称空间中:

# 位于名字空间中的资源
kubectl api-resources --namespaced=true

# 不在名字空间中的资源
kubectl api-resources --namespaced=false
标签功能

Kubernetes控制平面会为所有名字空间设置一个不可变更的标签kubernetes.io/metadata.name,只要 NamespaceDefaultLabelName这一特性被启用,标签的值是名字空间的名称。

删除

删除名称空间会删除其包含的所有内容,删除过程是异步的,在删除动作完成之前或出现异常状况名称空间会一直处于Terminating状态。
在删除名称空间时会做如下操作,查看官方关于名称空间删除的源码片段

// NamespacedResourcesDeleterInterface is the interface to delete a namespace with all resources in it.
type NamespacedResourcesDeleterInterface interface {
	Delete(nsName string) error
}

定义了NamespacedResourcesDeleterInterface接口来删除一个名称空间下的所有资源。

// Delete deletes all resources in the given namespace.
// Before deleting resources:
// * It ensures that deletion timestamp is set on the
//   namespace (does nothing if deletion timestamp is missing).
// * Verifies that the namespace is in the "terminating" phase
//   (updates the namespace phase if it is not yet marked terminating)
// After deleting the resources:
// * It removes finalizer token from the given namespace.
//
// Returns an error if any of those steps fail.
// Returns ResourcesRemainingError if it deleted some resources but needs
// to wait for them to go away.
// Caller is expected to keep calling this until it succeeds.
func (d *namespacedResourcesDeleter) Delete(nsName string) error {
    // Multiple controllers may edit a namespace during termination
	// first get the latest state of the namespace before proceeding
	// if the namespace was deleted already, don't do anything
	namespace, err := d.nsClient.Get(context.TODO(), nsName, metav1.GetOptions{})
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}
	if namespace.DeletionTimestamp == nil {
		return nil
	}

	klog.V(5).Infof("namespace controller - syncNamespace - namespace: %s, finalizerToken: %s", namespace.Name, d.finalizerToken)

	// ensure that the status is up to date on the namespace
	// if we get a not found error, we assume the namespace is truly gone
	namespace, err = d.retryOnConflictError(namespace, d.updateNamespaceStatusFunc)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}

	// the latest view of the namespace asserts that namespace is no longer deleting..
	if namespace.DeletionTimestamp.IsZero() {
		return nil
	}

	// return if it is already finalized.
	if finalized(namespace) {
		return nil
	}

	// there may still be content for us to remove
	estimate, err := d.deleteAllContent(namespace)
	if err != nil {
		return err
	}
	if estimate > 0 {
		return &ResourcesRemainingError{estimate}
	}

	// we have removed content, so mark it finalized by us
	_, err = d.retryOnConflictError(namespace, d.finalizeNamespace)
	if err != nil {
		// in normal practice, this should not be possible, but if a deployment is running
		// two controllers to do namespace deletion that share a common finalizer token it's
		// possible that a not found could occur since the other controller would have finished the delete.
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}
	return nil
}

根据源码及其注释说明,名称空间删除整体调用流程大致如下:
未命名文件.svg
当出现一直处于Terminating状态时,需要根据kubernetes日志具体的原因来解决。例如:使用命令查看名称空间信息

[root@master ~]# kubectl get ns ingress-nginx -o yaml

apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: "2022-03-31T08:53:41Z"
  labels:
    kubernetes.io/metadata.name: ingress-nginx
  name: ingress-nginx
  resourceVersion: "123397"
  uid: 48c4ec7a-10bb-4829-ba44-9c52c163fc54
spec:
  finalizers:
  - kubernetes
status:
  phase: Active

根据message提示信息,查看具体的资源状态,手动清理或者重置资源状态后重新进行删除。
如果是finalizer导致的名称空间删除一直处于Terminating状态,可以查看namespace资源对象的spec.finalizer[] 列表情况,如果不为空,手动进行编辑清空数组,再重试。

[root@master ~]# kubelet edit ns <name> #将metadata.finalizer[]列表删除

Node

Kubernetes中,Pod运行在节点(Node)上,每个节点包含运行Pod所需的服务,这些节点由控制平面组件负责管理。节点上的组件包括 kubelet、 容器运行时(CRI)以及 kube-proxy。
使用kubectl查看集群中节点信息,这些信息中通常会显示节点的ROLES(比如控制节点、工作节点)、NAME、节点的状态、存活时间及集群管理使用版本:

[root@master ~]# kubectl get nodes
NAME     STATUS   ROLES                  AGE   VERSION
master   Ready    control-plane,master   10d   v1.23.1
node1    Ready    <none>                 10d   v1.23.1
node2    Ready    <none>                 10d   v1.23.1
管理

节点的添加通过apiserver来添加,可以通过kubelet自动注册或者手动进行添加,也可以使用kubelet对节点进行修改。

  1. 自动注册

当kubelet设置--register-node为true(默认)时,它会尝试向apiserver注册。对于自动注册模式,kubelet使用下列参数启动:

  • --kubeconfig:用于向apiserver表明身份的凭据路径
  • --cloud-provider:与某云驱动进行通信以读取与自身相关的元数据的方式
  • --register-node:是否自动向apiserver注册
  • --register-with-taints:使用所给的污点列表注册节点。 当 register-node为false 时无效
  • --node-ip:节点 IP 地址
  • --node-labels:在集群中注册节点时要添加的标签
  • --node-status-update-frequency:指定kubelet向控制平面发送状态的频率
  1. 手动管理

手动加入节点时,需要确保节点已经安装必要Node组件,并且需要设置kubelet属性--register-node=false,生成节点加入集群的认证命令

# 在master节点执行
[root@master ~]# kubeadm token create --print-join-command

然后将节点加入到集群中

kubeadm join 127.0.0.1:6443 --token 0nqoof.k8n2cnz0ld1hlnm1 --discovery-token-ca-cert-hash sha256:25aaa4aba8ed96818ebfed5f02a7cda3f44de5e8952c762c9e11949165f14f9c

如果无法通信检查网络插件是否安装,如果未安装推荐使用calico,安装后尝试重新加入集群,使用calico注意端口是否开放。

export POD_SUBNET=10.100.0.0/16
kubectl apply -f https://kuboard.cn/install-script/v1.21.x/calico-operator.yaml
wget https://kuboard.cn/install-script/v1.21.x/calico-custom-resources.yaml
sed -i "s#192.168.0.0/16#${POD_SUBNET}#" calico-custom-resources.yaml
kubectl apply -f calico-custom-resources.yaml

使用下面命令可以将节点标记为不可调度

kubectl cordon $NODENAME

说明:如果标记节点为不可调度(unschedulable),将阻止新Pod调度到该节点之上,但不会影响任何已经在其上的Pod,这是重启节点或者执行其他维护操作之前的一个有用的准备步骤。

  1. 优雅退出

Kubernetes在V1.21[beta]版本提供了如何优雅的退出一个节点

状态

一个节点(Node)会包含地址、状态、容量及可分配空间、内核版本基本信息这四部分内容,使用kubectl查看节点的描述信息:

kubectl describe node <节点名称>

# 输出结果
Name:               node1
Roles:              <none>
Labels:             
  ......
Annotations:        
  ......
Conditions:
  Type                 Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----                 ------  -----------------                 ------------------                ------                       -------
  NetworkUnavailable   False   Thu, 31 Mar 2022 16:28:14 +0800   Thu, 31 Mar 2022 16:28:14 +0800   CalicoIsUp                   Calico is running on this node
  Ready                True    Sun, 10 Apr 2022 12:40:29 +0800   Wed, 30 Mar 2022 22:01:18 +0800   KubeletReady                 kubelet is posting ready status
Addresses:
  InternalIP:  10.0.4.5
  Hostname:    node1
Capacity:
  cpu:                2
  ephemeral-storage:  61860632Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3826332Ki
  pods:               110
Allocatable:
  cpu:                1900m
  ephemeral-storage:  57010758357
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3461788Ki
  pods:               110
System Info:
  Machine ID:                 5e629d3c708849d29bbc2a68d1d884a8
  System UUID:                5e629d3c-7088-49d2-9bbc-2a68d1d884a8
  Boot ID:                    5219e8f9-7310-48b5-994d-3ae2c7d90d6e
  Kernel Version:             4.18.0-305.10.2.el8_4.x86_64
  OS Image:                   CentOS Linux 8 (Core)
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.5.8
  Kubelet Version:            v1.23.1
  Kube-Proxy Version:         v1.23.1
PodCIDR:                      10.234.2.0/23
PodCIDRs:                     10.234.2.0/23
Non-terminated Pods:          (22 in total)
Namespace                   Name                                                       CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                                       ------------  ----------  ---------------  -------------  ---
  ......
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests          Limits
  --------           --------          ------
  cpu                857m (45%)        2770m (145%)
  memory             1659537408 (46%)  2560321280 (72%)
  ephemeral-storage  0 (0%)            0 (0%)
  hugepages-1Gi      0 (0%)            0 (0%)
  hugepages-2Mi      0 (0%)            0 (0%)
Events:              <none>
  ......
  • 地址(Addresses)主要包含以下配置信息
    • HostName:由节点的内核设置,可以通过kubelet的--hostname-override参数覆盖
    • ExternalIP:通常是节点的可外部路由(从集群外可访问)的IP地址
    • InternalIP:通常是节点仅可在集群内部路由的IP地址
  • 状态(Conditions)conditions 字段描述了节点的状态,状况的示例包括
    • Ready:如节点是健康的并已经准备好接收Pod则为True,False表示节点不健康而且不能接收Pod, Unknown表示节点控制器在node-monitor-grace-period期间(默认 40 秒)没有收到节点的消息
    • DiskPressure:True表示节点存在磁盘空间压力,即磁盘可用量低, 否则为False
    • MemoryPressure:True表示节点存在内存压力,即节点内存可用量低,否则为False
    • PIDPressure:True表示节点存在进程压力,即节点上进程过多,否则为False
    • NetworkUnavailable:True表示节点网络配置不正确,否则为False
  • 容量及分配空间(Capacity、Allocated resources)
    • 描述节点上的可用资源:CPU、内存和可以调度到节点上的Pod的个数上限。
    • capacity块中的字段标示节点拥有的资源总量, allocatable块指示节点上可供普通Pod消耗的资源量。
  • 基本信息(System Info)
    • 描述节点的一般信息,如内核版本、Kubernetes版本(kubelet和kube-proxy版本)、 容器运行时详细信息,以及节点使用的操作系统, kubelet从节点收集这些信息并将其发布到Kubernetes API。

说明:如果Ready状况的status值处于Unknown或者False状态的时间超过了pod-eviction-timeout值,默认超时时长为5分钟,节点控制器会对节点上的所有Pod触发apiserver发起的驱逐。当节点不可达时,apiserver不能和kubelet通信,删除Pod的决定会直到kubelet重新建立与apiserver的连接再执行,在建立连接的过程中,被计划删除的Pod可能会继续在游离的节点上运行。
节点控制器在确认Pod停止运行前,不会强制删除它们,这些在无法访问的节点上运行的Pod处于Terminating或者 Unknown状态。 如果kubernetes不能基于下层基础设施推断出某节点是否已经永久离开了集群, 集群管理员可能需要手动删除节点对象。从Kubernetes删除节点对象将导致apiserver删除节点上所有运行的Pod对象。
节点生命周期控制器会自动创建代表状况的污点,当调度器将Pod指派给某节点时,会考虑节点上的污点,Pod则可以通过容忍度(Toleration)表达所能容忍的污点。

心跳

Kubernetes中通过心跳来确定集群中每个节点的可用性,在新版本中,集群创建后会默认初始化一个kube-node-lease名称空间,存放节点心跳相关的内容。
对于节点有两种形式的心跳,并且集群会在检测到故障时采取相应的措施:

  • 更新节点的.status
  • 更新Lease对象的信息(其中Lease对象就存储在kube-node-lease中,每个节点关联一个对象)

与Node的.status更新相比,Lease是一种轻量级资源,使用Leases心跳在大型集群中可以减少这些更新对性能的影响。kubelet负责创建和更新节点的.status,以及更新它们对应的Lease对象。

  • 当状态发生变化时,或者在配置的时间间隔内没有更新事件时,kubelet会更新.status。.status更新的默认间隔为5分钟(比不可达节点的40秒默认超时时间长很多)。
  • kubelet会每10秒(默认更新间隔时间)创建并更新其Lease对象,Lease更新独立于NodeStatus更新发生, 如果Lease的更新操作失败,kubelet会采用指数回退机制,从200毫秒开始重试,最长重试间隔为7秒钟。

API

概述

REST API是Kubernetes的基本结构,所有操作和组件之间的通信及外部用户命令都是调用apiserver处理的REST API。Kubernetes平台视一切皆为API对象,且它们在API中有相应的定义。关于API版本的说明:

  • Alpha:
    • 版本名称包含alpha(例如,v1alpha1)。
    • 软件可能会有Bug,启用某个特性可能会暴露出Bug,某些特性可能默认禁用。
    • 对某个特性的支持可能会随时被删除,恕不另行通知。
    • API可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。
    • 由于缺陷风险增加和缺乏长期支持,建议该软件仅用于短期测试集群。
  • Beta:
    • 版本名称包含 beta (例如, v2beta3)。
    • 软件被很好的测试过,启用某个特性被认为是安全的,特性默认开启。
    • 尽管一些特性会发生细节上的变化,但它们将会被长期支持。
    • 在随后的Beta版或稳定版中,对象的模式和(或)语义可能以不兼容的方式改变。 当这种情况发生时,将提供迁移说明。模式更改可能需要删除、编辑和重建 API 对象。编辑过程可能并不简单。对于依赖此功能的应用程序,可能需要停机迁移。
    • 该版本的软件不建议生产使用,后续发布版本可能会有不兼容的变动,如果你有多个集群可以独立升级,可以放宽这一限制。
  • Stable:
    • 版本名称如 vX,其中X为整数。
    • 特性的稳定版本会出现在后续很多版本的发布软件中。

访问控制

Kubernetes提供了完善的访问控制策略,用户使用kubectl、客户端库或构造REST请求来访问 Kubernetes API。 用户和Kubernetes服务账户都可以被鉴权访问API。API请求要么与某普通用户相关联,要么与某服务账号相关联,或者被视作匿名请求。这意味着集群内外的每个进程在向apiserver发起请求时都必须通过身份认证,否则会被视作匿名用户。这里的进程可以是在某工作站上输入kubectl命令的操作人员,也可以是节点上的kubelet组件,还可以是控制平面的组件。
当请求到达API时,它会经历多个阶段,如下图所示:
image.png

用户分类

所有Kubernetes集群都有两类用户,由Kubernetes管理的服务账号和普通用户。Kubernetes假定普通用户是由一个与集群无关的服务通过以下方式之一进行管理的:

  • 负责分发私钥的管理员
  • 类似Keystone或者Google Accounts这类用户数据库
  • 包含用户名和密码列表的文件

Kubernetes中并不包含用来代表普通用户账号的对象,普通用户的信息无法通过API调用添加到集群中。尽管无法通过API调用来添加普通用户,Kubernetes仍然认为能够提供由集群的证书机构签名的合法证书的用户是通过身份认证的用户,Kubernetes使用证书中的 'subject' 的通用名称(Common Name)字段(例如,"/CN=bob")来确定用户名,基于角色访问控制(RBAC)子系统会确定用户是否有权针对某资源执行特定的操作。
与此不同,服务账号是Kubernetes所管理的用户。它们被绑定到特定的名称空间,由apiserver自动创建,或者通过API调用创建。服务账号与一组以Secret保存的凭据相关,这些凭据会被挂载到Pod中,从而允许集群内的进程访问Kubernetes API。

认证流程

Kubernetes中的访问控制主要包含下面的流程或者特性:

  • 传输安全

Kubernetes集群中,apiserver在443端口上提供服务,受TLS保护。apiserver出示证书,该证书可以使用私有证书颁发机构(CA)签名,也可以基于公认的CA的公钥基础架构签名。如果集群使用私有证书颁发机构,需要在客户端的 ~/.kube/config文件中提供该CA证书的副本,以便可以信任该连接不被拦截。

  • 认证

如上图步骤1所示,建立TLS后,HTTP请求将进入认证(Authentication)步骤。 集群创建脚本或者集群管理员配置apiserver,使之运行一个或多个身份认证组件。认证步骤的输入整个HTTP请求,通常组件只检查头部或客户端证书。认证模块包含客户端证书、密码、普通令牌、引导令牌和JSON Web令牌(JWT,用于服务账户)。可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。如果请求认证不通过,服务器将以HTTP状态码401拒绝该请求。反之,该用户被认证为特定的username,并且该用户名可用于后续步骤以在其决策中使用。

  • 鉴权

如上图的步骤2所示,将请求验证为来自特定的用户后,请求必须被鉴权。请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。如果现有策略声明用户有权完成请求的操作,那么该请求鉴权通过。例如,如果用户Bob有以下策略,那他只能在projectCaribou名称空间中读取Pod。

{
    "apiVersion": "abac.authorization.kubernetes.io/v1beta1",
    "kind": "Policy",
    "spec": {
        "user": "bob",
        "namespace": "projectCaribou",
        "resource": "pods",
        "readonly": true
    }
}

如果Bob执行以下请求,那么请求会被鉴权,因为允许他读取projectCaribou名称空间中的对象。

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "projectCaribou",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    }
  }
}

如果Bob在projectCaribou名字空间中请求写(create 或 update)对象,其鉴权请求将被拒绝。如果Bob在诸如projectFish这类其它名字空间中请求读取(get)对象,其鉴权也会被拒绝。
Kubernetes支持多种鉴权模块,例如ABAC模式、RBAC模式和Webhook模式等。管理员创建集群时,他们配置应在API服务器中使用的鉴权模块。如果配置了多个鉴权模块,则Kubernetes会检查每个模块,任意一个模块鉴权该请求,请求即可继续。如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。

  • 准入控制

准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。 准入控制器不会对仅读取对象的请求起作用。有多个准入控制器被配置时,服务器将依次调用它们。这一操作如上图的步骤3所示。与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。除了拒绝对象之外,准入控制器还可以为字段设置复杂的默认值。可用的准入控制模块在准入控制器中进行了描述。请求通过所有准入控制器后,将使用检验例程检查对应的 API 对象,然后将其写入对象存储(如步骤 4 所示)。

  • 审计

Kubernetes审计提供了一套与安全相关的、按时间顺序排列的记录,其中记录了集群中的操作序列。 集群对用户、使用Kubernetes API的应用程序以及控制平面本身产生的活动进行审计。

  • apiserver端口和IP

默认情况下,Kubernetes apiserver在2个端口上提供HTTP服务:

  • localhost端口
    • 用于测试和引导,以及主控节点上的其他组件(调度器,控制器管理器)与API通信
    • 没有TLS,默认为端口8080,默认IP为localhost,使用--insecure-bind-address进行更改
    • 请求绕过身份认证和鉴权模块,由准入控制模块处理的请求,受需要访问主机的保护
  • 安全端口
    • 尽可能使用,使用TLS,用--tls-cert-file设置证书,用--tls-private-key-file设置密钥
    • 默认端口6443,使用--secure-port更改,默认IP是第一个非本地网络接口,使用--bind-address更改
    • 请求须经身份认证和鉴权组件处理,须经准入控制模块处理,身份认证和鉴权模块运行
RBAC

基于角色(Role)的访问控制(RBAC)是一种基于组织中用户的角色来调节控制对计算机或网络资源的访问的方法。RBAC鉴权机制使用rbac.authorization.k8s.io API组来驱动鉴权决定,允许通过Kubernetes API动态配置策略。在启动apiserver时将--authorization-mode参数设置为一个逗号分隔的列表并确保其中包含RBAC来启用RBAC功能。

kube-apiserver --authorization-mode=Example,RBAC --<其他选项> --<其他选项>
  • Role

RBAC的Role或ClusterRole中包含一组代表权限的规则。这些权限是累加的(不存在拒绝某操作的规则)。Role用在某个名称空间内设置访问权限,在创建Role时,必须指定该Role所属的名称空间。ClusterRole则是一个集群作用域的资源。Role或ClusterRole 对象的名称必须是合法的路径分段名称。
如果希望在名称空间内定义角色,应该使用Role,如果定义集群范围的角色,应该使用ClusterRole。下面是一个位于 "default" 名称空间的Role的示例,可用来授予对Pods的读访问权限:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
resources: ["pods"]
  verbs: ["get", "watch", "list"]
  • ClusterRole

ClusterRole可以和Role相同完成授权,因为ClusterRole属于集群范围,所以它也可以为以下资源授予访问权限:

  • 集群范围资源(比如节点)
  • 非资源端点(比如/healthz)
  • 跨名称空间访问的作用域的资源,例:允许某特定用户执行kubectl get pods --all-namespaces

下面是一个ClusterRole的示例,可用来为任一特定名字空间中的Secret授予读访问权限,或者跨名称空间的访问权限(取决于该角色是如何绑定的):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
name: secret-reader
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 Secret 对象的资源的名称为 "secrets"
resources: ["secrets"]
  verbs: ["get", "watch", "list"]
  • RoleBinding/ClusterRoleBinding

角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一组用户。 它包含若干主体(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。RoleBinding在指定的名称空间中执行授权,ClusterRoleBinding 在集群范围执行授权。
一个RoleBinding可以引用同一的名称空间中的任何Role。或者,一个RoleBinding可以引用某ClusterRole并将该 ClusterRole绑定到RoleBinding所在的名称空间。如果希望将某ClusterRole绑定到集群中所有名称空间,需要使用ClusterRoleBinding。
RoleBinding或ClusterRoleBinding对象的名称必须是合法的路径分段名称。

  • RoleBinding示例

下面的例子中的RoleBinding将"pod-reader" Role授予在"default"名称空间中的用户"jane"。这样用户"jane"就具有了读取"default"名字空间中Pods的权限。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pods
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
  name: jane # "name" 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
  kind: Role # 此字段必须是 Role 或 ClusterRole
  name: pod-reader     # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
  apiGroup: rbac.authorization.k8s.io

RoleBinding也可以引用ClusterRole,以将对应ClusterRole中定义的访问权限授予RoleBinding所在名称空间的资源。这种引用使得可以跨整个集群定义一组通用的角色,之后在多个名称空间中复用。
例如,尽管下面的RoleBinding引用的是一个ClusterRole,"dave"(这里的主体, 区分大小写)只能访问 "development"名称空间中的Secrets对象,RoleBinding所在的名字空间(由metadata决定)是 "development"。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
  name: read-secrets
  # RoleBinding 的名字空间决定了访问权限的授予范围。
  # 这里隐含授权仅在 "development" 名字空间内的访问权限。
  namespace: development
subjects:
- kind: User
  name: dave # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
  • ClusterRoleBinding示例

要跨整个集群完成访问权限的授予,可以使用ClusterRoleBinding。下面的ClusterRoleBinding允许 "manager"组内的所有用户访问任何名字空间中的Secrets。

apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 secrets
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

创建了绑定之后,不能再修改绑定对象所引用的Role或ClusterRole。试图改变绑定对象的roleRef将导致合法性检查错误。命令kubectl auth reconcile可以创建或者更新包含RBAC对象的清单文件,并且在必要的情况下删除和重新创建绑定对象,以改变所引用的角色。
如果想要改变现有绑定对象中roleRef字段的内容,必须删除重新创建绑定对象。这种限制有两个主要原因:

  1. 针对不同角色的绑定是完全不一样的绑定,要求通过删除/重建绑定来更改roleRef,这样可以确保要赋予绑定的所有主体会被授予新的角色(而不是在允许或者不小心修改了roleRef的情况下导致所有现有主体未经验证即被授予新角色对应的权限)。
  2. 将roleRef设置为不可以改变,这使得可以为用户授予对现有绑定对象的update权限, 这样可以让他们管理主体列表,同时不能更改被授予这些主体的角色。
ABAC

基于属性的访问控制(Attribute-based access control - ABAC)定义了访问控制范例,其中通过使用将属性组合在一起的策略来向用户授予访问权限。
基于ABAC模式,可以指定策略文件--authorization-policy-file=SOME_FILENAME。文件格式为JSON Lines,不应存在外层的列表或映射,每行应只有一个映射。每一行都是一个策略对象,策略对象是具有以下属性的映射:

  • 版本控制属性:
    • apiVersion,字符串类型:有效值为abac.authorization.kubernetes.io/v1beta1,允许对策略格式进行版本控制和转换
    • kind,字符串类型:有效值为Policy,允许对策略格式进行版本控制和转换
  • spec 配置为具有以下映射的属性:
    • 主体匹配属性:
      • user,字符串类型:来自--token-auth-file的用户字符串,如果指定 user,它必须与验证用户的用户名匹配。
      • group,字符串类型:如果指定 group,必须与经过身份验证的用户组匹配,system:authenticated匹配所有经过身份验证的请求,system:unauthenticated匹配所有未经过身份验证的请求。
  • 资源匹配属性:
    • apiGroup,字符串类型:一个API组。例: extensions,通配符,*匹配所有 API 组。
    • namespace,字符串类型:一个命名空间。例:kube-system,通配符,*匹配所有资源请求。
    • resource,字符串类型:资源类型。例:pods,通配符:*匹配所有资源请求。
  • 非资源匹配属性:
    • nonResourcePath,字符串类型;非资源请求路径。
      • 例如:/version或 /apis
      • 通配符:* 匹配所有非资源请求,foo/* 匹配 /foo/ 的所有子路径。
  • readonly,键入布尔值,如果为 true,则表示该策略仅适用于 get、list 和 watch 操作。

说明:属性未设置等效于属性被设置为对应类型的零值( 例如空字符串、0、false),然而,出于可读性考虑,应尽量选择不设置这类属性。在将来,策略可能以 JSON 格式表示,并通过 REST 界面进行管理。

posted @ 2022-05-06 10:36  星光Starsray  阅读(153)  评论(0编辑  收藏  举报