Kubernetes 入门教程之九

大纲

Kubernetes 核心技术

配置管理

Secret

Secret 的介绍
  • Secret 的概述

    • Secret 是 Kubernetes 中一种用于存储敏感数据的对象类型。
    • Secret 会将敏感数据存储在 Etcd 里面,让 Pod 容器以环境变量或者挂载 Volume(卷)的方式进行访问。
    • Secret 的主要设计目标是:避免将敏感信息直接写入 Pod 的镜像或配置文件(如 Deployment、ConfigMap)中。
    • 这些敏感数据包括(不限于):
      • 数据库的用户名和密码
      • API Token 或访问密钥
      • SSL/TLS 私钥和证书
      • SSH 密钥
  • Secret 的作用

作用说明
保护敏感信息通过 Base64 编码的形式保存机密数据,防止在 YAML 文件中明文出现。
与 Pod 解耦应用不直接携带凭证,Secret 可独立管理、更新和分发。
灵活挂载可作为环境变量或 Volume(卷)文件挂载到容器中。
与 ServiceAccount 结合使用可用于保存访问 API Server 的 Token 等认证信息。
支持自动轮换和更新可结合控制器或外部系统(如 Vault)可实现密钥动态更新。
  • Secret 的类型
Secret 类型用途说明
Opaque 默认类型,用于存放任意用户定义的键值对。
kubernetes.io/dockerconfigjson 存放 Docker Registry 的认证信息,用于拉取私有镜像。
kubernetes.io/service-account-token 系统自动创建,用于 ServiceAccount 与 API Server 通信。
kubernetes.io/tls 存放 TLS 证书与私钥,用于 HTTPS、Ingress 等场景。
bootstrap.kubernetes.io/token 集群引导时 Kubelet 注册节点所用的临时令牌。
  • Secret 的使用场景
使用场景使用说明
应用访问数据库存放数据库的账号密码,通过环境变量注入。
拉取私有镜像创建 Docker Registry Secret 供 imagePullSecrets 使用。
HTTPS 服务存放 TLS 证书,用于 Ingress 或自签服务。
外部 API 调用存放第三方服务的 API Token。
集群内部通信认证 ServiceAccount Token 类型 Secret。
  • Secret 的注意事项
    • 虽然 Secret 可以用于保护敏感信息,但它并非绝对安全:
      • 默认仅使用 Base64 编码,并未将信息加密存储;
      • 通常需要启用 Kubernetes Encryption at Rest,确保在 Etcd 中加密存储;
      • 合理配置集群安全机制(RBAC),限制访问 Secret 的权限;
      • 避免将 Secret 信息直接输出到日志文件或终端;
      • 建议结合外部安全系统(如 HashiCorp Vault、Sealed Secrets、External Secrets Operator)进行管理。
    • Secret 以 Volume(卷)的方式挂载时支持热更新
      • Secret 更新后,不会自动更新 Pod 中容器内的环境变量,也不会触发容器重启;
      • 但是,如果 Secret 是以 Volume(卷)的方式挂载,并且该挂载未使用 subPath,则 Pod 中容器内挂载的文件会在 60 秒内自动更新(热更新);
      • 另外,应用程序需要在运行时重新读取这些文件(例如通过文件监听或定时重新加载配置)才能真正实现热更新。
    • 通过控制器(如 Deployment)触发 Pod 重启可以实现 Secret 更新生效
      • 更改 Secret 后,可以手动触发 Deployment 滚动重启(比如执行 kubectl rollout restart deployment <deployment-name>);
      • Deployment 滚动重启时,Kubernetes 会让该 Deployment 下的所有 Pod 重新创建,但不会更改镜像版本;
      • 当 Pod 重启后,容器启动时会重新加载 Secret,从而使最新的机密配置生效。

subPath 的作用

  • subPath 是 Kubernetes 在 Volume 挂载中的一个重要机制,用于只挂载卷里的单个文件或者子目录,而不是整个目录。
  • subPath 不支持热更新,即使底层 Volume(如 ConfigMap 或 Secret)更新后,挂载到 subPath 的文件也不会自动刷新(热更新)。
  • subPath 不能用于挂载整个目录时的热更新场景,如果需要实时更新配置(比如热更新 Nginx 配置),不可以使用 subPath
Secret 的创建

Secret 的创建通常有以下三种方式:

  • 通过命令行创建 Secret
1
2
# 手动创建 Secret
kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=123456
  • 通过文件创建 Secret
1
kubectl create secret generic db-secret --from-file=username.txt --from-file=password.txt
  • 通过 YAML 文件(比如 my-secret.yaml)创建 Secret
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # Base64(admin)
password: MTIzNDU2 # Base64(123456)
1
2
# 创建或更新 YAML 文件中定义的 Secret 对象
kubectl apply -f my-secret.yaml
Secret 的查看
  • 查看 Secret 的详情
1
kubectl describe secret my-secret
  • 查看 Secret 的完整内容
1
2
# 查看 Secret 的内容(注意:内容经过 Base64 编码)
kubectl get secret my-secret -o yaml
  • 查看 Secret 列表
1
2
3
4
5
6
7
8
# 查看默认命名空间下的所有 Secret
kubectl get secrets

# 查看特定命名空间下的所有 Secret
kubectl get secrets -n dev

# 查看所有命名空间下的 Secret
kubectl get secrets --all-namespaces
Secret 的更改

Secret 的更改有以下几种方式

  • (1) 使用 kubectl edit secret(最常用)直接编辑 Secret,默认会打开一个临时编辑器(比如 vinano),编辑后保存退出即可,保存后会自动更新 Secret。这种方式需要手动对配置内容进行 Base64 编码,比如 echo -n 'newuser' | base64
1
kubectl edit secret my-secret
  • (2) 使用 kubectl apply(声明式更新),如果有一个用于定义 Secret 的 YAML 文件(例如 my-secret.yaml),可以执行以下命令更新 Secret。这种方式可以直接在 YAML 文件中使用未经过 Base64 编码的配置内容,K8s 会自动将其转换成 Base64 编码。
1
kubectl apply -f my-secret.yaml
  • (3) 使用 kubectl patch secret(部分字段更新),只更新指定的字段(无需编辑整个 YAML)。这种方式 K8s 会自动将配置内容转换成 Base64 编码再存入 Etcd 中,不需要手动处理 Base64 编码。
1
2
3
4
5
# 更新单个字段(Key)
kubectl patch secret my-secret -p '{"stringData":{"password":"123456"}}'

# 同时改两个字段(Key)
kubectl patch secret my-secret -p '{"stringData":{"username":"root","password":"123456"}}'
  • (4) 直接重新创建 Secret(简单粗暴),也就是先删除旧的 Secret,然后创建新的 Secret。这种方式 K8s 会自动将配置内容转换成 Base64 编码再存入 Etcd 中,不需要手动处理 Base64 编码。
1
kubectl create secret generic my-secret --from-literal=username=root --from-literal=password=123456 --dry-run=client -o yaml | kubectl apply -f -

Secret 热更新说明

  • 上面介绍的四种 Secret 更新方式,都不会自动更新相关 Pod 中容器内的环境变量,也不会触发相关 Pod 的 滚动更新(即不会重启 Pod,不会重启容器)。
  • 但是,如果 Secret 是以 Volume(卷)的方式挂载,并且该挂载未使用 subPath,那么在 Secret 更新后,Pod 中容器内挂载的文件会在 60 秒内自动刷新(热更新)。

Secret 更新后滚动更新 Pod

  • 上面介绍的四种 Secret 更新方式,都不会触发相关 Pod 的滚动更新(Rolling Update),也就是 Pod 不会自动重启,但可以通过手动修改 Pod Annotations 的方式强制触发 Pod 的滚动更新。比如:
1
kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata":{"annotations": {"version/config": "20190411" }}}}}'
  • 在这个例子中,往 spec.template.metadata.annotations 中添加了 version/config,每次在 Secret 更新后,可以通过手动修改 version/config 来触发 Pod 的滚动更新。
  • 这里的 spec.template.metadata.annotations 是 Pod 模板(spec.template)元数据中的注解字段,当该字段的内容发生变化时,Kubernetes 会认为 Pod 模板被修改,于是触发新的 Replica Set(RS)创建,从而滚动替换所有旧的 Pod。

Pod 滚动更新方案

  • 在更新 Secret 后,除了可以通过手动修改 Pod Annotations 的方式强制触发 Pod 的滚动更新,还可以手动触发 Deployment 的滚动重启,从而让 Pod 重启,比如执行命令 kubectl rollout restart deployment <deployment-name>
  • 更推荐使用自动检测 Secret 变更的方案(更高级),例如借助 Stakater Reloader 等第三方工具监控 Secret 的变化。一旦检测到更新,就会自动触发相关 Pod 的滚动更新,从而确保配置自动生效。
Secret 的删除
1
2
# 删除默认命名空间下的单个 Secret
kubectl delete secret my-secret
1
2
# 删除默认命名空间下的多个 Secret
kubectl delete secret my-secret db-secret api-token
1
2
# 删除默认命名空间下的所有 Secret(慎用),系统自动生成的 ServiceAccount Token Secret 也会被删掉,从而影响 K8s 集群的正常运行
kubectl delete secret --all
1
2
# 删除特定命名空间下的单个 Secret
kubectl delete secret my-secret -n dev
在 Pod 中使用 Secret

Secret 创建后,可以通过以下两种方式供 Pod 容器使用:

  • 挂载为环境变量,K8s 会将 Secret 中的键值对映射为系统环境变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
name: secret-env-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "echo $DB_USER $DB_PASS; tail -f /dev/null"]
env: # 定义环境变量
- name: DB_USER # 环境变量名
valueFrom: # 值来源于外部引用
secretKeyRef: # 引用类型为 Secret
name: my-secret # Secret 的名称(需事先创建)
key: username # Secret 中对应的键
- name: DB_PASS
valueFrom:
secretKeyRef:
name: my-secret
key: password
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 或者使用 Deployment 管理 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: secret-env-demo
spec:
replicas: 1
selector:
matchLabels:
app: secret-env-demo
template:
metadata:
labels:
app: secret-env-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "echo $DB_USER $DB_PASS; tail -f /dev/null"]
env: # 定义环境变量
- name: DB_USER # 环境变量名
valueFrom: # 值来源于外部引用
secretKeyRef: # 引用类型为 Secret
name: my-secret # Secret 的名称(需事先创建)
key: username # Secret 中对应的键
- name: DB_PASS
valueFrom:
secretKeyRef:
name: my-secret
key: password
  • 挂载为 Volume(卷),K8s 会自动将 Secret 中每个键映射为文件名(最终会自动创建多个文件),文件内容为键对应的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "cat /etc/secret-data/username; tail -f /dev/null"]
volumeMounts: # 定义容器内要挂载的卷
- name: secret-volume # 对应下面 volumes 中的卷名称(必须一致)
mountPath: /etc/secret-data # 将 Secret 内容挂载到容器内的该目录下
readOnly: true # 设置为只读(推荐),防止容器内误修改
volumes: # 在 Pod 层定义卷(Volume)
- name: secret-volume # 指定卷的名称
secret: # 指定卷的类型为 Secret
secretName: my-secret # 指定引用的 Secret 名称(需事先创建)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 或者使用 Deployment 管理 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: secret-volume-demo
spec:
replicas: 1
selector:
matchLabels:
app: secret-volume-demo
template:
metadata:
labels:
app: secret-volume-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "cat /etc/secret-data/username; tail -f /dev/null"]
volumeMounts: # 定义容器内要挂载的卷
- name: secret-volume # 对应下面 volumes 中的卷名称(必须一致)
mountPath: /etc/secret-data # 将 Secret 内容挂载到容器内的该目录下
readOnly: true # 设置为只读(推荐),防止容器内误修改
volumes: # 在 Pod 层定义卷(Volume)
- name: secret-volume # 指定卷的名称
secret: # 指定卷的类型为 Secret
secretName: my-secret # 指定引用的 Secret 名称(需事先创建)
Secret 的完整使用案例
  • 通过 YAML 文件(比如 secret-env-demo.yaml)定义 Secret 和 Pod,并使用环境变量的方式引用 Secret
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 定义 Secret
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # Base64(admin)
password: MTIzNDU2 # Base64(123456)

---

# 定义 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: secret-env-demo
spec:
replicas: 1
selector:
matchLabels:
app: secret-env-demo
template:
metadata:
labels:
app: secret-env-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "echo $DB_USER $DB_PASS; tail -f /dev/null"]
env: # 定义环境变量
- name: DB_USER # 环境变量名
valueFrom: # 值来源于外部引用
secretKeyRef: # 引用类型为 Secret
name: my-secret # Secret 的名称(需事先创建)
key: username # Secret 中对应的键
- name: DB_PASS
valueFrom:
secretKeyRef:
name: my-secret
key: password
1
2
# 创建或更新 YAML 文件中定义的 Secret 和 Deployment 对象
kubectl apply -f secret-env-demo.yaml
  • 查看 Pod 的运行状态
1
kubectl get pods -o wide
1
2
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
secret-env-demo-6c74c9dd76-9rbgr 1/1 Running 0 39s 10.244.0.13 k8s-master <none> <none>
  • 查看 Pod 的日志信息
1
kubectl logs secret-env-demo-6c74c9dd76-9rbgr
1
admin 123456

ConfigMap

在 Kubernetes 中,ConfigMap(配置映射)是一种非常重要的配置管理对象,用于将配置数据与应用程序代码分离。它的设计初衷是:应用程序镜像保持通用性,而不同环境下的配置信息(如开发、测试、生产)通过 ConfigMap 动态注入。

ConfigMap 的介绍
  • ConfigMap 的概述

    • ConfigMap 是一种用于存储非敏感配置信息的键值对集合的 Kubernetes 资源对象。
    • ConfigMap 会将非敏感数据存储在 Etcd 里面,让 Pod 容器以环境变量和挂载 Volume(卷)的方式进行访问。
    • ConfigMap 的核心思想是:配置应该与镜像解耦,应用部署时再注入配置,换句话说:
      • 用户可以在不修改镜像的前提下更改配置;
      • 用户可以让相同的容器镜像在不同环境中以不同的方式运行。
  • ConfigMap 的作用

    • 配置解耦
      • 应用程序不再依赖镜像内置的配置,而是从外部(ConfigMap)加载配置。
    • 集中化管理配置
      • 所有 Pod 的环境变量、配置文件都可以统一由 ConfigMap 管理。
    • 灵活的注入方式
      • ConfigMap 可通过以下方式注入到 Pod 容器中:
        • 作为环境变量;
        • 以卷(Volume)挂载的方式出现在容器文件系统中。
  • ConfigMap 的注意事项

    • ConfigMap 不适合存储敏感信息
      • 因为信息是明文保存的,敏感信息应该使用 Secret 进行存储。
    • ConfigMap 以 Volume(卷)的方式挂载时支持热更新
      • ConfigMap 更新后,不会自动更新 Pod 中容器内的环境变量,也不会触发容器重启;
      • 但是,如果 ConfigMap 是以 Volume(卷)的方式挂载,并且该挂载未使用 subPath,则 Pod 中容器内挂载的文件会在 60 秒内自动更新(热更新);
      • 另外,应用程序需要在运行时重新读取这些文件(例如通过文件监听或定时重新加载配置)才能真正实现热更新。
    • 通过控制器(如 Deployment)触发 Pod 重启可以实现 ConfigMap 更新生效
      • 更改 ConfigMap 后,手动触发 Deployment 滚动重启(比如执行 kubectl rollout restart deployment <deployment-name>);
      • Deployment 滚动重启时,K8s 会让 Deployment 下的 Pod 全部重新创建,但不会改变镜像版本;
      • 当 Pod 重启后,容器启动时会重新加载 ConfigMap,从而使最新的配置生效。
    • 配置信息的大小限制
      • 单个 ConfigMap 的大小不能超过 1MB。
  • ConfigMap 的使用场景

使用场景示例
多环境配置开发、测试、生产等环境使用不同的 ConfigMap
应用启动参数通过环境变量动态配置应用的启动参数
配置文件注入将配置文件挂载进容器内部,如 Nginx、Tomcat 的配置文件
滚动更新配置更改 ConfigMap 后,可以通过 Deployment 滚动重启 Pod,动态加载新的配置
与 Secret 搭配使用 ConfigMap 负责管理非敏感配置信息,Secret 负责管理敏感配置信息(如密码、Token)
  • ConfigMap 的最佳实践
使用场景推荐方案
应用支持热更新(如 Nginx、Envoy)挂载 ConfigMap 文件,监控文件变更,感知配置更新
应用不支持热更新(如 Java Spring Boot)更改 ConfigMap 后,触发 Deployment 滚动重启(kubectl rollout restart deployment <deployment-name>),不会改变镜像版本
同时包含敏感与非敏感配置使用 Secret + ConfigMap 分开挂载
  • ConfigMap 与 Secret 对比
对比项 ConfigMapSecret
内容非敏感配置信息敏感配置信息(如密码、证书)
编码明文 Base64 编码
用途普通配置文件、应用启动参数密钥、Token、证书
安全性
存储方式 Etcd 明文存储 Etcd 加密存储(需要额外配置)

subPath 的作用

  • subPath 是 Kubernetes 在 Volume 挂载中的一个重要机制,用于只挂载卷里的单个文件或者子目录,而不是整个目录。
  • subPath 不支持热更新,即使底层 Volume(如 ConfigMap 或 Secret)更新后,挂载到 subPath 的文件也不会自动刷新(热更新)。
  • subPath 不能用于挂载整个目录时的热更新场景,如果需要实时更新配置(比如热更新 Nginx 配置),不可以使用 subPath
ConfigMap 的创建

ConfigMap 的创建通常有以下几种方式:

  • 通过命令行创建 ConfigMap
1
kubectl create configmap app-config --from-literal=app_mode=production --from-literal=app_debug=false
  • 通过文件创建 ConfigMap
1
kubectl create configmap app-config --from-file=app.properties
  • 通过目录创建 ConfigMap
1
kubectl create configmap app-config --from-file=./config/
  • 通过 YAML 文件(比如 app-config.yaml)创建 ConfigMap
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
app_mode: "production"
app_debug: "false"
database.conf: |
host=127.0.0.1
port=3306
1
2
# 创建或更新 YAML 文件中定义的 ConfigMap 对象
kubectl apply -f app-config.yaml
ConfigMap 的查看
  • 查看 ConfigMap 的详情
1
kubectl describe configmap app-config
  • 查看 ConfigMap 的完整内容
1
kubectl get configmap app-config -o yaml
  • 查看 ConfigMap 列表
1
2
3
4
5
6
7
8
# 查看默认命名空间下的所有 ConfigMap
kubectl get configmaps

# 查看特定命名空间下的所有 ConfigMap
kubectl get configmaps -n dev

# 查看所有命名空间下的 ConfigMap
kubectl get configmaps --all-namespaces
ConfigMap 的更改

ConfigMap 的更改有以下几种方式

  • (1) 使用 kubectl edit configmap(最常用)直接编辑 ConfigMap,默认会打开一个临时编辑器(比如 vinano),编辑后保存退出即可,保存后会自动更新 ConfigMap
1
kubectl edit configmap app-config
  • (2) 使用 kubectl apply(声明式更新),如果有一个用于定义 ConfigMap 的 YAML 文件(例如 app-config.yaml),可以执行以下命令更新 ConfigMap
1
kubectl apply -f app-config.yaml
  • (3) 使用 kubectl patch configmap(部分字段更新),只更新指定的字段(无需编辑整个 YAML)
1
2
3
4
5
# 更新单个字段(Key)
kubectl patch configmap app-config -p '{"data":{"app_mode":"development"}}'

# 同时改两个字段(Key)
kubectl patch configmap app-config -p '{"data":{"app_mode":"development","app_debug":"true"}}'
  • (4) 直接重新创建 ConfigMap(简单粗暴),也就是先删除旧的 ConfigMap,然后创建新的 ConfigMap
1
kubectl create configmap app-config --from-literal=app_mode=development --from-literal=app_debug=true --dry-run=client -o yaml | kubectl apply -f -

ConfigMap 热更新说明

  • 上面介绍的四种 ConfigMap 更新方式,都不会自动更新相关 Pod 中容器内的环境变量,也不会触发相关 Pod 的 滚动更新(即不会重启 Pod,不会重启容器)。
  • 但是,如果 ConfigMap 是以 Volume(卷)的方式挂载,并且该挂载未使用 subPath,那么在 ConfigMap 更新后,Pod 中容器内挂载的文件会在 60 秒内自动刷新(热更新)。

ConfigMap 更新后滚动更新 Pod

  • 上面介绍的四种 ConfigMap 更新方式,都不会触发相关 Pod 的滚动更新(Rolling Update),也就是 Pod 不会自动重启,但可以通过手动修改 Pod Annotations 的方式强制触发 Pod 的滚动更新。比如:
1
kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata":{"annotations": {"version/config": "20190411" }}}}}'
  • 在这个例子中,往 spec.template.metadata.annotations 中添加了 version/config,每次在 ConfigMap 更新后,可以通过手动修改 version/config 来触发 Pod 的滚动更新。
  • 这里的 spec.template.metadata.annotations 是 Pod 模板(spec.template)元数据中的注解字段,当该字段的内容发生变化时,Kubernetes 会认为 Pod 模板被修改,于是触发新的 Replica Set(RS)创建,从而滚动替换所有旧的 Pod。

Pod 滚动更新方案

  • 在更新 ConfigMap 后,除了可以通过手动修改 Pod Annotations 的方式强制触发 Pod 的滚动更新,还可以手动触发 Deployment 的滚动重启,从而让 Pod 重启,比如执行命令 kubectl rollout restart deployment <deployment-name>
  • 更推荐使用自动检测 ConfigMap 变更的方案(更高级),例如借助 Stakater Reloader 等第三方工具监控 ConfigMap 的变化。一旦检测到更新,就会自动触发相关 Pod 的滚动更新,从而确保配置自动生效。
ConfigMap 的删除
1
2
# 删除默认命名空间下的单个 ConfigMap
kubectl delete configmap app-config
1
2
# 删除默认命名空间下的多个 ConfigMap
kubectl delete configmap cm1 cm2 cm3
1
2
# 删除默认命名空间下的所有 ConfigMap(慎用),系统自动生成的 ConfigMap 也会被删掉,从而影响 K8s 集群的正常运行
kubectl delete configmap --all
1
2
# 删除特定命名空间下的单个 ConfigMap
kubectl delete configmap nginx-config -n dev

特别注意

  • 删除 ConfigMap 不会立即影响已运行的 Pod,除非该 ConfigMap 是以挂载卷(Volume)或环境变量形式注入,并且 Pod 被重新启动或重新加载。
在 Pod 中使用 ConfigMap

ConfigMap 创建后,可以通过以下两种方式供 Pod 容器使用:

  • 挂载为环境变量:K8s 会将 ConfigMap 中的键值对映射为系统环境变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
name: configmap-env-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "echo $APP_MODE $APP_DEBUG; tail -f /dev/null"]
env: # 定义环境变量
- name: APP_MODE # 环境变量名
valueFrom: # 值来源于外部引用
configMapKeyRef: # 引用类型为 ConfigMap
name: app-config # ConfigMap 的名称(需事先创建)
key: app_mode # ConfigMap 中对应的键
- name: APP_DEBUG
valueFrom:
configMapKeyRef:
name: app-config
key: app_debug
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 或者使用 Deployment 管理 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-env-demo
spec:
replicas: 1
selector:
matchLabels:
app: configmap-env-demo
template:
metadata:
labels:
app: configmap-env-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "echo $APP_MODE $APP_DEBUG; tail -f /dev/null"]
env: # 定义环境变量
- name: APP_MODE # 环境变量名
valueFrom: # 值来源于外部引用
configMapKeyRef: # 引用类型为 ConfigMap
name: app-config # ConfigMap 的名称(需事先创建)
key: app_mode # ConfigMap 中对应的键
- name: APP_DEBUG
valueFrom:
configMapKeyRef:
name: app-config
key: app_debug
  • 挂载为 Volume(卷):K8s 会自动将 ConfigMap 中每个键映射为文件名(最终会自动创建多个文件),文件内容为键对应的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: configmap-volume-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "cat /etc/config-data/app_mode; tail -f /dev/null"]
volumeMounts: # 定义容器内要挂载的卷
- name: config-volume # 对应下面 volumes 中的卷名称(必须一致)
mountPath: /etc/config-data # 将 ConfigMap 内容挂载到容器内的该目录下
readOnly: true # 设置为只读(推荐),防止容器内误修改
volumes: # 在 Pod 层定义卷(Volume)
- name: config-volume # 指定卷的名称
configMap: # 指定卷的类型为 ConfigMap
name: app-config # 指定引用的 ConfigMap 名称(需事先创建)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 或者使用 Deployment 管理 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-volume-demo
spec:
replicas: 1
selector:
matchLabels:
app: configmap-volume-demo
template:
metadata:
labels:
app: configmap-volume-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "cat /etc/config-data/app_mode; tail -f /dev/null"]
volumeMounts: # 定义容器内要挂载的卷
- name: config-volume # 对应下面 volumes 中的卷名称(必须一致)
mountPath: /etc/config-data # 将 ConfigMap 内容挂载到容器内的该目录下
readOnly: true # 设置为只读(推荐),防止容器内误修改
volumes: # 在 Pod 层定义卷(Volume)
- name: config-volume # 指定卷的名称
configMap: # 指定卷的类型为 ConfigMap
name: app-config # 指定引用的 ConfigMap 名称(需事先创建)
ConfigMap 的完整使用案例
  • 通过 YAML 文件(比如 configmap-env-demo.yaml)定义 ConfigMap 和 Pod,并使用环境变量的方式引用 ConfigMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 定义 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
app_mode: "production"
app_debug: "false"

---

# 定义 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-env-demo
spec:
replicas: 1
selector:
matchLabels:
app: configmap-env-demo
template:
metadata:
labels:
app: configmap-env-demo
spec:
containers:
- name: demo
image: busybox
command: ["sh", "-c", "echo $APP_MODE $APP_DEBUG; tail -f /dev/null"]
env: # 定义环境变量
- name: APP_MODE # 环境变量名
valueFrom: # 值来源于外部引用
configMapKeyRef: # 引用类型为 ConfigMap
name: app-config # ConfigMap 的名称(需事先创建)
key: app_mode # ConfigMap 中对应的键
- name: APP_DEBUG
valueFrom:
configMapKeyRef:
name: app-config
key: app_debug
1
2
# 创建或更新 YAML 文件中定义的 ConfigMap 和 Deployment 对象
kubectl apply -f configmap-env-demo.yaml
  • 查看 Pod 的运行状态
1
kubectl get pods -o wide
1
2
NAME                                  READY   STATUS    RESTARTS   AGE
configmap-env-demo-698c84b677-625sk 1/1 Running 0 49s
  • 查看 Pod 的日志信息
1
kubectl logs configmap-env-demo-698c84b677-625sk
1
production false
ConfigMap 实现 Nginx 自动热更新

集群安全机制

RBAC 的基本概念

RBAC(Role Based Access Control,基于角色的访问控制)在 Kubernetes v1.5 中首次引入,并在 v1.6 版本升级为 Beta,成为 Kubeadm 安装方式下的默认授权模式。RBAC 的核心思想是:通过角色定义权限,通过角色绑定将权限授予给特定的主体(比如 User、Group、ServiceAccount),从而实现精细化的访问控制。Kubernetes 集群要启用 RBAC 授权模式,需要在 API Server 的启动参数中添加 --authorization-mode=RBAC


  • 在 RBAC(基于角色的访问控制)中,主要包含以下四个核心概念:

    • 角色(Role / ClusterRole):
      • 定义一组可执行的权限规则,即允许对哪些资源执行哪些操作。
    • 角色绑定(RoleBinding / ClusterRoleBinding):
      • 将角色与主体进行关联,从而使主体获得该角色定义的权限。
    • 主体(Subject):
      • 表示可以被授予权限的实体,包括用户(User)、用户组(Group)和服务账户(ServiceAccount)。
    • 权限规则(Policy Rules):
      • 具体描述允许执行的操作,包括对哪些资源(resources)、在哪些命名空间(namespace)、执行哪些动作(verbs)等。
  • 相比其他访问控制方式,RBAC(基于角色的访问控制)具有以下优势:

    • 权限覆盖全面:
      • 对集群中的资源和非资源类型的访问权限均提供完整支持。
    • 动态调整:
      • 可在集群运行时更改权限配置,无需重启 API Server。
    • API 原生支持:
      • RBAC 由若干 API 对象构成,可像其他 Kubernetes 资源一样通过 kubectl 或 API 进行管理。

  • 在访问 Kubernetes 集群的时候,需要经过以下三个步骤:
    • 第一步:认证
      • 核心目标:
        • 用于确认访问者的身份。
      • 访问端口:
        • 对外不暴露 8080 端口(仅供集群内部组件访问);
        • 对外提供的访问端口为 6443(HTTPS 端口)。
      • 认证方式:
        • HTTPS 证书认证:基于 CA 证书验证客户端身份;
        • HTTP Token 认证:通过 Token 标识用户身份;
        • HTTP Basic 认证:基于用户名和密码进行身份校验。
    • 第二步:鉴权(授权)
      • 在确认用户身份后,判断其是否具备执行该操作的权限;
      • Kubernetes 主要基于 RBAC(基于角色的访问控制)机制进行鉴权;
      • 权限通过角色(Role / ClusterRole)与角色绑定(RoleBinding / ClusterRoleBinding)来定义和分配。
    • 第三步:准入控制
      • 用于在请求通过认证和鉴权后,对请求内容进行进一步的策略检查;
      • 本质上是一个由多个准入控制器(Admission Controllers)组成的列表;
      • 如果请求被这些准入控制器中的规则允许,则放行请求;否则,请求会被拒绝。

提示

在访问 Kubernetes 的过程中,都需要经过 API Server,由 API Server 做统一协调。比如,访问过程中需要 CA 证书、Token、或者用户名和密码;如果访问 Pod,则需要 Service Account。

RBAC 的 API 资源对象

在 Kubernetes 中,RBAC 引入了 4 个新的顶级资源对象:Role、ClusterRole、RoleBinding、ClusterRoleBinding。同其他 API 资源对象一样,用户可以使用 kubectl 命令或者 API 调用等方式操作这些资源对象。

Role(角色)

概念介绍

  • Role(角色)只能对命名空间内的资源进行授权。
  • 一个 Role(角色)就是一组权限的集合,这里的权限都是许可形式的,不存在拒绝的规则。
  • 在一个命名空间中,可以用 Role(角色)来定义一个角色。如果是集群级别的,就需要使用 ClusterRole(集群角色)了。

使用案例

  • 在下面的例子中,定义的 Role(角色)具备读取 Pod 的权限:
1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: default
rules:
- apiGroups: [""] # 空字符串表示核心 API 组(/api/v1)
resources: ["pods"]
verbs: ["get", "watch", "list"]
参数名称说明配置示例
apiGroups支持的 API 组列表,用于指定资源所属的 API 组。核心组使用空字符串 "" 表示。"", "apps", "batch", "extensions"(对应 apiVersion: v1apps/v1batch/v1 等)
resources支持的资源对象列表,指定该角色可操作的 Kubernetes 资源类型。podsdeploymentsjobsservices
verbs对资源对象的操作方法列表,定义允许的具体操作。getwatchlistcreateupdatedeletepatchreplace
ClusterRole(集群角色)

概念介绍

ClusterRole(集群角色)除了具备与 Role(角色)在单一命名空间内相同的资源管理能力外,由于其作用域为集群级别,还可用于授权以下几类对象:

  • 集群范围的资源,例如 Node(节点);
  • 非资源型路径,例如 /healthz
  • 跨全部命名空间的资源,例如在所有命名空间中的 pods

使用案例

  • 在下面的例子中,定义的 ClusterRole(集群角色)可以让用户有权访问任意一个或所有命名空间的 Secrets:
1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
# ClusterRole 不受限于命名空间,因此不需要定义 namespace
rules:
- apiGroups: [""] # 空字符串表示核心 API 组(/api/v1)
resources: ["secrets"]
verbs: ["get", "watch", "list"]
参数名称说明配置示例
apiGroups支持的 API 组列表,用于指定资源所属的 API 组。核心组使用空字符串 "" 表示。"", "apps", "batch", "extensions"(对应 apiVersion: v1apps/v1batch/v1 等)
resources支持的资源对象列表,指定该角色可操作的 Kubernetes 资源类型。podsdeploymentsjobsservices
verbs对资源对象的操作方法列表,定义允许的具体操作。getwatchlistcreateupdatedeletepatchreplace
RoleBinding(角色绑定)

核心概念

  • RoleBinding(角色绑定)用于将一个 Role(角色)绑定到一个指定的主体上,绑定的主体可以是 User(用户)、Group(用户组)或 ServiceAccount(服务账户)。
  • RoleBinding(角色绑定)的授权范围限定在某个命名空间内,它可以引用同一命名空间中的 Role,为该命名空间内的资源授予相应的访问权限。

使用案例

  • 在下面的例子中,RoleBinding(角色绑定)将在 default 命名空间中将 pod-reader 角色授予用户 jane,可以让 jane 用户读取 default 命名空间里的 Pod:
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
参数名称说明配置示例
subjects指定要绑定的访问主体,可以是用户(User)、用户组(Group)或服务账户(ServiceAccount)。此处绑定的主体是名为 jane 的用户。jane
roleRef指定要绑定的角色,用于定义该主体可执行的权限操作。此处引用的是命名空间内的 pod-reader 角色。pod-reader
namespace指定 RoleBinding 所属的命名空间,授权范围仅限于该命名空间内。此处为 default 命名空间。default

特别注意

RoleBinding 除了可以引用 Role,还可以引用 ClusterRole,对属于同一命名空间内 ClusterRole 定义的资源主体进行授权。一种常见的做法是 Kubernetes 集群管理员为集群范围预先定义好一组角色(ClusterRole),然后在多个命名空间中重复使用这些 ClusterRole。

  • 在下面的例子中,使用 RoleBinding 绑定 ClusterRole(集群角色)secret-reader,使用户 dave 只能读取 development 命名空间中的 Secret:
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-secrets
namespace: development
subjects:
- kind: User
name: dave
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
  • 配置说明:
    • RoleBinding 将集群角色 secret-reader 授权给命名空间 development 中的用户 dave
    • 虽然绑定的是 集群角色(ClusterRole),但作用仍限定在 development 命名空间。
    • subjects 指定了被授权的主体,roleRef 指定了要绑定的集群角色(ClusterRole)。
ClusterRoleBinding(集群角色绑定)

核心概念

  • ClusterRoleBinding(集群角色绑定)用于将一个 ClusterRole(集群角色)绑定到一个指定的主体上,绑定的主体可以是 User(用户)、Group(用户组)或 ServiceAccount(服务账户)。
  • ClusterRoleBinding(集群角色绑定)的授权范围为整个集群,它可以引用任意命名空间中的 ServiceAccount,并为所有命名空间或集群级资源授予相应的访问权限。
  • ClusterRoleBinding(集群角色绑定)中的角色只能是 ClusterRole(集群角色),不能是 Role(角色),用于执行集群级别或者对所有命名空间都生效的授权操作。

使用案例

  • 在下面的例子中,使用 ClusterRoleBinding 绑定 ClusterRole(集群角色)secret-reader,允许 manager 组的用户读取所有命名空间中的 Secret:
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
  • 配置说明:
    • ClusterRoleBinding 将集群角色 secret-reader 授权给整个集群中的用户组 manager
    • 授权作用范围为整个集群,不仅限于某个命名空间。
    • subjects 指定了被授权的主体,roleRef 指定了要绑定的集群角色(ClusterRole)。

RBAC 对资源的引用方式

  • 多数 Kubernetes 资源可以通过其名称字符串表示,即在 Endpoint 的 URL 相对路径中体现,例如 pods
  • 某些 Kubernetes API 包含下级资源,例如 Pod 的日志 (logs)。
    • 例如,Pod 日志的访问路径为: GET /api/v1/namespaces/{namespaces}/pods/{name}/log
    • 在这个例子中,Pod 是命名空间内的主资源,而 log 是 Pod 的下级资源。
  • 如果要在 RBAC 角色(Role 或 ClusterRole)中体现这种层级关系,需要用斜杠 / 来分隔主资源和下级资源。
    • 例如,如果希望某个主体同时拥有读取 Pod 以及读取 Pod 日志的权限,则可以配置 resources 为一个数组:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
      name: pod-and-pod-logs-reader
      namespace: default
      rules:
      - apiGroups: [""] # 空字符串表示核心 API 组(/api/v1)
      resources: ["pods", "pods/log"] # 同时包含了主资源 pods 和下级资源 pods/log
      verbs: ["get", "list"] # 指定该角色允许执行的操作
  • Kubernetes 资源还可以通过名称(ResourceName)进行引用。
    • 在指定 ResourceName 后,使用 getdeleteupdatepatch 动词的请求,就会被限制在这个资源实例范围内。
    • 例如,下面的声明可以授权一个主体只能对一个叫 my-configmap 的 ConfigMap 执行 getupdate 操作:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
      name: configmap-updater
      namespace: default
      rules:
      - apiGroups: [""] # 空字符串表示核心 API 组(/api/v1)
      resources: ["configmaps"] # resources 应为复数形式,比如 configmaps
      resourceNames: ["my-configmap"] # 指定作用对象
      verbs: ["update", "get"] # 指定该角色允许执行的操作

RBAC 中常见的角色定义示例

  • (1) 允许读取核心 API 组中 Pod 的资源
1
2
3
4
rules:
- apiGroups: [""] # 空字符串表示核心 API 组(/api/v1)
resources: ["pods"]
verbs: ["get", "list", "watch"]
  • (2) 允许读写 extensionsapps 两个 API 组中的 deployment 资源
1
2
3
4
rules:
- apiGroups: ["extensions", "apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  • (3) 允许读写 pods 及读写 jobs
1
2
3
4
5
6
7
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch", "extensions"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  • (4) 允许读取一个名为 my-config 的 ConfigMap(必须绑定到一个 RoleBinding 来限制到一个命名空间下的 ConfigMap):
1
2
3
4
5
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-config"]
verbs: ["get"]
  • (5) 允许读取核心 API 组的 Node 资源(Node 属于集群级别的资源,必须放在 ClusterRole 中,并使用 ClusterRoleBinding 进行绑定):
1
2
3
4
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
  • (6) 允许对非资源端点 /healthz 及其所有子路径进行 GET / POST 操作(必须使用 ClusterRole 和 ClusterRoleBinding)
1
2
3
rules:
- nonResourceURLs: ["/healthz", "/healthz/*"]
verbs: ["get", "post"]

RBAC 中常用的角色绑定示例

  • 绑定用户名 Alice@example.com
1
2
3
4
subjects:
- kind: User
name: "Alice@example.com"
apiGroup: rbac.authorization.k8s.io
  • 绑定组名 frontend-admins
1
2
3
4
subjects:
- kind: Group
name: "frontend-admins"
apiGroup: rbac.authorization.k8s.io
  • 绑定 kube-system 命名空间中的默认 Service Account
1
2
3
4
5
6
7
subjects:
- kind: Group
name: system:authentication
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: system:unauthentication
apiGroup: rbac.authorization.k8s.io

RBAC 中默认的角色和角色绑定

API Server 会创建一套默认的 ClusterRole 和 ClusterRoleBinding 资源对象,其中很多是以 system: 为前缀的,以表明这些资源属于基础架构,对这些对象的改动可能会造成集群故障。所有默认的 ClusterRole 和 RoleBinding 资源对象都会用标签 kubernetes.io/bootstrapping=rbac-defaults 进行标记。

常见的系统角色

默认的 ClusterRole 默认的 ClusterRoleBinding 描述
system:basic-usersystem:authenticatedsystem:unauthorized让用户能够读取自身的信息
system:discoverysystem:authenticatedsystem:unauthorized对 API 发现 Endpoint 的只读访问,用于 API 级别的发现和协商

常见的用户角色

有些默认角色不是以 system: 为前缀的,这部分角色是针对用户的,其中包含超级用户角色 cluster-admin,有的用于集群一级的角色 cluster-status,还有针对命名空间的角色 admineditview

默认的 ClusterRole 默认的 ClusterRoleBinding 描述
cluster-adminsystem:masters让超级用户可以对任何资源执行任何操作。如果在 ClusterRoleBinding 中使用,则影响的是整个集群的所有 NameSpace 中的任何资源;如果使用的是 RoleBinding,则能控制这一绑定的 NameSpace 中的资源,还包括 NameSpace 本身
cluster-statusNone 可以对基础集群状态信息进行只读访问
adminNone 允许 admin 访问,可以限制在一个 NameSpace 中使用 RoleBinding。如果在 RoleBinding 中使用,则允许对 NameSpace 中的大多数资源进行读写访问,其中包含创建角色和角色绑定的能力。这一角色不允许操作 NameSpace 本身,也不能写入资源限制
editNone 允许对 NameSpace 内的大多数资源进行读写操作,不允许查看或更改角色,以及角色绑定
viewNone 允许对多数资源对象进行只读操作,但是对角色、角色绑定及 Secret 是不可访问的

核心 Master 组件角色

默认的 ClusterRole 默认的 ClusterRoleBinding 描述
system:kube-schedulersystem:kube-scheduler 用户能够访问 kube-scheduler 组件所需的资源
system:kube-controller-managersystem:kube-controller-manager 用户能够访问 kube-controller-manager 组件所需的资源
system:nodesystem:nodes
- 允许访问 kubelet 所需的资源,包括对 Secret 的读取,以及对 Pod 的写入
- 未来会把上面的两个权限限制在分配到本 Node 的对象上
- 今后的鉴权过程,kubelet 必须以 system:node 及一个 system:node 形式的用户名进行。参看 https://pr.k8s.io/40476
system:node-proxiersystem:kube-proxy 用户允许访问 kube-proxy 所需的资源
system:kube-schedulersystem:kube-scheduler 用户能够访问 kube-scheduler 组件所需的资源

RBAC 中预防提权和授权初始化

  • RBAC API 会拒绝用户通过编辑角色或角色绑定的方式进行提权。这一限制是在 API 层面实现的,因此即使 RBAC 未启用,该限制仍然有效。
  • 用户只能在拥有某个角色的所有权限,且与该角色的生效范围一致的前提下,才能对角色进行创建和更改。例如,用户 user-1 没有列出集群中所有 Secret 的权限,就无法创建具有该权限的 ClusterRole
  • 要让一个用户能够创建或者更改角色,需要满足以下条件:
    • 授予用户一个允许创建 / 更改 RoleClusterRole 资源对象的角色;
    • 为用户授予角色时,要覆盖该用户所能控制的所有权限范围。
  • 如果用户尝试创建超出自身权限的 Role 或 ClusterRole,该 API 调用会被拒绝。
  • 如果一个用户的权限包含了某个角色的所有权限,则可以为其创建和更改该角色的绑定;或者,如果用户被授予了针对某个角色的绑定授权,也可以完成此操作。
    • 例如,用户 user-1 没有列出集群中所有 Secret 的权限,因此无法为一个具有此权限的角色创建 ClusterRoleBinding。要使用户能够创建或更改该角色绑定,需要:
      • 授予用户一个允许创建和更改 RoleBindingClusterRoleBinding 的角色;
      • 授予用户绑定某一角色的权限,可以通过两种方式:
        • 隐式:让用户拥有该角色的所有权限;
        • 显式:授予用户针对该角色或 ClusterRoleBinding 的操作权限。
  • 在进行第一个角色和角色绑定时,必须让初始用户具备其尚未被授予的权限,要进行初始的角色和角色绑定设置,有以下两种方法:
    • 使用属于 system:masters 组的身份,这一群组默认具有 cluster-admin 这一超级用户角色的绑定;
    • 如果 API Server 以 --insecure-port 参数启动,则客户端通过这个非安全端口进行接口调用,这一端口没有认证鉴权的限制。

举个例子,允许用户 user-1user-1-namespace 命名空间中,可以对其他用户授予 admineditview 角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings"]
verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
verbs: ["bind"]
resourceNames: ["admin", "edit", "view"]

---

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-grantor-binding
namespace: user-1-namespace
subjects:
- kind: User
name: user-1
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: role-grantor
apiGroup: rbac.authorization.k8s.io

RBAC 的完整使用演示案例

提示

  • 本节将演示如何在 Kubernetes 集群中使用 RBAC,并通过 CA 证书进行认证(识别身份)。
  • Kubernetes 集群要启用 RBAC 授权模式,需要在 API Server 的启动参数中添加 --authorization-mode=RBAC

(1) 创建命名空间

  • 创建命名空间
1
kubectl create ns roledemo
  • 查看所有命名空间
1
kubectl get ns
1
2
3
4
5
6
7
NAME              STATUS   AGE
default Active 83d
kube-flannel Active 81d
kube-node-lease Active 83d
kube-public Active 83d
kube-system Active 83d
roledemo Active 111m

(2) 创建 Deployment

  • 通过 YAML 文件(比如 nginx-deploy.yaml)创建 Deployment(用于创建和管理 Pod)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: roledemo # 指定命名空间
spec:
replicas: 1
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.15
ports:
- containerPort: 80
1
2
# 创建或更新 YAML 文件中定义的 Deployment 对象
kubectl apply -f nginx-deploy.yaml
  • 查看特定命名空间内的所有 Pod
1
kubectl get pods -n roledemo
1
2
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-85b7dd6b6d-29gs6 1/1 Running 0 16s

(3) 创建 Role(角色)

  • 通过 YAML 文件(比如 role-demo.yaml)创建 Role(角色)
1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: roledemo # 指定命名空间
name: pod-reader
rules:
- apiGroups: [""] # 空字符串表示核心 API 组(/api/v1)
resources: ["pods"] # resources 应为复数形式,比如 pods
verbs: ["get", "watch", "list"] # 指定该角色允许执行的操作
1
2
# 创建或更新 YAML 文件中定义的 Role 对象
kubectl apply -f role-demo.yaml
  • 查看特定命名空间内的所有 Role(角色)
1
kubectl get roles -n roledemo
1
2
NAME         CREATED AT
pod-reader 2025-10-08T11:47:10Z

(4) 创建 RoleBinding(角色绑定)

  • 通过 YAML 文件(比如 rolebinding-demo.yaml)创建 RoleBinding(角色绑定)
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: roledemo # 指定命名空间
subjects:
- kind: User
name: mary
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
1
2
# 创建或更新 YAML 文件中定义的 RoleBinding(角色绑定)
kubectl apply -f rolebinding-demo.yaml
  • 查看特定命名空间内的所有 RoleBinding(角色绑定)
1
kubectl get rolebindings -n roledemo
1
2
NAME        ROLE              AGE
read-pods Role/pod-reader 2m43s

(5) 基于 CFSSL 生成 CA 证书

  • 创建 ca-config.json 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
  • 创建 ca-csr.json 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing",
"O": "k8s",
"OU": "System"
}
]
}
  • 创建 mary-csr.json 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"CN": "mary",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
  • 拷贝 Kubernetes 集群搭建时所创建的 CA 证书(包括 ca.pemca-key.pem),请自行更改 CA 证书的路径
1
cp /opt/kubernetes/ssl/ca* .
  • 查看当前目录下的文件列表(最重要的是 ca-key.pemca.pem 文件)
1
ls .
1
ca-config.json  ca-csr.json  ca-key.pem  ca.pem  mary-csr.json  nginx-deploy.yaml  rolebinding-demo.yaml  role-demo.yaml
  • 通过 CFSSL 工具生成 CA 证书
1
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes mary-csr.json | cfssljson -bare mary
  • 查看当前目录下的文件列表(最重要的是新生成的 mary.csrmary-key.pemmary.pem 文件)
1
ls .
1
ca-config.json  ca-csr.json  ca-key.pem  ca.pem  mary.csr  mary-csr.json  mary-key.pem  mary.pem  nginx-deploy.yaml  rolebinding-demo.yaml  role-demo.yaml

(6) 为用户创建一个独立的 kubeconfig 配置文件

  • 定义集群连接信息(API Server 地址 + CA 证书),请自行将 192.168.2.191 更改为 API Server 的 IP 地址
1
2
3
4
5
kubectl config set-cluster kubernetes \
--certificate-authority=ca.pem \
--embed-certs=true \
--server=https://192.168.2.191:6443 \
--kubeconfig=mary-kubeconfig
  • 配置用户身份(客户端证书和私钥)
1
2
3
4
5
kubectl config set-credentials mary \
--client-key=mary-key.pem \
--client-certificate=mary.pem \
--embed-certs=true \
--kubeconfig=mary-kubeconfig
  • 将集群与用户 mary 绑定成一个上下文
1
2
3
4
kubectl config set-context default \
--cluster=kubernetes \
--user=mary \
--kubeconfig=mary-kubeconfig
  • 切换到 default 上下文,使后续 kubectl 命令能够以用户 mary 的身份访问集群
1
kubectl config use-context default --kubeconfig=mary-kubeconfig
  • 查看当前目录下的文件列表(最重要的是新生成的 mary-kubeconfig 文件)
1
ls .
1
ca-config.json  ca-csr.json  ca-key.pem  ca.pem  mary.csr  mary-csr.json  mary-key.pem  mary-kubeconfig  mary.pem  nginx-deploy.yaml  rolebinding-demo.yaml  role-demo.yaml
  • 查看凭证(用户)信息
1
kubectl --kubeconfig=mary-kubeconfig config view --minify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.2.191:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: mary
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: mary
user:
client-certificate-data: REDACTED
client-key-data: REDACTED

(7) 验证 RBAC 控制是否生效

  • 验证用户是否可以访问已被允许的资源,正常情况下用户 mary 可以在命名空间 roledemo 下列出或者查看 Pod
1
kubectl get pods -n roledemo --kubeconfig=mary-kubeconfig
1
2
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-85b7dd6b6d-29gs6 1/1 Running 1 18h
  • 验证用户是否可以越权访问其他命名空间的资源,正常情况下用户 mary 不可以在其他命名空间(比如 default)下列出或者查看 Pod
1
kubectl get pods -n default --kubeconfig=mary-kubeconfig
1
Error from server (Forbidden): pods is forbidden: User "mary" cannot list resource "pods" in API group "" in the namespace "default"
  • 验证用户是否可以执行未被允许的操作(例如,删除 Pod),正常情况下用户 mary 不能执行删除 Pod 的操作
1
kubectl delete pod nginx-deploy-85b7dd6b6d-29gs6 -n roledemo --kubeconfig=mary-kubeconfig
1
Error from server (Forbidden): pods "nginx-deploy-85b7dd6b6d-29gs6" is forbidden: User "mary" cannot delete resource "pods" in API group "" in the namespace "roledemo"
  • 通过 API Server + CA 证书直接验证用户身份,如果身份验证失败,可以添加 -v 参数让 curl 命令输出详细的日志信息来排查问题
1
curl --cert ./mary.pem --key ./mary-key.pem --cacert ./ca.pem https://192.168.2.191:6443/api/v1/namespaces/roledemo/pods
1
2
# 或者,curl 命令加上 -v 参数,输出详细的日志信息
curl -v --cert ./mary.pem --key ./mary-key.pem --cacert ./ca.pem https://192.168.2.191:6443/api/v1/namespaces/roledemo/pods