logo NodeSeekbeta

实现 Ubuntu 22.04 及以后版本安装的技术原理以及我对“原生网络安装”和“dd 安装”的观点与评价

github 项目地址如下,欢迎 star:

https://github.com/leitbogioro/Tools

图库来自 imgur.com ,需要挂梯子全局访问才能正常显示。

这个帖子放在主贴 https://www.nodeseek.com/post-9383-1 (史上最强,支持Debian 12 Ubuntu 22.04 AlmaLinux/RockyLinux/CentOS 9 Fedora 38 AlpineLinux Kali Linux和Windows一键重装脚本功能更新,bug反馈贴(长期更新))中,篇幅实在过于冗长,对仅需要使用 Linux 一键重装脚本的朋友来说会带来很大困惑,也有可能会忽略掉默认密码:LeitboGio0ro 等关键信息,让所以我把它单独开辟出来,供有兴趣的人自行研究。

Ubuntu 自从 18.04 版本后,采用的自动部署方式和 Debian 开始分家,引入了一种叫 cloud init 的部署工具,说实话,这个产品的产品力毋庸置疑,它的厉害之处是把不同发行版本 Linux 的自动部署配置格式从顶层加以统一的抽象化,说人话就是云服务运维人员只需写一套相同的配置文件,云服务商就可以快速给一台机器布置 Debian Kali Ubuntu AlpineLinux CentOS Almalinux RockyLinux Fedora 等等 Linux 系统,用户名、密码、网络配置等等关键参数都由 cloud init 来处理,不需要云服务商运维自己定期给不同的 Linux 发行版制作各种不同镜像,由于该项目是开源的,现在很多云服务厂家后台都是用 cloud init 来部署镜像,应用范围很广,是非常强势的一种应用。

但是,Ubuntu 的母公司 Canonical 在方便了云服务商的同时,却给普通用户造成了不小的麻烦,其中罪魁祸首就是把 debian installer 给移除。Ubuntu 支持兼容 Debian 的安装方法从 Ubuntu 20.04 后(这还是好多人不满,官方磨磨蹭蹭给的,声称是“兼容一些老用户的习惯”)就彻底放弃,比如 Ubuntu 22.04 官方源中,最小化传统网络启动(netboot)内核页面是空的:

http://ports.ubuntu.com/dists/jammy/main/installer-arm64/current/legacy-images/

5

Ubuntu netboot 页面:

https://cdimage.ubuntu.com/netboot/

6

虽然能看到 Ubuntu 22.04 jammy 的目录,但点击去就会发现,人家只是虚晃一枪,并不再提供相关文件,仅引导你去使用他们提供的 cloud init 安装方式:

https://cdimage.ubuntu.com/netboot/jammy/

7

从 Ubuntu 22.04 开始,仅支持读取 cloud init 格式的配置文件部署,不再与之前的 debian installer 兼容,这使得很多好用的,基于 Debian 安装方式部署 Ubuntu 系统的方式均已失效,我愿称之为 Canonical 损失了一大笔宝贵的遗产。

Canonical 一意孤行的同时,却没有给普通用户一个好的替选方案,我的意思就是供用户下载一个很小的网络启动文件,然后从 grub 引导启动,启动文件读取相应的配置文件,然后连接镜像源,完成系统的原生自动化安装工作。目前可行的方案仅有两种:

  1. 下载一个 iso 文件,烧录到外部存储设备,比如光盘或 U 盘,然后系统重启后从 iso 启动,读取 cloud init 进行自动化安装;
  2. 从 Ubuntu cloud images 网站下载每周官方打包好的 Ubuntu 对应版本镜像,转换格式后挂载,对这个镜像里的系统进行一些定制,然后再将其 unmount,像以前安装 ghostXP 一样,把镜像文件全盘解压到硬盘,在读取 cloud init 完成重启后的自动化部署。

注意,1中的 Ubuntu 安装文件是被压缩的,安装时需要重新解压,和我刚才说的网络启动方式“原生”安装一个系统的原理是一致的,2中的 Ubuntu 是一个已经完全被释放出来的 Ubuntu 系统包,这个系统只要重写到硬盘里,就能作为一个系统独立启动,只不过如果不加以配置,它的用户名、密码、网络等都是默认的,装完了你也连不上去。

所以问题来了,对于一个只有单硬盘,无法挂载外部存储设备的 VPS 来说,挂 iso 安装很明显是不现实的,独立于硬盘,可给对硬盘操作(格式化时硬盘不能自己操作自己)的存储设备就是内存,Debian、红帽系的网络安装都是基于加载在内存里的微缩系统来完成初始化,并往硬盘里写入系统、配置、启动的。既然 Ubuntu 暂时不提供网络安装选项,那么核心思路还是要先在内存里启动一个中介系统,才能保证后续操作最起码有可能。

煎饼哥的方式是采用 AlpineLinux 为中间系统,启动后用 AlpineLinux 给硬盘分区,预留一个存 Ubuntu iso 镜像的分区,然后给中间系统写入下一次从 Ubuntu iso 引导,再次重启后启动 Ubuntu 安装程序,读取 cloud init 配置完成后续安装,在新系统里还要埋一个自动扩展分区容量的脚本,否则新系统里会发现残留一个当时存储 iso 镜像的分区,以上步骤中,那个 iso 镜像分区就是“外部存储设备”,虽然它是虚拟的分区,不是额外的物理硬盘,也能完成预期效果。

我在思考和开发相关功能时,决定采取第二种办法,即简单修改一下 Ubuntu Cloud images 的内容,然后把它存放在服务器上,反正是基于 Ubuntu 官方 daily 构建(不一定是每天,有时候会隔几天甚至一周以上)的完整系统文件,兼容性和稳定性肯定值得信赖。然后启动中介系统,用 dd 方式将其下载并完全重写到目标硬盘,最后植入 cloud init 文件,让 Ubuntu 系统包完全释放到硬盘并重启后,按我预期的要求,完成自动化部署工作。

最初的中介系统选用的是和 dd Windows 一样的 Debian 12,因为相比仅支持 IPv4 动态或静态启动的 AlpineLinux 来说,Debian 12 可以以 IPv4 IPv6 任意静态或动态方式启动,兼容性无疑更强,但 Debian installer 具备数个无法有效解决的致命缺陷,包括但不限于:

  1. Debian installer 环境为 busybox,一个超级精简的 Linux 系统,精简到什么程度呢?就连普通的 grep sed 等命令支持的参数也被精简,导致许多在正常 Linux 发行版下能正确运行的参数也无法执行的程度,很多正则表达式也不能识别;
  2. Debian installer 在系统安装时不同阶段,会按需加载需要的组件,如果在某阶段之前去执行,很多命令会因为组件还未加载完成无法执行,最典型的例子就是我在 VNC 里,过了 partition 分区阶段,手搓命令,可以挂载 dd 到硬盘后的 Ubuntu 系统文件,并写入 cloud init,但如果在 installer 刚启动时就开始手搓命令,挂载命令就无法执行。
    如果你愿意和我一样把键盘搓冒烟,通过 Debian installer dd 也能获得一个能正常运行的 Ubuntu:
    8
    9
    10
    但是,一旦把以上案例中手搓的命令写入到 Debian installer 的自动应答文件 preseed.cfg 里,手搓效果和脚本执行效果就完全不同,Debian installer 始终无法完成 mount dd 到硬盘中的 Ubuntu 镜像文件,并把参数传递给 cloud init,这一步至关重要,如果无法完成,重启后的 Ubuntu 没有被进行过任何配置,包括 ssh 服务等都没有初始化,机器是无法连接的,整个安装过程,包括从服务器下载镜像,可以说是完全白费力气。
  3. preseed.cfg 本质上是一个规范 Debian 安装程序该如何自定义 Debian 系统安装的工具,它用来安装 Debian 系统足够优秀,但并不能作为一个可以让命令在 Debian installer 里自由运行,配置的脚本,preseed 中虽然可以通过指定 d-i partman/early_command string、d-i preseed/run string、d-i preseed/late_command string 等方法让 Debian installer 去执行一些有限功能的命令,但这些方法执行命令时要么时机太靠前(刚刚开始时就运行),要么太靠后(装完 Debian 后再执行),相关命令和依赖也不能自由地加载和安装,所以除了将其用于 dd Windows 用,dd Ubuntu 只能放弃。

虽然我老是抱怨 AlpineLinux 的各种缺点,并且它的环境也是 busybox,但当它作为一个中介系统,除了不支持从纯 IPv6 启动,简直完美,煎饼哥眼光真是太独特了,找到这么一个系统作为中间件。当其在内存启动时,就是一个具备完整功能的 Linux 发行版,需要什么组件,就用 apk add 装什么,需要挂载分区还是格式化硬盘,使用对应的命令执行即可,丰俭由人,这点真的比 Debian installer 好太多了。

通过启动中介 AlpineLinux 系统,在中介系统中 dd Ubuntu 官方镜像实现,镜像源来自于我从官方同步并打包的,Ubuntu 官方镜像源地址:

https://cloud-images.ubuntu.com/daily/server/

官方镜像源的文件不能直接用来 dd,必须使用 qemu-utils 工具做一下格式转换。

用我编写脚本并解包并重新打包好的 Ubuntu 镜像源地址:

https://cloud-images.a.disk.re/

打包过程完全自动化,仅需首次运行时手动执行,此后便会执行 crontab 自动任务,每周末凌晨 4 点 30 自动重新打包一次,避免手动操作出现的人为失误,打包脚本原代码:

https://github.com/leitbogioro/Tools/blob/master/Linux_reinstall/Ubuntu/autoRepackUbuntuCloudImages.sh

完整代码实现:
8

通过分析以上代码可以看到,修改仅涉及向启动内核添加 net.ifnames=0 biosdevname=0 启动参数,确保 cloud init 部署阶段面向的网卡统一为 eth0,否则不同的机器网卡名各有不同,很难统一化设置,其他分文未动,确保原汁原味,不加私货。

如果需要使用我的自动化打包脚本,仅需修改 websiteDir="/www/wwwroot/cloud-images.a.disk.re/Ubuntu" 中即网站目录部分,你就可以在你的服务器上部署对应的 Ubuntu cloud images,确保该脚本在 root 目录执行即可。

我从中获得灵感的 Ubuntu cloud images 制作方法原链接:https://bleatingsheep.org/2022/03/14/%E7%94%A8-Ubuntu-Cloud-Images-%E5%88%B6%E4%BD%9C%E8%87%AA%E5%B7%B1%E7%9A%84%E4%BA%91%E9%95%9C%E5%83%8F%EF%BC%88%E9%85%8D%E7%BD%AE-cloud-init-%E7%9A%84-NoCloud-%E6%95%B0%E6%8D%AE%E6%BA%90%EF%BC%89/

源代码和原理都在以上列出,如果还想剖开六子肚子,看看他吃了几碗粉,死缠烂打质疑我制作的 Ubuntu cloud images 不干净的,你可以带个 U 盘,买张机票去机房旁边蹲着装,那样最放心,也符合您多疑的性格,保证没人害你。

通过 dd cloud images 安装的 Ubuntu 20.04/22.04 仅支持从 IPv4 静态或动态启动,双栈机进入 Ubuntu 后 IPv6 网络能自动配好,由于完美地继承了 AlpineLinux 的缺陷,所以纯 IPv6、1GB 内存以下机器暂不支持。

Ubuntu 18.04 cloud images dd 包不再制作并提供的原因是 Ubuntu 18.04 采用的是第一代 cloud init 书写格式,为 version 1,其中网络配置→网关的书写语法与 version 2 有很大不同,比如 version 1 配置网关的方法为:

gateway4: IPv4 网关
gateway6: IPv6 网关

而 version 2 配置网关的书写语法为:

routes:
- to: default
via: IPv4 网关
- to: default
via: IPv6 网关

version 1 和 version 2 完全不能互相兼容,Ubuntu 20.04/22.04 的 netplan 在读取 version 1 的网关配置时会报错且无法正常启动网络,反之亦然,为保持一致性,故放弃对 Ubuntu 18.04 的支持。

由于打包好的镜像源存放在我的吃灰甲骨文机器上,所以国内机器安装 Ubuntu 的时候 dd 过程会卡很久,原因是国内互联网的国际进出口带宽不足,晚高峰国内机器连接我提供的 dd 镜像服务器会非常非常慢,等就是了,等一晚上都不奇怪。

如果你也按我以上提供的方法制作好了对应的 Ubuntu cloud images dd 包,并存放在了你的服务器上,你可以指定 --image '镜像源 url' 来使用你自己定制的 dd 包安装 Ubuntu。

bash InstallNET.sh -ubuntu 22.04 --image '存放 Ubuntu cloud images 的网站根目录,如:https://cloud-images.a.disk.re/Ubuntu/ ,不要包含 dd 包文件名本身!'

20230618 更新:

论原生启动网络启动文件,按自动应答制定的策略安装,和启动中介系统 dd 安装的区别,就和宝塔面板里安装 Nginx PHP 等“编译安装”和“快速安装”的区别差不多,前者是原生安装,后者是将打包好的系统直接 dd 解压到目标硬盘,在有可能的情况下,我还是坚持使用“编译安装”的思路,因为这种安装方式会经历安装程序对系统环境进行一个详尽检测的过程,它会对目标机器的硬件是否满足运行要求,展开适合目标机器硬件的二进制代码/驱动进行检查和展开。相比于“一个文件包”走天下的 dd 式安装,肯定是优势更大的。

dd 安装的优势仅仅是“能在 1GB 机器上安装 CentOS”,so?红帽官方显然为不同版本的 Redhat 制定了安装的内存要求,这是官方在经过大量机器的适配和反馈后,获得的经验,你再懂不可能有人家懂,内存方面,红帽 7 要求至少 1.5GB,红帽 8 要求至少 2.5GB,红帽 9 改善了很多,降到了 2GB 就能安装,这还是官方明确规定推荐内存大小 3GB,我去掉了启动内核时的内存检查,经过实验获得的实际最小内存要求,硬要强行突破安装程序自己的内存检查流程,在不适合的配置上强行 dd 安装,我觉得这种做法带来的后果和风险是很大的,我见过太多建立机器时选择红帽 8+ 模板的 1GB 机器,用 dnf install 常用软件面临“process killed”的例子了。

红帽 Redhat 系统运行时/安装时(iso 安装或网络安装)对硬件的基础要求:

https://access.redhat.com/articles/rhel-limits

9

所以:内存不够就不要强装,不符合女神要求不要强舔,选择与自己硬件条件匹配的最重要,无论是搞技术,还是谈恋爱,都是这么个道理,人人都应该懂。

通过以上论述,我们已经知道了安装 Ubuntu 不得不用 dd 方式的原因,而且由于对使用 Ubuntu 有需求的朋友多,所以再麻烦也要把这个难题解决,而且由官方制作的 dd 包,肯定比我们个人开发者自己制作的兼容性要更好,且更新及时,有问题能够及时修复,我服务端已实现脚本自动打包,无需我本人亲自参与,除非甲骨文把我号删了,如果脚本所有支持安装的目标系统都采用 dd,那我需要面临的后果就是:

  1. 兼容性大大减弱;
  2. 自己要定期制作、适配、存储一大批 dd 包;
  3. 安装镜像源不再提供自选,只能连接有限的几个(甚至只有一个)目标服务器,一旦我本人无力维护 2 步骤中的制作,或服务器 down 掉,脚本相关功能也就整个废掉;
  4. 全世界各地镜像源的维护者们一定比我更专业、财力更高、服务也更稳定,而且能选择就近(服务器所在地)源,大大提高安装速度,我很清楚我自己能力的局限和边界,能采用镜像源原生安装,我就绝不自己提供相关 dd 镜像包服务。

很典型的案例就是 cxt,他声称制作了一大堆 dd 包,自己为维护这些 dd 包,要用百余个 GB 云空间来存储这些 dd 包,而且每当如果有新的 Linux 发新版发布,未来可能出现新的主板固件(BIOS UEFI 之外)等,自己又要制作、上传新的系统包,让服务器的存储、流量压力日渐庞大,这种工作我的评价是:只有苦劳,没有功劳。把自己累死,也不一定获得什么好的成效,而且他那个网站,每十分钟甚至九分钟的时候,不是处于连接速度过慢,就是宕机的情况,github 上只有主程序一个代码空壳,所谓的“全球 CDN 加速”,仅仅是下载主程序时可以选一个更快的节点,一旦他维护的存储 dd 镜像的服务器炸了,整个脚本功能就废掉,号称支持再多,最后也只会变成 0,这种把鸡蛋都装在一个篮子里的思维无疑是值得商榷的。

10

前段时间隔壁有个坛友给我发了个私信,他说 Ubuntu 在今年(2023)2 月份的时候,鼓捣出来一个新型 netboot 方式,看看能不能重启以前加载最小内核,启动网络安装那一套。

11

https://lists.ubuntu.com/archives/ubuntu-devel/2023-February/042490.html

https://github.com/canonical/mini-iso-tools

我经过确认,相关项目已经更新到 Ubuntu 23.04 (Lunar Lobster) 分支:

https://releases.ubuntu.com/lunar/

12

我知道你很激动,但你先不要激动,因为我在激动完,测试了一下这个“netboot”文件后,对其评价是“依托答辩”,理由如下:

  1. 仅支持 amd64,不见 arm64 踪影,为保持不同架构 CPU 安装过程的一致性,没有经过全架构适配的 netboot 模式我是不会采取选择的;
    https://releases.ubuntu.com/lunar/netboot/
    13
  2. 我下载了该页面中的 initrd 和 linux 内核,并尝试写入 grub 引导菜单启动,但效果还是极其差强人意,它竟然要把整个安装的 iso 镜像加载到内存里,然后完成后续安装,拜托 Ubuntu 22.04 的 iso 镜像尺寸有 1.83G 诶,要有容纳这么大一个文件的内存空间,你这么搞机器没 4GB 都不够装的,得有多少小内存机器被拒之门外。
    14
    grub 引导选项,写在文件 /etc/grub.d/40_custom 中,重启前记得 grub-reboot "Install Ubuntu" 让这个新菜单启动:
    15
    启动后机器会疯狂地寻找并读取光盘设备内容 sr0(实验机型 Racknerd 的 sr0 光盘是空的,且 web 面板无法选择挂载远程或自己上传的 iso 镜像,只是系统里多了这么一个虚拟设备),然后尝试很久后,再弹出对话框确认是否配置网络、镜像源链接等:
    16
    17
    找不到本地光盘,开始跟你对话是否加载网络 iso,经过一次次回车(dhcp 还好办,如果是静态配置 IPv4/IPv6 网络,还得手输 IP 掩码 网关等静态网络参数),好家伙,开始往内存里写 iso 了,这得有多大内存才够你造的,即使我这台实验机型内存 4GB,也不能完整跑完 iso 镜像加载到内存中的流程,加载到 1422M 左右就提示“No space left on device”,低于这个内存量的机器更不可能满足它,照这么看,得 8GB 内存才能顺利完成 iso 加载到内存里的步骤,啥大户人家啊,能用得起 8GB 内存的大鸡鸡,除了白嫖的甲骨文 ARM。

后面的实验流程就不继续了,也无法继续,反正总体给人的感觉就是恶心他妈给恶心开门,恶心到家了:
18
19
3. 配置这种 netboot 内核启动方式可参考的文档和经验为 0,连 4GB 内存都不够加载就够要命的了,要是再出什么其他问题,根本没有办法调试。
4. Ubuntu 开发者们的脑子里有个弯还是没绕过来,他们还是坚持认为,所有人都应该下载一个硕大的 iso 文件,然后拥有写入 U 盘或光盘或内存,插到电脑上安装的条件,或者是像云服务商那样,可以随时给机器 dd,完全不考虑用一个精简内核启动,然后完成读取 cloud init 配置文件完成后续的安装过程。

所以综合以上论述,我还是决定采用服务端制作从官方同步的 Ubuntu cloud images,客户机采用从 AlpineLinux 中介系统启动 dd,重启后机器根据 dd 时埋入的 cloud init 配置完成正确的配置流程,是能够在“减少我个人维护 dd 包制作繁琐程度”和“原生初始化系统”之间,取得一个良好平衡的方案。

寻找并确定了当前最适合的解决方案后,Ubuntu netboot 更新与否我就不太关心了,如果你在未来哪天,发现 Ubuntu netboot 已经不再采用以上蛋疼的方式安装系统,回到 Debian 那种最小化内核启动,读取 cloud init 就能完成安装的方式,请立即联系我,我也会在后续迅速跟进并更新相关安装方案。

123
  • 好文,太硬了 xhj003

  • 好贴,顶

  • 感谢楼主!
    刚好你提到宝塔的编译安装和快速安装。我不知道有强迫症还是怎样,使用宝塔建站以来,任何组建一直都是选择编译安装,虽然大部分时候装个mysql都得几十分钟,但我就觉得选择编译安装会更稳定。偶尔有那么一两次试了一下快速安装,好像还出现奇怪的问题。
    请教楼主这两者在稳定性上的差别大吗,比如PHP8和mysql8两者编译安装和快速安装使用起来是否真的无差别。

  • @yahuisme #3 是的,编译安装更加稳定,消耗的时间也更久,快速安装消耗的时间更短,但有可能面临某些二进制文件不兼容当前机器的情况

  • 加鸡腿 xhj003

  • 可以

  • 加个鸡腿 ac09

  • @yahuisme #3 两者各有优劣,编译安装兼容性更好,速度慢,快速安装兼容性稍差,速度快,两者好处无法兼得,劣势无法一起丢弃,一般来说安装应用软件层面,如果快速安装无明显 bug,还是推荐快速安装,这和操作系统层面的安装还是不同的

  • 卧槽这是啥,论文?

  • @leitbogioro #8 感谢解答!

123

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有15467位seeker

🎉欢迎新用户🎉