Tremaの環境構築とハローワールド(Ubuntu18.04LT)

## 自己紹介
こんにちは、Othlotechにたまーに参加しているさっとです。
普段はサーバサイドやインフラを主に触っています。
最近はフロントエンドにも興味がでてきてReactなんかも書いています。

## 概要
研究でTremaと呼ばれるSDNのフレームワークを構築することがありましたが、
Ubuntu18.04LTで構築するとき躓いた部分がいくつかあったので、メモとして残したいと思います。

あとは、Tremaには便利なテストがついています。仮想のホストをいくつか用意してパケットを飛ばしたり、構築したTremaがどういった挙動をするのかチェックすることができるので、今回はこちらを試してSDNの雰囲気をつかんでいただきたいと思います。

## そもそもTremaとは？
SDNフレームワークの一種でRubyで書かれていることが特徴です。
Web開発で言うところのRailsみたいな感じです。

SDNについても説明しておくと、本来ネットワークを制御するためには物理スイッチの設定を直接書き込んだり、LANケーブルを繋ぎ変えたりと作業コストがかかっていました。
この問題を解決するために、ソフトウェアで柔軟にネットワークを制御することができるSDNが提案され一時期注目を浴びました。最近では下火ですが。。。

SDNフレームワークには、Tremaの他にもPythonで書かれたRyuやRestAPIで制御するOpenDaylightなどがあります。

研究ではTremaを使っていますが、選定理由としては、Tremaは日本人が開発したこともあり日本語のドキュメントが豊富でつまりづらいかつテストも容易でRubyについての知見もある程度あったためです。

## 環境構築
### 必要な環境

- git
- Ruby2.0〜2.3


まず初めに、TremaはRubyで動作するため、Rubyが実行できる環境を作る必要があります。また、TremaはOSSですが随分と更新されていないため最新のRubyバージョンでは動作できません。Ruby2.3あたりを入れる必要があるのでまずはそこからやっていきましょう。

### 前準備
Ruby2.3をそのままUbuntuに入れるとご自身の環境を汚しかねないので、anyenvと呼ばれる**env系を管理するツールでRubyのバージョンを管理するrbenvをインストールしたいと思います。

※ rbenvがすでにある方はここは飛ばしてもらっても構いません。

- anyenvを入れるために必要なgitをインストール

```bash
$ sudo apt install git
```

- anyenvをインストールします

```bash
$ git clone https://github.com/riywo/anyenv ~/.anyenv
```

- ターミナル起動時にanyenvを使えるようにするために```~/.bashrc```に以下を追記します。

```bash:~/.bashrc
if [ -d $HOME/.anyenv ]
then
    export PATH="$HOME/.anyenv/bin:$PATH"
    eval "$(anyenv init -)"
fi
```

- ```~/.bashrc```の変更を適用します

```bash
$ source ~/.bashrc
```

- ANYENV_DEFINITION_ROOT(/home/***/.config/anyenv/anyenv-install) doesn't exist. You can initialize it by:と怒られたら以下のコマンドを実行

```bash
$ anyenv install --init
```

- anyenvがインストールされていることを確認します

```bash
$ which anyenv 
/home/***/.anyenv/bin/anyenv
```

- rbenvのインストール

```bash
$ anyenv install rbenv
```

- ターミナルを更新しrbenvを適用します

```bash
$ exec $SHELL -l
```

- rbenvがインストールされていることを確認します

```bash
$ which rbenv
/home/***/.anyenv/bin/anyenv
```

### Ruby2.3のインストール
先程インストールしたrbenvを使ってRuby2.3をインストールしたいと思います。

- その前にインストールに必要なパッケージ類を入れておきましょう

```bash
$ sudo apt install autoconf bison build-essential libssl1.0-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev
```
1分ぐらいかかると思います

- 次に、rbenvを使ってRuby2.3をインストールします

目的のバージョンがあるかどうか確認

```bash
$ rbenv inistall --list
・・・
2.3.0
2.3.1
2.3.2
2.3.3
・・・
```

- 2.3系がいくつかありますが、無難に2.3.0をインストール

```bash
$ rbenv install 2.3.0
```
結構時間かかります。カップラーメン食べれるぐらい

- Rubyがインストールされていることを確認してみます

```bash
$ rbenv versions
2.3.0
```

- Ruby2.3.0をグローバル設定する

```bash
$ rbenv global 2.3.0
```

- Ruby2.3.0がグローバル設定されたかどうか確認してみる

```bash
$ rbenv versions
* 2.3.0 (set by /home/***/.anyenv/envs/rbenv/version)
```

### Tremaのインストール
- 適当なディレクトリに移動してTremaをインストールします

```bash
$ git clone https://github.com/trema/hello_trema
```

- クローンしてきたファイルに入る

```bash
$ cd hello_trema
```

- ここで必要なパッケージをインストールするためにbundleがいるのでインストールします
(最新のbundleだと動かないので1.11.2を指定します)

```bash
$ rbenv exec gem install bundler -v 1.11.2
```

- 例のごとくbundleがインストールされたか確認します

```bash
$ which bundler
```

- bundleを使って必要なパッケージをインストール

```bash
$ bundle _1.11.2_ install --binstubs
```

- 仮想ホストや仮想ネットワークを構築するためにopenvswitchをインストール

```bash
$ sudo apt install openvswitch-switch
```

以上で下準備は完了です

## ハローワールドしてみる
インストールしたTremaを起動してみます

```bash
$ ./bin/trema run lib/hello_trema.rb -c trema.conf
Trema started.
Hello 0xabc!
```

Helloと出ましたね

無事Tremaを起動することが確認できました

### 実行ファイルの確認

Tremaでは```./lib/hello_trema.rb```にネットワークを制御するためのコントローラを定義します。

中身はこんな感じです

```bash:./lib/hello_trema.rb
# Hello World!
class HelloTrema < Trema::Controller
  def start(_args)
    logger.info 'Trema started.'
  end

  def switch_ready(datapath_id)
    logger.info "Hello #{datapath_id.to_hex}!"
  end
end
```

ライフサイクルとしては、Tremaが実行されるとまずはじめに**start**メソッドが読み込まれます。
Trema実行時にすぐ```Trema started.```が表示されたのはそのためです。

その後、switchの接続が確認されると、**switch_ready**メソッドが呼ばれます。

ここで、お気づきだと思いますが、実行結果では、switchの接続をしていないのに**switch_ready**メソッドが呼ばれていることがわかると思います。

理由としては、```trema.conf```に仮想のスイッチが定義されているためです。

```bash:./trema.conf
vswitch { datapath_id 0xabc }
```
この一行を書くだけで、簡単に仮想スイッチを用意することができます。

ちなみに、datapath_idとはスイッチを識別するためのMACアドレスみたいなものです。

### スイッチを追加してみる
試しに、このファイルに新しいスイッチを定義してみましょう

```bash:./trema.conf
vswitch { datapath_id 0xabc }
vswitch { datapath_id 0xdef }
```

- Tremaの実行

```bash
$ ./bin/trema run lib/hello_trema.rb -c trema.conf
Trema started.
Hello 0xabc!
Hello 0xdef!
```

新しく追加したスイッチが表示されていることがわかると思います。

## おわりに
今回はTremaの一から環境構築をまとめてみました。

物理機器を用意しなくとも簡易的な実験やSDNを手軽に体験できるので、この記事を参考にしてぜひ触ってみてください。