配置
📋 本章概览
| 主题 | 考试权重 | 重要程度 |
|---|---|---|
| ConfigMaps | ★★★★★ | 必考 |
| Secrets | ★★★★★ | 必考 |
| 环境变量 | ★★★★★ | 高频考点 |
| 资源限制 | ★★★★☆ | 重要 |
| SecurityContext | ★★★★☆ | 重要 |
| ServiceAccount | ★★★★☆ | 重要 |
💡 考试占比:应用环境、配置和安全占 CKAD 考试的 25%
学习目标
完成本章后,你将能够:
- 熟练使用 ConfigMaps 管理配置
- 正确使用 Secrets 管理敏感数据
- 配置环境变量和卷挂载
- 设置资源请求和限制
- 配置 SecurityContext 和 ServiceAccount
📝 学习建议
用生活场景理解本章四大核心概念
在动手敲命令之前,先用几个生活场景把本章最重要的四个资源"装进脑子":
- ConfigMap — 像餐厅门口的菜单板。菜品名称、价格、今日特价都写在上面,所有人都能看到,随时可以更新,但你不会把后厨的秘方写在这上面。ConfigMap 就是存放"可以公开的配置信息"的地方。
- Secret — 像餐厅后厨的保险箱。配方、供应商合同、员工工资单都锁在里面,只有有权限的人才能打开。Secret 用来存放密码、Token、密钥这类"不能让所有人看到"的敏感数据。
- SecurityContext — 像大楼的安全等级系统。普通员工只能进一楼大厅,经理能刷卡进办公区,但谁都不能进服务器机房——除非有特殊审批。SecurityContext 就是告诉 Kubernetes "这个容器能做什么、不能做什么"。
- ServiceAccount — 像员工工牌。每个员工凭工牌出入公司、使用打印机、访问内部系统。Pod 也需要一个"身份"来跟 Kubernetes API 打交道,ServiceAccount 就是它的工牌。
这些比喻只帮助理解核心机制,真实系统在权限控制和加密细节上更复杂。考试里不会考比喻本身,会考具体的 YAML 配置和命令。
为什么第二章学配置?
你可能会想:第一章刚学会创建 Pod,为什么不直接学 Deployment、Service 这些"更大"的概念,而要先学配置管理?
原因很实际——在真实项目里,几乎每个 Pod 都需要读取配置(数据库地址、API 密钥、日志级别等)。如果你把这些配置直接写死在镜像里,每次改个端口号就得重新打包镜像、重新部署,效率极低。Kubernetes 的设计哲学是配置与代码分离:应用代码打成镜像,配置通过 ConfigMap 和 Secret 在运行时注入。掌握了这一章,后面学 Deployment、StatefulSet 时才能写出"生产级"的 YAML。
而且从考试角度看,Application Environment 占 25%,是 CKAD 五大领域中权重最高的。ConfigMap、Secret 的创建和使用几乎每次考试必出,是性价比最高的得分点。
本章知识地图
第 2 章:配置管理
│
├── ConfigMap:非敏感配置
│ ├── 创建方式(命令式 vs 声明式)
│ └── 注入方式(环境变量 vs Volume 挂载)
│
├── Secret:敏感数据
│ ├── 类型(Opaque / TLS / DockerRegistry)
│ ├── Base64 编码/解码
│ └── 注入方式(与 ConfigMap 类似,但有安全差异)
│
├── 资源限制:CPU & 内存
│ ├── requests(调度保证)
│ └── limits(使用上限)
│
├── SecurityContext:安全策略
│ ├── Pod 级别 vs 容器级别
│ └── 常用配置(runAsUser / readOnly / capabilities)
│
└── ServiceAccount:Pod 身份
├── 创建与绑定
└── Token 管理(1.24+ 变化)
ConfigMap:如何把配置和代码分开?
在没有 ConfigMap 之前,开发者通常把配置直接写在代码里或者 Dockerfile 的 ENV 指令中。这样做的问题是:换个环境(开发 → 测试 → 生产),就得改代码、重新打镜像。ConfigMap 解决的核心问题就是把配置从镜像中剥离出来,让同一个镜像在不同环境中通过不同的 ConfigMap 获得不同的行为。
术语:ConfigMap
- 一句话解释:Kubernetes 中专门存储非敏感配置数据的资源对象,以键值对的形式保存
- 形象比喻:餐厅门口的菜单板——公开的、可随时更新的、所有人都能看到的配置信息
- 考试怎么考:给你一组配置要求,让你创建 ConfigMap 并注入到 Pod 中(命令式创建 + YAML 编写)
- 最易混淆:ConfigMap vs Secret——前者存公开配置,后者存敏感数据;引用时用
configMapKeyRefvssecretKeyRef
配置注入方式对比
Kubernetes 提供了两种主要方式把 ConfigMap 的数据送进容器:环境变量和 Volume 挂载。选哪种取决于你的应用怎么读取配置——如果应用通过 os.Getenv("DB_HOST") 这样的方式读环境变量,就用环境变量注入;如果应用读配置文件(比如 application.properties),就用 Volume 挂载。
┌─────────────────────────────────────────────────────────┐
│ ConfigMap │
│ (app-config) │
│ │
│ data: │
│ DB_HOST: mysql │
│ DB_PORT: "3306" │
│ config.properties: | │
│ server.port=8080 │
│ server.name=myapp │
│ debug=true │
└────────────────────────┬────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ 环境变量方式 │ │ Volume 挂载 │
│ │ │ │
│ env: │ │ volumeMounts: │
│ - name: DB_HOST │ │ - name: config │
│ valueFrom: │ │ mountPath: /etc │
│ configMapKeyRef│ │ │
│ │ │ → 文件: /etc/DB_HOST│
│ → 值: mysql │ │ 内容: mysql │
└─────────────────────┘ └─────────────────────┘
图解说明:
- 这张图展示了 ConfigMap 数据注入到 Pod 的两条路径——左边是环境变量方式,右边是 Volume 文件挂载方式
- 先看顶部的 ConfigMap 数据结构,再分别看两种注入方式在 Pod YAML 中的写法和最终效果
- 环境变量方式在容器启动时注入,之后不会自动更新;Volume 挂载方式会在 ConfigMap 更新后自动刷新文件内容(有一定延迟),这是选型时的关键区别
创建 ConfigMap
创建 ConfigMap 有两种风格:命令式(直接用 kubectl create 命令)和声明式(写 YAML 文件再 apply)。考试中推荐用命令式,因为速度快、不容易出缩进错误。
命令式(考试推荐):
# 从字面值创建 ⭐ 最常用
kubectl create configmap app-config \
--from-literal=DB_HOST=mysql \
--from-literal=DB_PORT=3306
# 从文件创建
kubectl create configmap app-config --from-file=config.properties
# 从目录创建(多个文件)
kubectl create configmap app-config --from-file=config-dir/
# 指定 key 名
kubectl create configmap app-config --from-file=my-config=config.properties
# 生成 YAML
kubectl create configmap app-config --from-literal=DB_HOST=mysql --dry-run=client -o yaml
声明式:
当你需要在 ConfigMap 中存放多行配置文件(比如 nginx.conf、application.properties)时,声明式 YAML 更合适。注意 data 字段下的值必须是字符串,所以数字要用引号括起来。
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# 简单键值对
DB_HOST: mysql
DB_PORT: "3306" # 数字需要引号
LOG_LEVEL: INFO
# 多行配置文件
config.properties: |
server.port=8080
server.name=myapp
debug=true
# JSON 配置
config.json: |
{
"database": {
"host": "mysql",
"port": 3306
}
}
使用 ConfigMap
ConfigMap 创建好之后,需要在 Pod 的 YAML 中引用它。Kubernetes 提供了五种注入方式,从简单到灵活依次递进。日常工作中最常用的是方式 1(单个环境变量)和方式 3(Volume 挂载整个目录),考试中五种都可能出。
方式 1:环境变量(单个 key)
适合只需要引用 ConfigMap 中某几个值的场景,比如只需要数据库主机名和端口号。
apiVersion: v1
kind: Pod
metadata:
name: config-env-pod
spec:
containers:
- name: app
image: nginx
env:
- name: DATABASE_HOST # Pod 内的环境变量名
valueFrom:
configMapKeyRef:
name: app-config # ConfigMap 名称
key: DB_HOST # ConfigMap 中的 key
- name: DATABASE_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
方式 2:环境变量(全部 key)
envFrom — 把 ConfigMap 或 Secret 中的所有键值对一次性注入为环境变量。 好处是不用一个一个写
env,坏处是你无法控制变量名(key 名直接变成环境变量名)。
当 ConfigMap 中有很多配置项,而你的应用恰好都需要时,用 envFrom 一次全部注入,省去逐个列举的麻烦。
apiVersion: v1
kind: Pod
metadata:
name: config-envfrom-pod
spec:
containers:
- name: app
image: nginx
envFrom:
- configMapRef:
name: app-config # 所有 key 都成为环境变量
- prefix: APP_ # 可选:添加前缀
configMapRef:
name: app-config
方式 3:Volume 挂载(全部 key)
当应用通过读取配置文件工作(而不是读环境变量)时,需要把 ConfigMap 以文件的形式挂载进容器。每个 key 会变成一个文件,文件内容就是对应的 value。
apiVersion: v1
kind: Pod
metadata:
name: config-volume-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/config # 挂载目录
volumes:
- name: config-volume
configMap:
name: app-config
# 结果:/etc/config/DB_HOST, /etc/config/DB_PORT, ...
方式 4:Volume 挂载(指定 key)
有时候 ConfigMap 里存了好几个文件,但你只想挂载其中一个。用 items 字段可以精确选择要挂载的 key,还可以给文件重命名。
apiVersion: v1
kind: Pod
metadata:
name: config-volume-items-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: config.properties # 只挂载这个 key
path: app.properties # 重命名为 app.properties
# 结果:/etc/config/app.properties
方式 5:subPath 挂载单个文件(不覆盖目录)
术语:subPath
- 一句话解释:Volume 挂载时的一个选项,让你把 ConfigMap 中的单个 key 挂载为目标路径下的单个文件,而不是替换整个目录
- 形象比喻:往书架上插一本书,而不是把整个书架清空再放你的书
- 考试怎么考:题目要求"将配置文件挂载到 /etc/nginx/nginx.conf,不能影响 /etc/nginx 目录下的其他文件"
- 最易混淆:普通 Volume 挂载会覆盖 mountPath 整个目录;subPath 只添加/替换单个文件
这是一个高频考点。默认的 Volume 挂载会把 mountPath 指定的目录完全替换掉(原有文件全部消失)。如果你只想往一个已有目录里"放入"一个配置文件而不影响其他文件,就必须用 subPath。
apiVersion: v1
kind: Pod
metadata:
name: config-subpath-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/nginx.conf # 挂载为单个文件
subPath: nginx.conf # ConfigMap 中的 key
volumes:
- name: config-volume
configMap:
name: nginx-config
⚠️ 考试视角:ConfigMap 相关的考题通常不会只让你"创建一个 ConfigMap"就结束——它一定会要求你把 ConfigMap 注入到 Pod 中并验证。所以你需要熟练掌握"创建 ConfigMap + 编写 Pod YAML 引用它"这个两步操作。命令式创建 ConfigMap,然后用 --dry-run=client -o yaml 生成 Pod 模板再手动添加 env 或 volumeMounts,是考试中最快的做法。另外要特别注意 configMapKeyRef(引用 ConfigMap)和 secretKeyRef(引用 Secret)不要写混。
Secret:敏感数据怎么安全地传给应用?
如果说 ConfigMap 是"菜单板",那 Secret 就是"保险箱"。密码、API Token、TLS 证书这类数据,放在 ConfigMap 里虽然技术上可行,但 kubectl get configmap -o yaml 一看就是明文,任何有权限的人都能读到。Secret 在存储层做了 base64 编码(注意:这不是加密,只是编码),并且 Kubernetes 可以配置 etcd 加密来进一步保护 Secret 数据。
术语:Secret
- 一句话解释:Kubernetes 中专门存储敏感数据的资源对象,数据以 base64 编码存储
- 形象比喻:后厨的保险箱——只有有权限的人才能打开,里面放着配方和合同
- 考试怎么考:创建 Secret、注入 Pod(环境变量或 Volume)、用
base64 -d解码查看明文 - 最易混淆:
data(需要手动 base64 编码)vsstringData(写明文,自动编码)
Secret 类型
Kubernetes 内置了几种 Secret 类型,每种都有特定用途。考试中 90% 的题用的是 Opaque(通用类型),但你需要知道其他类型的存在和用法。
| 类型 | 用途 | 一句话记忆 |
|---|---|---|
| Opaque | 通用 Secret(默认) | "万能型,密码/Token/任意数据都用它" |
| kubernetes.io/service-account-token | ServiceAccount Token | "Pod 的工牌自动生成的门禁码" |
| kubernetes.io/dockerconfigjson | Docker Registry 认证 | "拉私有镜像时的登录凭证" |
| kubernetes.io/tls | TLS 证书 | "HTTPS 要用的证书和私钥" |
| kubernetes.io/basic-auth | 基本认证 | "用户名 + 密码的组合" |
创建 Secret
命令式(考试推荐):
和 ConfigMap 一样,考试中优先用命令式创建,速度快且不需要手动做 base64 编码。
# 通用 Secret ⭐
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password=secret123
# 从文件创建
kubectl create secret generic ssh-secret \
--from-file=ssh-privatekey=/path/to/id_rsa
# TLS Secret
kubectl create secret tls tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.key
# Docker Registry Secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=password \
--docker-email=user@example.com
# 生成 YAML
kubectl create secret generic db-secret --from-literal=password=secret123 --dry-run=client -o yaml
声明式(需要 base64 编码):
如果你必须手写 Secret 的 YAML(比如考试要求声明式创建),有两种写法:data(需要 base64 编码)和 stringData(写明文)。
术语:base64
- 一句话解释:一种把二进制数据转换为 ASCII 文本的编码方式,不是加密,任何人都能解码
- 形象比喻:把中文翻译成拼音——换了种写法但意思没变,谁都能读懂
- 考试怎么考:
echo -n 'admin' | base64编码,echo 'YWRtaW4=' | base64 -d解码 - 最易混淆:base64 编码 ≠ 加密。Secret 的安全性不靠 base64,而是靠 RBAC 权限控制和 etcd 加密
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
# base64 编码: echo -n 'admin' | base64
username: YWRtaW4=
password: c2VjcmV0MTIz
# 或使用 stringData(明文,自动编码)
---
apiVersion: v1
kind: Secret
metadata:
name: db-secret-plain
type: Opaque
stringData: # ⭐ 明文,无需编码
username: admin
password: secret123
Base64 编码/解码
这是一个考试中经常用到的操作——题目可能给你一个 Secret 的 YAML 让你找出密码明文,或者让你手写带 base64 编码的 Secret。
# 编码
echo -n 'admin' | base64
# YWRtaW4=
# 解码
echo 'YWRtaW4=' | base64 -d
# admin
# 注意:-n 避免换行符
# 错误:echo 'admin' | base64 → 包含换行符
# 正确:echo -n 'admin' | base64
这里有个经典坑:
echo默认会在末尾加一个换行符\n,导致 base64 编码结果不同。echo -n的-n参数表示不加换行符。考试中如果编码结果不对,八成就是忘了-n。
使用 Secret
Secret 的注入方式和 ConfigMap 几乎一样(环境变量、envFrom、Volume 挂载),区别只在 YAML 中的关键字不同。
方式 1:环境变量
将 Secret 中的特定字段注入为容器的环境变量。注意引用 Secret 用的是 secretKeyRef,不是 configMapKeyRef。
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: app
image: nginx
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
方式 2:envFrom 全部注入
把 Secret 中所有键值对一次性注入为环境变量。适合 Secret 中的 key 名称本身就是合法的环境变量名的场景。
apiVersion: v1
kind: Pod
metadata:
name: secret-envfrom-pod
spec:
containers:
- name: app
image: nginx
envFrom:
- secretRef:
name: db-secret
方式 3:Volume 挂载
将 Secret 以文件形式挂载进容器。每个 key 变成一个文件,文件内容是解码后的明文(不是 base64)。推荐加上 readOnly: true 防止容器意外修改。
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true # ⭐ 推荐只读
volumes:
- name: secret-volume
secret:
secretName: db-secret
defaultMode: 0400 # 设置文件权限
查看 Secret
在日常排障和考试中,你经常需要查看 Secret 的内容。kubectl get secret 默认不显示值,需要用 -o yaml 或 jsonpath 配合 base64 -d 解码。
# 查看 Secret(值被隐藏)
kubectl get secret db-secret
# 查看 Secret YAML(base64 编码)
kubectl get secret db-secret -o yaml
# 解码 Secret 值 ⭐
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
# 另一种方式
kubectl get secret db-secret -o go-template='{{.data.password | base64decode}}'
⚠️ 考试视角:Secret 相关的考题有两大类型。第一类是"创建 Secret + 注入 Pod",和 ConfigMap 的考法类似,但要注意 secretKeyRef 和 configMapKeyRef 不能混用。第二类是"查看/解码 Secret 内容",这时候 jsonpath + base64 -d 的组合是最快的做法。另外,声明式创建 Secret 时,data 字段必须用 base64 编码值,stringData 字段可以写明文——考试中如果时间紧张,优先用 stringData 或命令式创建来避免编码错误。
资源限制:给容器分配多少 CPU 和内存?
在一个 Kubernetes 集群里,多个 Pod 共享节点的 CPU 和内存资源。如果不做限制,一个"贪吃"的容器可能吃掉整个节点的资源,导致其他容器饿死。资源限制就是解决这个问题的——你可以给每个容器设定"至少给我多少"(requests)和"最多用多少"(limits)。
请求和限制
requests 是调度器的承诺——Kubernetes 保证你的容器能拿到这么多资源。limits 是硬性天花板——容器用量超过 limits 就会被处罚。CPU 超限会被限流(变慢但不会被杀),内存超限会被 OOMKilled(直接杀掉容器)。
apiVersion: v1
kind: Pod
metadata:
name: resource-pod
spec:
containers:
- name: app
image: nginx
resources:
requests: # 保证获得的资源
memory: "256Mi" # 256 MiB
cpu: "500m" # 0.5 CPU
limits: # 最多使用的资源
memory: "512Mi" # 512 MiB
cpu: "1" # 1 CPU
资源单位
资源单位是考试中的经典易错点。CPU 用小写 m(millicores,千分之一核),内存用大写 Mi(Mebibytes)。如果写反了——比如内存写成小写 m(milli,即 0.001 字节)——容器会因为几乎没有内存分配而立刻被杀。
| CPU | 描述 | 一句话记忆 |
|---|---|---|
| 1 | 1 个 CPU 核心 | "整颗 CPU 都归你" |
| 500m | 0.5 CPU = 500 millicores | "半个核,一般的 Web 应用够用" |
| 100m | 0.1 CPU | "十分之一核,轻量级 sidecar 常用" |
| 内存 | 描述 | 一句话记忆 |
|---|---|---|
| 1Gi | 1 GiB = 1024 MiB | "大写 i 才是二进制单位" |
| 256Mi | 256 MiB | "普通 nginx 容器的典型配置" |
| 1G | 1 GB = 1000 MB | "十进制单位,少用,容易混淆" |
记忆口诀:CPU 用小 m(milli),内存用大 Mi(Mebi)。内存永远带大写 i。
资源行为
┌────────────────────────────────────────────────────┐
│ 资源使用行为 │
├────────────────────────────────────────────────────┤
│ │
│ requests(请求) │
│ ├─ 调度器用于选择节点 │
│ ├─ 保证容器获得这些资源 │
│ └─ 不设置则默认无限制 │
│ │
│ limits(限制) │
│ ├─ 容器最多使用的资源 │
│ ├─ CPU 超限 → 被限流(throttled) │
│ └─ 内存超限 → OOMKilled │
│ │
└────────────────────────────────────────────────────┘
图解说明:
- 这张图对比了 requests 和 limits 两者的角色——requests 影响调度决策(Pod 被分配到哪个节点),limits 影响运行时行为(超额会怎样)
- 先看 requests 部分理解"调度保证",再看 limits 部分理解"超额惩罚"
- CPU 超限和内存超限的后果不同(限流 vs 杀掉)——这个区别是考试高频考点,题目常问"容器内存使用超过 limits 会发生什么"
⚠️ 考试视角:资源限制的考题通常给你一个场景(比如"创建 Pod,CPU 请求 200m 限制 500m,内存请求 128Mi 限制 256Mi"),让你写 YAML。这类题本身不难,但要注意单位不要写错。还有一类题会问"为什么 Pod 处于 Pending 状态"——答案往往是节点资源不足以满足 Pod 的 requests。
SecurityContext:谁能做什么,权限怎么管?
默认情况下,容器以 root 用户运行,这在安全上是不推荐的——如果容器被攻破,攻击者就拥有了容器内的 root 权限。SecurityContext 让你精确控制容器的安全行为:以哪个用户运行、能不能写文件系统、能不能获取额外的 Linux 权限等。
术语:SecurityContext
- 一句话解释:Kubernetes 中用来定义 Pod 或容器安全策略的配置块,控制运行时的权限边界
- 形象比喻:大楼的安全等级系统——不同楼层需要不同级别的门禁卡,SecurityContext 就是给容器设定"能进哪些门"
- 考试怎么考:给你安全要求(如"不能以 root 运行""只读文件系统"),让你写对应的 YAML 配置
- 最易混淆:Pod 级别 vs 容器级别——Pod 级别的设置对所有容器生效,容器级别的设置会覆盖 Pod 级别
Pod 级别 SecurityContext
Pod 级别的 SecurityContext 作用于 Pod 内所有容器。适合你想给整个 Pod 设定统一安全策略的场景,比如"所有容器都以 UID 1000 运行"。
apiVersion: v1
kind: Pod
metadata:
name: security-pod
spec:
securityContext: # Pod 级别(影响所有容器)
runAsUser: 1000 # 以 UID 1000 运行
runAsGroup: 3000 # 以 GID 3000 运行
fsGroup: 2000 # Volume 的组 ID
containers:
- name: app
image: nginx
Container 级别 SecurityContext
容器级别的 SecurityContext 只作用于当前容器,且会覆盖 Pod 级别的同名设置。当不同容器需要不同安全策略时使用。
术语:capabilities(Linux Capabilities)
- 一句话解释:Linux 内核把 root 的"超级权限"拆分成了几十个小权限(capabilities),容器可以按需添加或移除
- 形象比喻:不给你整把万能钥匙,而是按需给你特定房间的钥匙
- 考试怎么考:题目要求"移除所有 capabilities,只保留 NET_BIND_SERVICE"
- 最易混淆:
drop: ALL+add: [NET_BIND_SERVICE]是最佳实践写法(先全部移除,再按需添加)
apiVersion: v1
kind: Pod
metadata:
name: security-container-pod
spec:
containers:
- name: app
image: nginx
securityContext: # 容器级别(覆盖 Pod 级别)
runAsUser: 1000
runAsNonRoot: true # 禁止以 root 运行
readOnlyRootFilesystem: true # 只读根文件系统
allowPrivilegeEscalation: false # 禁止特权升级
capabilities:
drop:
- ALL # 移除所有 Linux capabilities
add:
- NET_BIND_SERVICE # 只添加需要的
常用配置
| 配置 | 描述 | 一句话记忆 |
|---|---|---|
| runAsUser | 运行用户 ID | "容器以谁的身份干活" |
| runAsGroup | 运行组 ID | "容器属于哪个部门" |
| runAsNonRoot | 禁止以 root 运行 | "安全底线:不许用超级管理员" |
| fsGroup | Volume 文件组 ID | "共享文件夹的归属组" |
| readOnlyRootFilesystem | 只读根文件系统 | "不许往系统目录写东西" |
| allowPrivilegeEscalation | 允许特权升级 | "能不能从普通用户变成 root" |
| capabilities | Linux capabilities | "精细化权限:只给需要的钥匙" |
⚠️ 考试视角:SecurityContext 的考题一般会给你一组安全需求,让你写 YAML。比如"以非 root 用户运行 + 只读文件系统 + 移除所有 capabilities"。注意两点:第一,readOnlyRootFilesystem: true 会导致应用无法写入任何目录,所以需要额外挂载 emptyDir 卷给 /tmp 等需要写入的目录;第二,Pod 级别和容器级别的 SecurityContext 可以同时存在,容器级别优先。
ServiceAccount:Pod 用什么身份跟集群打交道?
当你在容器里执行 kubectl get pods(或者应用代码调用 Kubernetes API)时,Kubernetes 怎么知道"你是谁,你有什么权限"?答案就是 ServiceAccount。每个 Pod 都会自动关联一个 ServiceAccount(默认是 default),Kubernetes 通过它来判断这个 Pod 能做什么。
术语:ServiceAccount
- 一句话解释:Kubernetes 中用来标识 Pod 身份的资源对象,Pod 通过它获得访问 Kubernetes API 的凭证
- 形象比喻:员工工牌——凭工牌出入公司、使用内部系统、申请资源。不同职级的工牌权限不同
- 考试怎么考:创建 ServiceAccount,在 Pod 中指定使用,有时还需要配合 RBAC 授权
- 最易混淆:ServiceAccount 本身只是"身份",不包含"权限"。权限是通过 Role/ClusterRole + RoleBinding 赋予的
创建和使用 ServiceAccount
# 创建 ServiceAccount
kubectl create serviceaccount my-sa
# 查看 ServiceAccount
kubectl get serviceaccount
kubectl get sa
# 查看详情
kubectl describe sa my-sa
Pod 中使用 ServiceAccount
在 Pod 的 spec 中通过 serviceAccountName 指定要使用哪个 ServiceAccount。如果不指定,Pod 会使用当前命名空间下的 default ServiceAccount。
apiVersion: v1
kind: Pod
metadata:
name: sa-pod
spec:
serviceAccountName: my-sa # 指定 ServiceAccount
containers:
- name: app
image: nginx
禁用自动挂载 Token
默认情况下,Kubernetes 会自动把 ServiceAccount 的 Token 挂载到容器的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录。如果你的应用不需要访问 Kubernetes API,可以禁用这个行为来减少安全风险。
apiVersion: v1
kind: Pod
metadata:
name: no-token-pod
spec:
automountServiceAccountToken: false # 不自动挂载 Token
containers:
- name: app
image: nginx
创建 ServiceAccount Token
从 Kubernetes 1.24 开始,创建 ServiceAccount 时不再自动生成长期 Token。你需要手动创建临时 Token 或显式创建 Secret 来获取长期 Token。
# Kubernetes 1.24+ 创建短期 Token
kubectl create token my-sa
# 创建长期 Token(Secret)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: my-sa-token
annotations:
kubernetes.io/service-account.name: my-sa
type: kubernetes.io/service-account-token
EOF
⚠️ 考试视角:ServiceAccount 的考题一般比较直接——创建 SA,然后在 Pod 中使用。但有时会结合 RBAC 一起考(创建 SA + 创建 Role + 创建 RoleBinding + Pod 使用 SA)。记住 serviceAccountName 这个字段写在 spec 级别(和 containers 同级),不要写错位置。另外,automountServiceAccountToken: false 这个选项也可能出现在安全相关的题目中。
🎯 考试技巧
快速命令速查
考试中时间宝贵,以下命令按使用频率排列,建议背熟前 6 条。
# ConfigMap 操作
k create configmap app-config --from-literal=key=value
k create configmap app-config --from-file=config.properties
k create configmap app-config --from-file=configs/
k get configmap app-config -o yaml
# Secret 操作
k create secret generic db-secret --from-literal=password=secret
k create secret tls tls-secret --cert=tls.crt --key=tls.key
k create secret docker-registry regcred --docker-server=... --docker-username=...
# 解码 Secret
k get secret db-secret -o jsonpath='{.data.password}' | base64 -d
# ServiceAccount 操作
k create serviceaccount my-sa
k create token my-sa
ConfigMap vs Secret 选择
遇到题目不确定用哪个时,就问自己一个问题:"这个数据如果被别人看到,有没有安全风险?"
需要存储什么数据?
│
├─→ 普通配置(URL、端口、环境) → ConfigMap
│
└─→ 敏感数据(密码、Token、密钥) → Secret
环境变量 vs Volume 挂载
配置如何使用?
│
├─→ 应用通过环境变量读取 → env/envFrom
│ 适合:简单配置、启动时读取
│
└─→ 应用通过文件读取 → Volume 挂载
适合:配置文件、证书、动态更新
图解说明:
- 这个决策树帮你在考试中快速判断应该用哪种注入方式——关键看题目中应用是读环境变量还是读文件
- 先看题目描述中有没有"配置文件""证书"等关键词,有的话选 Volume 挂载
- 如果题目要求"配置更新后容器能自动获取新值",必须用 Volume 挂载(环境变量在容器启动后不会自动更新)
⚠️ 常见错误与误区
错误 1:Secret 值未 Base64 编码
这是最常见的 Secret 创建错误。data 字段中的值必须是 base64 编码的,如果你直接写明文,Kubernetes 不会报错但解码出来会是乱码。
# ❌ 错误:data 中的值必须是 base64 编码
apiVersion: v1
kind: Secret
metadata:
name: db-secret
data:
password: mypassword # 明文!会报错或产生乱码
# ✅ 正确方式 1:使用 data + base64
data:
password: bXlwYXNzd29yZA== # echo -n 'mypassword' | base64
# ✅ 正确方式 2:使用 stringData(自动编码)
stringData:
password: mypassword # 明文,会自动编码
错误 2:ConfigMap/Secret key 名包含特殊字符
当 ConfigMap 的 key 名包含点号(.)或其他特殊字符时,它不能直接用作环境变量名(环境变量名只允许字母、数字和下划线)。这时要么手动指定环境变量名,要么改用 Volume 挂载。
# ❌ 错误:作为环境变量时 key 名不能包含 .
data:
config.properties: "value" # 不能直接用作环境变量
# ✅ 解决:使用 Volume 挂载,或重命名 key
env:
- name: CONFIG_PROPERTIES # 手动指定变量名
valueFrom:
configMapKeyRef:
name: app-config
key: config.properties
错误 3:Volume 挂载覆盖整个目录
这个错误在实际工作中和考试中都非常常见。默认情况下,ConfigMap 的 Volume 挂载会替换掉目标目录中的所有原有文件。比如你把配置挂载到 /etc/nginx,nginx 原来自带的配置文件全都消失了。
# ❌ 问题:挂载到 /etc/nginx 会覆盖原有文件
volumeMounts:
- name: config
mountPath: /etc/nginx # 整个目录被替换!
# ✅ 解决:使用 subPath 挂载单个文件
volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf # 只挂载这个文件
错误 4:资源单位写错
CPU 和内存的单位字母大小写不同,写反了后果很严重。
# ❌ 错误:单位错误
resources:
requests:
memory: "256" # 没有单位,默认字节
cpu: "0.5" # 字符串,应该用数字或 m
# ✅ 正确
resources:
requests:
memory: "256Mi" # 明确单位
cpu: 500m # 或 "0.5"
错误 5:环境变量引用错误
ConfigMap 和 Secret 的引用关键字只有一个单词的区别,但写错了容器就拿不到数据。
# ❌ 错误:configMapKeyRef vs secretKeyRef 混淆
env:
- name: PASSWORD
valueFrom:
configMapKeyRef: # 错误!Secret 应该用 secretKeyRef
name: db-secret
key: password
# ✅ 正确
env:
- name: PASSWORD
valueFrom:
secretKeyRef: # Secret 用 secretKeyRef
name: db-secret
key: password
📝 考试场景题
场景 1:创建 ConfigMap 并使用
问题:创建 ConfigMap 包含 APP_ENV=production 和 LOG_LEVEL=INFO,然后在 Pod 中作为环境变量使用。
解题思路:先用命令式创建 ConfigMap(最快),再写 Pod YAML 用 envFrom 引用(因为要注入全部 key)。
# 1. 创建 ConfigMap
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=INFO
# 2. 验证
kubectl get configmap app-config -o yaml
# 3. 创建 Pod 使用 ConfigMap
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: nginx
envFrom:
- configMapRef:
name: app-config
# 4. 验证环境变量
kubectl exec app-pod -- env | grep -E "APP_ENV|LOG_LEVEL"
场景 2:创建 Secret 并挂载为文件
问题:创建包含数据库凭据的 Secret,并挂载到 Pod 的 /etc/secrets 目录。
解题思路:题目要求"挂载为文件",所以用 Volume 挂载方式(不是环境变量)。加上 readOnly: true 是好习惯。
# 1. 创建 Secret
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=supersecret
# 2. 创建 Pod 挂载 Secret
apiVersion: v1
kind: Pod
metadata:
name: db-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
# 3. 验证
kubectl exec db-pod -- ls /etc/secrets
kubectl exec db-pod -- cat /etc/secrets/username
场景 3:配置资源限制
问题:创建 Pod,设置 CPU 请求 100m、限制 200m,内存请求 128Mi、限制 256Mi。
解题思路:先用 kubectl run --dry-run=client -o yaml 生成 Pod 模板,再手动添加 resources 块。注意单位大小写。
apiVersion: v1
kind: Pod
metadata:
name: limited-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
# 验证
kubectl describe pod limited-pod | grep -A10 "Limits"
场景 4:配置 SecurityContext
问题:创建 Pod,要求以用户 ID 1000 运行,并且使用只读根文件系统。
解题思路:只读根文件系统意味着容器不能往任何目录写数据。但 nginx 需要写 /tmp、/var/cache/nginx、/var/run 这些目录,所以必须额外挂载 emptyDir 卷给这些路径。这是考试中容易遗漏的点。
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
containers:
- name: app
image: nginx
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
场景 5:使用 ServiceAccount
问题:创建 ServiceAccount app-sa,并在 Pod 中使用。
解题思路:两步操作——先 kubectl create sa,再在 Pod YAML 的 spec.serviceAccountName 中引用。
# 1. 创建 ServiceAccount
kubectl create serviceaccount app-sa
# 2. 验证
kubectl get sa app-sa
# 3. 创建 Pod 使用 ServiceAccount
apiVersion: v1
kind: Pod
metadata:
name: sa-pod
spec:
serviceAccountName: app-sa
containers:
- name: app
image: nginx
# 4. 验证
kubectl describe pod sa-pod | grep "Service Account"
练习题
题目 1
创建一个 ConfigMap 名为 web-config,包含 NGINX_PORT=8080:
kubectl create configmap web-config --from-literal=NGINX_PORT=8080
验证:
kubectl get configmap web-config -o yaml
</details>
题目 2
如何查看 Secret 中密码的明文值?
<details> <summary>查看答案</summary># 方法 1:jsonpath + base64
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
# 方法 2:go-template
kubectl get secret db-secret -o go-template='{{.data.password | base64decode}}'
</details>
题目 3
ConfigMap 中的数字值应该如何写?
- A.
port: 3306 - B.
port: "3306" - C.
port: '3306' - D. 以上都可以
答案:B 或 C
解析:
- ConfigMap 的
data字段值必须是字符串 - 数字需要用引号括起来
port: 3306在某些情况下可能导致问题
data:
port: "3306" # ✅ 正确
port: '3306' # ✅ 正确
</details>
题目 4
哪种方式可以让容器读取 Secret 更新后的新值(无需重启)?
- A. 环境变量
- B. Volume 挂载
- C. envFrom
- D. 以上都可以
答案:B
解析:
- 环境变量:容器启动时注入,更新后不会自动刷新
- Volume 挂载:Kubernetes 会自动更新挂载的文件(有延迟)
- 如果需要实时感知配置变化,应用需要监听文件变化
- 这个区别在考试中属于高频考点,常以选择题形式出现
题目 5(实操题)
创建一个 Pod,同时使用 ConfigMap 作为环境变量和 Secret 作为挂载卷。
<details> <summary>查看答案</summary># 1. 创建 ConfigMap
kubectl create configmap app-config --from-literal=APP_ENV=production
# 2. 创建 Secret
kubectl create secret generic app-secret --from-literal=API_KEY=mysecretkey
# 3. 创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: mixed-config-pod
spec:
containers:
- name: app
image: nginx
envFrom:
- configMapRef:
name: app-config
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: app-secret
# 4. 验证
kubectl exec mixed-config-pod -- env | grep APP_ENV
kubectl exec mixed-config-pod -- cat /etc/secrets/API_KEY
</details>
题目 6(综合实操题)
创建一个安全的 Pod,满足以下所有要求:
- 使用 ServiceAccount
secure-sa - 以 UID 1000 运行,禁止 root
- 只读根文件系统
- 从 ConfigMap
app-settings读取LOG_LEVEL=debug作为环境变量 - 从 Secret
app-creds读取API_TOKEN=abc123并挂载到/etc/creds
# 1. 创建资源
kubectl create sa secure-sa
kubectl create configmap app-settings --from-literal=LOG_LEVEL=debug
kubectl create secret generic app-creds --from-literal=API_TOKEN=abc123
# 2. 创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
serviceAccountName: secure-sa
securityContext:
runAsUser: 1000
runAsNonRoot: true
containers:
- name: app
image: nginx
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
envFrom:
- configMapRef:
name: app-settings
volumeMounts:
- name: creds
mountPath: /etc/creds
readOnly: true
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: creds
secret:
secretName: app-creds
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
# 3. 验证
kubectl exec secure-app -- id # 确认 UID 1000
kubectl exec secure-app -- env | grep LOG_LEVEL # 确认环境变量
kubectl exec secure-app -- cat /etc/creds/API_TOKEN # 确认 Secret 挂载
kubectl exec secure-app -- touch /test 2>&1 # 确认只读(应该报错)
解题要点:这道题综合了本章所有知识点。注意 readOnlyRootFilesystem: true 必须配合 emptyDir 卷来给 nginx 提供可写目录,否则 Pod 会启动失败。
本章小结
| 资源 | 用途 | 创建命令 |
|---|---|---|
| ConfigMap | 非敏感配置 | kubectl create configmap |
| Secret | 敏感数据 | kubectl create secret generic |
| ServiceAccount | Pod 身份 | kubectl create serviceaccount |
| 注入方式 | 特点 | 适用场景 |
|---|---|---|
| 环境变量 | 启动时注入,不自动更新 | 简单配置 |
| Volume 挂载 | 文件形式,可自动更新 | 配置文件、证书 |
| envFrom | 注入全部 key | 批量环境变量 |
考试要点速记
ConfigMap 创建:kubectl create configmap NAME --from-literal=KEY=VALUE
Secret 创建:kubectl create secret generic NAME --from-literal=KEY=VALUE
Secret 解码:kubectl get secret NAME -o jsonpath='{.data.KEY}' | base64 -d
环境变量引用:
configMapKeyRef → ConfigMap
secretKeyRef → Secret
Volume 挂载:
configMap.name → ConfigMap
secret.secretName → Secret
资源单位:
CPU: 1 = 1000m
内存: 1Gi = 1024Mi
SecurityContext 速记:
runAsUser: 1000 → 以非 root 用户运行
runAsNonRoot: true → 强制非 root
readOnlyRootFilesystem → 只读(需配合 emptyDir)
capabilities.drop: [ALL] → 最小权限原则
ServiceAccount:
spec.serviceAccountName → 指定 SA(和 containers 同级)
automountServiceAccountToken: false → 禁用自动挂载