Kubernetes 核心概念与实战入门
- 1Docker 入门与实战
- 2Docker Compose 多容器编排实战
- 3Kubernetes 核心概念与实战入门本文
- 4CI/CD 持续集成与部署实战
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 运行的完整链路
- kubectl 读取 kubeconfig,把 YAML 转成 JSON,通过 HTTPS 发给 kube-apiserver。
- apiserver 依次执行认证(证书 / token)、授权(RBAC)、准入控制(MutatingWebhook 改值 → 校验 → ValidatingWebhook 拦截),把对象写入 etcd。此时 Deployment 已持久化。
- Deployment controller 通过 watch 感知新对象并创建 ReplicaSet;ReplicaSet controller 再创建指定数量的 Pod,此时
spec.nodeName为空、状态为Pending。 - kube-scheduler 发现未调度的 Pod,经过预选(Filter)与优选(Score)选出节点并绑定(binding),写回
spec.nodeName。 - 目标节点的 kubelet 感知到”有 Pod 分给我了”,调用 CRI 让运行时拉镜像、创建容器,并通过 CNI 配置网络。
- kubelet 持续上报状态;Pod 就绪后 Endpoints/EndpointSlice 被更新,Service 才会把流量转发过来。
每一步都是异步的 watch + 调谐,任何一环失败都会被下一轮调谐纠正,这就是 K8s “最终一致”的来源。
3. 核心对象逐个讲
3.1 Pod:最小调度单元
Pod 是最小的调度与运行单元,可理解为”一个或多个共享网络命名空间的容器组合”。
为什么不直接调度容器?现实里经常需要多个进程紧密协作(日志采集、代理、配置热加载),它们要共享网络、存储与生命周期。打包成 Pod 后,调度、IP 分配、卷挂载与生命周期管理都被统一处理。Pod 内所有容器共享同一网络命名空间:共享 IP 与端口空间,可用 localhost 互访,共享挂载的 Volume;但不共享文件系统,进程空间也各自独立。最常见的是 sidecar 模式:
apiVersion: v1kind: Podmetadata: { 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 相位包括 Pending、Running、Succeeded、Failed、Unknown。
生产环境几乎不直接创建裸 Pod:裸 Pod 没有自愈能力(节点故障或删除后不会重建),应交给 Deployment / StatefulSet / DaemonSet。
3.2 Deployment:无状态应用的副本管理
Deployment 描述”我要 N 个副本的某个 Pod 模板,且支持滚动更新与回滚”。
apiVersion: apps/v1kind: Deploymentmetadata: { 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: v1kind: Servicemetadata: { 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: ClientIP 与 sessionAffinityConfig.clientIP.timeoutSeconds。
3.4 Ingress:七层路由
Service 工作在四层,只能按端口暴露;Ingress 工作在七层,能按域名、路径、Header 路由,并集中管理 TLS。
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: { 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。 pathType有Prefix、Exact、ImplementationSpecific;TLS 证书通常放在 Secret 里,或用 cert-manager 自动签发。
3.5 ConfigMap 与 Secret:配置与密钥
ConfigMap 存非敏感配置,Secret 存敏感信息,用法几乎一致,但 Secret 需要 base64 编码、常用于 TLS/镜像拉取,并可开启静态加密。
apiVersion: v1kind: ConfigMapmetadata: { name: web-config }data: APP_ENV: production nginx.conf: | server { listen 80; location / { root /usr/share/nginx/html; } }---apiVersion: v1kind: Secretmetadata: { name: db-secret }type: Opaquedata: 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 类型有 Opaque、kubernetes.io/tls、kubernetes.io/dockerconfigjson、kubernetes.io/service-account-token、kubernetes.io/basic-auth。
3.6 Namespace、Label 与 Selector、Annotation
- Namespace:逻辑隔离单位,用于区分环境或团队。它不是网络隔离,跨 Namespace 默认互通,要靠 NetworkPolicy。
- Label:键值对,用于被选择,是 Service、Deployment、NetworkPolicy 的筛选依据,如
app=web。 - Selector:
matchLabels精确匹配,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: v1kind: PersistentVolumeClaimmetadata: { name: data-pvc }spec: accessModes: ["ReadWriteOnce"] storageClassName: standard resources: { requests: { storage: 10Gi } }---# 在 Pod 中引用该 PVCspec: volumes: - name: data persistentVolumeClaim: { claimName: data-pvc }访问模式有 ReadWriteOnce(单节点读写)、ReadOnlyMany(多节点只读)、ReadWriteMany(多节点读写,取决于存储插件);回收策略有 Retain、Delete(Recycle 已废弃)。
串成一句话:Pod 通过 PVC 申请存储,PVC 绑定 PV,PV 由 StorageClass 动态供给。 有状态应用注意:Deployment 的多个副本共享同一 PVC 会出问题,应使用 StatefulSet + volumeClaimTemplates,让每个副本拿到独立 PVC。
4. kubectl 常用命令
查看、诊断与操作:
kubectl get pods -n prod -o wide # 列表,带节点/IPkubectl 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.confkubectl 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 清单规范
每个清单至少有四个顶层字段:apiVersion、kind、metadata、spec(部分对象用 data / rules 替代 spec)。
常见 apiVersion 与 kind 对应:
| kind | apiVersion |
|---|---|
| Pod / Service / ConfigMap / Secret / Namespace / PV / PVC | v1 |
| Deployment / ReplicaSet / StatefulSet / DaemonSet | apps/v1 |
| Job / CronJob | batch/v1 |
| Ingress / NetworkPolicy | networking.k8s.io/v1 |
| HorizontalPodAutoscaler | autoscaling/v2 |
| Role / RoleBinding / ClusterRole / ClusterRoleBinding | rbac.authorization.k8s.io/v1 |
| PodDisruptionBudget | policy/v1 |
书写注意:缩进用空格不要用 Tab,同级字段缩进必须一致;列表项用 -;字符串里的 : 或特殊字符建议加引号;一个文件可用 --- 分隔多个对象。
查字段最快的方式不是搜索引擎,而是 apiserver 自带的文档:
kubectl explain deploy.spec.strategy.rollingUpdatekubectl explain pod.spec.containers.lifecycle --recursivekubectl api-resources # 所有资源及短名kubectl api-versions # 所有 API 组版本6. 滚动更新与回滚实战
把 web 这个 Deployment 的镜像从 1.27 升到 1.28:
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 逐步扩新缩旧。过程中可以随时暂停:
kubectl rollout pause deploy/web -n prod # 先滚一部分,观察指标与错误率kubectl rollout resume deploy/web -n prod # 没问题再恢复暂停常用于金丝雀思路。更规范的做法是用两个 Deployment 或引入 Argo Rollouts / Flagger。出问题时回滚:
kubectl rollout history deploy/web -n prod # 所有 revisionkubectl 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(连续失败几次算失败)。
两个经典翻车场景:
- 探针太激进反复重启。 应用冷启动要 60 秒,
initialDelaySeconds只给 5 秒,liveness 阈值又小,容器刚启动就被判”死了”并重启,陷入 CrashLoopBackOff。正确做法是用 startupProbe 兜住启动期,让 liveness 在启动完成后才开始计时。 - 没配 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.stat 中 nr_throttled / throttled_time。
9. 自动扩缩容
HorizontalPodAutoscaler(HPA)根据指标自动调整 Deployment / StatefulSet / ReplicaSet 的副本数,只做水平扩缩(加减副本),不改 resources。原理是 HPA controller 每 15 秒(默认)拉一次指标,计算 期望副本数 = ceil(当前副本数 × 当前指标 / 目标指标),再更新 Deployment 的 replicas。
apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: { 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 metrics,kubectl 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/v1kind: Deploymentmetadata: 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.yamlreplicaCount: 3image: { repository: nginx, tag: "1.28" }resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 500m, memory: 256Mi }常用命令:
helm create mychart # 生成骨架helm template mychart -f values-prod.yaml # 本地渲染,不安装(调试首选)helm install web ./mychart -n prod --create-namespacehelm upgrade web ./mychart -n prod -f values-prod.yamlhelm rollback web 2 -n prod # 回滚到第 2 个 revisionhelm list -n prod && helm history web -n prod # 查看已发布与发布历史helm uninstall web -n prodhelm lint ./mychart # 静态检查自己写一个简单 Chart 的要点:
Chart.yaml里version是 Chart 版本,appVersion是应用版本,两者独立。- 所有可变量都放进
values.yaml,模板里用.Values.xxx引用,不要硬编码。 - 用
_helpers.tpl定义fullname、labels等公共片段,避免重复;注意缩进陷阱,配合nindent/indent/toYaml使用。 - 先用
helm template在本地渲染检查,确认输出无误再 install;敏感值不要写进values.yaml,用--set或外部 Secret 注入。
Helm 3 相比 Helm 2 移除了 Tiller,权限直接走调用者的 kubeconfig,默认更安全。
11. 本地实践
| 方案 | 定位 | 多节点 | 适用场景 |
|---|---|---|---|
| minikube | 单机完整集群,功能全 | 可选 | 学习、验证多组件功能 |
| kind | Docker 里跑 K8s 节点容器 | 支持 | CI、本地测试、K8s 自身开发 |
| k3s | 轻量发行版,单二进制 | 支持 | 边缘、树莓派、轻量实验 |
| Docker Desktop K8s | 桌面一键开启 | 单节点 | 日常开发最省事 |
用 kind 起一个本地集群,并部署第一个应用:
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.yamlkubectl expose deployment hello --port=80 --type=NodePort --dry-run=client -o yaml >> hello.yamlkubectl apply -f hello.yamlkubectl rollout status deploy/hello && kubectl get pods,svckubectl port-forward svc/hello 8080:80 # kind 里用 port-forward 或 Ingress 访问curl -I http://localhost:8080kubectl 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。
- 其他:镜像别用
latesttag;配好探针;设置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-0、db-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 | 资源清单 |
资源对象:
| 资源 | 短名 | 作用 |
|---|---|---|
| Pod | po | 最小调度单元 |
| Deployment | deploy | 无状态副本管理 + 滚动更新 |
| ReplicaSet | rs | 维持副本数(由 Deployment 管理) |
| StatefulSet | sts | 有状态应用,稳定标识与独立存储 |
| DaemonSet | ds | 每节点跑一份(日志、监控 Agent) |
| Job / CronJob | cj | 一次性任务 / 定时任务 |
| Service | svc | 稳定入口与四层负载均衡 |
| Ingress | ing | 七层路由 |
| ConfigMap / Secret | cm | 非敏感配置 / 敏感信息(base64,非加密) |
| Namespace | ns | 逻辑隔离 |
| PersistentVolume / PersistentVolumeClaim | pv / pvc | 集群级存储资源 / 存储申请 |
| StorageClass | sc | 动态存储供给模板 |
| HorizontalPodAutoscaler | hpa | 自动扩缩容 |
| NetworkPolicy | netpol | 网络访问控制 |
| Role / RoleBinding | - | 命名空间级权限 |
| ClusterRole / ClusterRoleBinding | - | 集群级权限 |
| PodDisruptionBudget | pdb | 自愿中断的可用性保障 |
| ResourceQuota / LimitRange | quota | 命名空间总量限制 / 默认值 |
字段速查最常见的几项: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(优雅退出宽限期)。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!













