LoginSignup
0
0

More than 1 year has passed since last update.

自宅サーバー構築譚:Ubuntu on ZFS in デスクトップパソコン(BIOS,MBR)

Last updated at Posted at 2022-03-12

能書き

自宅サーバー構築譚:基本構想に基づく自宅サーバー構築の続き。今度は実機にインストールします。

参考文献

参考文献1:自宅サーバー構築譚:Ubuntu on ZFS in Hyper-V (第1世代)
参考文献2:ルートパーティションをZFSにしてUbuntu 20.04 LTSをインストールしてみた - hnakamur's blog
参考文献3:Ubuntu 20.04 Root on ZFS - OpenZFS Documentation - OpenZFS

インストール環境の整備

ハードウェアの仕様は昔書いた記事の通りです。将来的にはこれをセカンダリサーバにする予定ですが、今回はプライマリサーバとして構築します。

しかしこのマシンは古過ぎたようです。手順の通りでは、そもそもブートが途中で止まってしまうという体たらく。なぜかBIOS画面も表示できず、コンセントを抜いて数分待たないと戻らないという訳の分からない状態になってしまいました。原因はどうやらGPTにあった模様。

と言う訳で。前回のHyper-Vを利用した手順確認とは少し異なり、GPTではなくてMBRでパーティションを切るようにしています。

デスクトップインストーラーを起動しsshの環境整備

サーバーをインストールする場合でもデスクトップ版のisoを使用します。Ubuntuのサイトからダウンロードして、USBメモリに焼きます。
私は普段使いのWin10マシンでRufusというツールを使ってみましたが、ここはお好みでどうぞ。

このUSBメモリをインストール対象マシンに挿入して、ここから起動。

GUI インストーラーが立ち上がって Welcome という画面が表示されたら、 TAB キーを押して Try Ubuntu ボタンにフォーカスを移動して(ボタンにオレンジの枠がつきます) Enter キーを押します。

ウィンドウ環境に切り替わったらターミナルを起動しますが、マウスが繋がっていない場合はショートカットキー Ctrl+Alt+T でどうぞ。

主な作業はsshで接続して行いたいので、そのように準備します。sshで接続するのはコマンドをコピペできるから。私もやってみてわかりました。これ良いですな。

ターミナルで下記の2つのコマンドを実行。

sudo apt-add-repository universe
sudo apt update

OpenSSHサーバーをインストール。

sudo apt install --yes openssh-server

ssh接続用にユーザー ubuntu のパスワードを設定。

passwd

ssh接続先のIPアドレスを確認します。

ip a

別のマシンから対象のサーバーに ssh してインストール作業を続行

ssh はほとんどのOSで利用可能でしょう。Windows10でもコマンドプロンプトからsshコマンドを実行できます。

ssh ubuntu@接続先のIPアドレス

インストール作業を同じマシンでやり直してたりすると、ここでエラーが出る事があります。そんな時には .ssh\known_hosts を削除してから、sshコマンドを実行します。

del %USERPROFILE%\.ssh\known_hosts

rootユーザーになります。

sudo -i

ディスクのフォーマット

起動用のUSBメモリを挿します。ここにブートローダを仕込んで常時付けっ放しにしておく予定。

そのUSBメモリを含めたフォーマット対象ディスクを確認します。

lsblk

今回は下記のように表示されます。

root@ubuntu:~# lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop0    7:0    0     2G  1 loop /rofs
loop1    7:1    0   219M  1 loop /snap/gnome-3-34-1804/72
loop2    7:2    0    51M  1 loop /snap/snap-store/547
loop3    7:3    0  65.1M  1 loop /snap/gtk-common-themes/1515
loop4    7:4    0  55.4M  1 loop /snap/core18/2128
loop5    7:5    0  32.3M  1 loop /snap/snapd/12704
sda      8:0    0 465.8G  0 disk
├─sda1   8:1    0 465.8G  0 part
└─sda9   8:9    0     8M  0 part
sdb      8:16   0 465.8G  0 disk
├─sdb1   8:17   0 465.8G  0 part
└─sdb9   8:25   0     8M  0 part
sdc      8:32   1   7.5G  0 disk
├─sdc1   8:33   1   2.9G  0 part /cdrom
├─sdc2   8:34   1   3.9M  0 part
└─sdc3   8:35   1   4.6G  0 part /var/crash
sdd      8:48   1  14.9G  0 disk
├─sdd1   8:49   1     1G  0 part
└─sdd2   8:50   1   260M  0 part
sr0     11:0    1  1024M  0 rom

loop0loop5はインストーラによる設定でしょうか。
sdasdbが内蔵HDDで、以前入れたArchLinuxが残っています。
sdcが今回のUbuntuインストーラでしょう。
sddはブートローダを仕込む為のUSBメモリです。
sr0はCR-ROMドライブと思われますが、今回は無関係です。

ディスクの指定にはIDを使います。

ls -l /dev/disk/by-id

これを変数に設定するのがubuntu流らしいですが、ここは私の流儀で取得する事にします。

BOOTDISK=$(cd /dev/disk/by-id; ls ata-* usb-* | while read d; do if [ "$(readlink -f $d)" = "/dev/sdd" ]; then echo "/dev/disk/by-id/$d"; fi; done)
DISK1=$(cd /dev/disk/by-id; ls ata-* usb-* | while read d; do if [ "$(readlink -f $d)" = "/dev/sda" ]; then echo "/dev/disk/by-id/$d"; fi; done)
DISK2=$(cd /dev/disk/by-id; ls ata-* usb-* | while read d; do if [ "$(readlink -f $d)" = "/dev/sdb" ]; then echo "/dev/disk/by-id/$d"; fi; done)

ブート用USBメモリのパーティションを全削除します。

wipefs -a $BOOTDISK

パーティションを作成。GPTではなくてMBRにします。

sfdisk $BOOTDISK <<___
,1G,bf
,
___

設定を確認するコマンドは下記になります。

sfdisk -l $BOOTDISK

$DISK1$DISK2はパーティションに分けません。

wipefs -a $DISK1 $DISK2

いよいよzfsフォーマットします。ここが今回最大のコピペポイント!これをコピペする為にssh経由で接続していると言っても過言ではありません。

因みにこのzpool名はbpool固定らしい。

※なお2回目以降でエラーが出る際には、コマンドの末尾かどこかに -f オプションを付加します。

zpool create -o ashift=12 -d \
    -o feature@async_destroy=enabled \
    -o feature@bookmarks=enabled \
    -o feature@embedded_data=enabled \
    -o feature@empty_bpobj=enabled \
    -o feature@enabled_txg=enabled \
    -o feature@extensible_dataset=enabled \
    -o feature@filesystem_limits=enabled \
    -o feature@hole_birth=enabled \
    -o feature@large_blocks=enabled \
    -o feature@spacemap_histogram=enabled \
    -o feature@userobj_accounting=enabled \
    -O acltype=posixacl -O canmount=off -O devices=off \
    -O normalization=formD -O relatime=on -O xattr=sa \
    -O mountpoint=/ -R /mnt bpool $BOOTDISK-part1

参考文献には下記のように書かれていました。

normalization=formD は Unicode の normalization の NFD に相当します。 昔 Subversion で Windows は NFC, Mac OS X は NFD と違うせいで全角カナの濁点が分かれない、分かれるで苦労したので NFC にしようかと思いましたが、 formD でも Windows と mac から繋いで問題ないと聞いたので formD にしておきました。

私は最近この件で苦労したのでformCにしたいのですが、そもそものwikiには改変しない方が良いと書いてあったので止めておきます。

そして内蔵HDDもzfsフォーマットします。こちらはオプションがすっきりしてますが、2つ1組のHDDをミラーリングに設定します。

こちらのzpool名は何でも良いそうです。zfsの伝統に則ってtankにしてみました。

zpool create -o ashift=12 \
    -O acltype=posixacl -O canmount=off \
    -O dnodesize=auto -O normalization=formD -O relatime=on -O xattr=sa \
    -O mountpoint=/ -R /mnt tank mirror $DISK1 $DISK2

作成したプール一覧の確認は下記。このコマンドはコピペではなくて zpool list zpool statusと手打ちでお願いします。別に良いよねこの位は。

root@ubuntu:~# zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
bpool   960M   576K   959M        -         -     0%     0%  1.00x    ONLINE  /mnt
tank    464G   600K   464G        -         -     0%     0%  1.00x    ONLINE  /mnt
root@ubuntu:~# zpool status
  pool: bpool
 state: ONLINE
  scan: none requested
config:

        NAME                                                      STATE     READ WRITE CKSUM
        bpool                                                     ONLINE       0     0     0
          usb-SanDisk__Cruzer_Fit_4C530000180129117161-0:0-part1  ONLINE       0     0     0

errors: No known data errors

  pool: tank
 state: ONLINE
  scan: none requested
config:

        NAME                                            STATE     READ WRITE CKSUM
        tank                                            ONLINE       0     0     0
          mirror-0                                      ONLINE       0     0     0
            ata-WDC_WD5003ABYX-50WERA1_WD-WMAYP7972470  ONLINE       0     0     0
            ata-WDC_WD5003ABYX-50WERA1_WD-WMAYP7949276  ONLINE       0     0     0

errors: No known data errors

システムのインストール

ディレクトリ構造に合わせたデータセット作成

ルート用データセットを作成します。

zfs create -o canmount=off -o mountpoint=none tank/main
zfs create -o canmount=noauto -o mountpoint=/ tank/main/ubuntu
zfs mount tank/main/ubuntu

ブート用データセットを作成します。

zfs create -o canmount=off -o mountpoint=none bpool/boot
zfs create -o canmount=noauto -o mountpoint=/boot bpool/boot/ubuntu
zfs mount bpool/boot/ubuntu

一般的なデータセットを作成します。

zfs create -o canmount=off                 tank/sub0
zfs create -o mountpoint=/home             tank/sub0/home
zfs create -o mountpoint=/root             tank/sub0/root
zfs create -o canmount=off                 tank/sub0/var
zfs create -o canmount=off                 tank/sub0/var/lib
zfs create -o mountpoint=/var/log          tank/sub0/var/log
zfs create -o mountpoint=/var/spool        tank/sub0/var/spool
zfs create -o mountpoint=/var/snap         tank/sub0/var/snap
zfs create -o canmount=off                 tank/sub0/usr
zfs create -o mountpoint=/usr/local        tank/sub0/usr/local
zfs create -o com.sun:auto-snapshot=false -o mountpoint=/var/lib/docker tank/sub0/var/lib/docker

debootstrap

debootstrapではなくてmmdebstrapを使ってみようかと考えたのですが、残念ながら失敗しました。mdebstrapではインストール先ディレクトリが空でなければならないようです。

参考文献:第594回 mmdebstrapで最小のルートファイルシステムを作る - Ubuntu Weekly Recipe

そういう訳で、debootstrapを入れてから Ubuntu 20.04 LTS 通称 focal をインストール。

apt install --yes debootstrap
debootstrap focal /mnt

終わったら以下のコマンドを実行します。

zfs set devices=off tank

システム設定

マシン名

まずはマシン名を決めます。

HOSTNAME=<マシン名>
echo $HOSTNAME >/mnt/etc/hostname

ネットワーク関連

hostsファイルを修正。

sed -i -e"/^127.0.0.1/a 127.0.1.1\t$HOSTNAME" /mnt/etc/hosts

netplan のネットワーク設定ファイル/mnt/etc/netplan/01-netcfg.yamlを作成します。今回はIPv6は使わないようにするので下記のようにしてみました。

このマシンはNICを2枚持っています。片方はオンボードで、もう片方はギガビットイーサのPCIネットワークカードです。オンボードの方は遅いのでケーブルを外してあります。

root@ubuntu:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp2s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:16:01:5c:19:df brd ff:ff:ff:ff:ff:ff
    inet 172.16.2.22/16 brd 172.16.255.255 scope global dynamic noprefixroute enp2s1
       valid_lft 83861sec preferred_lft 83861sec
    inet6 2408:211:1283:500:1aa3:2163:dfcb:c720/64 scope global temporary dynamic
       valid_lft 602255sec preferred_lft 83327sec
    inet6 2408:211:1283:500:9cce:f37a:54a3:5efa/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 2591903sec preferred_lft 604703sec
    inet6 fe80::2fff:e4:2986:a19c/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: enp0s25: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether 00:21:9b:1a:5f:94 brd ff:ff:ff:ff:ff:ff

以前の記事の通りにIPアドレスを割り振ります。

参考文献:固定IPアドレスの割当(IPv4、IPv6) - Netplanの使い方 - komeの備忘録

cat > /mnt/etc/netplan/01-netcfg.yaml <<___
network:
  version: 2
  renderer: networkd
  ethernets:
    enp2s1:
      dhcp4: no
      addresses: [172.16.0.3/16]
      gateway4: 172.16.0.1
      nameservers:
        addresses: [172.16.0.1]
    enp0s25:
      dhcp4: no
      addresses: [172.16.0.4/16]
      gateway4: 172.16.0.1
      nameservers:
        addresses: [172.16.0.1]
___

参考文献には/mnt/etc/apt/sources.listの変更に言及がありますが、省略可との事ですし、今回は省略します。

chroot

Live CD 環境の仮想ファイルシステムをバインドマウントして、上記でセットアップした環境に chroot します( --bind ではなく --rbind を使っていることに注意)。

mount --rbind /dev  /mnt/dev
mount --rbind /proc /mnt/proc
mount --rbind /sys  /mnt/sys
chroot /mnt /usr/bin/env BOOTDISK=$BOOTDISK bash --login

chroot 環境内の apt のインデクスを更新します。

apt update

ロケール

en_US.UTF-8 と ja_JP.UTF-8 と、後は個人的な好みで。

locale-gen --purge en_US.UTF-8 ja_JP.UTF-8 ja_JP.EUC-JP
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales

タイムゾーン

日本なら Asia/Tokyo です。

ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
dpkg-reconfigure -f noninteractive tzdata

カーネルイメージなど

Linux のカーネルイメージと ZFS を chroot 環境にインストールします。

apt install --yes --no-install-recommends linux-image-generic zfs-initramfs

上記のコマンド実行の出力の最後に以下のようなメッセージが出ます。 update-initramfs/boot/initrd.img-5.4.0-26-genericを生成したという点が重要だそうです。この後 grub のインストール時に必要になる為です。

update-initramfs: Generating /boot/initrd.img-5.4.0-26-generic

GRUB をレガシー BIOS 用にインストール

参考文献に従って以下のコマンドを実行。

DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install grub-pc

rootパスワード

パスワードを設定します。

passwd

zfsのzpool関係

tank_boot を確実にインポートするためのサービスのユニットファイルを作成します。

cat >/etc/systemd/system/zfs-import-bpool.service <<___
[Unit]
DefaultDependencies=no
Before=zfs-import-scan.service
Before=zfs-import-cache.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/zpool import -N -o cachefile=none bpool

[Install]
WantedBy=zfs-import.target
___

このサービスの自動起動を有効にします。

systemctl enable zfs-import-bpool.service

tmpfs

次回起動時に tmpfs を /tmp にマウントするようにします。

cp /usr/share/systemd/tmp.mount /etc/systemd/system/
systemctl enable tmp.mount

GRUB のインストール

ZFS のブートファイルシステムが認識されていることを確認します。zfs と出力されれば OK です。

grub-probe /boot

grub の設定ファイルを修正します。sed コマンドにすると少々複雑ですが。

sed -i -e '/^GRUB_TIMEOUT=/{s/=.*/=5/;a\
GRUB_RECORDFAIL_TIMEOUT=5
}
/^GRUB_CMDLINE_LINUX_DEFAULT=/s/quiet splash//
s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX="root=ZFS=tank/main/ubuntu"|
/^#GRUB_TERMINAL=console/s/#//
' /etc/default/grub

修正結果は下記の通り。

# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=5
GRUB_RECORDFAIL_TIMEOUT=5
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="root=ZFS=tank/main/ubuntu"

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"

# Uncomment to disable graphical terminal (grub-pc only)
GRUB_TERMINAL=console

# The resolution used on graphical terminal
# note that you can use only modes which your graphic card supports via VBE
# you can see them in real GRUB with the command `vbeinfo'
#GRUB_GFXMODE=640x480

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux
#GRUB_DISABLE_LINUX_UUID=true

# Uncomment to disable generation of recovery mode menu entries
#GRUB_DISABLE_RECOVERY="true"

# Uncomment to get a beep at grub start
#GRUB_INIT_TUNE="480 440 1"

この設定変更を反映します。

update-grub

が、この結果はエラーが発生します。osprober でbusyエラーが出ますが OK らしい。Found linux image と Found initrd image の行が出ていれば大丈夫との事。

root@ubuntu:/# update-grub
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/init-select.cfg'
Generating grub configuration file ...
cannot open 'bpool/BOOT/ubuntu': dataset does not exist
Found linux image: vmlinuz-5.4.0-26-generic in tank/main/ubuntu
Found initrd image: initrd.img-5.4.0-26-generic in tank/main/ubuntu
device-mapper: reload ioctl on osprober-linux-sda1  failed: Device or resource busy
Command failed.
device-mapper: reload ioctl on osprober-linux-sdb1  failed: Device or resource busy
Command failed.
grub-probe: error: cannot find a GRUB drive for /dev/sdd1.  Check your device.map.
done

レガシー BIOS 用に GRUB をインストールします。

grub-install $BOOTDISK

ZFS のモジュールがインストールされていることを確認します。

ls /boot/grub/*/zfs.mod

初回のブート

スナップショット

初回インストールのスナップショットを作成しておきます。こういう事をしたいが為にZFSを使うのです。

zfs snapshot bpool/boot/ubuntu@$(date +%Y%m%d_%H%M%S)_install
zfs snapshot tank/main/ubuntu@$(date +%Y%m%d_%H%M%S)_install
zfs snapshot -r tank/sub0@$(date +%Y%m%d_%H%M%S)_install

スナップショットの一覧を表示して確認します。

zfs list -t snapshot

リブート

chroot 環境から抜けて Live CD 環境に戻ります。

exit

Live CD 環境でマウント中のファイルシステムを全てアンマウントします。

mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -n 1 -r umount -lf
zpool export -a

なぜかエラーが出る事があります。

root@ubuntu:~# zpool export -a
umount: /mnt: target is busy.
cannot unmount '/mnt': umount failed

そんな時にはもう一度実行。

mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -n 1 -r umount -lf
zpool export -a

成功したらシャットダウン。

shutdown -h now

その後、インストールメディアを取り出して、それからマシンの電源を入れます。

rootログイン

ここからはsshは使えません。

サーバーのコンソールで root ユーザーでパスワードを入力してログインします。

日本語キーボード配列

記号を打ってみるとわかりますが、英語配列になっています。日本語配列に変更する手順は以下の参考文献を参照。バージョンは古いですが、画面キャプチャがあるのでわかりやすいです。

参考文献:Ubuntuでキーボードレイアウト変更 - Ragnite Blue

コマンドは下記。

dpkg-reconfigure keyboard-configuration

設定後、次のコマンドを打つ必要があるようです。

setupcon

スワップ

物理メモリを十分に積んでいてもスワップは重要だそうです。

容量(4Gの箇所)はマシンの状況に合わせて検討して下さい。

zfs create -V 4G -b $(getconf PAGESIZE) \
    -o logbias=throughput -o sync=always \
    -o primarycache=metadata -o secondarycache=none \
    -o com.sun:auto-snapshot=false tank/swap

次にスワップを設定します。

mkswap -f /dev/zvol/tank/swap
echo /dev/zvol/tank/swap none swap discard 0 0 >> /etc/fstab
echo RESUME=none > /etc/initramfs-tools/conf.d/resume

スワップを有効にします。

swapon -av

有効になったかどうか確認。

swapon --show

フルのソフトウェアインストール

ミニマムインストールをアップグレードします。

apt dist-upgrade --yes

サーバ用としてコマンドライン環境のみをインストールします。

apt install --yes ubuntu-standard

再起動して確認

reboot

まだ一般ユーザーが居ないので、rootでログインします。
そして色々確認。

  • キーボードが日本の配列になっている事。アットマーク @ を打ってみて確認。
  • スワップが有効になっている事。上述のswapon --showコマンドで確認。
  • ネットワ-クが有効になっている事。仮想環境など状況によってNICの名称が変わる事があり、その場合は上述の設定/etc/netplan/01-network.yamlのNIC名eth0を変更する必要があります。

ネットワークに関しては、次のようなコマンドを試してみればわかります。
※ example.com はドメインの例示の際に使うべしとRFC2606で定められています。何か適当なサーバーを選んで下さい。

ping -c2 example.com

失敗した場合には、上述のネットワーク設定ファイル/mnt/etc/netplan/01-netcfg.yamlを修正します。その為のNIC名取得は下記のコマンドで。

参考文献:Linux から認識されている、すべてのネットワークインターフェイス名だけを取得する - Qiita

for d in `find /sys/devices -name net | grep -v virtual`; do ls $d; done

手打ちのコマンドとしてはip aの方が簡単ですが、上記のコマンドならNIC名だけを取り出せますので自動化が可能です。

結び

これで本当に最小限のUbuntuサーバをセットアップできました。これからサーバとして育てていく予定です。:thumbsup_tone1:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0