Ubuntu Weekly Recipe

第647回 Ubuntu CoreなRaspberry PiをUbuntuサーバーとして使う

この記事を読むのに必要な時間:およそ 10 分

小型・ファンレス・低消費電力・メンテナンスフリーなサーバーは人類の見果てぬ夢です。今回は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 Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。