OpenStack Kolla Ansible (Pike=6.0.0)をベアメタルサーバにデプロイした

自宅に放置されている物理サーバを使って、プライベートクラウドを構築しようと思い、OpenStackを見つけたのですが、普通のインストール方法だとどうしても途中で詰まったりで途方にくれていましたが、Kolla-ansibleを使うことによって、非常に簡単にデプロイすることができました。
この記事では、そのデプロイに成功するまでの手順を、備忘も兼ねて記載します。

いくつかのサービスがうまく動かなかったり、公式ドキュメントのみではダメな部分もありましたが、やり直しも簡単にできるので、何かしらの理由がない限りはKolla-ansibleを使ってのデプロイをすることをオススメします。

## 目次

- 環境構成
- 事前準備（主にハードウェア）
- kolla-ansibleのコンフィグ編集
- CinderとSwift用Diskのフォーマット
- デプロイ
- 動作確認
- 失敗したときは

## 環境構成

今回は、性能の低めなサーバ4台と、Xeonを積んだサーバを2台の計6台のサーバを用意しました。
ネットワークをコンピュート用と、管理ネットワーク用に分けないといけない[^1]ので、内部のルータとしてRTX1200を使用し、ネットワークを分断しました。また、それぞれのサーバはそれらのネットワークにアクセスするため、NICも二枚必要です。

![OpenStackDiagram_20180109.png](https://qiita-image-store.s3.amazonaws.com/0/119813/5830ba98-9d44-c6f2-c5db-ca4d67091aad.png)

## 事前準備

### Ubuntu16.04のインストール

SSHサーバをインストールのみをインストール

### NICの設定

NICをそれぞれ下記のとおり設定する必要があります。

```sh
# 管理ネットワーク用
auto enp2s0
iface enp2s0 inet static
   address 192.168.100.21 #アドレスはホストでそれぞれ変える。
   netmask 255.255.255.0
   gateway 192.168.100.1
   dns-nameservers 192.168.100.1

# コンピュートネットワーク用
auto enp3s0
iface enp3s0 inet manual
   up ip link set dev $IFACE up
   down ip link set dev $IFACE down
```

### ホストファイルの編集（もしくは内部DNSの設定）

各ノードのホストファイルを編集するか、内部DNSにより
それぞれがホスト名でアクセスできるようにする。

```sh
vi /etc/hosts
```

```conf
compute1 192.168.100.21
# 全ホスト分記載。管理ネットワークのアドレスでアクセスできるようにする
```

### SSH Keyでパスワードなしでアクセスできるようにする。

全ホストに対して、以下を実行

```sh
ssh-copy-id host1
```

### パスワードなしでRootユーザになれるようにする

```sh
sudo visudo
```

### 必要なソフトウェアのインストール

以下のコマンドを全てのホストで実行し、必要なモジュール類をインストールしてください。

```sh
# pipのインストール
apt-get update
apt-get install python-pip
pip install -U pip

# 必要なPythonライブラリのインストール
apt-get install python-dev libffi-dev gcc libssl-dev python-selinux

# ansibleのインストール
pip install -U ansible

# Dockerのインストール
curl -sSL https://get.docker.io | bash

# サービスファイルの作成と編集
mkdir -p /etc/systemd/system/docker.service.d

tee /etc/systemd/system/docker.service.d/kolla.conf <<-'EOF'
[Service]
MountFlags=shared
ExecStart=
ExecStart=/usr/bin/dockerd \
 -H fd:// \
 --mtu 1400
EOF

# Run these commands to reload the daemon
systemctl daemon-reload
systemctl restart docker

# pythonのDockerモジュールのインストール
pip install -U docker

# NTPのインストール
apt-get install ntp

# Ubuntu
service libvirt-bin stop
update-rc.d libvirt-bin disable
```

## kolla-ansibleのコンフィグ編集

以下の2つのファイルをどこかにコピーして編集します。
１つ目のファイルは、特に要件がなければ `/etc/kolla/` 配下にコピーすることをおすすめします。
※オプションでファイルを指定せずにすむため。

1. `kolla-ansible/etc/kolla`
2. `kolla-ansible/ansible/inventory/*`

### パスワードファイルの生成

パスワードファイルを生成しておきます。
上記の1つめのファイルをコピーした場所が、標準の`/etc/kolla/`であればオプション無しで問題ないですが、
それ以外の場所であれば、コピー先フォルダを指定する必要があります。

```
kolla-genpwd -p ~/kolla-pike/kolla/passwords.yml
```

### globals.ymlの編集

次に、全体の設定ファイルである`globals.yal`を編集します。
１つ目のファイルの配下にあります。

```sh
vi global.yaml
```

サービスの部分までで編集したところを抜粋すると以下の通り。

```conf
# Valid options are [ centos, oraclelinux, ubuntu ]
#kolla_base_distro: "centos"
kolla_base_distro: "ubuntu"

# Valid options are [ binary, source ]
#kolla_install_type: "binary"
kolla_install_type: "source"

# Valid option is Docker repository tag
openstack_release: "pike"

# 'network_interface' as set in the Networking section below.
kolla_internal_vip_address: "192.168.100.100"

# followed for other types of interfaces.
network_interface: "enp2s0"

# addresses for that reason.
neutron_external_interface: "enp3s0"

# 追記する。
# 外部ネットワークに直接インスタンスを接続できるようになる。
enable_neutron_provider_networks: "yes"
```

サービスの部分は色々書き換えた結果、以下の通りにしました。

```conf
# OpenStack services can be enabled or disabled with these options
enable_aodh: "yes"
enable_barbican: "yes"
enable_ceilometer: "yes"
enable_central_logging: "yes"
enable_ceph: "yes"
enable_ceph_rgw: "yes"
enable_chrony: "yes"
enable_cinder: "yes"
enable_cinder_backup: "yes"
#enable_cinder_backend_hnas_iscsi: "no"
#enable_cinder_backend_hnas_nfs: "no"
#enable_cinder_backend_iscsi: "no"
#enable_cinder_backend_lvm: "no"
#enable_cinder_backend_nfs: "no"
enable_cloudkitty: "no"
enable_collectd: "yes"
enable_congress: "yes"
#enable_designate: "no"
#enable_destroy_images: "no"
enable_etcd: "yes"
enable_fluentd: "yes"
enable_freezer: "no"
enable_gnocchi: "yes"
enable_grafana: "yes"
enable_haproxy: "yes"
enable_heat: "yes"
enable_horizon: "yes"
#enable_horizon_cloudkitty: "{{ enable_cloudkitty | bool }}"
#enable_horizon_designate: "{{ enable_designate | bool }}"
#enable_horizon_freezer: "{{ enable_freezer | bool }}"
#enable_horizon_ironic: "{{ enable_ironic | bool }}"
#enable_horizon_karbor: "{{ enable_karbor | bool }}"
#enable_horizon_magnum: "{{ enable_magnum | bool }}"
#enable_horizon_manila: "{{ enable_manila | bool }}"
#enable_horizon_mistral: "{{ enable_mistral | bool }}"
#enable_horizon_murano: "{{ enable_murano | bool }}"
#enable_horizon_neutron_lbaas: "{{ enable_neutron_lbaas | bool }}"
#enable_horizon_sahara: "{{ enable_sahara | bool }}"
#enable_horizon_searchlight: "{{ enable_searchlight | bool }}"
#enable_horizon_senlin: "{{ enable_senlin | bool }}"
#enable_horizon_solum: "{{ enable_solum | bool }}"
#enable_horizon_tacker: "{{ enable_tacker | bool }}"
#enable_horizon_trove: "{{ enable_trove | bool }}"
#enable_horizon_watcher: "{{ enable_watcher | bool }}"
#enable_horizon_zun: "{{ enable_zun | bool }}"
#enable_hyperv: "no"
enable_influxdb: "yes"
enable_ironic: "no" # デプロイエラー発生
enable_karbor: "no"
enable_kuryr: "no" # コンテナが落ちてしまう
enable_magnum: "yes"
enable_manila: "yes"
enable_manila_backend_generic: "yes"
#enable_manila_backend_hnas: "no"
enable_mistral: "yes"
enable_mongodb: "yes"
enable_murano: "no" #pikeバージョンではデプロイエラー発生
enable_multipathd: "yes"
enable_neutron_bgp_dragent: "no"
enable_neutron_dvr: "no"
enable_neutron_lbaas: "yes"
enable_neutron_fwaas: "yes"
enable_neutron_qos: "no"
enable_neutron_agent_ha: "no"
enable_neutron_vpnaas: "yes"
enable_neutron_sfc: "yes"
#enable_nova_fake: "no"
#enable_nova_serialconsole_proxy: "no"
enable_octavia: "no"
#enable_opendaylight: "no"
#enable_openvswitch: "{{ neutron_plugin_agent != 'linuxbridge' }}"
#enable_ovs_dpdk: "no"
enable_osprofiler: "yes"
enable_panko: "yes"
enable_qdrouterd: "no"
enable_rally: "yes"
enable_redis: "yes"
enable_sahara: "yes"
enable_searchlight: "no" # デプロイ時にエラー発生
enable_senlin: "yes"
enable_skydive: "yes"
enable_solum: "no" # デプロイ時にエラー発生
enable_swift: "yes"
enable_telegraf: "yes"
enable_tacker: "no"
enable_tempest: "no"
enable_trove: "yes"
enable_vmtp: "no"
enable_watcher: "no"
enable_zun: "no" # kuryrがエラー発生するので、デプロイ対象外に。
# 追記。osprofilerとskydiveに必要。
enable_elasticsearch: "yes"
```

サービスデプロイ対象選択部分より下の部分はGlanceの部分のみ修正しました。

```conf
########################
# Glance - Image Options
########################
# Configure image backend.
glance_backend_file: "no"
glance_backend_ceph: "yes"
```

### multinodeファイルの修正

各ホストを編集します。
環境に合わせた内容にしてください。

```conf
[control]
# These hostname must be resolvable from your deployment host
host5    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
host6    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
host4    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa

# The above can also be specified as follows:
#control[01:03]     ansible_user=kolla

# The network nodes are where your l3-agent and loadbalancers will run
# This can be the same as a host in the control group
[network]
host2    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
host3    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa

# inner-compute is the groups of compute nodes which do not have
# external reachability
[inner-compute]

# external-compute is the groups of compute nodes which can reach
# outside
[external-compute]
host5    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
host6    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa

[compute:children]
inner-compute
external-compute

[monitoring]
host4    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa

# When compute nodes and control nodes use different interfaces,
# you can specify "api_interface" and other interfaces like below:
#compute01 neutron_external_interface=eth0 api_interface=em1 storage_interface=em1 tunnel_interface=em1

[storage]
host3    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
host2    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
host1    ansible_ssh_user=user ansible_become=True ansible_private_key_file=/home/user/.ssh/id_rsa
```

## CinderとSwift用Diskのフォーマット

CinderとSwiftに使用するHDDをフォーマットしておきます。
フォーマットした上で、CinderとSwiftそれぞれにそのディスクを使用するということを明示するために、ラベルを付けます。
また、SwiftにはRingというものを事前に作成しておく必要があるので、そのコマンドも実行します。

```sh
# 各ストレージノードで以下を実行
mkfs.xfs -f /dev/sdb; mkfs.xfs -f /dev/sdc; mkfs.xfs -f /dev/sdd
parted /dev/sdb -s -- mklabel gpt mkpart KOLLA_CEPH_OSD_BOOTSTRAP_42 1 -1; parted /dev/sdd -s -- mklabel gpt mkpart KOLLA_CEPH_OSD_BOOTSTRAP_42_J 1 -1
for d in sdc ; do     sudo mkfs.xfs -f /dev/${d};     parted /dev/${d} -s -- mklabel gpt mkpart KOLLA_SWIFT_DATA 1 -1;     sudo mkfs.xfs -f -L d${index} /dev/${d}1;     (( index++ )); done
```

SWift用のRingを作成するためのスクリプトを作成。 @ Deployノード

```sh
STORAGE_NODES=(192.168.100.21 192.168.100.22 192.168.100.23)
KOLLA_SWIFT_BASE_IMAGE="kolla/ubuntu-source-swift-base:pike"

sudo rm -rf /etc/kolla/config/swift
sudo mkdir -p /etc/kolla/config/swift

# Object ring
docker run \
  --rm \
  -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
  $KOLLA_SWIFT_BASE_IMAGE \
  swift-ring-builder \
    /etc/kolla/config/swift/object.builder create 10 3 1

for node in ${STORAGE_NODES[@]}; do
    for i in {0..2}; do
      docker run \
        --rm \
        -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
        $KOLLA_SWIFT_BASE_IMAGE \
        swift-ring-builder \
          /etc/kolla/config/swift/object.builder add r1z1-${node}:6000/d${i} 1;
    done
done

# Account ring
docker run \
  --rm \
  -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
  $KOLLA_SWIFT_BASE_IMAGE \
  swift-ring-builder \
    /etc/kolla/config/swift/account.builder create 10 3 1

for node in ${STORAGE_NODES[@]}; do
    for i in {0..2}; do
      docker run \
        --rm \
        -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
        $KOLLA_SWIFT_BASE_IMAGE \
        swift-ring-builder \
          /etc/kolla/config/swift/account.builder add r1z1-${node}:6001/d${i} 1;
    done
done

# Container ring
docker run \
  --rm \
  -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
  $KOLLA_SWIFT_BASE_IMAGE \
  swift-ring-builder \
    /etc/kolla/config/swift/container.builder create 10 3 1

for node in ${STORAGE_NODES[@]}; do
    for i in {0..2}; do
      docker run \
        --rm \
        -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
        $KOLLA_SWIFT_BASE_IMAGE \
        swift-ring-builder \
          /etc/kolla/config/swift/container.builder add r1z1-${node}:6002/d${i} 1;
    done
done

for ring in object account container; do
  docker run \
    --rm \
    -v /etc/kolla/config/swift/:/etc/kolla/config/swift/ \
    $KOLLA_SWIFT_BASE_IMAGE \
    swift-ring-builder \
      /etc/kolla/config/swift/${ring}.builder rebalance;
done
```

Deployノードから実行

```sh
./swift.sh
```

## デプロイ

デプロイ前に、ポートが開いているか、設定ファイルに誤りがないかをチェックします。

```sh
kolla-ansible -i ~/kolla-pike/multinode --configdir ~/kolla-pike/kolla --passwords ~/kolla-pike/kolla/passwords.yml -v prechecs
```

エラーがなければ、デプロイします。
めちゃくちゃ時間がかかるのでひたすら見守ります。
デプロイ先のホストとの通信が切れないようにマシンの設定に気をつけてください。

```sh
kolla-ansible -i ~/kolla-pike/multinode --configdir ~/kolla-pike/kolla --passwords ~/kolla-pike/kolla/passwords.yml -v deploy
```

エラーなくデプロイできましたら、接続情報ファイルを作成します。

```sh
kolla-ansible -i ~/kolla-pike/multinode --configdir ~/kolla-pike/kolla --passwords ~/kolla-pike/kolla/passwords.yml -v post-deploy
```

## 動作確認

opensack clientのインストール

```sh
pip install python-openstackclient
```

デプロイ後最初に実行するコマンドの編集

```sh
vim /usr/share/kolla-ansible/init-runonce
```

```conf
# 欲しいイメージに合わせて編集。
# 私の環境ではcirrosはうまくいかなかったので、後でUbuntuイメージに変更しました。
# イメージ一覧：https://docs.openstack.org/ja/image-guide/obtain-images.html
IMAGE_URL=http://download.cirros-cloud.net/0.4.0/
IMAGE=cirros-0.4.0-${ARCH}-disk.img
IMAGE_NAME=cirros
IMAGE_TYPE=linux

# 外部ネットワークの情報に合わせて編集
EXT_NET_CIDR='192.168.0.0/24'
EXT_NET_RANGE='start=192.168.0.101,end=192.168.0.230'
EXT_NET_GATEWAY='192.168.0.1'
```

認証情報を読み込んで、初期ネットワークやフレーバー、イメージなどを作成します。

```sh
. /etc/kolla/admin-openrc.sh
cd /usr/share/kolla-ansible
./init-runonce
```

インスタンス起動

```sh
openstack server create \\
    --image ${IMAGE_NAME} \\
    --flavor m1.small \\
    --key-name mykey \\
    --nic net-id=demo1 \\
    demo1
```

floating IPの取得と付与

```sh
# floating ipの取得
openstack floating ip create public1
# floating ipの確認
openstack floating ip list
# インスタンスの確認
openstack server list
# floating ipの割当
openstack server add floating ip INSTANCE_NAME_OR_ID FLOATING_IP_ADDRESS
```

ここまでできたら、SSHで接続ができるはずです。
@の前のユーザ名は起動したインスタンスのイメージに合わせてください。

```sh
ssh ubuntu@INSTANCE_IP
```

## 失敗したときは

設定に間違いが合ったけどprecheckで検知できずにエラーした場合や、コネクションが途中で切れてしまって落ちた場合などは、
丸ごとデプロイし直すこともできますが、Roleを指定して実行することで途中からやり直すことができます。

```
ansible-playbook -i ~/kolla-pike/multinode -e @~/kolla-pike/kolla/globals.yml -e @~/kolla-pike/kolla/passwords.yml -e CONFIG_DIR=~/kolla-pike/kolla  -e action=deploy ~/.pyenv/versions/2.7.13/envs/kolla-pike/share/kolla-ansible/ansible/site.yml  --verbose --tags "searchlight,tacker,octavia,zun,skydive"
```

[^1]: https://docs.openstack.org/install-guide/overview.html

## 参考リスト

- [OpenStack Docs: Welcome to Kolla-Ansible’s documentation!](https://docs.openstack.org/kolla-ansible/latest/index.html)
- [kolla-ansibleでOpenStack(stable/pike)を構築する - Qiita](https://qiita.com/raichi/items/71869442e8101fd38230)
- [OpenStack Kolla(Ocata)環境の構築 - Qiita](https://qiita.com/raichi/items/e0a57c833450654006a5)