背景
最近发现阿里云的1核512M内存的云主机上的Ubuntu是18.04.5,但是/etc/apt/sources.list
中的dist名称却是bionic(20.04),就有些奇怪,为啥之前没有完成dist-upgrade的升级呢?
于是我试着做了一下do-release-upgrade
,然后提示直接告诉我说Ubuntu 20.04不打算支持i386架构了,所以不能升级。
我没有更换ecs.t1.xsmall
这个云主机的计划,且这个袖珍款从15年到现在的6年时间从运行Ubuntu 14.04 i386开始都很好的服务于我的博客系统,鉴于升级到x64会显著增加应用的内存占用,所以我想原地更换当前发行版到一个还持续维护的i386的发行版。看了一圈还是Debian最合适,毕竟是同源的东西。
但是如何原地升级呢? 今天打算具体实操下。
方案探索
网上搜索基本给出的都是说不可能或会失败。我有看过他们升级到视频,明显看出了各种问题。一个油管上的老哥的视频操作把dist release名称直接换成了debian-testing,然后升级到一半就挂了,这个是必然的,因为libc的版本差异太大了,原地运行必然挂掉。
这里总结出升级系统的几个必要的点
- 要提前安装兼容于两个发行版的高版本内核
- 新的Debian发行版的libc版本要与Ubuntu 18.04.5相近且二进制兼容
- 新的Debian发行版大部分包的版本要高于18.04.5且绝大多数版本是兼容的
其中第一条保证了两个发行版在内核层面都能正常工作。第二条保证了原地升级期间,无论是libc系列升级前还是升级后,都可以支撑Debian和Ubuntu的包正常运行。第三条保证绝大多数包都可以通过自动升级的机制直接升级到Debian的包,不用过多的手工操作。
另外还要准备一套保底机制
- 使用阿里云的快照功能,给升级前的机器的云盘创建一个可以恢复的快照,避免玩挂了之后回不去
- 使用阿里云的VNC远程功能,在出现无法通过SSH连接时,提供通过本地操作的方式进行维护的能力
- 准备一台应急的同可用区的云主机(如果没有现成的可以直接买一个按量付费的机器,用后删掉)如果在出现了rootfs中直接无法运行或启动的问题,可以挂载不可启动的磁盘到应急主机的数据盘,使用debootstrap和chroot进行修复
那么到底应该选择哪个Debian发行版呢?
根据Debian的发版时间,我本来选取了Debian 9 stretch作为备选系统。因为其libc是2.28,Ubuntu 18.04.5是2.27,两个发行版在时间上也相近,兼容性是不错的。
方案实操
接下来开始实操。
更换apt源
首先替换/etc/apt/sources.list
中的bionic
为stretch
,并进行apt update
操作。
此时会存在一个缺失debian的gpg的问题,参照这个文章的方法,将缺失的PUB_KEY获取到本地,再重新apt update
即可。
然后检查了一下可以升级到包数量
1 2 |
apt list --upgradable |
发现只有60多个包。对比apt list --installed | wc -l
的600+个包,差的有点多。
所以这里面临了一个选择,即我不得不再考虑升级到下一个Debian发行版。所以接着我替换/etc/apt/sources.list
中的stretch
为buster
,并进行apt update
操作。
然后再次查看可以升级到包的数量,这次是500+了,看起来比较稳了。剩下一部分包是使用第三方的apt源安装的,如nginx, php这些,升级完成后再替换即可。
apt原地升级包
接下来就要执行原地升级了。
1 2 |
apt full-upgrade |
接下来就是等待了,大概执行了有不到20分钟,期间需要在提示Y/N时进行一下操作,很顺利的就完成了,包括libc这些都顺利安装。
批量安装debian的必备包
因为debian毕竟和ubuntu有些基础包的差异,所以这里还需要手工选中debian工作的必备包,再执行一下安装。
我在这里找了一个相对比较干净的Debian 10 amd64架构的机器(如果不存在可以安装虚拟机,或制作一个chroot的rootfs来构建),执行了下面的命令来获取已经安装的软件包
1 2 |
dpkg --get-selections > pkgs.list |
然后因为其是amd64架构,将其中的:i386
全部提换掉
1 2 |
cat pkgs.list | sed -i 's/:i386//g' > pkgs-i386.list |
接下来将这些包在debian上选中
1 2 |
dpkg --set-selections < pkgs-i386.list |
然后执行变更动作
1 2 |
apt-get dselect-upgrade |
接下来比较快,大概几分钟就安装完了。
到现在这一步,支撑Debian 10运行的基础包已经不缺失了。
手工替换残留的Ubuntu源的包
接下来看看还有哪些包还是从Ubuntu的源安装的。一般情况下Ubuntu的包版本号里会带着ubuntu字样,所以我采取下面的方法来查看
1 2 |
apt list --installed | grep ubuntu |
这种情况,一般都是Debian的包具有以下特点
- 名称与Ubuntu包有不同
- 版本号一样或更低
- 依赖有问题,需要手工安装替换
这种包,只能手工安装dpkg来替换。提换的流程如下
- 从pakcages.ubuntu.com检查未被替换包的功能,版本号及依赖项,如
https://packages.ubuntu.com/bionic/zlib1g
- 从packages.debian.org检查新的后选项包的信息,如
https://packages.debian.org/buster/i386/libatomic1/download
- 如果没有对应名称的包,在debian这里搜索名称一样不带版本号的包,或者直接去google搜索功能一样的包对应的是什么名称。
- 然后从packages.debian.org下载对应的包,使用
dpkg -i
手工安装
鉴于这样的包有100多个,为了加速上面个步骤,我写了一个脚本来执行debian包的下载、安装及删除动作
pkg.sh
包内容
1 2 3 4 |
curl -s https://packages.debian.org/buster/i386/$1/download | grep ftp.cn.debian.org | cut -d'"' -f 2 |xargs wget ls *.deb | grep $1 | xargs dpkg -i ls *.deb | grep $1 | xargs rm |
只需要执行sh pkg.sh [pkg_name]
即可完成包的替换安装工作。
经过一系列的包替换工作,最后只剩两个包不能更新
- mysql-server-5.7
- php-7.4
接下来经过搜索,增加了新的第三方apt source,将my-sql-server-5.7
替换为mariadb-server-10.3
,添加了新的php-7.4
源。
至此带ubuntu字样的包都替换完成了。但是其实还是有剩余的,这里采用一个方法来进行检查,即重新reinstall包看哪些包不能安装
1 2 3 |
apt install aptitude aptitude reinstall '~i' |
根据提示,不能升级的包再执行一遍手工替换工作,到最后提示全部包可以重新安装时即可。
彻底删除不用的包
接下来执行删除所有旧的包
1 2 |
sudo apt-get purge $(dpkg -l | grep '^rc' | awk '{print $2}') |
这里注意,不要选择删除对应的数据。这里我就手滑删掉了数据库。。。后面花了很多功夫才恢复了关键数据
再重新安装一遍所有的包,确保之前purge时,被debian包覆盖安装时文件和配置都正常安装
1 2 |
aptitude reinstall '~i' |
更换内核及motd
至此就仅剩余内核还是使用了Ubuntu提供的版本,另外motd信息还是Ubuntu的信息
我在这里手工安装了Debian的最新内核
1 2 |
apt install linux-image-686 linux-image-5.10.0-0.bpo.7-686 |
安装成功后,重启机器,再卸载掉其他之前Ubuntu安装的内核包。
最后是motd信息的修改。因为Ubuntu存在/etc/lsb-release
信息,这里直接删除该文件即可
另外进入/etc/update-motd.d/
,删除掉除了00-header
和10-uname
之外的所有文件
再次使用ssh登录,发现提示已经变成Debian 10了
后记
此次成功将Ubuntu 18.04.5 LTS i386
升级到了Debian 10 i386
,在这个ecs.t1.xsmall
的阿里云机器上又可以享受到可以持续更新的发行版了。