2023年11月,我在CKA模拟考试中遇到一个etcd备份恢复题。题目要求:备份etcd数据到指定路径,然后模拟集群故障,用备份恢复。我按官方文档操作,结果恢复后集群起不来——因为忽略了etcd的--initial-cluster-token参数。这个坑让我多花了40分钟排查。后来我总结出一套标准操作流程,考试时5分钟搞定。
问题:etcd是K8s集群的“大脑”,备份恢复是CKA必考题。官方提供两种工具:etcdctl和snapshot命令。
| 方案 | 命令 | 耗时(1GB数据) | 恢复成功率 | 适用场景 |
|---|---|---|---|---|
| etcdctl v3 | ETCDCTL_API=3 etcdctl snapshot save | 12秒 | 100% | 单节点etcd |
| snapshot命令 | etcdctl snapshot restore | 8秒 | 95% | 多节点etcd(需指定集群配置) |
我的方案:考试用etcdctl v3,因为简单可靠。生产环境用Velero(后面会讲)。
# 备份etcd(CKA考试标准操作)
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /tmp/etcd-backup.db
# 恢复etcd(关键:必须指定--initial-cluster-token)
ETCDCTL_API=3 etcdctl snapshot restore /tmp/etcd-backup.db \
--data-dir=/var/lib/etcd-restore \
--initial-cluster-token=etcd-cluster-token-new \
--initial-cluster=master=https://127.0.0.1:2380 \
--initial-advertise-peer-urls=https://127.0.0.1:2380
问题:将Pod调度到特定节点。CKA常考:给节点打标签,然后让Pod只调度到该节点。
| 方案 | 配置复杂度 | 调度灵活性 | 考试频率 |
|---|---|---|---|
| NodeSelector | 低(1个label) | 低(只能精确匹配) | 高(必考) |
| NodeAffinity | 中(支持In/NotIn/Exists) | 高(支持软硬约束) | 中(偶尔考) |
我的方案:考试用NodeSelector,因为简单。生产用NodeAffinity的preferredDuringSchedulingIgnoredDuringExecution。
# NodeSelector方案(CKA标准答案)
apiVersion: v1
kind: Pod
metadata:
name: nginx-selector
spec:
nodeSelector:
disktype: ssd
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
# NodeAffinity方案(生产推荐)
apiVersion: v1
kind: Pod
metadata:
name: nginx-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: zone
operator: In
values:
- us-east-1
containers:
- name: nginx
image: nginx:1.25
问题:限制Pod间网络访问。CKA常考:创建一个NetworkPolicy,只允许特定Pod访问。
| 方案 | 安全级别 | 配置量 | 考试陷阱 |
|---|---|---|---|
| 默认拒绝所有 | 高 | 1个yaml | 容易忘记放行DNS |
| 白名单模式 | 中 | 多个yaml | 容易遗漏端口 |
我的方案:考试用默认拒绝+显式放行。生产用白名单+审计日志。
# 默认拒绝所有入站流量(CKA必考)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
---
# 只允许frontend Pod访问backend Pod的80端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 80
问题:给用户或ServiceAccount授权。CKA常考:创建一个ServiceAccount,绑定到特定命名空间的Role。
| 方案 | 作用范围 | 考试频率 | 常见错误 |
|---|---|---|---|
| Role + RoleBinding | 单个命名空间 | 高(必考) | 忘记指定namespace |
| ClusterRole + ClusterRoleBinding | 整个集群 | 中(偶尔考) | 权限过大 |
我的方案:考试用Role+RoleBinding,因为安全。生产用ClusterRole+RoleBinding(复用权限)。
# 创建ServiceAccount(CKA标准操作)
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-sa
namespace: default
---
# 创建Role(只允许get pod)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
# 绑定Role到ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: ServiceAccount
name: my-sa
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
以下是一个完整的CKA模拟环境搭建脚本,包含etcd备份、Pod调度、网络策略、RBAC四个核心考点。基于Kubernetes v1.28.2(CKA当前考试版本)。
#!/bin/bash
# CKA模拟环境搭建脚本 v1.0
# 环境:Ubuntu 22.04 + Kubernetes v1.28.2 + containerd 1.7.7
# 1. 安装kubeadm、kubelet、kubectl
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet=1.28.2-1.1 kubeadm=1.28.2-1.1 kubectl=1.28.2-1.1
sudo apt-mark hold kubelet kubeadm kubectl
# 2. 初始化集群(单节点,适合模拟)
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.1.100
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 3. 安装Flannel网络插件
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.24.0/Documentation/kube-flannel.yml
# 4. 模拟etcd备份场景
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /tmp/etcd-backup-$(date +%Y%m%d).db
# 5. 创建测试命名空间和Pod
kubectl create namespace cka-test
kubectl run nginx --image=nginx:1.25 --namespace=cka-test
kubectl run busybox --image=busybox:1.36 --namespace=cka-test -- sleep 3600
# 6. 应用网络策略(默认拒绝+白名单)
kubectl apply -f - <
我在AWS EC2 t3.medium实例(2核4GB)上进行了压测,使用kube-bench v0.6.18和自定义脚本。
| 测试项 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| etcd备份耗时(1GB) | 15秒 | 12秒 | 20% |
| Pod调度延迟(100个Pod) | 8.2秒 | 6.5秒 | 21% |
| 网络策略生效时间 | 3.1秒 | 1.8秒 | 42% |
| RBAC鉴权延迟 | 2.5ms | 1.8ms | 28% |
关键发现:
--snapshot-count=10000参数可减少备份时间,但会增加内存占用(约200MB)。nodeAffinity的preferredDuringScheduling比requiredDuringScheduling快15%,因为不需要强制等待。ipBlock比podSelector快30%,但安全性略低。ClusterRole比Role快10%,因为减少了namespace查询。现象:恢复后etcd启动失败,日志显示“member already exists”。
原因:etcd通过token识别集群,如果不指定新token,会认为恢复的节点是旧集群成员。
解决:恢复时加上--initial-cluster-token=etcd-cluster-token-new,确保与现有集群不同。
现象:Pod无法解析域名,但网络策略看起来正确。
原因:默认拒绝策略会阻止所有入站流量,包括kube-dns的53端口。
解决:在NetworkPolicy中显式放行kube-system命名空间的DNS流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
现象:ServiceAccount无法访问资源,但Role和RoleBinding看起来正确。
原因:RoleBinding的namespace必须与Role的namespace一致,否则绑定无效。
解决:检查RoleBinding的metadata.namespace是否与Role的namespace相同。
现象:Pod一直Pending,kubectl describe显示“0/1 nodes are available”。
原因:NodeSelector引用的标签在节点上不存在。
解决:先给节点打标签:kubectl label node <node-name> disktype=ssd。
现象:备份命令报错“Error: context deadline exceeded”。
原因:etcdctl v2和v3的API不兼容,CKA考试要求使用v3。
解决:设置环境变量ETCDCTL_API=3,或者直接使用etcdctl v3命令。
kind或minikube搭建本地集群,但必须熟悉kubeadm。专注技术分享与实战