在阿里云低配置服务器上安装 Alpine Linux

近期有几个服务器到期,又对国内低流量、大带宽的服务器有一定需求(主要是用于内网穿透),于是入手了阿里云的 2C0.5G T6 突发性能实例。综合下来五年仅需不到 200 元,性价比极高。

然而云服务的一大特点是「块存储」,也就是硬盘收费很贵,算下来硬盘价格比服务器本身还高。因此选用一个体积较小的操作系统就显得尤为重要。早在之前 LMS 就已介绍了如何自定义 Alpine Linux 镜像以超低价格购买该配置的实例。然而对我来说,还有几个需要考虑的地方:

  • 希望使用 LUKS 加密,以防止阿里云的「自动化扫描硬盘」
  • 出于个人便好,想要使用 btrfs 文件系统
  • 由于内存较小,因此使用 x86-32 而非 x86-64 架构以节省内存占用

同时,不同于该文章使用 Hyper-V 创建虚拟机的方式,本文使用了笔者更加熟悉的 Proxmox VE。

本文所涉 Alpine Linux 版本为 3.21,未来更新版本的配置方式可能有所区别,敬请读者在操作时注意。

分区

使用 Proxmox VE 获取镜像及创建虚拟机的过程在此不再赘述,由于 32 位系统默认不支持 UEFI,因此请选择 BIOS 引导类型。

笔者建议,在开始安装 Alpine Linux 之前,使用其他发行版的镜像(例如「Arch Linux 安装介质」或 SystemRescue)先行完成分区工作。

注意

这里需要注意的是,和其他发行版不同,Alpine Linux 默认的 syslinux 引导程序只支持从 512B 处读取 /boot 分区。因此,无法使用 cfdisk、fdisk 工具进行分区(因为其默认会进行 2048B 对齐)。为了指定准确的分区开始位置,建议使用 parted 工具进行分区。

为了获得更好的性能,笔者依然在划分主分区时进行了对齐,即在 /dev/vda1/dev/vda2 之间留下部分间隙,/dev/vda1 使用 parted 分区设置的起始位置为 512B、不对齐到 2048B;/dev/vda2 使用 cfdisk 分区工具对齐到 2048B。

然而,由于未知的 bug,cfdisk 写入分区表不会被 Alpine Linux 安装介质的内核重新加载,因此建议在其他操作系统完成分区后,再使用 Alpine Linux 安装介质进行格式化和安装。

1
2
3
4
5
6
parted -sa optimal /dev/vda mklabel msdos
parted -sa optimal /dev/vda mkpart primary 512B 128MB
parted -sa optimal /dev/sda set 1 boot

# 在 cfdisk 界面中创建第二个分区
cfdisk /dev/vda

注意

GRUB 可能并不需要为第一个分区设置 bootable flag,但是 Alpine Linux 在使用 syslinux 引导程序的情况下,为 /boot 分区设置 bootable flag 是必要的。否则,Alpine Linux 将无法引导。

提示

尽管 50MB 已足够 Alpine Linux 的 /boot 分区使用,但在默认情况下,btrfs 需要大于 128MB 的空间才能格式化。因此,笔者将 /boot 分区设置为 128MB。

安装和格式化

在分区完成后,使用 Alpine Linux 安装介质引导系统。进入安装界面后,使用 setup-alpine 命令进行安装。

1
setup-alpine

安装过程在此不再赘述。在完成镜像设置之后、选择安装磁盘前,使用 Ctrl+C 退出安装程序

首先安装需要的软件包:

1
apk add --no-cache cfdisk parted e2fsprogs btrfs-progs cryptsetup lsblk

然后对之前的分区进行格式化:

1
2
mkfs.btrfs -L alpine-boot /dev/vda1
cryptsetup luksFormat /dev/vda2

请在提示时输入 YES (必须为大写字母) 以确认格式化。接下来,使用 cryptsetup luksOpen 命令打开加密分区,并完成后续操作:

1
2
cryptsetup luksOpen /dev/vda2 root
mkfs.btrfs -L alpine-root /dev/mapper/root

由于 Alpine Linux 作为最小化操作系统,并不总是加载所有内核模块,因此如果直接进行分区挂载,会显示 Invalid argument 错误。因此,我们需要手动加载内核模块:

1
2
modprobe btrfs
modprobe ext4

然后挂载分区:

1
2
3
mount /dev/mapper/root /mnt
mkdir /mnt/boot
mount /dev/vda1 /mnt/boot

完成后,使用 lsblk 命令检查分区是否正确挂载。

如果一切正常,继续安装 Alpine Linux:

1
setup-disk -m sys /mnt

如果一切正常,重启后将进入引导界面,输入 LUKS 密码后可以正常进入系统。

镜像导出 Proxmox VE

在 Proxmox VE 关机的情况下,使用 qemu-img 命令将虚拟机的磁盘导出为 qcow2 格式:

1
qemu-img convert /data/images/<vmid>/vm-<vmid>-disk-0/disk.raw ~/alpine.qcow2 -O qcow2 -f raw

其中:

  • /data/images 为 Proxmox VE 中设置的 VM 磁盘存储路径
  • <vmid> 为虚拟机 ID
  • 假设磁盘 ID 为 0

完成后,通过 scp 或其他终端软件集成的文件管理器将 alpine.qcow2 文件下载到本地。

镜像导入阿里云

在计划创建服务器的同一地域(即,如果打算在杭州创建服务器,那么就需要在杭州地域创建一个 OSS)创建一个 OSS,名称随意,访问权限可以为私有。

将本地的 alpine.qcow2 文件上传到 OSS 中。在阿里云的镜像导入界面,选择 「导入镜像」

  • 镜像文件 URL:填写 OSS 中的文件 URL,有无签名均可
  • 镜像名称:根据自己需要填写
  • 操作系统类型:Linux
  • 操作系统版本:Customized Linux
  • 系统架构:32 位
  • 启动模式:BIOS
  • 镜像格式:qcow2

此处需要 勾选「配置云盘属性」,并设置云盘大小为 1GB 或 2GB(根据实际需要)。

注意

阿里云默认导入的镜像大小为 20GB,因此如果不勾选「配置云盘属性」,会导致创建服务器时无法将系统盘设置得更小。

完成导入后,创建服务器的过程不再赘述。

创建完成后,可以使用阿里云自带的 Web VNC 连接到服务器,以输入 LUKS 密码并登录。

SSH 安全配置

注意

由于 SSH 尚未配置,本节内容在 VNC 界面中完成。

由于安装 Alpine 时选择了 OpenSSH,因此其他发行版的 OpenSSH 配置可以通用。

1
2
3
4
5
6
sudo sed -i "/^#HostKey/s/^#//g"                                           /etc/ssh/sshd_config
sudo sed -i "/^#ListenAddress 0.0.0.0/s/^#//g" /etc/ssh/sshd_config
sudo sed -i "/^#PubkeyAuthentication yes/s/^#//g" /etc/ssh/sshd_config
sudo sed -i "s/^#PasswordAuthentication yes$/PasswordAuthentication no/g" /etc/ssh/sshd_config
sudo sed -i "s/^#PermitRootLogin yes$/PermitRootLogin prohibit-password/g" /etc/ssh/sshd_config
sudo sed -i "s/^#PermitRootLogin no$/PermitRootLogin prohibit-password/g" /etc/ssh/sshd_config

然后,重启 SSH 服务:

1
sudo rc-service sshd restart

配置 SSH 安全密钥:

1
2
mkdir -p ~/.ssh
echo "<YOUR_PUBLIC_KEY>" >> ~/.ssh/authorized_keys

如果使用阿里云,可以使用 VNC 控制台的「黏贴命令」来黏贴你的 SSH 公钥。

如果配置正常,至此,即可使用 SSH 连接服务器公网 IP。

如果无法连接,请检查云服务是否分配公网 IP 及安全组、防火墙等设置。

更换镜像源

注意

此节及之后的内容在 SSH 界面中完成,您需要以 root 身份登录 SSH。

修改 /etc/apk/repositories 文件,将 dl-cdn.alpinelinux.org 替换为想要的源地址,视情况可取消 Community 源的注释。

1
2
https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/main
https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/community

如果使用阿里云,可以使用内网源:

1
2
http://mirrors.cloud.aliyuncs.com/alpine/v3.21/main
http://mirrors.cloud.aliyuncs.com/alpine/v3.21/community

vi = vim,以及不得不装的工具

安装 vim:

1
2
3
apk add vim sudo curl
rm /bin/vi
ln -s /usr/bin/vim /bin/vi

修改默认 shell 为 bash

安装 bash:

1
2
apk update
apk add bash bash-completion shadow

修改默认 shell:

1
chsh -s /bin/bash

有强迫症可以清除 ash 的历史记录:

1
rm -v ~/.ash_history

配置 LUKS 自动解锁

会降低安全性,但是对于服务器来说,重启可以自动化,无需人工干预输入密码。是否进行这一步骤,请慎重考虑。

1
2
3
4
5
dd if=/dev/urandom of=/crypto_keyfile.bin bs=1024 count=4
chmod 000 /crypto_keyfile.bin
cryptsetup luksAddKey \
--pbkdf pbkdf2 --pbkdf-memory 25600 \
<luks-device> /crypto_keyfile.bin

其中,<luks-device> 是加密分区的设备名,例如 /dev/sda2

如果服务器内存很小(小于 1GB),可以使用 --pbkdf pbkdf2 --pbkdf-memory 25600 来换用内存更少的 PBKDF2 算,并限制内存使用量为 25600 KiB。

如果服务器内存较大,可以去掉 --pbkdf--pbkdf-memory 参数,使用更加安全的 Argon2 算法。

使用下面的命令确认是否成功,如果成功,则可以看见新增的 Key Slot(一般为 Slot 1):

1
cryptsetup luksDump <luks-device>

然后,修改 /etc/mkinitfs/mkinitfs.conf 文件,在 features 中添加 cryptkeybtrfs (由于我的 /boot 分区使用了 btrfs,因此需要添加这个选项)。

1
2
3
# /etc/mkinitfs/mkinitfs.conf
# 这是我的配置文件,可能和你的不一样,请根据实际情况添加
features="ata base ide scsi usb virtio ext4 btrfs cryptsetup cryptkey keymap"

更新 initramfs:

1
mkinitfs

此外,还需要修改 /etc/update-extlinux.conf 文件:

  • modules 中添加 btrfs(如果你的 /boot 分区使用了 btrfs,如果没有使用 btrfs,则不需要添加这个选项)
  • default_kernel_opts 中添加 cryptkey
1
2
3
4
5
6
7
8
9
10
# /etc/update-extlinux.conf
# 这是我的配置文件,可能和你的不一样,请根据实际情况添加(至少 UUID 是肯定不一样的!)

# default_kernel_opts
# default kernel options
default_kernel_opts="cryptroot=UUID=6dc7d6d2-8677-41d5-b38d-acc582080c8e cryptkey cryptdm=root quiet rootfstype=ext4"

# modules
# modules which should be loaded before pivot_root
modules=sd-mod,usb-storage,ext4,btrfs

根据我的个人习惯,另外的,我还会修改以下选项:

  • verbose 选项设置为 1,这样执行 update-extlinux 时会显示详细的执行过程
  • 去掉 default_kernel_opts 中的 quiet 选项,这样可以在启动时看到详细的启动过程

然后,更新 extlinux:

1
update-extlinux

重启服务器,如果没有配置错误,此时可以看到服务器自动解锁 LUKS 分区,进入系统。

最后,如果重启成功,可以删除原有的 LUKS 密码,以增加安全性:

1
2
3
cryptsetup luksKillSlot \
--key-file /crypto_keyfile.bin \
<luks-device> 0

用户默认的 dotfiles

使用 /etc/skel 目录中的文件作为新用户的默认配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
mkdir -p /etc/skel

cat <<EOF > /etc/skel/.vimrc
syntax on
filetype on
set showmatch
set ruler
set incsearch
set mouse=
set backspace=indent,eol,start
EOF

cat <<EOF > /etc/skel/.bashrc
alias ls='ls --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
alias dir='dir --color=auto'
alias vdir='vdir --color=auto'
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

export PS1="[\[\e[01;34m\]\u\[\e[0m\]@\[\e[01;31m\]\H \[\e[01;33m\]\w\[\e[0m\]] "
export EDITOR=vim
EOF

cat <<EOF > /etc/skel/.bash_profile
if [ -f $HOME/.bashrc ]; then
. $HOME/.bashrc
fi
EOF

cp /etc/skel/.* ~

配置开机后及 TTY 登出时清屏

在 Debian 上,计算机开机之后及在 TTY 登出时,都会自动清屏。要在 Alpine 上实现这个行为,需要修改 /etc/issue 文件。

1
2
3
4
clear > issue
cat /etc/issue >> issue
cat issue > /etc/issue
rm -iv issue

可以通过 vim 打开 /etc/issue 文件,查看是否有控制字符,来确定是否成功。

修改 MOTD

1
2
3
4
. /etc/os-release
echo -e "Welcome to \e[34m${PRETTY_NAME}\e[0m" >> /etc/motd
# 下面这行只对阿里云有效
echo -e "Public IP: \e[36m$(curl -sf --connect-timeout 5 http://100.100.100.200/latest/meta-data/eipv4)\e[0m" >> /etc/motd

调整内核参数

1
2
3
4
5
6
echo 'net.core.default_qdisc = fq'           | tee -a /etc/sysctl.d/10-tcp-bbr.conf
echo 'net.ipv4.tcp_congestion_control = bbr' | tee -a /etc/sysctl.d/10-tcp-bbr.conf

echo 'net.ipv6.conf.default.disable_ipv6 = 1' | tee -a /etc/sysctl.d/20-disable-ipv6.conf
echo 'net.ipv6.conf.all.disable_ipv6 = 1' | tee -a /etc/sysctl.d/20-disable-ipv6.conf
echo 'net.ipv6.conf.lo.disable_ipv6 = 1' | tee -a /etc/sysctl.d/20-disable-ipv6.conf

另外可以尝试 https://omnitt.com/ 的 TCP 调参算法,但是我没有尝试过。

添加 swap 和 zram

安装 zram-init:

1
apk add zram-init

编辑 /etc/conf.d/zram-init 文件,设置 zram 的大小:

1
2
num_devices=1  # zram 设备数量
size0=128 # 单位为MB,配置为物理 RAM 的一半

然后启动 zram-init:

1
rc-service zram-init start

然后配置 swap:

1
2
3
4
dd if=/dev/zero of=/swapfile bs=1M count=512
chmod 000 /swapfile
mkswap -L alpine-swap /swapfile
swapon /swapfile

然后,编辑 /etc/fstab 文件,添加 swap:

1
echo -e "/swapfile\tswap\tswap\tdefaults\t0 0" | tee -a /etc/fstab

如果有需要,也可以删除 fstab 内无用的 USB 和 CD-ROM 的挂载项。

配置 NTP

创建 /etc/periodic/hourly/10-timesync-ntpd 文件:

1
2
#!/bin/sh
ntpd -d -q -n -p ntp.cloud.aliyuncs.com

该 NTP 服务器为阿里云内网 NTP 服务器,如果你在其他云服务商上,请自行寻找合适的 NTP 服务器。

然后,给文件添加可执行权限:

1
chmod +x /etc/periodic/hourly/10-timesync-ntpd