从零开始打造你的专属 NAS

Fedora CoreOS + Cockpit = 完美的 NAS

19 min read

最近淘汰下来一个 NUC,本着不浪费的原则,决定搞一台 NAS 玩玩。

既然要玩就玩个大的!黑群晖因为精神洁癖首先排除。至于什么 TrueNAS、OMV… 归根结底,他们大多都是方案的整合商 (TrueNAS 团队确实对 OpenZFS 项目做了不少的贡献),就像 Ubuntu 之于 Debian;踩在巨人的肩膀上,降低了上手的难度;并不是 Ubuntu 模式有什么不好,但相比于 Arch Linux 或 Gentoo Linux 始终觉得它少了一分纯粹,让人喜欢不起来…

既然大家都是方案的整合商,那为何不自己掌握自己的命运,自己来当这个整合商呢?

于是就有了今天的故事。

提出方案并论证可行性

我的方案总结想来就两个要点:

  1. 所以服务尽可能的放在容器中
  2. 系统层只读化、最小化和升级自动化

熟悉云原生的同学可能已经猜到了。没错,就是 Fedora CoreOS

Linux 系统运维从古至今有两个相互矛盾的追求:安全性稳定性。“世界上没有绝对安全的系统”,但凡是软件都可能会有 Bug;有 Bug 不可怕,打上补丁修复掉就可以了。这就要求我们使用的软件尽可能的保持最新。但这就破坏了稳定性;新更新的软件可能会与旧有的软件相互冲突,甚至破坏整个系统!因此,时常能在 Stack Overflow 看到 Arch Linux 用户抱怨自己又滚挂了 (我自己也遇到过)。

对于这个问题,红帽的 Fedora CoreOS 团队给出了他们的解法。这是我目前发现的,在安全性、稳定性以及可维护性之间平衡点最好的解决方案了!

在安全性方面,CoreOS 上来就是一个大招:只读系统。这简直是对安全性的降维打击!这意味着,即使黑客拿到了 Root 权限,也无法对系统进行更改,**因为整个系统都是只读的!**这同时也意味着用户无法随心所欲的安装软件,**因为整个系统都是只读的!**而作为替代,Fedora CoreOS 中默认便拥有 (由 Podman 支持的) 容器运行环境。将应用层的所以软件、服务、负载都打包成标准化的容器,这也是云原生所倡导的最佳实践。

有了镜像化的只读的系统层作为支持,CoreOS 可以执行比 Arch Linux 更激进的升级策略:不仅滚动升级,还要自动更新!(突然想起了某 M 姓咖喱味系统) 但与 Arch Linux 靠信仰升级不同的是,CoreOS 有镜像化系统层作为保障;由于系统镜像中安装的软件都是固定的,每次升级都是对整个系统镜像的升级。并且每一个稳定版本 (Stable Stream) 发布前,都会经过测试版本 (Testing Stream) 和不要命版本 (Next Stream) 的沉淀;所以很难出现软件 A 与软件 B 冲突的情况。就算出现了升级失败的情况,Fedora CoreOS 也可以无感知的回滚到升级前的版本。

准备 Ignition 文件

与别的 Linux 发行版不同的是,安装 Fedora CoreOS 前需要准备一个 Ignition点火 文件。

Ignition 是一个 JSON 文件,它被用于在初始配置中执行常见的磁盘任务,如分区、格式化、写入文件和配置用户。coreos-installer 将在 (且仅在) 第一次启动过程中,从安装介质或您指定的位置读取 Ignition 配置,并将配置应用到机器。相当于我们安装别的 Linux 时在引导程序中配置的内容提前写好,然后安装程序直接对着我们给的配置执行安装。

在初始配置过程中使用 Ignition 来执行常见磁盘任务,如分区、格式化、写入文件和配置用户。

安装 Butane

如果你是个狠人,你当然可以手写 Ignition;但了更好的可读性,我们一般是先将配置写入一个 YAML 文件,然后再用 Butane 将它转换成 Ignition 文件 (JSON)。

Mac 或 Linux 可以直接通过 Homebrew 或系统的包管理器安装,Windows 用户可以使用 Docker Desktop 安装容器版的 Butane:

# For Fedora User
sudo dnf install -y butane
# For Mac (Homebrew) User
brew install --formula butane
# Windows, Or you prefer to use containers
docker pull quay.io/coreos/butane:release

制作 Ignition 文件

然后我们参考着文档编写一份配置文件,这里我准备了一份示例:

variant: fcos
version: 1.4.0
passwd:
  users:
    - name: core # 你的用户名
      ssh_authorized_keys:
        - ssh-rsa AAAA...
      # 设置你的密码 (通过 mkpasswd)
      # See: https://docs.fedoraproject.org/en-US/fedora-coreos/authentication/#_using_password_authentication
      password_hash: $y$j9T$Fd0/xTD2lR9...
storage:
  files:
    # 设置主机名
    - path: /etc/hostname
      mode: 0644
      contents:
        inline: nas
    # Fedora CoreOS 默认禁用基于密码的 SSH 登录
    # 开启它
    # 这个文件必须排在 40-disable-passwords.conf 之前
    - path: /etc/ssh/sshd_config.d/20-enable-passwords.conf
      mode: 0644
      contents:
        inline: PasswordAuthentication yes
    # 允许用户 core 通过用户密码 (不是 root 密码) 使用 sudo
    - path: /etc/sudoers.d/coreos-core-user
      mode: 0440
      contents:
        inline: core ALL=(ALL) ALL
# ...

最后,用 Butane 将其转换成 Ignition 文件:

# Use the binary version of Butane
butane --pretty --strict nas.yml > nas.ign
# Use the container version of Butane
docker run --rm -v ${PWD}:/data -w /data \
    quay.io/coreos/butane:release --pretty --strict nas.yml > nas.ign

制作好 Ignition 文件 (nas.ign) 后,将它上传到 GitHub Gist。

或者在内网中起一个用于传输文件的 HTTP 服务器:

python -m http.server

安装 Fedora CoreOS

这次我们准备直接在裸机上安装。如果你喜欢垫一层 EXSi 的话,可以参考这篇文档

制作 Live CD

需要提前准备一个容量 >= 4GB 的 U 盘 (建议至少 8GB 以上)。

对于裸机安装,我们有 Live CD 和 PXE/iPXE 两种方案。这里我只介绍更加基础和通用的通过 Live CD 安装 Fedora CoreOS 的方法,对于 PXE/iPXE 有需求的同学可以看看这里

我们需要在这里下载 Live CD 的 ISO 文件:

download-livecd-iso

或者,通过 coreos-installer 下载:

# Use the binary version of coreos-installer
coreos-installer download -s stable -p metal -f iso
# Use the container version of coreos-installer
docker run --rm -v ${PWD}:/data -w /data \
    quay.io/coreos/coreos-installer:release download -s stable -p metal -f iso

如果你使用 Linux 或 Mac,可以直接使用系统自带的 dd 命令制作启动盘:

# 假设你的 U 盘挂载在 /dev/sda4
sudo dd if=${PWD}/fedora-coreos-*.iso of=/dev/sda4 bs=4M

如果你使用 Windows,推荐你使用 RufusDD Image 模式刻录。

安装系统

把刻录好的 Live CD (U 盘) 插到 NAS 上,上电开机,选择从 U 盘启动。

进入 Live CD 后,确认一下想要安装的硬盘在不在 /dev/ 中?然后直接开始安装:

# 假设你准备把系统安装到 /dev/sda 中
sudo coreos-installer install /dev/sda \
    --ignition-url https://gist.github.com/path/for/nas.ign

由于我们已经准备好了 Ignition,所以不需要像别的操作系一样通过引导程序配置计算机。直接 (通过网络) 把 Ignition 交给 coreos-installer 就可以了。

这里我使用的是 GitHub Gist 来传输 Ignition,大家如果没有这个网络条件的话用 GitHub、GitLab、Gitee,甚至本地局域网开 HTTP 服务器传文件也可以!只要能被 curl 下载的链接,coreos-installer 都能用。

实在是不行,作为万不得已的方案,还可以另外找个 U 盘把 Ignition 拷贝到服务器,然后在本地用 --ignition-file 把文件传递给 coreos-installer

安装 Cockpit 核心包

标准 Fedora CoreOS 镜像中不包含 Cockpit。

Fedora CoreOS 中默认不包含 Cockpit,修改系统又不被允许,要怎么安装 Cockpit 呢?确实,我们没有办法直接对运行中系统进行任何修改,RPM 和 YUM 也不存在,但不代表我们不能修改系统的镜像!

其实,Fedora CoreOS 中存在类似于「表」、「里」两套系统,「表」系统,及运行中的、只读的那套系统;而「里」系统就是「表」系统的镜像。所谓升级 (安装软件) 就是把新的软件安装在「里」系统中,然后再在重启的过程中将「里」系统挂载为「表」系统。没错,在 CoreOS 中升级 (安装) 了新软件后,需要重启才能生效 (如此一看更像那个咖喱味系统了 😓)!

我这里用了“系统镜像”这个词,但它由与传统的 ISO 镜像有所不同;它在镜像的内部是有分层的,整体呈现一种树形结构。也因此,红帽将这种「表」、「里」系统的技术称作 OSTree。按照官方的说法,这是一套“操作系统二进制文件的 Git”git for operating system binaries,它允许你对整个操作系统的镜像进行 Add、Commit 或 Checkout。甚至,你还可以对每一层 (refs) 进行追踪和校验 (黑客听了头皮发麻)。如果你对 OSTree 感兴趣,可以查看他们的官方文档

在 CoreOS 中,我们用 rpm-ostree 向「里」系统安装软件并制作新的系统镜像。

通过 rpm-ostree 安装 Cockpit 的核心包:

sudo rpm-ostree install cockpit-system

除此之外你还可以根据需求安装 cockpit-* 为名的别的软件包,它们为 Cockpit 提供拓展。例如,你如果想通过 Cockpit 管理 RQAID 磁盘阵列,就需要安装 cockpit-storaged (作为 NAS 肯定要安装这个)。

这里我简单整理了一下几个比较常用的 Cockpit 扩展:

软件包官方支持?描述
cockpit-storaged管理系统的存储。支持本地分区、加密、NFS、RAID、iSCSI 等
cockpit-networkmanager管理您的网络接口并轻松编辑防火墙
cockpit-ostree在浏览器中升级基于 OSTree 的系统
cockpit-machines在浏览器中创建、运行和管理虚拟机
cockpit-podman在浏览器中下载、使用和管理容器 (基于 Podman)
cockpit-selinux查看和管理 SELinux 异常。
cockpit-kdump调试 Linux 内核?这个 kdump 插件有助于捕获堆栈的痕迹
cockpit-sosreport从系统中收集系统配置和诊断信息

由于 OSTree 的特性,安装完成后需要重启系统才会生效。

安装 Cockpit Web Service

刚刚我们安装了一大堆 cockpit-* (Cockpit 的核心程序和其扩展) 将 NAS 的各种功能通过 API 的形式暴露了出来;现在,为了更好的管理我们的 NAS,我们要为他们安装一个基于 Web 的 Dashboard。

当然,本着能容器化尽容器化的原则,这个 Dashboard 应该跑在容器中。

它与 cockpit-* 的关系大致如下:

  1. 确保你的系统允许你使用基于密码的 SSH 登录 (默认是不允许的):
cat /etc/ssh/sshd_config.d/20-enable-passwords.conf
# 如果显示不存在这个文件,使用以下命令开启对基于密码的 SSH 登录的支持
echo 'PasswordAuthentication yes' | sudo tee /etc/ssh/sshd_config.d/20-enable-passwords.conf
sudo systemctl try-restart sshd
  1. 使用特权容器 (以 Root 身份) 运行 Cockpit Web Service:
sudo podman container runlabel --name cockpit-ws RUN quay.io/cockpit/ws
  1. 使 Cockpit Web Service 在开机时启动:
sudo podman container runlabel INSTALL quay.io/cockpit/ws
sudo systemctl enable cockpit.service

然后,像往常一样使用浏览器登录主机 IP 地址上的端口 9090

创建并挂载 RAID 1 磁盘阵列

恭喜你,走到这儿困难的步骤已经走完了!现在有了 Cockpit Web Service,余下的所有任务只需要点点点就可以完成了。

确保你的电脑和服务器位于同一内网中,打开浏览器,通过 https://[服务器 IP]:9090 访问 Cockpit。输入用户名和密码,然后登录。

cockpit-login

首次登录的话会以 🔒 被限制的访问 的权限登录,创建 RAID 1 磁盘阵列是需要 Root 权限的,所以我们点击「Turn on administrative access」切换到 管理员模式

cockpit-admin-access_1

在这里输入用户的密码 (注意不是 Root 密码),然后「Authenticate」。

cockpit-admin-access_2

接着来到「储存」,在这里你可以查看和管理你的硬盘资源。在「设备」处,点击「更多」(也就是那三条杠),选择「创建 RAID 设备」。

cockpit-storage

填写名称 (放在 /dev/md/ 下的文件名),选择 RAID 级别,然后勾选供阵列使用的硬盘。

setup-raid

等 RAID 阵列创建好后,在「设备」处,点开我们创建好的阵列。在「内容」处选择对 RAID 设备进行「格式化」。

manage-raid

填写名称,选择文件系统,编辑挂载点

format-raid

完成格式化并挂载后,RAID 阵列就创建完成了!

基于容器安装其他应用层服务

对于一台 NAS,只会管理管理硬盘是不够的。你还需要安装服务,让 NAS 与你的计算机建立联系。一块无法被挂载的硬盘是没有意义的。

Fedora CoreOS 或 Cockpit 本身并不提供 Samba 等服务,但 Fedora CoreOS 默认遍集成了 Podman,一个强大的容器引擎 (类似于 Docker,但由于没有以 Root 权限运行的守护进程,所以比 Docker 更安全)。并且,由于两者均支持 OCI (Open Container Initiative) 协议,所以 podman 是可以直接从 dockerhub 拉取容器并运行的。

安装 Samba 服务

这里选择的容器是 dperson/samba

首先,我们来到「Podman 容器」的管理界面。点击「创建容器」。

podman

然后填写容器的名称镜像地址 (注意别忘了 docker.io/)。由于 Fedora CoreOS 的自动更新机制,我们需要保障我们的服务开机后自动重启,所以我们需要把重启策略设置为「总是」。

setup-samba_1

接着来到「集成」标签,为容器配置端口路径的映射。

setup-samba_2

「创建并运行」,然后家里的设备就可以通过 Samba 服务连接并访问 NAS 了!

安装 Webdav 服务 (基于 Nginx)

这里选择的容器是 ionelmc/webdav

相比于“老旧”的 Samba,我更喜欢 Webdav。

与 Samba 相似,我们点击「创建容器」,然后填写容器的名称镜像地址和设置重启策略。但不同的是,ionelmc/webdav 这个容器使用环境变量而非参数配置用户名和密码。所以我们不需要向容器传递任何命令;取而代之的,「集成」标签中除了端口路径,我们还需要配置环境变量 WEBDAV_USERNAMEWEBDAV_PASSWORD。更多配置详情,你可以访问 ionelmc/webdavdockerhub 页面,或者去它的 GitHub 查看源代码。

setup-webdav_1 setup-webdav_2

and so on…

除此在外,你还可以起个 Transmission 挂 BT/PT。还可以用 Alist 整合 XX 网盘,或者是用 ownCloud 创建属于自己的私人网盘。再或者装一个 Jellyfin,NAS 秒变 HTPC!Minecraft 服务器、各种挂机服务器、自动签到…

Podman 容器为 Fedora CoreOS 创造了无限多的可玩性!