小型・ファンレス・低消費電力・メンテナンスフリーなサーバーは人類の見果てぬ夢です。今回はRaspberry PiとUbuntu Core を使ってその夢を追い求めてみます。
サーバー版ではなくUbuntu Coreをサーバーとして使う
第646回の「Raspberry PiをIoTデバイスとして活用できるUbuntu Core 」ではIoTデバイス向けのUbuntu Coreを紹介しました。実はこのUbuntu Core、使いようによってはサーバーやクラウドインスタンスの用途にもマッチしているのです[1] 。
[1] 今回はRaspberry Piに特化してUbuntu Coreのサーバー化を説明しますが、Ubuntu Coreのインストール部分さえ気をつければ仮想マシンやNUCなど他のプラットフォームでも状況は同じです。他のアーキテクチャー向けのUbuntu Coreイメージはcdimages.ubuntu.com からダウンロードできます。また、個別のインストール方法はこちらのサイト を参照してください。
Raspberry Piを「Ubuntuサーバーとして使う」だけなら、第624回の「Raspberry Pi 4にデスクトップ版Ubuntuをインストール 」でも紹介したように、公式のサーバーイメージが用意されているのでそれを使う方法もあります。こちらは普通のUbuntuなので、aptコマンドでパッケージもインストールできますし、ルートファイルシステムも自由に読み書き可能です。
それに対してUbuntu Coreはセキュリティ上の制約からできることが限られているため普通のLinuxサーバーのように運用するとなるといろいろな壁が立ちはだかります。それでもDockerコンテナを立ち上げるならそこまで難しくありませんし、LXDでインスタンスを作ればその中は普通のUbuntuサーバーです。つまり使い方さえ合っていれば、Ubuntu Coreの利便性・セキュリティを享受しつつ、Ubuntuサーバーとして使えるというわけなのです。
ここではDockerとLXDのふたつを例に、Ubuntu Core環境のサーバー化を説明しましょう。Ubuntu Coreのインストールそのものは前回の記事 を参照してください。ネットワーク経由でSSHログインできているという前提に立ちます。今回はすべてSSH経由で操作しますので、インストールが終わればディスプレイ・キーボードは不要です。
Ubuntu Coreそのものは制約が大きいためそのままでは普通のパッケージはインストールできませんし、できることも限られてきます。そこでUbuntuサーバー化のために使うのが「Docker」と「LXD」のふたつのコンテナシステムです。Ubuntu Coreの上で直接Ubuntuサーバーが使えないのであれば、コンテナの中でUbuntuサーバー環境を作ってしまおう、という至極安直な考え方です。
Ubuntu Coreの上でDockerをセットアップ
サーバー上で動かしたいサービスが出来合いのものであるのなら、Dockerは大変便利です。古い時代にあった「ドキュメントを見てソフトウェアのセットアップを行う」という作業のほとんどが、コマンドひとつで完結してしまいます。snap版のDockerパッケージを使えば、Ubuntu Core上でもこの恩恵が受けられます。ただし現在snap版でサポートしているのは、19.03.xと若干バージョンが古い点には注意が必要です。
$ snap info docker
name: docker
summary: Docker container runtime
publisher: Canonical✓
store-url: https://snapcraft.io/docker
contact: https://github.com/docker-snap/docker-snap/issues?q=
license: Apache-2.0
description: |
Build and run container images with Docker.
This build requires all files that Docker uses, such as dockerfiles, to be in $HOME. Keep files
there for 'docker build', 'docker save' and 'docker load'.
This snap is built by Canonical based on source code published by Docker, Inc. It is not endorsed
or published by Docker, Inc.
Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United
States and/or other countries. Docker, Inc. and other parties may also have trademark rights in
other terms used herein.
snap-id: sLCsFAO8PKM5Z0fAKNszUOX0YASjQfeZ
channels:
latest/stable: 19.03.11 2020-06-09 (474) 102MB -
latest/candidate: 19.03.11 2020-06-09 (474) 102MB -
latest/beta: 19.03.13 2020-11-18 (654) 104MB -
latest/edge: 19.03.13 2020-12-19 (738) 104MB -
17.03/stable: 17.03.2-ce-1 2017-07-20 (161) 38MB -
17.03/candidate: 17.03.2-ce-1 2017-06-30 (161) 38MB -
17.03/beta: ↑
17.03/edge: 17.03.2-ce-1 2017-06-30 (161) 38MB -
インストールは次のコマンドを実行するだけです。
$ snap install docker
ちなみにDockerのdata-root
は、「 /var/snap/docker/common/var-lib-docker/
」になります。この下にインスタンスやイメージが作られるので、用途に合わせた容量が必要です。snapパッケージの場合、このPATH名そのものは変更できないため、ホームディレクトリ以下か「/media
」もしくは「/mnt
」以下をbind-mountしてしまうのが良いでしょう[2] 。Raspberry Piの場合、ホームディレクトリも容量が少ないため、前回の記事 と同様に「/media
」に外部ストレージをマウントし、それを使うのが良いでしょう。
[2] snap版のdockerパッケージはホームディレクトリ以下にはアクセス権が設定されてます。しかしながらそれ以外のシステムディレクトリにはアクセスできません。また、ホームディレクトリ以下であっても直下の隠しディレクトリ(ドットディレクトリ)にはアクセス不可です。
外部ストレージのマウント方法は、前回の記事を参照してください。ここでは、/media/data
にストレージがマウントされたものとします。なお、前回のnextcloudもそうですが、Dockerのdata-root
はDokcerを起動する上で必須といえるディレクトリです。これを外部ストレージに頼るのであれば、systemdの依存関係を使って「Dockerサービスの起動よりも前にマウントが完了していること」「 マウントできなかったときはDockerサービスを起動しない」ことを保証する必要があるでしょう。具体的には次のような記述になります。
[Unit]
Description=External Storage for Nextcloud
Before=snap.docker.dockerd.service
[Mount]
What=/dev/disk/by-label/EXT
Where=/media/data
Type=ext4
[Install]
WantedBy=multi-user.target
RequiredBy=snap.docker.dockerd.service
追加したのは「Before=
」と「RequiredBy=
」の行です。前者はDockerサービスが起動するよりも前にmountが完了することを要求し、後者はDockerサービスが起動するときは必ずmountが行われることを要求しています。今回はDockerのサービスだけ追加しましたが、Nextcloudのサービスも追加する場合は、空白区切りで追加してください。DockerにしろNextcloudにしろ、snapパッケージのサービスファイルは「snap.FOO.service
」のような名前になっていることに注意が必要です。
/media/data
がマウントできるようになったら、次の手順でdata-root
の移行を行います。
$ sudo systemctl stop snap.docker.dockerd.service
$ sudo cp -a /var/snap/docker/common/var-lib-docker/ /media/data/docker
$ sudo mv /var/snap/docker/common/var-lib-docker/ ~/var-lib-docker.old
$ sudo mkdir /var/snap/docker/common/var-lib-docker/
$ sudo mount --bind /media/data/docker /var/snap/docker/common/var-lib-docker
$ sudo snap connect docker:removable-media
$ sudo systemctl start snap.docker.dockerd.service
これで無事に/media/data/docker
以下にDockerのファイルが作られるようになるはずです。試しに適当なインスタンスを立ち上げてみましょう。
RasPi 2/3/4を32bitで起動している場合
$ sudo docker run arm32v7/hello-world
RasPi 3/4を64bitで起動している場合
$ sudo docker run arm64v8/hello-world
問題なければ「~/var-lib-docker.old
」は削除してかまいません。さらにbind mount処理自体もmount unit化してしまいます。念のため、Dockerサービスを一旦落としておきます。
$ sudo rm -rf ~/var-lib-docker.old/
$ sudo systemctl stop snap.docker.dockerd.service
$ sudo umount /var/snap/docker/common/var-lib-docker
unitファイルを次の手順で作成します。
$ cat <<'EOF' | sudo tee /etc/systemd/system/var-snap-docker-common-var\\x2dlib\\x2ddocker.mount
[Unit]
Description=External Storage for Docker
After=media-data.mount
Before=snap.docker.dockerd.service
[Mount]
What=/media/data/docker
Where=/var/snap/docker/common/var-lib-docker
Options=bind
[Install]
WantedBy=multi-user.target
[Install]
WantedBy=multi-user.target
RequiredBy=snap.docker.dockerd.service
Requires=media-data.mount
EOF
気をつけなければいけないのはハイフンの扱いです。前回の記事でも説明したように、mount unitはマウント先のパスをハイフンで繋いだものをファイル名として使います。つまりディレクトリ名の中にハイフンを使えません。この場合、エスケープする必要があります。エスケープした結果はsystemd-escape
コマンドで確認できます。
$ systemd-escape -p /var/snap/docker/common/var-lib-docker
var-snap-docker-common-var\x2dlib\x2ddocker
ハイフンは「\x2d
」で置き換えれば良さそうです。ただしシェルの中で直接「\
」を書くためには、「 \\
」とエスケープする必要があります。そのため上記では「var\\x2dlib\\x2ddocker.mount
」のような名前になっています。
前回と同様動作確認して、うまく動くならenableして起動時に自動的にマウントしておきましょう。
$ sudo systemctl daemon-reload
$ sudo systemctl start var-snap-docker-common-var\\x2dlib\\x2ddocker.mount
$ sudo systemctl enable var-snap-docker-common-var\\x2dlib\\x2ddocker.mount
$ sudo systemctl start snap.docker.dockerd.service
念のためシステムを再起動して、自動的に反映されるかどうかを確認しておくと良いでしょう。
あとは普通のDockerとして利用できます。snap版のdockerパッケージには「docker-compose
」コマンドも同梱されているので、デーモンとして動かしたいのであればこちらも活用してください。
snap版のDockerは「パッケージが提供された設定をほぼそのまま利用する」のが基本的な使い方です。カスタマイズもやろうと思えばできなくもないのですが、おそらく「snap固有の余計な手間」が増えるだけでしょう。Dockerをより細かく調整しながら使いたいのであれば、次項で説明するようにLXDをセットアップして、そのLXDインスタンスの中でDockerを使うほうが簡単です。
Ubuntu Coreの上でLXDをセットアップ
ここまで説明したように、Ubuntu Coreそのものは堅牢なシステムです。「 一般的なサーバー」としてSSHログインしていろいろと操作するには不向きです。しかしながらUbuntu Coreの上でシステムコンテナを活用し、「 普通のUbuntuサーバー環境」を作ってしまえば話は別です。要するにUbuntu CoreをVMWare ESXiのように使うということです。
LXDはシステムコンテナにもamd64/arm64向けの仮想マシンにも対応しています。よって仮想マシンマネージャーとしてLXDを使えば、実際にESXiのような機能を実現可能です[3] 。LXD自体はsnapパッケージ化されているため、Ubuntu Coreにも簡単にインストールできます。LXD上のUbuntuは、Ubuntu Coreのような制約がないため自由にサーバーとしてセットアップできます[4] 。マシンをまたいだマイグレーションにも対応しているので、複数台のRaspberry Piを用意して、問題が起きたら別のRaspberry Piに移行するといった使い方も可能になるのです。
注意すべきなのはストレージの設定でしょう。LXDは作成するコンテナをどこに・どのように保存するかを細かく設定できます。しかしながらイメージ・インスタンスのサイズはそれなりに大きくなるため、保存先もそれなりに大きな容量が必要です。ちょっとしたコンテナを複数動かす程度であれば数GiBでも十分ですが、本格的に使うなら最低でも数十GiBはほしいところです。そうなるとSDカードに保存するのは難しくなります。
Raspberry PiならUSBストレージを増設しましょう。今回は単にLXDをインストールするだけでなく、USBストレージ先にイメージやインスタンスを保存することにします。詳細はDockerの項目を参照してください。ここでは「/media/data/lxd/
」以下をストレージプール用のディレクトリとして使うことにします。
まずはsnap版のlxdパッケージをインストールして、次のように初期化しましょう。
$ snap install lxd --channel=4.0/stable
$ sudo lxd init
(中略)
Do you want to configure a new storage pool? (yes/no) [default=yes]:
Name of the new storage pool [default=default]:
Name of the storage backend to use (lvm, ceph, btrfs, dir) [default=btrfs]:
Create a new BTRFS pool? (yes/no) [default=yes]:
Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]:
Size in GB of the new loop device (1GB minimum) [default=5GB]: 20
(後略)
上記ではストレージプールに関する部分のみを抜き出してみました。ポイントは「empty block device」の行です。もし空のストレージもしくは空のパーティションをまるまるLXDに利用できるのであれば、ここを「yes」に設定することをおすすめします。
上記のように「no」を設定した場合、標準のストレージプールが「/var/snap/lxd/common/lxd/disks/default.img
」として作られます。これをより容量の大きいUSBストレージ上に移行する必要があるのです[5] 。
方法はいくつかあるのですが、Dockerと同じように/var/snap/lxd/common/lxd/disks/
を外部ストレージからのbind mountにしてしまうのが簡単です。
$ sudo lxc stop --all
$ sudo systemctl stop snap.lxd.daemon.unix.socket
$ sudo systemctl stop snap.lxd.daemon.service
$ sudo cp -a /var/snap/lxd/common/lxd/disks/ /media/data/lxd
$ sudo mv /var/snap/lxd/common/lxd/disks/ ~/lxd.old
$ sudo mkdir /var/snap/lxd/common/lxd/disks/
前回と同様に起動時に自動的にマウントするよう、unitファイルを次の手順で作成します。
$ cat <<'EOF' | sudo tee /etc/systemd/system/var-snap-lxd-common-lxd-disks.mount
[Unit]
Description=External Storage for LXD
After=media-data.mount
Before=snap.snap.lxd.daemon.service
[Mount]
What=/media/data/lxd
Where=/var/snap/lxd/common/lxd/disks
Options=bind
[Install]
WantedBy=multi-user.target
[Install]
WantedBy=multi-user.target
RequiredBy=snap.lxd.daemon.service
Requires=media-data.mount
EOF
unitファイルを再読込して、サービスを起動します。
$ sudo systemctl daemon-reload
$ sudo systemctl start var-snap-lxd-common-lxd-disks.mount
$ sudo systemctl enable var-snap-lxd-common-lxd-disks.mount
$ sudo systemctl start snap.lxd.daemon.unix.socket
$ sudo systemctl start snap.lxd.daemon.service
問題なければ次のようにストレージプールの情報が表示されるはずです。
$ sudo lxc storage show default
config:
size: 20GB
source: /var/snap/lxd/common/lxd/disks/default.img
description: ""
name: default
driver: btrfs
used_by:
- /1.0/profiles/default
status: Created
locations:
- none
最後に実際にLXDでインスタンスを作ってみます。
$ sudo lxc launch ubuntu:20.04 focal
Creating focal
Starting focal
ストレージプールの情報を表示すると、ダウンロードしたイメージ、作成したインスタンスがこのストレージプールに紐付いていることがわかります。
$ sudo lxc storage show default
config:
size: 20GB
source: /var/snap/lxd/common/lxd/disks/default.img
description: ""
name: default
driver: btrfs
used_by:
- /1.0/images/982ae89228891ed6306876ad21d83c7c1b08d93e2f470067a890f65426a0bf9e
- /1.0/instances/focal
- /1.0/profiles/default
status: Created
locations:
- none
$ sudo lxc storage info default
info:
description: ""
driver: btrfs
name: default
space used: 1.05GB
total space: 20.00GB
used by:
images:
- 982ae89228891ed6306876ad21d83c7c1b08d93e2f470067a890f65426a0bf9e
instances:
- focal
profiles:
- default
サーバーとして運用することを考えると、Raspberry Piが繋がっているネットワークにコンテナも繋いだほうが便利でしょう。そこでmacvlanの設定も行っておきます。
$ sudo lxc config device add focal eth1 nic nictype=macvlan parent=eth0
$ sudo lxc exec focal ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 00:16:3e:59:35:3f brd ff:ff:ff:ff:ff:ff link-netnsid 0
13: eth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 00:16:3e:33:c7:2b brd ff:ff:ff:ff:ff:ff link-netnsid 0
ここで最初のコマンドの末尾にある「eth0
」はRaspberry Piのインターネットに繋がっているネットワークインターフェースです。もしWiFi側を使う場合は「wlan0
」を指定してください。
たとえばDHCPでeth1にIPアドレスを設定したいのであれば、次のように設定ファイルを用意します。
$ cat <<'EOF' | sudo lxc exec focal -- tee -a /etc/netplan/eth1.yaml
network:
version: 2
ethernets:
eth1:
dhcp4: true
EOF
あとはNetplanで適用するとIPアドレスが割り当てられているはずです。
$ sudo lxc exec focal netplan apply
$ sudo lxc exec focal ip addr show eth1
13: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:16:3e:33:c7:2b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.42.0.218/24 brd 10.42.0.255 scope global dynamic eth1
valid_lft 3565sec preferred_lft 3565sec
inet6 fe80::216:3eff:fe33:c72b/64 scope link
valid_lft forever preferred_lft forever
ここまですればほぼ普通のUbuntuサーバーとして利用可能です。もしLXDのUbuntuインスタンスの上でDockerを動かしたい場合は、次のように名前空間等のネストを許可した上で、再起動しておきます。
$ sudo lxc config set focal security.nesting true
$ sudo lxc restart focal
あとはUbuntuリポジトリの「docker.io」パッケージをインストールしてください。こちらにはLXD上でDockerを動かすために必要なパッチが適用されています。
$ sudo lxc exec focal apt install docker.io
$ sudo lxc exec focal docker run arm64v8/hello-world
その他のLXDそのものの使い方は、第521回の「入門システムコンテナマネージャーLXD 3.0 」にリンクされている各記事を参照してください。
システムの自動アップデートの無効化
Ubuntu Coreは組み込み向けという扱いなので、サーバーとして使うためにはいくつか気をつけるべきことがあります。そのひとつが「自動アップデート機能(auto refresh機能) 」です。
セキュリティの観点に立つと、一般的にはアップデートは常に適用されているほうが望ましい状態となります。Ubuntu Coreはカジュアルな組み込み向け用途を想定しているため、できるだけメンテナンスフリーとなるべく、自動アップデート機能が有効化されています。具体的には6時間に一度、更新をチェックしアップデートを適用します。
しかしながらサーバーとして使う場合は、アップデート中のダウンタイムを考慮しなくてはなりません。特にsnapシステムにおいてベースシステムやカーネルのsnapパッケージが更新された場合は、自動的に再起動します。そこでsnapパッケージのアップデート機構の設定を見直しましょう。
自動アップデートのスケジュールについての詳細はsnapパッケージのドキュメントにある「Managing updates 」の項目を参照してください。残念ながら現在のsnapシステムは、完全に自動アップデートを無効化する公式の方法は存在しません[6] 。できるのは、最大60日遅らせるだけです。
[6] なぜできないかとか、何が必要かとか、歴史的経緯とかをどうしても知りたくて、なおかつ時間が十分に余っている人はこのあたり のスレッド をたどると何か情報を掴める可能性があるかもしれませんし、ないかもしれません。
前回のアップデートと次のアップデート予定の日時は次の方法で確認可能です。
$ snap refresh --time
timer: 00:00~24:00/4
last: today at 17:01 JST
next: today at 22:53 JST
これを、次のように「60日遅らせ」ます。
$ sudo snap set system refresh.hold="$(date --date=+60days +%Y-%m-%dT%H:%M:%S%:z)"
$ sudo snap get system refresh.hold
2021-02-18T20:31:17+09:00
再度確認してみると、「 次のアップデートはホールドされる」予定であることがわかります。
$ snap refresh --time
timer: 00:00~24:00/4
last: today at 17:01 JST
hold: in 60 days, at 17:01 JST
next: today at 22:53 JST (but held)
今の段階では、定期的にホールド期間を増やす方向でしか対応できなさそうです。このあたりはsystemdのタイマーユニット を使って自動化するというのもひとつの手でしょう。ちなみに「snap refresh
」を実行すれば手動でアップデートを実施できます。
なお、アプリが動いている間はアップデートさせない設定 も現在実装中のようです。
Ubuntu Coreをサーバーとして使用するメリット
ここまでRaspberry Pi上のUbuntu Coreを普通のUbuntuサーバー化する方法を説明してきました。それではUbuntu Coreそのものはどんな用途で採用すべきでしょうか。最後にその可能性について考えてみましょう[7]
利便性よりもセキュリティを重視する場合
あらゆるものがネットワークに繋がる時代において、「 セキュリティ」が最も注力すべき項目であることに異論はないでしょう。
セキュリティは利便性とのトレードオフな関係になることが多々あります。つまりよりセキュアな環境にしようと思ったら、どこかで利便性を下げざるを得ません。よくある例がAppArmorをはじめとする、強制アクセス制御(MAC)でしょう。MACでガチガチに固めた環境を作ればセキュアになりますが、いろいろなものが動かなくなります。逆にゆるくしすぎると安全性が下がります。このあたりはディストリビューターやシステム管理者のバランス感覚にかかっているのです。
AppArmorの場合、サーバー版のUbuntuは可もなく不可もなくという無難な設定のみを有効化し、残りはユーザーに任せるというスタンスです。それに対して、Ubuntu Coreではよりきめ細かく設定しているため、「 従来のツールが動かない」ということも多々あります。そもそもシステム管理者がインストールするソフトウェアは、原則としてsnapパッケージであるため、AppArmorやsecccomp等を活用してさらに厳しい制約がかけられています。たとえばsnapパッケージの中からは、基本的にルートファイルシステム上のほとんどのファイルにアクセスできないですし、ホームディレクトリすら明示的な権限の許可が必要です。
このように「基本的に禁止し、本当に必要なときだけ許可する」というスタイルで運用したいのであれば、Ubuntu Coreも選択肢となりえます。
さらにUbuntu Coreは、第646回でも紹介したように「物理的なセキュリティ」に対しても配慮しています。サーバーを誰でもアクセスできる場所に配置することは一般的にはあまりないものの、Raspberry Piのように小型デバイスであれば、小型であるがゆえの配置の自由度を活用するためにも、セキュアでない場所に置く可能性はあるでしょう。それを考えると、Ubuntu Coreのような「簡単にはログインできない」仕組みは有用となるはずです。
ちなみにUbuntu Coreのサイトでは「サポート期間が10年」を謳っていますが、Ubuntu Weekly Topicsの2019年1月25日号 の「注4」でも考察されているように、これは「UbuntuのLTSと同じで5年サポートし、残りの5年は有償サポート」という扱いだと思われます。このあたり公の場ではっきりと言明されることがないため、おそらく事実が判明するのは18.04の最初の5年サポートが終了する、2023年以降になるでしょう。
サーバーの上で動かすものがコンテナだけである場合
Dockerの登場以降「サーバーのアプリケーションはコンテナの中で動かす」手法がいろいろなサービスで当たり前に使われるようになってきました。
コンテナベースで運用する場合、必要なツールやライブラリは個々のコンテナの中に閉じ込めることが一般的です。そうなるとホストシステム自体は「最低限、コンテナシステムが動けば良い」になってきます。コンテナに関わらない余計なツールは極力省いたほうがセキュアになりますし、そもそも複雑なパッケージ管理システム自体不要になるかもしれません。実際、コンテナを動かすためのクラスタリングシステムの構築に特化した「Container Linux」なんてLinuxシステムも作られました。このContainer Linuxは最終的にRed Hatに買収され、その成果はFedora CoreOS/RHEL CoreOS(RHCOS)として活用されていることからも、その有用性はわかるでしょう。
Ubuntu Coreは「Ubuntu版のRHCOS」のような使い方が可能です[8] 。つまりターゲットシステムに「ハイパーバイザーのような薄い管理層」としてインストールし、その上でコンテナを管理するという使い方です。コンテナのハイパーバイザーを自称するLXD や、Dockerツールはもちろんのこと、プロダクション用途でコンテナを使う上で外せないKubernetesに至るまで、そのソフトウェアや構築ツールがsnapパッケージ化されているために、Ubuntu Core上でも簡単に導入可能なのです。
[8] ちなみにRHCOSのベースになっているFedora CoreOSが700MB強のイメージなのに対して、Ubuntu Coreはその半分以下の300MB弱です。中身や機能の比較をしたわけではないので、この数字に意味があるかは不明ですが、Ubuntu Core「も」十分に小さいことがわかるかと思います。
コンテナ化による利便性のひとつが、アップデートプランの建てやすさです。一般的なOSの場合、主要なライブラリはさまざまなソフトウェアで共有されています。あるソフトウェアを更新しようとすると、特定のライブラリの更新も必要で、そうするとそれに依存する他のソフトウェアも更新しなければならず……という「依存関係地獄」に陥った結果、「 何もしないのがベスト」に落ち着く経験をした人もたくさんいることでしょう。snap化やコンテナ化は、「 特定のソフトウェアに必要なものは、そのソフトウェアの専用領域で提供する」スタイルのため、ソフトウェアごとに独立してアップデート可能です[9] 。
[9] もちろん、主要なライブラリに深刻な脆弱性が見つかった場合、「 全部のコンテナを個別にアップデートしなくてはならない」という別の地獄の作業が発生します。ただし依存関係地獄に比べると個々の作業を細分化しやすいため、「 まだましな地獄」ではあるはずです。
つまりUbuntu Core自体は小さく作られているものの、snapという仕組みを介してモジューラブルかつセキュアに機能拡張でき、さらに必要なものはコンテナの中で管理できるということです。うまく動けば。
停電等の突然の障害時に対する耐久性がほしい場合
現代人にとって電気は空気と同じくらい重要です。空気を読むのは苦手だけれども、電気ならその流れも含めてすべて把握できる、という読者も多いでしょう。
商用のサーバーなら、安定的に電気が供給される環境に設置され、万が一のときも無停電電源装置(UPS)等によって適切にシャットダウンされるのが普通です。しかしながら個人用のサーバーで、そこまでするのはコスト的になかなか厳しいでしょう。Raspberry Piをサーバーとして使うなら、パススルー給電できるモバイルバッテリーをUPSとして使うという手はあるものの、通常のUPSのように「給電モードが切り替わったのでシャットダウンする」みたいな対応は難しいです。
また、Raspberry Pi固有の話として、ルートファイルシステムの冗長性を確保するのが難しいという制約もあります。もちろんSDブートは諦めて、USBやネットワーク経由のブートにすることで対応はできるものの、システムが複雑になりがちです。
Ubuntu Coreはシステムの基本部分を読み込み専用で用意し、書き込みできる領域は別パーティションに分離することで、不意の電源断にもある程度耐えられる作りになっています。あくまで、ある程度、です。システムが起動しなくなることはそうそうないものの、書き込み領域の不整合によりサービスが立ち上がらなくなる可能性は依然として残っています。「 システムが起動しないからリモート経由でのリカバリーもできない」という問題は起きにくい作りになっている、程度の安心感だと思っておいてください。
とにかく、ここまでの話をまとめると、Ubuntu Coreを使えば「最初から何もしなくてもある程度セキュア」 で、「コンテナを動かすために必要十分な小さなシステム」 の、「多少雑に扱っても壊れにくい」 サーバーを構築できるというわけです[10] 。
なお、逆に次のような用途だとUbuntu Coreよりは普通のサーバー版のほうがおすすめです。
Linuxサーバーの学習用途
システムをかなり細かくカスタマイズ・チューニングしたい
パッケージ化されていない独自バイナリ・PPAを多用したい
DKMSを利用したい
また、何度も言うようにあくまでUbuntu Coreのシステムは「壊れにくい」であって「壊れるときは壊れる」ことに変わりはありません。商用サーバーほどでもないものの「できる範囲で高耐性化」することで、おうちサーバーでもそれなりにメンテナンスフリーにしようというのが、本記事の主な目的です。
出来合いのものをそのまま使いたいもしくはカーネルのビルドから含めて全部自分でシステムを構築したい、という両極端なケースならUbuntu Coreを、その間に位置する用途なら普通のサーバー版を選ぶと良いでしょう。このようにサーバーとしてのUbuntu Coreの用途は限定されるものの、マッチすればとても便利な仕組みなのです。