KVMをベースに簡易的なIaaSを作った際の知見

# はじめに

AIITには『クラウドインフラ構築特論』という授業があります。(2017年時点)
この授業で、学生は3～4人程度のチームに分かれ、KVMやDockerを基にIaasを作るという課題に2ヶ月半かけて取り組みます。
↓シラバス
https://aiit.ac.jp/master_program/isa/lecture/pdf/h29/1_16.pdf

ここでは、その授業で私を含むチームが構築した簡易的なIaaSと、学習した点を記載しています。
AIITでどんなことを学ぶことができるのかを知る一助になればと思います。

## 環境
- 物理サーバー×4台(全てCentOS7）
  - 1台にはグローバルIPが割り振られている
  - 4台は192.168.0.1/24のNATに参加している
  - NICはそれぞれ2枚ずつ刺さっている

## 作ろうとしたもの
この授業では、**WebAPI**経由でサーバーの作成、削除、起動、停止やSSH接続、ネットワーク設定、オートスケール等が行えるサービスを作ることが求められます。
私達のチームは**KVM**というLinuxカーネルの仮想化基盤を使用し、これを実装することを目指しました。
名前は、講師の山崎氏が社長を務められているあくしゅ社のデータセンター仮想化ソフトウェアであるwakame-vdcに倣ってKoubu-vdcとしました。(https://wakame-vdc.org/)

## Konbuのシステム構成
![SystemStructure.png](https://qiita-image-store.s3.amazonaws.com/0/75888/a4e0c250-7708-2f6f-cd7f-33155df845e2.png)

## Konbuのサブシステム一覧と使用したソフトウェア
### キューイング
仮想マシンの作成や削除といった処理は、しばしば数十秒～数分程度の時間を要します。
実際に、我々が作成したKonbuでも仮想マシンの作成に30秒程度かかる状況でした。(※無論、これは様々な工夫により高速化することができますが、我々の技量不足でそれは実現できませんでした)

数分かかるような処理を単純なHTTPリクエスト/レスポンスで実装すると、その分だけセッションがWebサーバー上にたまり、それが様々な問題を起こすことが予想されました。そこで我々はキューイングを使用し、タスクを非同期的に処理することで、これを解決するアプローチをとりました。キューイングにはRabbitMQを用いました。

WebAPIはRabbitMQへタスクを投げ、Agentは空きができ次第RabbitMQからキューを取り出して処理を実行し、サーバーステータス更新クエリを逐次RabbitMQへ投げ、これをDatabaseが反映する、という処理の流れがメインでした。

### Agent
仮想マシンの作成、削除、起動等を担います。物理マシン上でデーモンプロセスとして常駐し、RabbitMQからキューを取り出しタスクを実行します。我々の仮想化基盤は、仮想化ソフトウェアとして**kvm**を使用し、これを操作するAPIとして**libvirt**を使用しました。なお実装言語はPython3です。

#### KVM
KVMの詳しい解説はWikipediaなどに記載されています。
https://ja.wikipedia.org/wiki/Kernel-based_Virtual_Machine

我々がKVMを使用するときは、**libvirt**というAPIをPythonから叩くため、あまりこれを意識することはありませんでした。

#### libvirt
Redhat社がメンテナンスしている仮想化ソフトウェアの制御用APIです。KVM以外にもXen、QEMU、LXC、OpenVZ、UML、VirtualBox等多数の仮想化ソフトウェアをサポートしています。

### WebAPI/Console
WebAPIと画面はSlimPHPとTwitter Bootstrapを使って作成しました。
RabbitMQへ仮想マシン操作のQueueを投げるか、DBを参照してサーバー一覧などを取得する普通のWebアプリです。

### Database
一般的なクラウドインフラでは、自身が所有する仮想サーバーの状態を見ることができます。我々はcreated/initializing/running/stopped/halting/haltedという状態を定義し、API経由でステータスを変えるような操作が要求された場合はAgentがサーバーの状態を更新するキューを逐次RabbitMQへ投げ、これをDatabase上で常駐しているデーモンプロセスが受取り、クエリを発行するように実装しました、実装言語はRubyを用いました。

### 仮想マシンのinitializing
仮想マシンは起動しただけでは利用者が使える状態にはなっていません。具体的にはIPアドレスの付与、SSHログインの設定等が必要になります。こうした処理は各OSが持つブートストラップ用のスクリプトを書くことで実装することができる。CentOS6では`rc.local`があります。

Konbuでは、起動した仮想マシンは物理マシンとのNFS共有、DHCPによって割り振られたIPアドレスのテキスト出力といった処理をこれで実装しました。

## 実装できた機能
- API経由でのサーバー作成、削除、起動、停止etc
  - 起動時は指定されたSSHの公開鍵に紐づく秘密鍵のみでログインできるように制御
  - 仮想マシンのbootstrapにはCentOS6では`rc.local`を使用

- 簡易的なオートスケール
  - オートスケール用のAPIに対してサーバーの台数を指定し、リクエストすることで、任意の数のサーバーを追加できる
  - 追加されたサーバーは全て同一のロードバランサーに紐づく

## 実装を目指したが、できなかった機能
- 仮想マシンへのインターネット経由でのSSHログイン
  - 物理マシンのネットワーク構成的に、インターネットへつながるネットワークには1台しか入っていなかった
  - 本来はAgentが稼働しているサーバーもそれぞれインターネットへつながるネットワークが別途必要であった
  - そこで我々はブリッジネットワークを使用し、物理マシンと同様のNATへ仮想マシンが参加するように試みた
  - これを実現すれば、あとは1号機のiptablesからポートフォワードすることでSSHとHTTP接続についてはインターネットから直接接続することができるためであった
  - ただし時間の制約と技術力不足により、ブリッジネットワークの設定に失敗したため、これを断念した

## 授業を終えて
分かってはいたことではありましたが、クラウドインフラというのはとどのつまりソフトウェアでサーバーを制御しているものだということが実感を以て言えるようになりました。
また我々が実装できた機能、実装を目ざしたができなかった機能以外にも、例えばRabbitMQやロードバランサーがダウンした際の対処やOSイメージの縮小による高速起動、S3といった独自サービスの開発など、開発したいと思う機能が沢山見つかりました。
物理サーバーが手元になくても、ノートPCを数台とそれらをつなぐケーブルおよびルータ、あとはインターネット環境があれば家でもある程度作れそうな部分はあるので、時間さえあればやってみたいと思いました。