这篇文章不是某一个组件的安装教程,而是用来解释我当前这套 Kubernetes 系统为什么这样搭。
如果只跟着命令敲,很容易变成“能跑,但不知道为什么”。这篇文章的目标是:看完以后,我能知道每一层解决什么问题、为什么要这么选、好处是什么、哪里会踩坑。以后重新搭建 Kubernetes 集群时,可以按同样的思路独立设计。
当前架构
当前集群是 3 台虚拟机组成的三主节点 Kubernetes:
k8s-master1 192.168.3.214
k8s-master2 192.168.3.215
k8s-master3 192.168.3.216
API VIP 192.168.3.217
Ingress VIP 192.168.3.231
MetalLB IP 192.168.3.230
整体访问路径可以先这样理解:
浏览器
|
| 访问 https://k8s-ai.jihw.top
v
DNS 解析到入口 VIP
|
v
Keepalived + HAProxy
|
v
Ingress NodePort
|
v
ingress-nginx-controller
|
v
new-api Service
|
v
new-api Pod
|
+--> CloudNativePG PostgreSQL
|
+--> redis-master-proxy
|
v
Redis Sentinel 当前 master
Kubernetes API 的访问路径是另一条:
kubectl / kubelet / 集群组件
|
v
API VIP 192.168.3.217:8443
|
v
HAProxy
|
v
三台 kube-apiserver:6443
这两条路径要分清楚:
192.168.3.217主要服务 Kubernetes 控制面,也就是 API Server。192.168.3.231主要服务业务入口,也就是 HTTP/HTTPS 访问。192.168.3.230是 MetalLB 分配给 ingress-nginx Service 的地址。
先理解高可用分层
高可用不是装了 3 台 Kubernetes 主节点就结束。三主节点只解决控制面一部分问题。
我把整套系统拆成几层:
1. 虚拟机和网络层
2. Kubernetes 控制面
3. 入口负载均衡层
4. Ingress 和域名证书层
5. 业务应用层
6. 数据库和缓存层
7. 存储层
8. 监控和运维入口层
每一层都可能有单点。
例如:
- 3 个 API Server 都正常,但 Ingress 只有 1 个 Pod,业务域名仍然会挂。
- new-api 有 3 个副本,但 PostgreSQL 是单实例,数据库节点挂了业务仍然不可用。
- PostgreSQL 做了主从,但存储卷无法重新挂载,数据库仍然可能恢复慢。
- 业务全部正常,但 Grafana 和 Headlamp 是单副本,运维入口仍然会断。
所以高可用要一层一层补。
虚拟机和网络层
当前是在 VMware 虚拟机里搭建集群。之前遇到过网卡相关问题,网卡类型从 e1000 调整为 vmxnet3 后,系统内网卡名从 ens33 变成了 ens160。
这个决定的原因:
vmxnet3是 VMware 优化过的虚拟网卡,性能和稳定性通常更好。- 压测时网卡稳定性很重要,虚拟网卡异常会让节点看起来像突然失联。
- Keepalived 依赖具体网卡名,如果网卡名变了,配置必须同步修改。
需要记住:
网卡类型变化
-> Linux 网卡名可能变化
-> keepalived.conf 里的 interface 要跟着改
-> NetworkManager 连接配置也可能要跟着改
检查命令:
ip addr
nmcli connection show
grep -R "ens33\|ens160" /etc/keepalived /etc/NetworkManager 2>/dev/null
经验结论:底层网络不稳时,Kubernetes 上层所有组件都会表现得很奇怪。先把网卡、IP、DNS、时间同步这些基础打牢,再谈应用高可用。
控制面高可用
当前 3 台节点都是 control-plane:
k8s-master1
k8s-master2
k8s-master3
控制面核心组件包括:
etcd
kube-apiserver
kube-controller-manager
kube-scheduler
为什么要 3 台,而不是 2 台?
因为 etcd 需要多数派。3 个 etcd 节点时,挂掉 1 个,还剩 2 个,仍然是多数派。
3 个 etcd 节点
挂 0 个:3/3,可用
挂 1 个:2/3,可用
挂 2 个:1/3,不可用
如果只有 2 个 etcd 节点:
2 个 etcd 节点
挂 1 个:1/2,不是多数派,不可用
所以三主节点是合理的入门 HA 规模。
但是要注意:控制面可用,不等于业务一定可用。控制面负责调度、管理和 API,业务是否可访问还要看 Ingress、Service、Pod、数据库、存储。
检查控制面:
kubectl get nodes -o wide
kubectl -n kube-system get pods -o wide
API 入口:Keepalived 和 HAProxy
kubectl 和集群组件需要一个稳定的 API Server 地址。如果直接写某一台机器的 6443,那台机器挂了,kubectl 就访问不了。
所以这里使用:
Keepalived 提供 VIP
HAProxy 转发到多个 kube-apiserver
设计思路:
kubectl
|
v
192.168.3.217:8443
|
v
HAProxy
|
+--> 192.168.3.214:6443
+--> 192.168.3.215:6443
+--> 192.168.3.216:6443
好处:
- kubectl 永远访问同一个地址。
- 某个 API Server 挂了,HAProxy 会把它摘掉。
- 当前持有 VIP 的节点挂了,Keepalived 可以把 VIP 漂移到其他节点。
为什么不是只用 DNS 轮询?
DNS 轮询不能很好判断后端是否健康,也不能快速漂移 VIP。Keepalived + HAProxy 更适合家庭/内网环境里做一个简单可靠的入口。
检查命令:
ip addr | grep 192.168.3.217
systemctl status keepalived
systemctl status haproxy
journalctl -u haproxy --since "10 minutes ago" --no-pager
业务入口:Ingress 和 NodePort
业务域名不是直接访问 Pod,而是走 Ingress。
例如:
https://k8s-ai.jihw.top
https://grafana.jihw.top
https://headlamp.jihw.top
访问路径:
浏览器
|
v
入口 VIP / HAProxy
|
v
Ingress NodePort
|
v
ingress-nginx-controller
|
v
对应业务 Service
为什么要 Ingress?
- Service 只适合集群内部访问。
- Ingress 可以按域名和路径转发。
- Ingress 可以统一处理 HTTPS。
- 多个应用可以共用 80/443 入口。
之前关闭 master3 后,业务域名访问失败,原因不是 new-api 挂了,而是 ingress-nginx-controller 只有 1 个副本,并且刚好在 master3。
修复方式:
ingress-nginx-controller replicas = 3
使用 topologySpreadConstraints 尽量分散
经验结论:入口层也必须高可用。业务 Pod 高可用,但入口 Pod 单副本,域名仍然会挂。
检查命令:
kubectl -n ingress-nginx get deploy,pods,svc -o wide
kubectl get ingress -A
curl -k -I https://k8s-ai.jihw.top/
MetalLB 的作用
当前 ingress-nginx Service 是 LoadBalancer 类型,并由 MetalLB 分配内网 IP:
192.168.3.230
为什么内网 Kubernetes 需要 MetalLB?
在云厂商里,Service type=LoadBalancer 会自动创建云负载均衡。但家庭内网或裸机环境没有云负载均衡,Kubernetes 自己不知道去哪里申请外部 IP。
MetalLB 的作用就是在裸机/内网环境中提供 LoadBalancer IP。
简单理解:
云上:Service LoadBalancer -> 云厂商负载均衡
内网:Service LoadBalancer -> MetalLB 分配局域网 IP
检查命令:
kubectl -n metallb-system get pods -o wide
kubectl -n ingress-nginx get svc ingress-nginx-controller -o wide
证书:cert-manager
HTTPS 证书由 cert-manager 管理。
好处:
- 不用手动申请和续期证书。
- Ingress 上写注解,cert-manager 自动创建 TLS Secret。
- 证书快过期时自动续期。
当前使用阿里云 DNS 验证来签发证书。DNS 验证的好处是:
- 不要求应用临时暴露 HTTP 校验路径。
- 内网服务也可以申请公网可信证书。
- 对多个子域名更方便。
检查命令:
kubectl get certificate -A
kubectl get clusterissuer
kubectl -n cert-manager get pods -o wide
存储:Longhorn
Kubernetes 的 Pod 可以随时重建,所以数据不能只放在容器本地。数据库、Grafana、Prometheus 这类有状态组件都需要 PVC。
当前使用 Longhorn 作为存储系统。
为什么用 Longhorn?
- 它适合家庭/小集群。
- 可以把多个节点磁盘做成分布式块存储。
- PVC 可以跟随 Pod 在节点之间重新挂载。
- 有 UI,便于观察卷状态。
但 Longhorn 不是魔法。要理解几个关键点:
RWO: 同一时间通常只能被一个节点读写挂载
RWX: 可以被多个节点同时读写,通常需要 NFS/share-manager
replica: Longhorn 卷副本,用来抵抗磁盘或节点故障
这次故障演练里,Grafana 恢复慢,就是因为它使用 SQLite + RWO PVC。旧 Pod 没完全释放卷时,新 Pod 不能在别的节点立刻挂载。
所以:
- 无状态应用可以直接多副本。
- 有状态应用不能盲目多副本。
- 数据库要用数据库自己的高可用方案,不只是靠 Longhorn 卷副本。
检查命令:
kubectl get storageclass
kubectl -n longhorn-system get pods -o wide
kubectl -n longhorn-system get nodes.longhorn.io -o wide
kubectl -n longhorn-system get volumes.longhorn.io -o wide
当前还需要继续关注:Longhorn 节点 Ready 状态、卷副本是否健康、节点恢复时是否有重建压力。
new-api 应用层
new-api 是当前主要业务应用。
最初它是单副本,单副本的问题很直接:
Pod 所在节点挂了
-> new-api 不可访问
现在 new-api 改成 3 副本:
new-api replicas = 3
好处:
- 某一个 Pod 挂了,还有其他 Pod。
- 某一个节点挂了,只要还有其他节点上的 Pod,业务仍然可以访问。
- Service 会自动只把流量发给 Ready 的 Pod。
为什么 new-api 要尽量无状态?
因为无状态应用最适合多副本。多个 Pod 之间不需要共享本地数据,业务数据放到 PostgreSQL 和 Redis。
设计原则:
应用本地不保存关键业务状态
业务数据放 PostgreSQL
缓存和临时状态放 Redis
日志后续交给统一日志系统
检查命令:
kubectl -n new-api get deploy new-api
kubectl -n new-api get pods -l app=new-api -o wide
kubectl -n new-api get endpoints new-api
curl -k https://k8s-ai.jihw.top/api/status
PostgreSQL:CloudNativePG
PostgreSQL 不能简单地把单实例改成 3 个 Pod。数据库有主从、复制、故障切换、数据一致性这些问题。
当前使用 CloudNativePG 做 PostgreSQL 高可用。
架构:
newapi-postgres-1
newapi-postgres-2
newapi-postgres-3
其中 1 个是 primary
其他是 replica
CloudNativePG 提供几个重要 Service:
newapi-postgres-rw 写入口,指向当前 primary
newapi-postgres-ro 只读入口,指向 replica
newapi-postgres-r 所有实例入口
为什么用 -rw Service?
因为主库可能切换。如果应用直接连某个 Pod 名,一旦主库换了,应用还会连旧主库。-rw Service 会自动指向当前 primary。
好处:
- 主库故障时可以自动切换。
- 应用不需要知道哪个 Pod 是主库。
- PostgreSQL 高可用逻辑由专业 Operator 管理。
注意:
PostgreSQL HA 不能替代备份
Longhorn 卷副本不能替代数据库复制
主从切换期间应用可能会短暂重连
检查命令:
kubectl -n new-api get cluster newapi-postgres
kubectl -n new-api get pods -l cnpg.io/cluster=newapi-postgres -o wide
kubectl -n new-api get svc | grep newapi-postgres
Redis:Sentinel + master 代理
Redis 也不能只保留单实例。最初的旧 Redis 是:
redis-master-0
它的问题是:所在节点挂了,new-api 就连不上 Redis。
现在 Redis 后端使用 Sentinel:
redis-ha-node-0
redis-ha-node-1
redis-ha-node-2
每个 redis-ha-node Pod 里有两个容器:
redis
sentinel
Sentinel 的作用:
- 监控当前 Redis master。
- 判断 master 是否故障。
- 在故障时选出新的 master。
但是当前 calciumion/new-api:latest 镜像对 Redis Sentinel 支持不够直接。它会把 REDIS_CONN_STRING 当普通 Redis URL 解析。
所以加了一个代理:
redis-master-proxy
访问路径:
new-api
|
v
redis-master-proxy
|
v
当前 Redis master
redis-master-proxy 使用 HAProxy 检查 Redis 节点的 role:master,只把流量转发给当前 master。
为什么 proxy 是 2 个副本,不是 3 个?
因为 proxy 只是代理层,不存数据。2 个副本已经可以避免代理单点:
proxy-1 挂了,还有 proxy-2
proxy-2 挂了,还有 proxy-1
真正的数据高可用由 3 个 redis-ha-node 承担。
检查命令:
kubectl -n new-api get statefulset redis-ha-node
kubectl -n new-api get pods -l app.kubernetes.io/instance=redis-ha -o wide
kubectl -n new-api get deploy redis-master-proxy
kubectl -n new-api get secret new-api-secret -o jsonpath='{.data.REDIS_CONN_STRING}' | base64 -d
echo
平台组件
平台组件是用来运维集群的,不一定是业务本身,但它们也需要考虑可用性。
当前主要有:
Headlamp
Prometheus
Grafana
Alertmanager
metrics-server
Headlamp
Headlamp 是 Kubernetes Web UI,适合看 Pod、Service、Ingress、日志和事件。
它是无状态服务,所以可以直接 2 副本。
好处:
- 一个 Headlamp Pod 挂了,还有另一个。
- 某个节点挂了,仍然可以通过页面观察集群。
metrics-server
kubectl top nodes 和 HPA 依赖 metrics-server。
如果 metrics-server 挂了,会看到:
Metrics API not available
所以 metrics-server 也适合至少 2 副本。
Grafana
Grafana 当前是单副本,因为它使用 SQLite 和 RWO PVC。
为什么不直接 2 副本?
因为两个 Grafana 同时访问同一个 SQLite 数据库不安全,RWO PVC 也不能跨节点同时挂载。
当前策略:
Grafana 单副本
Recreate 更新策略
放宽启动探针
后续如果要真 HA,改用外部 PostgreSQL
所以这里要理解一个重要原则:
无状态组件可以直接多副本
有状态组件要先解决数据一致性和存储访问方式
为什么资源会紧张
当前每台机器大约 2 核、4GB 内存级别。三主节点上同时跑:
Kubernetes 控制面
Longhorn
Ingress
new-api
PostgreSQL
Redis
Prometheus
Grafana
Headlamp
cert-manager
MetalLB
这对小虚拟机来说已经比较重。
故障演练时,如果关掉一台节点,剩下两台会承接更多 Pod,CPU 很容易接近 100%。这时很多组件不是配置错,而是健康探针超时。
常见表现:
Readiness probe failed: context deadline exceeded
Liveness probe failed: context deadline exceeded
kubectl top nodes 显示 CPU 很高
Longhorn 节点 Ready 反复变化
建议资源:
每台至少 4 vCPU
每台至少 8GB 内存
磁盘预留足够空间给 Longhorn
如果资源有限,要做取舍:
- 先保证业务和数据库。
- 监控组件减少保留时间和资源请求。
- Grafana 保持单副本快速恢复。
- 清理不用的旧 Redis、旧 PostgreSQL。
从零搭建顺序
以后重新搭建时,可以按这个顺序来。
1. 准备虚拟机
先准备 3 台机器:
固定 IP
统一主机名
关闭 swap
配置时间同步
选择稳定网卡类型
确保 ssh key 可登录
为什么先做这些?
因为 Kubernetes 对网络、时间和主机名很敏感。底层不稳,上层排错会非常痛苦。
2. 安装容器运行时和 Kubernetes
安装:
containerd
kubeadm
kubelet
kubectl
用 kubeadm 初始化第一个 control-plane,再加入另外两个 control-plane。
目标结果:
kubectl get nodes
能看到 3 个节点 Ready。
3. 安装 CNI
CNI 负责 Pod 网络。当前使用 flannel。
没有 CNI 时,Pod 之间无法正常通信,CoreDNS 也可能起不来。
检查:
kubectl -n kube-flannel get pods -o wide
kubectl -n kube-system get pods -o wide
4. 做 API Server 高可用入口
安装并配置:
Keepalived
HAProxy
让 kubectl 和 kubelet 统一访问 VIP。
5. 安装 MetalLB 和 ingress-nginx
MetalLB 负责内网 LoadBalancer IP。
ingress-nginx 负责域名 HTTP/HTTPS 转发。
从一开始就把 ingress-nginx 设置为多副本,否则入口会成为单点。
6. 安装 cert-manager
让证书自动签发和续期。
域名访问要尽量一开始就走 HTTPS,这样后面应用迁移时不用反复改入口。
7. 安装 Longhorn
安装存储系统,让 PVC 可以动态创建。
安装后重点检查:
kubectl get storageclass
kubectl -n longhorn-system get pods -o wide
kubectl -n longhorn-system get nodes.longhorn.io -o wide
8. 部署业务应用
先部署单副本验证能跑,再改多副本。
部署应用时要问自己:
应用是否无状态?
是否依赖本地文件?
是否可以多副本同时运行?
健康检查是否合理?
资源 requests/limits 是否设置?
9. 部署数据库和缓存高可用
PostgreSQL 使用 CloudNativePG。
Redis 使用 Sentinel,并为 new-api 增加 redis-master-proxy。
注意:数据库高可用不是“Pod 数量多”这么简单,要用合适的 Operator 或数据库自身机制。
10. 部署监控和运维工具
安装:
metrics-server
Headlamp
kube-prometheus-stack
然后根据是否有状态决定副本数:
- Headlamp:无状态,可以 2 副本。
- metrics-server:可以 2 副本。
- Grafana:当前 SQLite + RWO,只做单副本快速恢复。
- Prometheus:后续再考虑多副本和长期存储。
11. 做故障演练
不要等真正故障时才知道哪里没做 HA。
按顺序演练:
删除一个 new-api Pod
关闭一个 Redis master Pod
删除 PostgreSQL primary Pod
关闭一个 Ingress Controller Pod
关闭一台虚拟机
每次演练后检查:
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl top nodes
kubectl -n longhorn-system get volumes.longhorn.io -o wide
curl -k https://k8s-ai.jihw.top/api/status
独立搭建时的判断方法
以后遇到一个新组件,可以按这几个问题判断怎么部署:
1. 它是无状态还是有状态?
2. 它是否需要 PVC?
3. PVC 是 RWO 还是 RWX?
4. 它能不能多副本同时写同一个数据?
5. 它是否需要专门的 Operator?
6. 它挂了会影响业务,还是只影响运维观察?
7. 它的入口是 Service、Ingress,还是 APIService?
8. 它的健康检查是否会在高负载时误杀容器?
几个经验规则:
- 无状态服务优先用 Deployment,多副本。
- 数据库优先用成熟 Operator,不要自己手搓主从。
- 只有一个 RWO PVC 的组件,不要直接多副本。
- Ingress Controller、metrics-server、Headlamp 这类平台入口要多副本。
- Prometheus、Grafana 这类监控组件要区分“快速恢复”和“真正 HA”。
- Longhorn 卷健康和节点资源压力会影响大量组件。
当前架构的不足
这套架构已经能让 new-api、PostgreSQL、Redis、Ingress 具备基础高可用,但还不是完美的生产架构。
当前主要不足:
1. 虚拟机资源偏紧,故障切换时 214/215 容易 CPU 打满。
2. Longhorn 节点状态需要继续稳定,尤其是节点恢复后的 Ready 状态。
3. Grafana 还不是真正多副本 HA。
4. Prometheus 目前仍是单实例 StatefulSet。
5. 备份体系还需要继续完善。
后续优先级:
第一优先级:Longhorn 健康和资源扩容
第二优先级:PostgreSQL 定时备份和恢复演练
第三优先级:Prometheus/Grafana 监控告警完善
第四优先级:继续做完整故障演练
总结
这套 Kubernetes 架构的核心思想是:
控制面用三主保证管理能力
入口层用 Keepalived + HAProxy + ingress-nginx 保证域名访问
应用层用 Deployment 多副本保证服务可用
数据库用 CloudNativePG 保证 PostgreSQL 主从切换
缓存用 Redis Sentinel + master proxy 保证 Redis master 切换
存储用 Longhorn 提供 PVC 和卷副本
监控和运维工具按有无状态分别处理
真正理解以后,就不会只问“这个 Pod 为什么不是 3 个”,而是会先判断它属于哪一层、是不是存数据、能不能多副本、挂了影响什么。
这才是独立搭建 Kubernetes 集群最重要的能力。