部署指南-2B-集群K8s-自建方案

集群服务 Kubernetes 部署 - 自建方案(完整版)

适用场景:完全自主掌控、学习K8s、私有化部署、特殊安全要求

本文档:从零搭建完整 K8s 集群,包含网络、中间件、应用部署全流程


目录

  1. 核心概念详解
  2. 服务器规划
  3. 所有节点初始化
  4. 安装容器运行时
  5. 安装K8s组件
  6. 初始化集群
  7. 安装网络插件
  8. 部署中间件
  9. 部署应用
  10. 配置外部访问
  11. 配置HTTPS
  12. 监控系统
  13. 阿里云镜像仓库使用

核心概念详解

在开始部署之前,先理解这些核心概念

1. Namespace(命名空间)

什么是命名空间?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                          Namespace 命名空间                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   命名空间 = 虚拟集群 = 资源隔离的边界                                           │
│                                                                                 │
│   类比:                                                                         │
│   • 一个 K8s 集群 = 一栋大楼                                                    │
│   • 一个 Namespace = 大楼里的一个楼层/公司                                      │
│   • 不同公司(Namespace)有独立的办公室(Pod),但共用电梯(网络)               │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                        K8s 集群                                         │  │
│   │  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐           │  │
│   │  │  default        │ │  kube-system    │ │  prod           │           │  │
│   │  │  (默认命名空间)  │ │  (系统组件)      │ │  (生产环境)      │           │  │
│   │  │                 │ │                 │ │                 │           │  │
│   │  │  • 测试 Pod     │ │  • CoreDNS      │ │  • backend      │           │  │
│   │  │  • 临时应用     │ │  • kube-proxy   │ │  • frontend     │           │  │
│   │  │                 │ │  • Calico       │ │  • mysql        │           │  │
│   │  └─────────────────┘ └─────────────────┘ └─────────────────┘           │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
命名空间的作用
作用说明
资源隔离不同命名空间的资源互不干扰,名字可以相同
权限控制可以给不同团队分配不同命名空间的权限
资源配额可以限制每个命名空间使用的 CPU、内存
环境区分dev、test、prod 用不同命名空间
不同命名空间可以相互访问吗?

✅ 可以! 但需要使用完整的 DNS 名称:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                      跨命名空间访问规则                                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   DNS 格式:<service-name>.<namespace>.svc.cluster.local                        │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  prod 命名空间                      middleware 命名空间                  │  │
│   │  ┌─────────────────┐               ┌─────────────────┐                  │  │
│   │  │  backend Pod    │ ──────────→   │  mysql-service  │                  │  │
│   │  │                 │               │                 │                  │  │
│   │  │  访问地址:     │               │  端口: 3306     │                  │  │
│   │  │  mysql-service.middleware:3306  │                 │                  │  │
│   │  └─────────────────┘               └─────────────────┘                  │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   访问规则:                                                                     │
│   ┌──────────────────────────────────────────────────────────────────────────┐ │
│   │ 同命名空间:service-name:port                                            │ │
│   │ 例:backend-service:8080                                                 │ │
│   ├──────────────────────────────────────────────────────────────────────────┤ │
│   │ 跨命名空间:service-name.namespace:port                                  │ │
│   │ 例:mysql-service.middleware:3306                                        │ │
│   ├──────────────────────────────────────────────────────────────────────────┤ │
│   │ 完整格式:service-name.namespace.svc.cluster.local:port                  │ │
│   │ 例:mysql-service.middleware.svc.cluster.local:3306                      │ │
│   └──────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Service DNS 域名详解(重要!)
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    K8s 内部 Service DNS 域名解析                                 │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   完整 DNS 格式:                                                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   mysql-service.middleware.svc.cluster.local                             │  │
│   │   └─────┬─────┘ └────┬────┘ └┬┘ └─────┬─────┘                            │  │
│   │         │            │       │        │                                  │  │
│   │         │            │       │        └─ 集群域名(K8s 默认配置)         │  │
│   │         │            │       │           一般不需要改                     │  │
│   │         │            │       │                                           │  │
│   │         │            │       └─ svc = Service 的缩写                     │  │
│   │         │            │          表示这是一个 Service 资源                 │  │
│   │         │            │                                                   │  │
│   │         │            └─ 命名空间名称                                      │  │
│   │         │               Service 所在的 namespace                         │  │
│   │         │                                                                │  │
│   │         └─ Service 名称                                                  │  │
│   │            你创建 Service 时定义的 metadata.name                         │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ⭐ 这是 K8s 集群内部的 DNS,只能在集群内部访问!                               │
│   ⭐ 外部用户(浏览器)无法解析这个域名!                                        │
│                                                                                 │
│   为什么一般不写完整格式?                                                       │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   K8s 会自动补全 DNS 后缀!                                              │  │
│   │                                                                          │  │
│   │   你写:mysql-service.middleware                                         │  │
│   │   K8s 自动解析为:mysql-service.middleware.svc.cluster.local             │  │
│   │                                                                          │  │
│   │   所以:跨命名空间只需要写 service-name.namespace 就够了!               │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   三种写法对比:                                                                 │
│   ┌──────────────────────────────────────────────────┬─────────────────────┐  │
│   │ 写法                                             │ 适用场景            │  │
│   ├──────────────────────────────────────────────────┼─────────────────────┤  │
│   │ mysql-service                                    │ 同命名空间          │  │
│   │ mysql-service.middleware                         │ 跨命名空间 ✅ 推荐  │  │
│   │ mysql-service.middleware.svc.cluster.local       │ 完整格式(一般不需要)│  │
│   └──────────────────────────────────────────────────┴─────────────────────┘  │
│                                                                                 │
│   ⚠️ 注意:只有 Service 有这个 DNS 功能!                                        │
│   • ✅ Service → 有 DNS 域名                                                    │
│   • ❌ Pod → 没有稳定的 DNS(除非用 Headless Service)                          │
│   • ❌ ConfigMap/Secret → 没有 DNS,只能同命名空间引用                          │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
实际配置示例
# ═══════════════════════════════════════════════════════════════════════════════
# 后端应用配置(在 prod 命名空间)访问 middleware 命名空间的中间件
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: prod                    # 后端在 prod 命名空间
spec:
  template:
    spec:
      containers:
      - name: backend
        image: my-backend:v1.0
        env:
        # ─────────────────────────────────────────────────────────────────────
        # 跨命名空间访问中间件(middleware 命名空间)
        # ─────────────────────────────────────────────────────────────────────
        
        # 访问 MySQL(在 middleware 命名空间)
        - name: MYSQL_HOST
          value: "mysql-service.middleware"        # ⭐ 加 .middleware
        - name: MYSQL_PORT
          value: "3306"
        
        # 访问 Redis(在 middleware 命名空间)  
        - name: REDIS_HOST
          value: "redis-service.middleware"        # ⭐ 加 .middleware
        - name: REDIS_PORT
          value: "6379"
        
        # ─────────────────────────────────────────────────────────────────────
        # 同命名空间访问(都在 prod 中)
        # ─────────────────────────────────────────────────────────────────────
        
        # 访问同命名空间的其他服务
        - name: AUTH_SERVICE_URL
          value: "http://auth-service:8080"        # 不需要加命名空间
        
        # Spring Boot 完整配置
        - name: SPRING_DATASOURCE_URL
          value: "jdbc:mysql://mysql-service.middleware:3306/mydb"
命名空间常用命令
# 查看所有命名空间
kubectl get namespaces
kubectl get ns

# 创建命名空间
kubectl create namespace prod
kubectl create ns middleware

# 查看某命名空间下的所有资源
kubectl get all -n prod

# 删除命名空间(会删除里面所有资源!)
kubectl delete namespace test

# 设置默认命名空间(避免每次都加 -n)
kubectl config set-context --current --namespace=prod

2. Deployment(部署控制器)

什么是 Deployment?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                          Deployment 是什么?                                     │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   Deployment = Pod 的管理者 = 应用的部署控制器                                   │
│                                                                                 │
│   类比:                                                                         │
│   • Pod = 一个员工                                                              │
│   • Deployment = 部门经理                                                       │
│   • 经理负责:招人(创建Pod)、裁人(删除Pod)、换人(更新Pod)、保证人数          │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                        Deployment                                        │  │
│   │                    (名字: backend)                                       │  │
│   │                    (副本数: 3)                                           │  │
│   │                                                                          │  │
│   │      管理                管理                管理                         │  │
│   │        │                  │                  │                           │  │
│   │        ▼                  ▼                  ▼                           │  │
│   │   ┌─────────┐       ┌─────────┐       ┌─────────┐                       │  │
│   │   │ Pod-1   │       │ Pod-2   │       │ Pod-3   │                       │  │
│   │   │backend- │       │backend- │       │backend- │                       │  │
│   │   │xxx-abc  │       │xxx-def  │       │xxx-ghi  │                       │  │
│   │   └─────────┘       └─────────┘       └─────────┘                       │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
为什么需要 Deployment?(不能直接创建 Pod 吗?)
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    直接创建 Pod vs 使用 Deployment                               │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   直接创建 Pod(不推荐 ❌):                                                    │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  kubectl run my-pod --image=nginx                                       │  │
│   │                                                                          │  │
│   │  问题:                                                                   │  │
│   │  • Pod 挂了不会自动重启                                                  │  │
│   │  • 想要 3 个副本?手动创建 3 次                                          │  │
│   │  • 更新镜像?手动删除再创建                                              │  │
│   │  • 无法滚动更新、无法回滚                                                │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   使用 Deployment(推荐 ✅):                                                   │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  kubectl create deployment my-app --image=nginx --replicas=3            │  │
│   │                                                                          │  │
│   │  优点:                                                                   │  │
│   │  ✅ Pod 挂了自动重建(自愈能力)                                         │  │
│   │  ✅ 自动维护指定副本数                                                   │  │
│   │  ✅ 支持滚动更新(不停机更新)                                           │  │
│   │  ✅ 支持版本回滚                                                         │  │
│   │  ✅ 支持扩缩容                                                           │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Deployment 的核心功能
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Deployment 五大核心功能                                       │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   1️⃣ 副本管理(保证 Pod 数量)                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  replicas: 3  →  始终保持 3 个 Pod 运行                                  │  │
│   │                                                                          │  │
│   │  Pod-1 挂了? → 自动创建新的 Pod                                         │  │
│   │  多了一个 Pod? → 自动删除多余的                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   2️⃣ 滚动更新(不停机更新)                                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  更新镜像:v1.0 → v2.0                                                  │  │
│   │                                                                          │  │
│   │  过程:                                                                   │  │
│   │  [v1] [v1] [v1]  →  [v1] [v1] [v2]  →  [v1] [v2] [v2]  →  [v2] [v2] [v2]│  │
│   │                                                                          │  │
│   │  逐个替换,始终有 Pod 可用,用户无感知!                                  │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   3️⃣ 版本回滚                                                                   │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  发现 v2.0 有 bug?                                                      │  │
│   │                                                                          │  │
│   │  kubectl rollout undo deployment/backend                                │  │
│   │                                                                          │  │
│   │  自动回滚到上一个版本 v1.0                                               │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   4️⃣ 扩缩容                                                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  流量高峰?扩容:                                                        │  │
│   │  kubectl scale deployment/backend --replicas=10                         │  │
│   │                                                                          │  │
│   │  流量下降?缩容:                                                        │  │
│   │  kubectl scale deployment/backend --replicas=2                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   5️⃣ 自愈能力                                                                   │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  节点宕机?Pod 会自动在其他节点重建                                       │  │
│   │  容器崩溃?自动重启容器                                                   │  │
│   │  健康检查失败?自动替换不健康的 Pod                                       │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Deployment、ReplicaSet、Pod 的关系
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    三者的层级关系                                                │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                     Deployment                                           │  │
│   │                   (你创建的)                                             │  │
│   │                                                                          │  │
│   │   功能:声明式管理、滚动更新、版本回滚                                    │  │
│   │                        │                                                 │  │
│   │                        │ 自动创建                                        │  │
│   │                        ▼                                                 │  │
│   │   ┌─────────────────────────────────────────────────────────────────┐   │  │
│   │   │                   ReplicaSet                                     │   │  │
│   │   │              (Deployment 自动创建)                               │   │  │
│   │   │                                                                  │   │  │
│   │   │   功能:维护 Pod 副本数量                                        │   │  │
│   │   │                        │                                         │   │  │
│   │   │                        │ 自动创建                                │   │  │
│   │   │                        ▼                                         │   │  │
│   │   │   ┌─────────┐    ┌─────────┐    ┌─────────┐                     │   │  │
│   │   │   │  Pod-1  │    │  Pod-2  │    │  Pod-3  │                     │   │  │
│   │   │   │(实际运行)│    │(实际运行)│    │(实际运行)│                     │   │  │
│   │   │   └─────────┘    └─────────┘    └─────────┘                     │   │  │
│   │   └─────────────────────────────────────────────────────────────────┘   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   你只需要管 Deployment,ReplicaSet 和 Pod 都是自动管理的!                     │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Deployment 常用命令
# ═══════════════════════════════════════════════════════════════════════════════
# Deployment 常用命令
# ═══════════════════════════════════════════════════════════════════════════════

# 查看 Deployment
kubectl get deployment -n prod
kubectl get deploy -n prod          # 简写

# 查看详情
kubectl describe deployment backend -n prod

# 查看 Pod(Deployment 创建的)
kubectl get pods -n prod

# 扩缩容
kubectl scale deployment backend --replicas=5 -n prod

# 更新镜像(触发滚动更新)
kubectl set image deployment/backend backend=myapp:v2.0 -n prod

# 查看更新状态
kubectl rollout status deployment/backend -n prod

# 查看更新历史
kubectl rollout history deployment/backend -n prod

# 回滚到上一版本
kubectl rollout undo deployment/backend -n prod

# 回滚到指定版本
kubectl rollout undo deployment/backend --to-revision=2 -n prod

# 暂停更新(用于金丝雀发布)
kubectl rollout pause deployment/backend -n prod

# 恢复更新
kubectl rollout resume deployment/backend -n prod

# 重启所有 Pod(不改配置,只是重建)
kubectl rollout restart deployment/backend -n prod
Deployment YAML 关键字段解释
# ═══════════════════════════════════════════════════════════════════════════════
# Deployment 完整示例(带详细注释)
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1                    # API 版本,固定写法
kind: Deployment                       # 资源类型
metadata:
  name: backend                        # Deployment 名称
  namespace: prod                      # 命名空间
  labels:
    app: backend                       # Deployment 的标签

spec:
  replicas: 3                          # ⭐ 副本数量(Pod 数量)
  
  selector:                            # ⭐ 选择器:决定管理哪些 Pod
    matchLabels:
      app: backend                     # 必须和 template.labels 一致!
  
  strategy:                            # ⭐ 更新策略
    type: RollingUpdate                # RollingUpdate(滚动更新)或 Recreate(先删后建)
    rollingUpdate:
      maxSurge: 1                      # 更新时最多多创建几个 Pod(可以是数字或百分比)
      maxUnavailable: 0                # 更新时最多几个 Pod 不可用(0 表示始终保持可用)
  
  template:                            # ⭐ Pod 模板
    metadata:
      labels:
        app: backend                   # Pod 的标签(必须和 selector 一致)
    spec:
      containers:
      - name: backend                  # 容器名
        image: myapp:v1.0.0            # 镜像
        ports:
        - containerPort: 8080          # 容器端口
        
        resources:                     # 资源限制
          requests:                    # 最小资源(调度依据)
            memory: "512Mi"
            cpu: "250m"
          limits:                      # 最大资源(超过会被杀)
            memory: "1Gi"
            cpu: "500m"
        
        readinessProbe:                # 就绪探针(决定是否接收流量)
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        
        livenessProbe:                 # 存活探针(决定是否重启)
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10

3. Master 高可用与 Pod 调度

Master 节点高可用机制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Master 高可用原理                                             │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ❌ 错误理解:Master 挂了会自动选 Worker 升级为 Master                          │
│   ✅ 正确理解:提前部署多个 Master,一个挂了其他继续工作                          │
│                                                                                 │
│   3 Master 高可用架构:                                                          │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  │  │
│   │   │  Master-1    │  │  Master-2    │  │  Master-3    │                  │  │
│   │   │  (Leader)    │  │  (Follower)  │  │  (Follower)  │                  │  │
│   │   │              │  │              │  │              │                  │  │
│   │   │  etcd ◄──────┼──┼── etcd ◄─────┼──┼── etcd       │                  │  │
│   │   │  apiserver   │  │  apiserver   │  │  apiserver   │                  │  │
│   │   │  scheduler   │  │  scheduler   │  │  scheduler   │                  │  │
│   │   │  controller  │  │  controller  │  │  controller  │                  │  │
│   │   └──────────────┘  └──────────────┘  └──────────────┘                  │  │
│   │                                                                          │  │
│   │   Master-1 宕机 → Master-2 自动成为新 Leader → 集群继续运行              │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   为什么要奇数个 Master?(Quorum 机制)                                         │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │   Master 数量    需要存活数    可容忍故障数    推荐?                     │  │
│   │   ─────────────────────────────────────────────────────────────────────  │  │
│   │      1 个          1 个          0 个         ❌ 单点故障                │  │
│   │      2 个          2 个          0 个         ❌ 没意义                  │  │
│   │      3 个          2 个          1 个         ✅ 最小高可用              │  │
│   │      5 个          3 个          2 个         ✅ 更高可用                │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Pod 能否调度到 Master 节点?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Master 节点的 Taint(污点)机制                                │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   默认情况:Master 节点有污点,普通 Pod 不会调度上去                             │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  污点:node-role.kubernetes.io/control-plane:NoSchedule                 │  │
│   │  含义:新 Pod 不会调度到这个节点                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   Taint 和 Toleration 机制:                                                    │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  节点打污点(Taint)= "我有毒,别来调度"                                 │  │
│   │  Pod 加容忍(Toleration)= "我不怕毒,可以去"                            │  │
│   │                                                                          │  │
│   │  普通 Pod         → Master(有污点)  ❌ 不能调度                        │  │
│   │  带 Toleration Pod → Master(有污点)  ✅ 可以调度                       │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
让 Pod 调度到 Master 的方法
# ═══════════════════════════════════════════════════════════════════════════════
# 方法1:移除 Master 的污点(允许所有 Pod 调度)
# ═══════════════════════════════════════════════════════════════════════════════

# 查看污点
kubectl describe node k8s-master-1 | grep Taints

# 移除单个 Master 的污点(注意最后的减号 -)
kubectl taint nodes k8s-master-1 node-role.kubernetes.io/control-plane:NoSchedule-

# 移除所有 Master 的污点
kubectl taint nodes --all node-role.kubernetes.io/control-plane:NoSchedule-
# ═══════════════════════════════════════════════════════════════════════════════
# 方法2:给特定 Pod 添加容忍(推荐)
# 不改变 Master 的污点,只让特定 Pod 可以调度
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      # ⭐ 添加容忍,允许调度到 Master
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
      
      containers:
      - name: my-app
        image: nginx
# ═══════════════════════════════════════════════════════════════════════════════
# 方法3:强制只在 Master 运行(tolerations + nodeSelector)
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: master-only-app
spec:
  template:
    spec:
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
      
      # 指定只调度到 Master
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      
      containers:
      - name: my-app
        image: nginx
什么时候让 Pod 调度到 Master?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    调度到 Master 的场景判断                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ✅ 适合调度到 Master:                                                         │
│   • 小型集群/测试环境(只有 1-2 台机器)                                        │
│   • 资源紧张,需要充分利用 Master 资源                                          │
│   • 监控 Agent(需要运行在所有节点收集数据)                                    │
│   • 学习环境(单节点集群)                                                      │
│                                                                                 │
│   ❌ 不建议调度到 Master:                                                       │
│   • 生产环境(保持 Master 纯净稳定)                                            │
│   • 资源密集型应用(可能影响 Master)                                           │
│   • 有足够 Worker 节点时                                                        │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

4. Service vs Ingress(核心区别)

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Service vs Ingress 完整对比                                   │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                        Service(服务)                                   │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  作用:为一组 Pod 提供稳定的访问入口(负载均衡)                         │  │
│   │                                                                         │  │
│   │  类型:                                                                  │  │
│   │  ┌────────────┬──────────────────────────────────────────────────────┐ │  │
│   │  │ ClusterIP  │ 只能集群内访问(默认)                                │ │  │
│   │  │            │ 地址:10.96.x.x:port                                  │ │  │
│   │  ├────────────┼──────────────────────────────────────────────────────┤ │  │
│   │  │ NodePort   │ 集群外可通过 NodeIP:NodePort 访问                     │ │  │
│   │  │            │ 端口范围:30000-32767                                 │ │  │
│   │  ├────────────┼──────────────────────────────────────────────────────┤ │  │
│   │  │ LoadBalancer│ 云厂商提供外部负载均衡器                             │ │  │
│   │  │            │ 会分配公网 IP                                         │ │  │
│   │  └────────────┴──────────────────────────────────────────────────────┘ │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                        Ingress(入口)                                   │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  作用:HTTP/HTTPS 七层路由,基于域名/路径分发到不同 Service             │  │
│   │                                                                         │  │
│   │  功能:                                                                  │  │
│   │  • 域名路由:api.example.com → backend-service                         │  │
│   │  • 路径路由:example.com/api → backend-service                         │  │
│   │  • SSL 终止:统一处理 HTTPS 证书                                        │  │
│   │  • 负载均衡:多个后端服务                                               │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
什么时候用 Service?什么时候用 Ingress?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                          使用场景对比                                            │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   场景                              推荐                                        │
│   ────────────────────────────────────────────────────────────────────────────  │
│   Pod 之间内部通信                  Service (ClusterIP)                         │
│   数据库、Redis、消息队列           Service (ClusterIP)                         │
│   需要暴露 TCP/UDP 端口             Service (NodePort/LoadBalancer)             │
│   HTTP/HTTPS 外部访问               Ingress ✅                                  │
│   多个服务共用一个域名              Ingress ✅                                  │
│   需要 SSL/TLS                      Ingress ✅                                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
完整流量架构图
┌─────────────────────────────────────────────────────────────────────────────────┐
│                          完整流量架构                                            │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   用户浏览器                                                                     │
│        │                                                                        │
│        │ https://example.com                                                    │
│        ▼                                                                        │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                    Ingress Controller                                    │  │
│   │               (Nginx/Traefik/ALB)                                        │  │
│   │                                                                          │  │
│   │  规则:                                                                   │  │
│   │  • /        → frontend-service:80                                        │  │
│   │  • /api     → backend-service:8080                                       │  │
│   │  • /admin   → admin-service:8080                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│        │                                                                        │
│        ├────────────────────┬────────────────────┐                             │
│        ▼                    ▼                    ▼                             │
│   ┌──────────────┐   ┌──────────────┐   ┌──────────────┐                      │
│   │ frontend-svc │   │ backend-svc  │   │ admin-svc    │  ← Service           │
│   │ ClusterIP    │   │ ClusterIP    │   │ ClusterIP    │    (负载均衡)        │
│   └──────────────┘   └──────────────┘   └──────────────┘                      │
│        │                    │                    │                             │
│        ▼                    ▼                    ▼                             │
│   ┌──────────────┐   ┌──────────────┐   ┌──────────────┐                      │
│   │ Frontend Pod │   │ Backend Pod  │   │ Admin Pod    │  ← Pod               │
│   │ Frontend Pod │   │ Backend Pod  │   │              │    (实际应用)        │
│   └──────────────┘   └──────────────┘   └──────────────┘                      │
│                             │                                                  │
│                             │ mysql-service.middleware:3306                   │
│                             ▼                                                  │
│                      ┌──────────────┐                                         │
│                      │ mysql-service│  ← Service (ClusterIP)                  │
│                      │              │    只能集群内访问                        │
│                      └──────────────┘                                         │
│                             │                                                  │
│                             ▼                                                  │
│                      ┌──────────────┐                                         │
│                      │  MySQL Pod   │                                         │
│                      └──────────────┘                                         │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Service 三种类型详解
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      Service 类型详解                                            │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  1. ClusterIP(默认)                                                    │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  访问范围:只能集群内部访问                                              │  │
│   │  用途:中间件(MySQL、Redis)、后端服务之间互相调用                      │  │
│   │                                                                          │  │
│   │  type: ClusterIP                                                        │  │
│   │  访问地址:mysql-service:3306(只有 Pod 能访问)                        │  │
│   │                                                                          │  │
│   │  示例:                                                                  │  │
│   │  spec:                                                                   │  │
│   │    type: ClusterIP     # 或不写,默认就是 ClusterIP                      │  │
│   │    ports:                                                                │  │
│   │    - port: 3306                                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  2. NodePort                                                             │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  访问范围:集群外部也能访问                                              │  │
│   │  用途:直接暴露服务给外网、调试、Ingress Controller                      │  │
│   │                                                                          │  │
│   │  type: NodePort                                                         │  │
│   │  访问地址:任意节点IP:30000-32767                                       │  │
│   │  例:http://10.0.1.20:30080                                             │  │
│   │                                                                          │  │
│   │  示例:                                                                  │  │
│   │  spec:                                                                   │  │
│   │    type: NodePort                                                       │  │
│   │    ports:                                                                │  │
│   │    - port: 80                                                           │  │
│   │      nodePort: 30080   # 可选,不写会自动分配                            │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  3. LoadBalancer                                                         │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  访问范围:集群外部访问,云厂商自动分配公网 IP                           │  │
│   │  用途:云环境直接暴露服务(阿里云 SLB、AWS ELB)                         │  │
│   │                                                                          │  │
│   │  type: LoadBalancer                                                     │  │
│   │  访问地址:云厂商分配的公网 IP                                          │  │
│   │                                                                          │  │
│   │  示例:                                                                  │  │
│   │  spec:                                                                   │  │
│   │    type: LoadBalancer                                                   │  │
│   │    ports:                                                                │  │
│   │    - port: 80                                                           │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
谁能访问什么?(访问权限图)
┌─────────────────────────────────────────────────────────────────────────────────┐
│                                                                                 │
│   外部用户(浏览器)                                                             │
│        │                                                                        │
│        │  ❌ 不能直接访问 ClusterIP                                             │
│        │  ✅ 能访问 NodePort(节点IP:端口)                                     │
│        │  ✅ 能访问 LoadBalancer(公网IP)                                      │
│        │  ✅ 能访问 Ingress(域名)← 推荐方式                                   │
│        │                                                                        │
│   ═════╪════════════════════════════════════════════════════════════════════   │
│        │                        K8s 集群边界                                    │
│   ═════╪════════════════════════════════════════════════════════════════════   │
│        │                                                                        │
│        ▼                                                                        │
│   ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐         │
│   │ frontend-service│     │ backend-service │     │ mysql-service   │         │
│   │ ClusterIP       │────→│ ClusterIP       │────→│ ClusterIP       │         │
│   │ :80             │     │ :8080           │     │ :3306           │         │
│   └─────────────────┘     └─────────────────┘     └─────────────────┘         │
│                                                                                 │
│   Pod 之间互相访问:都用 ClusterIP Service,完全 OK ✅                          │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
为什么每个应用都需要 Service?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                  为什么需要 Service?                                            │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   没有 Service:                                                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   你的应用 ──?──→ Pod(IP 随机,重启会变)                               │  │
│   │                    172.16.1.15 → 172.16.2.88 → ???                      │  │
│   │                                                                          │  │
│   │   ❌ Pod 名字是随机的:mysql-7d8f9b6c4d-x2k9j                            │  │
│   │   ❌ Pod IP 是动态的:每次重启都变                                        │  │
│   │   ❌ 无法稳定访问!                                                      │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   有 Service:                                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   你的应用 ──→ Service ──→ Pod                                          │  │
│   │              (固定名字)   (IP 随便变)                                    │  │
│   │              mysql-service:3306                                         │  │
│   │                                                                          │  │
│   │   ✅ Service 名字固定                                                    │  │
│   │   ✅ Service IP 固定                                                     │  │
│   │   ✅ 永远稳定访问!                                                      │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   类比:Service 就像一个"固定的前台电话"                                        │
│   不管后面的员工(Pod)怎么换,电话号码(Service)不变                           │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
一个应用的标准结构
# ═══════════════════════════════════════════════════════════════════════════════
# 标准模板:任何应用都是 Deployment + Service
# ═══════════════════════════════════════════════════════════════════════════════

# 1. Deployment(管理 Pod)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp                    # Deployment 名字
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp                 # 标签(Service 通过这个找 Pod)
  template:
    metadata:
      labels:
        app: myapp               # Pod 的标签
    spec:
      containers:
      - name: myapp              # 容器名
        image: myapp:v1
        ports:
        - containerPort: 8080

---
# 2. Service(提供访问入口)
apiVersion: v1
kind: Service
metadata:
  name: myapp-service            # Service 名字(访问时用这个!)
spec:
  selector:
    app: myapp                   # 通过标签找到对应的 Pod
  ports:
  - port: 8080                   # Service 端口
    targetPort: 8080             # Pod 端口
  type: ClusterIP                # 类型

# 访问方式:myapp-service:8080
Service 类型选择总结
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      Service 类型选择指南                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌────────────────┬─────────────┬─────────────┬─────────────────────────────┐ │
│   │     服务        │ Service类型 │ 内部访问    │          原因               │ │
│   ├────────────────┼─────────────┼─────────────┼─────────────────────────────┤ │
│   │ MySQL          │ ClusterIP   │ ✅          │ 数据库不能暴露给外网        │ │
│   │ Redis          │ ClusterIP   │ ✅          │ 缓存不能暴露给外网          │ │
│   │ RabbitMQ       │ ClusterIP   │ ✅          │ 消息队列不能暴露给外网      │ │
│   │ 后端 API       │ ClusterIP   │ ✅          │ 通过 Ingress 暴露           │ │
│   │ 前端           │ ClusterIP   │ ✅          │ 通过 Ingress 暴露           │ │
│   │ Ingress Ctrl   │ NodePort    │ ✅          │ 需要外部访问入口            │ │
│   └────────────────┴─────────────┴─────────────┴─────────────────────────────┘ │
│                                                                                 │
│   最佳实践:                                                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │ • 所有业务服务都用 ClusterIP                                             │  │
│   │ • 统一通过 Ingress 暴露给外部                                            │  │
│   │ • 只有 Ingress Controller 用 NodePort/LoadBalancer                      │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

3. 前端打包成镜像

打包流程
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      前端打包镜像流程                                            │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   1. 前端源码                     2. npm run build              3. 放入 Nginx   │
│      ├── src/                         │                              │         │
│      ├── package.json         ───────→│ dist/               ───────→│ 镜像    │
│      └── vite.config.ts               │ ├── index.html              │         │
│                                       │ └── assets/                 │         │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
前端 Dockerfile(直接复制使用)
# ═══════════════════════════════════════════════════════════════════════════════
# 前端 Dockerfile
# 文件位置:前端项目根目录/Dockerfile
# ═══════════════════════════════════════════════════════════════════════════════

# ============================================
# 阶段1:构建阶段
# ============================================
FROM node:18-alpine AS builder

WORKDIR /app

# 先复制 package.json(利用 Docker 缓存)
COPY package*.json ./

# 安装依赖(使用国内镜像加速)
RUN npm ci --registry=https://registry.npmmirror.com

# 复制源码并构建
COPY . .
RUN npm run build

# ============================================
# 阶段2:运行阶段
# ============================================
FROM nginx:alpine

# 复制构建产物到 Nginx
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
前端 nginx.conf(处理 SPA 路由)
# ═══════════════════════════════════════════════════════════════════════════════
# nginx.conf
# 文件位置:前端项目根目录/nginx.conf
# ═══════════════════════════════════════════════════════════════════════════════

server {
    listen 80;
    server_name localhost;
    
    root /usr/share/nginx/html;
    index index.html;
    
    # ⭐ SPA 路由支持(所有路由返回 index.html)
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
    
    # Gzip 压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
}
构建和推送命令
# 在前端项目根目录执行

# 1. 构建镜像
docker build -t frontend:v1.0.0 .

# 2. 本地测试
docker run -d -p 8080:80 frontend:v1.0.0
# 访问 http://localhost:8080

# 3. 推送到阿里云(【改】改成你的地址)
docker tag frontend:v1.0.0 registry.cn-hangzhou.aliyuncs.com/你的命名空间/frontend:v1.0.0
docker login registry.cn-hangzhou.aliyuncs.com
docker push registry.cn-hangzhou.aliyuncs.com/你的命名空间/frontend:v1.0.0

4. 前后端同域(关键!避免跨域)

什么是同域?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      同域 vs 跨域                                                │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   跨域(有问题 ❌):                                                            │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │ 前端:https://www.example.com                                           │  │
│   │ 后端:https://api.example.com        ← 域名不同,跨域!                  │  │
│   │                                                                          │  │
│   │ 或者:                                                                    │  │
│   │ 前端:http://localhost:3000                                              │  │
│   │ 后端:http://localhost:8080          ← 端口不同,跨域!                  │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   同域(没问题 ✅):                                                            │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │ 前端:https://example.com/                                               │  │
│   │ 后端:https://example.com/api        ← 同域名,路径不同,不跨域!        │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
通过 Ingress 实现同域(核心方案)
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      Ingress 实现前后端同域                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   用户浏览器                                                                     │
│        │                                                                        │
│        │  https://example.com                                                  │
│        ▼                                                                        │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                         Ingress                                          │  │
│   │                                                                          │  │
│   │   同一个域名,不同路径转发到不同服务:                                    │  │
│   │                                                                          │  │
│   │   example.com/           →  frontend-service:80    (前端)             │  │
│   │   example.com/api        →  backend-service:8080   (后端)             │  │
│   │   example.com/api/user   →  user-service:8080      (用户服务)         │  │
│   │   example.com/api/order  →  order-service:8080     (订单服务)         │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│        │                              │                                        │
│        ▼                              ▼                                        │
│   ┌──────────────┐            ┌──────────────┐                               │
│   │ frontend-svc │            │ backend-svc  │                               │
│   │ (前端 Nginx) │            │ (Spring Boot)│                               │
│   └──────────────┘            └──────────────┘                               │
│                                                                                 │
│   ⭐ 关键:用户看到的都是 example.com,所以是同域,不存在跨域问题!              │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Ingress 配置(实现同域)
# ═══════════════════════════════════════════════════════════════════════════════
# ingress.yaml - 前后端同域配置
# 【改】把 example.com 改成你的域名
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: prod
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
spec:
  rules:
  - host: example.com                    # 【改】你的域名
    http:
      paths:
      # ⭐ 前端:访问 / 转发到前端服务
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      
      # ⭐ 后端:访问 /api 转发到后端服务
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 8080
pathType 路径匹配类型详解
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    pathType 三种类型                                             │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   1. Prefix(前缀匹配,最常用 ✅)                                               │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  pathType: Prefix                                                       │  │
│   │  path: /api                                                             │  │
│   │                                                                          │  │
│   │  匹配:                                                                   │  │
│   │  ✅ /api                                                                 │  │
│   │  ✅ /api/                                                                │  │
│   │  ✅ /api/users                                                          │  │
│   │  ✅ /api/users/123                                                      │  │
│   │  ❌ /apitest(不是路径前缀)                                             │  │
│   │  ❌ /ap                                                                  │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   2. Exact(精确匹配)                                                          │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  pathType: Exact                                                        │  │
│   │  path: /api                                                             │  │
│   │                                                                          │  │
│   │  匹配:                                                                   │  │
│   │  ✅ /api(只匹配这一个)                                                 │  │
│   │  ❌ /api/                                                                │  │
│   │  ❌ /api/users                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   3. ImplementationSpecific(由 Ingress Controller 决定)                       │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  pathType: ImplementationSpecific                                       │  │
│   │                                                                          │  │
│   │  • 不同的 Ingress Controller 行为可能不同                                │  │
│   │  • 一般不推荐使用                                                        │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ⭐ 推荐:99% 的情况用 Prefix 就行!                                           │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Ingress annotations 注解详解
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Ingress annotations 详解                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   annotations 是什么?                                                           │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 给 Ingress 添加额外配置的方式                                         │  │
│   │  • 不同的 Ingress Controller 支持不同的 annotations                      │  │
│   │  • 格式:key: "value"                                                    │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
annotations:
  # ═══════════════════════════════════════════════════════════════════════════════
  # 1. kubernetes.io/ingress.class: nginx
  # ═══════════════════════════════════════════════════════════════════════════════
  # 
  # 作用:指定使用哪个 Ingress Controller 来处理这个 Ingress
  # 
  # 为什么需要?
  # • 集群中可能安装了多个 Ingress Controller(Nginx、Traefik、Kong等)
  # • 需要告诉 K8s 这个 Ingress 由谁来处理
  # 
  # 可选值:
  # • nginx      - Nginx Ingress Controller(最常用)
  # • traefik    - Traefik Ingress Controller
  # • kong       - Kong Ingress Controller
  # • alb        - AWS ALB Ingress Controller
  # 
  # 新版本写法(K8s 1.18+):
  # 可以用 spec.ingressClassName: nginx 替代这个 annotation
  # 
  kubernetes.io/ingress.class: nginx

  # ═══════════════════════════════════════════════════════════════════════════════
  # 2. nginx.ingress.kubernetes.io/proxy-body-size: "100m"
  # ═══════════════════════════════════════════════════════════════════════════════
  # 
  # 作用:设置允许的请求体最大大小
  # 
  # 为什么需要?
  # • 默认 Nginx 只允许 1m 的请求体
  # • 上传文件时会报错 413 Request Entity Too Large
  # • 设置为 100m 允许上传最大 100MB 的文件
  # 
  # 可选值:
  # • "1m"    - 1 MB(默认)
  # • "10m"   - 10 MB
  # • "100m"  - 100 MB
  # • "0"     - 不限制大小(不推荐)
  # 
  # 【改】根据你的需求调整,如果不上传大文件可以用默认值
  # 
  nginx.ingress.kubernetes.io/proxy-body-size: "100m"
常用 Nginx Ingress annotations 大全
# ═══════════════════════════════════════════════════════════════════════════════
# Nginx Ingress Controller 常用 annotations
# ═══════════════════════════════════════════════════════════════════════════════

annotations:
  kubernetes.io/ingress.class: nginx
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 请求大小和超时
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/proxy-body-size: "100m"      # 请求体大小
  nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"  # 连接超时(秒)
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"     # 读取超时(秒)
  nginx.ingress.kubernetes.io/proxy-send-timeout: "60"     # 发送超时(秒)
  
  # ─────────────────────────────────────────────────────────────────────────────
  # SSL/HTTPS 相关
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/ssl-redirect: "true"         # HTTP 自动跳转 HTTPS
  nginx.ingress.kubernetes.io/force-ssl-redirect: "true"   # 强制 HTTPS
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 路径重写(去掉前缀)
  # ─────────────────────────────────────────────────────────────────────────────
  # 例:/api/users → 后端收到 /users(去掉 /api)
  nginx.ingress.kubernetes.io/rewrite-target: /$2
  # 配合 path: /api(/|$)(.*)
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 跨域 CORS
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/enable-cors: "true"
  nginx.ingress.kubernetes.io/cors-allow-origin: "*"
  nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
  nginx.ingress.kubernetes.io/cors-allow-headers: "Content-Type, Authorization"
  
  # ─────────────────────────────────────────────────────────────────────────────
  # WebSocket 支持
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
  nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
  # WebSocket 需要更长的超时
  nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
  nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 限流
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/limit-rps: "100"             # 每秒请求数限制
  nginx.ingress.kubernetes.io/limit-connections: "10"      # 并发连接数限制
  
  # ─────────────────────────────────────────────────────────────────────────────
  # Basic Auth 认证
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/auth-type: basic
  nginx.ingress.kubernetes.io/auth-secret: my-auth-secret  # Secret 名称
  nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 会话保持(粘性会话)
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/affinity: "cookie"
  nginx.ingress.kubernetes.io/session-cookie-name: "SERVERID"
  nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"  # 2天
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 自定义 Nginx 配置片段(高级)
  # ─────────────────────────────────────────────────────────────────────────────
  nginx.ingress.kubernetes.io/configuration-snippet: |
    add_header X-Custom-Header "MyValue";
    proxy_set_header X-Real-IP $remote_addr;
完整 Ingress 示例(带详细注释)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: prod
  annotations:
    # 指定 Ingress Controller
    kubernetes.io/ingress.class: nginx
    
    # 允许上传 100MB 文件
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
    
    # 超时设置(适合长请求)
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    
    # 强制 HTTPS
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    
spec:
  # 新版本推荐用这个替代 annotation
  # ingressClassName: nginx
  
  tls:                                   # HTTPS 配置
  - hosts:
    - example.com
    secretName: tls-secret               # 证书 Secret
    
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 8080
前端配置(请求后端)
// ═══════════════════════════════════════════════════════════════════════════════
// 前端 .env.production
// ═══════════════════════════════════════════════════════════════════════════════

# ⭐ 使用相对路径!因为前后端同域
VITE_API_BASE_URL=/api

// ═══════════════════════════════════════════════════════════════════════════════
// axios 配置
// ═══════════════════════════════════════════════════════════════════════════════

import axios from 'axios';

const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,  // 值为 /api
});

// 发起请求
api.get('/users');
// 实际请求:https://example.com/api/users
// 因为是同域,不存在跨域问题!✅
后端路径说明
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      请求路径转换                                                │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   前端请求                          Ingress 转发                后端收到        │
│   ────────────────────────────────────────────────────────────────────────────  │
│   GET /api/users          →        backend-service:8080   →    /api/users      │
│   POST /api/login         →        backend-service:8080   →    /api/login      │
│   GET /api/products/123   →        backend-service:8080   →    /api/products/123│
│                                                                                 │
│   ⚠️ 注意:Ingress 默认不会去掉 /api 前缀,后端 Controller 需要加 /api         │
│                                                                                 │
│   后端 Controller:                                                              │
│   @RestController                                                               │
│   @RequestMapping("/api/users")    ← 要加 /api                                  │
│   public class UserController {                                                 │
│       @GetMapping                                                               │
│       public List<User> list() { ... }                                         │
│   }                                                                              │
│                                                                                 │
│   或者使用 context-path:                                                        │
│   server.servlet.context-path=/api                                              │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
如果需要去掉 /api 前缀
# ═══════════════════════════════════════════════════════════════════════════════
# 如果后端不想加 /api 前缀,使用 rewrite 注解
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: prod
  annotations:
    kubernetes.io/ingress.class: nginx
    # ⭐ 重写路径:去掉 /api 前缀
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      # 使用正则匹配
      - path: /api(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 8080

# 效果:
# 请求:/api/users  →  后端收到:/users(去掉了 /api)
# 请求:/api/login  →  后端收到:/login
同域的好处
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      同域的好处                                                  │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ✅ 不需要处理 CORS 跨域问题                                                   │
│   ✅ Cookie 自动携带(不需要 withCredentials)                                  │
│   ✅ 不需要 OPTIONS 预检请求,性能更好                                          │
│   ✅ 更安全(Cookie 的 SameSite 默认策略就能工作)                              │
│   ✅ 配置更简单                                                                 │
│                                                                                 │
│   最佳实践:生产环境一定要用同域部署!                                          │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
标准部署架构总结
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      标准部署架构(1 Ingress + N Service)                       │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│                         Ingress(1个,统一入口)                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │   /           → frontend-service                                        │  │
│   │   /api        → backend-service                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│            │                           │                                       │
│            ▼                           ▼                                       │
│   ┌─────────────────┐         ┌─────────────────┐                             │
│   │ frontend-service│         │ backend-service │  ← 2 个 Service             │
│   │ (ClusterIP)     │         │ (ClusterIP)     │                             │
│   └─────────────────┘         └─────────────────┘                             │
│            │                           │                                       │
│            ▼                           ▼                                       │
│   ┌─────────────────┐         ┌─────────────────┐                             │
│   │ frontend        │         │ backend         │  ← 2 个 Deployment          │
│   │ Deployment      │         │ Deployment      │                             │
│   └─────────────────┘         └─────────────────┘                             │
│                                                                                 │
│   资源数量:                                                                     │
│   ┌────────────┬────────┬─────────────────────────────────────────────────────┐│
│   │ 资源        │ 数量   │ 说明                                                ││
│   ├────────────┼────────┼─────────────────────────────────────────────────────┤│
│   │ Ingress    │ 1 个   │ 统一入口,路由到不同 Service                        ││
│   │ Service    │ N 个   │ 每个应用 1 个                                       ││
│   │ Deployment │ N 个   │ 每个应用 1 个                                       ││
│   └────────────┴────────┴─────────────────────────────────────────────────────┘│
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

5. 暴露内部服务到外部(如 Prometheus Dashboard)

有时候需要把集群内部的服务暴露给外部访问,比如:

  • Prometheus 监控页面
  • Grafana Dashboard
  • Kibana 日志页面
  • 数据库管理工具(不推荐暴露)
方式1:通过 Ingress 暴露(推荐 ✅)
# ═══════════════════════════════════════════════════════════════════════════════
# 在现有 Ingress 中添加路径
# 优点:统一入口、支持 HTTPS、可以配置认证
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: prod
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: example.com
    http:
      paths:
      # 前端
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      # 后端 API
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 8080
      # ⭐ 新增:Grafana 监控面板
      - path: /grafana
        pathType: Prefix
        backend:
          service:
            name: prometheus-grafana       # Grafana Service 名
            port:
              number: 80

# 访问:https://example.com/grafana
方式2:单独域名暴露(推荐用于管理后台)
# ═══════════════════════════════════════════════════════════════════════════════
# 使用单独的子域名
# 优点:隔离性好、可以单独配置访问控制
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: monitor-ingress
  namespace: monitoring              # 监控组件的命名空间
  annotations:
    kubernetes.io/ingress.class: nginx
    # 可以添加 Basic Auth 认证
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: monitor-auth
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
spec:
  rules:
  # Grafana
  - host: grafana.example.com        # 【改】子域名
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-grafana
            port:
              number: 80
  # Prometheus
  - host: prometheus.example.com     # 【改】子域名
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-server
            port:
              number: 9090

# 访问:
# https://grafana.example.com
# https://prometheus.example.com
方式3:NodePort 直接暴露(简单但不推荐生产)
# ═══════════════════════════════════════════════════════════════════════════════
# 修改 Service 类型为 NodePort
# 优点:简单快速
# 缺点:端口暴露、没有 HTTPS、不安全
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: v1
kind: Service
metadata:
  name: grafana-nodeport
  namespace: monitoring
spec:
  type: NodePort                     # ⭐ 改为 NodePort
  selector:
    app: grafana
  ports:
  - port: 80                         # ① Service 端口(集群内用)
    targetPort: 3000                 # ② Pod 容器的端口(应用实际监听的)
    nodePort: 30300                  # ③ 节点端口(外部访问用,范围 30000-32767)

# 访问:http://任意节点IP:30300
# 例:http://10.0.1.20:30300

三个端口详解:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Service 三个端口的区别                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ports:                                                                        │
│   - port: 80              # ① Service 端口(集群内部访问用)                    │
│     targetPort: 3000      # ② Pod 端口(容器实际监听的端口)                    │
│     nodePort: 30300       # ③ 节点端口(集群外部访问用)                        │
│                                                                                 │
│   请求链路(从外到内):                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   外部用户                                                                │  │
│   │      │                                                                    │  │
│   │      │ http://10.0.1.20:30300                                            │  │
│   │      ▼                                                                    │  │
│   │   ③ nodePort (30300)  ← 节点上开放的端口,外部访问入口                    │  │
│   │      │                                                                    │  │
│   │      ▼                                                                    │  │
│   │   ① port (80)         ← Service 暴露的端口,集群内用 svc:80 访问         │  │
│   │      │                                                                    │  │
│   │      ▼                                                                    │  │
│   │   ② targetPort (3000) ← Pod 容器的端口,Grafana 在 3000 端口运行         │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   两种访问方式:                                                                 │
│   • 集群内部:grafana-nodeport:80     → 转发到 Pod 的 3000                     │
│   • 集群外部:节点IP:30300             → 转发到 Pod 的 3000                     │
│                                                                                 │
│   简单记忆:                                                                     │
│   ┌────────────┬──────────────────────────────────────────────────────────────┐│
│   │ nodePort   │ 外部入口(节点上开的门)                                     ││
│   │ port       │ 中间转发(Service 的门)                                     ││
│   │ targetPort │ 最终目的地(Pod 容器的门,必须和应用监听的端口一致)         ││
│   └────────────┴──────────────────────────────────────────────────────────────┘│
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
方式4:kubectl port-forward(临时调试用)
# ═══════════════════════════════════════════════════════════════════════════════
# 端口转发(只能本地访问,临时调试用)
# ═══════════════════════════════════════════════════════════════════════════════

# 转发 Grafana
kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring

# 转发 Prometheus
kubectl port-forward svc/prometheus-server 9090:9090 -n monitoring

# 然后本地浏览器访问:
# http://localhost:3000  (Grafana)
# http://localhost:9090  (Prometheus)

# ⚠️ 注意:关闭终端就断开了,只适合临时调试

port-forward 命令详解:

┌─────────────────────────────────────────────────────────────────────────────────┐
│              kubectl port-forward 命令解析                                       │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   命令格式:                                                                     │
│   kubectl port-forward <资源类型>/<资源名> <本地端口>:<目标端口> -n <命名空间>   │
│                                                                                 │
│   示例解析:                                                                     │
│   kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring            │
│                        │                      │    │      │                    │
│                        │                      │    │      └─ 命名空间          │
│                        │                      │    └─ Service 的端口(80)     │
│                        │                      └─ 本地监听的端口(3000)        │
│                        └─ svc/ 表示 Service 资源,prometheus-grafana 是名字    │
│                                                                                 │
│   工作原理:                                                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  你的电脑                           K8s 集群                             │  │
│   │  ┌──────────────┐                  ┌──────────────┐                     │  │
│   │  │ 浏览器       │                  │ Grafana Pod  │                     │  │
│   │  │ localhost:3000 ─────────────→   │ :3000        │                     │  │
│   │  └──────────────┘    隧道          └──────────────┘                     │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   资源类型可以是:                                                               │
│   • svc/xxx     - Service                                                      │
│   • pod/xxx     - 直接转发到 Pod                                               │
│   • deploy/xxx  - Deployment(会选择一个 Pod)                                 │
│                                                                                 │
│   使用场景:                                                                     │
│   • 临时调试集群内服务                                                          │
│   • 不想暴露服务到外网                                                          │
│   • 快速验证服务是否正常                                                        │
│                                                                                 │
│   ⚠️ 限制:                                                                     │
│   • 只能本机访问,其他电脑不行                                                  │
│   • 终端关闭就断开                                                              │
│   • 不适合生产环境                                                              │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
暴露方式对比
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      暴露内部服务的方式对比                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌────────────────┬───────────┬───────────┬───────────┬────────────────────┐  │
│   │     方式        │  安全性   │  HTTPS    │  生产环境  │      适用场景      │  │
│   ├────────────────┼───────────┼───────────┼───────────┼────────────────────┤  │
│   │ Ingress 路径   │   高 ✅    │   支持    │    推荐    │ 同域名下的管理页面 │  │
│   │ Ingress 子域名 │   高 ✅    │   支持    │    推荐    │ 独立的管理系统     │  │
│   │ NodePort      │   低 ❌    │   不支持  │   不推荐   │ 测试环境快速验证   │  │
│   │ port-forward  │   高 ✅    │   N/A     │   仅调试   │ 临时调试           │  │
│   └────────────────┴───────────┴───────────┴───────────┴────────────────────┘  │
│                                                                                 │
│   ⭐ 推荐:生产环境用 Ingress + 子域名 + Basic Auth / OAuth                     │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
为 Ingress 添加 Basic Auth 认证

什么是 Basic Auth?

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Basic Auth 认证                                               │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   Basic Auth = 最简单的 HTTP 认证方式                                            │
│                                                                                 │
│   访问流程:                                                                     │
│   1. 用户访问 https://grafana.example.com                                       │
│   2. 浏览器弹出用户名密码输入框                                                  │
│   3. 输入正确后才能访问页面                                                      │
│                                                                                 │
│   适用场景:                                                                     │
│   • 内部管理页面(Grafana、Prometheus)                                         │
│   • 快速添加访问控制                                                            │
│   • 不想集成复杂的认证系统                                                      │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
# ═══════════════════════════════════════════════════════════════════════════════
# 给监控页面加密码保护
# ═══════════════════════════════════════════════════════════════════════════════

# 1. 安装 htpasswd 工具(用于生成密码文件)
#    htpasswd 是 Apache 的工具,用于创建用户名密码文件
apt install apache2-utils

# 2. 创建密码文件
#    -c 表示创建新文件(如果已存在会覆盖)
#    auth 是文件名(会在当前目录生成)
#    admin 是用户名【改】可以改成你想要的用户名
#    执行后会提示输入密码(输入两次)
htpasswd -c auth admin

# 3. 创建 Secret(把密码文件存到 K8s 中)
#    generic 是 Secret 类型
#    monitor-auth 是 Secret 名称【改】可以自定义
#    --from-file=auth 从 auth 文件读取内容
#    -n monitoring 指定命名空间
kubectl create secret generic monitor-auth \
  --from-file=auth \
  -n monitoring

# 4. 验证 Secret 创建成功
kubectl get secret monitor-auth -n monitoring

# 5. 添加更多用户(不用 -c,否则会覆盖)
htpasswd auth another-user
kubectl delete secret monitor-auth -n monitoring
kubectl create secret generic monitor-auth --from-file=auth -n monitoring
完整示例:暴露 Prometheus + Grafana(带详细注释)
# ═══════════════════════════════════════════════════════════════════════════════
# monitor-ingress.yaml
# 文件位置:~/k8s/monitoring/ingress.yaml
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: monitor-ingress
  namespace: monitoring              # 【改】监控组件所在的命名空间
  annotations:
    kubernetes.io/ingress.class: nginx
    
    # ─────────────────────────────────────────────────────────────────────────
    # Basic Auth 认证配置(三个必须一起用)
    # ─────────────────────────────────────────────────────────────────────────
    
    # auth-type: 认证类型,目前只支持 basic
    nginx.ingress.kubernetes.io/auth-type: basic
    
    # auth-secret: 存储用户名密码的 Secret 名称
    # 【改】改成你创建的 Secret 名称(上面 htpasswd 创建的)
    nginx.ingress.kubernetes.io/auth-secret: monitor-auth
    
    # auth-realm: 认证提示信息(浏览器弹窗显示的文字)
    # 【改】可以自定义提示文字
    nginx.ingress.kubernetes.io/auth-realm: "Monitor Area"

spec:
  # ─────────────────────────────────────────────────────────────────────────────
  # TLS 配置(HTTPS 证书)
  # ─────────────────────────────────────────────────────────────────────────────
  tls:
  - hosts:
    # 这里列出需要 HTTPS 的域名
    - grafana.example.com          # 【改】你的域名
    - prometheus.example.com       # 【改】你的域名
    
    # 证书存储的 Secret 名称
    # 【改】需要提前创建这个 Secret,或使用 cert-manager 自动创建
    secretName: monitor-tls
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 路由规则(多个域名分别配置)
  # ─────────────────────────────────────────────────────────────────────────────
  rules:
  # Grafana
  - host: grafana.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-grafana
            port:
              number: 80
  # Prometheus
  - host: prometheus.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-kube-prometheus-prometheus
            port:
              number: 9090
  # AlertManager
  - host: alertmanager.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-kube-prometheus-alertmanager
            port:
              number: 9093

# 访问(需要输入用户名密码):
# https://grafana.example.com
# https://prometheus.example.com
# https://alertmanager.example.com
TLS Secret 如何创建?
# ═══════════════════════════════════════════════════════════════════════════════
# 方式1:手动创建 TLS Secret(已有证书文件)
# ═══════════════════════════════════════════════════════════════════════════════

# 假设你已有证书文件:
# - example.com.crt(证书文件)
# - example.com.key(私钥文件)

kubectl create secret tls monitor-tls \
  --cert=example.com.crt \
  --key=example.com.key \
  -n monitoring

# ═══════════════════════════════════════════════════════════════════════════════
# 方式2:使用 cert-manager 自动申请证书(推荐)
# ═══════════════════════════════════════════════════════════════════════════════

# cert-manager 会自动:
# 1. 向 Let's Encrypt 申请免费证书
# 2. 创建 TLS Secret
# 3. 证书到期前自动续期

# 只需要在 Ingress 添加注解:
# annotations:
#   cert-manager.io/cluster-issuer: letsencrypt-prod
# 
# tls:
# - hosts:
#   - grafana.example.com
#   secretName: monitor-tls    # cert-manager 会自动创建这个 Secret
TLS 配置详解
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Ingress TLS 配置详解                                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   spec:                                                                         │
│     tls:                           # TLS 配置数组                               │
│     - hosts:                       # 这个证书覆盖的域名列表                      │
│       - grafana.example.com        # 域名1                                     │
│       - prometheus.example.com     # 域名2(多个域名可以共用一个证书)          │
│       secretName: monitor-tls      # 证书存储的 Secret 名称                     │
│                                                                                 │
│   Secret 内容:                                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  apiVersion: v1                                                         │  │
│   │  kind: Secret                                                           │  │
│   │  metadata:                                                              │  │
│   │    name: monitor-tls                                                    │  │
│   │  type: kubernetes.io/tls       # 类型必须是 tls                         │  │
│   │  data:                                                                  │  │
│   │    tls.crt: <base64编码的证书>  # 证书内容                              │  │
│   │    tls.key: <base64编码的私钥>  # 私钥内容                              │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ⚠️ 注意:                                                                     │
│   • Secret 必须和 Ingress 在同一个命名空间                                      │
│   • hosts 中的域名必须和 rules 中的 host 匹配                                  │
│   • 没有配置 tls 的域名会使用 HTTP(不加密)                                   │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

6. K8s 集群网络核心概念(重要!)

为什么访问任意节点的 Ingress 都一样?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    K8s 集群是一体化的!                                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   核心概念:K8s 集群是一个统一的整体,不是独立的服务器!                          │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                        K8s 集群                                         │  │
│   │                                                                          │  │
│   │   ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                    │  │
│   │   │  Worker-1   │  │  Worker-2   │  │  Worker-3   │                    │  │
│   │   │             │  │             │  │             │                    │  │
│   │   │ • Pod-A     │  │ • Pod-B     │  │ • Pod-C     │                    │  │
│   │   │ • Pod-D     │  │ • Pod-E     │  │             │                    │  │
│   │   └─────────────┘  └─────────────┘  └─────────────┘                    │  │
│   │          │                │                │                            │  │
│   │          └────────────────┼────────────────┘                            │  │
│   │                           │                                             │  │
│   │                    ┌──────┴──────┐                                      │  │
│   │                    │   统一网络   │                                      │  │
│   │                    │  (扁平化)   │                                      │  │
│   │                    └─────────────┘                                      │  │
│   │                                                                          │  │
│   │   ⭐ 所有 Pod 都在同一个虚拟网络中,无论在哪个节点!                      │  │
│   │   ⭐ Service 会自动找到所有匹配的 Pod,无论在哪个节点!                   │  │
│   │   ⭐ 访问任意节点的 NodePort,都能到达任意节点的 Pod!                    │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
流量路由原理
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    请求到达任意节点后的流量路由                                   │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   假设 backend 有 3 个 Pod,分布在不同节点:                                     │
│   • backend-pod-1 在 Worker-1                                                  │
│   • backend-pod-2 在 Worker-2                                                  │
│   • backend-pod-3 在 Worker-3                                                  │
│                                                                                 │
│   情况1:请求到达 Worker-1                                                       │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   请求 → Worker-1:30080 → Ingress → backend-service                     │  │
│   │                                            │                             │  │
│   │                          ┌─────────────────┼─────────────────┐           │  │
│   │                          ▼                 ▼                 ▼           │  │
│   │                    backend-pod-1    backend-pod-2    backend-pod-3       │  │
│   │                    (Worker-1)       (Worker-2)       (Worker-3)          │  │
│   │                                                                          │  │
│   │   ⭐ Service 会负载均衡到任意一个 Pod,可能在本节点,也可能跨节点!        │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   情况2:请求到达 Worker-2(结果完全一样!)                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   请求 → Worker-2:30080 → Ingress → backend-service                     │  │
│   │                                            │                             │  │
│   │                          ┌─────────────────┼─────────────────┐           │  │
│   │                          ▼                 ▼                 ▼           │  │
│   │                    backend-pod-1    backend-pod-2    backend-pod-3       │  │
│   │                    (Worker-1)       (Worker-2)       (Worker-3)          │  │
│   │                                                                          │  │
│   │   ⭐ 无论请求从哪个节点进来,最终都能到达任意节点的 Pod!                  │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
为什么能做到这样?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    K8s 网络的三大关键组件                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   1. CNI 网络插件(如 Calico)                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 为每个 Pod 分配 IP(如 172.16.x.x)                                   │  │
│   │  • 建立跨节点的虚拟网络                                                  │  │
│   │  • 让所有 Pod 都能互相访问,无论在哪个节点                               │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   2. kube-proxy                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 运行在每个节点上                                                      │  │
│   │  • 维护 Service → Pod 的映射规则(iptables/ipvs)                       │  │
│   │  • 实现 Service 的负载均衡                                               │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   3. CoreDNS                                                                    │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 提供集群内部 DNS 解析                                                 │  │
│   │  • mysql-service → 10.96.x.x(Service IP)                              │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   结果:                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 访问任意节点的 NodePort → 都能到达集群内任意 Pod                      │  │
│   │  • 访问 Service 名称 → 自动负载均衡到所有匹配的 Pod                      │  │
│   │  • Pod 在哪个节点 → 对调用者完全透明!                                   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
总结:为什么负载均衡器指向任意节点都行?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    核心理解                                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   Q: 为什么负载均衡器配置所有 Worker 节点都行?                                  │
│                                                                                 │
│   A: 因为 K8s 集群是一体化的!                                                  │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   负载均衡器 ─┬─→ Worker-1:30080 ─┐                                     │  │
│   │              ├─→ Worker-2:30080 ─┼─→ Ingress → Service → 任意节点的Pod │  │
│   │              └─→ Worker-3:30080 ─┘                                     │  │
│   │                                                                          │  │
│   │   无论请求从哪个节点进入,最终效果完全一样!                              │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ✅ Ingress Controller 运行在每个节点(DaemonSet 或多副本)                    │
│   ✅ Ingress 规则是集群级别的,所有节点共享                                     │
│   ✅ Service 是集群级别的,自动发现所有 Pod                                     │
│   ✅ Pod 可以调度到任意节点,对外透明                                           │
│                                                                                 │
│   这就是 K8s 的魔力:把多台服务器变成一个统一的计算资源池!                     │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

7. 域名配置详解

K8s 内部域名 vs 外部域名
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    两种域名的区别                                                │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  1. K8s 内部 DNS(自动生成,不用买)                                     │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  格式:<service>.<namespace>.svc.cluster.local                          │  │
│   │  例:mysql-service.middleware.svc.cluster.local                         │  │
│   │                                                                          │  │
│   │  ⚠️ 只能在集群内部 Pod 之间使用!外部用户无法访问!                      │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  2. 外部域名(需要购买注册!)                                           │  │
│   │  ─────────────────────────────────────────────────────────────────────  │  │
│   │  用途:Ingress 配置,让外部用户访问                                      │  │
│   │  例:example.com、grafana.example.com                                   │  │
│   │                                                                          │  │
│   │  ⭐ 需要去阿里云/腾讯云等购买注册!然后配置 DNS 解析!                   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
域名访问的完整链路
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    域名到 Ingress 的完整链路                                     │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   用户输入:https://example.com                                                 │
│        │                                                                        │
│        │ ① DNS 解析(在阿里云配置的)                                           │
│        │    example.com → 47.100.1.200(负载均衡器 IP)                         │
│        ▼                                                                        │
│   负载均衡器(阿里云 SLB)                                                        │
│        │                                                                        │
│        │ ② 转发到 K8s 集群任意 Worker 节点                                      │
│        ▼                                                                        │
│   K8s Worker 节点:30080                                                         │
│        │                                                                        │
│        │ ③ Ingress Controller 接收请求,读取 Host 头                            │
│        ▼                                                                        │
│   Ingress 规则匹配                                                               │
│        │                                                                        │
│        │ ④ host: example.com, path: / → frontend-service                       │
│        ▼                                                                        │
│   Service → Pod                                                                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

7. 集群环境域名指向(负载均衡)

问题:集群有多个节点,域名指向哪个 IP?
┌─────────────────────────────────────────────────────────────────────────────────┐
│   你的集群:                                                                     │
│   Worker-1: 10.0.1.20 (公网: 47.100.1.100)                                      │
│   Worker-2: 10.0.1.21 (公网: 47.100.1.101)                                      │
│   Worker-3: 10.0.1.22 (公网: 47.100.1.102)                                      │
│                                                                                 │
│   问题:example.com 应该指向哪个 IP?                                           │
└─────────────────────────────────────────────────────────────────────────────────┘
方案对比
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      集群域名指向方案对比                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌────────────────┬────────────┬──────────┬──────────┬──────────────────────┐ │
│   │     方案        │   成本     │  复杂度  │  高可用  │      推荐场景        │ │
│   ├────────────────┼────────────┼──────────┼──────────┼──────────────────────┤ │
│   │ DNS 多记录     │ 免费       │ 低       │ ❌       │ 测试环境             │ │
│   │ 阿里云 SLB ✅  │ ¥50+/月   │ 低       │ ✅       │ 生产环境推荐         │ │
│   │ 自建 Nginx LB  │ 服务器成本 │ 中       │ ✅       │ 自建机房             │ │
│   └────────────────┴────────────┴──────────┴──────────┴──────────────────────┘ │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
方案1:阿里云 SLB(生产推荐 ✅)
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    使用阿里云 SLB 负载均衡                                       │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   用户 → example.com → SLB (47.100.1.200) → K8s Workers                        │
│                                                                                 │
│   步骤1:创建 SLB                                                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  1. 阿里云控制台 → 负载均衡                                              │  │
│   │  2. 创建负载均衡实例(公网类型)                                          │  │
│   │  3. 获得公网 IP:如 47.100.1.200                                        │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   步骤2:配置后端服务器组                                                        │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  ┌────────────────┬────────────┬────────────┐                           │  │
│   │  │    服务器       │   端口     │   权重     │                           │  │
│   │  ├────────────────┼────────────┼────────────┤                           │  │
│   │  │ Worker-1 ECS   │   30080    │   100      │                           │  │
│   │  │ Worker-2 ECS   │   30080    │   100      │                           │  │
│   │  │ Worker-3 ECS   │   30080    │   100      │                           │  │
│   │  └────────────────┴────────────┴────────────┘                           │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   步骤3:配置监听                                                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  ┌────────────┬────────────┬────────────────────────────────────────┐   │  │
│   │  │ 前端端口   │ 后端端口   │ 协议                                    │   │  │
│   │  ├────────────┼────────────┼────────────────────────────────────────┤   │  │
│   │  │ 80         │ 30080      │ HTTP                                   │   │  │
│   │  │ 443        │ 30443      │ HTTPS(上传证书)                      │   │  │
│   │  └────────────┴────────────┴────────────────────────────────────────┘   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   步骤4:配置健康检查                                                            │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  检查端口:30080                                                        │  │
│   │  检查路径:/healthz(或 /)                                             │  │
│   │  检查间隔:5秒                                                          │  │
│   │  不健康阈值:3次                                                        │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   步骤5:DNS 配置                                                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  example.com  →  A 记录  →  47.100.1.200(SLB 的公网 IP)              │  │
│   │  *.example.com → A 记录  →  47.100.1.200(泛解析)                     │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ✅ 优点:高可用、健康检查、自动剔除故障节点、SSL 终止                          │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
方案2:自建 Nginx 负载均衡(省钱方案)
# ═══════════════════════════════════════════════════════════════════════════════
# 单独一台服务器安装 Nginx 作为入口
# 服务器公网 IP: 47.100.1.200
# 配置文件:/etc/nginx/nginx.conf
# ═══════════════════════════════════════════════════════════════════════════════

upstream k8s_ingress {
    # 后端 K8s Worker 节点
    server 10.0.1.20:30080 weight=1;   # Worker-1
    server 10.0.1.21:30080 weight=1;   # Worker-2
    server 10.0.1.22:30080 weight=1;   # Worker-3
}

# HTTP
server {
    listen 80;
    server_name example.com *.example.com;
    
    # 重定向到 HTTPS
    return 301 https://$host$request_uri;
}

# HTTPS
server {
    listen 443 ssl;
    server_name example.com *.example.com;
    
    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    
    location / {
        proxy_pass http://k8s_ingress;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# DNS 配置:example.com → 47.100.1.200(Nginx 服务器 IP)
方案3:DNS 多记录(仅测试环境)
┌─────────────────────────────────────────────────────────────────────────────────┐
│   阿里云 DNS 配置多条 A 记录(DNS 轮询,不推荐生产)                             │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌──────────────┬────────┬─────────────────┐                                  │
│   │ example.com  │ A      │ 47.100.1.100    │  Worker-1                        │
│   │ example.com  │ A      │ 47.100.1.101    │  Worker-2                        │
│   │ example.com  │ A      │ 47.100.1.102    │  Worker-3                        │
│   └──────────────┴────────┴─────────────────┘                                  │
│                                                                                 │
│   ❌ 缺点:                                                                      │
│   • DNS 不会检测节点是否健康                                                    │
│   • 某个节点挂了,用户还是会被解析到这个 IP                                     │
│   • DNS 缓存导致切换慢                                                          │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
没有域名的临时方案
┌─────────────────────────────────────────────────────────────────────────────────┐
│                      没有域名的临时访问方案                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   方案1:直接用 IP + NodePort                                                    │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  http://47.100.1.100:30080                                              │  │
│   │                                                                          │  │
│   │  这里的 IP 是什么?                                                      │  │
│   │  ✅ 任意 Worker 节点的公网 IP(或内网 IP)                               │  │
│   │  ❌ 不是 Service 的 ClusterIP(那个只能集群内访问)                      │  │
│   │  ❌ 不是 Ingress 的 IP(Ingress 走 80/443 端口,不用 NodePort)          │  │
│   │                                                                          │  │
│   │  为什么"任意节点"都行?                                                  │  │
│   │  → K8s 会在每个节点都开放相同的 NodePort                                 │  │
│   │  → 访问任一节点:30080,K8s 内部自动路由到正确的 Pod                      │  │
│   │                                                                          │  │
│   │  缺点:不能用 HTTPS、不专业、单节点故障无法访问                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   方案2:修改本地 hosts 文件(仅自己电脑能访问)                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  # Windows: C:\Windows\System32\drivers\etc\hosts                       │  │
│   │  # Mac/Linux: /etc/hosts                                                │  │
│   │                                                                          │  │
│   │  47.100.1.100  myapp.com                                                │  │
│   │  47.100.1.100  grafana.myapp.com                                        │  │
│   │                                                                          │  │
│   │  然后浏览器访问 http://myapp.com:30080                                   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   方案3:使用免费域名服务(测试用)                                              │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  nip.io:47-100-1-100.nip.io 自动解析到 47.100.1.100                    │  │
│   │  sslip.io:47.100.1.100.sslip.io                                        │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
生产环境完整架构图
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    生产环境完整架构                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   用户                                                                          │
│     │                                                                           │
│     │ https://example.com                                                       │
│     ▼                                                                           │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                    DNS 解析                                              │  │
│   │              example.com → 负载均衡器 IP                                 │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│     │                                                                           │
│     ▼                                                                           │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │          负载均衡器(阿里云 SLB / 自建 Nginx)                            │  │
│   │          • 监听 80/443                                                   │  │
│   │          • SSL 证书                                                      │  │
│   │          • 健康检查                                                      │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│           │                    │                    │                          │
│           ▼                    ▼                    ▼                          │
│   ┌──────────────────────────────────────────────────────────────────────────┐ │
│   │                        K8s 集群                                          │ │
│   │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                   │ │
│   │  │  Worker-1    │  │  Worker-2    │  │  Worker-3    │                   │ │
│   │  │  :30080      │  │  :30080      │  │  :30080      │                   │ │
│   │  │              │  │              │  │              │                   │ │
│   │  │ Ingress Ctrl │  │ Ingress Ctrl │  │ Ingress Ctrl │                   │ │
│   │  └──────────────┘  └──────────────┘  └──────────────┘                   │ │
│   │                            │                                             │ │
│   │                            ▼                                             │ │
│   │   ┌─────────────────────────────────────────────────────────────────┐   │ │
│   │   │                    Ingress 规则                                  │   │ │
│   │   │                                                                  │   │ │
│   │   │   example.com/        → frontend-service                        │   │ │
│   │   │   example.com/api     → backend-service                         │   │ │
│   │   │   grafana.example.com → grafana-service                         │   │ │
│   │   └─────────────────────────────────────────────────────────────────┘   │ │
│   │                            │                                             │ │
│   │                            ▼                                             │ │
│   │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  │ │
│   │   │ frontend-svc │  │ backend-svc  │  │ grafana-svc  │                  │ │
│   │   │ (ClusterIP)  │  │ (ClusterIP)  │  │ (ClusterIP)  │                  │ │
│   │   └──────────────┘  └──────────────┘  └──────────────┘                  │ │
│   │          │                 │                 │                          │ │
│   │          ▼                 ▼                 ▼                          │ │
│   │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  │ │
│   │   │ Frontend Pod │  │ Backend Pod  │  │ Grafana Pod  │                  │ │
│   │   └──────────────┘  └──────────────┘  └──────────────┘                  │ │
│   └──────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

8. ConfigMap 使用详解

什么是 ConfigMap?
ConfigMap = 配置文件/配置项的存储
用途:把配置和应用分离,不用重新构建镜像就能修改配置
ConfigMap 三种使用方式
# ═══════════════════════════════════════════════════════════════════════════════
# ConfigMap 定义
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: prod
data:
  # 方式1:键值对
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  
  # 方式2:完整配置文件
  application.yml: |
    server:
      port: 8080
    spring:
      profiles:
        active: prod
    logging:
      level:
        root: INFO
  
  # 方式3:nginx 配置文件
  nginx.conf: |
    server {
        listen 80;
        location / {
            root /usr/share/nginx/html;
        }
    }
在 Pod 中使用 ConfigMap
# ═══════════════════════════════════════════════════════════════════════════════
# 方式1:作为环境变量
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: prod
spec:
  template:
    spec:
      containers:
      - name: backend
        image: myapp:v1
        env:
        # 引用单个值
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config        # ConfigMap 名称
              key: APP_ENV            # ConfigMap 中的 key
        
        # 引用所有值作为环境变量
        envFrom:
        - configMapRef:
            name: app-config

# ═══════════════════════════════════════════════════════════════════════════════
# 方式2:挂载为文件
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: prod
spec:
  template:
    spec:
      containers:
      - name: backend
        image: myapp:v1
        volumeMounts:
        - name: config-volume
          mountPath: /app/config         # 挂载到容器的这个目录
          # 结果:/app/config/application.yml 文件
      
      volumes:
      - name: config-volume
        configMap:
          name: app-config
          items:
          - key: application.yml         # ConfigMap 中的 key
            path: application.yml        # 文件名

# ═══════════════════════════════════════════════════════════════════════════════
# 方式3:挂载为单个文件(不覆盖目录)
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: prod
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/nginx.conf  # 直接挂载为文件
          subPath: nginx.conf               # 使用 subPath 不覆盖目录
      
      volumes:
      - name: nginx-config
        configMap:
          name: app-config
ConfigMap vs Secret 对比
对比项ConfigMapSecret
用途普通配置敏感信息(密码、密钥)
存储明文Base64 编码(可加密)
使用场景环境变量、配置文件密码、证书、Token
示例日志级别、端口号数据库密码、API Key
# Secret 示例(敏感信息用这个)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: prod
stringData:                    # stringData 自动 Base64 编码
  MYSQL_PASSWORD: "MyPassword123!"
  REDIS_PASSWORD: "RedisPass456!"

4. 前端项目访问地址配置

浏览器访问地址 vs 前端配置的后端地址
┌─────────────────────────────────────────────────────────────────────────────────┐
│                     前端项目地址配置详解                                         │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   假设你的域名是:example.com                                                    │
│                                                                                 │
│   ════════════════════════════════════════════════════════════════════════════ │
│   1. 用户浏览器访问地址                                                         │
│   ════════════════════════════════════════════════════════════════════════════ │
│                                                                                 │
│   用户在浏览器输入:https://example.com                                         │
│   或:http://10.0.1.20:30080(通过 NodePort)                                   │
│                                                                                 │
│   ════════════════════════════════════════════════════════════════════════════ │
│   2. 前端项目配置的后端地址(关键!)                                           │
│   ════════════════════════════════════════════════════════════════════════════ │
│                                                                                 │
│   前端 .env 或配置文件:                                                        │
│   ┌──────────────────────────────────────────────────────────────────────────┐ │
│   │ # 方式1:相对路径(推荐 ✅)                                              │ │
│   │ VITE_API_BASE_URL=/api                                                   │ │
│   │                                                                          │ │
│   │ # 方式2:完整域名                                                         │ │
│   │ VITE_API_BASE_URL=https://example.com/api                                │ │
│   └──────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│   为什么用 /api 而不是后端内部地址?                                            │
│   因为前端代码运行在用户浏览器,只能访问外部地址!                              │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
完整请求流程图
┌─────────────────────────────────────────────────────────────────────────────────┐
│                        完整请求流程                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   用户浏览器                                                                     │
│   │                                                                             │
│   │ 1. 访问 https://example.com                                                │
│   │    (获取前端静态文件 index.html, js, css)                                  │
│   │                                                                             │
│   │ 2. 前端 JS 发起 API 请求                                                   │
│   │    axios.get('/api/users')                                                 │
│   │    实际请求:https://example.com/api/users                                 │
│   ▼                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                         Ingress                                          │  │
│   │                                                                          │  │
│   │  路由规则:                                                               │  │
│   │  ┌────────────────────────────────────────────────────────────────────┐ │  │
│   │  │ path: /           → frontend-service:80   (前端静态文件)           │ │  │
│   │  │ path: /api        → backend-service:8080  (后端 API)               │ │  │
│   │  └────────────────────────────────────────────────────────────────────┘ │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   请求1: https://example.com/                                                   │
│        → frontend-service → Frontend Pod → 返回 index.html                     │
│                                                                                 │
│   请求2: https://example.com/api/users                                          │
│        → backend-service → Backend Pod → 返回 JSON 数据                        │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
前端配置示例
// ═══════════════════════════════════════════════════════════════════════════════
// Vue/React 前端配置示例
// ═══════════════════════════════════════════════════════════════════════════════

// .env.production(生产环境)
VITE_API_BASE_URL=/api

// axios 配置
const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,  // 值为 /api
});

// 发起请求
api.get('/users');
// 实际请求 URL:https://example.com/api/users(当前域名 + /api/users)
前端 Nginx 配置(前端镜像内)
# ═══════════════════════════════════════════════════════════════════════════════
# 前端镜像中的 nginx.conf(处理 SPA 路由)
# 文件位置:前端项目/nginx.conf
# ═══════════════════════════════════════════════════════════════════════════════

server {
    listen 80;
    server_name localhost;
    
    root /usr/share/nginx/html;
    index index.html;
    
    # SPA 路由支持
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}
前端 Dockerfile 示例
# ═══════════════════════════════════════════════════════════════════════════════
# 前端 Dockerfile
# ═══════════════════════════════════════════════════════════════════════════════

# 构建阶段
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

一、服务器规划

1.1 服务器清单

┌─────────────────────────────────────────────────────────────────────────────────┐
│                           服务器规划                                             │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   【改】下面的 IP 地址改成你实际的 IP                                           │
│                                                                                 │
│   ┌──────────────────┬─────────────────┬────────────┬────────────────────────┐ │
│   │      主机名       │       IP        │    角色    │         配置           │ │
│   ├──────────────────┼─────────────────┼────────────┼────────────────────────┤ │
│   │ k8s-master-1     │ 10.0.1.10       │ Master     │ 4核8G 100G SSD         │ │
│   │ k8s-master-2     │ 10.0.1.11       │ Master     │ 4核8G 100G SSD         │ │
│   │ k8s-master-3     │ 10.0.1.12       │ Master     │ 4核8G 100G SSD         │ │
│   │ k8s-worker-1     │ 10.0.1.20       │ Worker     │ 8核16G 200G SSD        │ │
│   │ k8s-worker-2     │ 10.0.1.21       │ Worker     │ 8核16G 200G SSD        │ │
│   │ k8s-worker-3     │ 10.0.1.22       │ Worker     │ 8核16G 200G SSD        │ │
│   └──────────────────┴─────────────────┴────────────┴────────────────────────┘ │
│                                                                                 │
│   VIP(高可用虚拟IP):10.0.1.100                                               │
│   网关:10.0.1.1                                                                │
│                                                                                 │
│   K8s 网络规划:                                                                 │
│   • Pod 网段:172.16.0.0/16                                                     │
│   • Service 网段:10.96.0.0/16                                                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

1.2 确认事项

# 确保所有服务器:
# 1. 操作系统:Ubuntu 22.04 LTS(推荐)或 CentOS 7.9
# 2. 网络互通:所有节点能互相 ping 通
# 3. 有 root 权限
# 4. 能访问互联网(下载软件包)

二、所有节点初始化

⚠️ 以下命令在【所有6台服务器】上执行

2.1 设置主机名

# ═══════════════════════════════════════════════════════════════════════════════
# 【分别在每台服务器执行】设置对应的主机名
# ═══════════════════════════════════════════════════════════════════════════════

# 在 10.0.1.10 执行:
hostnamectl set-hostname k8s-master-1

# 在 10.0.1.11 执行:
hostnamectl set-hostname k8s-master-2

# 在 10.0.1.12 执行:
hostnamectl set-hostname k8s-master-3

# 在 10.0.1.20 执行:
hostnamectl set-hostname k8s-worker-1

# 在 10.0.1.21 执行:
hostnamectl set-hostname k8s-worker-2

# 在 10.0.1.22 执行:
hostnamectl set-hostname k8s-worker-3

2.2 配置 hosts 文件

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】配置 hosts
# 【改】把 IP 改成你实际的 IP
# ═══════════════════════════════════════════════════════════════════════════════

cat >> /etc/hosts << 'EOF'
# K8s Cluster
10.0.1.10    k8s-master-1
10.0.1.11    k8s-master-2
10.0.1.12    k8s-master-3
10.0.1.100   k8s-vip
10.0.1.20    k8s-worker-1
10.0.1.21    k8s-worker-2
10.0.1.22    k8s-worker-3
EOF

# 验证
cat /etc/hosts

2.3 关闭 Swap

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】关闭 Swap
# 原因:K8s 要求关闭 Swap,否则 kubelet 无法启动
# ═══════════════════════════════════════════════════════════════════════════════

# 临时关闭
swapoff -a

# 永久关闭(删除 fstab 中的 swap 行)
sed -i '/swap/d' /etc/fstab

# 验证(Swap 行应该全是 0)
free -h

2.4 配置内核参数

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】配置内核参数
# 原因:K8s 网络需要这些内核参数
# ═══════════════════════════════════════════════════════════════════════════════

# 加载内核模块
cat > /etc/modules-load.d/k8s.conf << 'EOF'
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

# 配置内核参数
cat > /etc/sysctl.d/k8s.conf << 'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

# 应用配置
sysctl --system

# 验证
lsmod | grep br_netfilter
sysctl net.bridge.bridge-nf-call-iptables

2.5 配置时间同步

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】配置时间同步
# 原因:集群节点时间必须一致,否则证书验证会失败
# ═══════════════════════════════════════════════════════════════════════════════

# Ubuntu
apt update
apt install -y chrony
systemctl start chronyd
systemctl enable chronyd

# CentOS
# yum install -y chrony
# systemctl start chronyd
# systemctl enable chronyd

# 验证
chronyc sources
timedatectl

2.6 配置防火墙

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】配置防火墙
# ═══════════════════════════════════════════════════════════════════════════════

# Ubuntu (ufw)
ufw allow 22/tcp       # SSH
ufw allow 6443/tcp     # API Server
ufw allow 2379:2380/tcp # etcd
ufw allow 10250:10259/tcp # kubelet 等
ufw allow 30000:32767/tcp # NodePort
ufw --force enable

# 或者直接关闭防火墙(测试环境)
# ufw disable

三、安装容器运行时

⚠️ 以下命令在【所有6台服务器】上执行

3.0 容器运行时选择(重要概念)

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    K8s 容器运行时演变历史                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   K8s 1.20 之前:                                                               │
│   ┌──────────────────────────────────────────────────────────────────────────┐ │
│   │ K8s → dockershim → Docker → containerd → 容器                            │ │
│   └──────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│   K8s 1.24 之后(现在):                                                        │
│   ┌──────────────────────────────────────────────────────────────────────────┐ │
│   │ K8s → containerd → 容器    (推荐 ✅)                                    │ │
│   │ K8s → CRI-O → 容器         (也可以)                                     │ │
│   │ K8s → Docker(需要 cri-dockerd)(不推荐 ❌)                             │ │
│   └──────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│   ⚠️ 重要:K8s 1.24 移除了 dockershim,不再直接支持 Docker                      │
│   但是:构建镜像仍然用 Docker!只是运行时变了                                    │
│                                                                                 │
│   ┌──────────────┬──────────────┬──────────────┬──────────────────────────────┐│
│   │     项目      │  containerd  │    Docker    │          CRI-O             ││
│   ├──────────────┼──────────────┼──────────────┼──────────────────────────────┤│
│   │ K8s 支持     │ 原生支持 ✅   │ 需要适配层 ❌ │ 原生支持 ✅                  ││
│   │ 性能         │ 高           │ 中           │ 高                           ││
│   │ 资源占用     │ 低           │ 高           │ 低                           ││
│   │ 调试命令     │ ctr/crictl   │ docker       │ crictl                       ││
│   │ 镜像格式     │ OCI          │ OCI          │ OCI                          ││
│   │ 推荐场景     │ 生产环境     │ 开发/测试     │ OpenShift                   ││
│   └──────────────┴──────────────┴──────────────┴──────────────────────────────┘│
│                                                                                 │
│   ✅ 镜像是通用的!无论用什么运行时,docker build 构建的镜像都能用              │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

3.1 方案A:安装 containerd(推荐 ✅)

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】安装 containerd
# containerd 是 K8s 1.24+ 推荐的容器运行时
# ═══════════════════════════════════════════════════════════════════════════════

# Ubuntu
apt update
apt install -y containerd

# 生成默认配置
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

# 【重要】修改配置:使用 systemd cgroup
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# 启动 containerd
systemctl daemon-reload
systemctl restart containerd
systemctl enable containerd

# 验证
systemctl status containerd
ctr version

3.2 方案B:安装 Docker + cri-dockerd(如果你想用 Docker)

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】如果你想用 Docker 作为运行时
# 优点:熟悉、可以用 docker 命令调试
# 缺点:多一层、性能略差、需要额外维护 cri-dockerd
# ═══════════════════════════════════════════════════════════════════════════════

# 1. 安装 Docker
curl -fsSL https://get.docker.com | sh
systemctl start docker
systemctl enable docker

# 2. 安装 cri-dockerd(Docker 与 K8s 的桥梁)
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.4/cri-dockerd_0.3.4.3-0.ubuntu-jammy_amd64.deb
dpkg -i cri-dockerd_0.3.4.3-0.ubuntu-jammy_amd64.deb
systemctl enable cri-docker
systemctl start cri-docker

# 3. K8s 初始化时指定使用 Docker(后面初始化时加这个参数)
# kubeadm init --cri-socket unix:///var/run/cri-dockerd.sock ...

# 4. Worker 加入时也要指定
# kubeadm join ... --cri-socket unix:///var/run/cri-dockerd.sock

3.3 镜像加速配置(国内服务器必须)

containerd 镜像加速
# ═══════════════════════════════════════════════════════════════════════════════
# containerd 镜像加速配置
# 配置文件:/etc/containerd/config.toml
# ═══════════════════════════════════════════════════════════════════════════════

# 编辑配置文件
vim /etc/containerd/config.toml

# 找到 [plugins."io.containerd.grpc.v1.cri".registry] 部分
# 修改为:
[plugins."io.containerd.grpc.v1.cri".registry]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
    # Docker Hub 镜像加速
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://docker.m.daocloud.io", "https://registry-1.docker.io"]
    
    # Google Container Registry 镜像加速(K8s 组件镜像)
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
      endpoint = ["https://k8s.m.daocloud.io", "https://registry.k8s.io"]
    
    # 阿里云镜像仓库(如果用阿里云 ACR)
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.cn-hangzhou.aliyuncs.com"]
      endpoint = ["https://registry.cn-hangzhou.aliyuncs.com"]
# 重启 containerd
systemctl restart containerd
Docker 镜像加速
# ═══════════════════════════════════════════════════════════════════════════════
# Docker 镜像加速配置(如果你用 Docker 方案)
# 配置文件:/etc/docker/daemon.json
# ═══════════════════════════════════════════════════════════════════════════════

mkdir -p /etc/docker

cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://dockerhub.icu",
    "https://docker.rainbond.cc"
  ],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  }
}
EOF

# 重启 Docker
systemctl daemon-reload
systemctl restart docker

# 验证加速是否生效
docker info | grep -A 5 "Registry Mirrors"

3.4 网络代理配置(无法使用镜像加速时)

# ═══════════════════════════════════════════════════════════════════════════════
# containerd 代理配置
# 【改】把代理地址改成你的实际代理
# ═══════════════════════════════════════════════════════════════════════════════

mkdir -p /etc/systemd/system/containerd.service.d

cat > /etc/systemd/system/containerd.service.d/http-proxy.conf << 'EOF'
[Service]
Environment="HTTP_PROXY=http://你的代理IP:端口"
Environment="HTTPS_PROXY=http://你的代理IP:端口"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.svc,.cluster.local"
EOF

# 重载配置
systemctl daemon-reload
systemctl restart containerd

# ═══════════════════════════════════════════════════════════════════════════════
# Docker 代理配置(如果用 Docker 方案)
# ═══════════════════════════════════════════════════════════════════════════════

mkdir -p /etc/systemd/system/docker.service.d

cat > /etc/systemd/system/docker.service.d/http-proxy.conf << 'EOF'
[Service]
Environment="HTTP_PROXY=http://你的代理IP:端口"
Environment="HTTPS_PROXY=http://你的代理IP:端口"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
EOF

systemctl daemon-reload
systemctl restart docker

3.5 crictl 调试命令配置

# ═══════════════════════════════════════════════════════════════════════════════
# crictl 是 K8s 官方的容器调试工具,替代 docker 命令
# ═══════════════════════════════════════════════════════════════════════════════

# 配置 crictl 连接 containerd
cat > /etc/crictl.yaml << 'EOF'
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: false
EOF

# 常用命令对照:
# docker images        → crictl images
# docker ps            → crictl ps
# docker pull nginx    → crictl pull nginx
# docker logs <id>     → crictl logs <id>
# docker exec -it <id> sh → crictl exec -it <id> sh

四、安装K8s组件

⚠️ 以下命令在【所有6台服务器】上执行

4.1 添加 K8s 软件源

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】添加 K8s 软件源
# ═══════════════════════════════════════════════════════════════════════════════

# Ubuntu
apt install -y apt-transport-https ca-certificates curl gpg

# 添加 K8s GPG 密钥
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# 添加软件源
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' > /etc/apt/sources.list.d/kubernetes.list

4.2 安装 kubeadm、kubelet、kubectl

# ═══════════════════════════════════════════════════════════════════════════════
# 【所有节点执行】安装 K8s 组件
# kubeadm: 集群初始化工具
# kubelet: 节点代理,管理 Pod
# kubectl: 命令行工具
# ═══════════════════════════════════════════════════════════════════════════════

apt update
apt install -y kubelet kubeadm kubectl

# 锁定版本(防止自动升级)
apt-mark hold kubelet kubeadm kubectl

# 启动 kubelet(会自动等待 kubeadm 初始化)
systemctl enable kubelet

# 验证
kubeadm version
kubelet --version
kubectl version --client

五、初始化集群

5.1 初始化第一个 Master

⚠️ 只在【k8s-master-1 (10.0.1.10)】执行

# ═══════════════════════════════════════════════════════════════════════════════
# 【只在 k8s-master-1 执行】初始化集群
# 【改】把 IP 改成你实际的 IP
# ═══════════════════════════════════════════════════════════════════════════════

kubeadm init \
  --control-plane-endpoint="10.0.1.10:6443" \
  --apiserver-advertise-address=10.0.1.10 \
  --pod-network-cidr=172.16.0.0/16 \
  --service-cidr=10.96.0.0/16 \
  --upload-certs

# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ 参数说明:                                                                  │
# │                                                                             │
# │ --control-plane-endpoint="10.0.1.10:6443"                                  │
# │   API Server 的访问地址                                                     │
# │   【改】改成你的 Master-1 IP 或 VIP                                         │
# │                                                                             │
# │ --apiserver-advertise-address=10.0.1.10                                    │
# │   当前节点的 IP                                                             │
# │   【改】改成当前节点 IP                                                     │
# │                                                                             │
# │ --pod-network-cidr=172.16.0.0/16                                           │
# │   Pod 网络 CIDR,与网络插件配置一致                                         │
# │   【一般不改】除非与现有网络冲突                                            │
# │                                                                             │
# │ --service-cidr=10.96.0.0/16                                                │
# │   Service 网络 CIDR                                                        │
# │   【一般不改】                                                              │
# │                                                                             │
# │ --upload-certs                                                              │
# │   自动上传证书,其他 Master 加入时不用手动传证书                            │
# └─────────────────────────────────────────────────────────────────────────────┘

5.2 保存初始化输出

初始化成功后会输出类似以下内容,【务必保存】:

Your Kubernetes control-plane has initialized successfully!

# ══════════════════════════════════════════════════════════════════════════════
# 输出1:配置 kubectl 的命令(在 Master 上执行)
# ══════════════════════════════════════════════════════════════════════════════
To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

# ══════════════════════════════════════════════════════════════════════════════
# 输出2:其他 Master 加入集群的命令【保存这个!】
# ══════════════════════════════════════════════════════════════════════════════
You can now join any number of control-plane nodes by running:

  kubeadm join 10.0.1.10:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:xxx... \
    --control-plane --certificate-key xxx...

# ══════════════════════════════════════════════════════════════════════════════
# 输出3:Worker 加入集群的命令【保存这个!】
# ══════════════════════════════════════════════════════════════════════════════
Then you can join any number of worker nodes by running:

  kubeadm join 10.0.1.10:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:xxx...

5.3 配置 kubectl

在【k8s-master-1】执行

# ═══════════════════════════════════════════════════════════════════════════════
# 【在 k8s-master-1 执行】配置 kubectl
# ═══════════════════════════════════════════════════════════════════════════════

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

# 验证
kubectl get nodes
# 输出:k8s-master-1   NotReady   control-plane   1m   v1.28.0
# NotReady 是正常的,因为还没安装网络插件

5.4 其他 Master 加入集群

在【k8s-master-2】和【k8s-master-3】执行

# ═══════════════════════════════════════════════════════════════════════════════
# 【在 k8s-master-2 执行】加入集群
# 【改】使用上面保存的 Master 加入命令,修改 --apiserver-advertise-address
# ═══════════════════════════════════════════════════════════════════════════════

kubeadm join 10.0.1.10:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxx... \
  --control-plane --certificate-key xxx... \
  --apiserver-advertise-address=10.0.1.11

# 【改】10.0.1.11 是 k8s-master-2 的 IP

# ═══════════════════════════════════════════════════════════════════════════════
# 【在 k8s-master-3 执行】加入集群
# ═══════════════════════════════════════════════════════════════════════════════

kubeadm join 10.0.1.10:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxx... \
  --control-plane --certificate-key xxx... \
  --apiserver-advertise-address=10.0.1.12

# 【改】10.0.1.12 是 k8s-master-3 的 IP

# 配置 kubectl(在每个 Master 上执行)
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

5.5 Worker 加入集群

在【所有 Worker 节点】执行

# ═══════════════════════════════════════════════════════════════════════════════
# 【在所有 Worker 执行】加入集群
# 【改】使用上面保存的 Worker 加入命令
# ═══════════════════════════════════════════════════════════════════════════════

kubeadm join 10.0.1.10:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxx...

5.6 验证集群

在任意 Master 执行

kubectl get nodes

# 预期输出(状态可能是 NotReady):
# NAME           STATUS     ROLES           AGE   VERSION
# k8s-master-1   NotReady   control-plane   10m   v1.28.0
# k8s-master-2   NotReady   control-plane   5m    v1.28.0
# k8s-master-3   NotReady   control-plane   5m    v1.28.0
# k8s-worker-1   NotReady   <none>          3m    v1.28.0
# k8s-worker-2   NotReady   <none>          3m    v1.28.0
# k8s-worker-3   NotReady   <none>          3m    v1.28.0

六、安装网络插件

在【k8s-master-1】执行

6.1 安装 Calico

# ═══════════════════════════════════════════════════════════════════════════════
# 【在 k8s-master-1 执行】安装 Calico 网络插件
# Calico 提供 Pod 之间的网络通信
# ═══════════════════════════════════════════════════════════════════════════════

# 下载 Calico 配置文件
curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml

# 【重要】修改 Pod 网段(如果你用的不是默认的 192.168.0.0/16)
# 找到 CALICO_IPV4POOL_CIDR,修改为你的 Pod 网段
sed -i 's|192.168.0.0/16|172.16.0.0/16|g' calico.yaml

# 安装
kubectl apply -f calico.yaml

# 等待所有 Pod 就绪(需要几分钟)
kubectl get pods -n kube-system -w

# 验证节点状态(应该变成 Ready)
kubectl get nodes

# 预期输出:
# NAME           STATUS   ROLES           AGE   VERSION
# k8s-master-1   Ready    control-plane   15m   v1.28.0
# k8s-master-2   Ready    control-plane   10m   v1.28.0
# ...

七、部署中间件

以下所有命令在【k8s-master-1】执行

7.1 创建命名空间

# ═══════════════════════════════════════════════════════════════════════════════
# 创建命名空间
# ═══════════════════════════════════════════════════════════════════════════════

kubectl create namespace prod
kubectl create namespace middleware

7.2 部署 MySQL

7.2.1 创建 MySQL 配置文件
# 创建目录
mkdir -p ~/k8s/middleware
cd ~/k8s/middleware

文件:mysql.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# MySQL 部署配置
# 文件位置:~/k8s/middleware/mysql.yaml
# 执行:kubectl apply -f mysql.yaml
# ═══════════════════════════════════════════════════════════════════════════════

# 1. Secret - 存储密码
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
  namespace: middleware
stringData:
  root-password: "MyPassword123!"    # 【改】MySQL root 密码
  
---
# 2. PersistentVolumeClaim - 持久化存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
  namespace: middleware
spec:
  accessModes:
    - ReadWriteOnce                  # 单节点读写
  resources:
    requests:
      storage: 20Gi                  # 【改】存储大小
  # storageClassName: xxx            # 如果有 StorageClass,取消注释

---
# 3. Deployment - MySQL 部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: middleware
spec:
  replicas: 1                        # 单节点
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        - name: MYSQL_DATABASE
          value: "mydb"              # 【改】默认创建的数据库名
        - name: TZ
          value: "Asia/Shanghai"
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
        args:
        - --character-set-server=utf8mb4
        - --collation-server=utf8mb4_unicode_ci
      volumes:
      - name: mysql-data
        persistentVolumeClaim:
          claimName: mysql-pvc

---
# 4. Service - 内部访问入口
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  namespace: middleware
spec:
  selector:
    app: mysql
  ports:
  - port: 3306                       # Service 端口
    targetPort: 3306                 # Pod 端口
  type: ClusterIP                    # 只能集群内访问

# ═══════════════════════════════════════════════════════════════════════════════
# 内部访问方式:
# 
# 同命名空间:mysql-service:3306
# 跨命名空间:mysql-service.middleware:3306
# 完整格式:mysql-service.middleware.svc.cluster.local:3306
# 
# 连接字符串示例:
# jdbc:mysql://mysql-service.middleware:3306/mydb
# ═══════════════════════════════════════════════════════════════════════════════
7.2.2 部署 MySQL
# 创建文件
cat > ~/k8s/middleware/mysql.yaml << 'EOF'
# 把上面的 YAML 内容粘贴到这里
EOF

# 或者用编辑器
vim ~/k8s/middleware/mysql.yaml

# 部署
kubectl apply -f ~/k8s/middleware/mysql.yaml

# 查看状态
kubectl get pods -n middleware
kubectl get svc -n middleware

# 等待 MySQL Ready
kubectl get pods -n middleware -w

# 测试连接
kubectl exec -it $(kubectl get pod -n middleware -l app=mysql -o jsonpath='{.items[0].metadata.name}') -n middleware -- mysql -uroot -pMyPassword123! -e "SHOW DATABASES;"

7.3 部署 Redis

文件:redis.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# Redis 部署配置
# 文件位置:~/k8s/middleware/redis.yaml
# 执行:kubectl apply -f redis.yaml
# ═══════════════════════════════════════════════════════════════════════════════

# 1. Secret - 存储密码
apiVersion: v1
kind: Secret
metadata:
  name: redis-secret
  namespace: middleware
stringData:
  password: "RedisPassword123!"      # 【改】Redis 密码

---
# 2. ConfigMap - 配置文件
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
  namespace: middleware
data:
  redis.conf: |
    bind 0.0.0.0
    port 6379
    requirepass ${REDIS_PASSWORD}
    appendonly yes
    maxmemory 512mb
    maxmemory-policy allkeys-lru

---
# 3. Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: middleware
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
        env:
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: redis-secret
              key: password
        command:
        - redis-server
        - --requirepass
        - $(REDIS_PASSWORD)
        - --appendonly
        - "yes"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "250m"

---
# 4. Service
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: middleware
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
  type: ClusterIP

# ═══════════════════════════════════════════════════════════════════════════════
# 内部访问方式:
# 
# 同命名空间:redis-service:6379
# 跨命名空间:redis-service.middleware:6379
# 
# Spring Boot 配置:
# spring.redis.host=redis-service.middleware
# spring.redis.port=6379
# spring.redis.password=RedisPassword123!
# ═══════════════════════════════════════════════════════════════════════════════
部署 Redis
vim ~/k8s/middleware/redis.yaml
# 粘贴上面的内容,修改密码

kubectl apply -f ~/k8s/middleware/redis.yaml

# 验证
kubectl get pods -n middleware
kubectl get svc -n middleware

# 测试连接
kubectl exec -it $(kubectl get pod -n middleware -l app=redis -o jsonpath='{.items[0].metadata.name}') -n middleware -- redis-cli -a RedisPassword123! ping

7.4 中间件访问方式汇总

┌─────────────────────────────────────────────────────────────────────────────────┐
│                        中间件内部访问方式                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌────────────┬─────────────────────────────────────────────────────────────┐ │
│   │   中间件    │                    访问地址                                 │ │
│   ├────────────┼─────────────────────────────────────────────────────────────┤ │
│   │ MySQL      │ mysql-service.middleware:3306                               │ │
│   │            │ 或 mysql-service.middleware.svc.cluster.local:3306         │ │
│   ├────────────┼─────────────────────────────────────────────────────────────┤ │
│   │ Redis      │ redis-service.middleware:6379                               │ │
│   │            │ 或 redis-service.middleware.svc.cluster.local:6379         │ │
│   └────────────┴─────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│   在 prod 命名空间的应用中访问 middleware 命名空间的中间件:                     │
│   必须带命名空间:mysql-service.middleware                                      │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

八、部署应用

8.1 创建应用配置

文件:~/k8s/app/secret.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# 应用 Secret
# 文件位置:~/k8s/app/secret.yaml
# 【改】把密码改成你的实际密码
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
  namespace: prod
stringData:
  # MySQL 配置
  MYSQL_HOST: "mysql-service.middleware"
  MYSQL_PORT: "3306"
  MYSQL_DATABASE: "mydb"              # 【改】数据库名
  MYSQL_USERNAME: "root"
  MYSQL_PASSWORD: "MyPassword123!"    # 【改】数据库密码
  
  # Redis 配置
  REDIS_HOST: "redis-service.middleware"
  REDIS_PORT: "6379"
  REDIS_PASSWORD: "RedisPassword123!" # 【改】Redis密码

8.2 后端 Deployment

文件:~/k8s/app/backend.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# 后端应用部署
# 文件位置:~/k8s/app/backend.yaml
# 【改】把镜像地址改成你的实际镜像
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: prod
spec:
  replicas: 3                        # 【改】副本数
  selector:
    matchLabels:
      app: backend
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: your-registry/backend:v1.0.0  # 【改】你的镜像地址
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 8080
        env:
        - name: TZ
          value: "Asia/Shanghai"
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        # 数据库配置
        - name: SPRING_DATASOURCE_URL
          value: "jdbc:mysql://$(MYSQL_HOST):$(MYSQL_PORT)/$(MYSQL_DATABASE)?useSSL=false&serverTimezone=Asia/Shanghai"
        - name: MYSQL_HOST
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: MYSQL_HOST
        - name: MYSQL_PORT
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: MYSQL_PORT
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: MYSQL_DATABASE
        - name: SPRING_DATASOURCE_USERNAME
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: MYSQL_USERNAME
        - name: SPRING_DATASOURCE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: MYSQL_PASSWORD
        # Redis 配置
        - name: SPRING_REDIS_HOST
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: REDIS_HOST
        - name: SPRING_REDIS_PORT
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: REDIS_PORT
        - name: SPRING_REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: REDIS_PASSWORD
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 15"]
      terminationGracePeriodSeconds: 30

---
# Service
apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: prod
spec:
  selector:
    app: backend
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  type: ClusterIP

# ═══════════════════════════════════════════════════════════════════════════════
# 内部访问:
# http://backend-service:8080        (prod 命名空间内)
# http://backend-service.prod:8080   (其他命名空间)
# ═══════════════════════════════════════════════════════════════════════════════

8.3 前端 Deployment

文件:~/k8s/app/frontend.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# 前端应用部署
# 文件位置:~/k8s/app/frontend.yaml
# 【改】把镜像地址改成你的实际镜像
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: prod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: your-registry/frontend:v1.0.0  # 【改】你的镜像地址
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "256Mi"
            cpu: "200m"

---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: prod
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

8.4 部署应用

mkdir -p ~/k8s/app
cd ~/k8s/app

# 创建配置文件
vim secret.yaml
vim backend.yaml
vim frontend.yaml

# 部署
kubectl apply -f secret.yaml
kubectl apply -f backend.yaml
kubectl apply -f frontend.yaml

# 查看状态
kubectl get pods -n prod
kubectl get svc -n prod

# 查看日志
kubectl logs -f deployment/backend -n prod

九、配置外部访问

9.1 安装 Ingress Controller

Ingress Controller 是什么?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Ingress 和 Ingress Controller 的关系                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   Ingress = 规则定义(只是一个 YAML 文件,本身不干活)                            │
│   Ingress Controller = 执行者(真正处理流量的组件)                              │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   Ingress(规则)                    Ingress Controller(执行者)        │  │
│   │   ┌────────────────────┐            ┌────────────────────┐              │  │
│   │   │  rules:            │            │                    │              │  │
│   │   │  - host: a.com     │    →       │  实际处理流量      │              │  │
│   │   │    path: /api      │   读取     │  路由到后端服务    │              │  │
│   │   │    backend: xxx    │   规则     │  处理 HTTPS/认证  │              │  │
│   │   └────────────────────┘            └────────────────────┘              │  │
│   │                                                                          │  │
│   │   没有 Ingress Controller,Ingress 规则不会生效!                        │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
常见的 Ingress Controller
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Ingress Controller 种类                                       │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   K8s 只定义了 Ingress 规范,具体实现由不同厂商提供:                             │
│                                                                                 │
│   ┌─────────────────┬──────────────────────────┬────────────────────────────┐  │
│   │ Controller       │ 注解前缀                  │ 特点                       │  │
│   ├─────────────────┼──────────────────────────┼────────────────────────────┤  │
│   │ Nginx Ingress   │ nginx.ingress.k8s.io     │ 最常用、免费、功能全 ✅    │  │
│   │ Traefik         │ traefik.ingress...       │ 云原生、自动发现           │  │
│   │ HAProxy         │ haproxy.ingress...       │ 高性能                     │  │
│   │ Kong            │ konghq.com/...           │ API 网关功能               │  │
│   │ AWS ALB         │ alb.ingress...           │ AWS 专用                   │  │
│   │ 阿里云 ALB      │ alibaba-cloud...         │ 阿里云专用                 │  │
│   └─────────────────┴──────────────────────────┴────────────────────────────┘  │
│                                                                                 │
│   ⭐ 我们安装的是 Nginx Ingress Controller(最常用)                             │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
Nginx Ingress Controller 底层原理
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    底层就是 Nginx!                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   Nginx Ingress Controller = Nginx + K8s 控制器                                 │
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   Ingress YAML 规则              自动生成 nginx.conf                     │  │
│   │   ┌────────────────────┐        ┌────────────────────────────────────┐  │  │
│   │   │                    │        │ server {                            │  │  │
│   │   │  rules:            │        │   server_name example.com;          │  │  │
│   │   │  - host: example.com│  ──→  │   location /api {                   │  │  │
│   │   │    path: /api      │  自动  │     proxy_pass http://backend:8080; │  │  │
│   │   │    backend: xxx    │  转换  │   }                                 │  │  │
│   │   │                    │        │   location / {                      │  │  │
│   │   │                    │        │     proxy_pass http://frontend:80;  │  │  │
│   │   │                    │        │   }                                 │  │  │
│   │   └────────────────────┘        │ }                                   │  │  │
│   │                                  └────────────────────────────────────┘  │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   工作流程:                                                                     │
│   1. 你创建/修改 Ingress YAML                                                   │
│   2. Controller 监听到变化                                                      │
│   3. 自动把规则翻译成 nginx.conf                                               │
│   4. 热更新 Nginx 配置(无需重启)                                              │
│                                                                                 │
│   所以 nginx.ingress.kubernetes.io/xxx 注解 → 最终变成 nginx.conf 里的配置     │
│   • IP 白名单     → Nginx 的 allow/deny                                        │
│   • Basic Auth    → Nginx 的 auth_basic                                        │
│   • proxy-body-size → Nginx 的 client_max_body_size                           │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
安装 Nginx Ingress Controller
# ═══════════════════════════════════════════════════════════════════════════════
# 安装 Nginx Ingress Controller
# ═══════════════════════════════════════════════════════════════════════════════

# 下载配置文件
curl -O https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/baremetal/deploy.yaml

# 安装(会创建 ingress-nginx 命名空间和相关资源)
kubectl apply -f deploy.yaml

# 查看状态(等待 Pod 变为 Running)
kubectl get pods -n ingress-nginx
# 输出:
# NAME                                        READY   STATUS
# ingress-nginx-controller-xxx                1/1     Running  ← 这就是 Nginx Pod

kubectl get svc -n ingress-nginx

# 获取 NodePort(外部访问入口)
kubectl get svc ingress-nginx-controller -n ingress-nginx

# 输出示例:
# NAME                       TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)
# ingress-nginx-controller   NodePort   10.96.xxx.xxx  <none>        80:30080/TCP,443:30443/TCP
#                                                                    ↑           ↑
#                                                               HTTP 入口   HTTPS 入口

# 记住这两个端口:30080 (HTTP) 和 30443 (HTTPS)
# ═══════════════════════════════════════════════════════════════════════════════
# 验证:查看 Nginx 配置(可选,了解原理)
# ═══════════════════════════════════════════════════════════════════════════════

# 进入 Nginx Pod 查看配置
kubectl exec -it $(kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx -o jsonpath='{.items[0].metadata.name}') -n ingress-nginx -- cat /etc/nginx/nginx.conf

# 你会看到熟悉的 Nginx 配置格式!

9.2 创建 Ingress

文件:~/k8s/app/ingress.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# Ingress 配置
# 文件位置:~/k8s/app/ingress.yaml
# 【改】把域名改成你的实际域名
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: prod
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
spec:
  rules:
  - host: example.com                # 【改】你的域名
    http:
      paths:
      # 前端
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
      # 后端 API
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 8080

9.3 配置外部访问

kubectl apply -f ~/k8s/app/ingress.yaml

# 查看 Ingress
kubectl get ingress -n prod

# ═══════════════════════════════════════════════════════════════════════════════
# 外部访问方式
# ═══════════════════════════════════════════════════════════════════════════════
#
# 方式1:通过任意 Worker 节点 IP + NodePort 访问
# http://10.0.1.20:30080
# http://10.0.1.21:30080
# http://10.0.1.22:30080
#
# 方式2:配置域名解析后访问
# 将域名 A 记录指向任意 Worker IP
# http://example.com:30080
#
# 方式3:配置负载均衡器(生产环境推荐)
# 在边界路由器/云 SLB 配置:
# 公网 80 端口 → 10.0.1.20:30080, 10.0.1.21:30080, 10.0.1.22:30080
# 公网 443 端口 → 10.0.1.20:30443, 10.0.1.21:30443, 10.0.1.22:30443
# ═══════════════════════════════════════════════════════════════════════════════

9.4 访问方式汇总

┌─────────────────────────────────────────────────────────────────────────────────┐
│                        完整访问方式汇总                                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ════════════════════════════════════════════════════════════════════════════ │
│   外部访问(从集群外)                                                          │
│   ════════════════════════════════════════════════════════════════════════════ │
│                                                                                 │
│   用户 → http://example.com:30080                                               │
│        或 http://任意WorkerIP:30080                                             │
│            │                                                                    │
│            ▼                                                                    │
│   Ingress Controller (NodePort 30080)                                          │
│            │                                                                    │
│            ├── /        → frontend-service:80  → Frontend Pods                 │
│            └── /api     → backend-service:8080 → Backend Pods                  │
│                                                                                 │
│   ════════════════════════════════════════════════════════════════════════════ │
│   内部访问(Pod 之间)                                                          │
│   ════════════════════════════════════════════════════════════════════════════ │
│                                                                                 │
│   同命名空间:                                                                   │
│   ┌────────────────────────────────────────────────────────────────────────┐   │
│   │ backend-service:8080                                                   │   │
│   │ frontend-service:80                                                    │   │
│   └────────────────────────────────────────────────────────────────────────┘   │
│                                                                                 │
│   跨命名空间(prod → middleware):                                             │
│   ┌────────────────────────────────────────────────────────────────────────┐   │
│   │ mysql-service.middleware:3306                                          │   │
│   │ redis-service.middleware:6379                                          │   │
│   └────────────────────────────────────────────────────────────────────────┘   │
│                                                                                 │
│   完整 DNS 格式:                                                                │
│   ┌────────────────────────────────────────────────────────────────────────┐   │
│   │ <service-name>.<namespace>.svc.cluster.local:<port>                    │   │
│   │ 例:mysql-service.middleware.svc.cluster.local:3306                    │   │
│   └────────────────────────────────────────────────────────────────────────┘   │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

十、配置HTTPS(详细解释版)

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    HTTPS 自动化证书流程                                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   为什么用 Cert-Manager?                                                        │
│   • 自动从 Let's Encrypt 申请免费 HTTPS 证书                                    │
│   • 证书到期前自动续期(无需人工干预)                                          │
│   • 自动创建 K8s Secret 存储证书                                                │
│                                                                                 │
│   整体流程:                                                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   1. 安装 Cert-Manager                                                   │  │
│   │        ↓                                                                 │  │
│   │   2. 创建 ClusterIssuer(告诉它用 Let's Encrypt)                        │  │
│   │        ↓                                                                 │  │
│   │   3. 在 Ingress 中添加注解和 TLS 配置                                    │  │
│   │        ↓                                                                 │  │
│   │   4. Cert-Manager 自动:                                                 │  │
│   │      • 向 Let's Encrypt 申请证书                                        │  │
│   │      • 验证域名所有权(HTTP-01 验证)                                   │  │
│   │      • 把证书存入 K8s Secret                                            │  │
│   │      • 定期检查并续期                                                   │  │
│   │        ↓                                                                 │  │
│   │   5. Ingress Controller 读取 Secret 中的证书,提供 HTTPS 服务           │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

10.1 安装 Cert-Manager

# ═══════════════════════════════════════════════════════════════════════════════
# Cert-Manager 是什么?
# 一个 K8s 插件,专门用于自动化管理 TLS/SSL 证书
# 安装后会创建 cert-manager 命名空间,运行 3 个 Pod
# ═══════════════════════════════════════════════════════════════════════════════

# 安装 Cert-Manager(会自动创建 CRD 和相关资源)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

# 等待所有 Pod 就绪
kubectl get pods -n cert-manager -w
# 应该看到 3 个 Pod:
# - cert-manager-xxx             主控制器
# - cert-manager-cainjector-xxx  CA 注入器
# - cert-manager-webhook-xxx     Webhook 服务器

# 验证安装成功
kubectl get crd | grep cert-manager
# 应该看到:
# certificates.cert-manager.io
# clusterissuers.cert-manager.io
# issuers.cert-manager.io
# ... 等

10.2 创建证书颁发者(ClusterIssuer)

文件:~/k8s/app/issuer.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# ClusterIssuer 是什么?
# 告诉 Cert-Manager 从哪里获取证书,怎么验证域名
# ClusterIssuer = 全局可用(所有命名空间)
# Issuer = 只在某个命名空间可用
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: cert-manager.io/v1
kind: ClusterIssuer                  # 资源类型:全局证书颁发者
metadata:
  name: letsencrypt-prod             # 【改】颁发者名称(Ingress 中会引用)
spec:
  acme:                              # ACME 协议(Let's Encrypt 使用的协议)
    # ─────────────────────────────────────────────────────────────────────────
    # server: Let's Encrypt 的服务器地址
    # ─────────────────────────────────────────────────────────────────────────
    # 生产环境(正式证书,有速率限制)
    server: https://acme-v02.api.letsencrypt.org/directory
    # 
    # 测试环境(测试证书,无速率限制,但浏览器会提示不安全)
    # server: https://acme-staging-v02.api.letsencrypt.org/directory
    
    # ─────────────────────────────────────────────────────────────────────────
    # email: 证书关联的邮箱
    # ─────────────────────────────────────────────────────────────────────────
    # Let's Encrypt 会发送证书过期提醒到这个邮箱
    email: your-email@example.com    # 【改】换成你的真实邮箱
    
    # ─────────────────────────────────────────────────────────────────────────
    # privateKeySecretRef: 存储 ACME 账户私钥的 Secret
    # ─────────────────────────────────────────────────────────────────────────
    # Cert-Manager 会自动创建这个 Secret,用于与 Let's Encrypt 通信
    privateKeySecretRef:
      name: letsencrypt-prod-key     # Secret 名称(自动创建)
    
    # ─────────────────────────────────────────────────────────────────────────
    # solvers: 域名验证方式(证明你拥有这个域名)
    # ─────────────────────────────────────────────────────────────────────────
    solvers:
    - http01:                        # HTTP-01 验证方式
        ingress:
          class: nginx               # 使用 nginx Ingress Controller
    
    # ─────────────────────────────────────────────────────────────────────────
    # HTTP-01 验证原理:
    # ─────────────────────────────────────────────────────────────────────────
    # 1. Cert-Manager 在你的域名下创建一个临时路径
    #    http://你的域名/.well-known/acme-challenge/xxx
    # 2. Let's Encrypt 访问这个路径
    # 3. 如果能访问到,说明你确实拥有这个域名
    # 4. 验证通过,颁发证书
    #
    # ⚠️ 前提:你的域名必须已经解析到 K8s 集群,80 端口可访问
    # ─────────────────────────────────────────────────────────────────────────

10.3 更新 Ingress 启用 HTTPS

文件:~/k8s/app/ingress-https.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# 启用 HTTPS 的 Ingress 配置
# 关键点:添加 annotations 和 tls 配置
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: prod                    # 【改】你的应用所在命名空间
  annotations:
    # ─────────────────────────────────────────────────────────────────────────
    # kubernetes.io/ingress.class: nginx
    # 指定使用 nginx Ingress Controller 处理这个 Ingress
    # ─────────────────────────────────────────────────────────────────────────
    kubernetes.io/ingress.class: nginx
    
    # ─────────────────────────────────────────────────────────────────────────
    # cert-manager.io/cluster-issuer: letsencrypt-prod
    # 告诉 Cert-Manager 使用哪个 ClusterIssuer 来申请证书
    # 这里的值必须和上面创建的 ClusterIssuer 名称一致
    # ─────────────────────────────────────────────────────────────────────────
    cert-manager.io/cluster-issuer: letsencrypt-prod
    
    # ─────────────────────────────────────────────────────────────────────────
    # nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # HTTP 自动跳转到 HTTPS
    # 用户访问 http://example.com → 自动跳转到 https://example.com
    # ─────────────────────────────────────────────────────────────────────────
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    
    # ─────────────────────────────────────────────────────────────────────────
    # nginx.ingress.kubernetes.io/proxy-body-size: "100m"
    # 允许上传的最大文件大小(默认 1m 太小)
    # 【改】根据你的需求调整,如 "50m"、"200m"
    # ─────────────────────────────────────────────────────────────────────────
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
    
spec:
  # ─────────────────────────────────────────────────────────────────────────────
  # tls 配置:告诉 Ingress 哪些域名需要 HTTPS
  # ─────────────────────────────────────────────────────────────────────────────
  tls:
  - hosts:
    - example.com                    # 【改】需要 HTTPS 的域名
    # - www.example.com              # 可以添加多个域名
    # - api.example.com
    
    # ─────────────────────────────────────────────────────────────────────────
    # secretName: 存储证书的 Secret 名称
    # Cert-Manager 会自动创建这个 Secret,里面包含:
    # - tls.crt(证书)
    # - tls.key(私钥)
    # ⚠️ 你不需要手动创建,Cert-Manager 自动管理!
    # ─────────────────────────────────────────────────────────────────────────
    secretName: app-tls-secret       # 【改】Secret 名称(自动创建)
  
  # ─────────────────────────────────────────────────────────────────────────────
  # rules 配置:路由规则(和之前一样)
  # ─────────────────────────────────────────────────────────────────────────────
  rules:
  - host: example.com                # 【改】你的域名(必须和 tls.hosts 中的一致)
    http:
      paths:
      # 前端静态资源
      - path: /
        pathType: Prefix             # 前缀匹配:/ 开头的所有路径
        backend:
          service:
            name: frontend-service   # 【改】前端 Service 名称
            port:
              number: 80
      
      # 后端 API
      - path: /api
        pathType: Prefix             # 前缀匹配:/api 开头的所有路径
        backend:
          service:
            name: backend-service    # 【改】后端 Service 名称
            port:
              number: 8080

10.4 应用配置并验证

# 1. 创建 ClusterIssuer
kubectl apply -f ~/k8s/app/issuer.yaml

# 2. 创建/更新 Ingress
kubectl apply -f ~/k8s/app/ingress-https.yaml

# 3. 查看证书申请状态
kubectl get certificate -n prod
# 状态说明:
# - True   = 证书申请成功 ✅
# - False  = 申请中或失败

# 4. 查看证书详情(如果失败看这里找原因)
kubectl describe certificate app-tls-secret -n prod

# 5. 查看 Cert-Manager 日志(排查问题)
kubectl logs -f deployment/cert-manager -n cert-manager

# 6. 验证证书已存储
kubectl get secret app-tls-secret -n prod
# 应该看到类型为 kubernetes.io/tls 的 Secret

10.5 证书申请常见问题

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    证书申请失败排查                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   问题1:域名无法访问                                                            │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  症状:kubectl describe certificate 显示 "waiting for challenge"        │  │
│   │  原因:Let's Encrypt 无法访问你的域名                                   │  │
│   │  解决:                                                                  │  │
│   │  • 确认域名 DNS 已解析到你的服务器                                      │  │
│   │  • 确认 80 端口可从外网访问                                             │  │
│   │  • 确认防火墙/安全组已开放 80 和 443 端口                               │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   问题2:速率限制                                                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  症状:错误信息包含 "rate limit"                                        │  │
│   │  原因:Let's Encrypt 有申请频率限制(同一域名每周 5 次)                │  │
│   │  解决:                                                                  │  │
│   │  • 等待一周后重试                                                       │  │
│   │  • 测试时使用 staging 环境(无限制)                                   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   问题3:Ingress Class 不匹配                                                    │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  症状:Challenge 一直处于 pending                                       │  │
│   │  原因:ClusterIssuer 中的 class 和 Ingress Controller 不匹配           │  │
│   │  解决:确认 ingress.class 是否为 nginx(或你安装的 Controller 类型)   │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

十一、监控系统(完整搭建指南)

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    监控系统架构总览                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                        监控三件套                                        │  │
│   │                                                                          │  │
│   │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  │  │
│   │   │  Prometheus  │  │   Grafana    │  │ AlertManager │                  │  │
│   │   │   数据采集    │  │   可视化     │  │    报警      │                  │  │
│   │   │   时序存储    │→ │   仪表盘     │  │   通知推送   │                  │  │
│   │   └──────────────┘  └──────────────┘  └──────────────┘                  │  │
│   │          ↑                                    ↑                          │  │
│   │          │ 采集指标                           │ 触发报警                 │  │
│   │          │                                    │                          │  │
│   │   ┌──────┴─────────────────────────────────────┴─────┐                  │  │
│   │   │              K8s 集群(Node、Pod、Service)        │                  │  │
│   │   └──────────────────────────────────────────────────┘                  │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   可选扩展:                                                                     │
│   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                        │
│   │    Loki      │  │   Promtail   │  │   Jaeger     │                        │
│   │   日志存储    │  │   日志采集    │  │   链路追踪   │                        │
│   └──────────────┘  └──────────────┘  └──────────────┘                        │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

11.1 安装 Helm(包管理器)

Helm 是什么?
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Helm = K8s 的包管理器                                         │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   类比理解:                                                                     │
│   ┌────────────────┬────────────────────────────────────────────────────────┐  │
│   │  平台           │  包管理器                                              │  │
│   ├────────────────┼────────────────────────────────────────────────────────┤  │
│   │  Ubuntu        │  apt install nginx                                     │  │
│   │  CentOS        │  yum install nginx                                     │  │
│   │  macOS         │  brew install nginx                                    │  │
│   │  Node.js       │  npm install express                                   │  │
│   │  Python        │  pip install flask                                     │  │
│   │  Kubernetes    │  helm install prometheus ← Helm                       │  │
│   └────────────────┴────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   为什么需要 Helm?                                                              │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   ❌ 没有 Helm:手动安装 Prometheus 需要创建 10+ 个 YAML 文件            │  │
│   │      prometheus-deployment.yaml                                          │  │
│   │      prometheus-service.yaml                                             │  │
│   │      prometheus-configmap.yaml                                           │  │
│   │      grafana-deployment.yaml                                             │  │
│   │      ... 更多                                                            │  │
│   │                                                                          │  │
│   │   ✅ 有 Helm:一条命令搞定!                                              │  │
│   │      helm install prometheus prometheus-community/kube-prometheus-stack  │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   Helm 核心概念:                                                                │
│   ┌────────────────┬────────────────────────────────────────────────────────┐  │
│   │ Chart(图表)   │ 应用的安装包,包含所有 K8s 资源模板                    │  │
│   │ Repository     │ Chart 仓库,类似 npm registry、Docker Hub              │  │
│   │ Release        │ Chart 安装后的实例,可以安装多次                        │  │
│   └────────────────┴────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ⚠️ 注意:Helm 需要单独安装,K8s 不自带!                                      │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
安装 Helm
# ═══════════════════════════════════════════════════════════════════════════════
# 在任意可以执行 kubectl 的机器上安装(Master 节点或本地电脑)
# ═══════════════════════════════════════════════════════════════════════════════

# 方法1:官方脚本安装(需要外网)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 方法2:手动下载安装(国内推荐)
wget https://get.helm.sh/helm-v3.13.0-linux-amd64.tar.gz
tar -zxvf helm-v3.13.0-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm

# 验证安装
helm version
# 输出:version.BuildInfo{Version:"v3.13.0", ...}
Helm 常用命令
# 添加仓库(类似 apt add-repository)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 更新仓库(类似 apt update)
helm repo update

# 搜索 Chart
helm search repo prometheus

# 安装(类似 apt install)
helm install <release名> <chart名> -n <命名空间>

# 查看已安装
helm list -A

# 卸载(类似 apt remove)
helm uninstall prometheus -n monitoring

# 升级
helm upgrade prometheus prometheus-community/kube-prometheus-stack -n monitoring

# 回滚到上一版本
helm rollback prometheus 1 -n monitoring

11.2 安装 Prometheus + Grafana + AlertManager

# ═══════════════════════════════════════════════════════════════════════════════
# 使用 Helm 一键安装完整监控套件
# 包含:Prometheus、Grafana、AlertManager、Node Exporter、Kube State Metrics
# ═══════════════════════════════════════════════════════════════════════════════

# 1. 添加 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 2. 创建监控命名空间
kubectl create namespace monitoring

# 3. 安装 kube-prometheus-stack(推荐,功能最全)
helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --set grafana.adminPassword=YourPassword123 \
  --set prometheus.prometheusSpec.retention=15d \
  --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi

# ─────────────────────────────────────────────────────────────────────────────
# 参数说明:
# ─────────────────────────────────────────────────────────────────────────────
# --namespace monitoring              安装到 monitoring 命名空间
# --set grafana.adminPassword=xxx     【改】Grafana 管理员密码
# --set ...retention=15d              数据保留 15 天【改】根据需要调整
# --set ...storage=50Gi               存储空间 50GB【改】根据需要调整

# 4. 查看安装状态(等待所有 Pod 变为 Running)
kubectl get pods -n monitoring -w

# 5. 查看所有安装的服务
kubectl get svc -n monitoring

安装完成后的组件:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    安装完成后的组件                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   Pod 名称                                  作用                                 │
│   ─────────────────────────────────────────────────────────────────────────    │
│   prometheus-kube-prometheus-operator       管理 Prometheus 配置                │
│   prometheus-prometheus-node-exporter       采集节点指标(CPU/内存/磁盘)       │
│   prometheus-kube-state-metrics             采集 K8s 资源状态(Pod/Deployment) │
│   prometheus-prometheus-kube-prometheus-xxx Prometheus 主服务                   │
│   prometheus-grafana-xxx                    Grafana 可视化                      │
│   alertmanager-prometheus-kube-prometheus   AlertManager 报警                   │
│                                                                                 │
│   Service 名称                              端口                                 │
│   ─────────────────────────────────────────────────────────────────────────    │
│   prometheus-grafana                        80   (Grafana Web)                │
│   prometheus-kube-prometheus-prometheus     9090 (Prometheus Web)             │
│   prometheus-kube-prometheus-alertmanager   9093 (AlertManager Web)           │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

11.3 访问监控界面

方式1:kubectl port-forward(临时访问,调试用)
# ═══════════════════════════════════════════════════════════════════════════════
# port-forward 命令详解
# ═══════════════════════════════════════════════════════════════════════════════

# 访问 Grafana
kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring

命令逐字解析:

┌─────────────────────────────────────────────────────────────────────────────────┐
│              kubectl port-forward 命令完整解析                                    │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring            │
│   └──┬──┘ └─────┬─────┘ └─────────┬─────────┘ └──┬──┘ └───────┬──────┘         │
│      │          │                 │              │            │                 │
│      │          │                 │              │            └─ 命名空间       │
│      │          │                 │              │               monitoring    │
│      │          │                 │              │                              │
│      │          │                 │              └─ 端口映射                    │
│      │          │                 │                 本地3000 → Service的80     │
│      │          │                 │                                             │
│      │          │                 └─ 资源名称                                   │
│      │          │                    svc/ 表示 Service 资源                     │
│      │          │                    prometheus-grafana 是 Service 名           │
│      │          │                                                               │
│      │          └─ 子命令:端口转发                                             │
│      │             把集群内的端口转发到本地                                      │
│      │                                                                          │
│      └─ kubectl 命令行工具                                                      │
│                                                                                 │
│   执行后的效果:                                                                 │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   你的电脑                             K8s 集群                          │  │
│   │   ┌────────────────┐                  ┌────────────────┐                │  │
│   │   │  浏览器        │                  │  Grafana Pod   │                │  │
│   │   │  localhost:3000 │ ═══════════════→│  :3000(内部)   │                │  │
│   │   └────────────────┘     隧道         └────────────────┘                │  │
│   │                                                                          │  │
│   │   ⚠️ 只有执行命令的电脑能访问                                             │  │
│   │   ⚠️ 终端关闭连接就断了                                                   │  │
│   │   ⚠️ 适合临时调试,不适合生产                                             │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   端口映射 3000:80 的含义:                                                      │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │   3000                    :                     80                       │  │
│   │   └─┬─┘                                        └┬┘                      │  │
│   │     │                                           │                        │  │
│   │     └─ 本地监听端口                             └─ Service 暴露的端口    │  │
│   │        浏览器访问 localhost:3000                  (prometheus-grafana    │  │
│   │                                                   的 spec.ports.port)   │  │
│   │                                                                          │  │
│   │   为什么是 80?因为 Grafana Service 的端口是 80                          │  │
│   │   kubectl get svc prometheus-grafana -n monitoring                      │  │
│   │   NAME                 TYPE        PORT(S)                              │  │
│   │   prometheus-grafana   ClusterIP   80/TCP    ← 这个 80                  │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

⚠️ 问题:Linux 服务器没有图形界面怎么办?

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    服务器无界面的解决方案                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   方法1:在你的电脑(Windows/Mac)上执行 port-forward(推荐)                    │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   前提:你的电脑需要配置 kubectl 和 kubeconfig                           │  │
│   │                                                                          │  │
│   │   步骤:                                                                  │  │
│   │   1. 从 Master 节点复制 ~/.kube/config 到你的电脑                        │  │
│   │   2. 修改 config 中的 server 地址为公网 IP(如果需要)                   │  │
│   │   3. 在你的电脑终端执行:                                                │  │
│   │      kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring  │  │
│   │   4. 打开本地浏览器访问 http://localhost:3000                           │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   方法2:在服务器执行,绑定 0.0.0.0(临时调试用)                                │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   # 在服务器上执行(注意 --address 0.0.0.0)                             │  │
│   │   kubectl port-forward --address 0.0.0.0 \                              │  │
│   │     svc/prometheus-grafana 3000:80 -n monitoring                        │  │
│   │                                                                          │  │
│   │   # 开放防火墙                                                           │  │
│   │   firewall-cmd --add-port=3000/tcp --permanent && firewall-cmd --reload │  │
│   │                                                                          │  │
│   │   # 在你的电脑浏览器访问:http://服务器公网IP:3000                       │  │
│   │                                                                          │  │
│   │   ⚠️ 安全风险:任何人知道 IP:端口 都能访问,仅限临时调试!               │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   方法3:用 NodePort 暴露(快速测试)                                            │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   # 把 Service 改成 NodePort                                            │  │
│   │   kubectl patch svc prometheus-grafana -n monitoring \                  │  │
│   │     -p '{"spec":{"type":"NodePort"}}'                                   │  │
│   │                                                                          │  │
│   │   # 查看分配的端口                                                       │  │
│   │   kubectl get svc prometheus-grafana -n monitoring                      │  │
│   │   # 输出:80:30xxx/TCP,30xxx 就是 NodePort                             │  │
│   │                                                                          │  │
│   │   # 访问:http://服务器IP:30xxx                                          │  │
│   │                                                                          │  │
│   │   ⚠️ 安全风险:同上,任何人都能访问!                                    │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   方法4:Ingress + 域名 + 认证(生产推荐)                                       │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   配置域名 + Ingress + Basic Auth 认证                                   │  │
│   │   访问时需要输入用户名密码,详见下方"方式2"                              │  │
│   │                                                                          │  │
│   │   ✅ 安全:需要认证才能访问                                              │  │
│   │   ✅ 专业:通过域名访问                                                  │  │
│   │   ✅ 支持 HTTPS                                                          │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
# 访问 Prometheus
kubectl port-forward svc/prometheus-kube-prometheus-prometheus 9090:9090 -n monitoring
# 浏览器访问 http://localhost:9090

# 访问 AlertManager
kubectl port-forward svc/prometheus-kube-prometheus-alertmanager 9093:9093 -n monitoring
# 浏览器访问 http://localhost:9093
方式2:通过 Ingress 暴露(生产环境推荐)

⚠️ 安全警告:Ingress 暴露后,任何人通过域名都能访问!必须加认证!

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Ingress 暴露的安全问题                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ❌ 问题:配置域名后,全世界任何人都能访问你的 Grafana/Prometheus              │
│                                                                                 │
│   解决方案:                                                                     │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   方案1:Basic Auth(简单,见下方详细配置)                              │  │
│   │   • 访问时弹出用户名密码框                                               │  │
│   │   • 适合内部管理页面                                                     │  │
│   │                                                                          │  │
│   │   方案2:IP 白名单(见下方详细配置)                                     │  │
│   │   • 只允许指定 IP 访问                                                   │  │
│   │   • 非白名单 IP 访问会返回 403 Forbidden                                 │  │
│   │                                                                          │  │
│   │   方案3:VPN                                                             │  │
│   │   • 只有连接公司 VPN 才能访问                                            │  │
│   │   • 最安全但配置复杂                                                     │  │
│   │                                                                          │  │
│   │   方案4:OAuth2(如 Google/GitHub 登录)                                 │  │
│   │   • 使用第三方认证                                                       │  │
│   │   • 需要额外配置 oauth2-proxy                                            │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   ⭐ 推荐:生产环境至少要配置 Basic Auth!                                       │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
# ═══════════════════════════════════════════════════════════════════════════════
# 文件:~/k8s/monitoring/ingress.yaml
# 通过域名访问监控系统
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: monitoring-ingress
  namespace: monitoring
  annotations:
    kubernetes.io/ingress.class: nginx
    # Basic Auth 认证(防止未授权访问)
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: monitoring-basic-auth
    nginx.ingress.kubernetes.io/auth-realm: "Monitoring Area"
spec:
  rules:
  # Grafana【改】域名
  - host: grafana.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-grafana
            port:
              number: 80
  # Prometheus【改】域名
  - host: prometheus.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-kube-prometheus-prometheus
            port:
              number: 9090
Basic Auth 配置详细步骤
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Basic Auth 配置三步走                                         │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   步骤1:创建密码文件                                                            │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   htpasswd -c auth admin                                                 │  │
│   │            └┬┘ └┬─┘ └──┬─┘                                               │  │
│   │             │   │     └─ 用户名【改】                                    │  │
│   │             │   └─ 输出文件名(会在当前目录生成 auth 文件)              │  │
│   │             └─ -c 表示创建新文件(已存在会覆盖)                         │  │
│   │                                                                          │  │
│   │   执行后提示输入密码(输入两次,不会显示)                                │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   步骤2:创建 K8s Secret                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   kubectl create secret generic monitoring-basic-auth \                 │  │
│   │     --from-file=auth \                                                  │  │
│   │     -n monitoring                                                       │  │
│   │                                                                          │  │
│   │   • generic                = Secret 类型                                │  │
│   │   • monitoring-basic-auth  = Secret 名称【改】                          │  │
│   │   • --from-file=auth       = 从 auth 文件读取                           │  │
│   │   • -n monitoring          = 放到 monitoring 命名空间                   │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   步骤3:Ingress 添加注解(三个缺一不可)                                        │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   annotations:                                                           │  │
│   │     nginx.ingress.kubernetes.io/auth-type: basic                        │  │
│   │     nginx.ingress.kubernetes.io/auth-secret: monitoring-basic-auth      │  │
│   │     nginx.ingress.kubernetes.io/auth-realm: "请输入用户名密码"          │  │
│   │                                                                          │  │
│   │   • auth-type    = 认证类型,固定写 basic                               │  │
│   │   • auth-secret  = 上一步创建的 Secret 名称【改】                       │  │
│   │   • auth-realm   = 浏览器弹窗提示文字【改】                             │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
# ═══════════════════════════════════════════════════════════════════════════════
# Basic Auth 完整操作命令
# ═══════════════════════════════════════════════════════════════════════════════

# 1. 安装 htpasswd 工具
apt install apache2-utils    # Ubuntu/Debian
# yum install httpd-tools    # CentOS/RHEL

# 2. 创建密码文件(会提示输入密码)
htpasswd -c auth admin       # admin 是用户名【改】

# 添加更多用户(不要 -c,否则会覆盖)
# htpasswd auth user2

# 3. 创建 K8s Secret
kubectl create secret generic monitoring-basic-auth \
  --from-file=auth \
  -n monitoring

# 4. 验证 Secret 创建成功
kubectl get secret monitoring-basic-auth -n monitoring

# 5. 应用 Ingress(包含上面的认证注解)
kubectl apply -f ~/k8s/monitoring/ingress.yaml

# ═══════════════════════════════════════════════════════════════════════════════
# 测试:浏览器访问 http://grafana.example.com
# 会弹出登录框,输入 admin 和你设置的密码
# ═══════════════════════════════════════════════════════════════════════════════

访问效果:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                                                                                 │
│   用户访问 https://grafana.example.com                                          │
│                                                                                 │
│              ┌────────────────────────────────────┐                            │
│              │        Monitoring Area             │  ← auth-realm 的文字       │
│              │                                    │                            │
│              │  用户名: [__________]              │                            │
│              │                                    │                            │
│              │  密  码: [__________]              │                            │
│              │                                    │                            │
│              │       [登录]    [取消]             │                            │
│              │                                    │                            │
│              └────────────────────────────────────┘                            │
│                                                                                 │
│   ✅ 输入正确 → 进入 Grafana 页面                                               │
│   ❌ 输入错误 → 显示 401 Unauthorized                                          │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
IP 白名单配置详细步骤
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    IP 白名单配置                                                 │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   作用:只允许指定 IP 访问,其他 IP 返回 403 Forbidden                           │
│                                                                                 │
│   适用场景:                                                                     │
│   • 公司内部系统(只允许公司出口 IP)                                           │
│   • 管理后台(只允许管理员 IP)                                                 │
│   • 配合 VPN(只允许 VPN 出口 IP)                                              │
│                                                                                 │
│   配置方式:Ingress 添加一行注解即可                                             │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                          │  │
│   │   annotations:                                                           │  │
│   │     nginx.ingress.kubernetes.io/whitelist-source-range: "IP/掩码"       │  │
│   │                                                                          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

IP/掩码 格式说明:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    CIDR 格式详解                                                 │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   格式:IP地址/掩码位数                                                          │
│                                                                                 │
│   ┌────────────────────┬───────────────────────────────────────────────────┐   │
│   │ 写法               │ 含义                                              │   │
│   ├────────────────────┼───────────────────────────────────────────────────┤   │
│   │ 123.45.67.89/32    │ 只允许这 1 个 IP                                  │   │
│   │ 123.45.67.0/24     │ 允许 123.45.67.0 ~ 123.45.67.255(256 个 IP)    │   │
│   │ 123.45.0.0/16      │ 允许 123.45.0.0 ~ 123.45.255.255(65536 个 IP)  │   │
│   │ 0.0.0.0/0          │ 允许所有 IP(等于没有限制)                       │   │
│   └────────────────────┴───────────────────────────────────────────────────┘   │
│                                                                                 │
│   常用掩码:                                                                     │
│   • /32 = 单个 IP                                                               │
│   • /24 = 256 个 IP(最后一段 0~255)                                          │
│   • /16 = 65536 个 IP(最后两段 0.0~255.255)                                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

配置示例:

# ═══════════════════════════════════════════════════════════════════════════════
# IP 白名单 Ingress 配置示例
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: monitoring-ingress
  namespace: monitoring
  annotations:
    kubernetes.io/ingress.class: nginx
    
    # ─────────────────────────────────────────────────────────────────────────
    # IP 白名单配置
    # ─────────────────────────────────────────────────────────────────────────
    
    # 示例1:只允许单个 IP
    nginx.ingress.kubernetes.io/whitelist-source-range: "123.45.67.89/32"
    
    # 示例2:允许多个 IP(逗号分隔)
    # nginx.ingress.kubernetes.io/whitelist-source-range: "123.45.67.89/32,111.222.333.444/32"
    
    # 示例3:允许一个 IP 段(公司内网出口)
    # nginx.ingress.kubernetes.io/whitelist-source-range: "123.45.67.0/24"
    
    # 示例4:混合使用
    # nginx.ingress.kubernetes.io/whitelist-source-range: "公司IP/32,家里IP/32,VPN出口IP/24"

spec:
  rules:
  - host: grafana.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-grafana
            port:
              number: 80

如何获取你的公网 IP?

# 方法1:命令行查询
curl ifconfig.me
curl ip.sb
curl myip.ipip.net

# 方法2:浏览器访问
# https://www.ip.cn
# https://ip.sb

IP 白名单 vs Basic Auth 对比:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    两种方案对比                                                  │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌────────────────┬─────────────────────────┬────────────────────────────────┐│
│   │                │ IP 白名单                │ Basic Auth                     ││
│   ├────────────────┼─────────────────────────┼────────────────────────────────┤│
│   │ 配置复杂度     │ 简单(一行注解)         │ 中等(需要创建 Secret)        ││
│   │ 用户体验       │ 无感知(IP 对就能访问)  │ 需要输入用户名密码             ││
│   │ 适合场景       │ 固定 IP 的公司/VPN       │ IP 不固定、多人访问            ││
│   │ 安全性         │ 高(IP 难伪造)          │ 中(密码可能泄露)             ││
│   │ 灵活性         │ 低(换 IP 要改配置)     │ 高(任何地方都能访问)         ││
│   └────────────────┴─────────────────────────┴────────────────────────────────┘│
│                                                                                 │
│   ⭐ 最佳实践:IP 白名单 + Basic Auth 组合使用(双重保护)                        │
│                                                                                 │
│   annotations:                                                                  │
│     # IP 白名单                                                                 │
│     nginx.ingress.kubernetes.io/whitelist-source-range: "公司IP/32"            │
│     # Basic Auth                                                                │
│     nginx.ingress.kubernetes.io/auth-type: basic                               │
│     nginx.ingress.kubernetes.io/auth-secret: monitoring-basic-auth             │
│     nginx.ingress.kubernetes.io/auth-realm: "Monitoring Area"                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

11.4 Grafana 配置与使用

# ═══════════════════════════════════════════════════════════════════════════════
# Grafana 默认登录信息
# ═══════════════════════════════════════════════════════════════════════════════

# 用户名:admin
# 密码:安装时设置的密码(如 YourPassword123)

# 如果忘记密码,重置方法:
kubectl exec -it $(kubectl get pods -n monitoring -l app.kubernetes.io/name=grafana -o jsonpath='{.items[0].metadata.name}') -n monitoring -- grafana-cli admin reset-admin-password NewPassword123
Grafana 预置仪表盘

kube-prometheus-stack 安装后已经预置了很多仪表盘:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    Grafana 预置仪表盘                                            │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   仪表盘名称                           监控内容                                  │
│   ─────────────────────────────────────────────────────────────────────────    │
│   Kubernetes / Compute Resources / Cluster    集群整体资源(CPU/内存/网络)    │
│   Kubernetes / Compute Resources / Namespace  命名空间资源使用                  │
│   Kubernetes / Compute Resources / Pod        Pod 级别资源详情                  │
│   Kubernetes / Compute Resources / Node       节点资源使用                      │
│   Kubernetes / Networking / Cluster           集群网络流量                      │
│   Node Exporter / Nodes                       节点系统指标                      │
│   CoreDNS                                     DNS 查询统计                      │
│   Prometheus                                  Prometheus 自身状态               │
│                                                                                 │
│   查找路径:左侧菜单 → Dashboards → Browse → 搜索 "Kubernetes"                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
导入自定义仪表盘
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    从 Grafana 官网导入仪表盘                                     │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   1. 打开 Grafana → 左侧 + 号 → Import                                          │
│   2. 输入仪表盘 ID(从 grafana.com/grafana/dashboards 获取)                    │
│   3. 点击 Load → 选择数据源 → Import                                            │
│                                                                                 │
│   推荐仪表盘 ID:                                                                │
│   ┌────────────┬──────────────────────────────────────────────────────────────┐│
│   │    ID      │ 名称                                                         ││
│   ├────────────┼──────────────────────────────────────────────────────────────┤│
│   │   315      │ Kubernetes cluster monitoring (经典款)                       ││
│   │   13770    │ 1 Kubernetes All-in-one Cluster Monitoring CN (中文版)       ││
│   │   11074    │ Node Exporter for Prometheus Dashboard EN                    ││
│   │   12633    │ Linux Hosts Metrics(节点详细)                               ││
│   │   7249     │ Kubernetes Cluster (Prometheus)                              ││
│   └────────────┴──────────────────────────────────────────────────────────────┘│
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

11.5 配置报警(AlertManager)

11.5.1 报警架构
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    报警流程                                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌──────────────┐      ┌──────────────┐      ┌──────────────┐                 │
│   │  Prometheus  │ ──→  │ AlertManager │ ──→  │   通知渠道   │                 │
│   │              │      │              │      │              │                 │
│   │ 1.触发规则   │      │ 2.分组去重   │      │ 3.发送通知   │                 │
│   │   采集指标   │      │   静默抑制   │      │   邮件/钉钉  │                 │
│   │   判断阈值   │      │              │      │   企微/Slack │                 │
│   └──────────────┘      └──────────────┘      └──────────────┘                 │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
11.5.2 配置钉钉报警
# ═══════════════════════════════════════════════════════════════════════════════
# 文件:~/k8s/monitoring/alertmanager-config.yaml
# AlertManager 配置(发送到钉钉)
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: v1
kind: Secret
metadata:
  name: alertmanager-prometheus-kube-prometheus-alertmanager
  namespace: monitoring
  labels:
    app: kube-prometheus-stack-alertmanager
stringData:
  alertmanager.yaml: |
    global:
      resolve_timeout: 5m
    
    # 路由配置
    route:
      group_by: ['alertname', 'namespace']    # 按告警名和命名空间分组
      group_wait: 30s                         # 等待30秒收集同组告警
      group_interval: 5m                      # 同组告警发送间隔
      repeat_interval: 12h                    # 重复告警间隔
      receiver: 'dingtalk'                    # 默认接收器
      
      routes:
      # 关键告警立即发送
      - match:
          severity: critical
        receiver: 'dingtalk-critical'
        group_wait: 10s
      
      # 普通告警
      - match:
          severity: warning
        receiver: 'dingtalk'
    
    # 接收器配置
    receivers:
    - name: 'dingtalk'
      webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/webhook1/send'
        send_resolved: true    # 故障恢复时也发送通知
    
    - name: 'dingtalk-critical'
      webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/webhook1/send'
        send_resolved: true
    
    # 抑制规则(避免告警风暴)
    inhibit_rules:
    - source_match:
        severity: 'critical'
      target_match:
        severity: 'warning'
      equal: ['alertname', 'namespace']
11.5.3 部署钉钉 Webhook 转发器
# ═══════════════════════════════════════════════════════════════════════════════
# 文件:~/k8s/monitoring/dingtalk-webhook.yaml
# 钉钉消息转发器(将 AlertManager 消息转为钉钉格式)
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dingtalk-webhook
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dingtalk-webhook
  template:
    metadata:
      labels:
        app: dingtalk-webhook
    spec:
      containers:
      - name: dingtalk-webhook
        image: timonwong/prometheus-webhook-dingtalk:latest
        args:
        - --config.file=/config/config.yml
        ports:
        - containerPort: 8060
        volumeMounts:
        - name: config
          mountPath: /config
      volumes:
      - name: config
        configMap:
          name: dingtalk-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: dingtalk-config
  namespace: monitoring
data:
  config.yml: |
    targets:
      webhook1:
        # 【改】替换为你的钉钉机器人 Webhook URL
        url: https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN_HERE
        secret: YOUR_SECRET_HERE    # 【改】钉钉机器人的加签密钥
        message:
          title: '{{ template "dingtalk.default.title" . }}'
          text: '{{ template "dingtalk.default.content" . }}'
---
apiVersion: v1
kind: Service
metadata:
  name: dingtalk-webhook
  namespace: monitoring
spec:
  selector:
    app: dingtalk-webhook
  ports:
  - port: 8060
    targetPort: 8060
# 应用配置
kubectl apply -f ~/k8s/monitoring/dingtalk-webhook.yaml
kubectl apply -f ~/k8s/monitoring/alertmanager-config.yaml

# 重启 AlertManager 使配置生效
kubectl rollout restart statefulset alertmanager-prometheus-kube-prometheus-alertmanager -n monitoring
11.5.4 配置邮件报警
# ═══════════════════════════════════════════════════════════════════════════════
# AlertManager 邮件配置
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: v1
kind: Secret
metadata:
  name: alertmanager-prometheus-kube-prometheus-alertmanager
  namespace: monitoring
stringData:
  alertmanager.yaml: |
    global:
      # 【改】SMTP 邮件服务器配置
      smtp_smarthost: 'smtp.qq.com:465'
      smtp_from: 'your-email@qq.com'
      smtp_auth_username: 'your-email@qq.com'
      smtp_auth_password: 'your-smtp-password'    # QQ邮箱需要用授权码
      smtp_require_tls: false
    
    route:
      group_by: ['alertname']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 12h
      receiver: 'email'
    
    receivers:
    - name: 'email'
      email_configs:
      - to: 'admin@example.com'                   # 【改】接收告警的邮箱
        send_resolved: true
11.5.5 自定义报警规则
# ═══════════════════════════════════════════════════════════════════════════════
# 文件:~/k8s/monitoring/alert-rules.yaml
# 自定义报警规则
# ═══════════════════════════════════════════════════════════════════════════════

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: custom-alerts
  namespace: monitoring
  labels:
    release: prometheus    # 必须有这个标签才能被 Prometheus 发现
spec:
  groups:
  # ─────────────────────────────────────────────────────────────────────────────
  # Pod 相关告警
  # ─────────────────────────────────────────────────────────────────────────────
  - name: pod-alerts
    rules:
    # Pod 重启次数过多
    - alert: PodRestartTooMany
      expr: increase(kube_pod_container_status_restarts_total[1h]) > 3
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 重启次数过多"
        description: "Pod 在过去1小时内重启了 {{ $value }} 次"
    
    # Pod 处于非 Running 状态
    - alert: PodNotRunning
      expr: kube_pod_status_phase{phase!="Running",phase!="Succeeded"} == 1
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 状态异常"
        description: "Pod 状态为 {{ $labels.phase }}"
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 节点相关告警
  # ─────────────────────────────────────────────────────────────────────────────
  - name: node-alerts
    rules:
    # CPU 使用率过高
    - alert: NodeHighCPU
      expr: (1 - avg by(instance)(irate(node_cpu_seconds_total{mode="idle"}[5m]))) * 100 > 80
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "节点 {{ $labels.instance }} CPU 使用率过高"
        description: "CPU 使用率达到 {{ $value | printf \"%.2f\" }}%"
    
    # 内存使用率过高
    - alert: NodeHighMemory
      expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 80
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "节点 {{ $labels.instance }} 内存使用率过高"
        description: "内存使用率达到 {{ $value | printf \"%.2f\" }}%"
    
    # 磁盘使用率过高
    - alert: NodeHighDisk
      expr: (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes) * 100 > 85
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "节点 {{ $labels.instance }} 磁盘使用率过高"
        description: "磁盘 {{ $labels.mountpoint }} 使用率达到 {{ $value | printf \"%.2f\" }}%"
  
  # ─────────────────────────────────────────────────────────────────────────────
  # 应用相关告警
  # ─────────────────────────────────────────────────────────────────────────────
  - name: app-alerts
    rules:
    # HTTP 错误率过高
    - alert: HighHTTPErrorRate
      expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.1
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "HTTP 5xx 错误率过高"
        description: "5xx 错误率达到 {{ $value | printf \"%.2f\" }}%"
# 应用自定义报警规则
kubectl apply -f ~/k8s/monitoring/alert-rules.yaml

# 验证规则是否生效
kubectl get prometheusrule -n monitoring

11.6 日志系统(Loki + Promtail)

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    日志系统架构                                                  │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   为什么选择 Loki?                                                              │
│   • 专为日志设计,与 Grafana 完美集成                                           │
│   • 不索引日志内容,只索引标签,成本低                                          │
│   • 架构简单,易于部署和维护                                                    │
│   • 比 ELK(Elasticsearch)轻量得多                          
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值