Ubuntu18.04でLXD3.18をビルドからコンテナのデプロイまで

# はじめに
このエントリーは，CyberAgent20新卒エンジニアAdvent Calendar 2019の2日目の記事です.
大学院の研究でLXDを取り扱っているので，LXDのビルドからコンテナのデプロイまでを紹介します．
興味があったら，ぜひやってみてください．

# 目次
1. CRIUのビルドとインストール
1. LXCのビルドとインストール
1. LXDのビルドとインストール
1. LXD起動の準備とコンテナのデプロイ

# 1. CRIUのビルドとインストール
LXDのコンテナライブマイグレーションを行うためには，Linuxのプロセスマイグレーションツールである[CRIU](https://criu.org/Main_Page)が必要です．そのため，CRIUのビルドとインストールを行います．

## CRIUのビルド準備(必要なパッケージのインストールとソースコードの取得)
ここはほとんど，[公式ページ](https://criu.org/Installation)の「Installing build dependencies」情報とおりです．

何行かに分かれていたり，私の環境では足りなかったりするパッケージがあったりと面倒なので，全部インストールするコマンドを記載します

```bash
sudo apt install make gcc libprotobuf-dev libprotobuf-c0-dev \
protobuf-c-compiler protobuf-compiler python-protobuf \
pkg-config python-ipaddress libbsd-dev iproute2 libcap-dev libnl-3-dev \
libnet-dev libaio-dev asciidoc
```

続いて，ソースコードを取得します．GitHubからCloneするので，gitがない場合はインストールしてください．
ソースコードは[このリポジトリ](https://github.com/checkpoint-restore/criu)から取得できます．

## ビルドとインストール
ビルドとインストールも[公式ページ](https://criu.org/Installation)の「Building the tool」，「Installing」のとおりです．
ただ，criuリポジトリのデフォルトブランチでは動作が不安なので，リリースを確認して直近のタグにチェックアウトしてからビルドすることをおすすめします．ここでは3.13とします．

```bash
make
sudo make install
```

正常にビルドされインストールも完了したことを確認するため，以下のコマンドを実行します．
CRIUのバージョンやgitのcommit id等の情報が表示されれば，ひとまず安心です．

```bash
sudo criu --version
```

# 2. LXCのビルドとインストール
LXDは内部でLXCをコンテナランタイムとして利用することで，コンテナの作成や削除を行っています．そのため，LXCのビルドとインストールを行います．

## LXCのビルド準備(必要なパッケージのインストールとソースコードの取得)
LXCのビルドには，いくつかのビルドツールが必要なのでそれらをインストールします．それぞれのパッケージについては詳しく説明しません(私も理解していないため)．

```bash
sudo apt install libtool automake docbook m4
```

続いて，ソースコードの取得です．
[このリポジトリ](https://github.com/lxc/lxc)からソースコードをクローンします．CRIUと同様にデフォルトのブランチでは動作が不安なため，直近のリリースタグにチェックアウトしておくと安心です．ここでは，3.1.0とします．

## ビルドとインストール
[このリポジトリ](https://github.com/lxc/lxc)のREADMEにかかれているとおりです．以下のコマンドを実行しましょう．

```bash
./autogen.sh #configureの生成
./configure #Makefileの生成
make #ビルド
sudo make install #インストール
```

# 3. LXDのビルドとインストール
ようやくLXDのビルドとインストールを行います．

## LXDのビルド準備(必要なパッケージのインストールとソースコードの取得)
必要なパッケージは[このリポジトリ](https://github.com/lxc/lxd)のREADMEに記述されていますが，lxcのライブラリに関してはビルドしたライブラリを利用するため，インストールしません．ここでGo言語もインストールされています．

```bash
sudo apt install acl autoconf dnsmasq-base \
git golang libacl1-dev libcap-dev \
libtool libuv1-dev make pkg-config rsync squashfs-tools \
tar tcl xz-utils ebtables
#liblxc1 liblxc-dev これらはインストールしない 
```
セキュリティに係るパッケージをインストールします．

```bash
sudo apt install libapparmor-dev libseccomp-dev libcap-dev
```
LXDのストレージ用パッケージをインストールします

```bash
sudo apt install lvm2 thin-provisioning-tools btrfs-tools
```
テスト用パッケージをインストールします．(LXDの開発者でなければ必要ないでしょう)

```bash
sudo apt install curl gettext jq sqlite3 uuid-runtime bzr socat
```

続いて，ソースコードの取得を行います．LXDはGo言語で実装されているので，Goのお作法でソースコードを取得するのが良いです．

```bash
go get github.com/lxc/lxd
```

## 依存ライブラリのビルド
まずは，依存するライブラリのビルドです．
その前に，やはりデフォルトのブランチでは動作が不安なため，直近のリリースタグにチェックアウトします．ここでは，3.18とします．
`go get`で取得したソースコードは`${GOPATH}/src/github.com/lxc/lxd`にあります．

```bash
make deps
```
エラーなくライブラリのビルドが終了すると，以下のような情報が表示されるはずです．

```
export CGO_CFLAGS="${CGO_CFLAGS} -I${GOPATH}/deps/sqlite/ -I${GOPATH}/deps/dqlite/include/ -I${GOPATH}/deps/raft/include/ -I${GOPATH}/deps/libco/"
export CGO_LDFLAGS="${CGO_LDFLAGS} -L${GOPATH}/deps/sqlite/.libs/ -L${GOPATH}/deps/dqlite/.libs/ -L${GOPATH}/deps/raft/.libs -L${GOPATH}/deps/libco/"
export LD_LIBRARY_PATH="${GOPATH}/deps/sqlite/.libs/:${GOPATH}/deps/dqlite/.libs/:${GOPATH}/deps/raft/.libs:${GOPATH}/deps/libco/:${LD_LIBRARY_PATH}"
```
 
## ビルドしたライブラリをOSに共有ライブラリとして登録する
CGO_CFLAGSとCGO_LDFLAGSはそのまま，`.bashrc`などに記述すればよいのですが，LD_LIBRARY_PATHに関しては少し工夫が必要です．

後の話なのですが，ビルドしたLXDのバイナリはsudoで実行する必要があります．そのため，そのままLD_LIBRARY_PATHを`.bashrc`に記述するとsudoで実行する場合，ビルドしたライブラリが見えません．

この問題を解決するため，共有ライブラリとしてOSに登録する必要があります．

Ubuntu18.04の場合，`/ld.so.conf.d/`配下にライブラリが配置されているパスが記述されている設定ファイルを配置することで，そのパスを見て共有ライブラリを登録してくれます．

そのため，以下のファイルを作成します．
${GOPATH}の部分は，環境に合わせて変更された内容が依存ライブラリビルド時に表示されているはずなので，その内容と読み替えてください．

```/ld/so.conf.d/lxd-lib.conf
${GOPATH}/deps/sqlite/.libs/
${GOPATH}/deps/dqlite/.libs/
${GOPATH}/deps/raft/.libs
${GOPATH}/deps/libco/
```

追加した設定ファイルの内容をOSに知らせるために，以下のコマンドを実行します．

```bash
sudo ldconfig
```

## LXDのビルドとインストール
続いて，LXD本体のビルドを行います．ビルドはとっても簡単です．

```bash
make
```
ビルドが正常に終了した場合，${GOPATH}/bin配下に`lxd`と`lxc`というバイナリが配置されているはずです．
そのため，特にインストール操作はありません．

### LXDビルドのトラブルシューティング(go-dqliteのエラー対処)
簡単なはずなのですが，私の環境ではLXDが利用するGo言語のパッケージである`go-dqlite`でエラーが発生しました．そのエラーは以下です．

```
use of internal package not allowed
```

これは，LXDが利用しているパッケージが`github.com/CanonicalLtd/go-dqlite`であるにも関わらず，その`github.com/CanonicalLtd/go-dqlite`は`github.com/canonical/go-dqlite`というパッケージの複製なので，ソースコード内のimport分が以下の様になってしまっていることが原因です．

```golang
import (
    github.com/canonical/go-dqlite/internal/bindings
    //本当は github.com/CanonicalLtd/go-dqlite/internal/bindings であるべき
)
```

なので，この様な状態になってしまっているファイルをすべて修正します．
具体的には，`github.com/canonical/go-dqlite`になっている部分を`github.com/CanonicalLtd/go-dqlite`に変更します．

これで，上記のエラーは解消できました．

# 4. LXD起動の準備とコンテナのデプロイ
LXD起動の準備をします．

## subuid/subgidの設定
[ここ](https://github.com/lxc/lxd#machine-setup)に記述されている通り，LXDでコンテナのデプロイをするためには，以下の操作が必要です．

```bash
echo "root:1000000:65536" | sudo tee -a /etc/subuid /etc/subgid
```

## lxdグループの作成と，ユーザの追加
sudoなしでコマンドを実行できるようにします．
まず，以下のコマンドでlxdグループを作成します．

```bash
sudo groupadd lxd
```

その後，以下のコマンドでlxdを操作したいユーザをlxdに追加します．

```bash
sudo usermod -aG lxd $USER
```

## LXDの実行と初期化
まず以下のコマンドで，LXDを起動します．
--group オプションではsudoなしでlxcコマンドを実行できるようにするgroupを指定します．
logfileオプションは，ログを吐き出す場所を指定します．

```
sudo $GOPATH/bin/lxd --group lxd --logfile=/var/log/lxd/lxd.log
```

一度実行が確認できたら，systemdに自動起動してもらうようにします．
以下のserviceファイルを`/lib/systemd/system`配下に作成します．

```/lib/systemd/system/lxd.service
[Unit]
Description=LXD - main daemon

[Service]
ExecStart=/home/yota/go/bin/lxd --debug --group lxd --logfile=/var/log/lxd/lxd.log

[Install]
WantedBy=multi-user.target
```

続いて，以下のコマンドでLXDの設定を初期化します．
LXDをお好みに設定してください．
ただし，外部公開は必ずしてください．

```
sudo $GOPATH/bin/lxd init
```
参考までに，私はこの様な感じです

```bash
Would you like to use LXD clustering? (yes/no) [default=no]: 
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: 
Name of the storage backend to use (btrfs, dir, lvm) [default=btrfs]: 
Create a new BTRFS pool? (yes/no) [default=yes]: 
Would you like to use an existing block device? (yes/no) [default=no]: 
Size in GB of the new loop device (1GB minimum) [default=100GB]: 
Would you like to connect to a MAAS server? (yes/no) [default=no]: 
Would you like to create a new local network bridge? (yes/no) [default=yes]: 
What should the new bridge be called? [default=lxdbr0]: 
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
Would you like LXD to be available over the network? (yes/no) [default=no]: yes
Address to bind LXD to (not including port) [default=all]: 
Port to bind LXD to [default=8443]: 
Trust password for new clients: 
Again: 
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]: yes
```

## コンテナのデプロイ
以下のコマンドで軽量なalpineイメージでコンテナをデプロイします．

```bash
sudo $GOPATH/bin/lxc launch images:alpine/3.9 hoge 
# hogeはコンテナの名前
```

## コマンドの実行
以下のコマンドで起動したコンテナのashに入って色々操作できます.

```bash
lxc exec hoge /bin/ash
```

# おわりに
いかがでしょうか？
Docker以外のコンテナランタイム使ってみたいなと思った方はぜひLXD使ってみてください．

# ところで...
CyberAgentで21卒の本選考がはじまりました！
選考については[こちら](
https://www.cyberagent.co.jp/careers/special/engineer2021/?utm_source=twitter&utm_medium=sns&utm_campaign=social)を御覧ください！！

早期エントリーには就業型インターンシップの特典があるそうです．
このインターンではエンジニアとして個人で勉強していたり大学で勉強しているだけでは獲得できない経験が得られました！
それだけでなく，社内エンジニアの人の良さを知ることもでき会社の様子を知るのにも最適でした！
少しでも気になっている方は，ぜひエントリーを考えてみてください！！！！