集群服务 Kubernetes 部署 - 自建方案(完整版)
适用场景:完全自主掌控、学习K8s、私有化部署、特殊安全要求
本文档:从零搭建完整 K8s 集群,包含网络、中间件、应用部署全流程
目录
核心概念详解
在开始部署之前,先理解这些核心概念
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 对比
| 对比项 | ConfigMap | Secret |
|---|---|---|
| 用途 | 普通配置 | 敏感信息(密码、密钥) |
| 存储 | 明文 | 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)轻量得多
1710

被折叠的 条评论
为什么被折叠?



