Conjure-up 2.0.1 を使って Ubuntu 16.04 上に OpenStack Mitaka を構築する

## Abstract
Ubuntu 16.04 LTS (Xenial) をサポートした Conjure-up 2.0.1 が先日リリースされました。新しい Conjure-up を使ってUbuntuマシン一台で OpenStack Mitaka を構築してみたのでその手順をご紹介します。

## Conjure-up とは
Conjure-up は、KubernetesやDeepLearningのフレームワークといった、導入に手間のかかる大規模なソフトウェアを、 Juju や LXD を使ってできるだけ簡単に導入するツールです。[GitHub上にリポジトリ](https://github.com/conjure-up/conjure-up)があり、バージョン2.0のベータ版公開から長らく開発が進められていましたが、先日ようやく2.0.1リリース版が公開されました。

ソフトウェアの導入をどのように簡単化するか、全体像を理解するためにも、Conjure-up が用いている Juju と LXD について説明をしたいと思います。

### Juju: デプロイツール
Juju は Apache や MySQL といったクラウドアプリケーションのデプロイ、設定、管理を行うことができる Ubuntu 用のツールです。デプロイツールというと Chef や Puppet がありますが、 Juju はそれらの Ubuntu に特化したものという位置づけでしょうか[2]。詳しい説明は[公式のドキュメント](https://jujucharms.com/docs/2.0/about-juju)が参考になりますが、 Juju では各クラウドアプリケーションを Charm という単位で扱い、設定ファイルを元に Charm に書かれたスクリプトを実行することで Docker や LXD でコンテナを作り、その上にアプリケーションのデプロイを行います。

複雜なミドルウェアの設定や管理を行うこのようなツールはサービスオーケストレーションツールと呼ばれているみたいです。

### LXD: コンテナハイパーバイザ
LXD (Linux Container Daemon) は LXC (Linux Container) を使って一つのマシン上で複数のLinuxシステムを走らせるコンテナ仮想化ツールです。
似たようなツールとして Docker がありますが、 Docker はアプリケーション単位での隔離を目的としているのに対し、 LXD ではシステム単位での隔離を目的としており REST API をサポートするなどの違いがあります[1]。

### Conjure-up は何をするか
では Conjure-up は Juju や LXD を使ってどのように大規模ソフトウェアの導入を支援するか、と言う話ですが、 OpenStack を例にとって説明したいと思います。

OpenStack はクラウドコンピューティングを実現するためのソフトウェアプラットフォームですが、単体ではなく以下に上げたような複数の構成要素から成っています。

* **Nova**: 仮想インスタンスを動作させる
* **Neutron**: 仮想ネットワークの構成と管理を担う
* **Cinder**: ストレージを提供する
* **Keystone**: 認証情報を管理する
* **Glance**: 仮想インスタンスの起動イメージを管理する
* **Swift**: オブジェクトストレージを提供する
* **Horizon**: ダッシュボード(Web UI)を提供する
* ...

理想的にはこれらのアプリケーションは別々の物理マシンで立ち上げたいところですが、本格的に運用するわけでなければ1台のマシンだけで完結したい訳です。

そこで考えられたのが LXD を使って各アプリケーションをそれぞれのコンテナで動かし、1台の物理マシン上で動く複数台の仮想マシンで OpenStack を構成する方法。

さらに、各コンテナのセットアップを Juju で自動化してしまえば、**コマンド一発でLXD コンテナの作成からアプリケーションのデプロイ、設定まで**を行えるのではと。これをやったのが **conjure-up** です。

同様に、conjure-up を使えば複数の構成要素からなる大規模なソフトウェアやプラットフォームを Juju やら LXD やらを使って簡単にデプロイできるわけですね。

## 作業環境
前置きが長くなってしまいましたが、ここからは実際に conjure-up を使って OpenStack Mitaka を構築していきます。
今回は Ubuntu Server をクリーンインストールした物理マシン1台を使って構築します。

|項目|値|
|---|---|
|ホスト名|curiosity |
|OS|Ubuntu 16.04 LTS|
|CPU|Intel Core i7-3820|
|メモリ|64GB|
|ストレージ| /dev/sda: 512 GB SSD x 1 <br /> /dev/sdb: 3TB HDD x 1 |
|NIC|1000-base ethernet x 1|

注意点として、OpenStack を構成する LXD コンテナ自体、および OpenStack 上で動く nova 仮想マシンの両方のデータを ホスト側OSとは隔離して **/dev/sdb** 上に配置します。なお、この構成はマストでは無く、ホスト側OSと同じパーティションを使っても構築できます。詳しくは「01. 事前準備: ZFS と LXD の構築」で説明します。

また、執筆時点での各種依存パッケージのバージョンは次の通りです。

* lxd: **2.0.4-0ubuntu1~ubuntu16.04.1**
* juju-2.0: **2.0-rc3-0ubuntu1~16.04.1~juju1**
* conjure-up: **2.0.1-0~201610061938~ubuntu16.04.1**

バージョンの違いによって conjure-up の過程でエラーが起きたり上手く行かなかったりするかもしれません。

## 01. 事前準備1: ZFS と LXD の構築
LXD 2.0 ではデータの保存先として Linux 上のディレクトリに加え Ubuntu 16.04 からサポートされた **ZFS** ファイルシステムを選択できるようになりました。
次世代ファイルシステムとされている ZFS を使うメリットは沢山あるみたい[4]なので、今回は ZFS を使いたいと思います。

### ZFS と LXD パッケージのインストール
```
$ sudo apt install lxd zfs
```

### LXD 初期化
※ **[注意]** **以下のコマンドは SSH 経由で無くマシン上で直接実行してください。**でないとネットワークブリッジの設定の項目で SSH が切断され操作できなくなります。

```
$ sudo lxd init
Name of the storage backend to use (dir or zfs): zfs
Create a new ZFS pool (yes/no)? yes
Name of the new ZFS pool: openstack
Would you like to use an existing block device (yes/no)? yes
Path to the existing block device: /dev/sdb
Would you like LXD to be available over the network (yes/no)? no
Do you want to configure the LXD bridge (yes/no)? yes
LXD has been successfully configured.
```

※ ZFS を別ストレージで作らない場合はこんな感じになります

```
$ sudo lxd init
Name of the storage backend to use (dir or zfs): zfs
Create a new ZFS pool (yes/no)? yes
Name of the new ZFS pool: openstack
Would you like to use an existing block device (yes/no)? no
Size in GB of the new loop device (1GB minimum): 100
Would you like LXD to be available over the network (yes/no)? no
Do you want to configure the LXD bridge (yes/no)? yes
LXD has been successfully configured.
```

## 02. 事前準備2: Nested VT-x の有効化
LXD上でのLXDの実行をサポートするために Nested VT-x を有効化します。

Nested VT-x が有効かどうか確認します。

```
$ cat /sys/module/kvm_intel/parameters/nested
N
```
N なら無効なので、以下のコマンドを実行して有効化します。

```
$ sudo rmmod kvm-intel
$ sudo sh -c "echo 'options kvm-intel nested=y' >> /etc/modprobe.d/dist.conf"
$ sudo modprobe kvm-intel
$ cat /sys/module/kvm_intel/parameters/nested
Y
```

## 03. Conjure-up のインストール
apt へのリポジトリの登録をします。

```
$ sudo apt-add-repository ppa:conjure-up/next
$ sudo apt-add-repository ppa:juju/devel
$ sudo apt update
```

その後 conjure-up を導入します。

```
$ sudo apt install conjure-up
```

## 04. OpenStack 構築

### 1. conjure-up
conjure-up コマンドで OpenStack を構築します。

```
$ conjure-up openstack
```

### 2. アプリケーションのデプロイ
スクリーンが立ち上がるので **OpenStack with NovaLXD** を選択.
![conjureup-1.png](https://qiita-image-store.s3.amazonaws.com/0/38708/af23c8df-f3b8-06da-a6cc-87501f49813b.png)

その後デプロイするアプリケーション一覧と設定項目の画面が出るので、~~そのまま **Deploy all 15 Remaining Applications** を押します。~~

といきたいところなのですが、<font color="red">**筆者の環境では15個のアプリケーションすべてを同時にデプロイしたところ、 rabbitmq-server のデプロイに失敗して途中でコケてしまいました。**</font>
解決策として **rabbitmq-server だけを最初にデプロイし、後述する Troubleshooting にある通り rabbitmq-server の設定ファイルの修正と反映を行った後に残りのアプリケーションをすべてデプロイする**と上手くいきました。 （詳しくは[Github の issue #489](https://github.com/conjure-up/conjure-up/issues/489#issuecomment-253112250)に書かれています。）
![conjureup-3.png](https://qiita-image-store.s3.amazonaws.com/0/38708/06df35e1-491c-8f04-fcac-6b85a32e4d96.png)

アプリケーションがデプロイされるのを待ちます。
![conjureup-4.png](https://qiita-image-store.s3.amazonaws.com/0/38708/fe27a42b-6e03-3823-0a84-40820314235c.png)

進捗が気になる人は、別のコンソールを開いて

```
$ watch juju status
```

や

```
$ juju debug-log
```

としてみると状況がわかると思います。

### 3. 指示に従って後処理
すべてのアプリケーションのデプロイが終わると、 ソフトウェアの設定を行う画面に遷移します。
エンターを押してステップを進めていきます。

**Step 1.** Glance への Ubuntu Trusty (14.04) と Ubuntu Xenial (16.04) 起動イメージのインポート
![Screen Shot 2016-10-12 at 6.44.35 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/d909a0b7-4d3d-c403-b152-f7726a9f9a29.png)

**Step 2.** SSH キーペアのインポート
![Screen Shot 2016-10-12 at 6.47.01 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/c934120f-38d5-fed7-3a0f-1d2419ce2d5c.png)

**Step 3.** Neutron の設定
![Screen Shot 2016-10-12 at 6.49.35 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/1930b805-a786-0c30-52d6-89a3da1877f8.png)

**Step 4.** Horizon ダッシュボードの設定
![Screen Shot 2016-10-12 at 6.51.54 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/932b6c21-b14f-e89b-cd8e-8e5249d7d9aa.png)

**デプロイ完了**
![Screen Shot 2016-10-12 at 6.53.12 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/e471d57f-3579-589d-a40f-2294e5f0d409.png)

**サマリー**
![Screen Shot 2016-10-12 at 6.54.57 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/cea5cfdd-680e-5cf8-266d-9e28b7a508fc.png)

以上で構築完了です。

## 05. ダッシュボードへのアクセス

`openstack-dashboard/0` ユニットの IP アドレスをメモしておきます。

```
$ juju status
...
openstack-dashboard/0    active    idle   14       10.19.198.23    80/tcp,443/tcp  Unit is ready
...
```

ホスト側から `openstack-dashboard/0` へのリバースプロキシを作るため、 apache2 を入れて設定します。

```
$ sudo apt install apache2
$ sudo a2enmod proxy
$ sudo vim /etc/apache2/sites-enabled/000-default.conf
--> 以下、<VirtualHost />内に追記
---
        <IfModule mod_proxy.c>
        ProxyPass / http://10.19.198.23/
        ProxyPassReverse / http://10.19.198.23/
        </IfModule>
---
```

以上で完了。
`http://ホストのIPアドレス/horizon` をブラウザで開き、ユーザ名 `admin`, パスワード `openstack` でログインできます。

![Screen Shot 2016-10-11 at 3.01.30 PM.png](https://qiita-image-store.s3.amazonaws.com/0/38708/5c2c58e9-5c49-cd81-ca09-67328ef84063.png)


## Troubleshooting

### 1. rabbitmq-server の立ち上げに失敗する
`conjure-up openstack` としても一発でデプロイ出来ないことが多々あります。筆者の場合は、次のエラーが出て `rabbitmq-server/0` ユニットが立ち上がりませんでした。

```
hook failed: "amqp-relation-changed" for nova-compute:amqp
```

ユニットの立ち上げに失敗した場合、`juju ssh ユニット名` として仮想マシンのコンソールを開き、ログを参照すると原因がわかりやすいです。

```
$ juju ssh rabbitmq-server/0
--- (以下、仮想マシン上での操作) ---
ubuntu@juju-84f95a-15$ sudo less +F /var/log/juju/unit-rabbitmq-server-0.log
...
2016-10-11 01:29:59 DEBUG juju-log amqp:27: Hardening function 'install'
2016-10-11 01:29:59 DEBUG juju-log amqp:27: Hardening function 'upgrade_charm'
2016-10-11 01:29:59 DEBUG juju-log amqp:27: Hardening function 'config_changed'
2016-10-11 01:29:59 DEBUG juju-log amqp:27: Hardening function 'update_status'
2016-10-11 01:30:00 INFO amqp-relation-changed Error: unable to connect to node rabbit@ubuntu: nodedown
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed DIAGNOSTICS
2016-10-11 01:30:00 INFO amqp-relation-changed ===========
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed attempted to contact: [rabbit@ubuntu]
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed rabbit@ubuntu:
2016-10-11 01:30:00 INFO amqp-relation-changed   * unable to connect to epmd (port 4369) on ubuntu: nxdomain (non-existing domain)
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed current node details:
2016-10-11 01:30:00 INFO amqp-relation-changed - node name: 'rabbitmq-cli-20529@juju-84f95a-15'
2016-10-11 01:30:00 INFO amqp-relation-changed - home dir: /var/lib/rabbitmq
2016-10-11 01:30:00 INFO amqp-relation-changed - cookie hash: Grmb/HfpexA0abUh5/acVQ==
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 DEBUG juju-log amqp:27: Command '['/usr/sbin/rabbitmqctl', 'list_vhosts']' returned non-zero exit status 2
2016-10-11 01:30:00 DEBUG juju-log amqp:27: Running ['/usr/sbin/rabbitmqctl', 'add_vhost', u'openstack']
2016-10-11 01:30:00 INFO amqp-relation-changed Error: unable to connect to node rabbit@ubuntu: nodedown
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed DIAGNOSTICS
2016-10-11 01:30:00 INFO amqp-relation-changed ===========
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed attempted to contact: [rabbit@ubuntu]
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed rabbit@ubuntu:
2016-10-11 01:30:00 INFO amqp-relation-changed   * unable to connect to epmd (port 4369) on ubuntu: nxdomain (non-existing domain)
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:00 INFO amqp-relation-changed current node details:
2016-10-11 01:30:00 INFO amqp-relation-changed - node name: 'rabbitmq-cli-20656@juju-84f95a-15'
2016-10-11 01:30:00 INFO amqp-relation-changed - home dir: /var/lib/rabbitmq
2016-10-11 01:30:00 INFO amqp-relation-changed - cookie hash: Grmb/HfpexA0abUh5/acVQ==
2016-10-11 01:30:00 INFO amqp-relation-changed
2016-10-11 01:30:01 INFO amqp-relation-changed Segmentation fault (core dumped)
2016-10-11 01:30:01 INFO amqp-relation-changed Traceback (most recent call last):
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/var/lib/juju/agents/unit-rabbitmq-server-0/charm/hooks/amqp-relation-changed", line 765, in <module>
2016-10-11 01:30:01 INFO amqp-relation-changed     hooks.execute(sys.argv)
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/var/lib/juju/agents/unit-rabbitmq-server-0/charm/hooks/charmhelpers/core/hookenv.py", line 715, in execute
2016-10-11 01:30:01 INFO amqp-relation-changed     self._hooks[hook_name]()
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/var/lib/juju/agents/unit-rabbitmq-server-0/charm/hooks/amqp-relation-changed", line 212, in amqp_changed
2016-10-11 01:30:01 INFO amqp-relation-changed     admin=settings.get('admin', False))
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/var/lib/juju/agents/unit-rabbitmq-server-0/charm/hooks/amqp-relation-changed", line 153, in configure_amqp
2016-10-11 01:30:01 INFO amqp-relation-changed     rabbit.create_vhost(vhost)
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/var/lib/juju/agents/unit-rabbitmq-server-0/charm/hooks/rabbit_utils.py", line 177, in create_vhost
2016-10-11 01:30:01 INFO amqp-relation-changed     rabbitmqctl('add_vhost', vhost)
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/var/lib/juju/agents/unit-rabbitmq-server-0/charm/hooks/rabbit_utils.py", line 321, in rabbitmqctl
2016-10-11 01:30:01 INFO amqp-relation-changed     subprocess.check_call(cmd)
2016-10-11 01:30:01 INFO amqp-relation-changed   File "/usr/lib/python2.7/subprocess.py", line 541, in check_call
2016-10-11 01:30:01 INFO amqp-relation-changed     raise CalledProcessError(retcode, cmd)
2016-10-11 01:30:01 INFO amqp-relation-changed subprocess.CalledProcessError: Command '['/usr/sbin/rabbitmqctl', 'add_vhost', u'openstack']' returned non-zero exit sta
tus 139
2016-10-11 01:30:01 ERROR juju.worker.uniter.operation runhook.go:107 hook "amqp-relation-changed" failed: exit status 1
```

今回のエラーでは、 rabbitmq の管理コマンド `rabbitmqctl` を実行する際に使われる、接続先ホスト名の解決に失敗していました。 `rabbit@ubuntu` ではなく `rabbit@juju-84f95a-15` に接続してほしい訳です。
調べたところ **RabbitMQ の設定ファイルを書き変えれば解決**できました。

```
$ juju ssh rabbitmq-server/0
--- (以下、仮想マシン上での操作) ---
ubuntu@juju-84f95a-15$ hostname
juju-84f95a-15
ubuntu@juju-84f95a-15$ sudo vim /etc/rabbitmq/rabbitmq-env.conf
—-> 最終行を書き換え
---
#RABBITMQ_NODENAME=rabbit@ubuntu
RABBITMQ_NODENAME=rabbit@juju-84f95a-15
---
ubuntu@juju-84f95a-15$ sudo service rabbitmq-server restart
```

設定ファイルの反映後、5分毎に juju からコマンドが呼ばれるのを待ちます。
ホスト側で

```
$ watch juju status
```

などとしておけば状況が掴みやすいでしょう。

※ なお、 `/etc/hosts` にホスト名 `ubuntu` を追加しただけでは、上記問題は解決しませんでした。


### 2. conjure-up が途中でエラーになって終了したとき

rabbitmq-server の立ち上げに失敗するなどエラーが出てしまうと conjure-up は終了してしまいます。
本当は全ユニットが立ち上がった後にSSHキーファイルをコピーしたり dashboard へのアクセスを可能にしたりと、複数のステップがあるのですが、そこに行き着く前に終了する場合もあります。

そんなときは仕方が無いので手動で各ステップを実行していきます。

```
$ cd ~/.cache/conjure-up/openstack-novalxd/steps
$ ./step-03_keypair
Connection to 10.19.198.100 closed.
{"message": "SSH Keypair is now imported and accessible when creating compute nodes.", "returnCode": 0, "isComplete": true}Connection to 10.19.198.100 closed.
$ ./step-04_neutron
{"message": "Neutron networking is now configured and is available to you during instance creation.", "returnCode": 0, "isComplete": true}Connection to 10.19.198.100 closed.
$ ./step-05_horizon
{"message": "Login to Horizon: http://10.19.198.23/horizon l: admin p: openstack", "returnCode": 0, "isComplete": true}
```

### 3. OpenStack を最初からデプロイし直したいとき
conjure-up は一度デプロイすると `~/.cache/conjure-up` 下に設定ファイルとログを保管します。
2度目の実行からは設定ファイルを元にデプロイし直せますが、デプロイの途中でエラーが起きて中断しても、はじめからやり直しになります。(この辺なんとかして欲しいですが)

真っさらな状態に戻すなら、 Juju をモデルごと消せばよさそうです。

モデル一覧を確認します。

```
$ juju models
CONTROLLER: bobcat

MODEL       OWNER        STATUS      MACHINES  CORES  ACCESS  LAST CONNECTION
controller  admin@local  available          1      -  admin   just now
mite*       admin@local  available         16      -  admin   2 minutes ago
```

選択されている mite モデルを消します。

```
$ juju destroy-model mite
WARNING! This command will destroy the "mite" model.
This includes all machines, applications, data and other resources.

Continue [y/N]? y
$ juju models
CONTROLLER: bobcat

MODEL       OWNER        STATUS      MACHINES  CORES  ACCESS  LAST CONNECTION
controller  admin@local  available          1      -  admin   just now
mite        admin@local  destroying        16      -  admin   1 minute ago
```

conjure-up の設定ファイルを消します。

```
$ rm -rf ~/.cache/conjure-up
```

これでいいはず。

### 4. 一度作成した ZFS のファイルシステムを削除したいとき
`openstack` という名前の zfs プールは以下のコマンドで削除できます。

```
$ sudo zfs destroy -r openstack
$ sudo zpool destroy openstack
```

## 参考文献
1. [LXD 2.0: LXDについて [1/12] - Qiita](http://qiita.com/m-shibata/items/a818859c3c7bcccae742)
2. [DockerだけじゃないContainersの世界 - Qiita](http://qiita.com/kentarosasaki/items/b44b5ffb57a69085027e)
3. [第288回 デプロイツール「Juju」を使って大魔術師を目指そう : Ubuntu Weekly Recipe](http://gihyo.jp/admin/serial/01/ubuntu-recipe/0288)
4. [ZFS on Linuxで高信頼なストレージを作る（Ubuntu 14.04） - Qiita](http://qiita.com/plainbanana/items/eaf44a27e1e24085a57e)
5. [Removing services, units and environments | Documentation | Juju](https://jujucharms.com/docs/2.0/charms-destroy)
6. [openstack lxd | Juju](https://jujucharms.com/u/openstack-charmers-next/openstack-lxd)
7. [Openstack on LXD - OpenStack Charms Guide 0.0.1.dev11 documentation](http://docs.openstack.org/developer/charm-guide/openstack-on-lxd.html)