跳至主要内容

Kubernetes 从入门到精通实战指南

Kubernetes 从入门到精通实战指南

面向有编程基础、但是 K8s 新手的开发者。本文以"概念 + 可运行 YAML 示例 + 命令"的方式组织,建议边看边在自己的集群(本地可用 minikube / kind / k3d)里动手跑一遍。


目录

  1. 核心概念:Pod / Node / Service 等
  2. 应用部署基础
  3. 自动扩缩容(HPA / VPA / Cluster Autoscaler)
  4. 定时任务(CronJob)
  5. 配置与敏感信息管理(ConfigMap / Secret)
  6. 配置热加载
  7. GitOps 部署方式
  8. 进入 Pod 排障
  9. 将集群入口暴露到公网
  10. 灰度发布 / 金丝雀发布
  11. 服务网格与 Istio
  12. RBAC 权限管理
  13. 常用命令速查表

1. 核心概念

Kubernetes(简称 K8s)是一个"容器编排系统":你告诉它"我要什么样的最终状态",它负责持续调节集群,让实际状态逼近你声明的状态(这叫声明式 API / 控制循环,是理解一切的钥匙)。

1.1 Cluster(集群)与 Node(节点)

  • Cluster:一组机器(物理机或虚拟机)的集合,由 K8s 统一管理。
  • Node:集群里的一台机器,分两种角色:
    • Control Plane(控制平面):跑着 kube-apiserver(所有操作的入口)、etcd(存储集群状态的数据库)、scheduler(决定 Pod 调度到哪个节点)、controller-manager(跑各种控制循环)。
    • Worker Node:真正跑你业务容器的机器,上面跑着 kubelet(节点代理,负责启停容器)、kube-proxy(负责网络转发)、容器运行时(containerd/CRI-O)。
kubectl get nodes -o wide

1.2 Pod

Pod 是 K8s 里最小的可调度单元,不是容器本身。一个 Pod 里可以有 1 个或多个容器,这些容器:

  • 共享同一个网络命名空间(同一个 IP,容器间可以用 localhost 互访)
  • 可以共享 Volume(挂载卷)
  • 生命周期绑定在一起,一起调度、一起销毁

最常见的模式是 一个 Pod 一个主容器,多容器 Pod 常用于 Sidecar 模式(比如日志采集容器、服务网格代理容器跟主容器共存)。

apiVersion: v1
kind: Pod
metadata:
  name: hello-pod
  labels:
    app: hello
spec:
  containers:
    - name: hello
      image: nginx:1.27
      ports:
        - containerPort: 80

⚠️ 实际生产中你几乎不会直接创建裸 Pod(Pod 挂了不会自动重建),而是通过 Deployment / StatefulSet / Job 等更高级的控制器去管理 Pod。

1.3 Label 与 Selector

Label 是打在资源上的 key-value 标签(如 app: hello),Selector 是用来"筛选"这些标签的查询条件。K8s 几乎所有的关联关系(Service 找 Pod、Deployment 管 Pod)都是靠 Label + Selector 实现的,而不是靠名字硬编码。

1.4 Deployment 与 ReplicaSet

  • ReplicaSet:保证"某个 Label 的 Pod 永远有 N 个副本在运行",少了就拉起来,多了就杀掉。
  • Deployment:在 ReplicaSet 之上封装了滚动更新、版本回滚的能力。你 99% 的时间应该直接使用 Deployment,而不是手写 ReplicaSet。

1.5 Service

Pod 是"会死的"——重建后 IP 会变。Service 提供一个稳定的虚拟 IP(ClusterIP)和 DNS 名字,把流量负载均衡到一组 Pod 上(通过 Label Selector 匹配)。

Service 有几种类型:

类型 说明 典型场景
ClusterIP(默认) 只能集群内部访问 微服务之间互相调用
NodePort 在每个 Node 上开一个固定端口(30000-32767) 简单测试、本地环境
LoadBalancer 云厂商自动创建一个外部负载均衡器 生产环境暴露服务给公网
ExternalName 把 Service 映射到一个外部 DNS 名字 访问集群外的数据库等

1.6 Namespace

Namespace 是集群内的"虚拟隔离区",常用于按团队/环境(dev、staging、prod)划分资源,配合 RBAC 和 ResourceQuota 做权限和资源隔离。

1.7 其他你会经常遇到的对象

  • StatefulSet:管理有状态应用(如数据库),每个 Pod 有稳定的名字和独立存储。
  • DaemonSet:保证每个(或部分)Node 上都跑一个该 Pod 副本,常用于日志采集、监控 agent。
  • Job / CronJob:一次性任务 / 定时任务(第 4 节详述)。
  • ConfigMap / Secret:配置与敏感信息(第 5 节详述)。
  • Ingress:七层(HTTP/HTTPS)路由规则(第 9 节详述)。
  • PersistentVolume (PV) / PersistentVolumeClaim (PVC):持久化存储的抽象。

2. 应用部署基础

2.1 一个完整的 Deployment + Service 示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: myrepo/web-app:1.2.0
          ports:
            - containerPort: 8080
          resources:
            requests:      # 调度时的最低保证
              cpu: "100m"
              memory: "128Mi"
            limits:        # 硬上限,超过会被 OOMKill / CPU 限流
              cpu: "500m"
              memory: "256Mi"
          readinessProbe:  # 判断"是否可以接收流量"
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:   # 判断"是否需要重启"
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
  name: web-app-svc
spec:
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP
kubectl apply -f deployment.yaml
kubectl get deployment,pod,svc
kubectl rollout status deployment/web-app

2.2 滚动更新与回滚

# 修改镜像版本触发滚动更新
kubectl set image deployment/web-app web-app=myrepo/web-app:1.3.0

# 查看更新历史
kubectl rollout history deployment/web-app

# 出问题一键回滚到上一个版本
kubectl rollout undo deployment/web-app

# 回滚到指定版本
kubectl rollout undo deployment/web-app --to-revision=2

可以在 Deployment 的 spec.strategy 里控制滚动更新的节奏:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1   # 更新过程中最多允许多少个 Pod 不可用
      maxSurge: 1         # 更新过程中最多可以多起多少个新 Pod

2.3 资源限制(requests / limits)的重要性

  • requests 影响调度:调度器只会把 Pod 放到剩余资源满足 requests 的 Node 上。
  • limits 影响运行时:CPU 超限会被限流(不会杀进程),内存超限会被 OOMKill
  • 生产建议:一定要设置,否则一个"内存泄漏"的 Pod 可能拖垮整个 Node。

3. 自动扩缩容

3.1 HPA(Horizontal Pod Autoscaler)—— 横向扩缩容 Pod 数量

最常用,前提是集群装了 metrics-server(大部分云厂商托管集群默认已装)。

kubectl top nodes    # 能看到数据说明 metrics-server 正常

基于 CPU/内存的 HPA:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60   # 平均 CPU 使用率超过 60% 就扩容
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 75

也支持基于自定义指标(如 QPS、队列长度)扩缩容,需要额外部署 Prometheus Adapter 或类似的 Custom/External Metrics API 实现:

  metrics:
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "50"
kubectl get hpa -w   # 实时观察扩缩容过程

3.2 VPA(Vertical Pod Autoscaler)—— 纵向调整单个 Pod 的资源规格

自动帮你调整 requests/limits 的数值(而不是加 Pod 数量),适合无法水平扩展的单体应用。VPA 不是 K8s 内置的,需要单独安装 kubernetes/autoscaler 项目里的 VPA 组件。注意 VPA 和 HPA 同时基于 CPU/内存生效时会冲突,一般二选一。

3.3 Cluster Autoscaler —— 自动增减 Node 数量

当 Pod 因为资源不足无法调度(Pending)时,Cluster Autoscaler 会向云厂商申请新的 Node;当 Node 长期空闲时会自动缩容。这个通常由云厂商托管的 K8s 服务提供(如各大云的 CA 插件),需要结合 HPA 一起用才能形成"Pod 数量增加 → 资源不够 → Node 自动增加"的完整链路。

三者关系一句话总结:HPA 管 Pod 数量,VPA 管单个 Pod 大小,Cluster Autoscaler 管 Node 数量。


4. 定时任务(CronJob)

CronJob 底层是周期性创建 JobJob 再创建 Pod 跑完就退出。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-report
spec:
  schedule: "0 2 * * *"          # 标准 cron 表达式,UTC 时区(可用 timeZone 字段指定时区)
  timeZone: "Asia/Singapore"
  concurrencyPolicy: Forbid       # Allow / Forbid / Replace,防止上一次没跑完又叠加一次
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 2             # 失败重试次数
      activeDeadlineSeconds: 600  # 超时强制终止
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: myrepo/report-job:1.0.0
              command: ["python", "run_report.py"]
              envFrom:
                - secretRef:
                    name: report-db-secret
# 手动触发一次调试(不用等定时时间)
kubectl create job --from=cronjob/daily-report manual-test-run

kubectl get cronjob,job,pod
kubectl logs job/manual-test-run

排障要点:concurrencyPolicy 设错、时区设错、activeDeadlineSeconds 太短是最常见的三个坑。


5. 配置与敏感信息管理

5.1 ConfigMap(非敏感配置)

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  config.yaml: |
    feature_flags:
      new_ui: true
    max_connections: 100

5.2 Secret(敏感信息:密码、Token、证书)

# 推荐命令行创建,避免明文写进 git
kubectl create secret generic db-secret \
  --from-literal=DB_USER=admin \
  --from-literal=DB_PASSWORD='S3cretPass!'
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  DB_USER: YWRtaW4=          # base64 编码,注意:base64 不是加密!
  DB_PASSWORD: UzNjcmV0UGFzcyE=

⚠️ Secret 的 base64 只是编码不是加密,能拿到 etcd 访问权限或有 Secret 读权限的人都能还原明文。生产环境务必: 1. 开启 etcd 静态加密(Encryption at Rest); 2. 用 RBAC 严格限制谁能 get/list secrets(见第 12 节); 3. 更进一步可以用 External Secrets Operator 从云厂商的 KMS/Secrets Manager/Vault 同步敏感信息,而不是把明文放进 Git 仓库(哪怕是 base64 编码过的)。

5.3 两种注入方式:环境变量 vs 挂载文件

方式一:环境变量(简单,但不支持热加载

spec:
  containers:
    - name: web-app
      envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: db-secret

方式二:挂载成文件(支持热加载,见第 6 节)

spec:
  containers:
    - name: web-app
      volumeMounts:
        - name: config-volume
          mountPath: /etc/app/config
        - name: secret-volume
          mountPath: /etc/app/secret
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: app-config
    - name: secret-volume
      secret:
        secretName: db-secret

最佳实践建议:非敏感、启动时读取一次的配置用环境变量;需要运行时动态更新、或体积较大(如整份 config.yaml/证书)的配置用挂载文件方式。


6. 配置热加载

K8s 本身不会帮你的应用"重新读取"配置,它只负责把新内容同步到磁盘(或触发 Pod 重启),"reload"这个动作是应用自己的责任。常见三种方案:

方案 A:挂载文件 + 应用自己 watch 文件变化

ConfigMap/Secret 挂载成 Volume 后,K8s kubelet 会定期(默认约 1 分钟内,取决于 kubelet 同步周期)自动更新容器里挂载的文件内容(symlink 原子切换),但环境变量方式不会更新。所以:

  • 你的应用需要自己用 fsnotify(Go)/ watchdog(Python)等库监听配置文件变化,检测到变化后重新加载。
  • 常见做法:Nginx 场景下容器内跑一个轻量脚本监听文件变化后执行 nginx -s reload

方案 B:用 Reloader 类工具自动触发滚动重启

如果你的应用不支持自己热加载,最简单粗暴又可靠的方式是:ConfigMap/Secret 一旦变化,自动帮你做一次 Deployment 滚动重启(相当于优雅重启,不会有 downtime,因为是滚动进行的)。业界常用开源工具 stakater/Reloader

metadata:
  annotations:
    reloader.stakater.com/auto: "true"   # 加在 Deployment 上,ConfigMap/Secret 变了就自动滚动重启

方案 C:Sidecar 容器负责 reload

比如经典的 configmap-reload sidecar(Prometheus 社区常用):主容器(如 Prometheus)不需要改代码,sidecar 监听挂载目录变化后调用主容器的 reload HTTP 接口(如 POST /-/reload)。

选择建议:能改代码就优先方案 A(最优雅);不方便改代码、且能接受"重启换配置"就用方案 B(最省事);需要对第三方镜像做热加载,且第三方本身提供 reload API,就用方案 C。


7. GitOps 部署方式

7.1 什么是 GitOps

传统 CI/CD 是"CI 流水线里跑 kubectl apply 主动推送到集群"(Push 模式)。GitOps 反过来:Git 仓库是唯一真相来源(Single Source of Truth),集群内跑一个 Agent(如 ArgoCD/Flux)主动拉取(Pull 模式) Git 里声明的期望状态,持续对比、自动同步到集群,任何人为的手动 kubectl edit 改动都会被自动纠正回 Git 里声明的状态。

好处: - 部署历史 = Git 提交历史,天然可审计、可回滚(git revert 即可回滚)。 - 不需要把集群的 kubeconfig 凭据交给 CI 系统,减少攻击面。 - 多集群、多环境同步管理更方便。

7.2 以 ArgoCD 为例的典型结构

仓库结构(常见的"环境分层"模式):

gitops-repo/
├── apps/
│   └── web-app/
│       ├── base/
│       │   ├── deployment.yaml
│       │   ├── service.yaml
│       │   └── kustomization.yaml
│       └── overlays/
│           ├── dev/kustomization.yaml
│           ├── staging/kustomization.yaml
│           └── prod/kustomization.yaml

ArgoCD 的 Application 声明(本身也是一个 K8s 资源,同样可以放进 Git 里,也就是所谓的 "App of Apps" 模式):

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app-prod
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/gitops-repo.git
    targetRevision: main
    path: apps/web-app/overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: web-app-prod
  syncPolicy:
    automated:
      prune: true       # Git 里删掉的资源,集群里也自动删除
      selfHeal: true     # 有人手动改了集群,自动纠正回 Git 声明的状态
    syncOptions:
      - CreateNamespace=true

7.3 典型工作流

  1. 开发者改代码 → CI 构建镜像 → 推送到镜像仓库,打上版本 tag。
  2. CI(或专门的"镜像更新机器人",如 ArgoCD Image Updater)修改 GitOps 仓库里的镜像 tag 并提交。
  3. ArgoCD 检测到 Git 变化 → 自动(或人工点击 Sync)→ 同步到目标集群。
  4. ArgoCD UI/CLI 里可以看到实时的同步状态、资源健康状态、diff。
argocd app sync web-app-prod
argocd app get web-app-prod

备选方案:Flux CD(更"纯粹"的 Kubernetes-native 方式,配置即 CRD)、Kustomize(免模板引擎的分环境差异化配置工具,常与 ArgoCD/Flux 搭配)、Helm(模板化打包工具,适合复杂度较高的应用/第三方组件)。


8. 进入 Pod 排障

8.1 排障标准流程(自上而下)

# 1. 看 Pod 整体状态:Pending / CrashLoopBackOff / ImagePullBackOff 等
kubectl get pods -n <namespace> -o wide

# 2. 看详细事件(90% 的问题这里能看出线索:调度失败、镜像拉取失败、探针失败等)
kubectl describe pod <pod-name> -n <namespace>

# 3. 看容器日志
kubectl logs <pod-name> -n <namespace>
kubectl logs <pod-name> -c <container-name> -n <namespace>   # 多容器 Pod 指定容器
kubectl logs <pod-name> --previous                            # 看上一次崩溃前的日志(CrashLoopBackOff 常用)
kubectl logs -f <pod-name>                                     # 持续跟踪

# 4. 进容器内部交互式排障
kubectl exec -it <pod-name> -- /bin/sh     # 或 /bin/bash,取决于镜像是否有
kubectl exec -it <pod-name> -c <container-name> -- /bin/sh

# 5. 端口转发到本地,绕过 Service 直接测试单个 Pod
kubectl port-forward pod/<pod-name> 8080:8080

# 6. 看集群级事件(比如 Node 压力、驱逐)
kubectl get events -n <namespace> --sort-by='.lastTimestamp'

8.2 常见故障与定位思路

现象 常见原因
Pending 资源不足调度不了 / 有 Taint 没打 Toleration / PVC 绑定不上
ImagePullBackOff 镜像名/tag 写错、私有仓库没配 imagePullSecrets、网络不通
CrashLoopBackOff 应用启动即崩溃,用 --previous 看崩溃日志;也可能是探针配置太严格
Running 但服务不可用 Readiness Probe 一直没过;Service selector 和 Pod label 对不上
OOMKilled 内存 limits 设太小,kubectl describe pod 里能看到 Reason: OOMKilled

8.3 排障"神器":镜像里没有 shell 怎么办?

很多生产镜像是 distroless(没有 shell、没有调试工具),这时用 kubectl exec 会失败。用 临时调试容器(Ephemeral Container)

kubectl debug -it <pod-name> --image=busybox:1.36 --target=<container-name>

这会在同一个 Pod 的网络/进程命名空间里附加一个带 shell 的临时容器,不影响原容器运行,是排障 distroless 镜像的标准做法(K8s 1.25+ 默认可用)。

也可以对整个 Node 做排障:

kubectl debug node/<node-name> -it --image=busybox:1.36

9. 将集群入口暴露到公网

9.1 三种暴露层级

公网用户
   │
   ▼
┌─────────────────────┐
│  LoadBalancer(云厂商 LB) │  ← 四层(TCP/UDP),一个 Service 对应一个公网 IP
└─────────────────────┘
   │
   ▼
┌─────────────────────┐
│  Ingress Controller   │  ← 七层(HTTP/HTTPS),一个入口按域名/路径分流到多个 Service
└─────────────────────┘
   │
   ▼
   Service (ClusterIP) → Pod

9.2 方式一:Service type=LoadBalancer(最简单,但每个 Service 一个公网 IP,成本高)

apiVersion: v1
kind: Service
metadata:
  name: web-app-lb
spec:
  type: LoadBalancer
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 8080
kubectl get svc web-app-lb   # EXTERNAL-IP 字段就是公网可访问的地址

9.3 方式二:Ingress(推荐,多个域名/服务共用一个公网入口)

先装一个 Ingress Controller(最常用的是 ingress-nginx,云厂商也常有自己的实现):

# 以 ingress-nginx 为例(实际版本号请以官方文档为准)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

再声明路由规则:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: "letsencrypt-prod"   # 配合 cert-manager 自动签发 HTTPS 证书
spec:
  ingressClassName: nginx
  tls:
    - hosts: ["app.example.com"]
      secretName: web-app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-app-svc
                port:
                  number: 80

HTTPS 证书推荐用 cert-manager 配合 Let's Encrypt 自动签发和续期,不需要手动管理证书文件。

9.4 补充:Gateway API(新一代标准,逐渐取代 Ingress)

Kubernetes 社区正在推广更强大、更标准化的 Gateway APIGateway + HTTPRoute 等资源),语义比 Ingress 更清晰、原生支持更复杂的路由/流量拆分场景。如果你的 Ingress Controller/云厂商已支持,新项目可以优先考虑评估使用。


10. 灰度发布/金丝雀发布

灰度发布的核心思路:让一小部分流量先走新版本,观察指标(错误率、延迟)正常后再逐步扩大比例,出问题随时回滚。 常见 4 种实现方式,复杂度从低到高:

方式 1:副本比例灰度(最土但最简单,不需要额外组件)

同一个 Service 下同时存在 v1v2 两个 Deployment,Service 的 selector 只匹配公共 label(如 app: web-app),流量会按 Pod 数量比例随机负载均衡:

# v1: 9 个副本, v2: 1 个副本 → 约 10% 流量走 v2
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app-v1
spec:
  replicas: 9
  selector: { matchLabels: { app: web-app, version: v1 } }
  template:
    metadata: { labels: { app: web-app, version: v1 } }
    spec: { containers: [{ name: web-app, image: myrepo/web-app:1.2.0 }] }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app-v2
spec:
  replicas: 1
  selector: { matchLabels: { app: web-app, version: v2 } }
  template:
    metadata: { labels: { app: web-app, version: v2 } }
    spec: { containers: [{ name: web-app, image: myrepo/web-app:1.3.0 }] }
---
apiVersion: v1
kind: Service
metadata: { name: web-app-svc }
spec:
  selector: { app: web-app }   # 不带 version,两个版本都匹配
  ports: [{ port: 80, targetPort: 8080 }]

缺点:只能按副本数粗略控制比例(10 副本才能精确到 10% 粒度),无法按用户/Header 精确分流。

方式 2:Ingress-nginx 的 Canary 注解

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"   # 10% 流量走 canary
    # 也支持按 header 精确分流:
    # nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service: { name: web-app-svc-v2, port: { number: 80 } }

方式 3:Argo Rollouts(专门做渐进式发布的控制器)

比原生 Deployment 更强大,支持自动分析指标(对接 Prometheus)、自动暂停/回滚:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-app
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 50
        - pause: { duration: 5m }
        - setWeight: 100
  # 其余字段与 Deployment 基本一致

方式 4:Istio VirtualService 精确流量拆分(见第 11 节)

服务网格方案能做到按百分比精确拆分按 Header/Cookie 定向到指定用户,是最灵活的方案,但引入复杂度也最高。

选择建议:中小团队/简单场景用方式 1 或 2 足够;对发布过程有严格的自动化验证需求用 Argo Rollouts;已经在用 Service Mesh 的团队直接用方式 4。


11. 服务网格与 Istio

11.1 什么是服务网格(Service Mesh)

微服务多了之后,"服务间调用"会遇到一堆跨服务的通用问题:负载均衡策略、重试、超时、熔断、mTLS 加密、调用链追踪、流量拆分……如果每个服务自己用代码实现,会有大量重复劳动且难以统一治理。

服务网格的核心思想:把这些"跟业务逻辑无关的网络治理能力"从应用代码里剥离出来,下沉到基础设施层——具体做法是给每个 Pod 自动注入一个 Sidecar 代理容器(Istio 用的是 Envoy),所有进出该 Pod 的流量都会先经过这个代理。业务代码完全无感知。

┌──────────── Pod ────────────┐
│  ┌────────┐   ┌──────────┐  │
│  │  App    │◄─►│  Envoy   │◄─┼──► 其他服务的 Envoy
│  │ 容器    │   │ Sidecar  │  │
│  └────────┘   └──────────┘  │
└──────────────────────────────┘
              ▲
              │ 统一由 Istiod(控制面)下发规则

11.2 Istio 的三大能力

  1. 流量管理:灰度发布、A/B 测试、超时/重试策略、熔断、故障注入。
  2. 安全:服务间自动 mTLS 加密(零信任网络的基础)、细粒度的服务间访问授权(AuthorizationPolicy)。
  3. 可观测性:自动产生统一格式的指标(延迟、成功率、QPS)、分布式调用链追踪、服务拓扑图,不需要改一行业务代码

11.3 典型应用场景举例

场景 A:精确到 1% 的灰度发布 + 按 Header 定向

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata: { name: web-app }
spec:
  host: web-app-svc
  subsets:
    - name: v1
      labels: { version: v1 }
    - name: v2
      labels: { version: v2 }
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata: { name: web-app }
spec:
  hosts: ["web-app-svc"]
  http:
    - match:
        - headers:
            x-canary-user: { exact: "true" }   # 内部测试用户强制走 v2
      route:
        - destination: { host: web-app-svc, subset: v2 }
    - route:
        - destination: { host: web-app-svc, subset: v1, weight: 99 }
        - destination: { host: web-app-svc, subset: v2, weight: 1 }

场景 B:混沌工程 —— 主动注入故障验证系统容错能力

  http:
    - fault:
        delay: { percentage: { value: 10 }, fixedDelay: 3s }   # 10% 请求人为延迟 3 秒
        abort: { percentage: { value: 5 }, httpStatus: 500 }    # 5% 请求人为返回 500
      route:
        - destination: { host: web-app-svc }

不需要改代码就能验证"下游超时/报错时,上游服务是否有正确的重试/降级逻辑"。

场景 C:零信任安全 —— 强制服务间 mTLS + 细粒度授权

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata: { name: default, namespace: payment }
spec:
  mtls: { mode: STRICT }   # payment 命名空间下所有服务间通信强制加密,明文流量直接拒绝
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata: { name: payment-allow, namespace: payment }
spec:
  selector: { matchLabels: { app: payment-service } }
  rules:
    - from:
        - source: { principals: ["cluster.local/ns/checkout/sa/checkout-sa"] }   # 只允许 checkout 服务调用
      to:
        - operation: { methods: ["POST"], paths: ["/api/charge"] }

场景 D:多集群/多云统一流量治理、跨集群故障转移——大型企业常见诉求,Istio 支持跨多集群组建统一网格。

11.4 什么时候该引入 Istio,什么时候不该

适合引入:服务数量较多(一般认为 >15-20 个微服务开始收益明显)、有严格的零信任安全合规要求、需要精细化灰度/流量治理、需要统一可观测性但不想改代码。

不建议引入:服务数量很少(几个服务)、团队没有专职的平台/SRE 团队维护、对延迟极度敏感(Sidecar 会增加一定的网络跳数和延迟开销)。Istio 本身运维复杂度不低,"为了用而用"往往得不偿失,更轻量的替代方案是 Linkerd(更简单,功能less all-in-one)。


12. RBAC 权限管理

12.1 核心概念

RBAC(Role-Based Access Control)回答"谁能对什么资源做什么操作"这个问题,四个核心对象:

对象 作用范围 说明
Role 单个 Namespace 内 定义一组权限规则(能对哪些资源做哪些动词操作)
ClusterRole 整个集群(或作为 Namespace 内可复用的模板) 同 Role,但可以授权集群级资源(如 Node、PV),也可跨命名空间复用
RoleBinding 单个 Namespace 内 把 Role(或 ClusterRole)绑定给具体的用户/组/ServiceAccount
ClusterRoleBinding 整个集群 把 ClusterRole 绑定到集群范围

被授权的主体(Subject)可以是:User(人类用户,K8s 本身不管理用户账号,依赖外部认证如 OIDC/证书)、GroupServiceAccount(给 Pod 内应用程序或 CI/CD 流水线用的"机器人账号")。

12.2 实战示例:只读权限的 Role

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: web-app-prod
  name: pod-reader
rules:
  - apiGroups: [""]                  # "" 表示 core API group
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: qa-team-pod-reader
  namespace: web-app-prod
subjects:
  - kind: Group
    name: qa-team                    # 对应外部身份系统里的用户组
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

12.3 实战示例:给 CI/CD 流水线一个最小权限的 ServiceAccount

这是 GitOps/CI 场景的标准做法——永远不要把管理员权限的 kubeconfig 直接塞进 CI 系统,而是创建一个权限收得很紧的专用 ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-deployer
  namespace: web-app-prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: web-app-prod
  name: deployer
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "configmaps"]
    verbs: ["get", "list", "watch", "update", "patch", "create"]
  # 注意:没有给 secrets 权限、没有给 delete 权限 —— 最小权限原则
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deployer-binding
  namespace: web-app-prod
subjects:
  - kind: ServiceAccount
    name: ci-deployer
    namespace: web-app-prod
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io

12.4 常用内置 ClusterRole

K8s 内置了几个常用的通用 ClusterRole,可以直接绑定,不用每次自己写:cluster-admin(超级管理员,慎用)、admin(Namespace 内几乎所有权限,不含改 RBAC 本身)、edit(可读写大部分资源,不能改权限、不能删资源配额)、view(只读)。

12.5 调试权限问题

# 检查"我"(当前 kubeconfig 身份)能不能做某个操作
kubectl auth can-i delete pods --namespace=web-app-prod

# 检查指定 ServiceAccount 的权限
kubectl auth can-i list secrets \
  --as=system:serviceaccount:web-app-prod:ci-deployer \
  --namespace=web-app-prod

# 查看某个角色具体有哪些权限
kubectl describe role deployer -n web-app-prod

# 查看某个 RoleBinding 绑定了谁
kubectl describe rolebinding ci-deployer-binding -n web-app-prod

12.6 最小权限原则实践清单

  • 不同环境(dev/staging/prod)用不同 Namespace + 不同 RBAC,禁止开发者直接有 prod 的写权限。
  • 给应用 Pod 挂载的 ServiceAccount 默认应该没有任何 API 权限(除非该应用确实需要调用 K8s API,比如 Operator)。
  • 定期审计:kubectl get clusterrolebindings,rolebindings --all-namespaces 看看有没有过度授权的绑定。
  • 敏感资源(Secret)访问权限单独收紧,不要和 podsdeployments 混在同一个宽泛的 Role 里。

13. 常用命令速查表

# 资源查看
kubectl get pod,svc,deploy,ingress,cm,secret -n <ns>
kubectl get all -n <ns>
kubectl describe <resource> <name> -n <ns>

# 应用/删除配置
kubectl apply -f file.yaml
kubectl delete -f file.yaml
kubectl diff -f file.yaml          # 预览变更但不真正执行

# 日志与调试
kubectl logs -f <pod> -n <ns>
kubectl exec -it <pod> -- sh
kubectl debug -it <pod> --image=busybox --target=<container>
kubectl port-forward svc/<svc> 8080:80

# 扩缩容
kubectl scale deployment/<name> --replicas=5
kubectl autoscale deployment/<name> --min=2 --max=10 --cpu-percent=60

# 上下文/命名空间切换(多集群常用)
kubectl config get-contexts
kubectl config use-context <context-name>
kubectl config set-context --current --namespace=<ns>

# RBAC 排查
kubectl auth can-i <verb> <resource> --as=<user> -n <ns>

# 资源占用
kubectl top nodes
kubectl top pods -n <ns>

学习路径建议

  1. 先用 kindminikube 在本地搭一个单节点集群,把第 1、2 节的 Pod/Deployment/Service 手敲一遍。
  2. 练习第 8 节的排障命令——故意把 YAML 写错(镜像名拼错、探针路径错),观察 describe/logs 的报错信息,建立"看报错定位问题"的肌肉记忆。
  3. 跑通 HPA + CronJob(第 3、4 节),理解 metrics-server 的作用。
  4. 学会 ConfigMap/Secret 挂载和至少一种热加载方案(第 5、6 节)。
  5. 用 ingress-nginx 把一个应用暴露出去,配上 cert-manager 拿到真实 HTTPS 证书(第 9 节),会非常有成就感。
  6. 有了以上基础后,再学 GitOps(ArgoCD)、灰度发布策略、RBAC 精细化权限(第 7、10、12 节)。
  7. 最后再学 Istio——它依赖你已经理解 Service/Pod/流量治理的基本概念,直接上来学服务网格容易云里雾里。

此博客中的热门博文

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...

事务的ACID是什么

 事务的 ACID 是数据库事务必须满足的四个基本性质,用来保证在并发和故障情况下数据的正确性与可靠性: A(Atomicity,原子性) 一个事务中的操作要么 全部成功 ,要么 全部失败回滚 ,不存在“只做了一半”的中间状态。 C(Consistency,一致性) 事务执行前后,数据库都必须处于 一致的合法状态 ,满足约束(如主键、外键、唯一性、业务规则等)。 I(Isolation,隔离性) 并发执行的多个事务之间 相互隔离 ,一个事务未提交的中间结果对其他事务不可见(具体强弱由隔离级别决定)。 D(Durability,持久性) 一旦事务提交成功,其结果会被 永久保存 ,即使系统崩溃也不会丢失(通常依赖 WAL/redo log 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。