记一次个人服务器迁移

个人使用了三年的腾讯云轻量应用服务器最近快到期了,所以来了一次数据迁移。

问题背景

用了三年的腾讯云轻量应用服务器,最近快到期了。续费要500多一年,新购一台相同配置的99元一年。这个定价很有技术含量😭。续不续费让人有些犹豫。续吧,感觉太坑,我好歹也是搞技术的;不续吧,因为是个人拿来瞎折腾用的服务器,所以上面各种数据和服务有点乱,迁到新的服务器要花些时间。但转念一想,反正都是个人的东西,再怎么折腾也不会有任何风险。好处倒有一些:一是梳理一下自己常用的后端软件,二是来个数据大扫除方便下次迁移,三是小小地挑战一下自己解决问题的能力。

还剩20小时的服务器

梳理常用软件

迁移前先我梳理了一下旧服务器上装过的那些软件,剔除掉不怎么使用的,剩下的就是我个人的必备工具了。

  • 服务器
    • Nginx
    • PostgREST
  • 数据库
    • PostgreSQL
    • Redis
  • 容器
    • Docker
    • Docker Compose
  • 开发工具
    • nvm + Node.js
    • Pyenv + Python
    • Git
  • 管理工具
    • Screen
    • Rsync
    • Certbot
  • 其他工具
    • Gogs
    • Cloudreve

以上都是一些常见软件。有几个相对没那么知名的,这里简要介绍一下:

  • PostgREST - 将 PostgreSQL 数据库直接映射成 RESTful API 的 Web 服务器。作为一个多年的移动端开发,借助这个 PostgREST 也我能轻松搞定后端服务了
  • nvm - nvm 用于多版本的 Node.js 管理。(nvm is a version manager for Node.js)
  • Gogs - Gogs 用于搭建私有的轻量 Git 服务。 A painless self-hosted Git service.
  • Cloudreve - Cloudreve 用于搭建私有网盘。我老婆需要一个可以多端同步的笔记工具,还想自己永久保留数据。我们试用了一圈发现 Joplin 还不错,所以我搭了一个 Cloudreve 云盘作为 WebDAV 来存 Joplin 数据。

安装常用软件

CentOS 上安装软件很方便,大部分时候只是无脑执行 yum install 即可。安装过程中经常要我们确认 ,所以用这样输入命令会方便很多:

1
2
3
4
yes | yum install <name>

# 或者这样
yum install -y <name>

有部分软件是用 curl 加脚本方式安装的。有些参数我们可能不熟,不过有 AI + 搜索引擎,所以这些参数通常不是什么问题。这里有一份 curl 常用参数总结 | aimuke,供遇到问题时查询。

  • curl -o - -o 用于指定文件名。有时我们会看到 -o- 这种写法,它表示自动生成一个临时文件名
  • <curl 命令> | bash - | bash表示将前一个命令的输出作为 bash 的输入
  • curl -fsSL
    • -f- (HTTP)连接失败时(400以上错误)不返回默认错误页面,而是返回一个curl错误码“22”;
    • -s - Silent mode
    • -S - 输出错误信息
    • -L - 支持重定向

少量软件安装步骤稍繁琐,比如 Docker。

1
2
3
4
5
6
7
8
yum install -y yum-utils   //扩展yum功能
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo //添加软件源信息
yum makecache timer //自动选择最快yum仓库源

# 查看可用的 docker 版本
# yum list docker-ce --showduplicates | sort -r

yum -y install docker-ce-26.1.3-1.el8

注意:移除上图 version 前导的 “3:” 得到的字符串才是 yum 命令中的 [VERSION]

yum -y install docker-ce-[VERSION]

有些软件如 Docker 或 Redis 安装完成后记得要用 systemctlservice 命令启动:

1
2
3
4
5
systemctl restart nginx.service
systemctl start docker

# service <name> reload 命令会重定向到 systemctl reload <name>.service
service redis start

梳理待迁移数据

接下来是梳理待迁移的数据以及配置。

  • 服务器
    • Nginx 配置
    • PostgREST 配置
    • Certbot
  • 数据库
    • PostgreSQL 数据
    • Redis
  • 容器
    • Docker
    • Docker Compose 配置
  • 开发工具
    • Node.js
    • nvm
    • Pyenv
    • Python
    • Git
  • 管理工具
    • screen
    • rsync
  • 其他工具
    • Gogs 数据和配置
    • Cloudreve 配置
  • 个人项目
    • 博客
      • 博客评论系统
      • 博客内容数据
    • Anki Server
    • 其他静态网站数据

以上待迁移内容可以分个类:

  • 无需迁移:像 Certbot, Redis, Docker 等应用或工具本身的二进制内容,我会在新机器上重新安装,所以无需迁移
  • 需要迁移
    • 迁移成本极低。静态网站数据,只需要将数据同步到新机器指定位置即可
    • 迁移成本低。PostgreSQL 和 Gogs 部署在 Docker 中,所以我简单拷贝它们各自的 “Docker Compose 配置”、 ./data./conf 目录到新机器,重新执行 docker-compose up -d,几分钟就恢复了服务。(当然,这里没有算上解决镜像无法下载、镜像版本不对、恢复 Nginx 反向代理配置耗费的时间)
    • 迁移成本高。剩下的这些,迁移成本相对高一些。要么是它们本身不支持 Docker 部署,要么是我以前偷懒没有走 Docker 部署。毕竟,如果部署环境永远不会变、数据和服务不会发生迁移的话,直接部署有时也很简单。

梳理及迁移过程中发现有几个明显问题或风险:

  • Docker 相关问题
    • 无法下载 Docker 镜像
    • 之前未指定 Docker 镜像版本
  • Nginx 配置问题。
    • 第一个问题是我用 Nginx 反向代理了接近10个不同服务或静态站点,但全部配置都直接放在 /etc/nginx/nginx.conf 里面
    • 第二个问题是没有使用 Docker 部署 Nginx,无法快速地迁移反向代理配置以及静态站点数据
  • 开发工具版本一致性问题
    • Anki Server 是 Python 编写的,要求 Python 3.10 以上。而新的服务器上默认安装的 Python 3.8,版本过低
    • 博客评论系统 waline 是 Node.js 编写的,在 Node.js 18 上运行良好。新的服务器上已有的 Node.js 版本过高,运行 waline 可能有问题

开始迁移数据

Docker 问题处理

无法下载 Docker 镜像

无法下载 Docker 镜像是个常见问题。这个问题的错误信息类似于:

1
Error Get "https://registry-1.docker.io/v2/"

解决办法是使用国内镜像:

1
2
3
4
5
6
7
8
9
10
11
12
	# 创建目录
sudo mkdir -p /etc/docker
# 写入配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
<这里填上国内可用的镜像源>
]
}
EOF
# 重启docker服务
sudo systemctl daemon-reload && sudo systemctl restart docker

具体的操作步骤以及可用的镜像源,目前国内可用Docker镜像源汇总(截至2025年2月) - CoderJia一文中总结得很清晰,这里不赘述。

未指定 Docker 镜像版本号

Docker Compose 的配置文件中应当指定 Docker 镜像具体版本号,我们很容易忽略这一点。

不指定版本时,默认使用的是 latest 版本。容易理解,当前的 latest 版本肯定跟两年前的 latest 版本不同。一般来说,用当前的 latest 是没什么问题的。但是我保留着两年前的数据呢。

我在新的服务器上装好 PostgreSQL 数据库之后,不能正常访问。使用 docker-compose ps 查看发现 PostgreSQL 没有正常启动,一直在重启。

docker-compose logs 看错误日志发现以下提示:

1
The data directory was initialized by PostgreSQL version 14, which is not compatible with this version 17.4 (Debian 17.4-1.pgdg120+2).

这条日志简直能救命。先在配置文件中加上版本号,

1
2
3
4
5
version: "3.1"
services:
db:
image: postgres:14.9
...

再重新启动容器,可以正常访问了。搞定!

使用 Docker 启动 Gogs 时也有类似问题,但我并没有找到明确的错误日志。不过好在先遇到 PostgreSQL 版本不兼容问题,所以我马上想到 Gogs 的 Docker Compose 配置文件中也要指定版本号,否则很可能我要花费不少时间排查错误。

提示:使用 docker image inspect <IMAGE ID> 可以找到 latest 版本的具体信息。

Nginx 反向代理配置拆分

直接修改 /etc/nginx/nginx.conf 这个默认配置文件不是一个好的实践。如下的方式更可取。

先在 /etc/nginx/nginx.conf 中引入 sites-enabled 目录下的所有配置文件。

1
2
3
4
5
...
http {
...
include /etc/nginx/sites-enabled/*.conf;
}

再在 /etc/nginx/sites-available 目录中编辑具体的配置文件,每个配置文件对应一个站点或服务。

然后根据需要建立从 sites-enabled 目录到 sites-available 的软链接。

1
ln -s /etc/nginx/sites-available/a.site.conf /etc/nginx/sites-enabled/

最后,运行 service nginx reload 重新加载配置。

将配置拆分到不同的文件有很多好处,

  • 删除 sites-enabled 目录中某个软链接就能很方便地下线该站点或服务,不影响其他配置
  • 使用 Certbot 开启 HTTPS 访问时,Certbot 只自动修改对应的配置文件,而不是 /etc/nginx/nginx.conf
  • 备份配置和恢复配置很方便

Pyenv 安装和管理 Python

我有一些使用 Pyenv 管理和使用多版本 Python 的经验,见之前的浅谈编程环境管理 | 皮皮小黑屋

1
2
3
flowchart LR
as["Anki Server"] --> py["Python 3.10"]
py --> Pyenv

Anki Server 依赖 Python 3.10,所以需要用 Pyenv 安装 Python 3.10。一顿轻车熟路地操作下来,我傻眼了,Python 报错并提示 No module named '_sqlite3'

原来我忘记 Pyenv 编译安装 Python 前,要确保已安装必要的依赖项。

1
2
3
4
flowchart LR
as["Anki Server"] --> py["Python 3.10"]
py --> Pyenv
Pyenv --> sqlite-devel

yum install sqlite-devel 后重新在 pyenv 中安装 Python 3.10,问题解决。

一些小技巧

第一个是关于 tar 和 scp 的小技巧。

1
2
3
4
5
6
7
# 打包成一个文件来传输
tar -cvf dir.tar dir/

# 一定要走内网IP,否则速度太慢
scp dir.tar user@<内网IP>

tar -xvf dir.tar
  • 使用 tar 命令将目录打包成一个文件来传输
  • 一定要走内网IP,否则文件传输速度太慢

第二个是关于 ssh 的小技巧。迁移数据过程中经常要 ssh 远程登录两台不同的机器,所以可以编辑 ~/.ssh/config 给远程机器取个简单的名字,

1
2
3
4
5
6
7
8
~/.ssh/config
EOF
Host s2
HostName <IP_S2>
User user
Host s1
HostName <IP_S1>
User user

配置好之后可以使用 ssh user@s1ssh user@s2 来登录远程机器,方便很多。

数据备份与恢复

Docker

一开始我没太注意到 Docker Compose 的配置和数据这样存放会有什么好处。

1
2
3
4
5
6
7
8
.
├── gog
│   ├── docker-compose.yml
│   ├── gogs-conf
│   └── gogs-data
└── postgresql
├── data
└── docker-compose.yml

经过这次迁移,我发现如果把所有容器的配置和数据放在同一个目录下,备份和迁移起来是最方便的。

如果一开始我也使用 Docker Compose 来管理 Nginx,那么对应的配置和数据迁移起来会快很多。

备份点、快照与自定义镜像

数据迁移不是我一个人会遇到的问题。事实上,腾讯云也提供一些工具来支持数据迁移。我了解到的有三个功能:

  • 备份点。备份点可以为云硬盘保留某一时刻的历史数据。当前仅支持为数据盘创建备份点
  • 快照。快照可用于服务器系统盘数据备份。可以回滚到当前服务器的某个快照。不支持跨机器回滚
  • 自定义镜像。自定义镜像相当于自己的安装盘。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mindmap
root((轻量应用服务器))
硬盘
自带一块系统盘
最多5个数据盘,需要另购
数据备份功能
备份点
系统盘❌
数据盘✅
快照
系统盘✅
跨机器使用❌
自定义镜像
系统盘✅
跨机器使用✅

看起来使用自定义镜像最符合我的应用场景。明年很可能还会再迁移一次数据,到时候或许会试试。

总结

经过好几个小时的工作,数据终于迁移完成。我的博客又能访问和评论了,不过其实已经换了一台机器。

简单回顾一下:

  • 梳理了自己常用的后端软件
  • 处理了几个使用 Docker 过程遇到的问题
    • 一是镜像无法下载
    • 二是未指定镜像版本号
  • 拆分 Nginx 配置方便管理和迁移
  • 一些小技巧,例如使用内网IP传输数据