惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
H
Hacker News: Front Page
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
Lohrmann on Cybersecurity
Cisco Talos Blog
Cisco Talos Blog
O
OpenAI News
S
Securelist
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
H
Heimdal Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recorded Future
Recorded Future
Microsoft Azure Blog
Microsoft Azure Blog
MyScale Blog
MyScale Blog
Webroot Blog
Webroot Blog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
Latest news
Latest news
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
D
Docker
D
DataBreaches.Net
A
About on SuperTechFans
T
Tor Project blog
V
V2EX
G
Google Developers Blog
博客园 - Franky
N
News | PayPal Newsroom
T
The Blog of Author Tim Ferriss
I
InfoQ
H
Help Net Security
V2EX - 技术
V2EX - 技术
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Security Affairs
SecWiki News
SecWiki News
The Register - Security
The Register - Security
人人都是产品经理
人人都是产品经理
NISL@THU
NISL@THU
小众软件
小众软件
B
Blog
T
Threatpost
P
Palo Alto Networks Blog
博客园 - 【当耐特】
L
LangChain Blog
AWS News Blog
AWS News Blog
月光博客
月光博客
宝玉的分享
宝玉的分享

Origin

Agent 安全:环境变量泄露 人生第一次离职,感觉应该写点什么 在 Python 中复现 Race Condition 再看看 SQL 中的 null 2025Q3 订阅 Recap 再见 xlog Python 的 pth 文件:在程序启动前自动执行命令 浏览器从 A 到 Z 2024Q3 订阅 Recap 使用 TypeScript 撰写 OmniFocus 脚本 [备忘] Go init 行为 [随笔] 一开始就公布定价 [随笔] Server Action [随笔] Swift 异步:Task vs DispatchQueue [随笔] RSS 与稍后阅读 在 Shell 脚本中嵌入二进制文件 谈谈时区 利用 Fly.io 部署 Windmill 强制关闭 xLog 的 Dark Mode 解决 CentOS8 中 yum 源失效 [随笔] 避免完美主义导致的看似时间不够
迁移 Zeabur 集群
Singee · 2025-10-06 · via Origin

我之前的 Zeabur 集群是独立跑在一台物理机上的,物理机相比云服务器的劣势之一就是底层存储的数据安全性 —— 云服务器的硬盘通常是由云服务商保证了安全性不同,物理硬盘坏了就是坏了

而不幸的是,不久前我就遇到了…… 所有数据差点消失,幸亏坏的盘并没有完全坏,以只读方式还能读(只是不能写)算是挽留了我的数据

但那以后我就一直在思考怎么保证数据安全,经过数个不同的方案研究,我最终的选择是 —— 将整机移动到 PVE 中 然后在 PVE 的底层使用 RAID 1 硬盘

方案敲定,行动开始 —— 我整个的迁移不太具有可复制性,因此写一篇完整的「迁移指南」确实不太有意义,但中间确确实实遇到了一些小问题,我觉得记录下来还是比较有价值的

💡 我两台机器(原 Zeabur 物理机和新的在 PVE 里面装的虚拟机):

  1. 在同一个机房
  2. 都是用的 RHEL 系的系统
  3. 内部文件系统都是 LVM + xfs

将 PVE 的硬盘改成 RAID1

我的 PVE 并不是在安装时就直接做好 RAID1 的,因此保证安全性的第一点是将 PVE 的硬盘改成 RAID1

⚠️ 需要注意的是,RAID 只是冗余,不是备份也无法替代备份

这次我的迁移也顺便把备份加上了,但备份又是一个较大的话题,因此本文不会写备份相关的内容,如果未来有机会我会单独写一篇文章,不过可以顺便说一嘴的是,我选用的备份方案是 Velero

我没有阵列卡,那么硬盘改 RAID 就只能是通过软 RAID 来组,在 Linux 下组软 RAID 有两种办法,一是利用 mdadm 组 RAID,另一种则是用 lvm 组 RAID;二者各有优劣,我为了灵活性选择了 lvm 的方案 —— 我现在是 2 块 1TB 的硬盘,使用 lvm 的话后面扩容可以很方便的通过加 1 块 2TB 的硬盘来得到完整的 2TB 可用 RAID1 空间,而如果不用 lvm 虽然也能实现但相对来说更加麻烦,且有难以避免的 degrade 时段

哦对,还有,做冗余不能只将数据做冗余,也要考虑引导,都是老步骤,复制下 ELF 分区、重做下 grub 引导,没什么值得说的,有什么问题问问 ChatGPT 它应该能相当完美的回答。

假设我们现在的环境是:原有的数据卷 /dev/sda3、原有的 VG pve、原有的 LV pve/root /pve/swap pve/data、新的数据卷 /dev/sdb3

首先将新的数据卷转换为 PV pvcreate /dev/sdb3 然后将它加入至 VG vgextend pve /dev/sdb3

pve/root 转换为 RAID1 十分简单:一行命令 lvconvert --type raid1 -m1 pve/root /dev/sda3 /dev/sdb3 搞定

pve/swap 是个 swap 分区,没有做 RAID1 的必要,我们跳过

难点来到了 pve/data 这个逻辑卷 —— 它不是一个普通的 LV 而是一个 Thin Pool,如果我们直接执行 lvconvert --type raid1 -m1 pve/data /dev/sda3 /dev/sdb3 LVM 会报错 Operation not permitted on LV pve/data type thinpool.

可以认为 Thin Pool 是一个「逻辑逻辑卷」,它事实上分成了 meta 和 data 两部分(通过 lvs -a 命令可以看到,它是用 pve/data_tmetapve/data_tdata 组合而成的),所以我们说是对它做 RAID1 但事实上想做的是对它底层依赖的 meta 和 data 做 RAID1 —— 这样,Thin Pool 本身和从这个 Thin Pool 所分配出去的子 LV 也都是 RAID1 了。

所以我们要做的是,将它的底层数据卷转换为 RAID1 即可:

1
2
lvconvert --type raid1 -m1 pve/data_tmeta /dev/sda3 /dev/sdb3
lvconvert --type raid1 -m1 pve/data_tdata /dev/sda3 /dev/sdb3

然后等待 lvs -a 的输出中 Cpy%Sync 列变为 100% 即为 RAID 转换完成。

哦对,还有一件事情,虽然示例中我用的另一块盘也是 SATA 盘做例子,但事实上我的另一块盘是 NVME 的,因此我上面说的「pve/swap 不用做 RAID1」之外,我其实还将 pve/swap 给移动到了新的盘上

将 LV 从一个 PV 移动到另一个 PV 的命令:pvmove -n pve/swap /dev/sda3 /dev/nvme0n1p3

迁移方案的选择

物理机迁移到 PVE 最直接的方法就是直接做整盘磁盘镜像然后导入,这种方案没什么可说的,也是最简单的

但是基于下面几个原因

  1. 我想换个系统,原来的系统是 CentOS Stream,嗯…… 不太适合一个稳定的环境使用
  2. 原来的系统在装 Zeabur 之前还装了一些别的东西,现在已经不需要了
  3. 我所有有价值的数据全都在 k3s 里面了,其实只要迁移 k3s 数据就行,而且我也想试试到底怎么迁移下集群,都要迁移哪些数据
  4. 我讨厌 Live CD 带的 Bash 的体验,特别是我可能还得用 IPMI 的 Remote Console 而不是自己的本地终端 SSH 过去
  5. 我原来其实也不是一块硬盘,而是两块硬盘组的 LVM 卷,两块盘都有数据,整盘迁移还得在 live cd 版系统里面折腾下加载 LVM
  6. 我原来物理机的启动用的 UEFI,但新的跑在虚拟机里面的引导用的 BIOS
  7. 整机迁移我也得迁移完去 PVE 里面改各种网卡、硬盘的映射什么的,也不容易
  8. 嗯…… 我没有一块多余的足够大的盘让我存这么大的磁盘镜像了 = =

我最后放弃了整盘迁移的方案,而是选择 rsync 拷贝 k3s 相关的数据:

  1. 在 Zeabur 中添加一个新的 Dedicated Server 让它为我们安装 k3s 的相关内容
  2. 安装好以后,执行 /usr/local/bin/k3s-killall.sh 停止整个集群、删除给 zeabur 用的 ssh key、在 zeabur 中删除这个服务器
  3. 利用 rsync 拷贝所有集群需要的数据
  4. 启动 k3s 服务,耐心等待它把原来所有的镜像下好
  5. 登录 zeabur 面板,执行一次 Reinstall Zeabur Service

将 ufw 换成 firewalld

我之前使用的防火墙时 ufw,它简单易用,但有一个非常重要的缺陷:只要 k8s 暴露的端口,它没办法阻止

因此,趁着这次,换回 firewalld 了;根据 Zeabur 的说明和 k3s 文档,需要执行下面的内容放行相关流量

1
2
3
4
5
6
7
8
9
10
11
12
13
firewall-cmd --permanent --zone=public --add-service=ssh    
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https

firewall-cmd --permanent --zone=public --add-port=4222/tcp
firewall-cmd --permanent --zone=public --add-port=6443/tcp
firewall-cmd --permanent --zone=public --add-port=30000-32767/tcp
firewall-cmd --permanent --zone=public --add-port=30000-32767/udp

firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16
firewall-cmd --permanent --zone=trusted --add-source=10.43.0.0/16

firewall-cmd --reload

注:我个人不建议放开 30000-32767 —— 我询问过 Zeabur 支持人员,不放开这些端口不会影响 Zeabur 本身的能力,只是非 http 类型的端口映射无法被访问。Zeabur 会默认将所有端口都暴露到公网,部分端口所对应的服务可能有安全问题,所以我的建议是不要添加 30000-32767 两台规则,而仅在确实需要访问映射的端口时再添加

注 2:如果你是基于你自己需要的目的去访问内部服务(如数据库、Redis),我非常不建议你将这些服务暴露到公网,而是应当使用 zproxy 通过代理访问这些内部服务(当然,你需要将 zproxy 本身使用的端口放开)

不停机更换 IP

zeabur 和 k3s 本身对于「本机 IP 」有一定的偏好,如果要改的话比较麻烦,所以简单起见,我的选择是:

假设原机器 IP 1.1.1.1,现在新的机器 IP 2.2.2.2,我将两个机器的 IP 对调下就好了

云主机换个 IP 轻轻松松,物理机换个 IP 就很麻烦,特别是远程物理机换 IP(好吧,其实可以直接登录 IPMI 在 Remote console 里面改 IP 的,但我们当这个不存在)

不过挺好的一点是,我可以通过引入一个新的临时 IP 让这两台机器都不离线的情况下对调下 IP

💡 我这两台机器在同一个内网、网关相同、DNS 相同,都是手动指定的 IP 没有使用 DHCP

假设两台机器的网卡都是 eno1、临时 IP 是 3.3.3.3

流程是:

  1. 虽然和换 IP 的流程没关系,但是建议先执行 /usr/local/bin/k3s-killall.sh 把整个机器的 k3s 停掉
  2. 在原机器(1.1.1.1)执行 nmcli con mod eno1 +ipv4.addresses "3.3.3.3/24" && nmcli dev reapply eno1 让这个机器有 1.1.1.1 和 3.3.3.3 两个 IP
  3. 通过 3.3.3.3 登录原机器(1.1.1.1 + 3.3.3.3),执行 nmcli con mod eno1 -ipv4.addresses "1.1.1.1/24" && nmcli dev reapply eno1 让这台机器只剩下 3.3.3.3 一个 IP
  4. 登录新机器(2.2.2.2)执行 nmcli con mod eno1 +ipv4.addresses "1.1.1.1/24" && nmcli dev reapply eno1 让这个机器有 1.1.1.1 和 2.2.2.2 两个 IP
  5. 通过 1.1.1.1 登录新机器(2.2.2.2 + 1.1.1.1)执行 nmcli con mod eno1 -ipv4.addresses "2.2.2.2/24" && nmcli dev reapply eno1 让这台机器只剩下 1.1.1.1 一个 IP
  6. 通过 3.3.3.3 登录原机器,执行 nmcli con mod eno1 +ipv4.addresses "2.2.2.2/24" && nmcli dev reapply eno1 让这个机器有 2.2.2.2 和 3.3.3.3 两个 IP
  7. 通过 2.2.2.2 登录原机器(2.2.2.2 + 3.3.3.3)执行 nmcli con mod eno1 -ipv4.addresses "3.3.3.3/24" && nmcli dev reapply eno1 让这台机器只剩下 2.2.2.2 一个 IP
  8. 好了,现在原机器 IP 变成了 2.2.2.2、新机器 IP 则变成了 1.1.1.1 —— 对调完成

🤷 显得繁琐了点,但其实不难

利用 rsync 迁移数据

主要有两部分的信息我们需要迁移

  • Zeabur 连接集群所用的 SSH 凭据
  • 集群本身的各种配置、数据

我们需要用 rsync 同步下面的文件/目录到新的集群:

  • /etc/rancher/k3s k3s 配置及连接集群的凭证
  • /var/lib/rancher/k3s/server/db/ k3s 数据库
  • /var/lib/rancher/k3s/server/token k3s 内部授权 token
  • /var/lib/rancher/k3s/storage 使用的 local volume 的存储路径

同步时使用的命令为(在原机器上执行)

1
2
3
4
5
6
7
8
9
10
11
P=/etc/rancher/k3s 
NEW=2.2.2.2


rsync -aHAX --numeric-ids --info=progress2 \
--delete --delete-delay \
$P \
root@$NEW:$P


ssh root@$NEW "restorecon -RF $P || true"

在启动前,删掉下面的目录(k3s 启动时会自动重新创建)

  • /var/lib/rancher/k3s/server/cred
  • /var/lib/rancher/k3s/server/tls

合并卷容量

我为虚拟机分配了 512GB 的硬盘,但我没想到的是我使用的 Automatic Disk Partition 竟然将绝大多数空间给了 /home 导致我迁移数据一半告诉我没空间了 = =

幸好,虽然这个奇怪的硬盘分区有点烦人,但 RHEL 系一律使用 LVM —— 它也只是个 LV 而已!简简单单,删掉这个 LV 把空间匀给 root 就好了

对了,删除 /home 挂载点要记得改 /etc/fstab,不然下次系统可能启动不起来

更新 node

因为 Zeabur 不允许同一个 IP 有两个 Dedicated Server 出现,所以安装的过程中是使用了一个新的 IP 装的

在迁移完,如果想用回原来的 IP,需要修改 /etc/systemd/system/k3s.service.env 文件里面的 K3S_NODE_NAME

然后启动起来看看 kubectl get nodes,如果还有原来的 IP 的 Node,删了就好

Pod 扩容

我数据迁移完一启动各种飙红,仔细一看原来是 Pod 数量超限(默认 110),需要修改 kubelet 配置

/var/lib/rancher/k3s/agent/etc/kubelet.conf.d 目录下,创建一个 01-max-pods.conf ,里面写

1
2
3
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
maxPods: 1000

然后执行 systemctl restart k3s 就好

哦对,这个其实我之前就改过,但是迁移的时候漏了,你可以看看原来机器的 /var/lib/rancher/k3s/agent/etc/kubelet.conf.d 目录下有没有除了 00-k3s-defaults.conf 以外的文件,有的话最好前面 rsync 的时候直接一起迁移了,省事