背景与目标
本文围绕“我准备在两台vps上部署k3s,然后作为两个界点串联起来”展开,整理可直接复用的步骤、原理说明与排查经验。
核心内容
主题 1. 我准备在两台vps上部署k3s,然后作为两个界点串联起来
在两台 VPS 上部署 k3s 并将它们组成一个集群(一个 Server 节点 + 一个 Agent 节点)是一个非常经典且高效的选择。
既然你熟悉 Arch Linux、Neovim 并且正在做 RevieU 项目的架构,这种“轻量级 Kubernetes”方案非常适合你的开发和测试需求。
以下是实现这一目标的快速指南:
1. 准备工作
在开始之前,确保两台 VPS 满足以下条件:
- 网络连通性:两台机器之间需要互通。如果是跨云厂商,建议使用 Tailscale 或 WireGuard 建立虚拟内网,或者确保防火墙开放了以下端口:
6443(API Server)10250(Kubelet Metrics)8472(Flannel VXLAN)
- 系统环境:虽然你喜欢 Arch,但如果 VPS 运行的是其他发行版,k3s 也能完美支持。
2. 部署第一个节点 (Server/Master)
在第一台 VPS 上执行,这台将作为集群的控制平面。
curl -sfL https://get.k3s.io | sh -
关键操作: 部署完成后,你需要获取 Node Token,这是第二台机器加入集群的凭证:
sudo cat /var/lib/rancher/k3s/server/node-token
同时也记下这台机器的 公网 IP(或内网 IP)。
3. 部署第二个节点 (Agent/Worker)
在第二台 VPS 上执行,将其作为工作节点连接到 Server。
curl -sfL https://get.k3s.io | K3S_URL=https://<SERVER_IP>:6443 K3S_TOKEN=<NODE_TOKEN> sh -
请将
<SERVER_IP>替换为第一台服务器的 IP,将<NODE_TOKEN>替换为你刚刚获取的字符串。
4. 架构示意图
在你的场景中:
- Server 节点:运行 API Server、Scheduler 和 Etcd(默认使用 SQLite,适合双节点)。
- Agent 节点:只运行 Kubelet 和 Containerd,负责承载具体的 Pod 运行。
5. 进阶建议 (针对你的技术栈)
鉴于你目前的背景,这里有几个额外的小贴士:
- 管理集群:你可以把第一台机器的
/etc/rancher/k3s/k3s.yaml复制到本地的~/.kube/config。这样你就可以在本地用kubectl甚至是在 Neovim 里通过插件直接管理集群。 - ArgoCD 接入:由于你对 GitOps 感兴趣,建议在集群搭建好后立即部署 ArgoCD。这样你的 RevieU 项目(Go 后端 + React 前端)就可以通过
git push自动更新到这个双节点集群中。 - 数据库处理:由于你使用的是 PostgreSQL,建议将其运行在 Server 节点并挂载持久化存储,或者使用外部数据库,以防 Agent 节点漂移导致数据管理复杂化。
你想让我帮你写一个针对这两台 VPS 的基础 Docker Compose 转 Kubernetes Deployment 的配置模板吗。
主题 2. 我已经有一个node在运行了,但是我不想让这个节点作为主要节点,因为这台…
这是一个非常合理的决策。k3s 默认情况下,Server 节点既是“大脑”(控制平面)也会运行“体力活”(用户 Pod)。如果这台旧服务器性能较弱,我们应该通过 Taints(污点) 限制它运行任务,或者直接将新性能好的 VPS 设为新的 Server。
针对你现有的情况,我有两种优化方案:
方案一:保留旧节点为 Server,但禁止它跑业务 Pod
如果你不想重装,只是想让旧服务器只负责“发号施令”,不占用内存跑具体的业务程序(如你的 RevieU 后端),你可以给它打上“污点”。
- 在旧节点(Server)上执行:
# 这里的 <node-name> 通常是你的主机名
kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule
这样,除非你特别指定,否则所有的业务 Pod 都会自动调度到那台性能更好的新 Agent 节点上。
方案二:性能强的作为 Server,旧的作为 Agent (推荐)
对于双节点集群,通常建议将网络最稳定、性能较均衡的机器作为 Server。如果你的新 VPS 性能全方位碾压旧的,重新反向部署其实更健康。
步骤建议:
- 备份旧节点数据(如果有的话)。
- 在新 VPS 上部署 Server:
curl -sfL https://get.k3s.io | sh -
- 在旧 VPS 上重置并加入:
# 先卸载旧的 server
/usr/local/bin/k3s-uninstall.sh
# 作为 agent 加入新集群
curl -sfL https://get.k3s.io | K3S_URL=https://<新VPS_IP>:6443 K3S_TOKEN=<新TOKEN> sh -
针对你的 RevieU 项目架构建议
由于你正在负责 RevieU 的 Infra,考虑到性能差异,建议你这样分配资源:
| 资源类型 | 建议放置位置 | 原因 |
|---|---|---|
| PostgreSQL 数据库 | 性能强的新节点 | 数据库对 IO 和 CPU 要求高,旧节点容易产生瓶颈。 |
| Go (Gin) 后端 | 性能强的新节点 | 保证 API 响应速度。 |
| Nginx Proxy Manager | 旧节点 | 负载均衡和反向代理对资源消耗相对较低。 |
| ArgoCD / Monitoring | 性能强的新节点 | 这些 DevOps 工具其实挺吃内存的。 |
一个进阶的小技巧
既然你习惯用 Arch Linux 和 Neovim,我建议你在新节点部署好后,在本地开启一个 k9s(一个非常强大的终端 UI 管理工具)。它能让你像用 Neovim 快捷键一样管理这两台 VPS 上的 Pod,非常符合你的操作习惯。
需要我帮你写一份针对这两台机器性能差异的 nodeSelector 或 Affinity (亲和性) 配置示例吗。这样你可以精准控制哪些服务跑在强力节点上。
主题 3. server和agent有什么区别
在 Kubernetes(以及 k3s)的世界里,这就像是一个公司的管理层和员工层的关系。
虽然 k3s 为了轻量化把很多功能打包在一起,但 Server 和 Agent 的职责分工非常明确:
1. Server 节点 (Control Plane)
身份: 集群的“大脑”和“指挥中心”。
- 核心任务: 决策、存储、调度。
- 关键组件:
- API Server: 所有的命令(比如你输入的
kubectl)都要经过它。 - SQLite/Etcd: 存储整个集群的状态和配置(k3s 默认在单机使用 SQLite,更省资源)。
- Scheduler: 决定你的 Pod(比如 RevieU 的 Go 后端)应该运行在哪台机器上。
- API Server: 所有的命令(比如你输入的
- 资源消耗: 主要是内存和 IO。即使不跑任何业务,它也会占用一定的资源来维持集群状态。
2. Agent 节点 (Worker Node)
身份: 集群的“体力劳动者”。
- 核心任务: 运行容器、上报状态。
- 关键组件:
- Kubelet: 听从 Server 的指令,负责管理这台机器上的容器生命周期。
- Containerd: 实际运行容器的引擎。
- Kube-proxy: 处理网络规则,让外界能访问到你的服务。
- 资源消耗: 取决于你运行了多少业务。它本身非常轻量。
核心对比表
| 特性 | Server 节点 | Agent 节点 |
|---|---|---|
| 角色 | 控制平面 (Master) | 工作节点 (Worker) |
| 能否运行 Pod | 可以 (默认行为) | 可以 (唯一任务) |
| 存储数据 | 存储集群配置和状态 | 不存储集群配置 |
| 如果宕机 | 集群无法接受新指令/部署 | 仅丢失该节点上的任务,集群正常运行 |
| 安装命令参数 | k3s server | k3s agent |
结合你的情况:为什么你要在意这个区别。
既然你提到第一台服务器性能不行,理解它们的区别对你至关重要:
- 控制权分离:你可以让性能差的旧机器只当 Server。它不跑具体的业务(Pod),只负责管理。
- Server 也能当 Agent:默认情况下,k3s 的 Server 节点会自动启动 Agent 功能。也就是说,如果不做特殊配置,Server 也会跑业务。
- 建议: 如果你决定用性能强的新机器当 Server,那它既是管理节点也是主力运行节点。
- 建议: 如果你让旧机器当 Server,一定要用
taint(污点)把它的业务承载能力关掉,否则它可能会因为内存不足把整个集群带崩。
既然你要串联这两台机器,需要我为你提供一套“Server 禁用调度”的具体命令,好让旧机器专注于管理工作吗。
主题 4. 那我还是应该先在新服务器上安装k3s然后打通连哥哥节点
是的,这绝对是最理智的策略。
既然你有一台性能更强的新服务器,把它作为 Server (Master) 节点是长久之计。这样集群的“大脑”运行速度快,且有足够的余量处理后续的 API 请求和调度。
以下是针对你目前情况的最佳实践步骤:
第一步:清理旧节点(如果已安装 k3s)
如果你之前在那台性能弱的机器上已经装了 k3s 且它是作为 Server 运行的,请先卸载它,清理环境:
/usr/local/bin/k3s-uninstall.sh
第二步:在新服务器上部署 Server
在性能强的新服务器上执行,建议加上 --node-taint 参数。这样可以确保它既是 Server,也能跑业务,同时标记其角色:
curl -sfL https://get.k3s.io | sh -
安装完成后,获取加入集群所需的 Token:
sudo cat /var/lib/rancher/k3s/server/node-token
第三步:将旧服务器作为 Agent 加入
在性能弱的旧服务器上执行。既然它性能不行,我们只让它干点轻活:
curl -sfL https://get.k3s.io | K3S_URL=https://<新服务器_IP>:6443 K3S_TOKEN=<刚刚获取的TOKEN> sh -
第四步:打通网络(核心关键)
因为你是两台 VPS,通常不在同一个内网。为了让它们“串联”起来,你有两个选择:
- 公网直连(简单但不安全): 在安装命令中添加
--node-external-ip参数,并确保防火墙开放了6443和8472(UDP) 端口。 - 虚拟内网(推荐): 使用 Tailscale。给两台机器都装上 Tailscale,然后安装 k3s 时指定 Tailscale 分配的内网 IP(例如
100.x.x.x)。这样数据传输会通过加密隧道,且不需要在公网暴露端口。
针对你的配置建议
部署完成后,在新服务器(Server)上查看节点状态:
kubectl get nodes
为了保护那台弱鸡旧服务器: 你可以给旧服务器打上一个标签,比如 size=small。以后在部署 RevieU 项目的 PostgreSQL 或核心 Go 后端时,在 YAML 里加上 nodeAffinity,强制它们只在新服务器上运行。而一些轻量级的定时任务、监控埋点或静态页面 Nginx,可以放心地交给旧服务器。
由于你之后要部署 Go (Gin) 后端,需要我帮你写一个带有“节点亲和性(Node Affinity)”的 K8s 部署模板吗。这样可以确保后端服务自动避开性能差的那台机器。
主题 5. 我的老服务器上的东西我不想删掉啊
我完全理解。作为开发者,老服务器上肯定积攒了不少配置、脚本或者是还没迁移的数据,千万别随便运行卸载脚本。
如果想保留老服务器上的现有业务(非 K8s 业务),同时又想让它加入新集群,我们得换一种“温和”的策略。
最稳妥的方案:双系统并行
你不需要删除老服务器上的任何东西。你可以让老服务器既运行它原本的程序,又作为一个 k3s Agent 节点运行。
1. 确认资源余量
k3s Agent 本身非常轻量(大约占用 200MB-300MB 内存)。只要你的老服务器还有点剩余内存,这两个身份是可以共存的。
2. 操作步骤
- 在新服务器(强力机)上: 直接安装 Server。
curl -sfL https://get.k3s.io | sh -
拿到 Token 后备用。
- 在老服务器(弱机)上: 不要卸载任何东西。直接运行 Agent 安装命令:
curl -sfL https://get.k3s.io | K3S_URL=https://<新服务器IP>:6443 K3S_TOKEN=<TOKEN> sh -
k3s 会自动识别系统环境。它会作为一个新的进程运行,不会触碰你原有的 Nginx、数据库或其他应用。
️ 必须要解决的冲突:端口限制
这是最关键的一点。如果你的老服务器上已经跑了 Nginx (80/443端口) 或者 数据库 (5432端口),而 K8s 里的应用也想用这些端口,就会发生冲突。
解决方案:
- 让 K8s 避让:在部署 RevieU 项目时,不要使用
HostPort,而是使用NodePort(通常是 30000 以上的端口)或者Ingress。 - 禁用 k3s 默认流量入口:k3s 默认会占用 80/443 端口给 Traefik。如果你的老服务器已经占用了这些端口,安装 Agent 时可以加一个参数,防止它去抢占:
# 在老服务器安装时
curl -sfL https://get.k3s.io | K3S_URL=... K3S_TOKEN=... sh -s - --kubelet-arg="--image-gc-high-threshold=60"
注:其实 Agent 默认不运行控制平面的组件,冲突概率较小,主要是注意容器的网络映射。
保护老服务器的“生存空间”
既然老服务器性能不行,且还要跑原来的程序,我们必须限制 K8s 往它身上派重活:
- 限制资源使用: 在 K8s 的 YAML 配置文件里,给 Pod 设置
resources.requests和limits。 - 使用标签控制: 给老节点打个标签
node-role.kubernetes.io/legacy=true。 - 调度策略: 在你的 RevieU 部署文件中,明确指定
nodeAffinity(节点亲和性),让核心 Go 后端和数据库只跑在新服务器上。
你老服务器上原本运行的是哪些服务。(比如 Nginx、Docker 容器还是纯脚本。)我可以帮你看看具体的端口或资源冲突风险。
主题 6. 如果在运行服务的node上添加污点,pod会自动迁移到新服务器上吗
简单直接的回答是:不会立即自动迁移,除非你手动干预或者 Pod 发生了重启。
在 Kubernetes 中,污点(Taints)和容忍度(Tolerations)的逻辑是这样的:
1. 现状:已经运行的 Pod
当你给一个正在运行服务的 Node 添加 NoSchedule 污点时,Kubernetes 不会主动把已经在那儿运行的 Pod 赶走。
NoSchedule:仅影响新 Pod 的调度。已经在运行的 Pod 会继续留在那里“养老”。- 如果你想让它们立刻滚蛋,你需要使用更狠的污点效果:
NoExecute。
2. 如何让它们“自动”迁移。
你有两个选择:
方案 A:手动驱逐(推荐,最可控)
在你打完污点后,手动删掉那个 Pod(如果你是用 Deployment 部署的):
kubectl delete pod <pod名称>
由于 Deployment 会维持副本数量,它发现 Pod 没了,就会尝试重建。此时,由于老节点有污点,调度器(Scheduler)只能把新 Pod 塞到你的新服务器上。
方案 B:使用 NoExecute 污点(有风险)
如果你添加的是这个效果:
kubectl taint nodes <老节点名称> key=value:NoExecute
那么在该节点上所有没有对应“容忍度”的 Pod 都会被 立即驱逐。它们会被系统杀掉,然后在没有污点的新服务器上重启。 注意:这会导致服务出现短暂的秒级中断。
3. 针对你的实际操作建议
既然你不想删掉老服务器上的东西(非 K8s 服务),但又想让 K8s 的服务都跑在新服务器上,建议按以下步骤操作:
- 给老节点打上“尽量别来”的污点:
kubectl taint nodes <老节点名称> performance=low:NoSchedule
- “温和”地重启你的服务: 如果你的 RevieU 项目是用 Deployment 部署的,执行这个命令可以让它平滑地在新服务器上重建:
kubectl rollout restart deployment <你的项目名称>
总结
打污点是设关卡,不是赶房客。
- 打完污点 +
kubectl delete pod= 迁移到新服务器。 - 打完污点 + 什么都不做 = 旧 Pod 依然在老服务器,新 Pod 才会去新服务器。
你的 RevieU 项目现在是用 YAML 文件部署的吗。我可以帮你看看如何在 YAML 里直接加上“必须运行在新服务器”的硬性限制(NodeAffinity),这样比污点更保险。
主题 7. 我用内网,10.0.0.1,还需要https吗
在使用内网 IP(如 10.0.0.1)时,依然必须保留 https:// 前缀。
这并不是因为我们要走公网加密,而是因为 K3S 的控制平面(API Server)默认就是通过 HTTPS 协议监听 6443 端口的。即使在内网环境下,节点之间的通信也会进行双向 TLS 认证以确保集群安全。
1. 正确的内网安装命令
假设你的新服务器(Server)内网 IP 是 10.0.0.1,你在老服务器(Agent)上应该执行:
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.1:6443 K3S_TOKEN=<NODE_TOKEN> sh -
2. 为什么内网也强制 HTTPS。
- 身份验证:K8s 集群依赖证书来确认“这个节点确实是我的 Agent”,防止未经授权的机器接入你的内网集群。
- 通信加密:即使在
10.x.x.x段内,K3S 也会加密传输数据,防止内网抓包窃取敏感配置(比如你的数据库密码 Secret)。
3. ️ 关键检查:Server 端是否监听了内网 IP。
在两台 VPS 串联时,如果你的 Server 节点默认只绑定了公网 IP,Agent 访问 10.0.0.1 可能会被拒绝。
建议: 在安装新服务器(Server)时,最好显式指定它监听内网 IP,并将其加入 TLS 证书的许可名单中:
# 在新服务器(Server)执行
curl -sfL https://get.k3s.io | sh -s - server \
--bind-address=10.0.0.1 \
--advertise-address=10.0.0.1 \
--tls-san=10.0.0.1
解释:
--tls-san(Subject Alternative Name)非常重要,它告诉 Server:“如果有人通过10.0.0.1这个 IP 找你,你的 SSL 证书是有效的,不要报错。”
4. 针对你两台 VPS 的网络排烟(Troubleshooting)
既然你用的是内网 IP,安装前请先在老服务器上确认能不能“摸到”新服务器的 6443 端口:
ping 10.0.0.1
# 或者用 nc 检查端口(假设你装了 openbsd-netcat)
nc -zv 10.0.0.1 6443
一个关于 Arch Linux 的小提醒
由于你习惯使用 Arch Linux,如果你的 VPS 也是 Arch,安装 k3s 后记得检查一下 iptables。Arch 默认的 nftables 有时会拦截 k3s 的流量。如果发现节点 NotReady,可以尝试暂时关闭防火墙测试:
sudo systemctl stop ufw # 或者其他防火墙服务
你的这两台 VPS 是在同一个云服务商的同一个 VPC(私有网络)下吗。如果不在,这个 10.0.0.1 可能无法直接访问,需要建立隧道。
主题 8. 老服务器上必须开放的端口有哪些
既然你不打算删除老服务器上的原有服务,且要让它作为 Agent (Worker) 节点运行,那么端口管理就非常关键。
在 K3s 架构中,Agent 节点主要扮演“受控端”,它主动连接 Server 节点。因此,它对外开放的端口其实比 Server 少得多。
1. K3s 内部通讯必须开放的端口
为了让集群正常工作,老服务器(Agent)必须确保以下端口在防火墙(如 iptables, ufw 或云服务商安全组)中是通畅的:
| 端口 | 协议 | 角色 | 用途 |
|---|---|---|---|
| 10250 | TCP | Kubelet | 核心: 允许 Server 查看节点日志、执行命令 (kubectl exec)。 |
| 8472 | UDP | Flannel VXLAN | 网络: 节点间 Pod 互相通信的基础(Overlay 网络)。 |
注意: 如果你是在内网 (10.0.0.x) 环境下操作,建议直接对新服务器的内网 IP 开放所有流量,这样最省心。
2. 业务访问端口(容易冲突的地方)
除了 K3s 自身的端口,你还需要考虑你部署的 RevieU 项目:
- Kube-Proxy (随机范围):如果你使用
NodePort暴露服务,默认会占用 30000-32767 之间的端口。 - Traefik / Ingress (80/443):这是最危险的地方。K3s 默认会启动 Traefik 负载均衡。
- 风险:如果你老服务器原本就跑着 Nginx 占用了 80 端口,K3s Agent 启动 Traefik 时会报错或抢占失败。
- 对策:既然老服务器性能差,建议在安装 Agent 时禁用 Traefik(或者干脆只在 Server 节点运行 Ingress)。
3. 如何在老服务器上“温和”地安装。
为了避免端口冲突,我建议你在老服务器(Agent)执行安装命令时,加入一些限制参数:
curl -sfL https://get.k3s.io | K3S_URL=https://<新服务器IP>:6443 \
K3S_TOKEN=<TOKEN> \
sh -s - agent \
--node-ip=10.0.0.x \
--disable-selinux
4. 总结:你的防火墙检查清单
如果你在老服务器上配置防火墙,请按以下规则设置:
- 入站 (Inbound):
- 允许来自 新服务器内网 IP 的
TCP 10250。 - 允许来自 新服务器内网 IP 的
UDP 8472。 - (可选) 允许你原本服务的端口(如 22, 80, 443)。
- 允许来自 新服务器内网 IP 的
- 出站 (Outbound):
- 必须允许访问 新服务器内网 IP 的
TCP 6443(API Server)。
- 必须允许访问 新服务器内网 IP 的
避坑指南
由于你习惯使用 Arch Linux,记得检查一下系统的 cgroup 支持。K3s 需要 cgroup v2 或 v1。如果启动失败,可以在 /boot/cmdline.txt (或者 grub 配置) 中检查是否开启了必要的内核参数。
你老服务器上原本跑的服务,是通过 Docker 容器运行的,还是直接在 Linux 上跑的二进制/脚本。 如果是 Docker,我们要额外注意 Docker 和 K3s (Containerd) 同时运行时的网桥冲突。
主题 9. 这个可以用ufw实现吗,还是怎么做
可以使用 ufw (Uncomplicated Firewall) 轻松实现。在 Linux(包括你习惯的 Arch Linux 环境)中,ufw 是管理 iptables 规则最直观的方式。
既然你想对新服务器(Server)的内网 IP 开放所有流量,以下是具体的操作命令:
1. 使用 ufw 授权内网 IP
在**老服务器(Agent)**上执行,假设新服务器的内网 IP 是 10.0.0.1:
# 允许来自新服务器的所有 TCP/UDP 流量
sudo ufw allow from 10.0.0.1
执行完这一行,新服务器发往老服务器的任何端口(包括 K3s 必须的 10250、8472 等)就全通了。
2. 检查并启用 ufw
如果你之前没用过 ufw,记得确认它的状态:
# 检查状态
sudo ufw status
# 如果是 inactive,先确保 ssh 没被关掉,再启动
sudo ufw allow ssh
sudo ufw enable
3. 更进阶的做法:基于网段授权
如果你的两台 VPS 在同一个私有子网(比如都是 10.0.0.x),你也可以直接允许整个内网网段,这样以后如果你增加第三个、第四个节点,就不需要再单独配置了:
sudo ufw allow from 10.0.0.0/24
️ 一个必须要提醒你的“Arch 用户”细节
由于你平时使用 Arch Linux,ufw 底层实际上是在操作 iptables 或 nftables。K3s 会为了实现容器网络(Flannel/Cilium)自己往 iptables 里插入很多复杂的转发规则。
这就可能导致一个经典问题: ufw 的默认策略(比如 DEFAULT_INPUT_POLICY="DROP")可能会意外拦截 K3s 内部 Pod 之间的流量。
建议: 如果安装完 K3s 后发现 kubectl get nodes 显示节点是 Ready,但 Pod 之间无法通信(比如 RevieU 的后端连不上数据库),你需要修改 ufw 的转发政策:
- 编辑配置文件:
sudo nvim /etc/default/ufw(或者用你常用的 Neovim) - 将
DEFAULT_FORWARD_POLICY从"DROP"改为"ACCEPT":
DEFAULT_FORWARD_POLICY="ACCEPT"
- 重启防火墙:
sudo ufw reload
总结操作清单
- 老服务器:执行
sudo ufw allow from 10.0.0.1。 - 新服务器:执行
sudo ufw allow from 10.0.0.2(假设老服务器是 .2)。 - 验证:在新服务器上尝试
telnet 10.0.0.2 10250,通了就说明 K3s 串联没问题了。
你的两台 VPS 是直接在云服务商(如腾讯云、阿里云或 AWS)的控制台里有安全组设置,还是只靠系统内部的 ufw 管理。 如果有云端安全组,那里也要记得放开内网 IP。
主题 10. 老服务器ip是10.0.0.1,新服务器是10.0.0.4
既然 IP 已经明确了,那我们直接对号入座。你只需要在两台机器上分别执行几行命令,就能把“护城河”修好,同时给 K3s 留出专门的通道。
1. 在老服务器 (10.0.0.1) 上操作
这台机器性能较弱且有旧服务,我们要确保它只信任新服务器的指令:
# 1. 允许来自新服务器(Server 节点)的所有内网流量
sudo ufw allow from 10.0.0.4
# 2. 确保你原有的服务端口依然开放(假设是 80 和 443)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 3. 允许 SSH 访问(防止断开连接)
sudo ufw allow ssh
# 4. 修改转发策略(Arch Linux/K3s 必须,否则 Pod 无法联网)
sudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
# 5. 生效
sudo ufw reload
2. 在新服务器 (10.0.0.4) 上操作
这台是你的主力机(Server 节点),它需要接收来自老服务器(Agent)的心跳包和数据流:
# 1. 允许来自老服务器的所有内网流量
sudo ufw allow from 10.0.0.1
# 2. 开放 K3s 的控制平面端口(虽然前面 all from 已经包含了,但建议显式开放)
sudo ufw allow 6443/tcp
# 3. 允许 SSH
sudo ufw allow ssh
# 4. 修改转发策略
sudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
# 5. 生效
sudo ufw reload
3. 部署串联命令
网络打通后,你的 K3s 部署命令应该是这样的:
在新服务器 (10.0.0.4) 启动 Server:
curl -sfL https://get.k3s.io | sh -s - server \
--node-ip=10.0.0.4 \
--advertise-address=10.0.0.4 \
--tls-san=10.0.0.4
在老服务器 (10.0.0.1) 加入 Agent:
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.4:6443 \
K3S_TOKEN=<新服务器的TOKEN> \
sh -s - agent \
--node-ip=10.0.0.1
针对 RevieU 项目的小提示
由于老服务器(10.0.0.1)上已经有了旧的 Nginx 或服务,可能会占用 80 或 443 端口。
- 如果冲突了:新服务器部署好的 Traefik(K3s 自带的入口控制器)在老服务器上可能会启动失败。
- 解决办法:你可以利用 Arch/Neovim 的便携性,直接编辑控制台。在部署你的 RevieU 前端时,通过
Service的nodeSelector将流量入口(Ingress)强制固定在性能更强、且端口干净的 10.0.0.4 上。
既然你现在要把这两台串起来,需要我帮你写一段 nodeAffinity(节点亲和性)的 YAML 片段吗。这样你可以确保 RevieU 的后端只会在 10.0.0.4 上启动。
排查场景 1. 这是什么意思,我是ubuntu
- 场景:这是什么意思,我是ubuntu
- 处理要点:
虽然你使用的是 Ubuntu,但这一步同样非常重要。
简单来说,这是在告诉防火墙(ufw):“允许流量在不同的网卡之间穿梭。”
为什么要这么做。
在 Kubernetes (k3s) 中,流量的流动方式和普通服务器不一样:
- 外部流量进入服务器。
- ufw 拦截流量。
- K3s 的虚拟网络(Flannel) 需要把这个流量分发给运行在内部的 Pod(容器)。
默认情况下,Ubuntu 的 ufw 为了安全,会将这种“转发(Forward)”行为设为 DROP(丢弃)。如果不改为 ACCEPT,就会出现以下情况:
- 你的 RevieU 后端 Pod 运行正常,但无法访问外网去下载插件。
- 外部用户访问你的公网 IP,流量到了服务器后,被 ufw 拦住,进不去 Pod。
具体操作步骤
你可以手动检查并修改,不需要死记硬背 sed 命令:
- 打开配置文件:
sudo nano /etc/default/ufw
- 找到这一行(通常在第 19 行左右):
DEFAULT_FORWARD_POLICY="DROP"
- 修改为:
DEFAULT_FORWARD_POLICY="ACCEPT"
- 保存并重启 ufw:
sudo ufw reload
这样安全吗。
别担心,这并不代表你的服务器“全裸”了。
DEFAULT_INPUT_POLICY依然是DROP(依然会拦截未授权的入站请求)。- 我们只是开启了 转发能力,让 K3s 内部的网络插件(CNI)能够正常搬运数据包。
给你的 RevieU 项目避个坑
因为你在做 RevieU 的后端(Go/Gin),如果你在 Ubuntu 上不改这个设置,你可能会发现:即使容器启动了,curl localhost:8080 也是通的,但从其他机器访问这个端口就是死活不通。 改掉这个转发策略就能解决这个问题。
你现在准备好开始在新服务器 (10.0.0.4) 上运行安装脚本了吗。我可以帮你盯着 Server 端的启动参数。
主题 11. 你这一通操作,是把我原来的服务器变成了agent,新服务器变成了serv…
没错,完全正确。
简单说,我建议你把集群的**“大脑”搬到新服务器(10.0.0.4)上,而让老服务器(10.0.0.1)退居二线,只当一个“打工人”**。
这样做对你目前的情况有三个核心好处:
1. 保护老服务器的现有服务
正如你说的,老服务器性能不行,而且你不想删掉上面的东西。
- 作为 Server:它需要运行数据库(etcd/sqlite)、API 服务、调度器,非常吃 CPU 和内存。
- 作为 Agent:它只需要运行一个很轻的
kubelet进程。它会听从新服务器的指挥,你甚至可以设置它“只待命,不干活”,这样它就能安稳地运行你原本的那些旧服务。
2. 让 RevieU 项目跑得更稳
你正在做的 RevieU 项目(Go + React + PostgreSQL)对响应速度有要求。
- 将新服务器设为 Server,意味着主要的计算压力都在这台性能好的机器上。
- 即使老服务器因为性能问题卡顿或重启,整个 K8s 集群的“大脑”在新服务器上依然是清醒的,不会导致整个集群崩溃。
3. 现在的架构长这样
| 角色 | 服务器 IP | 任务 |
|---|---|---|
| Server (大脑) | 10.0.0.4 (新) | 决策、存储、管理,同时也运行你的核心业务 Pod。 |
| Agent (体力) | 10.0.0.1 (老) | 听令行事。因为性能差,我们主要用它跑一些不重要的辅助插件。 |
如果你一定要反过来(老服务器当 Server),会发生什么。
如果让老服务器(10.0.0.1)当 Server:
- 它会为了管理新服务器而消耗掉仅剩的资源,可能导致你原本在跑的服务崩溃。
- 当你用
kubectl下发指令时,API 响应会非常慢。 - 如果老服务器宕机,新服务器即便性能再强也会因为失去联络而变成“无头苍蝇”。
下一步操作建议
如果你同意这个方案,你现在的执行顺序应该是:
- 在新服务器 (10.0.0.4) 执行
curl安装 Server。 - 在老服务器 (10.0.0.1) 执行
curl加上K3S_URL=https://10.0.0.4:6443加入集群。
需要我把这两条最终的“一键安装脚本”连同 Token 占位符整理给你吗。
主题 12. 在解释一下这些参数
既然你准备在**新服务器(10.0.0.4)**上正式起航,理解这几个参数能帮你省去以后调试网络和证书的大麻烦。
简单来说,这些参数是在给 K3s 的“大脑”划定边界,告诉它:“只准走内网,别去公网乱跑。”
参数详细拆解
1. --node-ip=10.0.0.4
- 含义:指定当前节点的 IP 地址。
- 为什么要写:VPS 通常有两个 IP(公网和内网)。如果你不指定,K3s 可能会随机抓到一个 IP(通常是公网 IP)。
- 作用:强制 K3s 把所有的容器服务都绑定在内网 IP 上。这样你两台机器之间的流量就不会跑出外网,既安全又省流量费。
2. --advertise-address=10.0.0.4
- 含义:向集群其他成员“广播”自己的地址。
- 为什么要写:这是告诉老服务器(10.0.0.1):“嘿,如果你想找我(Server)汇报工作,请认准
10.0.0.4。” - 作用:确保老服务器(Agent)知道通过内网通道来连接,而不是绕道公网去访问。
3. --tls-san=10.0.0.4
- 含义:在 SSL/TLS 证书中添加“备用名称(Subject Alternative Name)”。
- 为什么要写:K3s 节点之间通信是强制加密的。如果你用
https://10.0.0.4:6443访问 Server,但证书里只写了公网 IP 或主机名,连接就会因为“证书不匹配”而失败。 - 作用:给证书开个“白名单”,允许通过
10.0.0.4这个内网地址进行加密连接。
整体架构逻辑
通过这一套参数,你构建了一个**“内网优先”**的闭环集群:
- 安全性:即使公网防火墙没关严,外部也无法通过公网 IP 轻易混入你的集群内部通讯。
- 稳定性:内网带宽通常比公网高且延迟低,对于你 RevieU 项目中 Go 后端与数据库之间的通讯非常有利。
- 兼容性:解决了由于 SSL 证书校验失败导致的“Node NotReady”经典报错。
接下来该做什么。
在新服务器运行完这条命令后,你需要做两件事:
- 获取令牌 (Token):
sudo cat /var/lib/rancher/k3s/server/node-token
- 在老服务器 (10.0.0.1) 执行加入命令:
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.4:6443 \
K3S_TOKEN=<刚刚获取的TOKEN> \
sh -s - agent --node-ip=10.0.0.1
等你两台都跑起来了,你可以运行 kubectl get nodes。如果看到两个节点都显示 Ready,就说明你的内网串联大功告成了。需要我帮你写一个简单的测试 Pod 来验证两台机器之间的内网网络是否真的通了吗。
主题 13. 你设置了nodeip是一个vpn ip,那我使用ingress的时候,还…
这是一个非常敏锐的问题。你担心的是:如果 K8s 内部通讯都锁死在内网 IP,那外网用户还能通过 Ingress 访问我的网站吗。
结论是:完全没问题,甚至更安全。
这里的关键在于理解 **K8s 内部通讯(Node IP)**和 **外部流量入口(Ingress/Service)**是两个不同的平面。
1. 为什么 Ingress 依然正常工作。
K3s 默认自带的 Ingress 控制器(Traefik)会自动监听机器上的 所有网卡(包括公网 IP 的 80/443 端口)。
- Node IP (10.0.0.x):只负责集群内部的“悄悄话”(比如 Server 告诉 Agent 该起哪个 Pod 了)。
- Ingress (Traefik):它像一个大门保安,虽然它住在内网环境里,但它依然会去监听这台 VPS 的公网 IP 端口。
2. 流量的具体路径
当一个洛杉矶的用户访问你 RevieU 的域名时,流量走势如下:
- 用户 -> 访问你的公网 IP (例如
1.2.3.4:80)。 - 操作系统 -> 收到流量,发现
80端口被 Traefik 占用了。 - Traefik (Ingress) -> 接过流量,根据域名(比如
api.revieu.com)查表。 - 内网转发 -> Traefik 通过你的内网 IP (10.0.0.4) 把流量转给对应的 Pod。
所以,设置 node-ip 只是给集群内部通讯加了个“防盗门”,并不会把对外的“窗户”(Ingress)关上。
3. 需要注意的一个小坑(重点)
虽然 Ingress 能通,但你需要确保你的 ufw 或 云服务商安全组 允许外网访问 80 和 443 端口:
# 在两台服务器上都要允许外网访问网页端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
4. 进阶:如何彻底利用你的双节点。
既然你有两台机器,你可以灵活决定让谁对外接客:
- 新服务器 (10.0.0.4):性能强,建议把 Ingress 流量主要引向这里。
- 老服务器 (10.0.0.1):如果它原本就跑着一个 Nginx 占用了 80 端口,你可以在老服务器上安装 Agent 时加上
--disable traefik。
这样,你的架构就变成了:新服务器负责处理外网所有的 K8s 流量,而老服务器只作为备用计算资源,不直接暴露 K8s 服务给外网。
验证建议
等你部署好第一个 Ingress 规则后,直接在浏览器访问你的 公网 IP。如果你能看到 Traefik 的 404 页面(因为还没配置具体的路径),就说明外部流量已经成功穿透 ufw 进到 K3s 内部了。
你的域名现在解析到哪台服务器的公网 IP 了。还是你打算用负载均衡器(Load Balancer)把流量分发给这两台。
排查场景 2. nc -zv 10.0.0.1 6443 29s root@cc 11:…
- 场景:nc -zv 10.0.0.1 6443 29s root@cc 11:…
- 处理要点:
哎呀,出师未捷。看到Job for k3s.service failed别担心,这在 K3s 部署中很常见,通常是由于残留配置或端口占用引起的。
既然你之前在 10.0.0.1 运行过 nc -zv 并且通了,说明那台机器上可能还跑着旧的 K3s 进程,或者是这台新机器(10.0.0.4)的环境有些冲突。
我们直接进入“排障三板斧”:
1. 查看具体的报错原因
这是最关键的一步,它会直接告诉你为什么起不来:
journalctl -xeu k3s.service
请留意日志里是否有以下关键词:
address already in use:说明 6443 端口被占了。failed to find memory cgroup v2:说明系统内核参数没开。invalid character in token:说明之前的配置文件干扰了现在的启动。
2. 彻底清理环境(最推荐的快速修复)
因为日志里显示 Skipping binary downloaded 和 symlink already exists,说明这台机器上之前装过 K3s 或者有残留。为了让你的 --node-ip 参数生效,我们先清空重来:
# 1. 运行卸载脚本(别担心,只卸载这台 10.0.0.4 的 k3s)
sudo /usr/local/bin/k3s-uninstall.sh
# 2. 检查是否有残留进程并杀掉(有时卸载脚本杀不干净)
sudo ps aux | grep k3s | awk '{print $2}' | xargs kill -9
# 3. 删掉残留的配置目录
sudo rm -rf /etc/rancher/k3s /var/lib/rancher/k3s
3. 再次尝试安装
清理干净后,再次执行你的安装命令:
curl -sfL https://get.k3s.io | sh -s - server \
--node-ip=10.0.0.4 \
--advertise-address=10.0.0.4 \
--tls-san=10.0.0.4
针对你的 Ubuntu 系统可能的特殊检查
如果你是 Ubuntu 24.04 或较新版本,有两个小坑可能会导致 systemd 启动失败:
- 防火墙冲突:即使你设置了
ufw,如果iptables版本太老也可能出问题。 - Cgroup 设置: 执行:
cat /proc/cgroups如果memory这一行最后面是0,说明内存限制没开,K3s 会崩。 解决方法:修改/etc/default/grub,在GRUB_CMDLINE_LINUX_DEFAULT里加入cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory,然后sudo update-grub并重启。
你可以把 journalctl -xeu k3s.service 输出的最后 20 行贴给我看看吗。我一眼就能看出是哪个组件在闹脾气。
排查场景 3. 好了,我现在在新服务器上已经安装好了,要检查一下这个是不是server节…
- 场景:好了,我现在在新服务器上已经安装好了,要检查一下这个是不是server节…
- 处理要点:
作为开发者,我们要用事实说话。你可以通过以下几个步骤进行验证:
1. 核心状态检查
运行最常用的命令来看看节点状态:
kubectl get nodes
看什么:
- NAME:应该是你的主机名。
- STATUS:必须是 Ready。
- ROLES:必须包含 control-plane,master。
2. 验证参数是否生效
我们需要确认 K3s 真的听话,绑定到了你的内网 IP:
kubectl get nodes -o wide
检查重点:
- INTERNAL-IP:必须显示为 10.0.0.4。如果这里显示的是你的公网 IP,说明之前的参数没生效,后续串联老服务器会出问题。
3. 查看组件运行情况
Server 节点会比 Agent 节点多跑很多东西(如 API Server, Scheduler 等)。你可以看看这些核心组件是否健康:
kubectl get pods -n kube-system
你会看到 traefik、coredns、local-path-provisioner 等 Pod 正在运行。
4. 关键:获取老服务器加入的“通行证”
既然确认了这台是 Server,现在就需要拿到那个 Token,好去老服务器上执行串联。
执行命令:
sudo cat /var/lib/rancher/k3s/server/node-token
请把这串很长的字符复制下来。
下一步:串联老服务器 (10.0.0.1)
现在请切换到你的老服务器,执行以下命令。注意我为你加上了 --node-ip 以确保它也走内网:
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.4:6443 \
K3S_TOKEN=<你刚刚复制的TOKEN> \
sh -s - agent --node-ip=10.0.0.1
一个关于 RevieU 项目的架构小技巧
等你执行完老服务器的加入命令,回到新服务器再跑一次 kubectl get nodes。当你看到两台机器都显示 Ready 时,你的分布式集群就搭好了。
你想让我帮你写一个简单的测试 YAML 吗。我们可以部署一个带页面的 Nginx,看看它会随机落在哪台服务器上,以及你是否能通过内网和外网分别访问它。
主题 14. kubectl get pods -A root@cc 11:16:58
漂亮。INTERNAL-IP 显示为 10.0.0.4,而且状态是 Ready。这意味着你的 Server 节点已经完全按照预期配置好了,所有的内网指令和证书都已经绑定到了私有网络。
顺便提一句,你的主机名 cc.weijun.online 看起来很专业,这也确认了你的域名指向没问题。
现在的状态解读:
- Traefik (0/1 ContainerCreating):这是正常的。因为是第一次启动,它正在下载镜像。几分钟后它就会变成
Running。 - Helm-install:显示
Completed是正确的,这些只是负责安装插件的临时任务。 - Internal IP:由于你指定了
10.0.0.4,这个集群的内部通讯现在就像在“自家屋里说话”一样安全。
关键下一步:串联老服务器 (10.0.0.1)
现在,请登录到你的老服务器。我们将它作为 Agent 节点加入这个“大脑”。
1. 先在新服务器上拿“入场券” (Token)
在新服务器(10.0.0.4)上运行:
sudo cat /var/lib/rancher/k3s/server/node-token
复制这串 Token。
2. 在老服务器(10.0.0.1)上执行加入
直接粘贴下面的命令(记得替换 Token):
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.4:6443 \
K3S_TOKEN=<你刚刚复制的TOKEN> \
sh -s - agent --node-ip=10.0.0.1
串联成功后会发生什么。
当你执行完老服务器的命令后,回到新服务器执行 kubectl get nodes,你会看到:
| NAME | STATUS | ROLES | INTERNAL-IP |
|---|---|---|---|
| cc.weijun.online | Ready | control-plane | 10.0.0.4 |
| (老服务器名) | Ready | <none> | 10.0.0.1 |
给你的 RevieU 项目留个“后手”
由于你之前提到不想删掉老服务器上的东西,等这个节点加入后,我们可以给它打上一个标签,防止 K8s 乱派活:
# 在新服务器上给老节点打标签 (假设名字叫 old-server)
kubectl label node <老服务器名> hardware-type=low-spec
这样,以后我们在部署 RevieU 的 PostgreSQL 数据库时,就可以在 YAML 里写明:“绝对不准跑在 hardware-type=low-spec 的节点上”,从而彻底保护你的老服务器不被撑爆。
老服务器那边执行顺利吗。如果也报了 systemctl 相关的错,记得先跑一遍卸载脚本清理一下旧残留。
排查场景 4. 但是老服务器上有一个k3s,直接安装行吗
- 场景:但是老服务器上有一个k3s,直接安装行吗
- 处理要点:
绝对不行。 如果老服务器上已经跑着一个旧的 k3s(哪怕它现在是停止状态),直接运行安装命令极大概率会失败,或者导致两个 k3s 的配置混在一起,最后让你抓狂。
由于老服务器现在的身份是 Server(它自己也是个“大脑”),如果不卸载,它会拒绝服从新服务器(10.0.0.4)的指挥。
1. 安全卸载老服务器的 k3s
别担心,运行卸载脚本只会删除 k3s 相关的程序和配置,不会动你原本在服务器上跑的其他非 K8s 业务(比如你的脚本、Docker 容器等)。
在老服务器 (10.0.0.1) 上执行:
# 卸载旧的 server
sudo /usr/local/bin/k3s-uninstall.sh
2. 清理残留(保险起见)
有时候旧的证书或 Token 会留在文件夹里,导致新集群加不进去,建议手动清一下:
sudo rm -rf /etc/rancher/k3s /var/lib/rancher/k3s
3. 正式加入新集群 (作为 Agent)
清理干净后,你可以放心地把这台老服务器作为“员工”挂载到新服务器下了。
在老服务器 (10.0.0.1) 执行:
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.4:6443 \
K3S_TOKEN=<你从新服务器拿到的TOKEN> \
sh -s - agent --node-ip=10.0.0.1
4. 验证“串联”结果
回到你的新服务器 (10.0.0.4),用你熟悉的 kubectl 看看:
kubectl get nodes -o wide
成功的标志: 你会看到两条记录,一条是 10.0.0.4 (Control-plane),另一条是 10.0.0.1 (none/worker)。
针对你老服务器的“防爆”建议
既然这台机器性能不行,串联成功后,记得在新服务器上给它打个“补丁”,防止 K8s 往它身上压太重的活:
# 给老节点打个标签,标记为“弱机器”
kubectl label node <老服务器的主机名> capacity=low
这样在你以后部署 RevieU 的 Go 后端时,我们可以通过配置,让核心服务优先跑在新服务器上,老服务器只负责跑一些轻量级的日志收集或静态页面。
老服务器卸载过程中有遇到报错吗。如果没有,现在就可以执行加入命令了。
主题 15. kubectl get nodes root@cc 11:18:16
太棒了。看到那行 racknerd-7829f7f Ready 出现,说明你的跨服务器 K3s 集群正式组建成功。
这种“一主一从”的分布式架构现在已经跑在你的内网(10.0.0.4 和 10.0.0.1)上了。
1. 现状分析
- cc.weijun.online (10.0.0.4): 你的 Server。它负责掌管全局,同时也是主力计算节点。
- racknerd-7829f7f (10.0.0.1): 你的 Agent。它现在是你的“扩展包”,虽然性能弱,但它已经整装待命,随时可以分担压力。
2. 核心操作:给老服务器打“补丁”
既然你反复强调老服务器性能不行,我们现在就要在新服务器(10.0.0.4)上对它进行限制。
第一步:打标签 (Label)
为了方便在部署 RevieU 时进行筛选,先给它贴个标签:
kubectl label node racknerd-7829f7f performance=low capacity=small
第二步:打污点 (Taint) —— 最关键的一步
如果你希望 K8s 默认不要往老服务器上派活(除非你手动指定),执行这个:
kubectl taint nodes racknerd-7829f7f performance=low:NoSchedule
这样做的效果:普通的 Pod(比如你随手运行的测试服务)会自动避开老服务器,只跑在你的新服务器上,从而保护老服务器的 CPU/内存不被占满。
3. 如何在 RevieU 项目中使用这个架构。
由于你习惯用 Neovim 编写配置,当你部署 RevieU 的 Go 后端时,你可以在 YAML 中利用这种“串联”关系。
比如,你想让 PostgreSQL 数据库只跑在新服务器上,可以这样写:
spec:
template:
spec:
nodeSelector:
node-role.kubernetes.io/control-plane: "true" # 强制跑在 Server 节点(新服务器)
4. 验证内网连通性
你可以跑一个简单的命令,看看两个节点之间能不能“对话”:
kubectl run net-test --image=busybox -it --rm --restart=Never -- ping -c 4 10.0.0.1
如果能 ping 通,说明你的 ufw 转发策略配置得非常完美。
现在集群搭好了,你想让我帮你写一个完整的 Docker-Compose 转 K8s Deployment 的 YAML 模板吗。我们可以针对你的 RevieU 项目,把后端、前端和数据库的调度逻辑一次性写好。
主题 16. 怎么让两个node负载均衡,不能老服务器啥也不干啊
这正是分布式集群最有趣的地方:物尽其用。
既然你不希望老服务器(Racknerd)“吃闲饭”,但又怕它被压死,我们可以采取**“差异化负载均衡”策略。不要简单地平分压力,而是让它们各司其职**。
1. 方案一:让老服务器跑“轻量级辅助” (推荐)
你可以把一些对 CPU/内存消耗极低,但又必须存在的服务“赶”到老服务器上。
- 适合放在老服务器的服务:
- Nginx 反向代理/静态页面(只负责转发流量,不涉及业务逻辑)。
- 监控 Agent (Prometheus Exporter):收集性能数据。
- 定时任务 (CronJobs):比如每天凌晨同步一次数据库备份。
- Redis 缓存(如果你只做简单的 Key-Value 存储,内存占用很低)。
操作方法: 在你的 Deployment YAML 中使用 nodeSelector 指定老服务器:
spec:
nodeSelector:
performance: "low" # 匹配我们之前打的标签
2. 方案二:利用水平伸缩 (HPA) 实现动态平衡
你可以让 RevieU 的 Go 后端跑两个副本。
- 副本 1:跑在新服务器(主力)。
- 副本 2:跑在老服务器(备用)。
通过 K8s 的 Service,流量会自动在两个节点之间进行负载均衡。
3. 方案三:精准限制 (Resource Quotas)
如果你想让老服务器也跑一些业务,但又要给它原本的非 K8s 服务留出“求生空间”,你必须在 K8s 里设置资源限额。
这是保护你老服务器不崩溃的唯一方法:
spec:
containers:
- name: revieu-backend
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi" # 限制它最多只能吃这么多内存
cpu: "200m"
4. 移除“污点”,允许调度
如果你之前按照我的建议打了 NoSchedule 污点,老服务器是永远不会有活干的。想要它干活,需要先撤销那个命令:
# 移除我们之前打的污点
kubectl taint nodes racknerd-7829f7f performance=low:NoSchedule-
(注意末尾那个减号 -,代表删除该污点)
针对 RevieU 的最佳实践:
由于你正在开发 RevieU,我建议你这样分配:
| 服务名称 | 建议节点 | 理由 |
|---|---|---|
| PostgreSQL | 新服务器 (10.0.0.4) | 数据库是重灾区,绝对不能放在性能差的机器上。 |
| Go 后端 (API) | 两台都跑 | 负载均衡,即使一台挂了,API 依然可用。 |
| React 前端静态文件 | 老服务器 (10.0.0.1) | 纯静态资源分发,对老服务器压力极小。 |
接下来我们可以做什么。
我们可以给老服务器设置一个 “软性偏好” (Preferred Scheduling)。 你想让我帮你写一个完整的 Deployment 脚本吗。 它可以实现:
- 优先把 Pod 放在新服务器。
- 当新服务器压力大时,自动分流一部分给老服务器。
- 同时严格限制老服务器的资源占用,不影响你原本在那上面的旧程序。
排查场景 5. 这个怎么做
- 场景:这个怎么做
- 处理要点:
要实现这个方案,我们需要利用 Kubernetes 的 Pod 反亲和性 (Pod Anti-Affinity)。
它的逻辑是:告诉 K8s “不要把同一个服务的多个副本放在同一个地方”。这样当你设置副本数为 2 时,它就会被迫在一台机器放一个,从而实现物理层面的负载均衡。
以下是针对你 RevieU 项目的完整配置思路:
1. 准备工作:确保老服务器可以被调度
如果你之前给老服务器打过“污点”(Taint),请先确保它被移除,否则副本 2 永远无法在老服务器上启动:
# 移除之前的 NoSchedule 污点
kubectl taint nodes racknerd-7829f7f performance=low:NoSchedule-
2. 编写 Deployment YAML
在你的 Go 后端配置中加入 affinity 项。你可以用你熟悉的 Neovim 创建一个 revieu-backend.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: revieu-backend
spec:
replicas: 2 # 开启两个副本
selector:
matchLabels:
app: revieu-api
template:
metadata:
labels:
app: revieu-api
spec:
# --- 核心配置:Pod 反亲和性 ---
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- revieu-api
topologyKey: "kubernetes.io/hostname"
# ----------------------------
containers:
- name: gin-server
image: your-repo/revieu-backend:latest
ports:
- containerPort: 8080
resources:
# 记得给老服务器留活路,限制资源占用
limits:
cpu: "200m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
3. 创建 Service 实现流量分发
有了两个 Pod 之后,你需要一个“统一入口”来自动把请求分给这两台机器。K8s 的 Service 会自动处理这部分负载均衡(通常通过 iptables 或 IPVS)。
apiVersion: v1
kind: Service
metadata:
name: revieu-service
spec:
selector:
app: revieu-api
ports:
- protocol: TCP
port: 80 # 集群内部访问端口
targetPort: 8080 # Go 程序监听端口
type: ClusterIP # 或者 NodePort/LoadBalancer
4. 为什么这样做能实现“动态平衡”。
- 流量分摊:当用户请求到达时,Service 会以轮询(Round Robin)的方式,一次把请求发给新服务器上的 Pod,下一次发给老服务器上的 Pod。
- 高可用:如果你的老服务器因为性能太差卡死了,K8s 会检测到该 Pod 不健康,自动把所有流量引向新服务器,直到老服务器恢复。
- 资源保护:通过设置
resources.limits,即使老服务器上的 Pod 遭遇高并发,它也只能吃掉你分配给它的那一点点内存,不会把你老服务器上原本运行的旧服务搞崩溃。
进阶建议:利用污点容忍 (Toleration)
如果你还是担心其他不相关的测试 Pod 跑去老服务器,你可以:
- 给老服务器重新打上污点。
- 只在 RevieU 的 YAML 里加入
tolerations。
这样,只有你的 RevieU 后端能进去老服务器,其他任何 Pod 都会被老服务器拒之门外。
你想让我帮你把这个“白名单容忍度”也加进 YAML 模板里吗。这样能最大程度保护你老服务器的纯净。
排查场景 6. 我在老服务器上,怎么不行
- 场景:我在老服务器上,怎么不行
- 处理要点:
别担心,这是一个非常经典且容易误解的问题。
原因很简单: 你的老服务器现在是 Agent(从节点)。在 Kubernetes 架构中,Agent 节点本身并不运行 API Server,所以它不知道集群的状态。当你直接在老服务器输入 kubectl get nodes 时,它默认会去尝试连接自己本地的 8080 端口,结果当然是 connection refused。
只有 Server(主节点) 也就是你的 10.0.0.4 才能直接响应 kubectl 命令。
解决方案
你有三个选择来解决这个问题,按推荐程度排序:
方案一:在“大脑”(新服务器)上执行(最标准)
所有的集群管理操作(部署 RevieU、查看状态)都应该在新服务器 10.0.0.4 上完成。那里才是集群的控制中心。
方案二:把“指挥权”共享给老服务器(如果你想在老服务器操作)
如果你非要在老服务器上用 kubectl,你需要把新服务器的“授权文件”考过来:
- 在新服务器 (10.0.0.4) 上运行:
sudo cat /etc/rancher/k3s/k3s.yaml
- 在老服务器 (10.0.0.1) 上运行:
mkdir -p ~/.kube
# 将刚才 cat 出来的全部内容粘贴到这个文件里
nvim ~/.kube/config
- 修改老服务器上的 config 文件: 打开
~/.kube/config,找到server: https://127.0.0.1:6443这一行。 将127.0.0.1改成你的新服务器内网 IP:10.0.0.4。
方案三:在你的笔记本电脑上操作(最优雅)
既然你习惯用 Arch + Neovim,最好的体验是在你自己的本地电脑(Mac/PC)上管理集群。
- 把新服务器的
/etc/rancher/k3s/k3s.yaml内容拷贝到你本地电脑的~/.kube/config。 - 同样把 IP 从
127.0.0.1改为新服务器的 公网 IP。 - 本地安装一个
k9s。
这样你坐在家里,就能像操作本地 Docker 一样管理这两台 VPS 上的 RevieU 项目。
为什么会出现 localhost:8080。
这是 kubectl 的默认保底行为。如果它找不到有效的配置文件(kubeconfig),它就会盲目地去撞 localhost:8080。
由于你的老服务器已经成功加入了集群(刚才在新服务器上已经看到它 Ready 了),你完全不需要在老服务器上修好这个报错也能运行服务。
你要不要试着在新服务器上跑一下那个副本数为 2 的 YAML。我们可以看看 Pod 是如何自动跨服务器部署的。
主题 17. 现在这个role还是none啊
这是完全正常的现象。在 Kubernetes 中,Agent 节点(Worker 节点)默认确实不显示特定的 Role。
只有 Server 节点会因为运行控制平面组件而被标记为 control-plane 或 master。对于老服务器这种纯粹干活的节点,显示 <none> 恰恰说明它已经成功卸下了“大脑”的重担,成为了一个纯粹的 Worker Node。
如果你觉得 <none> 看着不舒服,或者为了之后 RevieU 项目部署时方便识别,我们可以手动给它贴上 worker 的标签。
1. 手动添加 Worker 角色 (可选)
虽然这只是个“展示性”的标签,但能让 kubectl get nodes 的输出更整齐。在 新服务器 (10.0.0.4) 上运行:
kubectl label node racknerd-7829f7f kubernetes.io/role=worker
执行完后再看 kubectl get nodes,它的 ROLE 就会变成 worker 了。
2. 为什么显示 <none> 并不影响负载均衡。
Kubernetes 的调度器(Scheduler)并不是根据 ROLE 这一列来派活的,它是根据节点的状态(Ready)和资源余量来决定的。
即便显示为 <none>,只要它是 Ready 状态,当你部署 RevieU 的后端副本时,K8s 就会自动发现:“嘿,这里有一个空闲的计算资源”,然后把 Pod 调度过去。
3. 接下来建议的操作:实现真正的负载均衡
既然你现在的目标是让两个 Node 跑起来,不要让老服务器闲着,你可以尝试部署一个简单的测试:
- 创建一个 2 副本的 Nginx 部署:
kubectl create deployment net-test --image=nginx --replicas=2
- 观察 Pod 的分布:
kubectl get pods -o wide
你应该能看到一个 Pod 运行在 cc.weijun.online,另一个 Pod 运行在 racknerd-7829f7f。
给你的 RevieU 项目架构师的小建议
由于你之后要用 Go (Gin) 做后端,我建议你现在的下一步是:配置本地 Neovim 环境来管理这个集群。
你不想一直开着两个 SSH 窗口切换吧。你可以把新服务器上的 /etc/rancher/k3s/k3s.yaml 考回你本地 Arch Linux 的 ~/.kube/config。
需要我教你如何配置本地的 kubectl 和 k9s,让你在本地就能一键给这两台 VPS 下达指令吗。
主题 18. 那我的dns记录应该是把域名解析到两个服务器,还是只解析server那台
这取决于你打算如何使用这两台服务器来承载你的 RevieU 项目。
最简单的答案是:初期建议先解析到新服务器(Server)那台,后期如果你追求高可用,再考虑两台都解析。
以下是三种常见的配置方案,你可以根据目前的开发进度选择:
方案 1:只解析到新服务器 (10.0.0.4) —— 最推荐,最省心
既然新服务器性能强,且 K3s 默认的 Ingress (Traefik) 就跑在上面,把域名直接指向它最稳。
- DNS 设置:
A 记录 -> 10.0.0.4 的公网 IP。 - 流量走向:用户访问新服务器 -> Traefik 接收 -> Traefik 通过内网发现老服务器上也跑着一个 Go 后端副本 -> 自动把一部分流量转给老服务器。
- 优点:配置简单,证书(SSL)管理统一。即便老服务器重启,由于域名没指向它,用户访问不会报错。
方案 2:两台都解析(轮询 DNS)—— 高可用方案
如果你希望实现“双入口”,即万一新服务器挂了,用户还能从老服务器进去。
- DNS 设置:为同一个域名添加两条 A 记录,分别指向两台服务器的公网 IP。
- 流量走向:用户的浏览器会随机选择其中一个 IP 进行访问。
- 前提条件:
- 你必须确保老服务器上也跑着 Ingress 控制器(K3s 默认会在所有节点跑 Traefik 的副本)。
- 两台服务器的 80/443 端口都必须对公网开放。
- 缺点:如果老服务器性能太差导致 Traefik 响应慢,用户会感觉到网页偶尔加载很慢。而且如果某一台宕机,DNS 还没来得及更新,会有 50% 的用户访问失败。
方案 3:使用 Cloudflare (CDN) —— 架构师的选择
作为 USC 的工程学生,我建议你考虑这个更专业的做法,正好你也搜过 Cloudflare。
- 在 Cloudflare 后台设置两条 A 记录(双入口)。
- 开启 Cloudflare Proxy (小黄云)。
- 优点:
- 自动避障:Cloudflare 会检测你的服务器状态。如果老服务器挂了,它会自动只把流量发给新服务器。
- 安全性:隐藏你的真实公网 IP,防止 VPS 被攻击。
- RevieU 优化:Cloudflare 的边缘缓存能帮你分担老服务器的压力。
我的建议
对于你现在的 RevieU 开发阶段:
第一步:先把域名解析到 新服务器 (10.0.0.4) 的公网 IP。 第二步:在 K8s 里部署两个副本(按照我们之前说的反亲和性)。 第三步:验证一下,当你通过域名访问 API 时,查看 K8s 日志,你会发现新服务器在悄悄地把请求通过内网转发给老服务器。
你现在的域名是在 Cloudflare 托管吗。如果是的话,我们可以直接配置它的 Load Balancer 功能。
实施与验证清单
- 执行前先确认环境、版本和依赖与文中前置条件一致。
- 涉及系统级变更时,先备份关键配置或创建快照。
- 完成操作后按验证步骤确认结果,再执行清理或覆盖动作。