Kubernetes 核心模块详细总结
系统梳理了 Kubernetes 认证授权、Dashboard 部署、动态卷供应、StatefulSet 有状态应用 四大模块,所有 YAML 代码均附有详细中文注释,可直接用于实验或生产参考。
一、认证与授权(RBAC + ServiceAccount)
1.1 ClusterRole 和 ClusterRoleBinding
yaml
# ------------------------------------------------------------
# 1. 创建 ClusterRole:定义权限集合
# ------------------------------------------------------------
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-role # 角色名称
rules:
- apiGroups: [""] # "" 表示核心 API 组
resources: ["pods"] # 操作资源:Pod
verbs: ["get", "list", "watch"] # 允许的操作:查看和监听
---
# ------------------------------------------------------------
# 2. 将 ClusterRole 绑定给用户(User)
# ------------------------------------------------------------
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: laoma-pod-role-binding # 绑定名称
subjects:
- kind: User # 绑定主体类型:用户
name: laoma # 用户名
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pod-role # 引用的 ClusterRole 名称
apiGroup: rbac.authorization.k8s.io
命令行快速创建
bash
# 创建 ClusterRole
kubectl create clusterrole pod-role --verb=get,list,watch --resource=pods
# 绑定给用户 laoma(集群级别)
kubectl create clusterrolebinding laoma-pod-role --clusterrole=pod-role --user=laoma
# 验证:使用 laoma 用户身份查看 Pod
kubectl get pods --user=laoma
# 回收权限
kubectl delete clusterrolebinding laoma-pod-role
kubectl delete clusterrole pod-role
1.2 ServiceAccount(服务账户)
概念
- ServiceAccount 是 Pod 中进程访问 API Server 的凭证。
- 每个 Namespace 默认存在一个
defaultServiceAccount,Pod 默认使用它。 - 为 ServiceAccount 绑定角色,可使 Pod 内的进程获得相应权限。
实践:为 ServiceAccount 授予集群管理权限
yaml
# ------------------------------------------------------------
# 1. 创建 ServiceAccount
# ------------------------------------------------------------
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa1
namespace: auth # 指定命名空间
---
# ------------------------------------------------------------
# 2. 创建 ClusterRoleBinding 将 cluster-admin 绑定给 sa1
# ------------------------------------------------------------
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: auth-sa1-cluster-admin
subjects:
- kind: ServiceAccount
name: sa1
namespace: auth # 必须指定命名空间
roleRef:
kind: ClusterRole
name: cluster-admin # 内置最高权限角色
apiGroup: rbac.authorization.k8s.io
命令行操作
bash
# 创建 ServiceAccount
kubectl create sa sa1 -n auth
# 绑定 cluster-admin 角色(格式:--serviceaccount=命名空间:SA名)
kubectl create clusterrolebinding auth-sa1-cluster-admin \
--clusterrole=cluster-admin \
--serviceaccount=auth:sa1
# 在 Pod 中使用该 ServiceAccount(在 Pod spec 中添加)
# serviceAccountName: sa1
ServiceAccount Token 管理
bash
# 创建临时 Token(v1.24+,有效期默认 1 小时)
kubectl create token sa1 -n auth
# 创建永久 Token(通过 Secret 手动创建)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: sa1-secret
namespace: auth
annotations:
kubernetes.io/service-account.name: sa1
type: kubernetes.io/service-account-token
EOF
# 删除 SA 后,关联的 Secret 会被自动清理
kubectl delete sa sa1 -n auth
二、Kubernetes Dashboard 部署
2.1 部署 Dashboard(v2.6.1)
bash
# 下载官方 YAML(已替换为私有镜像)
wget -O recommended.yaml http://192.168.46.200/class/course-materials/softwares/stage03/kubernetes-dashboard-recommended-v2.6.1.yaml
# 查看并确认镜像地址
grep image: recommended.yaml
# 应用部署
kubectl apply -f recommended.yaml
2.2 授予管理员权限并获取 Token
bash
# 将 cluster-admin 角色绑定给 Dashboard 的 ServiceAccount
kubectl create clusterrolebinding kubernetes-dashboard-cluster-admin \
--clusterrole=cluster-admin \
--serviceaccount=kubernetes-dashboard:kubernetes-dashboard
# 生成 Token(有效期 24 小时)
kubectl -n kubernetes-dashboard create token kubernetes-dashboard
2.3 通过 Ingress 暴露 Dashboard(可选)
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-dashboard
namespace: kubernetes-dashboard
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true" # 透传 TLS
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: dashboard.laoma.cloud
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubernetes-dashboard
port:
number: 443
2.4 登录验证
- 浏览器访问
https://dashboard.laoma.cloud(需配置域名解析) - 选择 "Token" 方式,粘贴上一步生成的 Token。
- 若使用 NodePort 方式,访问
https://<节点IP>:<NodePort>,也需使用 Token 登录。
三、动态卷供应(StorageClass)
3.1 部署 Local Path Provisioner(本地存储)
Local Path Provisioner 利用节点本地磁盘提供动态存储,适合对性能要求高、无需网络存储的场景。
yaml
# ------------------------------------------------------------
# Local Path Provisioner 完整清单(关键部分)
# ------------------------------------------------------------
apiVersion: v1
kind: Namespace
metadata:
name: local-path-storage
---
# 1. ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: local-path-provisioner-service-account
namespace: local-path-storage
---
# 2. RBAC 授权(Role、ClusterRole、Binding)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: local-path-provisioner-role
rules:
- apiGroups: [""]
resources: ["nodes", "persistentvolumeclaims", "pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
---
# 3. Deployment(核心控制器)
apiVersion: apps/v1
kind: Deployment
metadata:
name: local-path-provisioner
namespace: local-path-storage
spec:
replicas: 1
selector:
matchLabels:
app: local-path-provisioner
template:
metadata:
labels:
app: local-path-provisioner
spec:
serviceAccountName: local-path-provisioner-service-account
containers:
- name: local-path-provisioner
image: hub.laoma.cloud/rancher/local-path-provisioner:v0.0.37
command: ["local-path-provisioner", "--debug", "start", "--config", "/etc/config/config.json"]
volumeMounts:
- name: config-volume
mountPath: /etc/config/
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumes:
- name: config-volume
configMap:
name: local-path-config
---
# 4. ConfigMap(定义本地存储路径)
apiVersion: v1
kind: ConfigMap
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
]
}
---
# 5. StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer # 等待 Pod 调度再创建 PV
reclaimPolicy: Delete
部署命令:
bash
kubectl apply -f local-path-storage.yaml
kubectl get sc local-path
验证使用:
yaml
# 创建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-path-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
storageClassName: local-path
---
# 创建 Deployment 挂载该 PVC(Pod 调度后 PV 才会创建)
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
persistentVolumeClaim:
claimName: local-path-pvc
3.2 部署 NFS Provisioner(共享存储)
NFS 无内置 Provisioner,需自行部署外部控制器。
1. 部署 NFS 服务(在 10.1.8.30 上)
bash
apt install -y nfs-kernel-server
mkdir -m 777 /shares
echo "/shares *(rw,sync,no_root_squash,no_all_squash,insecure)" >> /etc/exports
systemctl restart nfs-server
# 客户端安装 nfs-common
2. 部署 NFS Provisioner
yaml
# ------------------------------------------------------------
# RBAC 授权
# ------------------------------------------------------------
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: kube-storage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get","list","watch","create","delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get","list","watch","update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get","list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: kube-storage
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
# ------------------------------------------------------------
# NFS Provisioner Deployment
# ------------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: kube-storage
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
volumes:
- name: nfs-client-root
nfs:
server: 10.1.8.30 # NFS 服务器 IP
path: /shares # 共享根目录
containers:
- name: nfs-client-provisioner
image: hub.laoma.cloud/sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs # 必须与 StorageClass 的 provisioner 一致
- name: NFS_SERVER
value: 10.1.8.30
- name: NFS_PATH
value: /shares
---
# ------------------------------------------------------------
# StorageClass
# ------------------------------------------------------------
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: fuseim.pri/ifs
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate
parameters:
onDelete: retain # 删除 PVC 时保留 NFS 数据
archiveOnDelete: "false"
验证部署
bash
kubectl apply -f nfs-rbac.yaml
kubectl apply -f nfs-provisioner.yaml
kubectl apply -f nfs-storageclass.yaml
# 查看 StorageClass
kubectl get sc nfs-storage
# 测试:创建 PVC,绑定后写入数据
kubectl apply -f nfs-pvc.yaml
kubectl get pvc webclaim # 状态应为 Bound
四、StatefulSet 有状态应用部署
4.1 StatefulSet 核心概念
- 固定身份 :Pod 名称为
<statefulset-name>-<序号>(如nginx-0)。 - 有序操作:创建/缩容/更新均按序号顺序执行。
- 稳定存储 :通过
volumeClaimTemplates为每个 Pod 自动创建独立的 PVC,Pod 删除后 PVC 保留,重建后复用。 - 需要 Headless Service :提供固定 DNS 解析(
pod-name.service-name.namespace.svc.cluster.local)。
4.2 示例:部署 3 节点 Nginx 集群
Headless Service
yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: statefulset
spec:
clusterIP: None # 无头服务
selector:
app: nginx
ports:
- port: 80
targetPort: 80
name: nginx-port
StatefulSet
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
namespace: statefulset
spec:
serviceName: nginx # 关联 Headless Service
replicas: 3
minReadySeconds: 10 # Pod 就绪后等待 10 秒才认为可用
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
name: nginx
volumeMounts:
- name: nginx-data
mountPath: /usr/share/nginx/html
# ---------- PVC 模板 ----------
volumeClaimTemplates:
- metadata:
name: nginx-data # 与 volumeMounts.name 一致
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs-storage" # 使用动态卷存储类
resources:
requests:
storage: 10Gi
验证 Pod 顺序创建与数据持久性
bash
# 观察 Pod 创建顺序(nginx-0 → nginx-1 → nginx-2)
kubectl get pods -w
# 为每个 Pod 写入不同数据(模拟)
kubectl exec nginx-0 -- sh -c "echo 'nginx-0' > /usr/share/nginx/html/index.html"
kubectl exec nginx-1 -- sh -c "echo 'nginx-1' > /usr/share/nginx/html/index.html"
kubectl exec nginx-2 -- sh -c "echo 'nginx-2' > /usr/share/nginx/html/index.html"
# 通过 Headless Service DNS 访问
kubectl run test --image=nginx -it -- bash
curl nginx-0.nginx # 返回 nginx-0
curl nginx-1.nginx # 返回 nginx-1
curl nginx-2.nginx # 返回 nginx-2
# 删除 nginx-2,等待重建后数据依然存在
kubectl delete pod nginx-2
# 重建后再次访问 nginx-2.nginx,数据仍为 nginx-2
4.3 示例:部署 Etcd 集群(自动组网)
Headless Service
yaml
apiVersion: v1
kind: Service
metadata:
name: etcd
namespace: statefulset
spec:
clusterIP: None
selector:
app: etcd
ports:
- port: 2379
name: client
- port: 2380
name: peer
StatefulSet(关键环境变量配置)
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
namespace: statefulset
spec:
serviceName: etcd
replicas: 3
selector:
matchLabels:
app: etcd
template:
metadata:
labels:
app: etcd
spec:
containers:
- name: etcd
image: registry.aliyuncs.com/google_containers/etcd:3.5.10-0
command: ["/usr/local/bin/etcd"]
ports:
- containerPort: 2379
name: client
- containerPort: 2380
name: peer
env:
# 1. 节点名称:来自 Pod 名称
- name: ETCD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# 2. 数据目录
- name: ETCD_DATA_DIR
value: /var/lib/etcd
# 3. 节点间通信地址(固定 DNS)
- name: ETCD_INITIAL_ADVERTISE_PEER_URLS
value: "http://$(ETCD_NAME).etcd.statefulset.svc.cluster.local:2380"
- name: ETCD_LISTEN_PEER_URLS
value: "http://0.0.0.0:2380"
# 4. 客户端监听地址
- name: ETCD_LISTEN_CLIENT_URLS
value: "http://0.0.0.0:2379"
- name: ETCD_ADVERTISE_CLIENT_URLS
value: "http://$(ETCD_NAME).etcd.statefulset.svc.cluster.local:2379"
# 5. 初始集群成员列表(硬编码)
- name: ETCD_INITIAL_CLUSTER
value: "etcd-0=http://etcd-0.etcd.statefulset.svc.cluster.local:2380,etcd-1=http://etcd-1.etcd.statefulset.svc.cluster.local:2380,etcd-2=http://etcd-2.etcd.statefulset.svc.cluster.local:2380"
- name: ETCD_INITIAL_CLUSTER_TOKEN
value: "etcd-token"
- name: ETCD_INITIAL_CLUSTER_STATE
value: "new"
volumeMounts:
- name: etcd-data
mountPath: /var/lib/etcd
volumeClaimTemplates:
- metadata:
name: etcd-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs-storage"
resources:
requests:
storage: 10Gi
验证集群
bash
# 查看成员列表(应从 etcd-0 节点执行)
kubectl exec etcd-0 -- etcdctl member list
# 预期看到三个成员,状态均为 started
# 写入数据
kubectl exec etcd-0 -- etcdctl put /test/key "hello"
# 从 etcd-2 读取
kubectl exec etcd-2 -- etcdctl get /test/key # 输出 hello
4.4 示例:部署 Redis 集群(1 主 2 从 + Sentinel)
Headless Service
yaml
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: statefulset
spec:
clusterIP: None
selector:
app: redis
ports:
- port: 6379
name: redis
- port: 26379
name: sentinel
StatefulSet(启动脚本动态判断主从)
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: statefulset
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.0-alpine
ports:
- containerPort: 6379
- containerPort: 26379
command: ["/bin/sh", "-c"]
args:
- |
# 获取 Pod 序号
NODE_ID=$(hostname | awk -F'-' '{print $2}')
# 生成 Redis 配置
cat > /data/redis.conf << EOF
bind 0.0.0.0
protected-mode no
port 6379
dir /data
appendonly yes
requirepass "redis123"
masterauth "redis123"
EOF
# 非 0 节点设置为从节点
if [ "$NODE_ID" != "0" ]; then
echo "replicaof redis-0.redis.statefulset.svc.cluster.local 6379" >> /data/redis.conf
fi
# 启动 Redis
redis-server /data/redis.conf &
# 等待主节点域名解析成功
until nslookup redis-0.redis.statefulset.svc.cluster.local; do
sleep 2
done
# 生成 Sentinel 配置
cat > /data/sentinel.conf << EOF
bind 0.0.0.0
protected-mode no
port 26379
sentinel resolve-hostnames yes
sentinel announce-hostnames yes
sentinel monitor mymaster redis-0.redis.statefulset.svc.cluster.local 6379 2
sentinel auth-pass mymaster redis123
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
EOF
# 启动 Sentinel
redis-sentinel /data/sentinel.conf
volumeMounts:
- name: redis-data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs-storage"
resources:
requests:
storage: 10Gi
验证
bash
# 查看主从状态
kubectl exec redis-0 -- redis-cli -a redis123 info replication
# 预期 role:master,connected_slaves:2
# 查看 Sentinel 监控的主节点
kubectl exec redis-0 -- redis-cli -p 26379 sentinel master mymaster
# 故障转移测试:删除主节点
kubectl delete pod redis-0
# 等待重建后,redis-0 自动变为从节点,其他节点选举新主
五、常用运维命令速查
| 用途 | 命令 |
|---|---|
| 查看 StorageClass | kubectl get sc |
| 查看 PVC 绑定状态 | kubectl get pvc -n <ns> |
| 查看 StatefulSet 状态 | kubectl get sts -n <ns> |
| 查看 Pod 顺序创建日志 | kubectl get pods -n <ns> -w |
| 进入 Pod 调试 | kubectl exec -it <pod> -n <ns> -- /bin/sh |
| 删除 StatefulSet 但保留 PVC | kubectl delete sts <name> -n <ns> --cascade=orphan |
| 强制删除 Pod(模拟故障) | kubectl delete pod <pod> -n <ns> --grace-period=0 --force |
| 生成 ServiceAccount Token | kubectl create token <sa> -n <ns> |
| 创建 ClusterRoleBinding | kubectl create clusterrolebinding <name> --clusterrole=<role> --serviceaccount=<ns>:<sa> |
六、安全与最佳实践
- 敏感信息管理 :密码、Token 必须使用
Secret,禁止明文出现在 YAML 中。 - 资源配额 :为命名空间设置
ResourceQuota和LimitRange,防止资源抢占。 - 健康检查 :为所有工作负载配置
livenessProbe、readinessProbe和startupProbe。 - 存储回收策略 :根据数据重要性设置
reclaimPolicy: Retain或Delete。 - 权限最小化 :仅授予 ServiceAccount 所需的最小权限,避免使用
cluster-admin。