考证匠/CKAD/第 2 章
第 2 章

配置

⏱️ 150 分钟📚 Application Environment, Configuration and Security难度: ⭐⭐
📝 15 题练习
备考助手

配置

📋 本章概览

主题考试权重重要程度
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——前者存公开配置,后者存敏感数据;引用时用 configMapKeyRef vs secretKeyRef

配置注入方式对比

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       │
└─────────────────────┘       └─────────────────────┘

图解说明:

  1. 这张图展示了 ConfigMap 数据注入到 Pod 的两条路径——左边是环境变量方式,右边是 Volume 文件挂载方式
  2. 先看顶部的 ConfigMap 数据结构,再分别看两种注入方式在 Pod YAML 中的写法和最终效果
  3. 环境变量方式在容器启动时注入,之后不会自动更新;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 模板再手动添加 envvolumeMounts,是考试中最快的做法。另外要特别注意 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 编码)vs stringData(写明文,自动编码)

Secret 类型

Kubernetes 内置了几种 Secret 类型,每种都有特定用途。考试中 90% 的题用的是 Opaque(通用类型),但你需要知道其他类型的存在和用法。

类型用途一句话记忆
Opaque通用 Secret(默认)"万能型,密码/Token/任意数据都用它"
kubernetes.io/service-account-tokenServiceAccount Token"Pod 的工牌自动生成的门禁码"
kubernetes.io/dockerconfigjsonDocker Registry 认证"拉私有镜像时的登录凭证"
kubernetes.io/tlsTLS 证书"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 yamljsonpath 配合 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 的考法类似,但要注意 secretKeyRefconfigMapKeyRef 不能混用。第二类是"查看/解码 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描述一句话记忆
11 个 CPU 核心"整颗 CPU 都归你"
500m0.5 CPU = 500 millicores"半个核,一般的 Web 应用够用"
100m0.1 CPU"十分之一核,轻量级 sidecar 常用"
内存描述一句话记忆
1Gi1 GiB = 1024 MiB"大写 i 才是二进制单位"
256Mi256 MiB"普通 nginx 容器的典型配置"
1G1 GB = 1000 MB"十进制单位,少用,容易混淆"

记忆口诀:CPU 用小 m(milli),内存用大 Mi(Mebi)。内存永远带大写 i。

资源行为

┌────────────────────────────────────────────────────┐
│                  资源使用行为                        │
├────────────────────────────────────────────────────┤
│                                                    │
│  requests(请求)                                   │
│  ├─ 调度器用于选择节点                              │
│  ├─ 保证容器获得这些资源                            │
│  └─ 不设置则默认无限制                              │
│                                                    │
│  limits(限制)                                     │
│  ├─ 容器最多使用的资源                              │
│  ├─ CPU 超限 → 被限流(throttled)                 │
│  └─ 内存超限 → OOMKilled                           │
│                                                    │
└────────────────────────────────────────────────────┘

图解说明:

  1. 这张图对比了 requests 和 limits 两者的角色——requests 影响调度决策(Pod 被分配到哪个节点),limits 影响运行时行为(超额会怎样)
  2. 先看 requests 部分理解"调度保证",再看 limits 部分理解"超额惩罚"
  3. 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 运行"安全底线:不许用超级管理员"
fsGroupVolume 文件组 ID"共享文件夹的归属组"
readOnlyRootFilesystem只读根文件系统"不许往系统目录写东西"
allowPrivilegeEscalation允许特权升级"能不能从普通用户变成 root"
capabilitiesLinux 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 挂载
           适合:配置文件、证书、动态更新

图解说明:

  1. 这个决策树帮你在考试中快速判断应该用哪种注入方式——关键看题目中应用是读环境变量还是读文件
  2. 先看题目描述中有没有"配置文件""证书"等关键词,有的话选 Volume 挂载
  3. 如果题目要求"配置更新后容器能自动获取新值",必须用 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=productionLOG_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

<details> <summary>查看答案</summary>
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. 以上都可以
<details> <summary>查看答案</summary>

答案:B 或 C

解析

  • ConfigMap 的 data 字段值必须是字符串
  • 数字需要用引号括起来
  • port: 3306 在某些情况下可能导致问题
data:
  port: "3306"    # ✅ 正确
  port: '3306'    # ✅ 正确
</details>

题目 4

哪种方式可以让容器读取 Secret 更新后的新值(无需重启)?

  • A. 环境变量
  • B. Volume 挂载
  • C. envFrom
  • D. 以上都可以
<details> <summary>查看答案</summary>

答案:B

解析

  • 环境变量:容器启动时注入,更新后不会自动刷新
  • Volume 挂载:Kubernetes 会自动更新挂载的文件(有延迟)
  • 如果需要实时感知配置变化,应用需要监听文件变化
  • 这个区别在考试中属于高频考点,常以选择题形式出现
</details>

题目 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
<details> <summary>查看答案</summary>
# 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 会启动失败。

</details>

本章小结

资源用途创建命令
ConfigMap非敏感配置kubectl create configmap
Secret敏感数据kubectl create secret generic
ServiceAccountPod 身份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 → 禁用自动挂载
📝 章节练习 (15 题)
开始练习