2015年12月5日土曜日

環境変数1つで OpenStack 環境を切り替える os-client-config

OpenStack ユーザー会 2015 アドベントカレンダーの 12/5 分です。

みなさん、OpenStack の CLI (nova, neutron, ..., openstack コマンドも) を使うときにどうやって認証情報などを渡していますか?

おそらくほとんどの方は openrc を使っていると思います。 みなさんご存知のとおり、openrc はこんなです。シンプルですね。

export OS_TENANT_NAME=mytenant
export OS_USERNAME=test-user
export OS_PASSWORD=hey-secret
export OS_AUTH_URL=https://keystone.example.com:443/v2.0

シンプルなのはいいのですが、 複数の OpenStack ベースのクラウドを使用したり、 同じ OpenStack クラウドでも一般ユーザーと管理者のアカウントを切り替えたり、 といったことをしようとすると、だんだん大変になってきます。 openrc ファイルをたくさん用意して、毎回読み直すのは面倒です。

そこで os-client-config の出番です。 os-client-config は、OpenStack の CLI で複数の OpenStack クラウドを管理しやすくする目的で開発されています。現在は openstackclient と一部のプロジェクト単位の CLI (neutron など) で対応しています。

openrc に相当する情報は ~/.config/openstack/clouds.yaml で定義します。 ファイルの場所は、最近の設定ファイル置き場の流儀にしたがっており、
~/.config/openstack は Linux の場合です。 Mac OSX の場合は ~/Library/Application Support/openstack になります。

以下は devstack で定義される clouds.yaml です。 一般ユーザーの devstack と管理ユーザーの devstack-admin という 2 つの設定があります。パラメーターとしては auth セクションに openrc で定義されているのと同様の内容が定義されています。

clouds:
  devstack:
    auth:
      auth_url: http://auth.example.com:5000
      password: your-secret
      project_domain_id: default
      project_name: demo
      user_domain_id: default
      username: demo
    identity_api_version: '3'
    region_name: RegionOne
  devstack-admin:
    auth:
      auth_url: http://auth.example.com:35357
      password: another-secret
      project_domain_id: default
      project_name: admin
      user_domain_id: default
      username: admin
    identity_api_version: '3'
    region_name: RegionOne

これをコマンドラインで使う場合は、--os-cloud オプションか OS_CLOUD 環境変数で指定します。

--os-cloud オプションで指定してみた例です。 devstack と devstack-admin で簡単にアカウント情報を切り替えられることが分かります。

ubuntu@dev16:~$ openstack --os-cloud devstack server list
+--------------------------------------+------+--------+-----------------------------------------------------+
| ID                                   | Name | Status | Networks                                            |
+--------------------------------------+------+--------+-----------------------------------------------------+
| 9d2e8af2-45d4-4bbf-9cc6-85fb17174cbc | vm1  | ACTIVE | net1=10.0.0.5, fd12:877c:1d66:0:f816:3eff:fe15:40ee |
+--------------------------------------+------+--------+-----------------------------------------------------+

ubuntu@dev16:~$ openstack --os-cloud devstack-admin server list

ubuntu@dev16:~$ 

openrc の時と同様に OS_CLOUD 環境変数で指定することもできます。

ubuntu@dev16:~$ export OS_CLOUD=devstack
ubuntu@dev16:~$ env | grep OS_
OS_CLOUD=devstack
ubuntu@dev16:~$ openstack server list
+--------------------------------------+------+--------+-----------------------------------------------------+
| ID                                   | Name | Status | Networks                                            |
+--------------------------------------+------+--------+-----------------------------------------------------+
| 9d2e8af2-45d4-4bbf-9cc6-85fb17174cbc | vm1  | ACTIVE | net1=10.0.0.5, fd12:877c:1d66:0:f816:3eff:fe15:40ee |
+--------------------------------------+------+--------+-----------------------------------------------------+

neutron CLI でも利用できます。 (12/5 の執筆時点ではこの neutronclient はまだリリースされていません)

ubuntu@dev16:~$ env | grep OS_
OS_CLOUD=devstack
ubuntu@dev16:~$ neutron net-list
+--------------------------------------+-----------+----------------------------------------------------------+
| id                                   | name      | subnets                                                  |
+--------------------------------------+-----------+----------------------------------------------------------+
| 3698d3c7-d581-443e-bf86-53c4e3a738f7 | mynetwork |                                                          |
| a11600dc-cac9-4934-970b-0fa44d5097b8 | ext_net   | 2b86127b-424b-40a9-9a85-9f7be470bc25                     |
|                                      |           | 7e268119-891b-447e-97f8-8e900fb0664e                     |
| e8cbf82d-9f26-4d6f-ab02-dc3bdf05ffce | net1      | 6b832dfe-f271-443c-abad-629961414a73 10.0.0.0/24         |
|                                      |           | cdcc616b-0cff-482f-96f5-06fc63d21247 fd12:877c:1d66::/64 |
+--------------------------------------+-----------+----------------------------------------------------------+
admin 権限の devstack-admin でアクセスすると、 一般ユーザー権限では詳細が見えない neutron external network の ext_net のサブネットが見えていることが分かります。
ubuntu@dev16:~$ env | grep OS_
ubuntu@dev16:~$ neutron --os-cloud devstack-admin net-list
+--------------------------------------+-----------+----------------------------------------------------------+
| id                                   | name      | subnets                                                  |
+--------------------------------------+-----------+----------------------------------------------------------+
| 3698d3c7-d581-443e-bf86-53c4e3a738f7 | mynetwork |                                                          |
| a11600dc-cac9-4934-970b-0fa44d5097b8 | ext_net   | 2b86127b-424b-40a9-9a85-9f7be470bc25 172.24.4.0/24       |
|                                      |           | 7e268119-891b-447e-97f8-8e900fb0664e 2001:db8::/64       |
| e8cbf82d-9f26-4d6f-ab02-dc3bdf05ffce | net1      | 6b832dfe-f271-443c-abad-629961414a73 10.0.0.0/24         |
|                                      |           | cdcc616b-0cff-482f-96f5-06fc63d21247 fd12:877c:1d66::/64 |
+--------------------------------------+-----------+----------------------------------------------------------+

os-client-config のおもしろいところは、 プロファイルという仕組みがあり、クラウドプロバイダーの名前を指定することで、 そのプロバイダー固有の情報を指定できるところです。 簡単な例を挙げます。

clouds:
  dreamhost:
    profile: dreamhost
    auth:
      username: demo
      password: your-secret
      project_name: demo

dreamhost の例です。 devstack の例では存在した auth_url や region_name を 指定していない点に注目してください。これは dreamhost 用の設定が os-client-config の中にあり、 https://github.com/openstack/os-client-config/blob/master/os_client_config/vendors/dreamhost.json で auth_url や region_name が定義されているからです。 auth_url などはクラウドプロバイダーが決まれば一意に決まるものなので、 こう仕組みはありがたいですね。

Mitaka リリースでは、おそらくほとんどの CLI が os-client-config に対応するのではないかと予想しています。

それでは良い CLI 生活を!


最後になりますが、コマンドプロンプトの簡単なカスタマイズを紹介します。 環境変数で指定していたとしても、今どれを指定しているかは簡単に知りたいです。 私は以下のようにして、環境変数の内容をコマンドプロンプトに表示しています。 かなり便利です。

こんな感じになります。

ubuntu@dev16:~$ export OS_CLOUD=devstack
ubuntu@dev16:~ [OS_CLOUD:devstack]$ env | grep OS_
OS_CLOUD=devstack
ubuntu@dev16:~ [OS_CLOUD:devstack]$ unset OS_CLOUD 

ubuntu@dev16:~$ . devstack/openrc 
ubuntu@dev16:~ [OS:demo/demo@dev16]$ env | grep OS_
OS_REGION_NAME=RegionOne
OS_IDENTITY_API_VERSION=2.0
OS_PASSWORD=cloudstack-wakame-aws
OS_AUTH_URL=http://172.16.18.47:5000/v2.0
OS_USERNAME=demo
OS_TENANT_NAME=demo
OS_VOLUME_API_VERSION=2
OS_NO_CACHE=1

ubuntu@dev16:~ [OS:demo/demo@dev16]$ . devstack/openrc admin admin
ubuntu@dev16:~ [OS:admin/admin@dev16]$ env | grep OS_
OS_REGION_NAME=RegionOne
OS_IDENTITY_API_VERSION=2.0
OS_PASSWORD=cloudstack-wakame-aws
OS_AUTH_URL=http://172.16.18.47:5000/v2.0
OS_USERNAME=admin
OS_TENANT_NAME=admin
OS_VOLUME_API_VERSION=2
OS_NO_CACHE=1

~/.bashrc からの抜粋。色付けはお好みで。 ちょっと工夫すれば、admin 権限とそうでないときでプロンプトの色も変えられるでしょう。

__openrc_ps1() {
    if [ -n "$OS_CLOUD" ]; then
        echo " [OS_CLOUD:$OS_CLOUD]"
        return
    fi
    if [ ! -n "$OS_AUTH_URL" ]; then
        return
    fi
    local auth_url=$(echo $OS_AUTH_URL | cut -d / -f 3 | cut -d : -f 1)
    if which resolveip > /dev/null; then
        hostname=$(resolveip $auth_url 2>/dev/null | sed -e 's/^.* is //' -e 's/ //' | cut -d , -f 1)
        if [ -n "$hostname" ]; then
            auth_url=$hostname
        fi
    fi
    echo " [OS:${OS_USERNAME}/${OS_TENANT_NAME}@${auth_url}]"
}

PS1='\u@\h:\w$(__git_ps1)$(__openrc_ps1)\$ '

2014年12月16日火曜日

Linux で veth のペアのデバイスを調べる方法

この記事は、OpenStack Advent Calendar の 12/16 の記事です。 Advent Calendar らしく軽い小ネタを紹介したいと思います。

OpenStack Neutron を使っていると veth のペアがたくさん作成されます。 @enakai00 さんが書いた nova-compute内のネットワーク詳細 の図を見ると、veth pai がたくさん作成されるのが分かります。

ネットワークデバイスの種類を知る方法

VM が2個しか起動していない Compute Node でもたくさんのデバイスがあります。 ネットワークデバイスにはいろいろ種類があり、通常のネットワークデバイス以外にも、 linux bridge, tap device, veth (virtual ethernet device) などいろいろあります。 でも、ip link コマンドなどを使っても、その種類は通常表示されず、名前しか情報源がありません。

-d オプションを付けて ip link コマンドを実行すると、びっくりデバイスの種類が表示されます。 これを見ると qbr0f657850-c2bridgetap0f657850-c2 は tap (tun) デバイス、 qvb0f657850-c2veth デバイスだと分かります。

この -d オプション、ip(8) コマンドのマニュアルを見ても出て来ません。どうしてなのでしょうか? どなたかご存知でしたら教えて下さい。

ubuntu@stack02:~$ ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:06:f7:0e brd ff:ff:ff:ff:ff:ff promiscuity 0
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default
link/ether 7a:1a:cb:27:31:87 brd ff:ff:ff:ff:ff:ff promiscuity 1
4: br-int: <BROADCAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 9e:0d:03:df:e9:44 brd ff:ff:ff:ff:ff:ff promiscuity 1
6: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether fa:af:f9:17:d9:91 brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge
7: qbr0f657850-c2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP mode DEFAULT group default
link/ether 62:d9:1f:5e:46:b5 brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge
8: br-tun: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 06:67:1b:78:f4:48 brd ff:ff:ff:ff:ff:ff promiscuity 1
9: qvo0f657850-c2: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master ovs-system state UP mode DEFAULT group default qlen 1000
link/ether 0e:5a:08:46:0b:eb brd ff:ff:ff:ff:ff:ff promiscuity 2
veth
10: qvb0f657850-c2: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master qbr0f657850-c2 state UP mode DEFAULT group default qlen 1000
link/ether 62:d9:1f:5e:46:b5 brd ff:ff:ff:ff:ff:ff promiscuity 2
veth
11: tap0f657850-c2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master qbr0f657850-c2 state UNKNOWN mode DEFAULT group default qlen 500
link/ether fe:16:3e:09:9e:9e brd ff:ff:ff:ff:ff:ff promiscuity 1
tun
12: qbr2948dbdf-a4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP mode DEFAULT group default
link/ether a6:ab:a4:80:f5:13 brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge
13: qvo2948dbdf-a4: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master ovs-system state UP mode DEFAULT group default qlen 1000
link/ether e6:af:80:d8:df:9d brd ff:ff:ff:ff:ff:ff promiscuity 2
veth
14: qvb2948dbdf-a4: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master qbr2948dbdf-a4 state UP mode DEFAULT group default qlen 1000
link/ether a6:ab:a4:80:f5:13 brd ff:ff:ff:ff:ff:ff promiscuity 2
veth
15: tap2948dbdf-a4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast master qbr2948dbdf-a4 state UNKNOWN mode DEFAULT group default qlen 500
link/ether fe:16:3e:fd:92:56 brd ff:ff:ff:ff:ff:ff promiscuity 1
tun

veth デバイスのペアを特定する方法

veth デバイスは 2個のネットワークデバイスがセットになっていて、 一方の veth デバイスに入力したパケットはそのままペアになっている veth デバイスに出力されます。

そうすると、どのネットワークデバイスがペアになっているかを知る方法がほしいです。 OpenStack では、ポートIDを元に veth デバイスが作成されるので、名前からペアを推測することはできますが、 やはりシステム上で確認したいものです。

ethtool を使うと veth の相手の veth デバイスを知ることができます。

ubuntu@stack02:~$ sudo ethtool -S qvb2948dbdf-a4
NIC statistics:
     peer_ifindex: 13

peer_ifindex が 13 になっています。 この番号は上の ip link コマンドの出力の左端に表示されているネットワークデバイスの通し番号です。 13 は qvo2948dbdf-a4 が相手だと分かります。

qvo2948dbdf-a4qvb2948dbdf-a4 がペアの veth デバイスということになります。

まとめ

Neutron でたくさん作成されるネットワークデバイスの種類を Linux で確認する方法と、 veth のペアのデバイスを作成する方法を紹介しました。 こういう小ネタもたまっていくと便利ですね。

余談

Neutron で ML2 plugin + OVS mechanism driver を使っていると、 VM のポート毎に veth ペア qvo2948dbdf-a4qvb2948dbdf-a4 とブリッジ qbr2948dbdf-a4 が作成されます。

このデバイス名の由来を見ておきましょう。

  • q: Quantum の q です。これが実装された当時は Neutron ではなく Quantum だった名残です。すでに運用されているシステムがあるので、名前の変更は簡単ではなく q が残っています。
  • 2-3文字目 : 役割を表しています。
    • vo: vveth の頭文字です。o は OVS に接続される方の veth の片割れという意味です。
    • vb: vveth の頭文字です。b は (Linux) bridge に接続される方の veth の片割れという意味です。
    • br: bridge の頭文字です。
  • 4文字目以降 : Neutron Port UUID の前の方の値

2014年12月10日水曜日

openstack.jp のデザインを直してくれる人募集

OpenStack Advent Calendar (2枚目) の 12/10 分です。 最初は 2枚目のカレンダーには書く気は全くなかったのですが、 openstack.jp ウェブサイトのデザインのイケていないところを知ってもらい、 デザインしてくれる人を募集するいい機会だと思いますので、書いてみることにします。

openstack.jp のウェブサイトを見てみましょう。

左上に OpenStack のロゴはありますが、何かいまいちです。 いまいちなところをあげてみましょう。

  • なんか色合いが OpenStack っぽくない。
  • デフォルトのテーマを持ってきて使っている印象を受ける。
  • 最近のサイトだと、サイバーは左よりも右の方が多いかな。サイトの上にメニューがあって、右に追加の情報があることが多いですね。
  • トップページにユーザー会についての情報が二箇所もある。メインコンテンツとサイドバーの下の方。

せめて、色調を OpenStack っぽくして、メニューバーを上に横に並べるだけで、だいぶ改善するように思います。

同じ OpenStack ユーザー会が主催している OpenStack Days Tokyo のウェブサイトはイベント会社が作っているだけあってきちんとしていますね。

OpenStack と一緒に取り上げられることも多い同じ IaaS 基盤である CloudStackEucalyptus の日本ユーザー会のサイトを見てみましょう。色合いも本家と比較的あっていますし、デフォルトの設定のままという感じも薄いので、合格点というところだと思います。 これらに比べてるとだいぶ見劣りがしますね。

実は、半月くらい前はもっといまいちな状態でした。archive.org によると、2014年6月19日時点の様子はこんな感じでした。左上のロゴがテーマのデフォルトのままになっている、同じメニューが上と左にある、といった状況で、あまりにひどいので、直しました。

といろいろデザイン面でいまいちな点を挙げて来ましたが、 2〜3人のボランティアで運用されている状況の中、 コンテンツを更新いただいている方には感謝しています。

デザイン面がよくなれば、openstack.jp もそれなりのサイトになると思いますので、デザインを直してやろうという方がいれば大歓迎です。

2014年2月14日金曜日

OpenStack Third Party Testing: How to use Zuul for your Jenkins

When setting up OpenStack third party testing, it is useful to trigger rechcecking from Gerrit comment such as recheck no bug or recheck-<vendor> and there is an onging discussion whether recheck support should be a requirement.

This blog post describes the manual procedure of Zuul and how to migrate the trigger of Jenkins jobs from gerrit trigger jenkins plugin to Zuul.

After migrating to Zuul, I feel it is nice and makes it easy to control Jenkins jobs trigger flexibly.

If you are interested in setting up Zuul by puppet, please check the puppet manifests from Jay Pipes. https://github.com/jaypipes/os-ext-testing

Prerequisite

  • Ubuntu 12.04.3 LTS

Zuul setup

Installation

Install required dependency packages:

aptitude install python-pip
aptitude install python-webob python-lockfile python-paste
aptitude install python-yaml python-paramiko python-daemon

Create a user and a group for zuul service:

groupadd zuul
useradd -m -d /home/zuul -s /bin/bash -g zuul zuul

Retrieve and install zuul:

git clone https://git.openstack.org/openstack-infra/zuul /opt/zuul
pip install /opt/zuul
Configuration files
Preparation (optional)

puppet module in openstack-infra/config has useful config examples. Checkout them first to somewhere. I refer to the directory where you checkedout openstack-infra/config git repository by <INFRA_CONFIG>.

git clone https://git.openstack.org/openstack-infra/config
/etc/zuul/zuul.conf

Customize the following parameters. The detail description is available at http://ci.openstack.org/zuul/zuul.html#zuul-conf.

The following parameters are important:

  • [gerrit]
    • server : review.openstack.org
    • user : gerrit service account name
    • sshkey : gerrit service account ssh private key
    • baseurl : https://review.openstack.org/ if server field is different from review.openstack.org. It is useful if your zuul server is behind proxy ()or you are using SSH port forwarding to access OpenStack gerrit).
  • [zuul]
    • url_pattern : URL of each Jenkins job. Usually used to point a build job.
    • zuul_url : It is used as git repo URL. Jenkins job fetches a commit from this URL.
    • status_url : There is no need to customize it unless you post a start message.
[gearman]
server=127.0.0.1

[gearman_server]
start=true
log_config=/etc/zuul/gearman-logging.conf

[gerrit]
server=review.openstack.org
user=jenkins
sshkey=/var/lib/zuul/ssh/id_rsa

[zuul]
layout_config=/etc/zuul/layout.yaml
log_config=/etc/zuul/logging.conf
state_dir=/var/lib/zuul
git_dir=/var/lib/zuul/git
push_change_refs=false
url_pattern=http://<your log server>/logs/{job.name}/{build.number}
status_url=http://status.openstack.org/zuul/
job_name_in_report=true
zuul_url=http://ostack10.svp.cl.nec.co.jp/p
/etc/zuul

Prepare logging configurations.

cp config/modules/openstack_project/files/zuul/gearman-logging.conf /etc/zuul
cp config/modules/openstack_project/files/zuul/logging.conf /etc/zuul

openstack_functions.py is optional but it is useful to define LOG_PATH or single_use_node (disable a used slave node after the job completes).

cp config/modules/openstack_project/files/zuul/openstack_functions.py /etc/zuul
/etc/init.d/zuul

Create a startup script of zuul.

cp config/modules/zuul/files/zuul.init /etc/init.d/zuul
chmod +x /etc/init.d/zuul
/etc/zuul/layout.yaml

layout.yaml is a heart of zuul. The following is an example I used. The detail description is available at http://ci.openstack.org/zuul/zuul.html#layout-yaml.

  • check pipeline watches three gerrit events from master branch. branch parameter is optional. If you want to watch all branches, remove it.
    • Note that a target branch(es) can also be limited by branch parameter of jobs configuration. branch in jobs configuration selects jobs to be executed, but zuul post a result even when no job is selected. For such case, it looks better to limit a target branch in pipelines configuration.
  • If you remove success and/or failure entries from gerrit trigger definition, a result of a build job will not be posted to gerrit. It is useful while you are preparing zuul and jenkins.

  • If parameter-function: use_single_node is specified, a slave node where the build run will be offline. Enable it depending on your test infra.

includes:
  - python-file: openstack_functions.py

pipelines:
  - name: check
    description: Newly uploaded patchsets enter this pipeline to receive an initial +/-1 Verified vote from Jenkins.
    manager: IndependentPipelineManager
    precedence: low
    trigger:
      gerrit:
        - event: patchset-created
          branch: ^master$
        - event: change-restored
          branch: ^master$
        - event: comment-added
          branch: ^master$
          comment_filter: (?i)^\s*recheck(( (?:bug|lp)[\s#:]*(\d+))|( no bug))\s*$
    success:
      gerrit:
        verified: 1
    failure:
      gerrit:
        verified: -1

jobs:
  - name: ^.*$
    parameter-function: set_log_url
  - name: ^Neutron_Gate$
    parameter-function: single_use_node
  #- name: ^Neutron_Gate$
  #  branch: ^master$

projects:
  - name: openstack/neutron
    check:
      - Neutron_Gate
  - name: openstack-dev/sandbox
    testcheck:
      - TestJob
Create working directories

Create working directories for zuul.

# mkdir /var/log/zuul
# chown zuul /var/log/zuul
# mkdir /var/run/zuul
# chown zuul:zuul /var/run/zuul
# mkdir /var/lib/zuul
# chown zuul:zuul /var/lib/zuul
# mkdir /var/lib/zuul/git
# chown zuul /var/lib/zuul/git
# mkdir /var/lib/zuul/ssh
# chown zuul:zuul /var/lib/zuul/ssh
# chmod 700 /var/lib/zuul/ssh
# mkdir /var/lib/zuul/www
Apache
git repository

Zuul provides a git repository (if push_change_refs=false). The easiest way is to export git repositories via apache.

SetEnv GIT_PROJECT_ROOT /var/lib/zuul/git/
SetEnv GIT_HTTP_EXPORT_ALL

AliasMatch ^/p/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$ /var/lib/zuul/git/$1
AliasMatch ^/p/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/lib/zuul/git/$1
ScriptAlias /p/ /usr/lib/git-core/git-http-backend/

Enable required apache modues.

a2enmod proxy
a2enmod proxy_http
a2enmod rewrite
status page

(It is not tested yet.)

aptitude install libjs-jquery
cd /opt
git clone https://github.com/mathiasbynens/jquery-visibility.git
cd /var/lib/zuul/www
ln -s /opt/zuul/etc/status/public_html/index.html
ln -s /opt/zuul/etc/status/public_html/app.js
ln -s /usr/share/javascript/jquery/jquery.min.js
ln -s /opt/jquery-visibility/jquery-visibility.js
wget http://getbootstrap.com/2.3.2/assets/bootstrap.zip
unzip bootstrap.zip
rm bootstrap.zip

/etc/apache2/site-enabled/000-default

RewriteEngine on
RewriteRule ^/status.json$ http://127.0.0.1:8001/status.json [P]
Reload apache

Finally make sure apache loads a new configuration.

service apache2 reload
Start Zuul
service zuul start

The detail of command line under the hood is described at http://ci.openstack.org/zuul/zuul.html#starting-zuul.

Jenkins configuration for zuul

Zuul uses Gearman to dispatch build requests to Jenkins and receive build results. Jenkins Gearman plugin needs to be installed.

  1. Install Gearman plugin
  2. In Gearman Plugin config menu, set Gearman server host and port and test connection.
  3. In Gearman Plugin config menu, ensure to check "Enable Gearman".

Migrate Jenkins job trigger to zuul

Jenkins Gearman plugin dispatch Jenkins builds when it received build requests from Zuul (via Gearman), so no build trigger is needed in Jenkins job configruation.

Make sure that remove "Gerrit Event" from "Build Triggers" in Jenkis job configuration.

Otherwise, one gerrit event triggers two Jenkins builds per job through Gerrit Trigger plugin and Zuul.

That's all.

Tips

Defining vendor specific recheck
It is useful to kick only your CI system without affecting other CI systems. The following layout.yaml configuration allows you to trigger your build by a comment recheck-XXXX in addition to the existing recheck no bug or recheck bug NNNNN comments.
    trigger:
      gerrit:
        - event: patchset-created
        - event: change-restored
        - event: comment-added
          comment_filter:
            - (?i)^\s*recheck(( (?:bug|lp)[\s#:]*(\d+))|( no bug))\s*$
            - (?i)^\s*recheck-XXXX\s*$

2013年12月24日火曜日

Network Namespace を使って OpenVNet を動かしてみる

先日 Wakame Advent Calendar の記事として コマンドラインからだけで OpenVNet の機能を推測してみる という記事を書きましたが、その後実際に動かしていませんでした。

最初は、Ubuntu で OpenVNet を動かして、いんちきで OpenStack と接続してみようと思ったのですが、Ubuntu では 12.04 でも 13.04 でも 13.10 でも VNA が起動できなかったので、もう少し軽く動かしてみます。 Ubuntu 13.10 では wakame-edge のコンパイルも失敗しました (trema-edge では修正されています)。

今回は Network Namespace を使って OpenVNet の動作を確認してみようと思います。動作確認するのは以下の3つです。

  • 仮想ネットワークを作成
  • IPアドレスが重複する仮想ネットワークでの動作
  • 仮想ネットワーク間のルーティング

Network Namespace は 1 つの Linux ホストの中に仮想的なネットワーク環境を複数作れる機能です。これを使うと、ネットワークの実験を VM などリソースをたくさん消費する手段を使わずにできるので便利です。仮想ネットワークを複数作ってみる方法については、OpenVNet Installation Guideには、VM 環境を使って OpenVNet を動かしてみる方法が紹介されていますが、Network Namespace を使うとお手軽に実験できるので、今回はその方法を紹介します。

今回の動作確認の中でいくつかスクリプトを作りました。OpenVNet test tools with network namespace においてありますので、適当に参考にしてください。

CentOS6 で Network Namespace を使う準備

Network Namespace は最近新しめの Linux Kernel を採用している Ubuntu などではそのままで利用できますが、OpenVNet の推奨環境となっている CentOS6 では残念ながら、まだそのままでは動きません。

設定方法を CentOS6 で Linux Network Namespace を使うにまとめましたので、こちらを参照して、必要な iproute パッケージ (ip コマンド) を入れておいて下さい。

OpenVNet のインストール

OpenVNet Installation Guideにしたがって、インストールを行います。

"Let's try 1Box OpenVNet" の前までを実施します。

注意点としては、OpenVNet のサービスを開始する前に redis も開始しておく必要があります。

# chkconfig redis on
# service redis start

CentOS6.4 が推奨となっていますが、今回は CentOS6.5 を使いましたが問題なく動作しました。

仮想ネットワークの作成

準備ができたので、2つの VM が接続された仮想ネットワーク net1 を作成してみます。

  • 仮想ネットワーク net1 : 172.16.1.0/24
    • インタフェース1 (VM1 相当) : 172.16.1.1 (@netns ns1)
    • インタフェース1 (VM2 相当) : 172.16.1.2 (@netns ns2)

手順を順番に見ていきます。 Installation Guide に説明がある 1 ホストで VM を使って動かす例についている設定スクリプト db.sh を参考に整理してみました。

まず、最初に OpenFlow として動作する Open vSwitch bridge を OpenVNet に登録します。Open vSwitch bridge は br0 です。今回は 1 ホストなので、最初に一回 datapath を登録します。

https://github.com/amotoki/openvnet-test-tools/blob/master/vnet-register-datapath.sh#L17
# uuid は dp- で始める必要あり
# dpid は 0x を先頭に付けること (OVS の出力とは違うので注意)
./vnctl datapath add ¥
    --uuid dp-br0 ¥
    --display-name=br0 ¥
    --dc-segment-id=seg1 ¥
    --dpid=0x000002fdee8e0a44 ¥
    --ipv4-address=$HOST_IP192.168.122.207 ¥
    --node-id=192.168.122.207
openvnet にネットワークを作成します。
https://github.com/amotoki/openvnet-test-tools/blob/master/vnet-register-net1.sh#L20
# ネットワークの作成
# 仮想ネットワークなので network-mode は virtual を指定する
# uuid は nw- で始める必要あり
./vnctl network add ¥
  --uuid nw-net1 --display-name=net1 --ipv4-network 172.16.1.0 --ipv4-prefix=24 ¥
  --domain-name=dom1 --network-mode=virtual

# datapath に作成したネットワークを関連付ける
# broadcast-mac-address は MAC2MAC でブロードキャストパケットを転送する際に使用されるはず
./vnctl datapath networks add dp-$BRNAME nw-net1 --broadcast-mac-address=08:00:27:10:03:01

# ネットワークに接続されるインタフェースを登録する
# uuid は単なる ID ではなく、OVS に追加するポートの名前と一致している必要があるみたい。また、if- で始める必要あり。
# IPv4 address と MAC はそのインタフェースの値を指定する。VM の場合は VM の中の NIC、network namespace の場合は
# namespace 内の NIC の値になる。OVS に add-port する network device の値ではない点に注意。
./vnctl interface add --uuid=if-veth1 --ipv4-address=$IP1 --network-uuid=nw-net1 --mac-address=$MAC1
./vnctl interface add --uuid=if-veth2 --ipv4-address=$IP2 --network-uuid=nw-net1 --mac-address=$MAC2
上記で OpenVNet 側の設定は終わりです。

次に、実際のデバイスを作成します。
./ovs-create.sh
./if-create-net1.sh
ovs-create.sh は OVS bridge を作成し、datapath ID 設定、OpenFlow 設定などを行っています。今回は OpenVNet の設定を行った後でないと、うまく動かなかったので、このタイミングで行っています。

/var/log/wakame-vnet/vna.log を見ると、OVS から接続や、スイッチポートが追加されていることが分かります。

ping を実行してみましょう。
network namespace ns1 の 172.16.1.1 側から ns2 の 172.16.1.2 に ping を送ります。
# ip netns
ns2
ns1
# ip netns exec ns1 ip -o addr
207: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
207: lo    inet 127.0.0.1/8 scope host lo
207: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
208: ns-veth1:  mtu 1500 qdisc pfifo_fast state UP qlen 1000\    link/ether 52:54:00:0d:84:01 brd ff:ff:ff:ff:ff:ff
208: ns-veth1    inet 172.16.1.1/24 brd 172.16.1.255 scope global ns-veth1
208: ns-veth1    inet6 fe80::5054:ff:fe0d:8401/64 scope link \       valid_lft forever preferred_lft forever
# ip netns exec ns1 ping -c 5 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=0.466 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=0.134 ms
64 bytes from 172.16.1.2: icmp_seq=3 ttl=64 time=0.058 ms
64 bytes from 172.16.1.2: icmp_seq=4 ttl=64 time=0.066 ms
64 bytes from 172.16.1.2: icmp_seq=5 ttl=64 time=0.063 ms

--- 172.16.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4000ms
rtt min/avg/max/mdev = 0.058/0.157/0.466/0.157 ms
# ip netns exec ns1 arp -na
? (172.16.1.2) at 52:54:00:0d:84:02 [ether] on ns-veth1
ns2 の 172.16.1.2 側で tcpdump を仕掛けておくとパケットが見えます。
# ip netns exec ns2 tcpdump -i ns-veth2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns-veth2, link-type EN10MB (Ethernet), capture size 65535 bytes
01:16:35.234573 ARP, Request who-has 172.16.1.2 tell 172.16.1.1, length 28
01:16:35.234610 ARP, Reply 172.16.1.2 is-at 52:54:00:0d:84:02 (oui Unknown), length 28
01:16:35.234709 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 1, length 64
01:16:35.234758 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 1, length 64
01:16:36.234855 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 2, length 64
01:16:36.234917 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 2, length 64
01:16:37.234633 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 3, length 64
01:16:37.234651 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 3, length 64
01:16:38.234501 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 4, length 64
01:16:38.234522 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 4, length 64
01:16:39.234528 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 12927, seq 5, length 64
01:16:39.234550 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 12927, seq 5, length 64
01:16:40.234515 ARP, Request who-has 172.16.1.1 tell 172.16.1.2, length 28
01:16:40.234793 ARP, Reply 172.16.1.1 is-at 52:54:00:0d:84:01 (oui Unknown), length 28
^C
14 packets captured
14 packets received by filter
0 packets dropped by kernel

IPアドレスが重複する仮想ネットワークでの動作


仮想ネットワークといえば、IP アドレス重複に対応できるか!です。もちろん試してみましょう。
先ほどの net1 に追加で、同じ IP アドレスを持つ net3 を作成します。
  • 仮想ネットワーク net1 : 172.16.1.0/24
    • インタフェース : 172.16.1.1 (@netns ns1)
    • インタフェース : 172.16.1.2 (@netns ns2)
  • 仮想ネットワーク net3 : 172.16.1.0/24
    • インタフェース : 172.16.1.1 (@netns ns5)
    • インタフェース : 172.16.1.2 (@netns ns6)
OpenVNet にネットワークとインタフェースを登録して、その後実際のデバイスを作成します。net1 のときと全く同じなので、説明は省略します。
./vnet-register-net3.sh
./if-create-net3.sh
ns1 の 172.16.1.1 と ns5 の 172.16.1.1 からそれぞれ 172.16.1.2 に ping を送ってみます。ns1 から送った時は同じ net1 側の ns2 の 172.16.1.2で、ns5 から送った時は net3 側の ns6 の 172.16.1.2 にパケットが届いていることが確認できます。片方の結果だけ載せておきます。
(net3 側の 172.16.1.1 から ping)
# ip netns exec ns5 ping -c 3 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=1.68 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from 172.16.1.2: icmp_seq=3 ttl=64 time=0.048 ms

--- 172.16.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.048/0.596/1.683/0.768 ms

(net1 側の 172.16.1.2)
# ip netns exec ns2 tcpdump -i ns-veth2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns-veth2, link-type EN10MB (Ethernet), capture size 65535 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel

(net3 側の 172.16.1.2)
# ip netns exec ns5 tcpdump -i ns-veth5
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns-veth5, link-type EN10MB (Ethernet), capture size 65535 bytes
01:34:38.493450 ARP, Request who-has 172.16.1.2 tell 172.16.1.1, length 28
01:34:38.493998 ARP, Reply 172.16.1.2 is-at 52:54:00:0d:84:06 (oui Unknown), length 28
01:34:38.494005 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 57983, seq 1, length 64
01:34:38.494093 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 57983, seq 1, length 64
01:34:39.493507 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 57983, seq 2, length 64
01:34:39.493546 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 57983, seq 2, length 64
01:34:40.493471 IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 57983, seq 3, length 64
01:34:40.493503 IP 172.16.1.2 > 172.16.1.1: ICMP echo reply, id 57983, seq 3, length 64
01:34:43.493577 ARP, Request who-has 172.16.1.1 tell 172.16.1.2, length 28
01:34:43.493588 ARP, Reply 172.16.1.1 is-at 52:54:00:0d:84:05 (oui Unknown), length 28
^C
10 packets captured
10 packets received by filter
0 packets dropped by kernel
IP アドレス帳服がある場合でも、期待通り仮想ネットワークが機能しています。

仮想ネットワーク間のルーティング

最後に、仮想ネットワーク間でのルーティングをさせてみたいと思います。先日、コマンドラインからだけで OpenVNet の機能を推測してみる記事を書いた時にはいまいちよく分からなかったのですが、サンプルスクリプトの db.sh などと睨めったことしつつなんとなく分かって来ましたので、試してみました。

IP アドレスの違う論理ネットワークを2つ用意し、それらを論理ルータで接続する構成です。論理ルータは 172.16.1.254 と 172.16.2.254 という2つのインタフェースを持ちます。
  • 仮想ネットワーク net1 : 172.16.1.0/24
    • インタフェース : 172.16.1.1 (@netns ns1)
    • インタフェース : 172.16.1.2 (@netns ns2)
    • ルータインタフェース : 172.16.1.254
  • 仮想ネットワーク net2 : 172.16.2.0/24
    • インタフェース : 172.16.2.3 (@netns ns3)
    • インタフェース : 172.16.2.4 (@netns ns4)
    • ルータインタフェース : 172.16.2.254
まず、net2 を作成しておきます。手順はこれまでと全く同じです。net2 で ping などを行って、導通を確認しておきましょう。
./vnet-register-net2.sh
./if-create-net2.sh
OpenVNet 側にルータを作成していきます。次のような手順になります。
  1. ルータインタフェースの作成
  2. ルータインタフェースに対して network_service を関連付け (なくても動く)
  3. route_link の作成
  4. route_link の datapath への関連付け (なくても動く)
  5. route 情報の登録
最初の2つがルータのインタフェースに関する操作、後ろの3つがルータの宣言のようなものです。
ざっくり言うと、route_link が論理ルータに対応します。 route は論理ルータの経路情報です。名前が分かりにくいですね。

サンプルスクリプト db.sh に書かれている手順を参考にしていますが、network_service の作成、route_link の datapath への関連付けの2つはなくても動きました。どういうことでしょう?

実際のコマンドベースで説明します。一部のコマンドは CLI では提供されていないので、curl を直接叩いています。
https://github.com/amotoki/openvnet-test-tools/blob/master/vnet-register-router.sh#L29
# net1 の論理ルータに対応するインタフェースを作成
# OpenVNet が機能を提供するので mode=simulated を指定する
# vnctl CLI では mode が指定できないので、curl を使う
curl -s -X POST --data-urlencode uuid=if-vnet1gw ¥
  --data-urlencode network_uuid=nw-net1 ¥
  --data-urlencode mac_address=52:54:00:74:00:00 ¥
  --data-urlencode ipv4_address=172.16.1.254 ¥
  --data-urlencode mode=simulated ¥
  http://localhost:9090/api/interfaces

# ルータインタフェースの終端を OpenVNet が行うことの宣言のようです。
# ★ 1 ホストでのテストでは、なくても動きました。
./vnctl network_service add --uuid=ns-vnet1gw --interface-uuid=if-vnet1gw --display-name=router

# net2 の論理ルータに対応するインタフェースを作成
# OpenVNet が機能を提供するので mode=simulated を指定する
# vnctl CLI では mode が指定できないので、curl を使う
curl -s -X POST --data-urlencode uuid=if-vnet2gw ¥
  --data-urlencode network_uuid=nw-net2 ¥
  --data-urlencode mac_address=52:54:00:74:22:22 ¥
  --data-urlencode ipv4_address=172.16.2.254 ¥
  --data-urlencode mode=simulated ¥
  http://localhost:9090/api/interfaces

# ルータインタフェースの終端を OpenVNet が行うことの宣言のようです。
# ★ 1 ホストでのテストでは、なくても動きました。
./vnctl network_service add --uuid=ns-vnet2gw --interface-uuid=if-vnet2gw --display-name=router

# 論理ルータの作成
# OpenVNet では route_link という概念として表現される
# MAC address が何に利用されるのかは不明
./vnctl route_link add --uuid=rl-vnetlink1 --mac-address=52:54:00:60:11:11

# 作成した route_link を datapath に登録する
# ★ 1 ホストでのテストでは、なくても動きました。
curl -s -X POST --data-urlencode route_link_uuid=rl-vnetlink1 \
  --data-urlencode mac_address=08:00:27:20:01:01 \
  http://localhost:9090/api/datapaths/dp-br0/route_links/rl-vnetlink1

# 論理ルータでの経路情報を登録する
curl -s -X POST --data-urlencode uuid=r-vnet1 ¥
  --data-urlencode interface_uuid=if-vnet1gw ¥
  --data-urlencode route_link_uuid=rl-vnetlink1 ¥
  --data-urlencode ipv4_network=172.16.1.0 ¥
  http://localhost:9090/api/routes
curl -s -X POST --data-urlencode uuid=r-vnet2 ¥
  --data-urlencode interface_uuid=if-vnet2gw ¥
  --data-urlencode route_link_uuid=rl-vnetlink1 ¥
  --data-urlencode ipv4_network=172.16.2.0 ¥
  http://localhost:9090/api/routes
上記を実行して、OpenVNet に登録します。ルータ機能は OpenVNet が提供するので、実際のデバイスの設定は必要ありません。
./vnet-register-router.sh
net1 と net2 の間で ping を行ってみます。 net1 の 172.16.1.1 (@ns1) から net2 の 172.16.2.3 (@ns3) に ping を実行しています。ちゃんと ping が通りました。成功です。

# ip netns exec ns1 ping 172.16.2.3
PING 172.16.2.3 (172.16.2.3) 56(84) bytes of data.
64 bytes from 172.16.2.3: icmp_seq=1 ttl=64 time=20.0 ms
64 bytes from 172.16.2.3: icmp_seq=2 ttl=64 time=0.060 ms
64 bytes from 172.16.2.3: icmp_seq=3 ttl=64 time=0.056 ms
^C
--- 172.16.2.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2550ms
rtt min/avg/max/mdev = 0.056/6.711/20.018/9.409 ms
/var/log/wakame-vnet/vna.log を見ると、vna が ARP に応答している記録もあります。

I, [2013-12-24T02:41:50.725318 #2463]  INFO -- : 0x000002fdee8e0a44 interfaces/simulated: simulated arp reply (arp_tpa:172.16.1.254)
I, [2013-12-24T02:41:50.735063 #2463]  INFO -- : 0x000002fdee8e0a44 interfaces/simulated: simulated arp reply (arp_tpa:172.16.2.254)
ちなみに、ルータインタフェースへの ping は通りませんでした。

# ip netns exec ns1 ping 172.16.1.254
PING 172.16.1.254 (172.16.1.254) 56(84) bytes of data.
^C
--- 172.16.1.254 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4590ms

まとめ

今回は OpenVNet の基本的な使い方を Network Namespace を使って実験してみました。仮想ネットワークの作成、重複 IP アドレスの扱い、仮想ルータを使った通信をどうやって行うのかは分かりました。外部の物理ネットワークとの通信方法は時間があれば調査してみようと思います。

テスト中には、コマンドの投入順序の違いなどで動かず、やり直してみたら動いた、といった点もありました。全体的に、OpenVNet に情報 (datapath や interface) を登録してから、OVS から VNA への接続やインタフェースの OVS への追加を行う必要があるみたいです。今後そのあたりは改善されていくと思います。また、一部の機能は CLI からは制御できないようです。そのうち対応されるのではないかと思います。

OpenVNet API を見ながら仮想ネットワークのAPIについて考えたこと

Wakame Advent CalendarをきっかけにOpenVNetのAPIを見て思ったことを書いてみたいと思います。OpenFlow Controller の Northband API や OpenStack Neutron API なども見ているので、どうなっているとうれしいのか考えてみたい。

API はまだまだ発展途上にあると思われるので、何かの参考になればと思います。

  • datapath にネットワークを登録するのはなぜ?
  • API で datapath の登録は必要なのか?
  • Interface の論物マッピングの指定方法
  • 論理モデルや名称は既存の概念に近い方がよいのでは?
  • OpenVNet 内部で使用する MAC アドレスの指定はない方がよい
  • UUID が UUID ではない

datapath にネットワークを登録するのはなぜ?

ネットワークは論理的なものであり、どのノードに広がって存在するかはそのときの インタフェースの登録状況で決まるのが自然だろう。 一方、datapath は特定のノードに存在する OpenFlow スイッチ (OVS) に対応するものなので、 datapath にネットワークを登録するということは、ネットワークが新しいノードの広がる場合には ネットワークを datapath に登録しないといけないということにもなるし、 逆に datapath にネットワークを登録していないと、ネットワークがそのノードには 広がることができないということになる。

datapath とネットワークの関連付けは OpenVNet の中で面倒をみるようになっていると API を使う側から見るとうれしい。 datapath にネットワークを登録するのは実装上の都合のように思える。

同じことが route_link についても言えると思う。

API で datapath の登録は必要なのか?

OVS が VNA に接続してくるので、OpenVNet はどの OVS が配下にいるのか分かるので、 datapath を API 経由で登録しておく必要はないように思える。

上で書いたように、現在はネットワークを datapath に登録するというステップがあるので、 登録対象となる datapath を API で登録する手順になっているように思えた。 それだけの理由であれば、datapath を API 経由で登録する必要はないと思われる。

datapath を API で登録することに意味がないか、他に考えてみると、 自分の配下にある OVS を明示的に管理しておき、意図しない OVS が接続されてしまうことを避けるという 可能性がある。OpenVNet の場合は、現在のところ OpenFlow Controller と OVS が同じホスト上で動作することが前提になっているので、この目的は必要性が少なそうに見える。

やはりいらないのだろうか? もちろん datapath 一覧を取得する方はインベントリーの意味があると思う。

Interface の論物マッピングの指定方法

リソース Interface は、論理ネットワークにおける論理インタフェースを表しているが、 同時に論理インタフェースと物理情報の対応付けも必要である。 現在の Interface 作成時のパラメータにも、これらの両方が含まれているが、 物理情報の指定はもっと明示的なパラメータにした方がよいと思われる。

現在、物理インタフェースとの対応付けに使われる情報は、以下の2つである。

  • uuid
  • owner-datapath-id

UUID は必ず "if-****" という形式である必要があり、この情報が OVS に登録する物理ポートとの対応付けにも使用されている。UUID という名前からは物理インタフェースとの対応付けに使用されるとは、到底想像できないので、明らかに別パラメータを用意した方がいいと思う。

datapath-id の方は省略可能なパラメータになっているが、これは物理インタフェース名がシステム全体で一意であれば datapath-id がなくても一意に識別できるからだと推測している。

OpenFlow ネットワークで、スイッチポートを識別する情報としては、

  • (datapath_id, switch port name)
  • (datapath_id, switch port number)
  • (datapath_id, MAC address)

などが考えられる。

安全に OpenFlow ネットワークでスイッチポートを識別するには、最初の2つのいずれかを利用できる必要がある。 この方法は、OpenFlow Controller では一般的に使用されている。例えば、 Trema ベースで作られている Virtual Network PlatformAPI のポート作成 (POST /networks/<net_id>/ports) では、 datapath_id と インタフェース名 (name) または スイッチポート番号 (number) のいずれかを指定する。Trema Sliceable Switch の API では datapath_id と port (ポート番号) の組み合わせで指定する。他の OpenFlow Controller でもこの方法が一般的に使用されている。

ポート番号を指定する方法とインタフェース名を指定する方法を比べると、インタフェースを指定する方法の方がサーバー管理者から見ると敷居が低いと思う。いくつか理由があると思う。一つは、VM を起動する際に、インタフェース名は libvirt の設定ファイルなどでも登場するため、サーバー管理者にも比較的馴染みがあるパラメータだという点。もうひとつは、ポート番号は OpenFlow スイッチにインタフェースを接続してみないと分からず、インタフェース名の方が変化が少ないため、管理しやすいということである。

(datapath_id, MAC address) の方法は、VM 管理などサーバー側の管理者から見るとわかりやすい指定方法である。OpenFlow スイッチでのポート番号を知る必要がないので、API を利用するにあたっての敷居が低くなる。一方、スイッチポートから受信したパケットの MAC アドレスを信用して処理を行うため、MAC アドレスの信頼性をどうやって確保するかが課題となる。そのため、あまり積極的には使われていない印象を受ける。

比較すると、(datapath_id, インタフェース名) を指定する方法が自然に見える。

論理モデルや名称は既存の概念に近い方がよいのでは?

最初に API を見た時、route_link っていったい何だろう?と思いました。サンプルスクリプトを見てみると、実は論理ルータと1:1に対応しているみたいと気付くと、あとは簡単でした。

このとき思ったのが、最初から router という名前だったら分かりやすかったのではないか?ということでした。

仮想ネットワークを作成する際も、論理構造としては、L2、L3 などのこれまで広まっている IP ネットワークの考え方やモデルが基本になってくると思います。ネットワーク仮想化は、論理構造と物理構造を分離して使い勝手を向上するのがひとつの価値ですが、その際に論理構造の定義は既存の概念を踏襲できると、とっつきやすいと思います。一方で、論理構造を物理構造にどのようにマッピングするかは、それぞれのネットワーク仮想化プラットフォームの腕の見せどころなのだと思います。

OpenVNet 内部で使用する MAC アドレスの指定はない方がよい

OpenVNet の API では、ネットワークの MAC2MAC で使用するブロードキャストアドレスや、 route_link の MAC アドレスなど、OpenVNet の内部で使用する MAC アドレスを指定する必要がある。 これらは API を利用する人から見ると、どの MAC アドレスが割り当て済みなのか管理する必要があり、指定することが難しい。あらかじめ OpenVNet が使用してよい MAC アドレス空間をプールとして登録しておき、自動で払い出すようにすべきだと思う。場合によっては Wakame や OpenVNet で OUI を取得してもいいかもしれない。

同様のことは、OpenVNet が機能を提供するルータや DHCP などの mode-simulated のインタフェースについても言える。これらの MAC アドレスも自動で払いだしてくれるとうれしい。

UUID が UUID ではない

これは些細なこと。API でリソース識別子として UUID というフィールドがあるが(以下の引用はネットワークの例)、 正確には UUID ではなく、単なる ID である。

また、OpenVNet の UUID フィールドはリソース種別に応じたプレフィックスが必要である (ネットワークの場合は nw-)。この意味でも、純粋な UUID ではない。

UUID は RFC 4122 http://www.ietf.org/rfc/rfc4122.txt でそのフォーマットや ユニーク性を担保して値を生成する方法が規定されている。 UUID とするのであれば、きちんと RFC で定められた UUID を生成するのがよいと思う。

# ./vnctl.sh network show
{"id"=>1,
 "uuid"=>"nw-net1",
 "display_name"=>"net1",
 "ipv4_network"=>"172.16.1.0",
 "ipv4_prefix"=>24,
 "domain_name"=>"dom1",
 "network_mode"=>"virtual",
 "editable"=>nil,
 "created_at"=>"2013-12-23T17:24:53Z",
 "updated_at"=>"2013-12-23T17:24:53Z",
 ""=>nil}

まとめ

OpenVNet API へのコメントが中心ですが、仮想ネットワークの API について考えたことを書き連ねてみました。 API を比較すると設計思想などが見えてくるので、比べてみるのはおもしろいです。

OpenVNet の話をすると、API はいろいろと改善の余地がありそうです。まだ生まれたてなので、機能が大きくなる前に API がどうあるべきかの観点からの見直しを行うのも一つの選択肢かなとも思いました。

2013年12月23日月曜日

CentOS6 で Linux Network Namespace を使う

Network Namespace は 1 つの Linux ホストの中に仮想的なネットワーク環境を複数作れる機能です。これを使うと、ネットワークの実験を VM などリソースをたくさん消費する手段を使わずにできるので便利です。

Network Namespace は最近新しめの Linux Kernel を採用している Ubuntu などではそのままで利用できますが、RHEL6/CentOS6 ではまだ標準では利用できません。 RedHat が中心となって開発している OpenStack ディストリビューション RDO のパッケージを使うことで、簡単に CentOS6 環境でも Network Namespace が利用できるので、紹介します。

RDO が RHEL6/CentOS6 向けに Network Namespace に対応したパッケージを用意しているのは、OpenStack Neutron では Reference Implementation は Network Namespace の機能が利用されており、RHEL6/CentOS6 環境で OpenStack を動かせるようにするためです。

Network Namespace (netns) を動かすためには、netns に対応した Linux Kernel と netns に対応した iproute パッケージ (ip コマンド) が必要です。
CentOS6.5 とそれ以前では、カーネルの対応状況が異なりますので、分けて説明します。

CentOS 6.5 (以降)

CentOS 6.5 で提供されるカーネル自身は netns に対応していますが、標準で提供されている iproute は netns に対応していません (ip netns というサブコマンドがありません)。

RDO の yum レポジトリから iproute パッケージをインストールします。
  • RDO の yum レポジトリを追加してからインストールする方法
  • RDO で提供される iproute RPM パッケージを直接指定する
の2つの方法があります。今現在は後者の方が楽だと思いますが、パッケージバージョンの変更には前者の方が強いでしょう。
RDO の yum レポジトリを追加してからインストールする方法
rdo-release.rpm をインストールすると、RDO 関連の yum repository 定義が /etc/yum.repos.d 以下に登録されます。その後、yum install で iproute を指定してインストールします。
# yum install http://rdo.fedorapeople.org/rdo-release.rpm
# yum install iproute
インストール後に期待通りのバージョンがインストールされているか確認します。あわせて、netns がきちんと動作するか確認しておきます。
# rpm -qa | grep iproute
iproute-2.6.32-130.el6ost.netns.2.x86_64
# ip netns
# ip netns add testns1
# ip netns
testns1
# ip netns delete testns1
RDO で提供される iproute RPM パッケージを直接指定する
# yum install http://repos.fedorapeople.org/repos/openstack/openstack-havana/epel-6/iproute-2.6.32-130.el6ost.netns.2.x86_64.rpm
パッケージは以下の URL にありますので、もしパッケージのバージョン番号が変わっていた場合はチェックしてみてください。古いものがなくなることはあまりありませんが。
http://repos.fedorapeople.org/repos/openstack/openstack-havana/epel-6/

CentOS 6.4 以前

CentOS 6.4 以前では、カーネルも network namespace に対応していません。そのため、カーネルと iproute パッケージの両方を新しくする必要があります。
http://repos.fedorapeople.org/repos/openstack/openstack-havana/epel-6/ にパッケージがありますので、kernel と iproute を探してインストールします。
  • kernel-2.6.32-358.123.2.openstack.el6.x86_64.rpm, kernel-firmware-2.6.32-358.123.2.openstack.el6.noarch.rpm
  • iproute-2.6.32-130.el6ost.netns.2.x86_64.rpm
個別にインストールするよりは、上記の CentOS6.5 の説明の「RDO の yum レポジトリを追加してからインストールする方法」にあるように yum repository を追加しておいた方が楽だと思います。

では、Network Namespace を使って、お手軽にネットワーク実験をお楽しみください。