K3s 双节点集群部署指南

背景与目标

本文围绕“我准备在两台vps上部署k3s,然后作为两个界点串联起来”展开,整理可直接复用的步骤、原理说明与排查经验。

核心内容

主题 1. 我准备在两台vps上部署k3s,然后作为两个界点串联起来

在两台 VPS 上部署 k3s 并将它们组成一个集群(一个 Server 节点 + 一个 Agent 节点)是一个非常经典且高效的选择。

既然你熟悉 Arch LinuxNeovim 并且正在做 RevieU 项目的架构,这种“轻量级 Kubernetes”方案非常适合你的开发和测试需求。

以下是实现这一目标的快速指南:

1. 准备工作

在开始之前,确保两台 VPS 满足以下条件:

  • 网络连通性:两台机器之间需要互通。如果是跨云厂商,建议使用 TailscaleWireGuard 建立虚拟内网,或者确保防火墙开放了以下端口:
    • 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 后端),你可以给它打上“污点”。

  1. 在旧节点(Server)上执行:
    # 这里的 <node-name> 通常是你的主机名
    kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule

这样,除非你特别指定,否则所有的业务 Pod 都会自动调度到那台性能更好的新 Agent 节点上。

方案二:性能强的作为 Server,旧的作为 Agent (推荐)

对于双节点集群,通常建议将网络最稳定、性能较均衡的机器作为 Server。如果你的新 VPS 性能全方位碾压旧的,重新反向部署其实更健康。

步骤建议:

  1. 备份旧节点数据(如果有的话)。
  2. 在新 VPS 上部署 Server:
    curl -sfL https://get.k3s.io | sh -
  1. 在旧 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 LinuxNeovim,我建议你在新节点部署好后,在本地开启一个 k9s(一个非常强大的终端 UI 管理工具)。它能让你像用 Neovim 快捷键一样管理这两台 VPS 上的 Pod,非常符合你的操作习惯。

需要我帮你写一份针对这两台机器性能差异的 nodeSelectorAffinity (亲和性) 配置示例吗。这样你可以精准控制哪些服务跑在强力节点上。

主题 3. server和agent有什么区别

在 Kubernetes(以及 k3s)的世界里,这就像是一个公司的管理层员工层的关系。

虽然 k3s 为了轻量化把很多功能打包在一起,但 Server 和 Agent 的职责分工非常明确:

1. Server 节点 (Control Plane)

身份: 集群的“大脑”和“指挥中心”。

  • 核心任务: 决策、存储、调度。
  • 关键组件:
    • API Server: 所有的命令(比如你输入的 kubectl)都要经过它。
    • SQLite/Etcd: 存储整个集群的状态和配置(k3s 默认在单机使用 SQLite,更省资源)。
    • Scheduler: 决定你的 Pod(比如 RevieU 的 Go 后端)应该运行在哪台机器上。
  • 资源消耗: 主要是内存和 IO。即使不跑任何业务,它也会占用一定的资源来维持集群状态。

2. Agent 节点 (Worker Node)

身份: 集群的“体力劳动者”。

  • 核心任务: 运行容器、上报状态。
  • 关键组件:
    • Kubelet: 听从 Server 的指令,负责管理这台机器上的容器生命周期。
    • Containerd: 实际运行容器的引擎。
    • Kube-proxy: 处理网络规则,让外界能访问到你的服务。
  • 资源消耗: 取决于你运行了多少业务。它本身非常轻量。

核心对比表

特性Server 节点Agent 节点
角色控制平面 (Master)工作节点 (Worker)
能否运行 Pod可以 (默认行为)可以 (唯一任务)
存储数据存储集群配置和状态不存储集群配置
如果宕机集群无法接受新指令/部署仅丢失该节点上的任务,集群正常运行
安装命令参数k3s serverk3s agent

结合你的情况:为什么你要在意这个区别。

既然你提到第一台服务器性能不行,理解它们的区别对你至关重要:

  1. 控制权分离:你可以让性能差的旧机器只当 Server。它不跑具体的业务(Pod),只负责管理。
  2. 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,通常不在同一个内网。为了让它们“串联”起来,你有两个选择:

  1. 公网直连(简单但不安全): 在安装命令中添加 --node-external-ip 参数,并确保防火墙开放了 64438472 (UDP) 端口。
  2. 虚拟内网(推荐): 使用 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 里的应用也想用这些端口,就会发生冲突。

解决方案:

  1. 让 K8s 避让:在部署 RevieU 项目时,不要使用 HostPort,而是使用 NodePort(通常是 30000 以上的端口)或者 Ingress
  2. 禁用 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 往它身上派重活:

  1. 限制资源使用: 在 K8s 的 YAML 配置文件里,给 Pod 设置 resources.requestslimits
  2. 使用标签控制: 给老节点打个标签 node-role.kubernetes.io/legacy=true
  3. 调度策略: 在你的 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 的服务都跑在新服务器上,建议按以下步骤操作:

  1. 给老节点打上“尽量别来”的污点:
    kubectl taint nodes <老节点名称> performance=low:NoSchedule
  1. “温和”地重启你的服务: 如果你的 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 或云服务商安全组)中是通畅的:

端口协议角色用途
10250TCPKubelet核心: 允许 Server 查看节点日志、执行命令 (kubectl exec)。
8472UDPFlannel 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. 总结:你的防火墙检查清单

如果你在老服务器上配置防火墙,请按以下规则设置:

  1. 入站 (Inbound):
    • 允许来自 新服务器内网 IPTCP 10250
    • 允许来自 新服务器内网 IPUDP 8472
    • (可选) 允许你原本服务的端口(如 22, 80, 443)。
  2. 出站 (Outbound):
    • 必须允许访问 新服务器内网 IPTCP 6443 (API Server)。

避坑指南

由于你习惯使用 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 必须的 102508472 等)就全通了。

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 底层实际上是在操作 iptablesnftables。K3s 会为了实现容器网络(Flannel/Cilium)自己往 iptables 里插入很多复杂的转发规则。

这就可能导致一个经典问题: ufw 的默认策略(比如 DEFAULT_INPUT_POLICY="DROP")可能会意外拦截 K3s 内部 Pod 之间的流量。

建议: 如果安装完 K3s 后发现 kubectl get nodes 显示节点是 Ready,但 Pod 之间无法通信(比如 RevieU 的后端连不上数据库),你需要修改 ufw 的转发政策:

  1. 编辑配置文件:sudo nvim /etc/default/ufw (或者用你常用的 Neovim)
  2. DEFAULT_FORWARD_POLICY"DROP" 改为 "ACCEPT":
    DEFAULT_FORWARD_POLICY="ACCEPT"
  1. 重启防火墙:sudo ufw reload

总结操作清单

  1. 老服务器:执行 sudo ufw allow from 10.0.0.1
  2. 新服务器:执行 sudo ufw allow from 10.0.0.2(假设老服务器是 .2)。
  3. 验证:在新服务器上尝试 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 或服务,可能会占用 80443 端口。

  • 如果冲突了:新服务器部署好的 Traefik(K3s 自带的入口控制器)在老服务器上可能会启动失败。
  • 解决办法:你可以利用 Arch/Neovim 的便携性,直接编辑控制台。在部署你的 RevieU 前端时,通过 ServicenodeSelector 将流量入口(Ingress)强制固定在性能更强、且端口干净的 10.0.0.4 上。

既然你现在要把这两台串起来,需要我帮你写一段 nodeAffinity(节点亲和性)的 YAML 片段吗。这样你可以确保 RevieU 的后端只会在 10.0.0.4 上启动。

排查场景 1. 这是什么意思,我是ubuntu

  • 场景:这是什么意思,我是ubuntu
  • 处理要点:
    虽然你使用的是 Ubuntu,但这一步同样非常重要

简单来说,这是在告诉防火墙(ufw):“允许流量在不同的网卡之间穿梭。

为什么要这么做。

在 Kubernetes (k3s) 中,流量的流动方式和普通服务器不一样:

  1. 外部流量进入服务器。
  2. ufw 拦截流量。
  3. K3s 的虚拟网络(Flannel) 需要把这个流量分发给运行在内部的 Pod(容器)。

默认情况下,Ubuntu 的 ufw 为了安全,会将这种“转发(Forward)”行为设为 DROP(丢弃)。如果不改为 ACCEPT,就会出现以下情况:

  • 你的 RevieU 后端 Pod 运行正常,但无法访问外网去下载插件。
  • 外部用户访问你的公网 IP,流量到了服务器后,被 ufw 拦住,进不去 Pod。

具体操作步骤

你可以手动检查并修改,不需要死记硬背 sed 命令:

  1. 打开配置文件
    sudo nano /etc/default/ufw
  1. 找到这一行(通常在第 19 行左右):
    DEFAULT_FORWARD_POLICY="DROP"
  1. 修改为
    DEFAULT_FORWARD_POLICY="ACCEPT"
  1. 保存并重启 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:

  1. 它会为了管理新服务器而消耗掉仅剩的资源,可能导致你原本在跑的服务崩溃。
  2. 当你用 kubectl 下发指令时,API 响应会非常慢。
  3. 如果老服务器宕机,新服务器即便性能再强也会因为失去联络而变成“无头苍蝇”。

下一步操作建议

如果你同意这个方案,你现在的执行顺序应该是:

  1. 在新服务器 (10.0.0.4) 执行 curl 安装 Server。
  2. 在老服务器 (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 这个内网地址进行加密连接。

整体架构逻辑

通过这一套参数,你构建了一个**“内网优先”**的闭环集群:

  1. 安全性:即使公网防火墙没关严,外部也无法通过公网 IP 轻易混入你的集群内部通讯。
  2. 稳定性:内网带宽通常比公网高且延迟低,对于你 RevieU 项目中 Go 后端与数据库之间的通讯非常有利。
  3. 兼容性:解决了由于 SSL 证书校验失败导致的“Node NotReady”经典报错。

接下来该做什么。

在新服务器运行完这条命令后,你需要做两件事:

  1. 获取令牌 (Token)
    sudo cat /var/lib/rancher/k3s/server/node-token
  1. 在老服务器 (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 的域名时,流量走势如下:

  1. 用户 -> 访问你的公网 IP (例如 1.2.3.4:80)。
  2. 操作系统 -> 收到流量,发现 80 端口被 Traefik 占用了。
  3. Traefik (Ingress) -> 接过流量,根据域名(比如 api.revieu.com)查表。
  4. 内网转发 -> Traefik 通过你的内网 IP (10.0.0.4) 把流量转给对应的 Pod。

所以,设置 node-ip 只是给集群内部通讯加了个“防盗门”,并不会把对外的“窗户”(Ingress)关上。

3. 需要注意的一个小坑(重点)

虽然 Ingress 能通,但你需要确保你的 ufw云服务商安全组 允许外网访问 80443 端口:

# 在两台服务器上都要允许外网访问网页端口
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 downloadedsymlink 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 启动失败:

  1. 防火墙冲突:即使你设置了 ufw,如果 iptables 版本太老也可能出问题。
  2. 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

你会看到 traefikcorednslocal-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,你会看到:

NAMESTATUSROLESINTERNAL-IP
cc.weijun.onlineReadycontrol-plane10.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 脚本吗。 它可以实现:

  1. 优先把 Pod 放在新服务器。
  2. 当新服务器压力大时,自动分流一部分给老服务器。
  3. 同时严格限制老服务器的资源占用,不影响你原本在那上面的旧程序。

排查场景 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 会自动处理这部分负载均衡(通常通过 iptablesIPVS)。

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 跑去老服务器,你可以:

  1. 给老服务器重新打上污点。
  2. 只在 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,你需要把新服务器的“授权文件”考过来:

  1. 在新服务器 (10.0.0.4) 上运行:
    sudo cat /etc/rancher/k3s/k3s.yaml
  1. 在老服务器 (10.0.0.1) 上运行:
    mkdir -p ~/.kube
    # 将刚才 cat 出来的全部内容粘贴到这个文件里
    nvim ~/.kube/config 
  1. 修改老服务器上的 config 文件: 打开 ~/.kube/config,找到 server: https://127.0.0.1:6443 这一行。 将 127.0.0.1 改成你的新服务器内网 IP:10.0.0.4

方案三:在你的笔记本电脑上操作(最优雅)

既然你习惯用 Arch + Neovim,最好的体验是在你自己的本地电脑(Mac/PC)上管理集群。

  1. 把新服务器的 /etc/rancher/k3s/k3s.yaml 内容拷贝到你本地电脑的 ~/.kube/config
  2. 同样把 IP 从 127.0.0.1 改为新服务器的 公网 IP
  3. 本地安装一个 k9s

这样你坐在家里,就能像操作本地 Docker 一样管理这两台 VPS 上的 RevieU 项目。

为什么会出现 localhost:8080。

这是 kubectl 的默认保底行为。如果它找不到有效的配置文件(kubeconfig),它就会盲目地去撞 localhost:8080

由于你的老服务器已经成功加入了集群(刚才在新服务器上已经看到它 Ready 了),你完全不需要在老服务器上修好这个报错也能运行服务。

你要不要试着在新服务器上跑一下那个副本数为 2 的 YAML。我们可以看看 Pod 是如何自动跨服务器部署的。

主题 17. 现在这个role还是none啊

这是完全正常的现象。在 Kubernetes 中,Agent 节点(Worker 节点)默认确实不显示特定的 Role。

只有 Server 节点会因为运行控制平面组件而被标记为 control-planemaster。对于老服务器这种纯粹干活的节点,显示 <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 跑起来,不要让老服务器闲着,你可以尝试部署一个简单的测试:

  1. 创建一个 2 副本的 Nginx 部署
    kubectl create deployment net-test --image=nginx --replicas=2
  1. 观察 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

需要我教你如何配置本地的 kubectlk9s,让你在本地就能一键给这两台 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 进行访问。
  • 前提条件
    1. 你必须确保老服务器上也跑着 Ingress 控制器(K3s 默认会在所有节点跑 Traefik 的副本)。
    2. 两台服务器的 80/443 端口都必须对公网开放。
  • 缺点:如果老服务器性能太差导致 Traefik 响应慢,用户会感觉到网页偶尔加载很慢。而且如果某一台宕机,DNS 还没来得及更新,会有 50% 的用户访问失败。

方案 3:使用 Cloudflare (CDN) —— 架构师的选择

作为 USC 的工程学生,我建议你考虑这个更专业的做法,正好你也搜过 Cloudflare。

  1. 在 Cloudflare 后台设置两条 A 记录(双入口)。
  2. 开启 Cloudflare Proxy (小黄云)
  3. 优点
    • 自动避障:Cloudflare 会检测你的服务器状态。如果老服务器挂了,它会自动只把流量发给新服务器。
    • 安全性:隐藏你的真实公网 IP,防止 VPS 被攻击。
    • RevieU 优化:Cloudflare 的边缘缓存能帮你分担老服务器的压力。

我的建议

对于你现在的 RevieU 开发阶段:

第一步:先把域名解析到 新服务器 (10.0.0.4) 的公网 IP第二步:在 K8s 里部署两个副本(按照我们之前说的反亲和性)。 第三步:验证一下,当你通过域名访问 API 时,查看 K8s 日志,你会发现新服务器在悄悄地把请求通过内网转发给老服务器。

你现在的域名是在 Cloudflare 托管吗。如果是的话,我们可以直接配置它的 Load Balancer 功能。

实施与验证清单

  1. 执行前先确认环境、版本和依赖与文中前置条件一致。
  2. 涉及系统级变更时,先备份关键配置或创建快照。
  3. 完成操作后按验证步骤确认结果,再执行清理或覆盖动作。