两台便宜 VPS 拼出高可用网站:Keepalived + 浮动IP 实战,单机宕机不再慌
2026-08-14 · DevCraft Studio
便宜 VPS 经常单机故障宕机?本文手把手教你用两台低价 VPS + Keepalived/VRRP + 浮动IP 拼出高可用架构,覆盖 MySQL 主从、Syncthing 文件同步与脑裂防护,附可直接复制的配置。
一、为什么便宜 VPS 更需要高可用
咱买便宜 VPS,图的就是一个性价比:年付十几二十刀,1 核 1G 跑个小博客、API、或者给朋友搭的网盘,香得很。但便宜是有代价的——便宜机器大概率是超售严重的母鸡上分出来的,单机故障率比大厂独服高得多。我见过最离谱的,一台 RackNerd 年付机一个月里重启了三次,每次都是母鸡维护。
问题来了:你的站点、接口、机器人,全压在这一台机器上。它一挂,全部访问 502,用户骂街,你也只能半夜爬起来重启。这其实就是"把鸡蛋放在一个篮子里"。高可用(High Availability,简称 HA)的核心思路就一句话:用冗余换可用性——多准备一台机器顶着,一台挂了另一台无缝接管,用户几乎无感知。
别被"高可用"三个字吓到,它不等于要上 Kubernetes、不等于要买企业级负载均衡。对绝大多数个人和小团队项目来说,两台便宜 VPS + 一个浮动 IP + Keepalived 就能把可用性从"看运气"拉到"月宕机几分钟以内"。下面咱们一步步来。
二、先搞懂:可用性到底怎么算
常听人说 99.9%、99.99% 这些数字,到底意味着啥?其实很简单,就是"一个月允许宕机多久":
- 99.9%(三个九):一个月最多宕机约 43 分钟。
- 99.95%:约 22 分钟。
- 99.99%(四个九):约 4.3 分钟。
单台便宜 VPS 的实际可用性往往只有 99% 甚至更低(一年能宕好几天)。但如果你有两台独立机器(最好不在同一台母鸡、甚至不同机房),同时挂的概率会大幅降低——两台各自 99% 的话,同时挂只有 1%×1%=0.01%,可用性直接到 99.99%。这就是冗余的威力。
所以 HA 第一步不是上软件,是让你的两台机器尽量"独立":不同厂商、不同机房最好;实在不行同厂商也要选不同数据中心。别两台都买同一个 RackNerd 洛杉矶机房还放同一机位,那叫"假冗余"。
三、浮动 IP(Floating IP)是什么鬼
想象一下:你有两台机器 A 和 B,用户怎么访问?如果直接用 A 的 IP,A 挂了用户还是打 A 的 IP,没用。HA 的关键就是对外只暴露"一个" IP,这个 IP 能随时从 A 解绑、绑到 B 上。这个可漂移的公网 IP,就叫浮动 IP(Floating IP / Elastic IP / 高可用 IP)。
工作原理:A 正常时,浮动 IP 绑在 A 上,流量全进 A;A 挂了,系统把浮动 IP 重新绑到 B,流量改走 B。整个过程用户只认那个 IP,不用改 DNS、不用改客户端。
哪些厂商支持浮动 IP
- Vultr:原生支持 Floating IP,控制台一键买,后台用 API 或脚本解绑/绑定。
- DigitalOcean:叫 Floating IP,同样支持 API 重绑定。
- AWS / 腾讯云 / 阿里云:叫弹性公网 IP(EIP / Elastic IP),不能直接用 VRRP 漂移,但可以通过厂商 API + 脚本在故障时自动换绑。
- Bandwagon(搬瓦工)、CloudCone、RackNerd 等超低价商家:大多不支持浮动 IP,也没有跨实例私有网络。这种要做 HA 就得走别的路子(见下文"云上注意点")。
所以如果你铁了心要玩 Keepalived 真漂移,优先选 Vultr 或 DigitalOcean,这俩对浮动 IP 最友好。
四、Keepalived + VRRP:让 IP 自己漂起来
Keepalived 是 Linux 上最主流的 HA 软件,它实现了 VRRP(虚拟路由冗余协议)。简单说:两台机器组成一个 VRRP 组,一台是 MASTER(持有浮动 IP),一台是 BACKUP。MASTER 每秒发一次"心跳"(advertisement),BACKUP 盯着,一旦连着几秒没收到心跳,就判定 MASTER 挂了,自己上位接管浮动 IP。整个过程秒级完成,全自动。
云上必看:multicast 与 unicast
VRRP 默认用组播(224.0.0.18)通信,但很多云厂商(尤其 AWS)禁组播,而且两台机器往往不在同一个二层网络。解决办法是用 unicast(单播)模式,直接写死对端 IP。下面配置我会给 unicast 版本,适用性更广。
前置:允许绑定不存在的 IP
Keepalived 要让机器绑定一个"暂时不属于自己"的浮动 IP,内核得放行,两台机器都执行:
# 允许进程绑定尚未在本机生效的 IP(VIP 漂移必备)
echo 'net.ipv4.ip_nonlocal_bind = 1' | sudo tee /etc/sysctl.d/99-keepalived.conf
sudo sysctl --system防火墙放行 VRRP
# VRRP 协议号是 112,放行它(用 UFW 的话)
sudo ufw allow from 10.0.0.0/24 to 224.0.0.18 proto vrrp
# 或者直接允许对端 IP
sudo ufw allow from 对端内网IP主节点(MASTER)配置
假设你两台机器内网 IP 分别是 10.0.0.10(主)和 10.0.0.11(备),浮动 IP 用厂商给的那个公网浮动地址(这里先用占位 203.0.113.100 表示)。编辑 /etc/keepalived/keepalived.conf:
global_defs {
router_id NODE_A
}
vrrp_instance VI_1 {
state MASTER
interface eth0 # 改成你实际网卡名,云上常是 ens3/eth0
virtual_router_id 51 # 同一 HA 组必须一致, range 1-255
priority 150 # 主节点优先级高
advert_int 1 # 每秒发一次心跳
# 云厂商禁组播,改用单播
unicast_src_ip 10.0.0.10
unicast_peer {
10.0.0.11
}
authentication {
auth_type PASS
auth_pass 你自己设的密码
}
virtual_ipaddress {
203.0.113.100/24 dev eth0
}
}备节点(BACKUP)配置
只有三处不同:router_id、state、priority,以及 unicast 的源/对端 IP 反过来:
global_defs {
router_id NODE_B
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51 # 必须和主节点一致!
priority 100 # 比主节点低
advert_int 1
unicast_src_ip 10.0.0.11
unicast_peer {
10.0.0.10
}
authentication {
auth_type PASS
auth_pass 你自己设的密码 # 必须和主节点一致!
}
virtual_ipaddress {
203.0.113.100/24 dev eth0
}
}两台都装好并启动:
# 两台都装
sudo apt update && sudo apt install -y keepalived
# 两台都启动并开机自启
sudo systemctl enable --now keepalived
# 在主节点确认浮动 IP 已经挂上
ip addr show eth0 | grep 203.0.113.100加健康检查:服务挂了也要漂移
光检测"机器死没死"不够——万一 Nginx 崩了但 OS 还活着,VIP 不会漂移,用户照样 502。用 vrrp_script 做应用级健康检查:
# 健康检查脚本 /usr/local/bin/check_nginx.sh
#!/bin/bash
if systemctl is-active --quiet nginx; then
exit 0
else
exit 1
fichmod +x /usr/local/bin/check_nginx.sh然后在 vrrp_instance 里加上脚本关联(两台都加):
vrrp_script check_nginx {
script "/usr/local/bin/check_nginx.sh"
interval 2 # 每 2 秒检查一次
weight -50 # 检查失败,优先级减 50
fall 2 # 连续失败 2 次才算挂
rise 2 # 连续成功 2 次才恢复
}
vrrp_instance VI_1 {
# ... 前面照旧 ...
track_script {
check_nginx
}
}逻辑是这样:主节点优先级 150,Nginx 一挂,优先级变 100,低于备节点的 100?注意这里要留余量——把主节点 weight 减完必须明确低于备节点。比如主 150、备 100、weight -60,主挂了变 90 < 100,漂移触发。具体数值以你实际环境为准,自己算一下。
真刀真枪测一次
# 在主节点直接停掉 keepalived,看备节点是否接管
sudo systemctl stop keepalived
# 到备节点执行,应该能看到浮动 IP 已经漂过来
ip addr show eth0 | grep 203.0.113.100能漂过去、再启回来能漂回来(默认开启抢占 preempt),说明成了。
五、数据库怎么同步:MySQL 主从复制
IP 漂了,但两台机器的数据得一致才有意义。数据库同步最经典就是 MySQL 主从(Master-Slave):主库负责写,binlog 记录所有改动,从库把 binlog "搬"过来在自己身上重放,数据就和主库一致了。主挂了,从库可以顶上(提升为新的主)。
主库配置(/etc/mysql/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf)
[mysqld]
server-id = 1 # 每台机器必须唯一
log-bin = mysql-bin # 开启 binlog
binlog_format = ROW # 行格式,最安全
gtid_mode = ON # 强烈建议开 GTID
enforce_gtid_consistency = ON
binlog_expire_logs_seconds = 604800 # binlog 保留 7 天
sync_binlog = 1从库配置
[mysqld]
server-id = 2 # 必须和主库不同
log-bin = mysql-bin
gtid_mode = ON
enforce_gtid_consistency = ON
read_only = ON # 从库只读,防误写
super_read_only = ON
log_slave_updates = ON # 方便以后从库再被提升改完两边 systemctl restart mysql。然后主库建个专门的复制账号:
CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY '设个强密码';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';
FLUSH PRIVILEGES;从库指向主库(MySQL 8.0.23+ 用新语法):
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='10.0.0.10',
SOURCE_PORT=3306,
SOURCE_USER='repl',
SOURCE_PASSWORD='设个强密码',
SOURCE_AUTO_POSITION=1; # GTID 自动定位位点,香
START REPLICA;老版本 MySQL 用 CHANGE MASTER TO ... / START SLAVE;。检查同步状态:
SHOW REPLICA STATUS\G
# 重点看 Replica_IO_Running 和 Replica_SQL_Running 是否都是 Yes提升从库:主库真挂了,在从库执行 STOP REPLICA; RESET REPLICA ALL;,然后让应用连从库(配合 Keepalived 漂移后的新主),它就成了新主。注意提升前确保从库已经重放完所有 relay log,别丢数据。
六、网站文件怎么同步:Syncthing 实时双向同步
数据库有主从,那网站代码、用户上传的图片/附件呢?两台机器得都有同一份文件。最简单稳妥的是 Syncthing——开源、端到端加密、多机目录实时双向同步,比 rsync 定时跑省心,比 NFS 不依赖单点。
两台机器都装(以 Docker 为例,数据目录映射出来):
docker run -d \
--name syncthing \
--restart=unless-stopped \
-p 8384:8384 \
-p 22000:22000/tcp \
-p 22000:22000/udp \
-v /opt/syncthing:/var/syncthing \
louislam/syncthing:latest浏览器开 http://机器IP:8384,每台机器都有个 Device ID。在 A 上"添加远程设备"粘贴 B 的 ID,B 上确认;然后建一个共享文件夹(比如 /var/syncthing/www),Folder Type 选 Send & Receive(双向同步),勾选对方设备。两边一确认,文件改动就实时互相同步了。
踩坑提醒:
- 务必开文件版本管理(File Versioning):比如 Trash Can 模式,误删/被覆盖的旧文件会进版本目录,能救回。网站文件被同步"误删" propagation 是很疼的。
- 网站根目录让 Nginx 直接读
/opt/syncthing/www就行,两台配置一致。 - 双向同步有个经典坑:两边同时改同一个文件会冲突,生成
.sync-conflict文件。代码部署建议只在一台机器上 git pull,另一台自动同步过去,别两头都改。
七、脑裂(Split-Brain)与仲裁,这个坑必须说
HA 最怕的就是脑裂:两台机器因为网络抖动互相收不到心跳,都以为对方挂了,于是都把自己提升成 MASTER,同时持有浮动 IP、同时写数据库。结果就是数据冲突、用户请求被分流到两台不一致的机器上,惨烈。
怎么防?
- 不要两台都在同一个网络孤岛:脑裂往往源于心跳链路和流量链路是同一根线。条件允许用心跳走内网、业务走公网浮动 IP。
- 用 nopreempt(非抢占):备节点接管后,即使原主恢复也不自动抢回 MASTER,避免来回横跳。在 BACKUP 节点加
nopreempt。 - 加仲裁(quorum):引入第三台廉价机器或云上探测服务做"裁判",谁拿到仲裁谁才是真主。Keepalived 本身没有内置 quorum,可以用
notify_master脚本里调用外部检查,或用track_script检测到"对端其实还活着"就拒绝接管。 - fencing( fencing / 隔离):真发生脑裂时,强制把"失联但可能还活着"的那台关机或摘掉它的 IP,确保只有一个主。云上可以用厂商 API 强制停机器。
- 数据库层用半同步复制 + GTID:主库提交事务前至少等一个从库 ACK(无损半同步
AFTER_SYNC),能大幅降低脑裂时丢数据的概率。
八、总结 / 避坑清单
用两台便宜 VPS 拼高可用,核心就这几块:
- IP 漂移:Keepalived + VRRP,云上用 unicast,浮动 IP 优先选 Vultr / DigitalOcean。
- 数据一致:MySQL 主从(GTID + 半同步),写主读从,主挂从提升。
- 文件一致:Syncthing 双向实时同步,开版本管理防误删。
- 防脑裂:nopreempt + 独立心跳 + 仲裁 + fencing,数据库上半同步。
避坑清单(都是血泪):
- 两台机器尽量不同厂商/不同机房,别搞"假冗余"。
- 云上默认禁组播,一定用 unicast,不然心跳根本不通。
- 浮动 IP 漂移后,注意 ARP / 路由缓存,必要时在
notify_master脚本里arping一下刷新网关 ARP。 priority和weight算好余量,减完必须明确低于对端,否则漂不动。- 数据库提升从库前,先确认 relay log 重放干净,别丢数据。
- Syncthing 别两头同时改代码,部署走单点。
- 真 HA 别忘了备份:HA 防的是"机器挂",防不了"误删库",定期离线备份才是底线。
最后说句大实话:HA 不是万能药,它解决的是"单机宕机",解决不了"你代码有 bug"和"没备份"。但花两台便宜机器的钱,把可用性从看运气拉到四个九级别,这波投入绝对值。
延伸阅读
更多相关攻略推荐:【VPS 硬件选型指南 (CPU 篇) 04】AMD EPYC 还是、【发行版选型 07】Arch Linux 深度科普:滚动发布、pac、【VPS 进阶玩法精选 014】ARM 架构 VPS 值不值得上?A、【对象存储 01】对象存储怎么选:Backblaze B2 vs C、【TCP优化 01】美国 VPS 跑不快?多半是没开 BBR,一个命。