Kubernetes 核心概念与实战入门

6375 字
32 分钟
Kubernetes 核心概念与实战入门

Docker 把”打包应用”变成了标准动作,但要把应用稳定跑在生产环境里,单机容器远远不够。本文从设计动机讲到核心对象,再落到 kubectl、滚动更新、探针、资源管理、Helm 与本地实践的完整链路。

1. 为什么需要编排#

单机 Docker(或 Compose)在生产会立刻暴露以下局限:

  • 故障自愈:--restart=always 能拉起崩溃的容器,但节点整机宕机时容器不会漂移到其他机器。
  • 水平扩缩容:docker compose up --scale 只能单机扩副本,跨节点分发要自己写脚本。
  • 服务发现与负载均衡:容器重建后 IP 会变,多副本之间缺少稳定的虚拟地址、DNS 名字与统一入口。
  • 滚动发布与回滚:Compose 默认”停旧起新”,做不到按比例灰度,也做不到一条命令回滚。
  • 配置与密钥:环境变量散落在多个 compose 文件,无法统一注入与轮转。
  • 资源隔离与存储编排:多容器抢 CPU/内存缺少调度层配额,Pod 漂移后数据卷也无法跟着走。

Kubernetes(简称 K8s)把这些能力抽象成声明式 API:你只描述期望状态(desired state),控制器持续把实际状态(actual state)拉回期望。这与 docker run 的命令式思路有本质区别——K8s 关心的是”持续稳定在某个状态”。

要解决的问题可以概括为:调度、自愈、扩缩容、服务发现、负载均衡、配置管理、密钥管理、存储编排、滚动发布、批处理、多租户隔离。

2. 集群架构#

集群分为控制平面(Control Plane)与工作节点(Worker Node)。

2.1 控制平面组件#

组件职责
kube-apiserver集群唯一入口,提供 REST API,负责认证、授权、准入控制与校验
etcd分布式 KV 存储,保存集群全部状态,是唯一的真相来源
kube-scheduler为尚未绑定节点的 Pod 选择运行节点
kube-controller-manager运行各类控制器,做调谐(reconcile)
cloud-controller-manager对接云厂商 API,管理负载均衡器、路由、节点生命周期

2.2 工作节点组件#

组件职责
kubelet接收 PodSpec,调用 CRI 启动容器,上报状态,执行探针
kube-proxy维护节点网络规则(iptables / IPVS / nftables),实现 Service 转发
容器运行时真正跑容器的组件,如 containerd、CRI-O

只有 apiserver 与 etcd 交互,其余组件都通过 watch/list 与 apiserver 通信。这种中心辐射结构让状态变更路径唯一,便于鉴权与审计。

2.3 一次 kubectl apply 到 Pod 运行的完整链路#

  1. kubectl 读取 kubeconfig,把 YAML 转成 JSON,通过 HTTPS 发给 kube-apiserver。
  2. apiserver 依次执行认证(证书 / token)、授权(RBAC)、准入控制(MutatingWebhook 改值 → 校验 → ValidatingWebhook 拦截),把对象写入 etcd。此时 Deployment 已持久化。
  3. Deployment controller 通过 watch 感知新对象并创建 ReplicaSet;ReplicaSet controller 再创建指定数量的 Pod,此时 spec.nodeName 为空、状态为 Pending
  4. kube-scheduler 发现未调度的 Pod,经过预选(Filter)与优选(Score)选出节点并绑定(binding),写回 spec.nodeName
  5. 目标节点的 kubelet 感知到”有 Pod 分给我了”,调用 CRI 让运行时拉镜像、创建容器,并通过 CNI 配置网络。
  6. kubelet 持续上报状态;Pod 就绪后 Endpoints/EndpointSlice 被更新,Service 才会把流量转发过来。

每一步都是异步的 watch + 调谐,任何一环失败都会被下一轮调谐纠正,这就是 K8s “最终一致”的来源。

3. 核心对象逐个讲#

3.1 Pod:最小调度单元#

Pod 是最小的调度与运行单元,可理解为”一个或多个共享网络命名空间的容器组合”。

为什么不直接调度容器?现实里经常需要多个进程紧密协作(日志采集、代理、配置热加载),它们要共享网络、存储与生命周期。打包成 Pod 后,调度、IP 分配、卷挂载与生命周期管理都被统一处理。Pod 内所有容器共享同一网络命名空间:共享 IP 与端口空间,可用 localhost 互访,共享挂载的 Volume;但不共享文件系统,进程空间也各自独立。最常见的是 sidecar 模式:

apiVersion: v1
kind: Pod
metadata: { name: web-with-sidecar, labels: { app: web } }
spec:
restartPolicy: Always
containers:
- name: web
image: nginx:1.27
volumeMounts: [{ name: logs, mountPath: /var/log/nginx }]
- name: log-shipper
image: busybox:1.36
command: ["sh", "-c", "tail -F /logs/access.log"]
volumeMounts: [{ name: logs, mountPath: /logs }]
volumes:
- name: logs
emptyDir: {}

restartPolicy 有三档:Always(默认,常驻服务)、OnFailure(非 0 退出才重启,适合一次性任务)、Never,它作用于整个 Pod。Pod 相位包括 PendingRunningSucceededFailedUnknown

生产环境几乎不直接创建裸 Pod:裸 Pod 没有自愈能力(节点故障或删除后不会重建),应交给 Deployment / StatefulSet / DaemonSet。

3.2 Deployment:无状态应用的副本管理#

Deployment 描述”我要 N 个副本的某个 Pod 模板,且支持滚动更新与回滚”。

apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
replicas: 3
selector:
matchLabels: { app: web }
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: nginx:1.27
ports: [{ containerPort: 80 }]
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }

关键点:

  • Deployment 不直接创建 Pod:层级是 Deployment → ReplicaSet → Pod
  • 修改 Pod 模板(通常是镜像)会生成新的 ReplicaSet,旧的保留并缩容到 0,这是滚动更新的基础。
  • selector 必须匹配 template.metadata.labels,否则 apiserver 直接拒绝。
  • maxSurge:更新期间最多超出期望副本数多少;maxUnavailable:最多不可用多少。设为 0 表示必须先起新、就绪后再停旧,容量不下降但更新更慢。

回滚依赖 ReplicaSet 的历史,默认保留 revisionHistoryLimit=10 个。

3.3 Service:稳定的服务入口#

Pod IP 是临时的,Service 提供稳定的虚拟 IP(ClusterIP)与 DNS 名字,并通过 label selector 找到后端 Pod,维护 Endpoints。

apiVersion: v1
kind: Service
metadata: { name: web }
spec:
type: ClusterIP
selector: { app: web }
ports: [{ port: 80, targetPort: 80 }]

四种类型对比:

类型访问范围典型用途是否依赖云厂商
ClusterIP仅集群内部服务间互调(默认)
NodePort集群外,节点 IP + 30000-32767测试环境、临时暴露
LoadBalancer集群外,云负载均衡器生产对外暴露是(裸机需 MetalLB)
ExternalName无代理,返回 DNS CNAME映射集群外服务

selector 选中 Pod 后,Endpoints controller 会把**就绪(Ready)**的 Pod IP 写入 Endpoints。没通过 readinessProbe 的 Pod 不会出现,也就收不到流量。默认转发是随机(iptables)/轮询(IPVS),不保证同一客户端落到同一 Pod;需要会话保持可设 sessionAffinity: ClientIPsessionAffinityConfig.clientIP.timeoutSeconds

3.4 Ingress:七层路由#

Service 工作在四层,只能按端口暴露;Ingress 工作在七层,能按域名、路径、Header 路由,并集中管理 TLS。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: { name: web-ingress }
spec:
ingressClassName: nginx
tls:
- hosts: ["sakura-hu.top"]
secretName: sakura-tls
rules:
- host: sakura-hu.top
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: web, port: { number: 80 } } }
- path: /api
pathType: Prefix
backend: { service: { name: api, port: { number: 8080 } } }

要点:

  • Ingress 资源本身只是路由规则,必须有 Ingress Controller(ingress-nginx、Traefik、Envoy Gateway)去实现它。没装 Controller,规则就是一条没人执行的死配置。
  • 后端是 Service 而不是 Pod:Ingress → Service → Endpoints → Pod
  • pathTypePrefixExactImplementationSpecific;TLS 证书通常放在 Secret 里,或用 cert-manager 自动签发。

3.5 ConfigMap 与 Secret:配置与密钥#

ConfigMap 存非敏感配置,Secret 存敏感信息,用法几乎一致,但 Secret 需要 base64 编码、常用于 TLS/镜像拉取,并可开启静态加密。

apiVersion: v1
kind: ConfigMap
metadata: { name: web-config }
data:
APP_ENV: production
nginx.conf: |
server {
listen 80;
location / { root /usr/share/nginx/html; }
}
---
apiVersion: v1
kind: Secret
metadata: { name: db-secret }
type: Opaque
data:
username: cm9vdA== # echo -n 'root' | base64
password: c2VjcmV0MTIz

关键认知:base64 是编码,不是加密。 任何能读到该对象的人一行命令就能解出来:kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d。所以 Secret 的安全性靠 RBAC、etcd 静态加密(EncryptionConfiguration)与外部密钥管理(Vault、Sealed Secrets、External Secrets Operator),而不是 base64。

使用方式有两种,注入环境变量或挂载为文件:

spec:
containers:
- name: web
image: nginx:1.27
envFrom: [{ configMapRef: { name: web-config } }]
env:
- name: DB_PASSWORD
valueFrom: { secretKeyRef: { name: db-secret, key: password } }
volumeMounts: [{ name: config-vol, mountPath: /etc/nginx/conf.d }]
volumes:
- name: config-vol
configMap:
name: web-config
items: [{ key: nginx.conf, path: default.conf }]

用 Volume 挂载时,ConfigMap/Secret 更新后文件会自动同步(kubelet 周期性刷新),而环境变量方式不会更新,必须重建 Pod——这是”改了配置没生效”的最常见原因。常见 Secret 类型有 Opaquekubernetes.io/tlskubernetes.io/dockerconfigjsonkubernetes.io/service-account-tokenkubernetes.io/basic-auth

3.6 Namespace、Label 与 Selector、Annotation#

  • Namespace:逻辑隔离单位,用于区分环境或团队。它不是网络隔离,跨 Namespace 默认互通,要靠 NetworkPolicy。
  • Label:键值对,用于被选择,是 Service、Deployment、NetworkPolicy 的筛选依据,如 app=web
  • SelectormatchLabels 精确匹配,matchExpressions 支持 In/NotIn/Exists/DoesNotExist;kubectl 里写作 -l app=web,tier!=db
  • Annotation:也是键值对,但不可被选择,存放工具元数据,如 ingress 的 nginx.ingress.kubernetes.io/rewrite-target

一句话区分:Label 给机器选,Annotation 给人/工具读。

3.7 存储:从 emptyDir 到 StorageClass#

卷类型生命周期适用场景
emptyDir随 Pod 创建与删除容器间临时共享,缓存、日志中转
hostPath绑定节点单节点测试、访问宿主机路径(生产慎用)
PersistentVolume (PV)集群级资源管理员预先或动态提供的存储
PersistentVolumeClaim (PVC)用户申请声明”我要 10Gi、ReadWriteOnce”
StorageClass供给模板动态创建 PV,指定磁盘类型与回收策略
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: data-pvc }
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources: { requests: { storage: 10Gi } }
---
# 在 Pod 中引用该 PVC
spec:
volumes:
- name: data
persistentVolumeClaim: { claimName: data-pvc }

访问模式有 ReadWriteOnce(单节点读写)、ReadOnlyMany(多节点只读)、ReadWriteMany(多节点读写,取决于存储插件);回收策略有 RetainDeleteRecycle 已废弃)。

串成一句话:Pod 通过 PVC 申请存储,PVC 绑定 PV,PV 由 StorageClass 动态供给。 有状态应用注意:Deployment 的多个副本共享同一 PVC 会出问题,应使用 StatefulSet + volumeClaimTemplates,让每个副本拿到独立 PVC。

4. kubectl 常用命令#

查看、诊断与操作:

Terminal window
kubectl get pods -n prod -o wide # 列表,带节点/IP
kubectl get deploy,svc,ing -n prod # 一次看多种资源
kubectl describe pod web-abc -n prod # 详细事件(排查第一站)
kubectl logs web-abc -n prod -f --tail=100 # 实时日志
kubectl exec -it web-abc -n prod -- sh # 进入容器
kubectl top pod -n prod # 资源占用(需 metrics-server)
kubectl apply -f deploy.yaml # 声明式创建/更新(推荐)
kubectl delete -f deploy.yaml # 按清单删除
kubectl scale deploy/web --replicas=5 -n prod # 手动扩缩容
kubectl port-forward svc/web 8080:80 -n prod # 本地转发调试
kubectl cp prod/web-abc:/etc/nginx/nginx.conf ./nginx.conf
kubectl get events -n prod --sort-by=.metadata.creationTimestamp

常用参数:

参数含义
-o wide输出额外列(节点、IP)
-o yaml / -o json输出完整对象,对照字段
-o jsonpath='{.spec.replicas}'精确取值,写脚本首选
-n NAME指定 Namespace,-A 为全部
-l app=web按 Label 过滤
--watch / -w持续监听变化
--dry-run=client -o yaml只生成 YAML 不提交,用来抄模板

不确定资源怎么写时,先用 kubectl create deploy web --image=nginx --dry-run=client -o yaml 生成骨架,再改成声明式文件。

5. YAML 清单规范#

每个清单至少有四个顶层字段:apiVersionkindmetadataspec(部分对象用 data / rules 替代 spec)。

常见 apiVersion 与 kind 对应:

kindapiVersion
Pod / Service / ConfigMap / Secret / Namespace / PV / PVCv1
Deployment / ReplicaSet / StatefulSet / DaemonSetapps/v1
Job / CronJobbatch/v1
Ingress / NetworkPolicynetworking.k8s.io/v1
HorizontalPodAutoscalerautoscaling/v2
Role / RoleBinding / ClusterRole / ClusterRoleBindingrbac.authorization.k8s.io/v1
PodDisruptionBudgetpolicy/v1

书写注意:缩进用空格不要用 Tab,同级字段缩进必须一致;列表项用 -;字符串里的 : 或特殊字符建议加引号;一个文件可用 --- 分隔多个对象。

查字段最快的方式不是搜索引擎,而是 apiserver 自带的文档:

Terminal window
kubectl explain deploy.spec.strategy.rollingUpdate
kubectl explain pod.spec.containers.lifecycle --recursive
kubectl api-resources # 所有资源及短名
kubectl api-versions # 所有 API 组版本

6. 滚动更新与回滚实战#

web 这个 Deployment 的镜像从 1.27 升到 1.28

Terminal window
kubectl get deploy web -n prod -o jsonpath='{.spec.template.spec.containers[0].image}' # 记录当前版本
kubectl set image deploy/web web=nginx:1.28 -n prod # 更新镜像,触发滚动更新
kubectl rollout status deploy/web -n prod # 观察新 RS 扩容、旧 RS 缩容
kubectl get rs -n prod -l app=web # 查看两个 RS 的副本数变化

kubectl set image 本质是修改 spec.template,Deployment controller 因此创建新 ReplicaSet,并按 maxSurge / maxUnavailable 逐步扩新缩旧。过程中可以随时暂停:

Terminal window
kubectl rollout pause deploy/web -n prod # 先滚一部分,观察指标与错误率
kubectl rollout resume deploy/web -n prod # 没问题再恢复

暂停常用于金丝雀思路。更规范的做法是用两个 Deployment 或引入 Argo Rollouts / Flagger。出问题时回滚:

Terminal window
kubectl rollout history deploy/web -n prod # 所有 revision
kubectl rollout history deploy/web -n prod --revision=3 # 某个 revision 的细节
kubectl rollout undo deploy/web -n prod # 回到上一个可用版本
kubectl rollout undo deploy/web -n prod --to-revision=1 # 回到指定 revision

两点补充:给 revision 加可读备注用 kubectl annotate deploy/web kubernetes.io/change-cause="upgrade nginx to 1.28"rollout undo 的实现是把旧 ReplicaSet 的 Pod 模板重新写回 Deployment,会再生成一个新 revision——不是”回到过去”,而是”用旧模板再滚一次”。

7. 健康检查#

三种探针职责不同:

探针回答的问题失败后果
livenessProbe进程还活着吗?达阈值后重启容器
readinessProbe现在能接流量吗?从 Endpoints 摘除,不重启
startupProbe启动完了吗?未成功前抑制 liveness/readiness
spec:
containers:
- name: web
image: nginx:1.28
startupProbe:
httpGet: { path: /, port: 80 }
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet: { path: /healthz, port: 80 }
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet: { path: /ready, port: 80 }
periodSeconds: 5
failureThreshold: 2

三种探测方式:httpGet(返回 200-399 视为成功,最常用)、tcpSocket(端口能连上即成功)、exec(命令退出码为 0 即成功)。关键参数:initialDelaySeconds(首检延迟)、periodSeconds(周期)、timeoutSeconds(超时)、successThreshold(连续成功几次算就绪)、failureThreshold(连续失败几次算失败)。

两个经典翻车场景:

  1. 探针太激进反复重启。 应用冷启动要 60 秒,initialDelaySeconds 只给 5 秒,liveness 阈值又小,容器刚启动就被判”死了”并重启,陷入 CrashLoopBackOff。正确做法是用 startupProbe 兜住启动期,让 liveness 在启动完成后才开始计时。
  2. 没配 readinessProbe,流量打到未就绪 Pod。 容器一 Running 就进 Endpoints,但应用还在加载缓存或连数据库,用户直接 500。正确做法是给 readiness 一个能真实反映”可服务”的检查点。

反过来说,livenessProbe 也不该依赖外部依赖(比如数据库):数据库抖一下会让所有 Pod 被判死并同时重启,造成雪崩。记住:“我活着”用 liveness,“我能干活”用 readiness。

8. 资源管理#

  • requests:调度依据,也是 cgroup 的 cpu.shares 参考。调度器把 Pod 放到”剩余可分配 requests 足够”的节点。
  • limits:运行上限。CPU 超限被节流(throttle),内存超限直接 OOMKilled
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { cpu: 500m, memory: 512Mi }

单位:CPU 的 1 = 1 个核心,1000m = 1 核,250m = 0.25 核,CPU 是可压缩资源;内存 Mi = 1024×1024 字节,M = 1000×1000 字节,Gi 同理,内存是不可压缩资源,超了只能杀。

QoS 等级由 requests/limits 的配置方式决定:

QoS 等级条件驱逐优先级
Guaranteed每个容器都设了 cpu/memory 且 requests 等于 limits最低(最后被驱逐)
Burstable设置了 requests,但不满足 Guaranteed中等
BestEffort完全没设 requests/limits最高(最先被驱逐)

节点内存压力大时,kubelet 按 BestEffort → Burstable → Guaranteed 的顺序驱逐;同一等级内,超出 requests 越多的越先被驱逐。

实践建议:requests 按稳态占用设置,limits 按峰值容忍设置,两者别相等(失去弹性)也别差太多(突发就被节流);关键服务设 Guaranteed,批处理设 Burstable 即可。排障方面:kubectl describe pod 出现 Last State: Terminated, Reason: OOMKilled 说明内存超限,结合 kubectl top pod 与容器内真实曲线判断是 limits 太小还是内存泄漏;CPU 节流则看 cgroup 的 cpu.statnr_throttled / throttled_time

9. 自动扩缩容#

HorizontalPodAutoscaler(HPA)根据指标自动调整 Deployment / StatefulSet / ReplicaSet 的副本数,只做水平扩缩(加减副本),不改 resources。原理是 HPA controller 每 15 秒(默认)拉一次指标,计算 期望副本数 = ceil(当前副本数 × 当前指标 / 目标指标),再更新 Deployment 的 replicas

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: web-hpa, namespace: prod }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: web }
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource: { name: cpu, target: { type: Utilization, averageUtilization: 60 } }
- type: Resource
resource: { name: memory, target: { type: Utilization, averageUtilization: 75 } }
behavior:
scaleDown: { stabilizationWindowSeconds: 300 }

要点:

  • HPA 依赖 metrics-server 提供 CPU/内存指标。没装会报 unable to get metricskubectl top 也无法工作。
  • averageUtilization: 60 指所有 Pod 的平均 CPU 使用率相对 requests 达到 60% 时扩容,因此必须设置 requests,否则无法计算百分比。
  • 自定义指标(QPS、队列长度)需要 Prometheus Adapter 或 KEDA,通过 custom / external metrics API 暴露。
  • behavior.stabilizationWindowSeconds 是缩容冷却窗口,防止指标抖动导致副本数上下横跳。
  • 节点层面还需扩容:Cluster Autoscaler(云上)或 Karpenter 在 Pod 因资源不足 Pending 时增加节点。
  • 垂直方向有 VPA,但调整 requests 需要重建 Pod,与 HPA 用在同一指标上会互相打架。

10. Helm:包管理#

原生 YAML 的问题是同一套应用要部署到多个环境,镜像 tag、副本数、域名都不同,只能复制多份或手工改,容易漂移。Helm 用模板 + 变量解决,并提供版本化的发布管理。

Chart 目录结构:

mychart/
├── Chart.yaml # 元信息:name、version、appVersion、依赖
├── values.yaml # 默认变量
├── charts/ # 子 Chart(依赖)
└── templates/
├── deployment.yaml
├── service.yaml
├── _helpers.tpl # 可复用的模板片段
└── NOTES.txt # 安装后提示信息

模板渲染与对应的 values.yaml

# templates/deployment.yaml(节选)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
{{- toYaml .Values.resources | nindent 12 }}
---
# values.yaml
replicaCount: 3
image: { repository: nginx, tag: "1.28" }
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }

常用命令:

Terminal window
helm create mychart # 生成骨架
helm template mychart -f values-prod.yaml # 本地渲染,不安装(调试首选)
helm install web ./mychart -n prod --create-namespace
helm upgrade web ./mychart -n prod -f values-prod.yaml
helm rollback web 2 -n prod # 回滚到第 2 个 revision
helm list -n prod && helm history web -n prod # 查看已发布与发布历史
helm uninstall web -n prod
helm lint ./mychart # 静态检查

自己写一个简单 Chart 的要点:

  1. Chart.yamlversion 是 Chart 版本,appVersion 是应用版本,两者独立。
  2. 所有可变量都放进 values.yaml,模板里用 .Values.xxx 引用,不要硬编码。
  3. _helpers.tpl 定义 fullnamelabels 等公共片段,避免重复;注意缩进陷阱,配合 nindent / indent / toYaml 使用。
  4. 先用 helm template 在本地渲染检查,确认输出无误再 install;敏感值不要写进 values.yaml,用 --set 或外部 Secret 注入。

Helm 3 相比 Helm 2 移除了 Tiller,权限直接走调用者的 kubeconfig,默认更安全。

11. 本地实践#

方案定位多节点适用场景
minikube单机完整集群,功能全可选学习、验证多组件功能
kindDocker 里跑 K8s 节点容器支持CI、本地测试、K8s 自身开发
k3s轻量发行版,单二进制支持边缘、树莓派、轻量实验
Docker Desktop K8s桌面一键开启单节点日常开发最省事

用 kind 起一个本地集群,并部署第一个应用:

Terminal window
brew install kind kubectl # 安装
kind create cluster --name dev # 创建集群(默认单节点)
kubectl cluster-info --context kind-dev && kubectl get nodes
kubectl create deployment hello --image=nginx:1.28 --dry-run=client -o yaml > hello.yaml
kubectl expose deployment hello --port=80 --type=NodePort --dry-run=client -o yaml >> hello.yaml
kubectl apply -f hello.yaml
kubectl rollout status deploy/hello && kubectl get pods,svc
kubectl port-forward svc/hello 8080:80 # kind 里用 port-forward 或 Ingress 访问
curl -I http://localhost:8080
kubectl delete -f hello.yaml && kind delete cluster --name dev

若要在 kind 里验证 Ingress,还需额外安装 ingress-nginx,并给节点配置 extraPortMappings 映射 80/443。

12. 生产必备#

  • 命名空间隔离:按团队或环境划分 Namespace,配合 ResourceQuota 与 LimitRange 限制总量与默认值。
  • RBAC 最小权限:用 Role/RoleBinding(命名空间级)或 ClusterRole/ClusterRoleBinding(集群级)精确授权,别把 cluster-admin 发给服务账号。
  • NetworkPolicy:默认所有 Pod 互通;一旦某 Pod 被任意策略选中,就变成”默认拒绝 + 显式放行”。生产建议先做默认拒绝入站,再逐条开放。
  • 镜像拉取密钥:私有仓库用 kubernetes.io/dockerconfigjson 类型的 Secret,挂到 ServiceAccount 的 imagePullSecrets 上。
  • PodDisruptionBudget:声明自愿中断时至少保留几个可用副本(如 minAvailable: 2),防止节点维护时把服务全部驱逐。
  • 日志与监控:指标用 Prometheus + Grafana,日志用 Loki(或 ELK),链路用 OpenTelemetry;采集走 sidecar 或 DaemonSet,应用只输出到 stdout。
  • 其他:镜像别用 latest tag;配好探针;设置 terminationGracePeriodSeconds 并处理 SIGTERM 优雅退出;用 topologySpreadConstraints 打散副本;给关键命名空间设 PriorityClass。

13. 什么时候不该用 K8s#

K8s 强大但要付代价:学习曲线陡、组件多、网络与存储抽象复杂、排障链路长、需要有人持续维护。以下情况值得认真考虑替代方案:

  • 团队小、没有专职运维:一两个人维护集群的隐性成本很高,直接用托管容器服务能少踩坑。
  • 单机就扛得住:流量不大、可用性要求不苛刻时,Docker Compose 足够。
  • 只是定时任务或简单 API:Serverless(云函数、Cloud Run、Cloudflare Workers)零运维,按调用计费。
  • 需要精细调度但不是容器场景:Nomad 更简单,适合混合编排容器与普通进程;边缘与资源受限环境可用 k3s、KubeEdge。

成本权衡要算三笔账:人力账(谁维护集群与升级)、资源账(控制平面与系统组件自身占用,托管集群按节点收费)、机会成本(时间花在 K8s 还是业务上)。一个经验法则是:如果容器数量少于十几个、没有强制的多租户隔离与弹性需求,先别上 K8s。

14. 面试高频问题#

Pod 和容器的区别? 容器是最小运行单元,Pod 是最小调度单元。一个 Pod 可含多个容器,共享网络命名空间与 Volume,共享生命周期,容器间可用 localhost 互访,端口不能重复。

Deployment 和 StatefulSet 的区别? Deployment 面向无状态应用:Pod 可互换、随机名字、不需要稳定存储、支持并行滚动更新。StatefulSet 面向有状态应用:Pod 名字稳定有序(db-0db-1)、网络标识稳定(配合 Headless Service)、每个 Pod 有独立 PVC、按顺序创建与删除。

Service 如何实现负载均衡? Service 通过 label selector 选中 Pod,Endpoints controller 把就绪 Pod 的 IP 写入 Endpoints,kube-proxy 在节点上把 ClusterIP 的流量按 iptables/IPVS 规则分发到后端。默认是四层转发;ClusterIP 是虚拟 IP,不绑定网卡,只存在于节点规则中。

三种探针的区别? liveness 决定”要不要重启”,readiness 决定”要不要接流量”,startup 决定”启动是否完成(用于抑制前两者)”。

滚动更新的原理?spec.template 后 Deployment 创建新 ReplicaSet,按 maxSurge/maxUnavailable 逐步扩新缩旧;新 Pod 通过 readiness 后才接流量,全部就绪后旧 RS 缩到 0(保留历史以便回滚)。rollout undo 即把旧 RS 的模板写回。

requests 和 limits 的区别? requests 参与调度、决定 QoS 与驱逐优先级;limits 是运行硬上限,CPU 超限被节流,内存超限被 OOMKilled。

Pod 一直 Pending 怎么排查?kubectl describe pod 的 Events。常见原因:资源不足无法满足 requests、节点有 Taint 且缺少 Toleration、nodeSelector/affinity 不匹配、PVC 未绑定、超过 ResourceQuota。

CrashLoopBackOff 怎么排查?kubectl logs --previous 看上次崩溃日志,kubectl describe pod 看退出码与原因(OOMKilled / 探针失败 / 命令写错 / 配置缺失)。退出码 137 通常是 OOMKilled 或 SIGKILL。

Service 和 Ingress 的区别? Service 工作在一到四层,暴露 IP + 端口,通常对应一个后端;Ingress 工作在七层,按域名/路径路由到不同 Service,还能统一管理 TLS。

ConfigMap 更新后为什么没生效? 环境变量方式在容器启动时已固化,不会随 ConfigMap 更新,必须 kubectl rollout restart;Volume 挂载方式则由 kubelet 周期性同步。

15. 命令与资源对象速查表#

常用命令:

命令用途
kubectl get RES -n NS -o wide列表查看
kubectl describe RES NAME详情与事件
kubectl logs POD -f --tail=100日志
kubectl exec -it POD -- sh进入容器
kubectl apply -f FILE声明式应用
kubectl delete -f FILE删除
kubectl scale deploy/NAME --replicas=N扩缩容
kubectl set image deploy/NAME C=IMG更新镜像
kubectl rollout status deploy/NAME查看发布进度
kubectl rollout undo deploy/NAME回滚
kubectl port-forward svc/NAME 8080:80本地端口转发
kubectl top pod / kubectl top node资源占用
kubectl cp POD:/path ./local文件拷贝
kubectl explain RES.FIELD查字段说明
kubectl api-resources资源清单

资源对象:

资源短名作用
Podpo最小调度单元
Deploymentdeploy无状态副本管理 + 滚动更新
ReplicaSetrs维持副本数(由 Deployment 管理)
StatefulSetsts有状态应用,稳定标识与独立存储
DaemonSetds每节点跑一份(日志、监控 Agent)
Job / CronJobcj一次性任务 / 定时任务
Servicesvc稳定入口与四层负载均衡
Ingressing七层路由
ConfigMap / Secretcm非敏感配置 / 敏感信息(base64,非加密)
Namespacens逻辑隔离
PersistentVolume / PersistentVolumeClaimpv / pvc集群级存储资源 / 存储申请
StorageClasssc动态存储供给模板
HorizontalPodAutoscalerhpa自动扩缩容
NetworkPolicynetpol网络访问控制
Role / RoleBinding-命名空间级权限
ClusterRole / ClusterRoleBinding-集群级权限
PodDisruptionBudgetpdb自愿中断的可用性保障
ResourceQuota / LimitRangequota命名空间总量限制 / 默认值

字段速查最常见的几项:spec.replicas(期望副本数)、spec.selector(选择后端 Pod 的 Label)、spec.template(Pod 模板)、spec.strategy.rollingUpdate(maxSurge / maxUnavailable)、spec.containers[].resources(requests / limits)、spec.nodeName(Pod 绑定的节点)、restartPolicy(Always / OnFailure / Never)、terminationGracePeriodSeconds(优雅退出宽限期)。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Kubernetes 核心概念与实战入门
https://sakura-hu.top/posts/devops/kubernetes核心概念与实战/
作者
Sakura
发布于
2026-09-11
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Sakura
Hello, I'm Sakura.
公告
欢迎来到我的博客! 我是Sakura,祝你拥有美好的一天~
分类
标签
最新动态
站点统计
文章
21
分类
7
标签
35
总字数
85,101
运行时长
0
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.16.7
文章许可
CC BY-NC-SA 4.0
文章目录