Kubernetes 最佳实践阅读笔记:核心实践与 Gateway API 等新特性补充
目录
原文链接#
前言#
这份 GitHub 仓库是一份覆盖面相当广的 K8s 实践手册,从集群基础配置、安全、网络到 GitOps 都有涉及,对于想系统梳理 K8s 知识体系的工程师来说很有参考价值。
但该仓库最后更新停留在较早期的内容,遗漏了 K8s 近几个版本中已经稳定(或接近稳定)的重要特性,最典型的就是 Gateway API。本文以仓库内容为骨架,加入我自己的实践理解,同时补充这些新特性,力求给出一份更完整的参考。
一、集群基础:规划要在最前面#
网络 CIDR 规划#
集群建好之后最难改的就是网络。在创建集群之前必须认真计算三块地址段:
- 节点子网:根据最大节点数预留,留足余量
- Pod CIDR:每个节点 maxPods 数量决定每个节点需要多大的子网块。例如每节点最多 110 个 Pod,则每节点需要 /25(128 个地址),256 个节点就需要 /17
- Service CIDR:独立规划,避免与内网路由冲突
对于托管 K8s(GKE/EKS/AKS),云厂商通常会额外保留部分地址,规划时要读清楚文档,留出 1.5~2 倍余量。
私有集群#
节点和 API Server 不应暴露在公网。控制面通过私有端点访问,节点出流量经过 NAT 网关。这一点在公有云环境下几乎是标配,但在自建集群(K3s/kubeadm)里容易被忽视。
Infrastructure as Code#
所有集群配置(节点池、网络、IAM)都应该用 Terraform / Pulumi 管理,而不是在控制台手点。没有 IaC 的集群,两周后就没人知道某个配置是怎么来的。
二、安全#
Pod Security Standards 替代 PSP#
Pod Security Policy 在 K8s 1.21 被废弃,1.25 彻底移除。现在的替代方案是 Pod Security Admission(PSA),通过 Namespace 标签控制:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
三个级别:
| 级别 | 适用场景 |
|---|---|
privileged |
系统组件、CNI 插件 |
baseline |
普通业务,防止明显的提权 |
restricted |
严格硬化,生产推荐 |
restricted 模式要求:非 root 运行、只读根文件系统、drop all capabilities、禁止 hostPath、禁止 hostNetwork/hostPID 等。
业务容器的 SecurityContext 示例:
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
RBAC:最小权限原则#
几个容易踩的坑:
- 不要用
cluster-admin——即使是管理脚本也不行,用有限权限的 ServiceAccount - 命名空间 Role 优先于 ClusterRole——只有真正需要跨命名空间权限时才用 ClusterRole
- 每个应用独立 ServiceAccount——不要共用
defaultSA - 定期审计:
kubectl auth can-i --list --as system:serviceaccount:<ns>:<sa>
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: app-reader
rules:
- apiGroups: [""]
resources: ["pods", "configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: my-app
namespace: production
roleRef:
kind: Role
name: app-reader
apiGroup: rbac.authorization.k8s.io
仓库未覆盖:ValidatingAdmissionPolicy(CEL 策略,K8s 1.30 GA)#
ValidatingAdmissionPolicy 用 CEL(Common Expression Language)表达式直接在 API Server 层做校验,不再需要部署 Webhook 服务器(OPA Gatekeeper / Kyverno),显著降低复杂度。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: require-resource-limits
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: >
object.spec.template.spec.containers.all(c,
has(c.resources) &&
has(c.resources.limits) &&
has(c.resources.limits.memory)
)
message: "所有容器必须设置 memory limits"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: require-resource-limits-binding
spec:
policyName: require-resource-limits
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
env: production
对于简单策略(禁止特权容器、强制 label、强制资源限制),VAP + CEL 比引入 Kyverno/OPA 轻量得多。复杂策略才考虑 Webhook 方案。
三、Pod 配置#
三种探针#
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
startupProbe: # 启动慢的应用(ML 模型、遗留系统)
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 30 * 10s = 5 分钟最大启动时间
periodSeconds: 10
- liveness 失败 → 重启容器。不要把依赖外部服务的检查放进来,否则外部抖动会引发雪崩重启
- readiness 失败 → 从 Endpoints 移除,不接收流量。用于应用预热完成前的流量隔离
- startup 专为慢启动设计,期间 liveness/readiness 暂停
资源请求与限制#
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "256Mi" # memory 设置等于 request(不可压缩资源)
# cpu 不设 limit(可压缩,设 limit 会导致不必要的 throttle)
用 VPA 的推荐模式(updateMode: "Off")来验证初始值:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off" # 只给建议,不自动修改
四、扩缩容与调度#
HPA 目标值计算#
原始公式:
目标利用率 = (1 - 安全余量) / (1 + 预期增长率)
举例:memory limit 100%,保留 15% 安全余量,考虑 45% 流量增长:
(1 - 0.15) / (1 + 0.45) = 0.85 / 1.45 ≈ 58%
HPA 配置:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 58
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 防止频繁缩容
policies:
- type: Percent
value: 10
periodSeconds: 60
Pod Disruption Budget#
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2 # 或 maxUnavailable: 1
selector:
matchLabels:
app: my-app
PDB 保护自愿中断(节点排空、升级),对于 crash / OOM 无效。多副本部署必须配置。
反亲和性 + 跨可用区分布#
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: my-app
topologyKey: kubernetes.io/hostname # 同节点不调度
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: my-app
topologyKey: topology.kubernetes.io/zone # 尽量跨 AZ
五、部署策略#
Rolling Update vs Recreate#
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多比期望多 1 个 Pod
maxUnavailable: 0 # 不允许不可用(零停机)
Recreate 策略适合有状态应用不能多版本并存的场景,代价是有停机窗口。
Canary 发布#
手动简单实现:用两个 Deployment 共享同一个 Service selector,通过副本比例控制流量。
v1: 9 replicas → 90% 流量
v2: 1 replica → 10% 流量
生产建议使用 Argo Rollouts 或配合 Gateway API 的流量权重分割(见第七节)。
六、优雅关闭#
K8s 终止流程:
Pod 进入 Terminating
↓
执行 preStop Hook
↓
发送 SIGTERM
↓
等待 terminationGracePeriodSeconds(默认 30s)
↓
发送 SIGKILL
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # 给 LB 时间摘除该 Pod
preStop 中 sleep 5 是为了等负载均衡器(Ingress/Service)完成摘除,避免新请求还在进来时应用已经停止接收。应用本身需要捕获 SIGTERM 做连接排空。
七、网络#
Network Policy:默认拒绝,显式放行#
# 默认拒绝所有入站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes: [Ingress]
---
# 只允许同命名空间的 frontend 访问 backend 的 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
CNI 需要支持 NetworkPolicy(Calico、Cilium、Weave Net),Flannel 默认不支持。
仓库未覆盖:Gateway API(独立版本号,v1.0 已在 2023-10 GA)#
这是本文重点补充内容。
为什么 Ingress 不够用?
Ingress API 设计于 2015 年,职责模糊、扩展性差,所有高级功能(流量权重、Header 改写、TLS 终结策略)都靠 Annotation 实现,不同 Ingress Controller 的 Annotation 互不兼容,配置无法移植。
Gateway API 的三层模型
GatewayClass(基础设施层,由平台团队管理)
└── Gateway(网关实例,由平台/运维团队管理)
└── HTTPRoute / GRPCRoute / TCPRoute(路由规则,由开发团队管理)
这种分层设计把「提供网关能力」和「使用网关能力」清晰解耦,符合大型团队的权责划分。
基本配置示例
# 1. GatewayClass:声明用哪个实现(Cilium / Envoy Gateway / Istio / Kong 等)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cilium
spec:
controllerName: io.cilium/gateway-controller
---
# 2. Gateway:实例化一个 HTTPS 网关
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: prod-gateway
namespace: infra
spec:
gatewayClassName: cilium
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: prod-tls-cert
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: allowed
---
# 3. HTTPRoute:由业务团队在自己的命名空间管理
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-route
namespace: production
spec:
parentRefs:
- name: prod-gateway
namespace: infra
hostnames: ["api.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /api/v1
backendRefs:
- name: my-app-svc
port: 8080
流量权重分割(Canary 发布)
Gateway API 原生支持,不需要额外 Annotation:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
namespace: production
spec:
parentRefs:
- name: prod-gateway
namespace: infra
rules:
- backendRefs:
- name: my-app-v1
port: 8080
weight: 90
- name: my-app-v2
port: 8080
weight: 10
Header/Path 改写
rules:
- matches:
- path:
type: PathPrefix
value: /legacy
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /api
backendRefs:
- name: backend-svc
port: 8080
与 Ingress 对比总结
| 维度 | Ingress | Gateway API |
|---|---|---|
| 标准化程度 | 低,依赖 Annotation | 高,规范内置 |
| 权限分层 | 无 | GatewayClass / Gateway / Route 三层 |
| 流量权重 | Annotation(不可移植) | 原生 weight 字段 |
| 协议支持 | HTTP/HTTPS | HTTP、GRPC、TCP、UDP、TLS |
| 成熟度 | 稳定但不再扩展新能力 | v1.0 已 GA(2023-10),当前 v1.5.x |
目前支持 Gateway API 的实现:Cilium、Envoy Gateway、Istio、Kong、Traefik、HAProxy Ingress 等,主流控制器已全面跟进。
八、Secrets 管理#
| 方案 | 复杂度 | 适用场景 |
|---|---|---|
| Kubernetes Secrets(明文 base64) | 低 | 开发环境,必须配合 etcd 加密 |
| Sealed Secrets | 低 | GitOps 场景,加密存 Git |
| External Secrets Operator | 中 | 对接 AWS Secrets Manager / GCP Secret Manager |
| HashiCorp Vault | 高 | 企业级,多集群,动态凭证 |
几个必做项:
- 开启 etcd 静态加密(
EncryptionConfiguration) - RBAC 严格限制 Secret 读权限
- Secret 不出现在日志、环境变量
env.value里(用valueFrom.secretKeyRef) - 定期轮换,有审计日志
九、可观测性#
三大支柱#
- Metrics:Prometheus + Grafana,应用暴露
/metrics(Prometheus 格式) - Logs:结构化 JSON 输出到 stdout/stderr,由 Loki / ELK 采集
- Traces:OpenTelemetry SDK 埋点,Jaeger / Tempo 收集
OpenTelemetry 是现在的标准选择,SDK 和协议层与后端解耦,一套代码可以对接任意兼容 OTLP 的收集器。
# ServiceMonitor 让 Prometheus 自动发现应用 metrics
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app-monitor
spec:
selector:
matchLabels:
app: my-app
endpoints:
- port: metrics
interval: 30s
path: /metrics
仓库未覆盖:原生 Sidecar 容器(K8s 1.29 Beta,1.33 GA)#
传统 Sidecar(如 Envoy、日志代理)和主容器平起平坐,存在启动顺序和生命周期不确定的问题:主容器比 Sidecar 先启动时会连接失败;Job 中主容器退出后 Sidecar 不自动退出导致 Job 永远不结束。
K8s 1.28 以 Alpha 引入、1.29 默认启用、1.33 进入 Stable,通过 initContainers 的 restartPolicy: Always 声明:
spec:
initContainers:
- name: otel-collector # 原生 Sidecar
image: otel/opentelemetry-collector:latest
restartPolicy: Always # 关键字段
ports:
- containerPort: 4317
containers:
- name: app
image: my-app:latest
原生 Sidecar 保证:
- 在主容器之前启动并就绪,主容器才会开始运行
- 主容器退出时,Sidecar 随之停止(解决 Job 挂住问题)
- 重启策略独立,主容器崩溃不影响 Sidecar
十、GitOps#
GitOps 核心原则:Git 是唯一的事实来源。所有集群变更通过 Git PR/commit 触发,控制器持续对比期望状态和实际状态并自动收敛。
# Argo CD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests
targetRevision: main
path: apps/my-app
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # 删除 Git 中不存在的资源
selfHeal: true # 漂移后自动恢复
syncOptions:
- CreateNamespace=true
CI/CD 必做项:
- 镜像 tag 用 commit SHA,禁止
latest - 构建时做漏洞扫描(Trivy / Grype)
- 镜像来源只允许受信任的 Registry
- Manifest 变更经过 review,不直接
kubectl apply
十一、生产就绪清单#
在将工作负载推向生产前,逐项检查:
安全
- Pod Security Standards 已应用(推荐
restricted) - 容器以非 root 运行,读写权限最小化
- RBAC 遵循最小权限,无
cluster-admin滥用 - Secrets 不以明文存储,etcd 开启加密
配置
- 所有容器设置了
resources.requests,memory 设置了limits - liveness / readiness probe 已配置,startup probe 按需添加
- 标准 label 已应用(
app.kubernetes.io/*)
可用性
- 副本数 ≥ 2
- PodDisruptionBudget 已配置
- Pod 反亲和性规则已配置,跨节点/跨 AZ 分布
扩缩容
- HPA 已配置并验证目标利用率计算合理
- VPA 推荐模式已验证资源配置合理
网络
- Network Policy 默认拒绝,显式开放必要端口
- 对外暴露的服务使用 Gateway API / Ingress,而非直接 NodePort
- 服务间通信考虑 mTLS(Service Mesh 或 Cilium Network Policy)
关闭
-
terminationGracePeriodSeconds已配置 - preStop hook 或应用内 SIGTERM 处理已实现
可观测性
- Metrics endpoint 已暴露,ServiceMonitor 已创建
- 结构化日志输出到 stdout
- 关键路径有分布式追踪
部署
- 镜像 tag 为 commit SHA,非
latest - CI 镜像扫描已接入
- GitOps 工具管理所有变更,无手动
kubectl apply
总结#
kubernetes-best-practices 是一份质量不错的参考手册,覆盖了从安全、资源管理到 GitOps 的主要实践。建议补充关注:
- Gateway API:Ingress 的正式继任者,v1.0 于 2023-10 GA、当前已发布到 v1.5.x,新项目应优先考虑
- ValidatingAdmissionPolicy:轻量级策略引擎,适合替代简单的 Kyverno/OPA 用例
- 原生 Sidecar:解决了长期存在的启动顺序和 Job 生命周期问题
K8s 生态演进很快,核心 API 的稳定性和表达能力都在持续提升。定期回顾 CHANGELOG 和 enhancements 是保持知识更新的好习惯。