
👋 大家好,欢迎来到我的技术博客!
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Kubernetes这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
- Kubernetes - 零基础理解 K8s 的核心组件及功能分工 🌐✨
- 🌱 一、先问一个问题:我们为什么需要 Kubernetes?
- 🧩 二、K8s 不是单体程序,而是一组协同工作的控制平面 + 工作节点 🏗️
- 🧠 三、控制平面四大金刚:谁在发号施令?怎么发?
- 🧱 四、工作节点三剑客:谁在真正干活?
- 🌐 五、一次完整的 HTTP 请求之旅:K8s 内部发生了什么?🚀
- 🧩 六、核心资源对象全景图:它们不是概念,而是 API 资源!
- 🌟 七、给 Java 工程师的终极建议:如何与 K8s 和谐共处?
- 🌈 结语:Kubernetes 是舞台,Java 是主角
Kubernetes - 零基础理解 K8s 的核心组件及功能分工 🌐✨
“Kubernetes 是一个可移植、可扩展的开源平台,用于自动化容器化应用的部署、扩展和管理。”
—— 官方定义(kubernetes.io)
如果你刚接触云原生,第一次看到 kubectl get pods、Deployment、Service、etcd、kubelet 这些词时感到头晕目眩——别担心!你不是一个人。🎯
本篇博客将从零开始、不预设任何前置知识,用清晰的逻辑脉络 + 真实可运行的 Java 示例 + 可视化 Mermaid 图表 + 恰到好处的类比,带你穿透 Kubernetes 的抽象迷雾,真正理解:
✅ 每个核心组件是谁?它坐在系统哪个位置?
✅ 它到底在做什么?为什么非它不可?
✅ Java 应用如何与它协作?代码里哪一行在跟 K8s 打交道?
✅ 当一个 HTTP 请求从浏览器发出,K8s 内部发生了什么?
全文约 8000 字,全程无术语轰炸,只有层层递进的理解。
🌱 一、先问一个问题:我们为什么需要 Kubernetes?
想象你是一位 Java 后端工程师,刚完成了一个 Spring Boot 微服务:用户注册服务 user-service,打包成 Docker 镜像 registry.example.com/user-service:v1.2.0。
你本地 docker run -p 8080:8080 user-service:v1.2.0 ✅ 正常启动;
你把它拷贝到一台阿里云 ECS 上,手动 docker run ... ✅ 也能访问;
但当业务爆发,你需要:
- 同时运行 5 个副本抗住流量;
- 某个副本 OOM 崩溃了,要 自动拉起新实例;
- 新版本发布时,要 灰度 10% 流量 → 50% → 全量,出问题秒级回滚;
- 用户请求进来,要 自动负载均衡到任意一个健康副本;
- 日志统一收集、指标集中监控、配置动态更新……
⚠️ 如果全靠 Shell 脚本 + docker ps + curl 健康检查 + scp 配置文件……
👉 你会在凌晨三点被 PagerDuty 报警叫醒,一边喝冰美式一边手抖写 docker kill $(docker ps -q --filter "status=exited")。
这就是 Kubernetes 存在的根本意义:
它不是“另一个 Docker”,而是“容器的操作系统”——负责调度、编排、自愈、网络、存储、安全等一切基础设施职责,让你专注写 Java 业务代码。 💡
🔗 拓展阅读:Kubernetes 官方架构概览(官方文档,语言简洁,配图清晰)
🧩 二、K8s 不是单体程序,而是一组协同工作的控制平面 + 工作节点 🏗️
Kubernetes 是典型的 控制平面(Control Plane) + 工作节点(Worker Node) 架构:
┌─────────────────────────────────────────────────────────────┐
│ 控制平面(Master) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ kube- │ │ kube- │ │ kube- │ │ etcd │ │
│ │ scheduler│ │ controller│ │ apiserver│ │ (数据库) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
↓ HTTPS API(RESTful)
↓
┌─────────────────────────────────────────────────────────────┐
│ 工作节点(Node) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────────┐ │
│ │ kubelet │ │ container │ │ Pod(含多个容器) │ │
│ │ (代理) │ │ runtime │ │ ┌─────────┐ ┌─────────┐ │ │
│ └──────────┘ └──────────┘ │ │ Java App│ │ Redis │ │ │
│ │ └─────────┘ └─────────┘ │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
别急着记名字!我们用一个生活类比来锚定认知:
🍳 把 Kubernetes 想象成一家 24 小时运转的智能中央厨房:
- 控制平面 = 厨房大脑(主厨 + 食材库 + 订单中心)
- 工作节点 = 分布在各楼层的智能烹饪工作站(带灶台、冰箱、机械臂)
- Pod = 一个密封料理盒,里面放「必须一起烧」的菜(如:主菜 Java + 辅料 Redis 容器)
- 你的 Spring Boot 应用 = 盒子里那道主菜 👨🍳
现在,我们逐个拆解每个组件——不仅说它是什么,更说它在“做一道菜”的过程中具体干了什么。
🧠 三、控制平面四大金刚:谁在发号施令?怎么发?
1️⃣ kube-apiserver —— 整个集群的唯一“前台接待 + 总机” 📞
✅ 它是 Kubernetes 的唯一入口,所有操作(无论是
kubectl、CI/CD 脚本、还是 Java 客户端)都必须通过它。
✅ 它不直接干活,只负责鉴权、校验、转发、提供 REST API。
✅ 它是无状态的,可以水平扩展(加机器)。
🔍 它在做什么?
- 当你执行
kubectl get pods -n default,实际是向https://<master-ip>:6443/api/v1/namespaces/default/pods发 GET 请求; - 当 CI/CD 流水线调用
kubectl apply -f deployment.yaml,API Server 解析 YAML,验证字段合法性(比如replicas: 3是整数),再存入 etcd; - 它还强制执行 RBAC 权限检查:“这个 Jenkins 机器人账号,有没有权限在
prod命名空间删 Pod?” ❌
💡 关键特性:
- RESTful 设计:所有资源(Pod、Service、ConfigMap…)都是 API 对象,有标准 URL 路径
/api/v1/namespaces/{ns}/pods/{name}; - 声明式接口:你告诉它“我要 3 个副本”,而不是“请帮我启 3 个容器”——它自己决定怎么做;
- Watch 机制:其他组件(如 kubelet、controller-manager)会建立长连接,监听资源变更(如 Pod 状态从
Pending→Running)。
🐘 Java 示例:用 Fabric8 Kubernetes Client 调用 API Server(真实可运行)
假设你有一个 Spring Boot 管理后台,想实时展示集群中所有 Java 应用 Pod 的状态:
// pom.xml 添加依赖
<!-- https://search.maven.org/artifact/io.fabric8/kubernetes-client -->
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>6.12.0</version> <!-- 请使用最新稳定版 -->
</dependency>
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class K8sPodMonitor {
// 在集群内运行时,自动加载 ServiceAccount Token 和 CA 证书
private final KubernetesClient client = new DefaultKubernetesClient();
/**
* 获取 default 命名空间下所有包含 'java' 标签的 Pod(模拟 Java 应用)
*/
public List<PodInfo> listJavaPods() {
PodList podList = client.pods()
.inNamespace("default")
.withLabel("app.kubernetes.io/component", "java-backend") // 自定义标签
.list();
return podList.getItems().stream()
.map(pod -> new PodInfo(
pod.getMetadata().getName(),
pod.getStatus().getPhase(), // Running / Pending / Failed
pod.getStatus().getHostIP(),
pod.getStatus().getPodIP()
))
.collect(Collectors.toList());
}
// 简单 DTO
public static class PodInfo {
private final String name;
private final String status;
private final String hostIP;
private final String podIP;
public PodInfo(String name, String status, String hostIP, String podIP) {
this.name = name;
this.status = status;
this.hostIP = hostIP;
this.podIP = podIP;
}
// getter...
}
}
✅ 这段 Java 代码没有碰 Docker、没连 etcd、没调用 kubelet——它只和 kube-apiserver 通信,就像前端调后端 API 一样自然。
✅ 它利用了 Kubernetes 的 ServiceAccount 自动挂载机制:当 Pod 运行在集群内,K8s 会自动注入 Token 和 CA 证书到 /var/run/secrets/kubernetes.io/serviceaccount/,客户端开箱即用。
2️⃣ etcd —— 集群的“中央档案馆” 📚
✅ 它是 Kubernetes 的唯一可信数据源,一个分布式的、强一致性的键值数据库。
✅ 所有集群状态(Pod 列表、Service IP、Secret 内容、Deployment 副本数…)都持久化在这里。
✅ API Server 是它唯一的“合法写入者”,其他组件只能通过 API Server 读写。
🔍 它在做什么?
- 当你
kubectl apply -f nginx-deployment.yaml,API Server 把 Deployment 对象序列化为 JSON,存入 etcd 的/registry/deployments/default/nginx-deployment路径; - 当某个 Node 失联,kube-controller-manager 检测到其上 Pod 长时间未上报心跳,就从 etcd 中读取该 Pod 的原始定义,触发“驱逐”逻辑,并写入新 Pod 对象;
- 它不存日志、不存应用数据、不存镜像——只存“集群应该长什么样”的声明式事实。
⚠️ 重要原则:
- 绝不直连 etcd! 所有组件(包括你写的 Java 程序)必须通过
kube-apiserver间接访问。这是安全与一致性的基石。 - 备份 etcd = 备份整个集群。生产环境必须定期快照(
etcdctl snapshot save)。
🧩 类比加深理解:
📦 想象 etcd 是厨房的「电子点餐系统后台数据库」:
- 顾客下单(
kubectl apply)→ 前台(API Server)录入订单 → 存入数据库(etcd);- 主厨(Controller Manager)定时查库:“哪些订单还没做?” → 下达指令给灶台(kubelet);
- 如果数据库崩了,前台无法接单,主厨不知道该做什么,灶台不知道自己烧的是哪道菜——整个厨房停摆。💥
3️⃣ kube-controller-manager —— “永不停歇的自动化管家” 🤖
✅ 它是一组控制器(Controller)的集合,持续“观察-比较-修正”,确保集群实际状态(Actual State)永远匹配你声明的目标状态(Desired State)。
✅ 它是 Kubernetes 自愈能力的核心。
🔍 它在做什么?看几个经典控制器:
| 控制器 | 监控对象 | 做什么? | Java 开发者关联场景 |
|---|---|---|---|
| ReplicaSet Controller | ReplicaSet | 确保当前运行的 Pod 数量 = .spec.replicas。若 Pod 意外终止,立刻新建一个。 | Deployment 的底层支撑,保障 Java 应用高可用 |
| Node Controller | Node | 检测 Node 心跳超时(默认 40 秒),标记为 NotReady,并驱逐其上所有 Pod。 | 你的 Java Pod 自动漂移到健康节点,用户无感 |
| EndpointSlice Controller | Service + Pod | 监听 Pod 变化,自动更新 Service 对应的 EndpointSlice(记录后端 Pod IP+Port 列表)。 | Spring Cloud LoadBalancer 无需再自己维护实例列表! |
| PersistentVolume Controller | PersistentVolumeClaim | 绑定 PVC 到 PV,或动态创建 PV(对接 NFS/AWS EBS)。 | Java 应用挂载 MySQL 数据盘 |
🧪 Java 示例:用 Deployment 声明“我要 3 个 Java 实例”,Controller 如何响应?
# java-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-user-service
labels:
app: user-service
spec:
replicas: 3 # ← 这就是 Desired State!
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: app
image: registry.example.com/user-service:v1.2.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
✅ 提交后,kube-controller-manager 中的 ReplicaSet Controller 立刻行动:
- 读取 etcd 中该 Deployment 的
replicas: 3; - 查询当前
app=user-service的 Pod 数量 → 发现是 0; - 创建一个 ReplicaSet 对象(带 OwnerReference 指向 Deployment);
- 再创建 3 个 Pod 对象,写入 etcd;
kube-scheduler开始为这 3 个 Pod 选择 Node;kubelet在对应 Node 上拉起容器……
整个过程全自动,无需人工干预。你的 Java 代码完全感知不到——它只管处理 HTTP 请求 🌐。
4️⃣ kube-scheduler —— “最强大脑调度员” 🧠
✅ 它负责将“待运行”的 Pod(处于
Pending状态)分配到合适的 Node 上。
✅ 它不执行启动,只做决策:“这个 Pod,应该放在哪台机器上跑?”
🔍 它在做什么?
Scheduler 持续监听 API Server:
➡️ 发现新 Pod(.spec.nodeName 为空,且 .status.phase == Pending)
➡️ 运行两阶段算法:
- Predicate(过滤):排除不满足硬性条件的 Node
- 资源足够?(CPU 2C / Memory 4Gi → Node 剩余资源 ≥ 此值)
- 满足亲和性?(
requiredDuringSchedulingIgnoredDuringExecution) - 没有污点冲突?(Node 有
node-role.kubernetes.io/master:NoSchedule,而 Pod 没容忍)
- Priority(打分):对剩余 Node 打分,选最高分
- CPU 利用率低的 Node 分更高(均衡负载)
- 同一 Zone 的 Node 分更高(降低网络延迟)
- 有更多空闲磁盘的 Node 分更高
➡️ 选定 Node 后,调用 API Server 的 PATCH /api/v1/namespaces/{ns}/pods/{name},设置 .spec.nodeName = "node-03"
➡️ 此时 Pod 状态变为 Scheduled,kubelet 在 node-03 上看到自己被指派,立刻行动。
🧩 Java 场景思考:
假设你的 Java 应用是风控服务,对延迟极度敏感,且需访问同机房的 GPU 推理服务:
# 风控服务 Pod 模板片段
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["cn-shanghai-a"] # 强制同可用区
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["risk-engine"]
topologyKey: kubernetes.io/hostname # 尽量不在同一物理机
tolerations:
- key: "gpu-only"
operator: "Exists" # 容忍 GPU 节点的污点
✅ kube-scheduler 会严格按这些规则筛选,确保你的 Java 风控服务永远运行在最优位置。你不用改一行 Java 代码,只需声明意图。
🧱 四、工作节点三剑客:谁在真正干活?
控制平面发号施令,但真正烧菜、洗碗、擦灶台的,是工作节点上的三个核心组件:
1️⃣ kubelet —— Node 上的“全能现场经理” 👷
✅ 它是每个 Node 上必须运行的代理,唯一职责:确保本机上所有 Pod 都按 API Server 的描述准确运行。
✅ 它不关心“为什么启动这个 Pod”,只关心“启动了没?健康吗?日志在哪?”
🔍 它在做什么?
- 定期(默认 10s)向 API Server 报告本机状态(NodeCondition)、Pod 状态(PodStatus);
- 通过 CRI(Container Runtime Interface)调用底层容器运行时(Docker、containerd、CRI-O);
- 执行生命周期钩子(
postStart,preStop); - 运行 Liveness/Readiness Probe(健康检查);
- 挂载 Volume(ConfigMap、Secret、PersistentVolume)到容器路径。
🐘 Java 示例:Spring Boot 的 Readiness Probe 如何与 kubelet 协作?
// Spring Boot Actuator + Kubernetes 集成
// application.yml
management:
endpoint:
health:
show-details: when_authorized
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
health:
probes:
show-details: always
# 定义 Readiness 探针(告诉 kubelet:“我准备好接流量了吗?”)
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
// 自定义 Readiness Indicator(例如:等待数据库连接池初始化完成)
@Component
public class DatabaseReadinessIndicator implements HealthIndicator {
private final HikariDataSource dataSource;
public DatabaseReadinessIndicator(HikariDataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Health health() {
try {
// 尝试获取连接(轻量级检测)
try (Connection conn = dataSource.getConnection()) {
return Health.up().withDetail("db-status", "connected").build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
✅ 当 kubelet 调用 http://localhost:8080/actuator/health/readiness:
- 若返回
200 { "status": "UP" }→ kubelet 标记该 Pod 为Ready→ EndpointSlice 加入此 Pod IP; - 若连续失败(如 DB 连不上)→ kubelet 标记
NotReady→ Service 流量自动剔除该实例; - 你的 Java 代码无需监听 Kubernetes 事件!只需暴露标准健康端点,kubelet 主动来问。
🔗 拓展阅读:Kubernetes 官方探针文档
2️⃣ container-runtime(如 containerd)—— “真正的灶台与锅具” 🍳
✅ 它负责下载镜像、解压、创建容器命名空间、挂载 rootfs、启动进程……是容器技术的真正执行者。
✅ Kubernetes 通过 CRI(Container Runtime Interface)与它解耦——你可以自由切换 Docker → containerd → CRI-O。
🔍 它在做什么?
- 接收 kubelet 的 CRI gRPC 请求(如
RunPodSandbox,CreateContainer,StartContainer); - 拉取镜像(
registry.example.com/user-service:v1.2.0); - 创建 Linux cgroup + namespace 隔离环境;
- 启动 Java 进程:
java -Xms512m -Xmx1g -jar app.jar; - 管理容器生命周期(stop、pause、exec)。
💡 Java 开发者注意:
- 你的
Dockerfile决定了 container-runtime 如何启动 Java 进程:FROM openjdk:17-jre-slim COPY target/user-service.jar /app.jar EXPOSE 8080 ENTRYPOINT ["java","-Xms512m","-Xmx1g","-jar","/app.jar"] - 不要在容器内跑
systemd或supervisord!Kubernetes 期望容器是“一个前台进程”,Java 应用天然符合。
3️⃣ kube-proxy —— “集群内部的智能流量转发器” 🚦
✅ 它负责实现 Kubernetes Service 的抽象:让 Pod 可以通过 Service 名称互相访问,且自动负载均衡。
✅ 它不处理外部流量(那是 Ingress 的事),只管集群内部东西向通信。
🔍 它在做什么?
kube-proxy 有三种模式(v1.22+ 默认 iptables → ipvs → nftables),我们以现代 ipvs 模式为例:
- 监听 API Server 的 Service 和 EndpointSlice 变化;
- 在 Node 上创建 IPVS 规则,例如:
# Service: user-service.default.svc.cluster.local → ClusterIP 10.96.12.100:8080 # 后端 Pod: 10.244.1.10:8080, 10.244.1.11:8080, 10.244.2.5:8080 ipvsadm -A -t 10.96.12.100:8080 -s rr ipvsadm -a -t 10.96.12.100:8080 -r 10.244.1.10:8080 -m ipvsadm -a -t 10.96.12.100:8080 -r 10.244.1.11:8080 -m ipvsadm -a -t 10.96.12.100:8080 -r 10.244.2.5:8080 -m - 当 Pod A(
order-service)访问http://user-service:8080/api/users:- DNS 解析
user-service.default.svc.cluster.local→10.96.12.100(ClusterIP); - Linux netfilter 截获包,IPVS 按 rr 策略转发到某个后端 Pod IP;
- 整个过程对 Java 应用完全透明——你代码里写
http://user-service:8080,就像调本地服务一样简单!
- DNS 解析
🐘 Java 示例:Spring Boot 调用另一服务,零配置实现服务发现
@Service
public class UserServiceClient {
private final RestTemplate restTemplate;
// 构造注入,使用 Service 名称(DNS 自动解析!)
public UserServiceClient(RestTemplateBuilder builder) {
this.restTemplate = builder
.rootUri("http://user-service:8080") // ← 注意!不是 IP,不是域名,就是 Service 名!
.build();
}
public User getUser(Long id) {
// 自动负载均衡到任意一个 user-service Pod
return restTemplate.getForObject("/api/users/{id}", User.class, id);
}
}
✅ 没有 Eureka、没有 Nacos、不需要 @LoadBalanced 注解(那是 Spring Cloud Netflix 时代的遗留)——Kubernetes 原生 Service + kube-proxy 已为你搞定一切。
🌐 五、一次完整的 HTTP 请求之旅:K8s 内部发生了什么?🚀
让我们把所有组件串起来,追踪一个真实请求:
🌐 用户在浏览器输入:
https://shop.example.com/order/123
→ 经过公网 DNS → Ingress Controller(如 Nginx Ingress)→order-service→ 调用user-service→ 返回 JSON
🔍 步骤分解(从 Ingress 到 Java 应用):
| 步骤 | 组件 | 动作 | 关键点 |
|---|---|---|---|
| ① 入口 | Cloud LB | 将 shop.example.com 的 443 端口流量,转发到集群内 Ingress Controller 的 NodePort(如 31234) | LB 不懂 Kubernetes,只做四层转发 |
| ② 路由 | Ingress Controller | Nginx 解析 HTTP Host 和 Path:host: shop.example.com + path: /order/* → 匹配到 order-service Service | Ingress 是 Kubernetes 对象,由 ingress-controller 实现 |
| ③ 发现 & 转发 | kube-proxy(在 Ingress Controller Node 上) | 将请求目标 IP:Port 替换为 order-service 的 ClusterIP(如 10.96.10.20:80),再通过 IPVS 转发到某个 order-service Pod IP(如 10.244.1.15:8080) | Service 抽象在此生效 |
| ④ 启动 Java | kubelet + containerd | 在 10.244.1.15 节点上,containerd 启动容器,执行 java -jar order-service.jar,暴露 8080 端口 | Java 进程作为容器主进程运行 |
| ⑤ 内部调用 | order-service Java 代码 | 调用 RestTemplate.getForObject("http://user-service:8080/api/users/1", User.class) | DNS 解析 user-service → 10.96.12.100,再经 kube-proxy 转发 |
| ⑥ 自愈保障 | kube-controller-manager | 若 user-service 某 Pod OOM 退出,Controller 检测到 Running 数 < 3,立即创建新 Pod;kubelet 拉起;kube-proxy 自动更新 IPVS 规则 | 用户无感知 |
✅ 看到了吗?你的 Java 代码只写了两行关键逻辑:
RestTemplate调用http://user-service:8080(服务发现)- 暴露
/actuator/health/readiness(健康检查)
其余所有运维复杂性——扩缩容、故障转移、网络策略、证书管理——全部由 Kubernetes 组件分担。
🧩 六、核心资源对象全景图:它们不是概念,而是 API 资源!
Kubernetes 的一切,最终都落地为 API Server 管理的资源对象(Resource Object)。理解它们,就是理解 K8s 的“编程模型”。
我们重点聚焦 Java 应用最常打交道的 5 个对象:
✅ 1. Pod —— 最小的、不可分割的部署单元 📦
- 一个 Pod = 一个“逻辑主机”,可含 1 个或多个紧密耦合的容器(如 Java App + log-forwarder sidecar);
- 所有容器共享 Network Namespace(同 IP、同端口空间)、IPC、UTS,挂载相同 Volume;
- Pod 是短暂的(Ephemeral):崩溃即销毁,新 Pod 是全新实体(IP、Hostname 都变)。
# 一个典型的 Java Pod(通常由 Deployment 管理,不手动创建)
apiVersion: v1
kind: Pod
metadata:
name: user-service-7f8d9c4b5-xz2qk # 自动生成
labels:
app: user-service
pod-template-hash: 7f8d9c4b5
spec:
containers:
- name: app
image: registry.example.com/user-service:v1.2.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
restartPolicy: Always # Kubernetes 控制重启,不是容器内进程
✅ 2. Deployment —— 声明式管理 Pod 生命周期的“指挥官” 🎯
- 它不直接创建 Pod,而是创建
ReplicaSet,再由 ReplicaSet 创建 Pod; - 支持滚动更新(RollingUpdate)、回滚(
kubectl rollout undo)、暂停/恢复; - Java 应用发布的事实标准。
# deployment.yaml 片段(已见前文)
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新时最多多启 1 个 Pod
maxUnavailable: 0 # 更新时 0 个 Pod 不可用(保证 100% SLA)
✅ 3. Service —— 为 Pod 提供稳定的网络端点 🌐
- ClusterIP(默认):集群内访问,如
user-service.default.svc.cluster.local; - NodePort:通过
<NodeIP>:<NodePort>从集群外访问(测试用); - LoadBalancer:对接云厂商 LB(生产常用);
- Service 的 IP 是虚拟的(VIP),由 kube-proxy 实现。
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service # 匹配 Pod labels
ports:
- protocol: TCP
port: 8080 # Service 暴露的端口
targetPort: 8080 # Pod 容器端口
type: ClusterIP
✅ 4. ConfigMap & Secret —— 解耦配置与镜像 🗂️🔒
ConfigMap:存非敏感配置(application.yml、JVM 参数);Secret:存敏感信息(DB password、API keys),Base64 编码(⚠️ 不是加密!需配合 RBAC 和 Encryption at Rest);- 挂载为文件 or 环境变量,Java 应用零改造读取。
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
application.yml: |
spring:
profiles:
active: k8s
datasource:
url: jdbc:mysql://mysql:3306/shop?useSSL=false
server:
port: 8080
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM= # base64 encoded "password123"
// Java 代码读取(通过 Spring Boot 自动绑定)
@Component
public class DbConfig {
@Value("${spring.datasource.password}")
private String dbPassword; // ← 自动从 Secret 注入!
}
✅ 5. Ingress —— 集群南北向流量的七层网关 🌍
- 它本身不转发流量,只是一个“路由规则声明”;
- 必须搭配
Ingress Controller(如 Nginx、Traefik)才能工作; - 支持基于 Host/Path 的路由、TLS 终止、重写。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
spec:
tls:
- hosts:
- shop.example.com
secretName: shop-tls-secret # 引用 Secret 中的证书
rules:
- host: shop.example.com
http:
paths:
- path: /order
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
- path: /user
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080
🌟 七、给 Java 工程师的终极建议:如何与 K8s 和谐共处?
你不需要成为 K8s 专家,但掌握以下原则,能让你写出更云原生、更健壮的 Java 应用:
✅ 1. 拥抱“十二要素应用”(The Twelve-Factor App)
- 配置外置:用
ConfigMap/Secret,而非打包进 Jar; - 无状态优先:Session 存 Redis,文件存 OSS/S3,数据库用 StatefulSet + PV;
- 一个进程一个容器:不要在容器里跑多个服务(Java + Nginx + crond);
- 快速启动与优雅关闭:
preStopHook 中调用/actuator/shutdown,等待连接 draining。
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "curl -X POST http://localhost:8080/actuator/shutdown"]
✅ 2. 善用 Spring Boot Actuator + Kubernetes 原生集成
health端点 → Readiness Probe;prometheus端点 → Prometheus 抓取指标;loggers端点 → 动态调整日志级别(无需重启);threaddump→ 排查 CPU 飙高。
✅ 3. 监控不是可选项,而是上线前提
- 必埋指标:
jvm_memory_used_bytes,http_server_requests_seconds_count,tomcat_sessions_active_current; - 用 Prometheus + Grafana 做 SLO 看板(如错误率 < 0.1%,P95 延迟 < 500ms);
kubectl top pods查实时资源占用,比ps aux更准。
✅ 4. 调试技巧:当 Pod 启动失败
# 1. 看 Pod 事件(最有价值!)
kubectl describe pod user-service-7f8d9c4b5-xz2qk
# 2. 看容器日志(-c 指定容器名,如有 sidecar)
kubectl logs user-service-7f8d9c4b5-xz2qk -c app --previous
# 3. 进容器调试(临时)
kubectl exec -it user-service-7f8d9c4b5-xz2qk -c app -- /bin/sh
# 4. 检查 Service 是否正确关联 Pod
kubectl get endpoints user-service
✅ 5. 学习路径推荐(循序渐进)
- ✅ 先掌握
kubectl常用命令(get,describe,logs,exec,port-forward); - ✅ 理解
Pod/Deployment/Service/ConfigMapYAML 结构; - ✅ 在 Minikube 或 Kind 本地集群跑通一个 Spring Boot 示例;
- ✅ 学习 Helm(K8s 的包管理器),一键部署复杂应用;
- ✅ 深入网络(CNI)、存储(CSI)、安全(PodSecurityPolicy / Pod Security Admission)。
🔗 拓展阅读:Kubernetes 官方交互式教程(免费) —— 15 分钟动手体验,强烈推荐!
🌈 结语:Kubernetes 是舞台,Java 是主角
回到最初的问题:Kubernetes 到底是什么?
它不是一个让你加班 debug 的新工具链,而是一套现代化的、声明式的、面向终态的基础设施契约。
当你写下:
replicas: 5
readinessProbe: { httpGet: { path: "/actuator/health/readiness" } }
resources: { requests: { memory: "512Mi" }, limits: { memory: "1Gi" } }
你不是在配置服务器,而是在向宇宙宣告:我的 Java 应用,应以何种尊严存在。
Kubernetes 的使命,就是不惜一切代价,让这个宣言成真。
所以,放下对 etcd 的恐惧,忘记 kube-scheduler 的算法细节,不必深究 ipvs 的哈希函数——
先让一个 Spring Boot 应用,在 kubectl apply 后,稳稳地跑起来。
然后,再一层层剥开洋葱,你会爱上这种“所见即所得”的确定性。
毕竟,最好的架构,是让你忘记架构的存在。
而 Kubernetes,正努力成为那个“隐形的巨人”。 🌟
本文完。愿你每一次 kubectl get pods,都看到绿色的 Running。 🍀
🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_41187124/article/details/157586305




