# mini-paas **Repository Path**: zmym/mini-paas ## Basic Information - **Project Name**: mini-paas - **Description**: 一个基于 Kubernetes 的轻量级 PaaS 平台,让你像使用 Vercel 一样简单地部署容器应用。 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-08 - **Last Updated**: 2025-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Mini-PaaS 极简应用托管平台 一个基于 Kubernetes 的轻量级 PaaS 平台,让你像使用 Vercel 一样简单地部署容器应用。 ## 🎯 这是什么? Mini-PaaS 是一个学习项目,帮助你理解: - 如何用 Java 操作 Kubernetes API - 前后端分离的全栈开发 - 容器编排的核心概念(Deployment、Service、Ingress) **一句话总结**:输入镜像名 → 自动创建 K8s 资源 → 返回可访问的 URL ## 🏗️ 架构概览 ``` ┌─────────────────────────────────────────────────────────────┐ │ 用户浏览器 │ └─────────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Frontend (Next.js 16) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ DeployForm │ │ AppList │ │ LogViewer │ │ │ │ 部署表单 │ │ 应用列表 │ │ 实时日志(WebSocket) │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ └─────────────────────────┬───────────────────────────────────┘ │ REST API / WebSocket ▼ ┌─────────────────────────────────────────────────────────────┐ │ Backend (Spring Boot 3.5) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ AppService │ │ K8sService │ │ LogWebSocketHandler │ │ │ │ 业务逻辑 │ │ K8s操作封装 │ │ 日志流推送 │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ └─────────────────────────┬───────────────────────────────────┘ │ Fabric8 Kubernetes Client ▼ ┌─────────────────────────────────────────────────────────────┐ │ Kubernetes Cluster │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │Deployment │ │ Service │ │ Ingress │ │ │ │ 管理Pod │ │ 内部负载均衡│ │ 外部路由 │ │ │ └───────────┘ └───────────┘ └───────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ## ✨ 功能特性 | 功能 | 说明 | |------|------| | 🚀 一键部署 | 输入镜像地址,自动创建 Deployment + Service + Ingress | | 🔨 源码构建 | 从 Git 仓库拉取代码,Kaniko 构建镜像并自动部署 | | 📊 应用管理 | 查看所有应用状态、副本数、访问地址 | | ⚖️ 弹性伸缩 | 动态调整副本数 (1-10) | | 🔄 滚动更新 | 修改镜像、资源配置、环境变量,零停机更新 | | 🔁 滚动重启 | 创建新 Pod 替换旧 Pod(重启次数重置) | | 📝 实时日志 | WebSocket 推送 Pod 日志 | | 📈 资源监控 | 查看 CPU/内存使用情况(需 metrics-server) | | 🔧 资源配置 | 支持 CPU/内存限制、环境变量 | | 💓 健康检查探针 | 配置 liveness/readiness 探针,自动重启不健康 Pod | | ❤️ 服务健康检查 | Actuator 端点监控 | | 📖 API 文档 | Swagger UI 自动生成 | ### � 高源码构建功能 除了直接部署镜像,Mini-PaaS 还支持从 Git 仓库源码构建镜像并自动部署。 #### 工作流程 ``` ┌─────────────────────────────────────────────────────────────┐ │ 源码构建流程 │ │ │ │ 1. 用户提交 Git 仓库地址 │ │ │ │ │ ▼ │ │ 2. 自动检测 Dockerfile 中的 EXPOSE 端口 │ │ │ │ │ ▼ │ │ 3. 创建 Kaniko Job(在 K8s 中构建镜像) │ │ │ │ │ ▼ │ │ 4. 构建完成后推送到 Docker Registry │ │ │ │ │ ▼ │ │ 5. 自动部署应用(创建 Deployment + Service + Ingress) │ │ │ └─────────────────────────────────────────────────────────────┘ ``` #### 使用方式 1. 切换到"源码构建"标签页 2. 填写 Git 仓库地址(支持 GitHub、GitLab、Gitee) 3. 填写应用/镜像名称 4. 端口会自动从 Dockerfile 的 EXPOSE 指令检测 5. 点击"开始构建" #### 前置条件 需要配置 Docker Registry 凭据(用于推送构建好的镜像): ```bash # 环境变量配置 export DOCKER_REGISTRY_SERVER=https://index.docker.io/v1/ export DOCKER_REGISTRY_USERNAME=your_dockerhub_username export DOCKER_REGISTRY_PASSWORD=your_dockerhub_password # 可选:Git 凭据(用于私有仓库) export GIT_CREDENTIALS_USERNAME=your_git_username export GIT_CREDENTIALS_TOKEN=your_git_token ``` #### 技术实现 - 使用 [Kaniko](https://github.com/GoogleContainerTools/kaniko) 在 K8s 集群内构建镜像(无需 Docker daemon) - 自动创建 `docker-registry-secret` 用于推送镜像 - 支持私有 Git 仓库(通过 `git-credentials-secret`) - 构建完成后自动触发部署流程 #### 构建请求示例 ```json { "gitRepoUrl": "https://github.com/username/my-app", "imageName": "my-app", "dockerfile": "Dockerfile", "branch": "main", "port": 3000, "replicas": 2, "envVars": { "NODE_ENV": "production" }, "cpuLimit": "500m", "memoryLimit": "256Mi" } ``` ### 🔧 高级配置详解 部署表单中点击"展开高级配置"可以设置以下参数: #### 环境变量 为容器注入环境变量,应用可以通过 `process.env`(Node.js)或 `System.getenv()`(Java)读取。 **使用场景**: - 数据库连接串:`DATABASE_URL=postgres://...` - API 密钥:`API_KEY=xxx` - 运行模式:`NODE_ENV=production` **前端操作**: ``` KEY: DATABASE_URL VALUE: postgres://user:pass@host:5432/db 点击 [添加] ``` **K8s 中的效果**: ```yaml spec: containers: - name: my-app env: - name: DATABASE_URL value: "postgres://user:pass@host:5432/db" - name: NODE_ENV value: "production" ``` **代码实现**(K8sService.java): ```java List envVarList = new ArrayList<>(); config.getEnvVars().forEach((k, v) -> envVarList.add(new EnvVarBuilder() .withName(k) .withValue(v) .build())); ``` #### 资源限制 控制容器可以使用的 CPU 和内存,防止单个应用占用过多资源。 | 参数 | 默认值 | 说明 | |------|--------|------| | CPU 请求 | `100m` | 最低保证的 CPU(100m = 0.1 核) | | CPU 限制 | `500m` | 最大可用 CPU(500m = 0.5 核) | | 内存请求 | `128Mi` | 最低保证的内存 | | 内存限制 | `256Mi` | 最大可用内存,超出会被 OOM Kill | **单位说明**: - CPU:`100m` = 0.1 核,`1` = 1 核,`2000m` = 2 核 - 内存:`128Mi` = 128 MiB,`1Gi` = 1 GiB **请求 vs 限制**: ``` 请求 (requests) 限制 (limits) │ │ ▼ ▼ ├──────────────────────────────────────────────────────────┤ 0 100m 500m CPU │ │ 调度依据 硬性上限 "至少给我这么多" "最多用这么多" ``` - **请求**:K8s 调度时参考,确保节点有足够资源 - **限制**:运行时强制执行,超出会被限流(CPU)或杀死(内存) **K8s 中的效果**: ```yaml spec: containers: - name: my-app resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi" ``` **代码实现**(K8sService.java): ```java ResourceRequirements resources = new ResourceRequirementsBuilder() .withLimits(Map.of( "cpu", new Quantity(config.getCpuLimit()), "memory", new Quantity(config.getMemoryLimit()))) .withRequests(Map.of( "cpu", new Quantity(config.getCpuRequest()), "memory", new Quantity(config.getMemoryRequest()))) .build(); ``` #### 副本数 控制运行多少个相同的 Pod 实例。 | 副本数 | 适用场景 | |--------|---------| | 1 | 开发测试、无状态简单应用 | | 2-3 | 生产环境基础高可用 | | 3+ | 高流量、需要负载均衡 | **注意**:本项目限制 1-10 个副本,生产环境可根据需要调整。 #### 健康检查探针 K8s 通过探针检测容器健康状态,自动重启不健康的 Pod。 | 参数 | 默认值 | 说明 | |------|--------|------| | 检查路径 | `/` | HTTP GET 请求路径(如 `/health`) | | 初始延迟 | `10s` | 容器启动后等待多久开始检查 | | 检查间隔 | `10s` | 每次检查的间隔时间 | | 超时时间 | `5s` | 单次检查的超时时间 | | 失败阈值 | `3` | 连续失败多少次判定为不健康 | **两种探针**: - **Liveness Probe**(存活探针):检测容器是否存活,失败则重启容器 - **Readiness Probe**(就绪探针):检测容器是否准备好接收流量,失败则从 Service 移除 **K8s 中的效果**: ```yaml spec: containers: - name: my-app livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 5 periodSeconds: 10 ``` **使用建议**: - 应用需要提供健康检查端点(如 `/health` 返回 200) - 初始延迟要大于应用启动时间 - 留空则不启用健康检查 #### 完整部署请求示例 ```json { "name": "my-api", "image": "node:18-alpine", "port": 3000, "replicas": 2, "envVars": { "NODE_ENV": "production", "DATABASE_URL": "postgres://localhost:5432/mydb", "REDIS_URL": "redis://localhost:6379" }, "cpuRequest": "100m", "cpuLimit": "500m", "memoryRequest": "128Mi", "memoryLimit": "512Mi", "healthCheck": { "path": "/health", "initialDelaySeconds": 15, "periodSeconds": 10, "timeoutSeconds": 5, "failureThreshold": 3 } } ``` ## 🛠️ 技术栈 **后端** - Java 17 + Spring Boot 3.5 - Fabric8 Kubernetes Client 7.0 - Spring Data JPA + PostgreSQL - WebSocket (实时日志) - SpringDoc OpenAPI (API 文档) **前端** - Next.js 16 + React 19 - TanStack Query (数据请求) - Tailwind CSS 4 - TypeScript ## 📁 项目结构 ``` mini-paas/ ├── backend/ # 后端服务 │ └── src/main/java/com/minipaas/orchestrator/ │ ├── controller/ # REST 接口 │ │ ├── AppController.java # 应用 CRUD + 伸缩 │ │ ├── BuildController.java # 源码构建 API │ │ └── SystemController.java # 集群状态 │ ├── service/ │ │ ├── AppService.java # 业务逻辑层 │ │ ├── BuildService.java # 源码构建服务 ⭐新增 │ │ └── K8sService.java # K8s 操作封装 ⭐核心 │ ├── websocket/ │ │ └── LogWebSocketHandler.java # 日志流推送 │ ├── dto/ # 数据传输对象 │ ├── entity/ # 数据库实体 │ └── config/ # 配置类 │ └── frontend/ # 前端应用 └── src/ ├── app/ # Next.js App Router ├── components/ │ ├── DeployForm.tsx # 镜像部署表单 │ ├── BuildForm.tsx # 源码构建表单 ⭐新增 │ ├── DeployTabs.tsx # 部署方式切换 │ ├── AppList.tsx # 应用列表 │ ├── LogViewer.tsx # 日志查看器 │ └── ClusterStatus.tsx # 集群状态 └── lib/ └── api.ts # API 封装 ``` ## 🔑 核心代码解读 ### 1. K8sService - Kubernetes 操作封装 这是整个项目的核心,封装了所有 K8s 操作: ```java // 创建 Deployment(简化版) public Deployment createDeployment(AppConfig config) { Deployment deployment = new DeploymentBuilder() .withNewMetadata() .withName(config.getName()) .endMetadata() .withNewSpec() .withReplicas(config.getReplicas()) .withNewTemplate() .withNewSpec() .addNewContainer() .withName(config.getName()) .withImage(config.getImage()) // 用户指定的镜像 .withResources(resources) // CPU/内存限制 .endContainer() .endSpec() .endTemplate() .endSpec() .build(); return client.apps().deployments().create(deployment); } ``` **关键点**: - 使用 Fabric8 的 Builder 模式构建 K8s 资源 - 自动添加 `managed-by: mini-paas` 标签便于管理 - 支持环境变量、资源限制等高级配置 ### 2. 部署流程 ``` 用户点击部署 │ ▼ AppController.deploy() │ ▼ AppService.deploy() ├── 1. 创建 Deployment (Pod 模板) ├── 2. 创建 Service (内部负载均衡) ├── 3. 创建 Ingress (外部访问路由) └── 4. 保存到数据库 │ ▼ 返回访问 URL: http://{app-name}.127.0.0.1.nip.io ``` ### 3. 实时日志 - WebSocket ```java // LogWebSocketHandler.java private void streamLogs(WebSocketSession session, String podName) { LogWatch logWatch = kubernetesClient.pods() .withName(podName) .tailingLines(50) // 最近50行 .watchLog(); // 开启日志流 BufferedReader reader = new BufferedReader( new InputStreamReader(logWatch.getOutput()) ); String line; while (session.isOpen() && (line = reader.readLine()) != null) { session.sendMessage(new TextMessage(line)); // 推送到前端 } } ``` ### 4. 前端数据流 ```typescript // 使用 TanStack Query 管理数据 const { data: apps } = useQuery({ queryKey: ['apps'], queryFn: getApps, refetchInterval: 5000, // 每5秒刷新 }); // WebSocket 日志 const ws = new WebSocket(`ws://localhost:8080/ws/logs/${podName}`); ws.onmessage = (event) => setLogs(prev => [...prev, event.data]); ``` ## 🚀 快速开始 ### 前置条件 - Java 17+ - Node.js 18+ - PostgreSQL - Kubernetes 集群 (推荐 Docker Desktop 或 minikube) - kubectl 已配置 (~/.kube/config) ### 1. 启动后端 ```bash cd backend # 配置数据库 (修改 application.yml 或设置环境变量) export DATABASE_URL=jdbc:postgresql://localhost:5432/minipaas export DATABASE_USER=your_user export DATABASE_PASSWORD=your_password # 启动 mvn spring-boot:run ``` ### 2. 启动前端 ```bash cd frontend npm install npm run dev ``` ### 3. 访问 - 前端界面: http://localhost:3000 - API 文档: http://localhost:8080/swagger-ui.html - 健康检查: http://localhost:8080/actuator/health ## 📡 API 接口 ### 应用管理 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/deploy` | 部署应用 | | GET | `/api/apps` | 获取应用列表 | | PUT | `/api/apps/{name}` | 更新应用(镜像/资源/环境变量) | | DELETE | `/api/apps/{name}` | 删除应用 | | PUT | `/api/apps/{name}/scale` | 调整副本数 | | POST | `/api/apps/{name}/restart` | 滚动重启应用 | ### Pod 操作 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/apps/{name}/pods` | 获取 Pod 列表 | | GET | `/api/apps/{name}/pods/details` | 获取 Pod 详细状态 | | GET | `/api/apps/{name}/metrics` | 获取资源使用情况 | | GET | `/api/pods/{podName}/logs` | 获取 Pod 日志 | | WS | `/ws/logs/{podName}` | 实时日志流 | ### 源码构建 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/builds` | 创建构建任务 | | GET | `/api/builds/{jobName}/status` | 获取构建状态 | | GET | `/api/builds/{jobName}/logs` | 获取构建日志 | | DELETE | `/api/builds/{jobName}` | 取消构建任务 | | GET | `/api/builds/detect-port` | 检测 Dockerfile 端口 | ### 系统 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/system/status` | 获取集群状态 | | GET | `/actuator/health` | 健康检查 | | GET | `/swagger-ui.html` | API 文档 | ## 🔧 配置说明 ### 后端环境变量 | 变量 | 默认值 | 说明 | |------|--------|------| | `DATABASE_URL` | `jdbc:postgresql://localhost:5432/minipaas` | 数据库连接 | | `DATABASE_USER` | `jackz` | 数据库用户 | | `DATABASE_PASSWORD` | - | 数据库密码 | | `SERVER_PORT` | `8080` | 服务端口 | | `LOG_LEVEL` | `DEBUG` | 日志级别 | | `DOCKER_REGISTRY_SERVER` | `https://index.docker.io/v1/` | Docker Registry 地址 | | `DOCKER_REGISTRY_USERNAME` | - | Docker Registry 用户名 | | `DOCKER_REGISTRY_PASSWORD` | - | Docker Registry 密码 | | `GIT_CREDENTIALS_USERNAME` | - | Git 用户名(私有仓库) | | `GIT_CREDENTIALS_TOKEN` | - | Git Token(私有仓库) | | `APP_BASE_DOMAIN` | `127.0.0.1.nip.io` | 应用访问域名后缀 | ### 前端环境变量 ```bash # .env.local NEXT_PUBLIC_API_URL=http://localhost:8080 NEXT_PUBLIC_WS_URL=ws://localhost:8080 ``` ## ☸️ Kubernetes 基础概念 > 如果你是 K8s 新手,先看这部分;熟悉的可以跳到下一节。 ### 什么是 Kubernetes? Kubernetes(简称 K8s)是一个容器编排平台,解决的核心问题是:**如何管理大量容器?** 想象一下:你有 100 个容器要运行,需要考虑: - 容器挂了怎么自动重启? - 流量怎么分配到多个容器? - 怎么滚动更新不停机? - 资源不够怎么自动扩容? K8s 就是解决这些问题的。 ### 核心概念速览 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Kubernetes 集群 │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Namespace (命名空间) │ │ │ │ 逻辑隔离,类似文件夹。默认使用 "default" │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ Pod ──── 最小部署单元,包含一个或多个容器 │ │ │ │ │ │ │ │ │ │ │ │ │ └── Container (容器) ── 你的应用运行在这里 │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` ### K8s "三大件" 详解 部署一个 Web 应用,通常需要创建三种资源: #### 1️⃣ Deployment(部署)- "我要运行什么" **作用**:定义应用的运行方式,管理 Pod 的生命周期 **类比**:就像一个"工厂",负责生产和维护指定数量的"产品"(Pod) **核心能力**: - 声明式部署:告诉 K8s "我要 3 个副本",它会自动维护 - 滚动更新:更新镜像时,逐个替换 Pod,不停机 - 回滚:发现问题可以一键回退到上个版本 - 自愈:Pod 挂了自动重建 ``` Deployment: my-app │ │ 管理 ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ ← 副本数 = 3 │ nginx │ │ nginx │ │ nginx │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────┴─────────────┘ │ 如果 Pod 2 挂了 │ ▼ Deployment 自动创建新的 Pod 2 ``` **关键字段**: ```yaml spec: replicas: 3 # 副本数 selector: # 选择器,匹配哪些 Pod matchLabels: app: my-app template: # Pod 模板 spec: containers: - name: my-app image: nginx:1.21 # 容器镜像 resources: # 资源限制 limits: cpu: "500m" # 0.5 核 memory: "256Mi" ``` #### 2️⃣ Service(服务)- "怎么找到我" **作用**:为一组 Pod 提供稳定的访问入口(IP + DNS) **类比**:就像一个"前台",客户不需要知道后面有多少员工,只需要打前台电话 **为什么需要 Service?** - Pod 的 IP 是动态的,重启后会变 - 多个 Pod 需要负载均衡 - 需要服务发现(通过名字找到服务) ``` Service: my-app ClusterIP: 10.96.100.1 DNS: my-app.default.svc.cluster.local │ │ 负载均衡 ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ │ 10.1.1.1│ │ 10.1.1.2│ │ 10.1.1.3│ └─────────┘ └─────────┘ └─────────┘ │ │ │ └───────────────┴───────────────┘ │ 通过 Label 选择 selector: app=my-app ``` **Service 类型**: | 类型 | 说明 | 使用场景 | |------|------|---------| | `ClusterIP` | 集群内部 IP(默认) | 内部服务通信 | | `NodePort` | 在每个节点开放端口 | 开发测试 | | `LoadBalancer` | 云厂商负载均衡器 | 生产环境(云上) | #### 3️⃣ Ingress(入口)- "外部怎么访问我" **作用**:管理集群外部到内部服务的 HTTP/HTTPS 路由 **类比**:就像一个"门卫 + 路由器",根据域名/路径把请求转发到对应服务 **为什么需要 Ingress?** - Service 的 ClusterIP 只能集群内访问 - NodePort 端口有限(30000-32767) - 需要基于域名/路径的路由 - 需要 HTTPS 终止 ``` 互联网 │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Ingress Controller │ │ (如 nginx-ingress) │ │ │ │ 规则1: api.example.com → Service: api-service:80 │ │ 规则2: web.example.com → Service: web-service:80 │ │ 规则3: example.com/admin → Service: admin-service:80 │ └─────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ api-service│ │ web-service│ │admin-service│ └────────────┘ └────────────┘ └────────────┘ ``` **Ingress vs Service vs Deployment 的关系**: ``` 外部请求 │ ▼ Ingress ─────── "我负责路由,根据域名/路径转发" │ ▼ Service ─────── "我负责负载均衡,把请求分给 Pod" │ ▼ Deployment ──── "我负责管理 Pod,保证数量和健康" │ ▼ Pod ─────────── "我是真正运行容器的地方" ``` ### 其他常见资源 | 资源 | 作用 | 本项目是否使用 | |------|------|--------------| | `ConfigMap` | 存储配置(非敏感) | ❌ | | `Secret` | 存储敏感信息(密码、密钥) | ❌ | | `PersistentVolume` | 持久化存储 | ❌ | | `Job` / `CronJob` | 一次性/定时任务 | ❌ | | `DaemonSet` | 每个节点运行一个 Pod | ❌ | | `StatefulSet` | 有状态应用(如数据库) | ❌ | | `Job` | 一次性任务 | ✅ 源码构建 | ### nip.io 是什么? 本项目使用 `*.127.0.0.1.nip.io` 作为应用域名,这是一个免费的通配符 DNS 服务: ``` my-app.127.0.0.1.nip.io → 解析到 127.0.0.1 test.192.168.1.100.nip.io → 解析到 192.168.1.100 ``` **好处**:不需要配置 DNS 或修改 hosts 文件,开发测试非常方便。 ### Ingress Controller 安装 Ingress 资源本身只是"规则",需要 Ingress Controller 来执行。常用的有: ```bash # 安装 nginx-ingress(推荐) kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml # 验证安装 kubectl get pods -n ingress-nginx # Docker Desktop 用户可能需要 kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml ``` --- ## ☸️ Kubernetes 深入理解 ### K8s 资源关系图 ``` 用户请求 │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Ingress │ │ 规则: my-app.127.0.0.1.nip.io → Service:my-app:80 │ └─────────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Service │ │ 类型: ClusterIP │ │ 选择器: app=my-app │ │ 端口映射: 80 → 80 │ └─────────────────────────┬───────────────────────────────────┘ │ 负载均衡 ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ │ my-app │ │ my-app │ │ my-app │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────┴─────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Deployment │ │ 副本数: 3 │ │ 镜像: nginx:latest │ │ 资源限制: 500m CPU / 256Mi 内存 │ └─────────────────────────────────────────────────────────────┘ ``` ### 本项目创建的 K8s 资源详解 #### 1. Deployment - Pod 管理器 ```yaml # 等效的 YAML(代码自动生成) apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app managed-by: mini-paas # 标识由本平台管理 spec: replicas: 2 # 副本数 selector: matchLabels: app: my-app template: spec: containers: - name: my-app image: nginx:latest ports: - containerPort: 80 env: # 环境变量 - name: NODE_ENV value: production resources: # 资源限制 requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi ``` **Java 代码对应**: ```java // K8sService.createDeployment() Deployment deployment = new DeploymentBuilder() .withNewMetadata() .withName(config.getName()) .withLabels(Map.of("app", config.getName(), "managed-by", "mini-paas")) .endMetadata() .withNewSpec() .withReplicas(config.getReplicas()) // ... 省略 .endSpec() .build(); client.apps().deployments() .inNamespace("default") .resource(deployment) .create(); ``` #### 2. Service - 内部负载均衡 ```yaml apiVersion: v1 kind: Service metadata: name: my-app spec: type: ClusterIP # 集群内部访问 selector: app: my-app # 匹配 Pod 标签 ports: - port: 80 targetPort: 80 ``` **Java 代码对应**: ```java // K8sService.createService() Service service = new ServiceBuilder() .withNewMetadata() .withName(appName) .endMetadata() .withNewSpec() .withType("ClusterIP") .withSelector(Map.of("app", appName)) .addNewPort() .withPort(port) .withTargetPort(new IntOrString(port)) .endPort() .endSpec() .build(); client.services().inNamespace("default").resource(service).create(); ``` #### 3. Ingress - 外部路由 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app spec: ingressClassName: nginx rules: - host: my-app.127.0.0.1.nip.io # 自动生成的域名 http: paths: - path: / pathType: Prefix backend: service: name: my-app port: number: 80 ``` **Java 代码对应**: ```java // K8sService.createIngress() String host = appName + ".127.0.0.1.nip.io"; Ingress ingress = new IngressBuilder() .withNewMetadata() .withName(appName) .endMetadata() .withNewSpec() .withIngressClassName("nginx") .addNewRule() .withHost(host) .withNewHttp() .addNewPath() .withPath("/") .withPathType("Prefix") .withNewBackend() .withNewService() .withName(appName) .withNewPort().withNumber(port).endPort() .endService() .endBackend() .endPath() .endHttp() .endRule() .endSpec() .build(); client.network().v1().ingresses().inNamespace("default").resource(ingress).create(); ``` ### K8s 操作命令对照表 部署应用后,可以用 kubectl 验证: | 操作 | kubectl 命令 | 本项目 API | |------|-------------|-----------| | 查看 Deployment | `kubectl get deploy` | `GET /api/apps` | | 查看 Pod | `kubectl get pods -l app=my-app` | `GET /api/apps/{name}/pods` | | 查看 Service | `kubectl get svc my-app` | - | | 查看 Ingress | `kubectl get ingress my-app` | - | | 查看日志 | `kubectl logs -f ` | `WS /ws/logs/{podName}` | | 扩缩容 | `kubectl scale deploy my-app --replicas=3` | `PUT /api/apps/{name}/scale` | | 删除应用 | `kubectl delete deploy,svc,ingress my-app` | `DELETE /api/apps/{name}` | | 查看详情 | `kubectl describe deploy my-app` | - | | 进入容器 | `kubectl exec -it -- /bin/sh` | - | ### 常用调试命令 ```bash # 查看本平台管理的所有资源 kubectl get all -l managed-by=mini-paas # 查看 Pod 事件(排查启动失败) kubectl describe pod # 查看 Pod 资源使用 kubectl top pod # 查看 Ingress Controller 日志 kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx # 测试 Service 连通性 kubectl run curl --image=curlimages/curl -it --rm -- curl http://my-app:80 # 端口转发(绕过 Ingress 直接访问) kubectl port-forward svc/my-app 8888:80 ``` ### Pod 状态说明 | 状态 | 含义 | 常见原因 | |------|------|---------| | `Pending` | 等待调度 | 资源不足、镜像拉取中 | | `Running` | 正常运行 | ✅ | | `CrashLoopBackOff` | 反复崩溃 | 应用启动失败、配置错误 | | `ImagePullBackOff` | 镜像拉取失败 | 镜像不存在、私有仓库认证 | | `Completed` | 已完成 | Job 类型任务 | | `Terminating` | 正在终止 | 删除中 | ## 📚 学习要点 1. **Kubernetes 三件套**:Deployment (管理 Pod) → Service (内部通信) → Ingress (外部访问) 2. **Fabric8 Client**:Java 操作 K8s 的最佳实践 3. **WebSocket**:实现服务端推送 4. **TanStack Query**:前端数据状态管理 ## 🤔 常见问题 **Q: 部署后无法访问应用?** A: 确保安装了 Ingress Controller (如 nginx-ingress),且 `*.127.0.0.1.nip.io` 能正确解析 **Q: 日志无法显示?** A: 检查 Pod 是否正常运行,WebSocket 连接是否成功 **Q: 资源监控显示"暂无数据"?** A: 需要安装 metrics-server,执行以下命令: ```bash # 安装 metrics-server kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml # Docker Desktop / minikube 可能需要添加参数(跳过证书验证) kubectl patch deployment metrics-server -n kube-system --type='json' -p='[ {"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"} ]' # 验证安装 kubectl top nodes kubectl top pods ``` **Q: 健康检查导致 Pod 反复重启?** A: 检查以下几点: - 应用是否提供了健康检查端点 - 初始延迟是否大于应用启动时间 - 检查路径是否正确返回 200 状态码 **Q: 如何在生产环境使用?** A: 这是学习项目,生产使用需要添加认证、HTTPS、资源隔离等 ## 📄 License MIT