在 Linode 上部署 Guix 镜像
摘要
本文提供了一篇技术指南,关于直接在 Linode 云服务器上部署 Guix 系统镜像,包括用于系统配置的代码片段。
<p><a href="https://lobste.rs/s/mf2lic/deploying_guix_images_on_linode">评论</a></p>
查看缓存全文
缓存时间: 2026/09/27 01:31
# 在Linode上部署Guix系统镜像 — dthompson
来源:https://dthompson.us/posts/deploying-guix-images-on-linode.html
今日,我将从Digital Ocean迁移至Linode(现为Akamai Cloud,但我绝不会*真的*用这个名字称呼它),原因之一是Digital Ocean最近向Omarchy(https://www.digitalocean.com/blog/digitalocean-joins-omacom-foundation)捐赠了300万美元——这是一个为法西斯主义者准备的劣质发行版。为自由开源软件开发筹集资金本就艰难,因此当某个*甚至不再编写代码的人*(https://jardo.dev/what-about-rails)能轻松获得巨额资金,而诚实的项目却只能争夺小型资助机构那点微薄经费时,这种现象确实*颇具讽刺意味*。不过暂且不谈这些!
Guix手册中有一些关于在Linode上运行Guix的文档(https://guix.gnu.org/cookbook/en/html_node/Running-Guix-on-a-Linode-Server.html)。其方法是从Linode内置的Debian(安息吧(https://toot.cat/@dthompson/117322463609022181))镜像开始,然后将其转换为Guix系统。作为前运维人员,我对此方案并不满意。我真正希望的是直接上传一个可直接用于Linode的Guix镜像。长话短说:我找到了解决方案。
我的Linode磁盘镜像实现了以下功能:
- 允许在初始内存盘中使用虚拟磁盘设备
- 为根目录 `/` 和交换空间挂载正确的设备
- 在启动时调整根文件系统大小以使用Linode底层卷的全部空间(需要自定义服务实现,因为Guix自带的`resize-file-system-service`在此场景下无法工作)
- 启动能识别我公钥的SSH服务器
- 启用免密码`sudo`以便后续使用`guix deploy`
- 将引导文件安装到Linode能识别的位置
- 除IPv4地址外,通过DHCP获取IPv6地址
以下是最终实现的代码:
```scheme
(define-module (dthompson linode)
#:use-module (gnu)
#:use-module (gnu packages linux)
#:use-module (gnu packages ssh)
#:use-module (gnu services)
#:use-module (gnu services admin)
#:use-module (gnu services networking)
#:use-module (gnu services shepherd)
#:use-module (gnu services ssh)
#:use-module (gnu system linux-initrd)
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (guix profiles)
#:use-module (nongnu packages linux)
#:export (%linode-base-services
linode-base-os))
(define ssh-authorized-keys
`(("dave" ,(local-file "../keys/dave.pub"))))
(define guix-signing-keys
(list (local-file "../keys/signing-key.pub")))
;; 理想情况下我们应直接使用Guix的resize-file-system-service,
;; 但其预设假设与Linode环境不兼容。
(define (resize2fs-shepherd-service device)
(list (shepherd-service
(provision '(resize2fs))
(requirement '(user-processes))
(one-shot? #f)
(respawn? #f)
(start (with-imported-modules (source-module-closure
'((guix build utils)))
#~(lambda _
(invoke #$(file-append e2fsprogs "/sbin/resize2fs")
#$device))))
(documentation "启动时调整ext文件系统大小。"))))
(define resize2fs-service-type
(service-type
(name 'resize2fs)
(extensions
(list (service-extension shepherd-root-service-type
resize2fs-shepherd-service)))
(default-value #f)
(description "启动时调整ext文件系统大小")))
(define %linode-base-services
(cons*
(service dhcpcd-service-type
(dhcpcd-configuration
;; Linode服务器通过SLAAC获取IPv6地址,
;; 默认"private"设置无法正常工作。
(slaac "hwaddr")))
;; 仅通过公私钥对访问SSH。
(service openssh-service-type
(openssh-configuration
(password-authentication? #f)
(authorized-keys ssh-authorized-keys)))
;; 自动调整根文件系统大小以利用全部分配空间。
(service resize2fs-service-type "/dev/sda")
;; 防火墙默认阻止几乎所有连接。
(service nftables-service-type)
(modify-services %base-services
;; 允许其他Guix机器通过'guix deploy'推送存储项。
(guix-service-type config =>
(guix-configuration
(inherit config)
(authorized-keys
(append guix-signing-keys
%default-authorized-guix-keys)))))))
(define linode-base-os
(operating-system
(locale "en_US.utf8")
(timezone "America/New_York")
(host-name "linode")
(users
(cons (user-account
(name "dave")
(comment "David Thompson")
(group "users")
(home-directory "/home/dave")
(supplementary-groups '("wheel")))
%base-user-accounts))
(sudoers-file
(plain-file "sudoers"
(string-append (plain-file-content %sudoers-specification)
;; 'guix deploy'需要免密码sudo权限。
"%wheel ALL=NOPASSWD: ALL\n")))
(packages (cons openssh %base-packages))
(services %linode-base-services)
;; 使用虚拟磁盘设备需要virtio_scsi模块。
(initrd-modules (append '("virtio_scsi") (base-initrd-modules linux)))
;; Guix默认在启动设备上运行grub-install的做法
;; 在Linode环境中不适用。我们采用Guix处理磁盘镜像的方式:
;; 将字体和GRUB模块安装到根文件系统。
(bootloader
(bootloader-configuration
(bootloader
(bootloader
(inherit grub-bootloader)
(installer
#~(lambda (bootloader device mount-point)
(let* ((install-dir (string-append mount-point "/boot"))
(fonts (string-append install-dir "/grub/fonts")))
(mkdir-p fonts)
(copy-file (string-append bootloader "/share/grub/unicode.pf2")
(string-append fonts "/unicode.pf2"))
(copy-recursively (string-append bootloader "/lib/")
install-dir))))))
(targets '("/dev/sda"))))
(file-systems
(cons (file-system
(device "/dev/sda")
(mount-point "/")
(type "ext4"))
%base-file-systems))
(swap-devices (list (swap-space (target "/dev/sdb"))))))
;; 允许使用'guix system image'构建初始磁盘镜像。
(when (batch-mode?) linode-base-os)
```
此代码也可在此Git仓库(https://git.dthompson.us/guix-config/tree/dthompson/linode.scm)中找到。
现在需要将此操作系统配置转换为可用的磁盘镜像。Guix可以使用`mbr-raw`镜像类型生成与Linode兼容的镜像。上传前需对Linode镜像进行gzip压缩。我编写了如下处理脚本:
```sh
#!/bin/sh
set -e
# 首先需要用Guix生成可上传至Linode的原始磁盘镜像。
# Guix的'mbr-raw'镜像类型基本满足需求,但不完全匹配。
# 如名称所示,这类镜像包含主引导记录(MBR),
# 并将GRUB安装在MBR后间隙中。经过反复测试,
# 我们发现Linode需要的是*仅*包含根分区的镜像。
image=$(guix system image -L . --image-type=mbr-raw dthompson/linode.scm)
# Guix将根分区放置在磁盘起始位置偏移1048576字节处,
# 因此生成最终镜像时需要跳过这部分数据。
#
# 为提升操作速度,我们选择128K作为'dd'的块大小(而非默认512字节)。
# 8个128K块=1048576字节,因此使用'skip=8'参数。
#
# Linode还要求上传的镜像必须经过gzip压缩。
dd if="$image" bs=128K skip=8 conv=sync,noerror | gzip -c > linode-base.gz
```
创建并上传磁盘镜像后,我可以使用它启动新的Linode实例。*但是*,Linode的默认配置文件不适用,新实例会直接内核恐慌。首先我关闭服务器,然后编辑其配置文件并进行以下修改:
- 在"启动设置"下打开"选择内核"下拉菜单,选择"GRUB 2"
- 关闭*所有*文件系统/启动辅助开关
保存后重启服务器。至此我可以通过SSH使用`guix deploy`进行所有系统更新。大功告成!
或许应该将此改进流程更新到Guix手册中,但我目前无暇顾及,希望这篇博客能暂时提供帮助。
相似文章
Guix: 从二进制创建包
使用copy-build-system从二进制创建Guix包的指南,解释如何设置安装计划。
切换到 GNU Guix:初学者的视角
一位初学者分享了从 Arch Linux 切换到 GNU Guix 的经验,讨论了声明式系统管理的好处、与 NixOS 的比较以及所面临的挑战。
从零开始编写Guix服务:初学者教程
本文提供了一份面向初学者的指南,教您如何在Guix中编写自定义服务,以Caddy为例,并涵盖系统配置和Scheme编程基础。
Guix Nix 的怪异融合:在 Nix 中利用 Guix 派生
一项技术探索,展示了 Nix 如何构建 Guix 派生项,强调了共享底层“输入输出机”架构以及跨生态系统互操作的可能性。
我喜欢的 NixOS 声明式安装方式
一份关于使用 nixos-anywhere 等工具通过网络声明式安装 NixOS 的指南,重点强调在版本控制下管理配置文件。