Kubernete
简介
Kubernetes(简称K8s)是一个开源的容器编排平台,用于自动化部署、扩展、管理容器化应用的工具。
假设你有很多个应用(比如用 Docker 打包的服务),Kubernetes 能帮你:
- ✅自动部署:像安装软件一样部署多个容器。
- 📦管理资源:统一分配 CPU、内存等资源。
- 🔁自动重启:服务挂了自动重启。
- ⬆️滚动升级:不中断服务的情况下发布新版本。
- 💻负载均衡:用户访问自动分配到不同容器上。
- 📈自动扩容缩容:根据流量自动增加或减少容器数量。
Kubernetes 通常运行在一个集群中,包括:
- 一个或多个控制节点(Master)
- 多个工作节点(Node)
- 节点上运行的是容器,比如用 Docker、containerd 等。Docker用来“打包”应用(像造车)。Kubernetes用来“调度管理”这些容器(像管车队)。Docker 管一个容器,Kubernetes 管一堆容器。
Kubernetes 架构采用了主从(Master-Node)模式,具有高度的可扩展性和模块化。
+---------------------------------------------------------------+ | Kubernetes 集群 | | | | +----------------+ +---------------------------+ | | | Master 节点 | <-------> | Node 节点(工作节点) | | | +----------------+ +---------------------------+ | | 控制面(Control Plane) 每个节点上运行 Pod、容器等资源 | +---------------------------------------------------------------+Master 控制面组件
kube-apiserver
- 对外暴露的统一接口,所有操作都要通过它进行。
- 是 Kubernetes 的“前门”。
etcd
- 高可用的键值存储系统。
- 用于保存所有集群状态(如节点信息、Pod 状态、配置等)。
kube-scheduler:调度器,负责将 Pod 分配到合适的节点上。
kube-controller-manager管理集群中的各种控制器,例如:
- 节点控制器
- 副本控制器(ReplicaSet)
- 端点控制器(Endpoints)
- 服务账户控制器
cloud-controller-manager(可选)用于和云平台交互(如创建负载均衡器、磁盘等)。
Node 工作节点组件
kubelet
- 与 API Server 通信,确保容器按照 Pod 规范运行。
- 监控 Pod 和容器状态。
kube-proxy
- 管理服务的网络通信(Service -> Pod)。
- 实现 Kubernetes 的服务发现与负载均衡。
容器运行时(Container Runtime)
- 比如 Docker、containerd、CRI-O。
- 负责真正拉取镜像并运行容器。
其他关键概念
- Pod:K8s 最小的调度单位,一个或多个容器的集合。
- Service:提供稳定的访问入口,负责负载均衡。
- Deployment:控制 Pod 的副本数量,实现滚动更新等。
- Namespace:多租户隔离机制。
🧭 总结图示(简化)
用户 -> kubectl -> kube-apiserver -> etcd | +--------------+--------------+ | | | scheduler controller-manager ... | 控制 Pod 部署 / 更新 / 状态 | 各 Node 节点 +----------------------+ | kubelet + kube-proxy | | + 容器运行时 | +----------------------+Pod 是 Kubernetes 中最小的调度单元。它是运行一个或多个紧密关联容器的封装体**,比如:
dockerrun nginx在 Kubernetes 里,它不会直接运行容器,而是运行一个Pod,Pod 里面装着这个容器。
一个 Pod 可能包含一个容器(最常见);多个容器(共享网络 & 存储,用于协作,比如主服务 + 辅助 sidecar)
📦 Pod 特性:
| 特性 | 说明 |
|---|---|
| 共享网络 | Pod 里的容器用同一个 IP,localhost 之间可以通信 |
| 共享存储 | 可挂载同一个 Volume(持久化数据) |
| 生命周期一致 | 一个 Pod 里的容器是一起创建、一起销毁的 |
| 短暂性 | Pod 本身是“易失”的,挂了就没了,通常通过 Deployment 来控制它们重建 |
🧭 Pod 工作流程:
- 创建一个 Pod(直接或通过 Deployment 等)
- kube-scheduler 把它调度到某个 Node
- kubelet 在 Node 上启动里面的容器
- kube-proxy 负责网络连接
- Pod 运行、暴露端口、接收流量等
Kubernetes 中导致 pod 重启的原因
应用程序异常
- 应用进程崩溃:Pod 内部的应用程序由于未处理的异常、内存溢出(OOM)、访问非法地址等原因崩溃,导致容器退出并被 K8s 重新拉起。
- 主动退出(exit 非 0):如果容器的主进程(PID 1)主动退出并返回非零状态码,K8s 会认为容器异常终止,并可能触发重启。
OOM(Out Of Memory)杀死
- 内存不足(OOMKilled):当容器内存超出
requests或limits限制,K8s 可能会触发 OOM 终止容器。 - Node 级别 OOM:如果 Node 本身内存不足,K8s 可能会触发 OOM 终止某些 Pod(优先终止低优先级的 Pod)。
Liveness Probe 检测失败
- 存活探针(Liveness Probe)失败:如果 Pod 配置了
livenessProbe,但探测失败(例如健康检查接口未响应或返回错误),K8s 会认为该容器不健康并重启它。
Readiness Probe 失败
- 就绪探针(Readiness Probe)失败:虽然 Readiness Probe 失败不会直接导致重启,但如果 Pod 一直无法进入就绪状态,可能会被调度器驱逐或被运维人员删除并重新创建。
节点问题
- Node 故障:如果运行 Pod 的 Node 发生故障(如宕机、网络异常等),K8s 会将该 Node 标记为
NotReady,并可能在其他 Node 上重新调度 Pod。 - Node 重启:如果 Node 由于系统更新、管理员操作等原因重启,Pod 也会随之重启。
可以使用以下命令查看 Pod 的状态和重启原因:
kubectl get pod<pod_name>-owide kubectl describe pod<pod_name>kubectl logs<pod_name>--previous# 查看上次退出的日志kubectl get events --sort-by=.metadata.creationTimestamp# 查看最近的事件如果 Pod 处于CrashLoopBackOff状态,可以进一步检查:
kubectl get pod<pod_name>-oyaml|grepreason如果怀疑是 OOMKilled:
kubectl describe pod<pod_name>|grep-ioom如果 Pod 运行的是 Java 应用,JVM 的堆内存(Heap)和 Kubernetes 分配的内存可能存在不匹配的问题:
- JVM 默认会基于物理内存计算
-Xmx(最大堆内存),但 K8s 容器中的可用内存是受limits.memory限制的。 - 如果 JVM 误以为它可以使用整个节点的内存,而不是容器的限制,可能导致堆内存(Heap)+ 其他非堆内存(Metaspace、Stack)总和超出 K8s 限制,最终触发 OOMKilled。
如何避免 OOMKilled?
使用requests.memory和limits.memory
requests.memory:表示Pod 启动时的最小预留内存,用于调度时分配。limits.memory:表示Pod 最大可使用的内存,超出后会触发 OOMKilled。
resources:requests:memory:"512Mi"# 申请 512MBlimits:memory:"1Gi"# 限制最大 1GB避免limits.memory过低,否则 JVM 可能会因 GC 频繁触发 OOM。如果业务允许,可以不设置limits.memory,仅使用requests.memory,这样 Kubernetes 不会强行杀死超限的进程,而是让 JVM 进行 GC 以尝试释放内存。
监控和优化内存使用
- 监控 JVM 内存占用
- 使用Prometheus + Grafana监控 Pod 内存使用情况
- 结合JVM Metrics(Micrometer、JMX Exporter)监控 Heap、GC 频率
- 优化代码
- 通过调优 GC(G1GC、ZGC)减少堆外内存泄露
- 避免大量对象滞留,减少
OutOfMemoryError: Metaspace
Kubernetes 中 Pod 节点相关问题
重启是否导致 Pod 名称发生变化
在 Kubernetes 中,Pod 的名称是否变化取决于它是如何创建的。
手动创建的 Pod(裸 Pod)
如果你是用kubectl apply -f pod.yaml创建的裸 Pod,那么:
- Pod 重启(节点重启)后不会自动恢复;
- 如果节点崩溃了,Pod 会直接消失(不会自动迁移到其他节点);
- 如果你自己重新创建 Pod,名称可能会改变,取决于你是否指定相同名称;
- Pod 是不可恢复的资源—— 不推荐在生产中使用裸 Pod。
由控制器管理的 Pod(如 Deployment、StatefulSet)
- Deployment / ReplicaSet 管理的 Pod
- 如果节点重启、Pod 宕机,Deployment 会重建一个新的 Pod;
- 新的 Pod 名称会变化,因为它们是动态生成的,比如:
my-app-7cc7b569b6-lmwvh → my-app-7cc7b569b6-k9zdf
- StatefulSet 管理的 Pod
- StatefulSet 会保留 Pod 名称(有状态);
- 名称不会变,例如:
my-db-0、my-db-1 这种格式; - 即使节点重启、Pod 被重新调度,名称还是
my-db-0。
在现代生产环境中,使用最多的是 Deployment + ReplicaSet 管理的 Pod,原因如下:
✅Deployment 是默认的无状态服务部署方式
- 适合大多数业务场景(如 Web 服务、API 服务、Worker 等);
- 支持滚动更新、回滚、扩缩容、健康检查;
- 简单易用,社区支持广泛;
- 与 HPA(水平自动扩展)无缝配合。
✅StatefulSet 用于有状态服务,使用相对少一些
- 适用于数据库、Kafka、ZooKeeper 这类有状态服务;
- 支持固定 Pod 名称和稳定持久化存储(如
my-app-0,my-app-1); - 支持有序部署、扩容、缩容;
- 配置和管理更复杂,不适合大规模无状态服务。
✅DaemonSet 适合做守护进程
- 用于每个节点运行一个 Pod,比如日志采集(Fluentd)、监控 Agent(Prometheus Node Exporter)等;
- 用量少但场景明确。
❌裸 Pod 几乎不用于生产
- 不具备自愈能力;
- 无法自动调度、自动扩缩容;
- 容易丢失,不安全。
在现代生产环境中,大多数情况下一个 Pod 只运行一个主要容器,但也存在运行多个容器的情况,具体看应用场景。
单容器 Pod(最常见)
- 绝大多数业务服务(Web 服务器、API 服务、后台任务)都是一个 Pod 只运行一个主容器;
- 简单、易管理、资源隔离清晰;
- Kubernetes 的设计理念是“一组紧密相关的容器组成一个 Pod”,但大多数场景一个容器足够了。
多容器 Pod(Sidecar 模式等)
- 多个容器共享一个 Pod 网络和存储,彼此协作;
- 常见场景包括:
- Sidecar 容器:
- 日志采集(如 Fluentd sidecar 跟主容器一起工作);
- 配置更新(如 Envoy 代理、Istio Sidecar);
- 监控 Agent;
- 数据同步;
- Adapter 容器:
- 对主应用进行辅助处理,例如缓存、代理等。
- Sidecar 容器:
- 多容器 Pod 让业务容器和辅助容器能紧密协同,且共享生命周期。
在一个 Pod 里运行多个容器时,Pod 的生命周期是绑定在这些容器上的,所以:
- 只要 Pod 里任意一个容器崩溃或异常,整个 Pod 都会被认为“异常”;
- Kubernetes 会根据 Pod 的重启策略(
restartPolicy)来决定是重启单个容器,还是整个 Pod 重启; - 但实际上,Pod 里的容器共享同一个 Pod 生命周期和调度单元,当关键容器挂掉,通常会触发 Pod 重启;
- Pod 里的所有容器都会被杀死并重新启动,这意味着任何一个容器异常都会导致整个 Pod 重启。
