先日リリースされたUbuntu 18.04 LTSでは、4月にリリースされたばかりのLXD 3.0が導入されました。今回はUbuntu標準のシステムコンテナであるLXDについて、改めて基本的な使い方を説明します。
システムコンテナの管理ツールであるLXD
LXD はCanonical主導のもと開発されているシステムコンテナの管理ツール です。
コンテナ型の仮想環境と言えばDockerが有名ですが、LXDはDockerと同じような技術的要素を用いてはいるものの[1] 、その目的が異なります。Dockerが特定のアプリケーション・プロセスを動かすための環境を構築することを主目的としたアプリ(プロセス)コンテナであるのに対して、LXDはシステムそのものを提供することが目的なのです。
LXDは一度インスタンスを構築しログインしたあとは、普通のLinux環境とほぼ同じように使えます。Dockerはよく「Immutable Infrastracture(不変のインフラ) 」と合わせて語られることが多いですが、LXDで作った環境はMutable(可変)として使われることが一般的です。
つまりLXDはコンテナ技術を利用した、軽量の仮想環境構築ツールとも言えます。その用途を考えるとKVM/libvirtdやVirtualBox、ESXiのLinuxコンテナ版だと思えばいいでしょう。また、第491回 でも紹介されているsystemd-nspawnも直接の競合と言えるかもしれません。
Ubuntu 18.04 LTSのリリースにあわせて、LTSリリースとしてLXD 3.0がリリースされました。LXDにはいくつかのリリースブランチが存在します。
LXD 2.0.x: 2018年4月以前のLTSブランチ
LXD 2.x: 2018年4月以前のフィーチャーブランチ
LXD 3.0.x: 最新のLTSブランチ
LXD 3.x: 現在のフィーチャーブランチ
16.04に合わせてリリースされた前回のLTSである2.0以降、大きな新機能はフィーチャーブランチである2.xに追加され、2.0.xは不具合の修正のみにとどめられていました。それに対して大きな変更が加えられるフィーチャーブランチ側は原則として毎月リリースされるため、LXD 2.1から2.21を経て3.0になるまで、2年間に22回のリリースを行っています。
LXD 3.0は2.x系で追加された新機能や、ユーザーインターフェースの改善などを取り込んだ集大成となるLTSリリースなのです。2.0に比べて格段に改善されていますので、今からLXDを始めるのであれば3.0をおすすめします。
いくつかの改善ポイントをあげてみましょう[2] 。
ホストとコンテナ間でのGPUパススルーやSR-IOVなどの設定が簡単に
storage/networkの設定UIの拡充
UID/GIDマッピングをコンテナごとに設定できるように
事前設定ファイルを用いたlxd init
の自動化
EC2ライクな「インスタンスタイプ」によるリソース制限設定
Dockerライクな「ストレージボリューム」機能
lxc console
によるコンソールへのアタッチ
複数のマシン間のLXDのクラスタリング
ホスト上のキャラクターデバイス・ブロックデバイスのホットプラグ(コンテナへの自動追加)のサポート
ホスト・コンテナ間のネットワーク転送を簡単に実現できるproxyデバイスの追加
なお、Ubuntu 18.04 LTSをインストールもしくは18.04へアップグレードした場合は自動的にLXD 3.0に更新されます。よってLXDのバージョンを2.0系にとどめておきたい場合は、16.04を使い続けてください。
LXDのインストール方法
Ubuntu 18.04 LTSのサーバー版をインストールした場合は、最初からLXD 3.0 がインストールされています。デスクトップ版の初期状態の場合は次の方法でインストールしてください。
$ sudo apt install lxd btrfs-progs
btrfs-progsパッケージは、LXDのストレージバックエンドとしてbtrfsを使いたい場合に必要です。他のバックエンドを使う場合は、適宜読み替えてください。
Ubuntu 16.04 LTSの場合、上記コマンドだと2.0系がインストールされます。Backportリポジトリを使うことで2.x系をインストール可能でありますが。まだ3.0にはなっていません。将来的に3.0になる可能性はあります。
(Ubuntu 16.04 LTSで2.0系よりあとのリリースをインストールする方法)
$ sudo apt -t xenial-backports install lxd
18.04/16.04共通の方法としてsnapパッケージ版LXD をインストールする方法もあります。特に常に最新のフィーチャーリリースを追いかけたい場合におすすめの方法です。
snap版はchannelを利用してリリースブランチを指定可能です。
latest: 現在のフィーチャーブランチ
2.0: 一つ前のLTSブランチ
3.0: 最新のLTSブランチ
$ sudo snap install lxd ←常に最新のフィーチャーリリースに更新される
$ sudo snap install lxd --channel=2.0 ←2.0系の最新LTS版を使用する
$ sudo snap install lxd --channel=3.0 ←3.0系の最新LTS版を使用する
ちなみにLXDの管理コマンドであるlxc
コマンドを管理者権限なしに実行したい場合は、lxdグループに所属する必要があります。
Ubuntuサーバーをインストールした場合は、インストール時に作成したアカウントは自動的にlxdグループに所属します。あとからLXDをインストールした場合は、インストール時にsudoグループに所属しているユーザーのみがlxdグループに追加されます。
上記に該当しないユーザーを管理者権限なしにLXDの操作をしたい場合は、個別にグループへの追加を行ってください。
LXDの初期設定
LXDを利用する前に、一度だけLXDサービスの初期設定を行う必要があります。特にこだわりがなければ、次のように初期値をそのまま設定すると良いでしょう。
$ sudo lxd init
Would you like to use LXD clustering? (yes/no) [default=no]:
=> 異なるマシンに存在するLXDサービスと連携したいかどうか
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 (btrfs, dir, lvm) [default=btrfs]:
=> ストレージバックエンドの選択
速度・利便性の両方においてbtrfsを選ぶのが無難です
ホストのファイルシステムがbtrfsである必要はありません
Create a new BTRFS pool? (yes/no) [default=yes]:
=> btrfsのプールを作成するかどうか
Would you like to use an existing block device? (yes/no) [default=no]:
=> 既存のブロックデバイスを利用するかどうか
利用しない場合はbtrfsでフォーマットしたイメージファイルを作成します
Size in GB of the new loop device (1GB minimum) [default=21GB]:
=> ストレージプールのサイズ
Would you like to connect to a MAAS server? (yes/no) [default=no]:
=> MAASサーバーに接続するかどうか
Would you like to create a new network bridge? (yes/no) [default=yes]:
=> LXD用にネットワークブリッジI/Fを作成するかどうか
What should the new bridge be called? [default=lxdbr0]:
=> ネットワークブリッジのデバイス名
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
=> コンテナインスタンス向けIPv4アドレスの設定
autoにすると適当なサブネットマスクを割り当てます
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
=> コンテナインスタンス向けIPv6アドレスの設定
Would you like LXD to be available over the network? (yes/no) [default=no]:
=> LXDサービスをネットワーク上の他のマシンから見えるようにするか
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]
=> 取得したコンテナベースイメージを自動的に更新するか
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:
=> 上記設定情報をYAML形式で出力するか
ちなみにLXD 2.10からは、LXDを利用している状態でも再度「sudo lxd init
」を実行することで設定を変更できるようになっています。
おそらく初期設定のうちもっとも利用に影響しそうなのがストレージプールの設定でしょう。ストレージプールは、LXDで作成するコンテナを保存する領域です。バックエンドとしてbtrfs、LVM、ZFS、Ceph、それにディレクトリをそのまま保存する方法の5つから選択できます。ただしディレクトリ以外を使いたい場合は、別途パッケージのインストールなどが必要です。サーバー版をインストール直後のパッケージ構成ならbtrfs、LVM、ディレクトリが使えます。デスクトップ版のパッケージ構成ではディレクトリのみが使えます。
ストレージバックエンドによって、LXD上で利用できる機能に違いがあります。詳しい各バックエンドの違いはLXDドキュメントの「Storage Backends and supported functions 」を参照してください。
デスクトップ上で試す場合など、環境による指定がないのであればbtrfsを選ぶと良いでしょう。ただしデスクトップ版でbtrfsを使いたいなら、あらかじめbtrfs-progsパッケージもインストールしておいてください。
btrfsを使う場合、以前は空のブロックデバイスを用意する必要があったのですが、最近はループバックイメージファイルを使った設定が可能になっています。これによりお手軽にLXDのほぼすべてのストレージ関連の機能を使えるようになります。
$ lxc storage list
+---------+-------------+--------+--------------------------------+---------+
| NAME | DESCRIPTION | DRIVER | SOURCE | USED BY |
+---------+-------------+--------+--------------------------------+---------+
| default | | btrfs | /var/lib/lxd/disks/default.img | 1 |
+---------+-------------+--------+--------------------------------+---------+
$ sudo file /var/lib/lxd/disks/default.img
/var/lib/lxd/disks/default.img: BTRFS Filesystem label "default", (後略)
しかしながらbtrfsやZFSのループバックイメージファイルをプロダクション用途で使うのは推奨されていないようです。もし本格的に使用する予定であれば、専用のブロックデバイスを用意しましょう。
たとえばbtrfsの場合、あらかじめ未フォーマットの/dev/sdb2
などを用意しておきます。lxd init
時の「Would you like to use an existing block device? (yes/no) [default=no]:」で「yes」と回答すれば、「 Path to the existing block device:」が表示されるのでそこで「/dev/sdb2
」と回答します。lxd init
完了後に「/dev/sdb2
」がbtrfsでフォーマットされ、専用のストレージプールとして利用できます。
$ lxc storage show default
config:
source: 7d555893-d8a9-4c42-a609-f4f40900ee7b
volatile.initial_source: /dev/sdb2
description: ""
name: default
driver: btrfs
used_by:
- /1.0/profiles/default
status: Created
locations:
- none
$ lxc storage info default
info:
description: ""
driver: btrfs
name: default
space used: 16.62MB
total space: 1.36TB
used by:
profiles:
- default
LXDの基本的な使い方
コンテナのライフサイクルから、LXDの基本的な使い方を見ていきましょう。
インスタンスの作成と起動
まずコンテナのイメージからインスタンスを作成・起動する方法です。
$ lxc launch ubuntu:18.04 bionic
bionic を作成中
bionic を起動中
上記コマンドを実行することによりリモートイメージーサーバー であるubuntuから、イメージ名 18.04をダウンロードし、bionicという名前でコンテナを作成 かつ起動 します。
LXDにはベースイメージファイルを公開するイメージサーバーという概念があります。ローカルのLXDサービス自身もイメージサーバーになるので、任意のイメージをインターネットに公開することも可能です。LXDには最初からいくつかのリモートイメージサーバーが登録されています。
$ lxc remote list
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| NAME | URL | PROTOCOL | AUTH TYPE | PUBLIC | STATIC |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| images | https://images.linuxcontainers.org | simplestreams | | YES | NO |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| local (default) | unix:// | lxd | tls | NO | YES |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| ubuntu | https://cloud-images.ubuntu.com/releases | simplestreams | | YES | YES |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| ubuntu-daily | https://cloud-images.ubuntu.com/daily | simplestreams | | YES | YES |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
NAMEフィールドで指定された名前をイメージ名の前のコロンの前に追加することで、リモートイメージサーバーを指定できます。
URLとPROTOCOLのフィールドは提供元のURLとそのイメージの提供方法です。
AUTH TYPEフィールドはイメージサーバーとして利用するにあたって認証が必要かどうか、PUBLICフィールドはネットワーク上の他のマシンに公開されているかどうか、STATICはlxc remote
で設定変更可能かどうかを示します。
イメージサーバーからダウンロードするイメージは、原則として最新版です。特にubuntuイメージの場合、cloud-imageとして定期的にアップデートが適用されたイメージを公開していますので、インストーラーイメージとは違いほとんどすべてのパッケージの更新が適用済みの状態となります。ダウンロードしたイメージは次のコマンドで確認できます。
$ lxc image list
+-------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+-------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
| | 9879a79ac2b2 | no | ubuntu 18.04 LTS amd64 (release) (20180522) | x86_64 | 172.97MB | May 27, 2018 at 9:10am (UTC) |
+-------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
初回はダウンロードに時間がかかりますが、一度ダウンロードしたイメージファイルはキャッシュに保存されるため、次回からはすぐに作成できます。なお、LXDサービスがバックグラウンドでダウンロードしたイメージの更新作業も行います。
lxc launch
コマンドは、実際には次の2つのコマンドを1つにまとめたものです。
$ lxc init ubuntu:18.04 bionic
$ lxc start bionic
もし作成したコンテナを起動する前に設定を行いたい場合は、lxc launch
ではなくこちらのコマンドを使うと良いでしょう。
コンテナの操作
作成したコンテナはlxc list
コマンドで確認できます。
$ lxc list
+--------+---------+--------------------+-----------------------------------------------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+--------+---------+--------------------+-----------------------------------------------+------------+-----------+
| bionic | RUNNING | 10.41.184.9 (eth0) | fd42:dbb6:9ea0:3ab0:216:3eff:fed7:124e (eth0) | PERSISTENT | 0 |
+--------+---------+--------------------+-----------------------------------------------+------------+-----------+
起動中のコンテナはSTATEフィールドがRUNNINGになります。IPアドレスが割り当てられているので、コンテナ内部でSSHサーバーが動いていたらそのままログインできます。
また、lxc info
コマンドでコンテナインスタンスの詳細を確認できます。
$ lxc info bionic
コンテナ名: bionic
リモート名: unix://
アーキテクチャ: x86_64
作成日時: 2018/05/27 09:10 UTC
状態: Running
タイプ: persistent
プロファイル: default
Pid: 2311
IPアドレス:
eth0: inet 10.41.184.9 veth4335QT
eth0: inet6 fd42:dbb6:9ea0:3ab0:216:3eff:fed7:124e veth4335QT
eth0: inet6 fe80::216:3eff:fed7:124e veth4335QT
lo: inet 127.0.0.1
lo: inet6 ::1
リソース:
プロセス数: 36
CPU使用量:
CPU使用量(秒): 6
メモリ消費量:
メモリ (現在値): 132.04MB
メモリ (ピーク): 215.07MB
ネットワーク使用状況:
eth0:
受信バイト数: 306.69kB
送信バイト数: 12.77kB
受信パケット: 331
送信パケット: 158
lo:
受信バイト数: 892B
送信バイト数: 892B
受信パケット: 12
送信パケット: 12
Ubuntuのcloud-imageの場合、SSHサーバーは起動しますし、「 ubuntu」アカウントが自動的に作成されますが、パスワードが無効化されています。しかしながらcloud-initを使用しない限り、SSH公開鍵は設定されません。つまりリモートログインする方法がありません。プロダクション用途での正しい対応はcloud-initを利用して、コンテナ内部の初期設定を行うことですが、ここではad-hocに解決してみましょう。
lxc exec
コマンドを使うと、特定のコンテナに対して特定のコマンドを実行可能です。コマンドとしてbashを指定すると、コンテナ内部のrootアカウントとして「ログイン」できます[3] 。
[3] 実際はログインシェルとして動作するわけではなく、ログインしているように見えるというだけです。コンテナにローカルでログインするのであれば「lxc console
」が使えますが、ubuntuアカウントのパスワードが未設定である以上、こちらも利用できません。
$ lxc exec bionic bash
root@bionic:~#
たとえばGitHubに登録しているSSHの公開鍵を、ubuntuアカウントの~/.ssh/authorized_keys
に追加したい場合は、ssh-import-idを使うと良いでしょう。
root@bionic:~# sudo -i -u ubuntu ssh-import-id gh:(GitHubのアカウント名)
コンテナ内部とファイルのやりとり
先程実行したbashのプロセスをコンテナの中から見てみましょう。
root@bionic:~# ps -f $$
UID PID PPID C STIME TTY STAT TIME CMD
root 321 0 0 09:49 ? Ss 0:00 bash
コンテナの中のプロセスIDは321で、UIDはroot(UID=0)であることがわかります。しかしながらホスト上で「ps -fe | grep "321.*bash"
」しても該当するプロセスは見つかりません。これはプロセス名前空間を使って、コンテナ上のプロセスIDはホスト上のそれから分離されているためです。ホスト上の該当するプロセスは、プロセスのstatusファイルのNSpidフィールドで確認できます。
$ grep NSpid.*321$ /proc/*/status
/proc/22494/status:NSpid: 22494 321
$ ps -f 22494
UID PID PPID C STIME TTY STAT TIME CMD
165536 22494 22485 0 18:49 pts/5 Ss+ 0:00 bash
これによりホスト上のプロセスIDは22494になっていることがわかります。さらにUIDも1665536になっています。これはコンテナ上のrootアカウントは、ホスト上から見るとrootではないということです。LXDでは特に設定しなければ非特権コンテナとして作成します。これによりコンテナ上で特権を取得できたとしても、かんたんにはホスト上の特権を取得できません。何か危険な動作をするかもしれないコードを環境を隔離して実行する際にもLXDは有効なのです。
ユーザー名前空間を用いたUID/GIDをホストから分離する仕組みは、root以外にも適用されます。プロセスもユーザー名も隔離されているため、コンテナとファイルのやり取りは何かと不便です。そこでLXDにはlxc file
を用いたファイルの管理方法が存在しています。
コンテナからファイルを取得する
$ lxc file pull コンテナ名/フルパス 保存先
コンテナにファイルを渡す
$ lxc file push ファイル コンテナ名/フルパス
コンテナ上のファイルを削除する
$ lxc file delete コンテナ名/フルパス
コンテナ上のファイルを編集する
$ lxc file edit コンテナ名/フルパス
気をつけなければいけないのは、コンテナ側のファイル名は「コンテナ名/フルパス」の書式であるということです。sampleコンテナの/etc/environment
なら「sample/etc/environment
」になります。「 -r
」オプションをつけると、再帰的に受け渡しします。またcpコマンドなどと同じく送信元のファイル(=最後の引数を除いたファイル)は複数指定可能です。その場合、保存先はディレクトリになります。「 -p」オプションを付けることで保存先のパスに合わせてディレクトリを作成します。
またlxc file push
の場合は、「 --uid」「 --gid」「 --mode」オプションによって保存先のファイルオーナーやモードを変更できます。
単にファイルをやり取りするのであれば、SSH経由でも可能です。lxc file
を使う一番のメリットはコンテナ間でも同じインターフェースを使えることでしょう。特に別のホスト上にあるLXDサービスのコンテナに対してもlxc file
を用いてファイルを送受信することが可能です。
lxc file edit
で編集時に使用するテキストエディターは、環境変数VISUAL、環境変数EDITOR、editorコマンドの順番で評価されます。Ubuntuをインストールした直後の環境であれば、第518回 でも紹介したGNU nanoが起動することでしょう。
コンテナを停止・終了する
lxc stop
でコンテナを停止できます。
$ lxc stop bionic
もしうまく停止できないようなら「--force
」オプションを付けてみましょう。また、コンテナ内部でshutdownコマンドやhaltコマンド使っても停止できます。
一度停止したコンテナはlxc start
で起動できます。また、lxc restart
でコンテナを再起動できます。
startやstop、restartサブコマンドは「--all
」を付けることで作成済みのすべてのコンテナに適用可能です。
コンテナを削除する
使わなくなったコンテナインスタンスはlxc delete
で削除できます。
$ lxc delete bionic
一度削除したコンテナインスタンスは復旧できませんので注意してください。
LXDでコンテナ生活を
このようにLXDを使うと、お手軽に隔離環境を構築できます。
今回は紹介しませんでしたが、Ubuntuの別のリリースはもちろんのこと、imagesリモートサーバー には他のディストリビューションイメージが用意されていますので、一台のマシンで複数のディストリビューションをテストできます。コンテナ機能を利用しているため、仮想マシンに比べるとほとんどリソースを消費しません。ノートPCで数十台のインスタンスを立ち上げることも可能なのです。
単に決まったサービスを立ち上げるだけであればDockerを始めとするOCI準拠なあれやこれやを使うほうがシンプルですし、ドキュメントやノウハウも豊富です。しかしながら本当に欲しいのはサービスではなく、サービスを動かすためのホストから隔離された環境であれば、LXDを試してみませんか?