1 引言:K8s 热潮下的冷静思考
过去几年,Kubernetes(简称 K8s)几乎成了”云原生”的标准答案。一时间,似乎不迁移到 K8s 就落后于时代。很多团队在还没搞清楚 K8s 能解决什么问题、自己的业务需不需要的情况下,就投入大量人力物力去做迁移。结果往往是:迁移完成后,运维复杂度大幅上升,团队疲于应付各种 K8s 特有的概念和故障,而实际业务价值并没有显著提升。
本文不讨论 K8s 是什么、怎么安装、怎么部署——这些内容在官方文档和各种技术博客里已经汗牛充栋。本文的核心目的是帮助运维工程师和开发负责人回答一个根本问题:你的业务场景是否真的适合 Kubernetes?
在做出迁移决策之前,你需要想清楚三个核心问题:
- 你的应用架构是否适合容器化和 K8s 的调度模型?
- 你是否有足够的运维能力和工具链支撑 K8s 的运行?
- 迁移到 K8s 的成本与收益是否成正比?
下面逐一展开。
2 问题一:你的应用架构是否适合 Kubernetes
2.1 Kubernetes 的核心抽象:Pod、Deployment、Service
在讨论应用架构适配性之前,需要先理解 K8s 的核心抽象。
Pod 是 K8s 的最小调度单元。一个 Pod 里可以包含一个或多个容器,这些容器共享同一个网络命名空间(同一个 IP 和端口空间)和存储卷。K8s 不调度容器,它调度的是 Pod。直觉上可以把 Pod 理解为”一个或多个紧耦合的容器加上它们共享的资源”。
Deployment 管理 Pod 的部署和扩缩容。你声明”我需要 3 个 Pod 副本”,Deployment 负责确保这个状态一直成立——无论是因为某个 Pod 崩溃了,还是因为需要应对流量高峰,Deployment 都会自动创建新的 Pod 来维持期望数量。
Service 为一组 Pod 提供稳定的访问入口。Pod 的 IP 是临时的,Pod 被重启或扩缩容后 IP 会变化。Service 通过 Label Selector 动态追踪一组 Pod,并为它们提供一个固定的 ClusterIP(虚拟 IP),实现了服务发现和负载均衡。
ConfigMap / Secret 管理配置数据和敏感信息,使配置与容器镜像解耦。
Ingress 管理 HTTP/S 层的七层负载均衡和路由规则。
理解这些抽象之后,就能判断自己的应用是否与 K8s 的设计理念匹配。
2.2 适合 K8s 的应用特征
K8s 的设计目标是为云原生微服务架构提供最佳运行平台。适合跑在 K8s 上的应用通常具有以下特征:
无状态服务(Stateless Service):应用不保存客户端会话数据,所有会话数据存储在外部(Redis、数据库、Cookie 等)。扩缩容时,旧请求可以无缝路由到新 Pod,因为任何 Pod 都能处理任何请求。
典型的无状态应用:Web 服务器(Nginx、Apache)、API 网关、RESTful API 服务、静态文件服务、前后端分离的前端构建物。
无状态化的有状态应用(Stateful but Stateless-access):应用本身是有状态的(需要保存数据),但访问方式是 stateless 的——通过外部数据库来持久化,而不是在本地磁盘。Spring Boot 应用、Node.js 微服务都属于这一类。
微服务架构且服务间依赖明确:应用被拆分为多个独立的服务,每个服务可以独立部署和扩缩容,服务之间通过 HTTP/gRPC/API 调用通信。这种架构天然契合 K8s 的 Service 抽象和 Ingress 路由。
需要快速弹性伸缩:业务流量有明显的波峰波谷(如电商大促、热点事件),需要快速在分钟级别扩缩容。K8s 的 HPA(Horizontal Pod Autoscaler)可以基于 CPU、内存或自定义指标自动调整副本数。
多环境一致性要求高:开发、测试、预生产、生产环境需要完全一致的运行时环境。K8s 的 YAML 声明式配置保证了”所写即所运行”,消除了”在我机器上能跑”的环境差异问题。
2.3 不适合 K8s 的应用场景
单体巨石应用(Monolith)且无拆分计划:一个数百万行代码的 WAR 包,直接打成 Docker 镜像跑在 K8s 里,并没有获得微服务架构的可独立部署优势,反而因为每次发布都要重新构建整个镜像而降低了部署效率。这种场景下,K8s 只是把原来的 VM/物理机换成了一个更复杂的运行平台。
对磁盘 I/O 有极致性能要求的应用:如高性能数据库、消息队列(Kafka、RocketMQ)、实时大数据处理引擎。容器存储(尤其是 overlay 文件系统)的 I/O 开销远高于物理机直接挂载的 NVMe SSD。虽然可以通过 Local PV 或 HostPath 绕过去,但增加了配置复杂度。MySQL/PostgreSQL 在 K8s 里的性能问题一直是社区讨论的热点,并非不能跑,但需要仔细调优存储和网络插件。
依赖特定内核版本或系统调用:某些商业软件(如 Oracle Database)依赖特定的 Linux 内核参数、系统调用或硬件驱动,在容器中可能无法获得预期的性能甚至无法运行。
硬件直通需求:GPU 计算、科学计算、FPGA 编程等需要直接访问物理硬件的场景。K8s 虽然支持 device plugin 和 GPU 调度,但配置复杂度远高于普通容器部署。
团队规模小且业务稳定:如果团队只有 3~5 个人,业务是一个面向企业客户的定制化 ERP 系统,每年部署频次不超过 10 次,花几周时间搭建和维护 K8s 集群的投入产出比极低。一台 4 核 8GB 的虚拟机 + Docker Compose + CI/CD 自动化完全能够满足需求。
2.4 过渡方案:Docker Compose 的天花板
在决定上 K8s 之前,Docker Compose 是一个值得认真考虑的过渡方案。对于大多数中小型团队,Docker Compose 足以应对以下场景:
- 开发和测试环境的多容器协同(Web + API + DB + Redis)
- 单宿主机上的服务编排
- 轻量级的服务发现(通过自定义 bridge 网络的容器名解析)
- 简单的健康检查和重启策略
# docker-compose.yml - 典型的 LNMP 架构
version: "3.9"
services:
web:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
api:
condition: service_healthy
restart: unless-stopped
api:
image: python:3.11-slim
command: gunicorn -w 4 -b 0.0.0.0:8000 app:app
volumes:
- ./app:/app
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: "${DB_PASSWORD}"
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --requirepass "${REDIS_PASSWORD}" --appendonly yes
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
pg_data:
redis_data:
Docker Compose 的局限在于:它只能在单台宿主机上运行,无法做跨主机的服务编排;没有内置的服务高可用和自动故障恢复(虽然有 restart 策略,但无法自动重新调度到其他节点);扩缩容能力有限(docker-compose up --scale 已经deprecated)。
如果你的业务开始出现以下信号,说明 Docker Compose 的天花板已经触及,需要认真评估 K8s 了:
- 服务数量超过 15~20 个,单台宿主机资源开始紧张
- 需要跨多个可用区(AZ)部署以实现高可用
- 需要灰度发布、蓝绿部署、金丝雀发布等高级发布策略
- 团队开始按服务划分职责,渴望服务级别的独立部署权限
- 业务需要基于指标(CPU/内存/自定义 QPS)的自动扩缩容
3 问题二:你的团队能驾驭 Kubernetes 的复杂度吗
3.1 K8s 的学习曲线
Kubernetes 有一个公认陡峭的学习曲线。即使是经验丰富的运维工程师,也需要数周甚至数月才能对 K8s 的核心概念和操作建立扎实的理解。以下是 K8s 涉及的知识点:
基础概念层:Pod、Deployment、StatefulSet、DaemonSet、Job、CronJob、ConfigMap、Secret、Service、Ingress、PV、PVC、Namespace、Label、Annotation、Selector、ReplicaSet。
网络层:Service 类型(ClusterIP、NodePort、LoadBalancer、ExternalName)、网络策略(NetworkPolicy)、Ingress Controller、CNI 插件(Calico、Flannel、Weave、Cilium)、Service Mesh(Istio、Linkerd)、DNS 和服务发现。
存储层:StorageClass、PersistentVolumeClaim、动态卷供给(Dynamic Provisioning)、本地卷(Local PV)、存储快照与恢复、不同存储后端(AWS EBS、GCE PD、Azure Disk、NFS、Ceph)。
安全层:RBAC(基于角色的访问控制)、NetworkPolicy、Pod Security Policy / Pod Security Standards、Secret 管理(KMS 加密)、容器安全上下文、ImagePullPolicy、Resource Limits、LimitRange、Quota。
运维层:滚动更新(Rolling Update)策略、健康检查( readinessProbe / livenessProbe)、HPA/VPA/KEDA、自动扩缩容、污点和容忍(Taints and Tolerations)、亲和性调度(Node Affinity / Pod Affinity / Pod Anti-Affinity)、污点驱赶(Taints/Tolerations)。
观测层:Metrics Server、Prometheus Operator、Grafana Dashboard、日志聚合(EFK / ELK / Loki)、分布式追踪(Jaeger、Zipkin)、PromQL 查询语言。
生态工具层:Helm(包管理器)、Kustomize(配置管理)、Argo CD / Flux(GitOps)、Terraform / Pulumi(基础设施即代码)、Ansible(节点配置)、K9s / Lens(终端 UI 工具)。
故障排查层:kubectl describe、kubectl logs、kubectl exec、kubectl get events、kubectl top、kubectl port-forward、kubectl debug,以及各种资源对象的 YAML 调试。
这还没算上云厂商的 K8s 托管服务(EKS、GKE、AKS)的特有配置和限制。
3.2 团队能力自检清单
在决定迁移 K8s 之前,团队需要能自信地回答以下问题:
- 容器基础:团队成员是否熟悉 Docker 核心原理(镜像分层、存储驱动、网络模式、资源限制)?如果连 Docker 都不熟悉,直接上 K8s 会在排查问题时陷入”两个系统叠加的不确定性”。
- YAML 能力:运维和开发是否具备阅读和编写 K8s YAML 配置的能力?K8s 的所有资源管理都通过 YAML 声明式 API,YAML 写错一个字段可能导致整个 Deployment 无法启动。
- 网络基础:是否理解 Linux 网络命名空间、Veth Pair、iptables/VxLAN、CNI 插件的工作原理?K8s 的网络是它最复杂的子系统之一,大多数生产环境故障(Pod 无法通信、Service 无法访问、Ingress 不生效)都与网络配置有关。
- 监控和日志:当前团队是否有成熟的监控告警体系(Prometheus + Grafana 或类似方案)?K8s 上的微服务日志是分散在多个节点上的,没有集中日志收集几乎无法排查问题。
- 值班能力:团队是否有 7×24 值班能力?K8s 集群出现故障时,排查难度远高于单台 Docker 主机。如果团队只有工作日白天响应,故障可能导致长时间的业务中断。
- SRE 能力:团队是否理解 SLA/SLO/SLI 概念?是否能够定义和测量服务的可用性指标?K8s 提供了自动化能力,但指标的设定和业务连续性保障仍需人工决策。
如果团队对以上大部分问题都缺乏信心,那么在迁移 K8s 之前,应当先补充这些基础能力。
3.3 运维 K8s 集群的日常工作量
不要低估维护 K8s 集群本身的工作量。一个最小化的生产 K8s 集群(3 个控制平面节点 + 3 个工作节点)需要持续的运维投入:
集群升级:K8s 小版本大约每季度发布一次,每个版本都需要测试兼容性。生产环境的 K8s 版本升级是一个高风险操作,通常需要:
- 在测试环境完整验证
- 制定回滚方案
- 选择非业务高峰期执行
- 滚动升级控制平面节点
- 滚动升级工作节点(Pod 驱逐和重建)
- 验证所有核心服务正常运行
版本跨度越大升级风险越高,有些团队会选择”跳过”一些次版本,但这可能导致安全漏洞和兼容性问题。
节点维护:操作系统内核升级、安全补丁、kubelet 版本同步、容器运行时版本升级、存储插件更新。节点维护期间需要将 Pod 驱逐到其他节点,这要求集群有足够的冗余容量。
证书管理:K8s 集群的各组件之间通过 TLS 证书通信,证书有效期通常为 1 年。到期前需要续期,否则集群功能会异常。可以通过 kubeadm certs check-expiration 和 kubeadm certs renew 管理。
# 检查证书过期时间
kubeadm certs check-expiration
# 在所有控制平面节点上续期证书
kubeadm certs renew all
systemctl restart kubelet
# 确认续期成功
kubeadm certs check-expiration
存储类维护:StorageClass 的参数调整、存储后端的容量规划、数据快照和恢复演练。
网络策略维护:随着服务数量增长,NetworkPolicy 规则会越来越复杂,需要定期审计和清理。
安全运营:K8s 的安全是一个专门的领域:RBAC 权限审计、容器镜像漏洞扫描、安全上下文配置、Pod Security Standards 的持续合规检查、网络策略收紧。
Etcd 维护:作为 K8s 的数据存储,Etcd 的数据一致性和备份策略是集群稳定性的根基。每月至少应该有一次 Etcd 快照备份,并在另一个环境中做恢复演练。
# Etcd 快照备份(控制平面节点执行)
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db
# 检查快照状态
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot status /backup/etcd-snapshot-$(date +%Y%m%d).db
3.4 Kubernetes 的关键资源对象详解
运维工程师在日常工作中打交道最多的 K8s 资源对象,除了前面提到的 Deployment 和 Service,还有几个需要深入理解:
StatefulSet:管理有状态应用(如 MySQL、Redis、Kafka)的资源对象。与 Deployment 的核心区别在于 StatefulSet 为每个 Pod 维护一个稳定的、唯一的网络标识(固定的主机名和 DNS 名称),以及稳定的持久存储(PVC)。MySQL 主从集群、Redis 哨兵/集群模式、Kafka Broker 集群都适合用 StatefulSet 管理。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql"
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
StatefulSet 的 Pod 是有序创建、顺序终止的(Pod 0 先启动,Pod 2 最后启动;终止时 Pod 2 先终止,Pod 0 最后终止)。这个顺序对于需要主从顺序的数据库集群至关重要。
DaemonSet:确保每个节点(或满足特定条件的节点)上都运行一个 Pod 副本。典型用途包括日志收集代理(Filebeat、Fluentd)、监控 exporter(node-exporter)、网络插件(CNI agents)。当新增节点加入集群时,DaemonSet 会自动在新节点上部署对应 Pod;节点从集群移除时,这些 Pod 会被自动回收。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
labels:
app: filebeat
spec:
selector:
matchLabels:
app: filebeat
template:
metadata:
labels:
app: filebeat
spec:
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:8.11.0
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
hostPath:
path: /var/log
Job 和 CronJob:用于运行一次性任务和定时任务。Job 创建的一个或多个 Pod 会持续重试直到成功完成指定的运行次数。如果 Job 的 Pod 持续失败超过一定次数,Job 会停止并标记为失败状态。CronJob 按照 cron 表达式定时触发 Job。
# 数据库备份 Job
apiVersion: batch/v1
kind: Job
metadata:
name: mysql-backup
spec:
backoffLimit: 3
template:
spec:
containers:
- name: backup
image: mysql:8.0
command: ["bash", "-c", "mysqldump -h $DB_HOST -u root -p$DB_PASSWORD --all-databases > /backup/db-$(date +%Y%m%d).sql"]
env:
- name: DB_HOST
value: "mysql"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: ROOT_PASSWORD
restartPolicy: OnFailure
---
# 定时备份 CronJob(每天凌晨 3 点执行)
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup-daily
spec:
schedule: "0 3 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 3
template:
spec:
containers:
- name: backup
image: mysql:8.0
command: ["bash", "-c", "mysqldump -h $DB_HOST -u root -p$DB_PASSWORD --all-databases > /backup/db-$(date +%Y%m%d).sql"]
restartPolicy: OnFailure
concurrencyPolicy: Forbid 表示如果上一次 Job 还没执行完,新的一次会跳过,避免并发备份导致数据库压力过大。这是生产环境中定时备份 Job 必须配置的参数。
HorizontalPodAutoscaler(HPA):基于指标自动调整 Pod 副本数。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleDown.stabilizationWindowSeconds: 300 表示缩容前需要等待 5 分钟没有指标下降,防止频繁抖动导致服务不稳定。scaleUp.policies 中的 periodSeconds: 15 表示 15 秒内最多扩容 100%,即一次扩容可以翻倍,但需要等待 15 秒才能再次扩容。
3.5 kubectl 故障排查核心命令速查
K8s 故障排查有三板斧:kubectl describe、kubectl logs 和 kubectl get events。在实际生产环境中,这三个命令能解决 80% 以上的常见问题。
# 查看资源对象的详细信息,包括事件、配置、状态
kubectl describe pod <pod-name> -n <namespace>
kubectl describe deployment <deployment-name> -n <namespace>
kubectl describe service <svc-name> -n <namespace>
# 查看 Pod 日志(实时跟踪加 -f,--previous 查看上一个容器的日志)
kubectl logs -f <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> --previous
kubectl logs <pod-name> -n <namespace> --since=1h
# 查看集群范围内的事件(按时间排序)
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name>
# 查看资源状态(watch 模式)
kubectl get pods -n <namespace> -w
kubectl get nodes -w
# 查看资源使用量
kubectl top pod -n <namespace>
kubectl top node
# 临时进入容器调试
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
kubectl exec -it <pod-name> -n <namespace> -c <container-name> -- /bin/bash
# 端口转发(本地调试)
kubectl port-forward <pod-name> 8080:80 -n <namespace>
# 导出资源 YAML
kubectl get deployment <deployment-name> -n <namespace> -o yaml > backup.yaml
# 查看 Pod 的完整配置(包括默认值)
kubectl get pod <pod-name> -n <namespace> -o yaml
# 检查 API Server 连接
kubectl cluster-info
kubectl get componentstatuses # 1.19 后 deprecated,用 kubectl get --raw /healthz 替代
3.6 生产环境 K8s 故障案例与排查路径
案例一:Pod 一直处于 Pending 状态
# 第一步:describe 查看原因
kubectl describe pod myapp-5f9b8c7d6-x7s2j -n production
# 典型输出:
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Warning FailedScheduling 12s default-scheduler 0/6 nodes are available: 3 Insufficient memory, 3 node(s) had taints that the pod didn't tolerate.
# 原因分析:
# 1. 节点资源不足(Insufficient memory)- 需要扩容节点或减少 Pod 资源请求
# 2. 节点有污点(Taints)但 Pod 没有配置容忍(Tolerations)
# 第二步:检查节点资源
kubectl describe node <node-name> | grep -A 5 "Allocated resources"
kubectl top nodes
# 第三步:如果资源充足,检查污点
kubectl describe node <node-name> | grep -i taint
# 输出类似:node-role.kubernetes.io/master:NoSchedule
# 说明该节点是 master 节点,默认不调度普通 Pod
# 解决方案:添加对应的 toleration 到 Pod spec
案例二:Pod 不断 CrashLoopBackOff
# 查看 Pod 状态和重启次数
kubectl get pod myapp-5f9b8c7d6-x7s2j -n production
# 输出:RESTARTS 5 状态 CrashLoopBackOff
# 查看详细日志
kubectl logs myapp-5f9b8c7d6-x7s2j -n production --previous
# 典型根因:
# 1. 应用启动时连接数据库失败(配置错误、网络问题、DNS 不稳定)
# 2. 健康检查失败(readinessProbe 配置不正确,容器启动太慢但 probe 太激进)
# 3. 应用自身 panic 或未捕获的异常
# 4. 配置文件缺失或权限问题
# 快速验证:进入容器手动测试
kubectl exec -it myapp-5f9b8c7d6-x7s2j -n production -- /bin/sh
# 在容器内执行启动命令看具体报错
案例三:Service 无法访问
# 第一步:确认 Pod 是否 Running 且 Ready
kubectl get pods -n production -l app=myapp
# 期望输出:READY 3/3 STATUS Running
# 第二步:确认 Endpoint 存在
kubectl get endpoints myapp -n production
# 如果 ENDPOINTS 列是空的,说明 Label Selector 没有匹配到 Pod
# 检查 Deployment 的 labels 和 Service 的 selector 是否一致
# 第三步:测试 DNS 解析
kubectl run -it --rm debug --image=busybox:1.36 --restart=Never -- nslookup myapp.production.svc.cluster.local
# 第四步:从 Pod 内部测试连通性
kubectl exec -it myapp-pod -n production -- curl -v http://myapp:8080/health
# 第五步:检查网络策略
kubectl get networkpolicy -n production
kubectl describe networkpolicy allow-myapp -n production
# NetworkPolicy 默认拒绝所有入站流量,如果没有为需要的来源配置 allow 规则,流量会被丢弃
案例四:镜像拉取失败(ImagePullBackOff)
# 查看详细状态
kubectl describe pod myapp-xxx -n production
# 典型输出:
# Failed to pull image "registry.example.com/myapp:1.0.0": rpc error: code = Unknown desc = Error response from daemon: pull access denied
# 根因一:镜像仓库未配置 secret
kubectl create secret docker-registry reg-secret \
--docker-server=registry.example.com \
--docker-username=deploy \
--docker-password=${REGISTRY_TOKEN} \
--docker-email=ops@example.com \
-n production
# 在 Pod spec 中引用 imagePullSecrets
# spec:
# imagePullSecrets:
# - name: reg-secret
# 根因二:镜像 tag 不存在
docker pull registry.example.com/myapp:1.0.0 # 在能访问仓库的节点上手动验证
# 根因三:TLS 证书问题
# 如果镜像是通过 HTTPS 访问但证书不受信任,需要在 Pod 中配置证书或使用 --registry-config
3.7 K8s 网络模型与故障排查
理解 K8s 的网络模型是排查 Service/Ingress 故障的基础。K8s 网络有三个核心约定:
- 每个 Pod 拥有唯一的 IP(由 CNI 插件分配)
- 同一 Pod 内的所有容器共享该 IP
- 节点上的 Pod 之间可以直接通信,不需要 NAT
Pod 间通信的实际路径取决于 CNI 插件的实现。Calico 是生产环境最常用的选择,它使用 BGP 协议在节点间分发路由,支持网络策略和 eBPF 加速模式。
Service 的工作原理:K8s 为每个 Service 分配一个虚拟 IP(ClusterIP),这个 IP 实际由每个节点上的 kube-proxy 通过 iptables 或 IPVS 规则实现。发送到 Service IP 的流量会被负载均衡到后端的 Pod IP。
# 查看 kube-proxy 创建的转发规则(调试用,正常运维不需要)
# 在节点上执行
iptables -t nat -L KUBE-SERVICES -n -v | grep myapp
# 查看 IPVS 模式(如果 kube-proxy 使用 IPVS 模式)
ipvsadm -L -n | grep myapp
# 查看 DNS 记录(CoreDNS)
kubectl exec -it -n kube-system coredns-xxxx -- cat /etc/resolv.conf
kubectl exec -it -n kube-system coredns-xxxx -- nslookup myapp.production.svc.cluster.local
Service 类型的选择决定了服务如何对外暴露:
- ClusterIP:仅集群内部访问,是默认类型
- NodePort:通过
NodeIP:NodePort访问,适合开发测试 - LoadBalancer:通过云厂商的负载均衡器暴露,适合生产环境
- ExternalName:将 Service 映射到外部域名(CNAME),用于将外部服务纳入 K8s DNS 体系
3.8 生产环境 K8s 安全加固 Checklist
如果最终决定上 K8s,以下安全加固项是生产环境的最低要求:
# 1. Pod 安全上下文:禁止以 root 运行,禁止特权容器
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# 2. 资源限制(必须配置,防止单个 Pod 占满节点资源)
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "512Mi"
cpu: "500m"
# 3. Pod Disruption Budget:确保滚动更新时至少有 minAvailable 个 Pod 运行
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: api
# 4. NetworkPolicy:默认拒绝所有流量,按需开放
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: nginx-ingress
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: mysql
ports:
- protocol: TCP
port: 3306
如果你的团队没有专人负责集群运维,又没有可靠的托管服务支持,这些工作会大量占用开发和运维工程师的精力。
4 问题三:迁移成本与收益的理性计算
4.1 隐性成本清单
在评估是否迁移 K8s 时,很多团队只计算了”迁移本身”的成本,而忽略了大量隐性成本。
时间成本:
- K8s 基础培训:每个运维工程师约 2~4 周
- 测试集群搭建和配置:1~2 周
- 应用改造(无状态化、健康检查、配置外置、资源限制):每个服务约 3~5 天
- CI/CD 流水线改造:1~2 周
- 监控告警体系重建:1~2 周
- 日志收集方案重建:1~2 周
- 生产迁移和验证:每个应用约 0.5~1 天
- 回滚方案测试:每个应用约 0.5 天
一个 20 个微服务的团队,完整迁移周期通常在 2~4 个月。这期间团队的正常迭代开发会受到显著影响。
基础设施成本:
- EKS/GKE/AKS 的托管 K8s 费用:控制平面免费(EKS)或按节点收费,通常比同等算力的虚拟机贵 20~40%(因为 K8s 自身占用了约 10~15% 的节点资源用于系统 Pod)
- 存储成本:K8s 的 PVC 通常使用云厂商的块存储,价格比普通云盘贵 10~20%
- 网络成本:K8s 的 Service/Ingress 通常需要云厂商的负载均衡器配合,每个月可能产生额外的 LB 费用
人力成本(持续投入):
- 集群升级:每个季度约 1 人周
- 安全合规检查:每月约 0.5 人周
- 故障排查(K8s 相关):平均每周约 0.5~1 人天
- 存储和网络调优:每月约 0.5 人周
4.2 收益量化分析
K8s 的核心价值在于以下几点,需要量化评估是否对你的业务真实产生价值:
收益一:部署效率和灵活性提升
如果当前每次发布需要 30 分钟手动操作,而 K8s + GitOps 可以将发布时间缩短到 5 分钟,那么一年发布 100 次就能节省 40 小时×工程师人数的人力。这是有明确数字的收益。
收益二:资源利用率提升
K8s 的 bin-packing 调度能在同一组物理节点上混合部署多个服务,提高资源利用率。如果原来 10 台 4 核 8GB 的物理机跑 10 个服务,每个服务平均 CPU 利用率只有 15%,迁移到 K8s 后可能只需要 6 台同样规格的机器就够了,节省 4 台机器的硬件和托管成本。
# 查看当前各节点的资源使用情况
kubectl top nodes
# 查看每个 Pod 的资源 requests 和 limits
kubectl describe nodes | grep -A 5 "Allocated resources"
收益三:故障自恢复能力
Deployment 的 ReplicaSet 确保指定数量的 Pod 副本始终运行。如果某个节点宕机,K8s 自动将 Pod 调度到其他节点,用户无感知。如果原本需要运维工程师半夜手动登录服务器重启服务,这项收益是真实的。
收益四:弹性伸缩能力
HPA 基于 CPU/内存指标自动扩缩容。如果你的业务有明显波峰波谷,且波峰期间人工扩缩容的响应速度跟不上,K8s 的自动扩缩容就有价值。
收益五:多环境一致性
K8s YAML 可以同时用于 dev、staging、production 环境,消除环境差异导致的”测试通过但上线失败”问题。
4.3 决策树:是否需要 K8s
下面提供一个简化的决策树,帮助你快速判断:
你的服务数量是否超过 20 个?
否 → 继续用 Docker Compose 或轻量级编排工具
是 → 你的服务是否需要跨多台机器部署和高可用?
否 → 继续用 Docker Compose + 负载均衡器
是 → 你的团队是否有人具备 K8s 运维经验(至少 1 人)?
否 → 先培训或招聘 K8s 工程师,再评估迁移
是 → 你的业务是否对弹性伸缩有强需求?
否 → 评估成本收益后,可考虑 ECS/VM + 容器化
而非完整的 K8s 集群
是 → 你的服务是否有复杂的发布策略需求
(金丝雀、蓝绿、灰度)?
否 → 使用托管容器服务(ECS、EKS Fargate)
获得容器化好处而不管理集群
是 → K8s 是合适的选择
但建议从小规模试点开始,逐步扩大
4.4 如果决定上 K8s:推荐的渐进式路径
如果你经过评估后确定需要 K8s,不要试图一口气迁移所有应用。建议的渐进式路径:
第一阶段:建立基础设施(2~4 周)
- 搭建测试/预生产 K8s 集群
- 配置 CNI 插件(推荐生产使用 Calico,网络策略丰富且性能好)
- 配置存储类(推荐云厂商的 CSI 存储)
- 部署核心监控组件(Prometheus + Grafana + Loki/ELK)
- 配置日志收集(EFK Stack 或 Loki)
- 部署 Ingress Controller(推荐 Nginx Ingress Controller,生态成熟)
- 建立 CI/CD 流水线(Argo CD 或 Jenkins X)
- 团队 K8s 培训
第二阶段:试点迁移(4~8 周)
- 选择 2~3 个无状态微服务作为试点
- 改造应用:添加健康检查(readinessProbe / livenessProbe)、配置资源限制(requests / limits)、配置启动命令(startupProbe)、将配置外置到 ConfigMap/Secret
- 编写 K8s YAML(Deployment / Service / ConfigMap / Secret)
- 部署到 K8s 集群
- 验证功能、性能、监控、告警
- 编写回滚方案并测试
# 试点应用的标准 Deployment 配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
labels:
app: api
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
version: v1
spec:
containers:
- name: api
image: registry.example.com/api:1.0.0
ports:
- containerPort: 8080
name: http
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 10
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: api-config
key: DB_HOST
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: api-secret
key: DB_PASSWORD
restartPolicy: Always
第三阶段:扩大迁移(8~16 周)
- 逐步迁移其他无状态服务
- 引入 Helm 或 Kustomize 管理多环境配置
- 完善 RBAC 权限控制
- 配置 NetworkPolicy 实现网络隔离
- 建立 GitOps 工作流
- 制定 K8s 运维手册和故障排查 SOP
第四阶段:生产迁移
- 灰度切流:先切 5% 流量到 K8s 集群,观察 24 小时无异常后逐步扩大
- 建立 SLO 和告警
- 完善灾难恢复方案(Etcd 快照、PV 数据备份)
- 拆除旧环境
5 托管 K8s vs 自建 K8s 的选择
如果决定上 K8s,面临的第一个技术决策是使用托管服务(EKS/GKE/AKS)还是自建集群。
托管 K8s(EKS/GKE/AKS)的优势:
- 控制平面由云厂商托管,不需要手动升级 kube-apiserver、etcd、controller-manager、scheduler
- 控制平面高可用开箱即用
- 与云厂商其他服务(负载均衡器、存储、IAM、网络)深度集成
- 按使用量计费,不需要为控制平面付费(EKS)
托管 K8s 的劣势:
- 云厂商的 K8s 版本通常比上游晚 1~2 个小版本,升级周期不完全可控
- 部分高级功能(如 GPU 调度、特殊网络插件)需要使用云厂商的特定方案
- 成本可能比自建高 20~40%
- 调试 K8s 控制平面问题时,云厂商的透明度有限
自建 K8s 的优势:
- 完全控制集群所有组件和版本
- 适合对数据安全有严格合规要求的场景(金融、政府)
- 成本可控(主要是裸金属/虚拟机成本,无平台溢价)
自建 K8s 的劣势:
- 集群升级复杂度高,需要人工介入
- 控制平面高可用需要额外配置(etcd 集群、API Server 负载均衡)
- 与云服务集成的开发工作量较大
- 运维团队需要具备 K8s 控制平面运维能力
对于大多数中小型团队,托管 K8s 是更务实的选择。它让团队把精力集中在应用层的运维上,而不是被集群本身的运维拖垮。
6 总结:K8s 不是银弹
回到最初的问题:你的应用真的需要 Kubernetes 吗?
需要 K8s 的场景:
- 微服务架构,服务数量超过 20 个
- 需要跨可用区的高可用部署
- 业务有明显的弹性伸缩需求
- 团队具备 K8s 运维能力或有计划培养
- 需要复杂的发布策略(蓝绿、金丝雀、灰度)
暂时不需要 K8s 的场景:
- 单体应用或服务数量少
- 业务稳定,变更频率低
- 团队规模小,没有专职 K8s 工程师
- 迁移成本(时间、人力、基础设施)超出业务收益
K8s 是一个强大的平台,但它解决的是分布式系统运维的复杂度问题。如果你的系统还没有到达需要分布式运维的规模,强行上 K8s 只会把问题从”运维复杂度低但系统简单”变成”运维复杂度高且系统复杂”。
对于还在发展中的团队,建议的路线是:
- 先容器化(Docker),建立标准化的镜像构建和运行流程
- 用 Docker Compose 管理多容器协作
- 当 Docker Compose 开始不够用时,评估是上 K8s 还是使用 ECS/Fargate 等半托管方案
- 真正上了规模后,再根据团队的 K8s 能力决定自建还是托管
容器化和 K8s 是工具,不是目的。选择工具的唯一标准是它能否以合理的成本解决你当前的实际问题。

【公开课】从零纯手搓 K8s 1.36 二进制集群,吃透云原生底层核心
⏰ 5/21(周四)16:00
讲师:王晓春(红帽元老级认证专家)
✅课程内容:
·K8s 核心组件深度精讲
·K8s 全套证书机制详解
·重磅实战:从零纯手搓 K8s 1.36 二进制高可用集群
👉扫码免费听课

本文链接:https://www.yunweipai.com/49281.html





网友评论comments