レビュー「サーバ/インフラエンジニア養成読本 仮想化活用編 第二版」

先月、「サーバ/インフラエンジニア養成読本 仮想化活用編」の改訂版が発売されました。献本頂きましたので、レビューをしたいと思います。

  • 技術説明では、教科書的な説明にとどまらず、一歩踏み込んだ説明をしてくれています。この本は、Software Design 本誌に掲載されていた記事を、再編集してまとめたものなので、違う章で重複した説明があったりします。しかし、一線で活躍している各章の著者がどのように説明しているのか、その違いを比べてみるのも面白いかもしれません。
  • 運用監視やセキュリティなど、実用上重要なポイントも取り扱っています。Hinemos/Zabbix/Nagios の比較記事などは、実際に現場で役立つ知識を得るのに適した記事かと思います。
  • OpenFlow の記事では、Trema でのプラグラミングを取り扱っています。「こんな夜中に」の記事を元にしていますが、Trema 最新版を使用するよう内容が修正されています。これから Trema を始める人の入門として適した内容になっています。

サーバ/インフラエンジニア養成読本には、仮想化活用編以外にも、無印編、管理/監視編がありますが、こちらも改訂版が発売されているようです。興味のある方は、ご覧になってみるとよいでしょう。

rvm を使って trema/trema-edge 共存環境をつくる

今回から OpenFlow 1.3 に対応した trema-edge に挑戦してみます。
streocat さんに先をこされた気もしますが、気にしないこととします。

trema と trema-edge では必要とする ruby のバージョンが異なります。両者を扱える環境では、複数のバージョンの ruby を使い分ける必要があります。今回は、このために rvm を使う方法を紹介します。

ruby パッケージを削除

今回 ruby はすべて rvm 経由で入れることとします。そのため、パッケージを使ってインストール済みの ruby があるとややこしくなるため、ここではこれを削除することとします。

$ sudo apt-get --purge remove ruby rubygems

ただし、この段階でシステム上に ruby が存在しない状態になります。trema 以外にも ruby を使用している場合には、悪影響が及ばないかをご確認の上、削除してください。

rvm のインストール

次に rvm をインストールします。Ubuntu にて apt-get install ruby-rvm すると、問題があるらしいとの記事をちらほら見かけます(例えばここ)。なので、パッケージを使わないでインストールします。

curl -L https://get.rvm.io | bash -s stable --ruby

curl が入っていなければ、apt-get で入れてからインストールしましょう。
インストールが出来たら、別に新しいターミナルを立ちあげて、そこで以下のコマンドを打ってください。

$ rvm list

rvm rubies

=* ruby-2.0.0-p247 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

rvm のインストールと同時に ruby の最新版がインストールされているのがわかります。

rvm 経由で ruby のインストール

trema では 1.8.7-p371 を、trema-edge では 2.0.0-p0 の ruby が必要です (ここや、ここ を参照のこと)。rvm コマンドを使い、それぞれのバージョンの ruby をインストールします。

$ rvm install 2.0.0-p0
...
$ rvm install 1.8.7-p371
...

インストールが完了したら、rvm list で確認してみましょう。

Ubuntu に最新版の Open vSwitch をインストールする

今回は Ubuntu に、最新版の Open vSwitch をインストールする方法を紹介します。Open vSwitch は、今まさに開発が進行中のプロジェクトなので、使いたい機能が最新版においてのみサポートといったことが多々有ります。そのため、なるべく最新版を使えるようにしておきたいものです。

この記事を書いている段階での Open vSwitch の最新版は 1.10.0 ですが(注 8/27 に 1.11.0 が出ました)、apt-get で入れようとすると 1.9.0 が入ります。最新版を入れるためには、ソースコードからインストールする必要があります。もちろん、make install でインストールしてもいいのですが、後々の管理を考えるとパッケージにしておいたほうがベターです。なので今回はパッケージを作り、インストールする方法の紹介です。(といっても、ほとんどはソース中に同梱の INSTALL.Debian を読めば書いてあることですが。)

なお、RPM パッケージに作り方は、ここ にあります。Redhat 系のディストリビューションを使用している人は、こちらをご参考に。

準備

ビルドに必要なパッケージを予めインストールしておきます。

$ sudo apt-get install gcc make build-essential fakeroot dkms procps 
$ sudo apt-get install debhelper autoconf automake libssl-dev pkg-config openssl
$ sudo apt-get install python-all python-qt4 python-zopeinterface python-twisted-conch

ダウンロード、ビルド、インストール

Open vSwitch 1.10 をダウンロードし、ソースを展開します。

$ wget http://openvswitch.org/releases/openvswitch-1.10.0.tar.gz
$ tar zxvf ./openvswitch-1.10.0.tar.gz

Debian のパッケージを作成します。ビルドからパッケージの作成まで一括して行うためのスクリプトが用意されているので、それを fakeroot を使って実行します。

$ cd openvswitch-1.10.0
$ fakeroot debian/rules binary

一つ下のディレクトリにパッケージ類が作成されているので、以下のパッケージをインストールします。

$ cd ..
$ sudo dpkg -i ./openvswitch-common_1.10.0-1_amd64.deb
$ sudo dpkg -i ./openvswitch-switch_1.10.0-1_amd64.deb
$ sudo dpkg -i ./openvswitch-datapath-dkms_1.10.0-1_all.deb

起動

openvswitch.ko は、bridge.ko と共存できません。lsmod を使って bridge.ko がロードされているかを確認し、されていればアンロードしておきます。

$ sudo rmmod bridge

ここまで準備ができたら、以下のコマンドで openvswitch を起動します。

$ sudo service openvswitch-switch start

ovs-vsctl コマンドで、インストールされた openvswitch のバージョンを確認します。

$ sudo ovs-vsctl show
...
    ovs_version: "1.10.0"

Flow エントリをポート指定で消す (その2)

今回は、ポート番号の指定によりフローエントリを消す方法について、実際に動かして試してみましょう。

以下の内容のコントローラを outport_test.rb というファイル名で用意します。

class OutportTest < Controller
  def switch_ready dpid
    send_flow_mod_add(
      dpid,
      :match => Match.new( :in_port => 1 ),
      :actions => SendOutPort.new( 2 )
    )
    send_flow_mod_add(
      dpid,
      :priority => 10000,
      :match => Match.new( :in_port => 1 ),
      :actions => SendOutPort.new( 3 )
    )
    send_message dpid, FeaturesRequest.new
  end


  def features_reply dpid, message
    message.ports.each do | each |
      puts "Port no: #{ each.number }, name: #{ each.name }"
    end
  end


  def port_status dpid, message
    return if message.phy_port.state & Port::OFPPS_LINK_DOWN == 0
    puts "Port #{ message.phy_port.number } is down."
    send_flow_mod_delete(
      dpid,
      :match => Match.new,
      :out_port => message.phy_port.number
    )
  end
end

エミュレータ用の設定ファイル (outport_test.conf) も用意しましょう。

vswitch {  datapath_id "0x1" }

vhost ( "host1" )
vhost ( "host2" )
vhost ( "host3" ) 

link "0x1", "host1"
link "0x1", "host2"
link "0x1", "host3"

まずは作成したコントローラを実行してみましょう。Features Request を用いて取得したポート番号と名前の一覧が表示されます。

$ trema run ./outport_test.rb  -c ./test.conf 
Port no: 3, name: trema0-0
Port no: 2, name: trema2-0
Port no: 65534, name: vsw_0x1
Port no: 1, name: trema1-0

このコントローラは、switch_ready の段階で二つのフローエントリを設定します。trema apps に用意されている flow_dumper を用いて、フローエントリが二つ設定されていることを確認してみましょう。

$ cd ../apps/flow_dumper/
$ trema run ./flow-dumper.rb 
[0x1] table_id = 0, priority = 65535, cookie = 0x357c000000000001, idle_timeout = 0, hard_timeout = 0, duration = 2, packet_count = 0, byte_count = 0, match = [wildcards = 0x3820fe(dl_src|dl_dst|dl_type|dl_vlan|dl_vlan_pcp|nw_proto|nw_tos|nw_src(32)|nw_dst(32)|tp_src|tp_dst), in_port = 1, dl_src = 00:00:00:00:00:00, dl_dst = 00:00:00:00:00:00, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0, nw_tos = 0, nw_proto = 0, nw_src = 0.0.0.0/0, nw_dst = 0.0.0.0/0, tp_src = 0, tp_dst = 0], actions = [Trema::SendOutPort: port_number=2, max_len=65535]
[0x1] table_id = 0, priority = 10000, cookie = 0x357c000000000002, idle_timeout = 0, hard_timeout = 0, duration = 2, packet_count = 0, byte_count = 0, match = [wildcards = 0x3820fe(dl_src|dl_dst|dl_type|dl_vlan|dl_vlan_pcp|nw_proto|nw_tos|nw_src(32)|nw_dst(32)|tp_src|tp_dst), in_port = 1, dl_src = 00:00:00:00:00:00, dl_dst = 00:00:00:00:00:00, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0, nw_tos = 0, nw_proto = 0, nw_src = 0.0.0.0/0, nw_dst = 0.0.0.0/0, tp_src = 0, tp_dst = 0], actions = [Trema::SendOutPort: port_number=3, max_len=65535]

次にポート 2 をダウンさせ、該当するフローエントリの削除を確認してみましょう。ポート 2 の名前は、trema2-0 なので、これを ifconfig でダウンさせてみます。その後、flow-dumper.rb でフローエントリを確認してみましょう。

$ sudo ifconfig trema2-0 down
$ trema run ./flow-dumper.rb 
[0x1] table_id = 0, priority = 10000, cookie = 0x357c000000000002, idle_timeout = 0, hard_timeout = 0, duration = 10, packet_count = 0, byte_count = 0, match = [wildcards = 0x3820fe(dl_src|dl_dst|dl_type|dl_vlan|dl_vlan_pcp|nw_proto|nw_tos|nw_src(32)|nw_dst(32)|tp_src|tp_dst), in_port = 1, dl_src = 00:00:00:00:00:00, dl_dst = 00:00:00:00:00:00, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0, nw_tos = 0, nw_proto = 0, nw_src = 0.0.0.0/0, nw_dst = 0.0.0.0/0, tp_src = 0, tp_dst = 0], actions = [Trema::SendOutPort: port_number=3, max_len=65535]

出力ポートが 2 であるフローエントリが削除されていることが確認できたでしょうか?

Flow エントリをポート指定で消す (その1)

今回は、出力ポートが同じである複数のフローエントリを消す方法を紹介します。

早速ですが、Flow Mod メッセージフォーマットを見てみましょう。

/* Flow setup and teardown (controller -> datapath). */
struct ofp_flow_mod {
    struct ofp_header header;
    struct ofp_match match;      /* Fields to match */
    uint64_t cookie;             /* Opaque controller-issued identifier. */

    /* Flow actions. */
    uint16_t command;             /* One of OFPFC_*. */
    uint16_t idle_timeout;        /* Idle time before discarding (seconds). */
    uint16_t hard_timeout;        /* Max time before discarding (seconds). */
    uint16_t priority;            /* Priority level of flow entry. */
    uint32_t buffer_id;           /* Buffered packet to apply to (or -1).
                                     Not meaningful for OFPFC_DELETE*. */
    uint16_t out_port;            /* For OFPFC_DELETE* commands, require
                                     matching entries to include this as an
                                     output port.  A value of OFPP_NONE
                                     indicates no restriction. */
    uint16_t flags;               /* One of OFPFF_*. */
    struct ofp_action_header actions[0]; /* The action length is inferred
                                            from the length field in the
                                            header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72);

ポイントは、このフィールド中の out_port です。ここにポート番号が指定された場合、Output アクションで指定されたポート番号と一致したエントリのみ削除されることになります。特に指定する必要がない場合には OFPP_NONE を指定する必要があります。

通常は削除したい Flow エントリの match を含む Flow Mod メッセージを作り、command に OFPFC_DELETE または OFPFC_DELETE_STRICT でスイッチに送れば該当するエントリが削除されます。しかし、out_port に値を指定すると、該当するエントリのうち、アクションの出力ポートが out_port で指定された値と同一の場合のみ、そのエントリが削除されます。

この out_port の指定を使えば、例えばあるリンクが故障した場合、そのリンクに接続するポートに対して出力するアクションを含むフローエントリを一括して削除することができます。match はすべてワイルドカードに、command は OFPFC_DELETE にした上で、out_port にそのポート番号を格納した Flow Mod メッセージを作り、スイッチに送ればよいです。

次回以降、具体例を紹介したいと思います。