Alpine Linux 是我们提供的最小的镜像。在我们的服务器上,全新的 Alpine 3.21 空闲时占用 55 MB 内存和 153 MB 磁盘,而 Ubuntu 24.04 是 313 MB 和 2.7 GB。代价是放弃熟悉的工具。Alpine 用 apk 代替 apt,用 doas 代替 sudo,用 OpenRC 代替 systemd,用 musl 代替 glibc。
下面的步骤和在 Debian 或 Ubuntu 上的前十分钟相同,只是换成了 Alpine 的做法。发布前,我们在自己的 Alpine 3.21 镜像上运行了每一条命令,其中两条第一次就让我们栽了跟头。用控制面板里的密码以 root 登录。
1. 更新,然后重启一次
apk update && apk upgrade
uname -r
ls /lib/modules
服务器在第一次启动时已经安装了可用的更新,所以 apk upgrade 通常没剩多少事可做。重点看最后两行。如果那次首启更新带来了新内核,正在运行的内核(uname -r)在磁盘上就已经没有对应的模块了。任何需要加载模块的操作都会失败,直到重启为止。在我们的测试服务器上,安装防火墙时输出了 ERROR: Couldn't determine iptables version。如果两个版本不一致,现在就重启:
reboot
我们的测试服务器不到一分钟就恢复了。
2. 创建一个能登录的用户
adduser -D deploy
passwd deploy
adduser deploy wheel
不要跳过 passwd。adduser -D 创建的账户没有密码,Alpine 会把它标记为锁定,而 sshd 会拒绝锁定的账户,即使密钥正确。在我们的测试里,唯一的线索是 /var/log/messages 中的这一行:
User deploy not allowed because account is locked
密码之后也有用:doas 要的就是它。
如果下单时添加了 SSH 密钥,它就在 root 的 authorized_keys 里。把它复制给新用户:
install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
install -m 600 -o deploy -g deploy /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
还没有密钥?在自己的电脑上生成一个,把公钥部分粘贴到 /home/deploy/.ssh/authorized_keys。
3. 让 doas 要求输入密码
Alpine 用 doas 代替 sudo,我们的镜像已经允许 wheel 组使用它。/etc/doas.d/wheel.conf 里的规则是 permit nopass :wheel,也就是说 wheel 组的任何成员不用输入任何东西就能变成 root。改成要求输入密码,并在几分钟内记住它:
echo 'permit persist :wheel' > /etc/doas.d/wheel.conf
从现在起,doas apk add nano 的用法就和 Ubuntu 上的 sudo apt install nano 一样。
4. SSH:只用密钥,禁止 root
sshd 按文件名顺序读取 /etc/ssh/sshd_config.d/ 里的文件,每项设置取它找到的第一个值。在我们的镜像里,第一个文件是 00-eqvps.conf,它允许 root 和密码登录,方便你第一天进入服务器。替换它的内容:
cat > /etc/ssh/sshd_config.d/00-eqvps.conf <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
EOF
sshd -t && rc-service sshd restart
sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication) '
第三行在 Alpine 上很关键。镜像用的是带 PAM 的 OpenSSH 版本,只写前两行时,我们的测试服务器仍然提供 keyboard-interactive 登录。三行都写上后,不带密钥的登录尝试会得到:
Permission denied (publickey).
保持 root 会话不要关闭。在第二个终端里确认 ssh deploy@YOUR.SERVER.IP 能登录(NAT 套餐要加上 -p 和控制面板里的专属端口),并确认 doas id 接受密码。
5. 防火墙:ufw
ufw 在 community 仓库里,我们的镜像已经启用了这个仓库:
apk add ufw
ufw limit 22/tcp
ufw --force enable
rc-update add ufw default
ufw status
limit 放行 SSH,但会拖慢连续打开大量连接的一方。这里 rc-update 很重要,因为如果不把防火墙加入 runlevel,OpenRC 重启后不会启动它。重启后,我们的服务器回来时显示 Status: active。在 NAT 套餐上,让 SSH 留在 22 端口:你的专属外部端口会转发到服务器内部的 22。在带独立 IPv4 的套餐上,需要时再开放网页端口(ufw allow 80/tcp、ufw allow 443/tcp)。其余内容见 ufw 指南。
6. 开启 cron 和每日更新
全新的 Alpine 服务器根本没有运行 cron。把它打开:
rc-update add crond default
rc-service crond start
Alpine 中 root 的 crontab 已经会在每天 02:00 运行 /etc/periodic/daily 里的所有内容。把更新脚本放进去:
cat > /etc/periodic/daily/apk-upgrade <<'EOF'
#!/bin/sh
apk upgrade --update-cache --no-progress >> /var/log/apk-auto.log 2>&1
EOF
chmod +x /etc/periodic/daily/apk-upgrade
run-parts --test /etc/periodic/daily
最后一条命令会列出 cron 将要运行的脚本,你的脚本应该在列表里。这样你就能持续获得 3.21 的修复。升级到下一个 Alpine 版本是另一个需要手动完成的步骤。内核更新仍然需要重启:时不时重复一下第 1 步里的 uname -r / ls /lib/modules 检查。
7. 一张表看懂 OpenRC
如果你熟悉 systemd,变化大致就是这些:
| 任务 | systemd | Alpine (OpenRC) |
|---|---|---|
| 启动服务 | systemctl start nginx | rc-service nginx start |
| 开机自启 | systemctl enable nginx | rc-update add nginx default |
| 状态 | systemctl status nginx | rc-service nginx status |
| 正在运行的服务 | systemctl list-units | rc-status |
| 系统日志 | journalctl | /var/log/messages |
8. Docker,以及 ufw 挡不住的端口
Alpine 很适合做 Docker 主机,因为系统本身几乎不碍事:
apk add docker docker-cli-compose
rc-update add docker default
rc-service docker start
docker run --rm hello-world
在我们的服务器上,包括下载在内用了 12 秒。Docker 守护进程运行后,已用内存从 57 MB 增加到 124 MB。
有一个陷阱并不只存在于 Alpine:Docker 会写入自己的防火墙规则。在 ufw 只放行 SSH 的情况下,我们用 -p 8080:80 启动了 nginx,结果从服务器外部通过 8080 端口访问到了它。ufw 没有拦住。如果一个容器只应通过同一台服务器上的反向代理访问,就把它发布到 localhost:
docker run -d -p 127.0.0.1:8080:80 nginx:alpine
选哪个套餐
Alpine 几乎把全部内存都留给你自己的软件。Nano 套餐(2 vCPU、1 GB、15 GB,$3/月)足够跑一个小服务或几个容器。这是 NAT 套餐,SSH 通过专属端口进入。如果需要向互联网开放 80 和 443 端口,Nano-IP 带独立 IPv4,$8/月。较重的 Docker 栈可以选 Micro,2 GB,$5/月。
Alpine 3.21 在 Alpine VPS 页面上,所有 VPS Linux 套餐都可选。支付方式为 Base、Ethereum 或 Polygon 网络上的 USDT、USDC 或 ETH,无需证件,也无需银行卡。
评论
暂无评论。来做第一个吧。