云原生 ·
Kubernetes (K8s) 入门:核心对象、kubectl 常用命令与排障全解
K8s 是容器编排的事实标准。本文从 Pod、Deployment、Service、Ingress、PV/PVC 五大基础对象入手,配合 kubectl 常用命令、集群搭法、以及 CrashLoopBackOff / ImagePullBackOff 等 20+ 常见报错的排查路径。
Kubernetes (K8s) 入门:核心对象、kubectl 常用命令与排障全解
Kubernetes(简称 K8s,K + 8 个字母 + s) 是 Google 开源的容器编排与调度平台。你把一组容器的期望状态(多少副本、暴露什么端口、挂哪个盘、健康检查怎么算)用 YAML 声明,K8s 就会自动把它调度到合适的机器、不断观察、失败自动恢复、滚动升级、自动扩缩容。
一、架构总览
1.1 控制平面(Control Plane,以前叫 Master)
| 组件 | 作用 |
|---|---|
| kube-apiserver | 集群唯一入口,所有组件都通过它读写 etcd;kubectl 也是连它 |
| etcd | 分布式 KV 数据库,存集群所有状态(“事实唯一来源”) |
| kube-scheduler | 看着新建的 Pod 还没绑定节点 → 选一台合适的 Node 绑上去 |
| kube-controller-manager | 一堆”控制环”:Node Controller、Replication(副本数)、Endpoint、ServiceAccount… |
| cloud-controller-manager | 对接公有云:LB、云盘、路由(可选,用云厂商 K8s 服务就有) |
1.2 工作节点(Worker Node)
| 组件 | 作用 |
|---|---|
| kubelet | Node 上的代理:APIServer 说这台机要跑哪些 Pod → 真的把容器拉起来、做健康检查 |
| kube-proxy | 在每台 Node 上维护 iptables / ipvs 规则,实现 Service 网络转发 |
| 容器运行时 | containerd(默认) / cri-o 等,真正跑容器(Docker Engine 1.24+ 弃用) |
| Pod | K8s 最小调度单元。一个 Pod 里可以有多个容器(共享网络栈 + 共享卷),通常”一个 Pod = 一个主容器” |
二、先搭一个最小化集群(学习用)
方案 A:kind 最快(Docker 起 3 个容器当 3 个节点)
# 装 kind(macOS)
brew install kind kubectl
# 建集群
cat > kind.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
kind create cluster --name demo --config kind.yaml
# 切到 demo 上下文
kubectl cluster-info
kubectl get nodes -o wide
方案 B:minikube / k3s
- minikube:单机体验型,VM/Docker 都能装,addon 多
- k3s:Rancher 出品,二进制 <100MB,砍掉了云插件,适合边缘/测试。生产小规模也能直接用
方案 C:生产级:kubeadm 三台裸机 / ECS
# 所有节点:装 containerd / kubeadm / kubelet / kubectl(版本 1.30)
# 然后在 Master:
sudo kubeadm init \
--kubernetes-version=v1.30.0 \
--apiserver-advertise-address=192.168.1.100 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--upload-certs
# 按输出让 Worker 节点 join
# 在本机:
mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
# 装 CNI 网络插件(必须,不然 CoreDNS 起不来),这里选 Flannel:
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 等 30s,看 Node 状态 Ready 没
kubectl get nodes -w
三、五大核心对象
3.1 Pod:最小调度单元
pod-demo.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
labels:
app: nginx
tier: web
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 512Mi }
readinessProbe:
httpGet: { path: /, port: 80 }
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet: { path: /, port: 80 }
initialDelaySeconds: 10
periodSeconds: 20
实际项目里几乎不会手写 Pod,而是用下面的 Deployment 来管理。
3.2 Deployment:管理 Pod 副本 + 滚动升级
deploy-nginx.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
spec:
replicas: 3
revisionHistoryLimit: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 升级时最多多起 1 个 Pod
maxUnavailable: 0 # 升级时最少保持 3 个 Pod 可用
selector:
matchLabels:
app: nginx
template: # 下面就是 Pod 的 template,直接复用上面 Pod 的 spec
metadata:
labels: { app: nginx }
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports: [{ containerPort: 80 }]
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 512Mi }
readinessProbe: { httpGet: { path: /, port: 80 }, initialDelaySeconds: 3, periodSeconds: 10 }
livenessProbe: { httpGet: { path: /, port: 80 }, initialDelaySeconds: 10, periodSeconds: 20 }
3.3 Service:给一组 Pod 一个稳定的访问入口
svc-nginx.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP # 仅集群内访问。可选 NodePort / LoadBalancer / ExternalName
selector: { app: nginx } # 把流量转给所有带 app=nginx 的 Pod
ports:
- name: http
port: 80 # Service 端口
targetPort: 80 # Pod 容器端口
四种 Service 类型:
| 类型 | 说明 | 场景 |
|---|---|---|
| ClusterIP(默认) | 分配一个集群内虚拟 IP,其他 Pod / Service 能访问 | 绝大多数内部服务 |
| NodePort | 在每台 Node 上开一个端口(30000-32767)外部就能 <任意NodeIP:NodePort> 访问 | 临时测试、配合外层 LB |
| LoadBalancer | 调云厂商 API 自动分配一个公网 LB 挂到 Service 上 | 云上对外服务(Nginx Ingress Controller 常用这种) |
| ExternalName | 直接 CNAME 到外部域名,比如 xxx.rds.aliyuncs.com | 让集群内访问外部 DB 像访问内部 Service 一样 |
3.4 Ingress:把 7 层域名路由到 Service
先装 Nginx Ingress Controller(kind 自带):
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
ing-nginx.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ing
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: nginx.k8s.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port: { number: 80 }
3.5 PV / PVC:持久化存储
- StorageClass(集群管理员先配好,大多数云 K8s 自带)
- PVC(PersistentVolumeClaim):用户声明”我要 10GB ReadWriteOnce”
- PV(PersistentVolume):对应真实的磁盘(云盘 / NFS / Ceph / LocalPath)
pvc-demo.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
storageClassName: standard
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: mysql }
spec:
selector: { matchLabels: { app: mysql } }
template:
metadata: { labels: { app: mysql } }
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- { name: MYSQL_ROOT_PASSWORD, value: "ChangeMe123" }
ports: [{ containerPort: 3306 }]
volumeMounts:
- { name: data, mountPath: /var/lib/mysql }
volumes:
- name: data
persistentVolumeClaim: { claimName: mysql-pvc }
3.6 ConfigMap & Secret:放配置
apiVersion: v1
kind: ConfigMap
metadata: { name: app-cm }
data:
APP_ENV: "production"
DB_HOST: "mysql.default.svc.cluster.local"
config.toml: |
[server]
port = 3000
---
apiVersion: v1
kind: Secret # 只是默认 base64,不是加密!生产用 SealedSecret / ExternalSecret / Vault
metadata: { name: app-secret }
type: Opaque
stringData:
DB_PASSWORD: "s3cret!p@ss"
---
# Pod 里引用
envFrom:
- configMapRef: { name: app-cm }
- secretRef: { name: app-secret }
volumeMounts:
- { name: cm, mountPath: /etc/app/config.toml, subPath: config.toml }
volumes:
- name: cm
configMap: { name: app-cm }
四、kubectl 常用命令速查
4.1 看
kubectl get nodes / pods / deploy / svc / ing / pvc / sc / events -o wide -w
# -A = --all-namespaces;-n 指定命名空间
kubectl describe pod <POD> # 最有用:调度/事件/探针失败/卷挂不上都在 Events
kubectl logs -f --tail=200 <POD> -c <CONTAINER> # 多容器要加 -c
kubectl logs -f deployment/nginx-deploy --all-containers=true
kubectl exec -it <POD> -- bash # 进 Pod
4.2 改 / 应用
kubectl apply -f xxx.yaml # 声明式改(推荐,会算 diff)
kubectl create -f xxx.yaml # 命令式(一次性)
kubectl delete -f xxx.yaml
kubectl set image deploy/nginx-deploy nginx=nginx:1.28-alpine --record # 换镜像 + 记原因
kubectl rollout status deploy/nginx-deploy
kubectl rollout history deploy/nginx-deploy
kubectl rollout undo deploy/nginx-deploy # 回滚到上一版
kubectl rollout undo deploy/nginx-deploy --to-revision=3 # 回滚到指定版本
kubectl scale deploy/nginx-deploy --replicas=5
kubectl autoscale deploy/nginx-deploy --min=2 --max=10 --cpu-percent=70 # 基于 CPU 自动扩缩
4.3 排错”三板斧”
# 1. 看 Pod 状态
kubectl get pods -A -o wide
# 2. 看事件(调度、绑定、拉镜像、探针失败都在这)
kubectl describe pod -n <NS> <POD>
# 3. 看真实日志
kubectl logs -n <NS> --previous <POD> # --previous 看上次挂掉那一版的日志
五、学习路线建议
- 会写五大核心对象的 YAML:Pod / Deploy / Service / Ingress / PVC
- 理解调度:
nodeSelector/affinity/taint&toleration/ 资源 requests & limits - 理解配置管理:ConfigMap、Secret、Downward API、Helm Charts
- 理解网络:kube-proxy iptables 模式、Service ClusterIP 怎么来的、CNI 插件(Flannel / Calico / Cilium)
- 理解存储:StorageClass、CSI、扩容、快照
- 可观测性:Metrics Server(HPA 要)/ Prometheus + Grafana / ELK / Jaeger
- 包管理:Helm → 进阶 Helmfile、ArgoCD (GitOps)
- 安全:RBAC、SA + Token、NetworkPolicy、PodSecurity / PSA、OPA Gatekeeper
六、日常排障手册
6.1 Pod 常见状态速查
| STATUS | 含义 | 典型原因 |
|---|---|---|
| Pending | 还没调度到节点 / 调度到了但卷没挂上 | 资源不够 / PVC 没绑定 / 镜像拉取中慢 / 污点不匹配 |
| ContainerCreating | 容器创建中 | 多半是镜像在拉(慢)或 init container 没完 |
| ImagePullBackOff / ErrImagePull | 镜像拉取失败 | 镜像名写错、私人仓库没登、网络不通 Docker Hub、tag 不存在、exec format error 架构不对 |
| CrashLoopBackOff | 容器一启动就挂,K8s 指数退避重试 | 程序本身 panic / exit 1 / 配置错 / 健康检查配错 / 端口占用 |
| RunContainerError | 容器跑不起来 | 启动命令写错、挂载点目录不存在、磁盘只读没权限 |
| Error / ExitCode 非 0 | 容器启动直接报错退出 | 同上,logs --previous 看真因 |
| OOMKilled | 内存越界被宿主机 kill | 调大 resources.limits.memory 或查程序 leak |
| Evicted | 节点 Disk / Memory 压力大,kubelet 把 Pod 赶出来了 | 清节点磁盘 / 调 requests 避免争抢 |
| Terminating | 在删 Pod 但一直删不掉 | 1) 容器里进程没响应 SIGTERM;2) 挂载卷脱不下来。kubectl delete pod --force --grace-period=0 |
6.2 常用报错排查路径
A. CrashLoopBackOff(最常见)
# ① 事件里看看是什么错
kubectl describe pod <POD> | grep -A 20 Events
# ② 看上次启动为什么退出(重要!)
kubectl logs --previous <POD> -c <CONTAINER>
# ③ 退出码
kubectl get pod <POD> -o jsonpath='{.status.containerStatuses[0].lastState.terminated}' | jq
B. ImagePullBackOff
# ① 先在同一台 Node 上 docker pull / crictl pull 同款镜像试
# ② 看事件里具体报错:"not found" / "unauthorized" / "i/o timeout"
# ③ 私有仓库:加 imagePullSecrets 到 Pod 或 ServiceAccount
kubectl create secret docker-registry regcred \
--docker-server=registry.cn-hangzhou.aliyuncs.com \
--docker-username=xxx --docker-password=xxx
# 在 Pod spec 里写:
# imagePullSecrets: [{ name: regcred }]
C. Service 连不通
# ① Endpoints 有没有?(Service selector 有没有匹配到 Pod)
kubectl get endpoints <SVC>
# 为空 = selector 写错 / Pod 没就绪(检查 Pod labels & readiness)
# ② 直接打 Pod IP + 端口通不通?
kubectl run tmp --rm -it --image=nicolaka/netshoot -- bash
# 进了容器里:
curl -v http://<POD_IP>:80
# ③ Pod 通但 Service 不通:
# a. Pod 没 readiness,还没进 endpoints
# b. kube-proxy 挂了:journalctl -u kube-proxy
# c. iptables/ipvs 规则错:iptables-save | grep <SVC ClusterIP>
D. Ingress 404 / 502 / 503
# 1. Ingress Controller Pod 是否 healthy?
kubectl -n ingress-nginx get pod
# 2. Controller 日志看请求有没有到
kubectl -n ingress-nginx logs -f deployment/ingress-nginx-controller
# 3. Ingress 规则 + IngressClass 是否匹配
kubectl get ing -A -o wide
kubectl get ingressclass
# 4. 503 = Service 或 Endpoints 空 / Service 端口名写错
# 5. 404 = pathType / host / rewrite 规则不匹配
E. PVC 一直 Pending
kubectl describe pvc <PVC> | grep Events
# 常见原因:
# ① 没有 StorageClass(名字写错)
# ② 存储池容量满
# ③ 云盘/存储插件报错,看 controller-manager 日志或 csi-provisioner
F. Node NotReady
# ① ssh 上去看 kubelet
systemctl status kubelet
journalctl -u kubelet -n 200 --no-pager
crictl ps # 看容器运行时
# ② 常见根因:
# - 节点磁盘满(docker system prune / /var/lib/kubelet / journalctl --vacuum-size=500M)
# - 时间不同步(timedatectl set-ntp true)
# - apiserver 网络不通(curl https://<APISERVER>:6443/version)
# - kubelet 证书过期(kubeadm certs renew all 后重启)
6.3 通用 Debug Pod(进入集群任意节点网络视角)
# 临时起一个"排障工具箱 Pod":自带 curl/wget/dig/traceroute/iperf3/tcpdump/strace/lsof...
kubectl run tmp-shell --rm -it --image=nicolaka/netshoot -- bash
# 想共享某个 Pod 的网络栈(不用进容器本身,省得它没装工具):
kubectl debug -it <POD> --image=nicolaka/netshoot --target=<容器名> --share-processes
6.4 集群慢 / 性能分析
kubectl top nodes / top pods -A→ 看 CPU / 内存水位(要装 metrics-server)- 某 Pod CPU 高:
kubectl exec -it <POD> -- sh -c 'apk add htop 2>/dev/null; htop',或宿主机pidstat / perf - 调度慢:看
kube-scheduler日志 → 所有 Node 都被过滤器(资源、亲和、污点)拒了? - apiserver 慢:
kubectl top pod -n kube-system | grep kube-apiserver # 看 etcd:ETCD 写 WAL 慢是常见瓶颈(盘慢 → SSD 升级) - kube-proxy 规则爆炸(万级 Service 用 iptables 模式很慢)→ 切
mode: ipvs
七、安全与 RBAC 速记
K8s 权限三件套:
- Role / ClusterRole:定义”对什么资源可以做什么操作”(verbs: get/list/watch/create/update/patch/delete…)
- RoleBinding / ClusterRoleBinding:把上面的 Role 绑定到”谁”(User / Group / ServiceAccount)
示例:给某个 ServiceAccount 只能看 default 命名空间的 Pod:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: { name: pod-reader, namespace: default }
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: { name: read-pods, namespace: default }
subjects:
- kind: ServiceAccount
name: default # 某个 SA
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
八、进阶学习方向
- Helm:K8s 的包管理,K8s 生态 90% 的组件都是 Helm chart 发的
- ArgoCD / Flux(GitOps):Git 改了自动 apply 到集群
- Cilium / eBPF:取代 kube-proxy,提供更好的网络、网络策略、可观测
- Istio / Linkerd 服务网格:mTLS、灰度、流量镜像、熔断
- KEDA:基于事件(Kafka lag / 队列)的自动扩缩容
- Kyverno / Gatekeeper(OPA):策略引擎,“所有镜像必须来自公司私有仓库”这种规则强制
- kube-state-metrics + Prometheus + Grafana + Alertmanager:标准监控三件套
- Karpenter / Cluster Autoscaler:集群级自动扩节点
参考资料
- K8s 官方文档:https://kubernetes.io/zh-cn/docs/home/
- kubectl 速查:https://kubernetes.io/zh-cn/docs/reference/kubectl/cheatsheet/
- Kubernetes by Example:https://kubernetesbyexample.com/
- kind 文档:https://kind.sigs.k8s.io/
- netshoot 工具箱:https://github.com/nicolaka/netshoot
- Troubleshooting 指南:https://kubernetes.io/zh-cn/docs/tasks/debug/