4. Kubernetes进阶:存储卷与持久化、Helm包管理、RBAC与安全、Pod安全策略
好,咱们继续往下走。前面几章我们把Kubernetes的基础概念和核心工作负载都过了一遍。说实话,到了生产环境,你会发现光会跑Pod是远远不够的。这一章我挑了几个真正让Kubernetes变得「可用」的关键点:存储怎么持久化、应用怎么打包部署、权限怎么管、安全怎么防。嗯,每一个都是我在项目里踩过坑的地方。
4.1 存储卷与持久化:让数据不随Pod消失
先问个问题:Pod挂了,数据还在吗?默认情况下,Pod里的容器一重启,所有临时数据就没了。这显然不行,对吧?
Kubernetes的存储卷(Volume)就是为了解决这个问题的。它本质上是一个目录,Pod里的容器都能访问。但要注意,Volume的生命周期和Pod绑定——Pod删了,Volume也就没了。那怎么办?我们需要持久化存储。
4.1.1 emptyDir:临时存储的实用场景
先说说最简单的emptyDir。它就是个空目录,Pod启动时创建,Pod删除时清空。听起来很鸡肋?其实不然。
我在项目中经常用它来做「边车容器」的数据交换。比如一个容器写日志,另一个容器用Filebeat收集日志,中间放个emptyDir共享。Pod挂了日志丢了?没关系,反正日志已经发出去了。
apiVersion: v1
kind: Pod
metadata:
name: log-collector
spec:
containers:
- name: app
image: my-app
volumeMounts:
- mountPath: /var/log
name: log-volume
- name: filebeat
image: docker.elastic.co/beats/filebeat:7.10.0
volumeMounts:
- mountPath: /var/log
name: log-volume
volumes:
- name: log-volume
emptyDir: {}
4.1.2 hostPath:小心使用,但有时真香
hostPath直接把宿主机目录挂进Pod。我建议你谨慎使用——因为它破坏了Pod的隔离性。但有些场景确实绕不开,比如DaemonSet要读取宿主机日志,或者某些监控组件需要访问宿主机文件系统。
4.1.3 PersistentVolume与PersistentVolumeClaim:真正的持久化
这才是生产环境的主角。PV(PersistentVolume)是集群管理员准备的存储资源,PVC(PersistentVolumeClaim)是用户对存储的「需求声明」。两者通过匹配来绑定。
我个人习惯把PV和PVC分开管理。运维团队负责PV,开发团队只写PVC。这样职责清晰,互不干扰。
# PV定义
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.1.100
path: "/data/k8s"
# PVC定义
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
这里有个关键概念:访问模式。ReadWriteOnce(单节点读写)、ReadOnlyMany(多节点只读)、ReadWriteMany(多节点读写)。选错了,Pod就起不来。我遇到过有人把数据库的PVC配成ReadWriteMany,结果多个Pod同时写同一个数据卷,数据全乱了。
4.1.4 StorageClass:动态供给的利器
手动创建PV太累了。StorageClass可以自动创建PV。你只需要写PVC,系统会根据StorageClass自动分配存储。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
replication-type: none
4.2 Helm包管理:Kubernetes的「应用商店」
你有没有遇到过这种情况:部署一个复杂应用,要写十几个YAML文件,还要手动调整参数?Helm就是来解决这个痛点的。
Helm把Kubernetes资源打包成一个Chart。一个Chart可以包含Deployment、Service、ConfigMap、PVC等等。你只需要改几个参数,就能部署出不同配置的应用。
4.2.1 Helm的核心概念
- Chart:应用的打包格式,包含所有Kubernetes资源模板
- Repository:Chart的存储仓库,类似Docker Hub
- Release:Chart的一次部署实例,同一个Chart可以部署多次
我记得第一次用Helm部署Nginx时,就一行命令:helm install my-nginx bitnami/nginx。然后所有资源都自动创建好了。当时的感觉就是——这才是云原生该有的样子。
4.2.2 编写一个简单的Helm Chart
一个Chart的目录结构大概是这样的:
my-chart/
├── Chart.yaml # Chart元数据
├── values.yaml # 默认配置值
├── templates/ # 模板文件
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl # 辅助函数
└── charts/ # 子Chart依赖
模板文件里用Go模板语法。比如在deployment.yaml里:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-chart.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
然后在values.yaml里定义默认值:
replicaCount: 3
image:
repository: nginx
tag: latest
部署时可以用--set覆盖:helm install my-release ./my-chart --set replicaCount=5
4.2.3 Helm的版本管理
Helm v3已经废弃了Tiller,直接和Kubernetes API交互。升级时用helm upgrade,回滚用helm rollback。每次部署都会生成一个Release版本号。
我曾经在生产环境用helm upgrade --install做滚动更新,发现新版本有问题,直接helm rollback my-release 1就回退到上一个版本。整个过程不到10秒,比手动改YAML快太多了。
4.3 RBAC与安全:谁可以做什么
Kubernetes的RBAC(基于角色的访问控制)说白了就是三件事:谁(User/ServiceAccount)、能干什么(Role/ClusterRole)、在哪些资源上(Resource)。
我见过很多团队一开始图省事,直接给所有人cluster-admin权限。结果某天有人误删了kube-system命名空间下的资源,整个集群挂了。嗯,从那以后我再也不敢偷懒了。
4.3.1 Role与ClusterRole的区别
| 类型 | 作用范围 | 使用场景 |
|---|---|---|
| Role | 单个命名空间 | 限制某个团队只能操作自己的命名空间 |
| ClusterRole | 整个集群 | 集群级别的资源(如Node、PV)或跨命名空间的操作 |
4.3.2 实战:创建一个只读用户
假设我们要给一个运维人员只读权限,只能看Pod和Service:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: ops-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
verbs有get、list、watch、create、update、patch、delete。我建议遵循最小权限原则——只给够用的权限,不多给一个。
4.3.3 ServiceAccount:Pod的身份
Pod要访问Kubernetes API怎么办?用ServiceAccount。每个Pod都可以绑定一个ServiceAccount,然后通过RBAC控制这个ServiceAccount的权限。
我习惯为每个微服务创建独立的ServiceAccount,而不是用默认的。这样即使某个Pod被攻破,攻击者也拿不到集群的管理权限。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-binding
subjects:
- kind: ServiceAccount
name: my-app-sa
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
4.4 Pod安全策略:给Pod上把锁
Pod安全策略(PSP)在Kubernetes 1.21之后被废弃了,取而代之的是Pod Security Admission(PSA)。但原理是一样的:限制Pod能做什么,不能做什么。
说白了,就是防止有人用特权容器、挂载宿主机目录、或者以root身份运行。这些都是安全漏洞的高发区。
4.4.1 Pod Security Standards的三个级别
| 级别 | 说明 | 典型限制 |
|---|---|---|
| Privileged | 无限制 | 无 |
| Baseline | 基本安全 | 禁止特权容器、禁止hostPID、禁止hostNetwork |
| Restricted | 严格限制 | 禁止所有特权、必须非root、只读根文件系统 |
4.4.2 用Pod Security Admission实施策略
在命名空间上打标签即可:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: baseline
pod-security.kubernetes.io/warn: baseline
这里三个标签分别表示:强制级别(违反则拒绝)、审计级别(记录但不拒绝)、警告级别(提示但不拒绝)。
4.4.3 Pod安全上下文的配置
在Pod定义里,可以通过securityContext来声明安全需求:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: app
image: my-app
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
这些配置看起来繁琐,但能有效防止容器逃逸攻击。我建议所有生产环境的Pod都加上这些安全上下文,哪怕只是多花几分钟配置。
好了,这一章的内容就到这里。存储、Helm、RBAC、安全策略——这四个点看似独立,其实都是生产环境绕不开的「硬骨头」。下一章我们会聊聊监控和日志,到时候你会发现,没有这些基础,监控数据都拿不到。嗯,慢慢来,每一步都踩实了。