Kubernetes 从入门到精通实战指南
面向有编程基础、但是 K8s 新手的开发者。本文以"概念 + 可运行 YAML 示例 + 命令"的方式组织,建议边看边在自己的集群(本地可用 minikube / kind / k3d)里动手跑一遍。
目录
- 核心概念:Pod / Node / Service 等
- 应用部署基础
- 自动扩缩容(HPA / VPA / Cluster Autoscaler)
- 定时任务(CronJob)
- 配置与敏感信息管理(ConfigMap / Secret)
- 配置热加载
- GitOps 部署方式
- 进入 Pod 排障
- 将集群入口暴露到公网
- 灰度发布 / 金丝雀发布
- 服务网格与 Istio
- RBAC 权限管理
- 常用命令速查表
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)。
- Control Plane(控制平面):跑着
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 底层是周期性创建 Job,Job 再创建 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 典型工作流
- 开发者改代码 → CI 构建镜像 → 推送到镜像仓库,打上版本 tag。
- CI(或专门的"镜像更新机器人",如 ArgoCD Image Updater)修改 GitOps 仓库里的镜像 tag 并提交。
- ArgoCD 检测到 Git 变化 → 自动(或人工点击 Sync)→ 同步到目标集群。
- 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 API(Gateway + HTTPRoute 等资源),语义比 Ingress 更清晰、原生支持更复杂的路由/流量拆分场景。如果你的 Ingress Controller/云厂商已支持,新项目可以优先考虑评估使用。
10. 灰度发布/金丝雀发布
灰度发布的核心思路:让一小部分流量先走新版本,观察指标(错误率、延迟)正常后再逐步扩大比例,出问题随时回滚。 常见 4 种实现方式,复杂度从低到高:
方式 1:副本比例灰度(最土但最简单,不需要额外组件)
同一个 Service 下同时存在 v1 和 v2 两个 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 的三大能力
- 流量管理:灰度发布、A/B 测试、超时/重试策略、熔断、故障注入。
- 安全:服务间自动 mTLS 加密(零信任网络的基础)、细粒度的服务间访问授权(
AuthorizationPolicy)。 - 可观测性:自动产生统一格式的指标(延迟、成功率、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/证书)、Group、ServiceAccount(给 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)访问权限单独收紧,不要和
pods、deployments混在同一个宽泛的 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>
学习路径建议
- 先用
kind或minikube在本地搭一个单节点集群,把第 1、2 节的 Pod/Deployment/Service 手敲一遍。 - 练习第 8 节的排障命令——故意把 YAML 写错(镜像名拼错、探针路径错),观察
describe/logs的报错信息,建立"看报错定位问题"的肌肉记忆。 - 跑通 HPA + CronJob(第 3、4 节),理解 metrics-server 的作用。
- 学会 ConfigMap/Secret 挂载和至少一种热加载方案(第 5、6 节)。
- 用 ingress-nginx 把一个应用暴露出去,配上 cert-manager 拿到真实 HTTPS 证书(第 9 节),会非常有成就感。
- 有了以上基础后,再学 GitOps(ArgoCD)、灰度发布策略、RBAC 精细化权限(第 7、10、12 节)。
- 最后再学 Istio——它依赖你已经理解 Service/Pod/流量治理的基本概念,直接上来学服务网格容易云里雾里。