みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

lxd入門

はじめに

コンテナ技術としてはDockerが有名だが、こちらはアプリケーションコンテナとなっている。 LXDはシステムコンテナという位置づけになっているが、これらがどう違っているのかわからなかった。

DockerとLXDの大きな違いはセッションのライフサイクルとなる。

Dockerでもbashを実行することでコンテナにログインすることができる。しかし、シェルからexitすると、コンテナそのものが終了してしまう。

LXDはシステムコンテナなのでbashでログインして作業し、そこからexitしてもコンテナそのものは起動したままになる。

つまり、Dockerはあるアプリケーションを実行するための実行環境としてコンテナを使用するもので、 LXDはVMのような仮想的な環境としてコンテナを使用するものという違いがある。

LXDでも下記のようにlxc execで特定のコマンドを実行することもできるが、コンテナ自体はずっと可動している。

$ lxc exec <container name> -- ls

lxdのインストール

Ubuntu 20.04環境ではsnapで導入するのが無難

$ sudo snap install lxd

コンテナ環境の初期化

$ sudo lxd init

初期のストレージプールの作成などが行われる。

グループの追加

root以外でlxcを実行可能にする。

$ sudo gpasswd -a ${USER} lxd

UID/GIDマッピングの準備

LXDコンテナ中でホストのホームディレクトリを読み書きする方法

ホーム以外にも、ホストのディレクトリをコンテナ内にマッピングしてアクセス可能にするために必要。

ホスト環境の/etc/subuid/etc/subgidマッピング情報が必要になる。

$ echo "root:$(id -u):1" | sudo tee -a /etc/subuid
$ echo "root:$(id -g):1" | sudo tee -a /etc/subgid

IDのマッピングを許可する範囲を指定する。書式は下記。

USER:START:COUNT

上記の例ではマッピングを許可するのはコンテナのrootで、マッピングを許可されるのはホストの$(id -u)のUIDを持つユーザーから1個と指定している。

コンテナの作成

手順を汎用化するために環境変数CONTAINERにコンテナ名を設定しておく。 ${CONTAINER}の部分をコンテナ名直打ちにしても問題ない。

$ lxc init ubuntu:18.04 ${CONTAINER}

ubuntu:18.04はインターネット経由で提供されているベースイメージ。

使用可能なものは下記のコマンドで確認できる。

$ lxc image list images:

下記のようにするとディストリビューションを絞って表示できる。

$ lxc image list images:ubuntu

コンテナの起動

$ lxc start ${CONTAINER}
$ lxc exec ${CONTAINER} -- /bin/bash

一般ユーザーの作成

ユーザー名は任意、 コマンドの例では環境変数USERNAMEに設定してある。 ホストPCの${USER}と併せておくといろ都合がよい。

# adduser ${USERNAME}

ubuntu:18.04のコンテナは初期状態でubuntuユーザーがいるためここで作るユーザーのUIDは1001になる。

sudo可能にする

一般ユーザーにsudo権限を追加する。

# gpasswd -a ${USERNAME} sudo

ubuntu 16.04や18.04ではこのままではsudo実行時に下記のエラーになる。

sudo: no tty present and no askpass program specified

/etc/sudoers.d/にファイルを作成して、NOPASSWDを設定する。

$ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/${USERNAME}

2回目以降

一度抜けたあとに再度コンテナに入る場合は下記のようにする。

$ lxc exec ${CONTAINER} -- /bin/bash

上記では毎回rootでログインすることになるので、作成した一般ユーザーでログインしたい場合は下記のようにする。

$ lxc exec ${CONTAINER} -- su --login ${USER}

上記はコンテナの一般ユーザー名がホストの${USER}と一致していることを想定している。

コンテナの一般的な操作

起動

$ lxc start ${CONTAINER}

終了

$ lxc stop ${CONTAINER}

作成済みのコンテナの確認

$ lxc list

削除

$ lxc delete ${CONTAINER}

ホストのディレクトリのバインド

ホストとコンテナのUID/GIDを紐付ける

lxc config set <container name> raw.imapで実際の紐付けを行う。

書式は下記のようになっている。

"(uid|gid|both) <ホスト側のID> <コンテナ側のID>"

UIDとGIDをどちらも紐付けしたい場合はbothそれぞれどちらかの場合はその値を指定する。

コンテナで一般ユーザーを追加した場合は1001になるので、ホストの実行中のユーザーとコンテナの追加した一般ユーザーを紐付ける場合下記のようになる。

$ lxc config set ${CONTAINER} raw.idmap "both $(id -u) 1001"

この例ではホストのUIDとGIDが一致している場合を想定している。

ディレクトリのバインド

ディレクトリのバインドはlxc config device addで行う。

書式は下記のようになっている。

lxc config device add <コンテナ名> <名前> disk source=<ホスト上のパス> path=<コンテナ上のパス>

コンテナの/mnt/hdd/work/home/${USER}/workに紐付ける場合下記のようにする。

$ lxc config device add ${CONTAINER} work disk \
    source="/mnt/hdd/work" \
    path="/home/${USER}/work"

有効化するにはコンテナを再起動する必要がある。

$ lxc restart ${CONTAINER}

まとめ

Dockerでもbashを実行してコンテナの中にログインすることができるため、LXDを使用するモチベーションがわからなかったが、 コンテナにログインして、ホストのリソースをフルに間借りできる仮想環境として利用するにはLXDのほうが使いやすいということがわかった。

ちなみに、LXD自体はコンテナの他にも同じコマンド体系でVMを実行することもできる。