ゆううきブログ

$yuuki->blog(type => 'infra');

100Gbpsソフトウェアルータの実現可能性に関する論文

前回のGPUを用いたSSLリバースプロキシの実装について - ゆううきブログ に続いて,同じ研究グループが出している100Gbpsのソフトウェアルータの実現可能性に関する論文を読みました.

Sangjin Han ; KAIST, Daejeon, South Korea ; Keon >Jang ; KyoungSoo Park ; Sue Moon

"Building a single-box 100 Gbps software router" Local and Metropolitan Area Networks (LANMAN), 2010 17th IEEE Workshop on

IEEE Xplore - Building a single-box 100 Gbps software router

論文紹介

現在または予測可能な未来の技術を用いて,100 Gbpsのソフトウェアルータを構築することができるかどうかが考察されています. できるかどうかは明言されていませんが,I/O周りのボトルネックの削減がまだ難しそうという印象でした.

論文では,近年のIntel/AMDの改良されたアーキテクチャ(マルチコアプロセッサ,CPU統合メモリコントローラ,PCI Express,マルチCPUソケット,QPIなど)を用いて,100Gbpsソフトウェアルータを実現しようとするとどこがボトルネックとなるかが書かれています. IPルーティングまでは考えておらず,パケットの送信,受信,フォワーディング性能のみ考慮されています.

具体的には,以下のようなCPUソケット2個,IOハブ2個,2ポートの10GbE NICを6個もつNUMAな構成を例にとり,CPUサイクル,I/O帯域幅,メモリ帯域幅の観点で何がボトルネックとなるかが議論されています.

f:id:y_uuki:20130514233714p:plain

CPUサイクル,メモリ帯域幅については測定値というよりは理論性能を元に考察されていますが,I/O帯域幅については上記構成を単純化したいくつかの構成を用いて,どのリンクがボトルネックかを実験で測定した値を用いて議論されています.

CPUサイクルについては,パケットの動的確保をやめて静的に確保して使いまわすなどいくつかの最適化を施し,なおかつパケットI/O以外のIPルーティングなどの処理をFPGAまたはGPUにオフロードすれば,CPUサイクルはさほど問題にならないということでした.

ちなみに,IPルーティングやIPsecをGPUで高速化する話は下記の論文にあります.

PacketShader - GPU-accelerated Software Router

I/O帯域幅については,PCIeリンクおよびQPIリンクの実効帯域はさほど問題ではないが,マルチIOハブ構成時にIOハブ数にパケットの受信性能がスケールしないようです.IOハブのchipsetがマルチIOに対して最適化されていない?ような感じでした.

(PCIeについてはPCIe 2.0 x8で実効帯域幅が双方向で32Gbps以上なので2ポートの10GbE NICの帯域を損なわない. QPIについては双方向で100Gbpsくらいでるので,一方のノードに受信パケットが偏っていてなおかつそれらのパケットをもう一方のノードにフォワーディングするような状況でなければ,問題ない.)

スライド

詳細については原文または輪講で使用した以下のスライドを参照してください.

補足

論文が書かれた2010年時点ではPCIe 2.0が最新でしたが,現在は "PCIe 3.0"が最新です. 実効帯域幅は約2倍となっています.

感想

どうでもいいですが,論文の3ページ目に"By Googling, we find that …"とか書いてあってお茶目な感じでした.

日本アイ・ビー・エム Intel Xeon Processor X7560 8C 2.26 GHz 24MB Cache 130w 60Y0311

日本アイ・ビー・エム Intel Xeon Processor X7560 8C 2.26 GHz 24MB Cache 130w 60Y0311

ゆとりbotで学ぶ転職の極意 #yutori_history

こちらは,ゆとり Advent Calendar の9日目のエントリーです.

前回は@grimroseさんのopen build/reports/life/index.html: ゆとりを持って行動するには #yutori_historyでした.

ゆとり Advent Calendar : ATND



ゆとりさん、ご転職おめでとうございます.

ゆとりさんとは会ったことさえありません.

そんなゆとりさんにみんな大好きhotchemi転職推進botをプレゼント致します.

毎日あの時間に起動します.

ゆとりさん、ご転職おめでとうございます.

ゆとりさんとは会ったことさえありません.


次回の担当は@gab_kmさんです.よろしくおねがいします.

GrowthForecastでサーバルームの室温監視

研究室のサーバールームの空調が弱くて,40コアくらいある計算サーバにフルで仕事させると室温が上がって,全サーバが落ちるみたいな事例が去年あったらしいので,室温のグラフ化とアラートメール送信をやれみたいなタスクが降ってきた.

そこで,サーバルームのうちのサーバ1台にUSB温度計を差して,定期的に温度を取得し,GrowthForecastに投げればいいと考えた. アラートメールのほうは温度を取得したのちに閾値を超えていたらsendmailでメール投げる感じ。

USB温度計として下記を使用する.Linuxから温度データを取得できることが重要.

Amazon.co.jp: サンコー USB温度計 AKIBA58: 家電・カメラ

必要なもの

  • Ubuntu 12.04
  • perl (GrowthForacastがPerlで書かれているので必要)
  • GrowthForecast (グラフ表示用のWebアプリケーション)
  • Temper (上記温度をGrowthForecastに登録してくれるスクリプト.アラートメール機能もある)

必要なパッケージとモジュールのインストール

$ sudo apt-get install build-essential libusb-0.1-4 libusb-dev build-dep sendmail git
$ curl -L http://cpanmin.us/ | sudo perl - App::cpanminus
$ sudo cpanm -n GrowthForecast HTTP::Tiny Email::Simple Email::Sender

GrowthFocastのインストール

以前の 研究室のサーバに流行りのGrowthForecastを導入してみた - ゆううきブログ を参照. perlbrew使ってたけど,perl-buildにした.

Temper

Temperをインストールする.TemperはUSBドライバAPIを叩いて上記USB温度計から温度情報を摂氏で取得して標準出力に出力してくれるもので,たぶん有志の人がつくってくれたやつ.

bitplane/temper · GitHub

今回はこれをforkして,出力される時間をJSTに直して,さらにGrorwthForecastのAPIを叩く処理とメール投げる処理を書いたスクリプトを入れておいた.

リポジトリの中身

  • temperコマンド: 温度情報を摂氏で取得して標準出力
  • temper_gfcast.pl: temperコマンドを実行して,GrowthForecastのAPIを叩いて温度を登録し,閾値を超えていたらアラートメールを投げる
  • temper_gfcast.conf: 閾値などの設定を行う
  • install.sh
$ git clone https://github.com/y-uuki/temper
$ cd temper
$ sudo ./install.sh

これで,temperコマンドとtemper_gfcast.plが/usr/local/binに入り,/etc/temper_gfcast/temper_gfcast.confが作成される.

まず, /etc/temper_gfcast/temper_gfcast.confを書き換える.

# Examle
#
# sender: root@example.com
# receiver: server-group@example.com
#
# graph_url: http://example.com/list/server-room/watch
#
# warning_threshold: 35
# critical_threshold: 45
#

sender: server-room@laboraoty
receiver: y_uuki@laboratory

graph_url: http://growthforecast.laboratory/list/server-room/temper

warning_threshold: 35
critical_threshold: 45

senderはメールの送信者,receiverはメールの受信者,graph_urlはGrowthForecast APIのendpointで, warnings_thresholdとcriticalthresholdにはメールアラートのための閾値を設定する.上記の場合,温度が35度以上ならばwarningメールを,45度以上ならばcriticalメールを送信する.

最後に,cronで一定時間ごとにtemper_gfcast.plを実行させておけばよい,

$ sudo crontab -e

cronに下記の一行を登録する. 5分ごとに温度監視することにした.

*/5 * * * * /opt/perl-5.14/bin/perl /usr/local/bin/temper_gfcast.pl >> /var/log/temper.log 2>&1

以上で,おわり.

temper_gfcast.pl

GrowthForecastのAPIはHTTP::Tinyで叩いて,アラートメールは Email::Simpleでメール内容を作成して,Email::Senderを介してsendmailで投げる感じ.

#!/usr/bin/env perl
use utf8;
use strict;
use warnings;

use Try::Tiny;
use HTTP::Tiny;
use Email::Sender::Simple 'sendmail';
use Email::Simple;

use constant {
    TEMPER_PATH => '/usr/local/bin/temper',
    CONF_PATH   => '/etc/temper_gfcast/temper_gfcast.conf',
    DEFAULT_WARN_THRESHOLD     => 35,
    DEFAULT_CRITICAL_THRESHOLD => 45,
};


main();
exit;

sub main {
    my $temp_value = current_temperature();

    # putput log
    my ($sec, $min, $hour, $day, $month, $year) = localtime(time);
    print sprintf("temperature:%d\ttime:%04d%02d%02d-%02d:%02d:%02d\n",
        $temp_value, $year + 1900, $month + 1, $day, $hour, $min, $sec);

    send_to_gfcast($temp_value);

    my $params = parse_conf(CONF_PATH);
    my $mail_from = $params->{sender} or die "Not found sender in conf file";
    my $mail_to   = $params->{receiver} or die "Not found receiver in conf file";
    my $graph_url = $params->{graph_url} or die "Not found graph_url in conf file";
    my $warn_threshold     = $params->{warning_threshold} || DEFAULT_WARN_THRESHOLD;
    my $critical_threshold = $params->{critical_threshold} || DEFAULT_CRITICAL_THRESHOLD;

    for ($warn_threshold, $critical_threshold) {
        die "threshold is invalid. $_ is not integer" if $_ !~ /\d+/;
    }

    return if $temp_value < $warn_threshold;
    # Warning or Critical process

    my $status = $critical_threshold <= $temp_value ? 'critical' : 'warning';

    my $email = create_alertmail($mail_from, $mail_to, $graph_url, $status, $temp_value);
    try {
        sendmail($email);
    } catch {
        die "Failed sending mail $_";
    };
}


sub current_temperature {
    my $output = readpipe(TEMPER_PATH);
    chomp $output;
    unless ($output =~ /,([\d|.]+)/) {
        die "Not Found temperature";
    }
    return int($1);
}


sub parse_conf {
    my $conf_path = shift;

    unless (-f $conf_path) {
        die "file:$conf_path is not found";
    }

    open(my $conf_fh, "<", $conf_path)
        or die "failed to open file:$conf_path";

    my $params = {};
    while (my $line = <$conf_fh>) {
       chomp $line;
       next if $line =~ /^\s*$/; # empty line
       next if $line =~ /^\s*#/; # for commentout

       my ($name, $value) = split(/:\s/, $line);
       $value =~ s/(.+?)#?/$1/;
       $params->{$name} = $value;
    }

    return $params;
}


sub send_to_gfcast {
    my $temp_value = shift;

    my $http = HTTP::Tiny->new;
    my $response = $http->post_form('http://localhost/api/server-room/watch/temperature', {
        number => int($temp_value),
        mode   => 'gauge',
        color  => '#333399'
    });

    if ($response->{status} =~ /^(4\d\d|5\d\d)/) {
       die "$response->{status}: $response->{reason}\n" . $response->{content};
    }
}


sub create_alertmail {
    my ($from, $to, $graph_url, $status, $temp_value) = @_;

    my $body = <<"EOS";
Server Room Temperature

Status: $status
Temperature: '$temp_value' degrees

Graph: $graph_url
EOS

    my $email = Email::Simple->create(
        header => [
            From    => "\"Temperature Alert\" <$from>",
            To      => "<$to>",
            Subject => "Server room tempature $status",
        ],
        body => $body,
        attributes => {
            content_type => 'text/html',
        },
    );
    return $email;
}

参考

CentOS 5.9でNICを2枚挿したときのネットワーク設定

10GbpsNIC環境でOSのネットワークスタックのパフォーマンスを測定しようとしたら意外と面倒だった.厳密には,面倒だったのはCentOS 6系での設定で結局あきらめた. 仕方がないからCentOS 5.9でやるとそれほどハマらずに設定できた.

2台のマシンがあり,オンボードNICと10GbpsNICが載っている.このとき,

  • 10GbpsNIC同士を直接10Gbps Eternetで接続する.
  • オンボードNICはゲートウェイを通してインターネットに接続できるようにする.

というのが課題.

これに対して以下の図のようなネットワーク構成にしたらちゃんと通信できた.

f:id:y_uuki:20130422021415p:plain

設定

大雑把な設定内容を以下のような感じ.

  • オンボードNIC周りと10GbpsNIC周りを異なるサブネットに置く.
  • インターネットに接続するために192.168.1.1をデフォルトゲートウェイとする.
  • 10.0.0.1を10.0.0.0/8サブネットをゲートウェイとする.
  • eth1に入ってきたパケットはeth1に出て行くようにルーティングする.

まず,eth0やeth1に対するIPアドレスなどの設定を/etc/sysconfig/network-scripts/ifcfg-eth*に書く.

以下,Host1のみだけどHost2においてもIPADDRとHWADDR以外は同様.

  • /sbin/sysconfig/ifcfg-eth0
DEVICE=eth0
BOOTPROTO=static
BROADCAST=192.168.1.255
HWADDR=xx:xx:xx:xx:xx:xx
IPADDR=192.168.1.10
NETMASK=255.255.255.0
NETWORK=192.168.1.0
ONBOOT=yes
  • /sbin/sysconfig/ifcfg-eth1
DEVICE=eth1
BOOTPROTO=static
BROADCAST=10.255.255.255
HWADDR=yy:yy:yy:yy:yy:yy
IPADDR=10.0.0.100
NETMASK=255.0.0.0
NETWORK=10.0.0.0
ONBOOT=yes
GATEWAY=10.0.0.1

設定を有効にするためにnetworkを再起動.

# service network restart

しかし,ここでeth0経由でインターネットにつなげないという問題が発生した. routeコマンドでみるとデフォルトゲートウェイの設定がおかしかった(どうおかしかったかはメモってなくて忘れた)ので,/sbin/sysconfig/networkのGATEWAYDEVにデフォルトゲートウェイに対してパケットを送受信したいインタフェース名を書く.

GATEWAYDEV=eth0

networkを再起動すると,ipコマンドの出力は以下のようになり,eth0経由でインターネットに接続できた. これをHost1,Host2の両方で設定する.

host1% /sbin/ip -v
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.0.0     *               255.255.252.0   U     0      0        0 eth0
169.254.0.0     *               255.255.0.0     U     0      0        0 eth1
10.0.0.0        *               255.0.0.0       U     0      0        0 eth1
default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0

次は,eth1側の方で,NICが複数あるとパケットを受信したときにそのパケットに対してどのインタフェースから送信するかをルーティングする必要がある.

今回の場合,eth1で受信したパケットはそのままeth1で返せば良い. 受信パケットに対するルーティングはiproute2で行う.

古来のrouteコマンドですと、宛先に応じてNICのデバイスやnext hop routerの指定を行いますが、Linuxではこのテーブルを複数持つことができ、さまざまな条件でどのテーブルを参照させるかを指定することができます。

「さまざまな条件」とは、例えば

  • 送信元IPアドレス
  • パケットが入ってきたデバイス名
  • fwmark といったものです。

同じサブネットへのNIC 2枚指し、またはソースルーティングのおはなし - (ひ)メモ

Host1を例にとり,まず10GbpsNIC通信用のテーブルexpを作成する.(新規に作成しなくても既存のmainテーブルにルールを追加すればよいだけな気がする) このテーブルはパケットの送信時に参照され,送信元IPアドレスが10.0.0.100であれば,eth1にパケットを送るという意味である.

  • /sbin/sysconfig/network-scripts/route-eth2
dev eth1 src 10.0.0.100 table exp

次に,上記テーブルを参照する条件をrule-eth2に書く. 参照条件として送信元IPアドレスが10.0.0.100であればexpテーブルを見るというように設定する.

  • /sbin/sysconfig/network-scripts/rule-eth2
from 10.0.0.100 table exp prio 200 

networkを再起動して設定を有効にするとpingは通るようになった.

host1% service network restart
host1% ping -I eth1 10.0.0.200

しかし,TCP/UDPのベンチマークツールであるiperfで通信すると,

host1% iperf -s -B 10.0.0.100
host2% iperf -c 10.0.0.100

No route to hostと怒られる.

tcpdumpでみてみると到達不能ICMPパケットが返ってきてるっぽい.

16:46:08.071054 IP 10.0.0.200.51674 > 10.0.0.100.ndmp: S 2955619377:2955619377(0) win 5840 <mss 1460,sackOK,timestamp 23138650 0,nop,wscale 7>
16:43:56.564510 IP 10.0.0.100 > 10.0.0.200: ICMP host 10.0.0.100 unreachable - admin prohibited, length 68

hidekiy blog: [linux] ping は通るのに No route to host と言われる

この辺はSELinuxとiptablesが邪魔をしている可能性があるので,あんまりよくないけどSELinuxとiptablesを切る.

SELinuxを切る.

# sestatus
# setenforce 0

SELinuxを無効化する | Smart -Web Magazine

iptablesを切る

# service iptables off

ここまでやって,10GbpsNIC間で問題なく通信できるようになった.

参考

途中要らない設定が入ってそう. ネットワーク難しい.

GPUを用いたSSLリバースプロキシの実装について

近年,汎用計算の高速化のためのアクセラレータとして注目されているGPUを,ネットワーク処理に適用する一環として,サーバサイドのSSL処理に注目した論文を読んだので,内容を軽く紹介します.

SSLShader: Cheap SSL acceleration with commodity processors Proceedings of the 8th USENIX conference on Networked systems design and implementation 2011

なお,評価に使われた実装の一部のソースコードが公開されています.

紹介

背景

SSL(Secure Socket Layer)ですがHTTP2.0のたたき台となっているSPDYなどではSSL(正確にはTLS)の使用が前提となっており,今後サーバサイドでのSSL対応の要求は高まってくると思います.
SSLの暗号処理部分はCPUバウンドな処理なので,CPU負荷の分散のために,高速なハードウェアアクセラレータを用いたり,複数の低速なソフトウェアアクセラレータを用意して,それらに対してロードバランサにより処理を並列分散したりします.

動機

しかし,ハードウェアアクセラレータの問題として価格コストが高い,新暗号アルゴリズムの対応など柔軟性に欠ける,ロジックがハードウェアで実装されているので,障害原因を突き止めにくい,ということがあります. さらに,ソフトウェアアクセラレータを複数台並べるには,これも複数台分のコストがかかりますし,システム構成も複雑になるので運用しづらいという問題があります.

提案手法

これらの問題点を解決するのが,論文の主題となっているSSLShaderです. SSLShaderは,暗号化部分を安価なGPUにオフロード機能を備えたSSLリバースプロキシであり,高負荷時に高スループット,低負荷時に低レイテンシとなるように設計されています.

GPUの一般の話は下記スライドが参考になると思います.

高負荷時に高スループット,低負荷時に低レイテンシというのが論文中で何度も繰り返されており,論文の重要なポイントとなっています. これをどのように達成するのかというアイデアをまとめると下記のようになります.

  • 高スループットを達成するためには数百個(最新世代では1000個以上)のGPUコア(単体コアの性能はCPUに劣る)を活用しなければならないため,並列化が重要
  • 各クライアントから発行されるSSLリクエストは独立しているため,各SSLリクエストは並列に処理可能.したがって,GPUに複数のリクエスト分の暗号処理をまとめてつっこむ.
  • 低負荷時(同時接続数が少ない)ときはGPUに渡すタスク量が小さすぎるためCPUのみのほうがスループットが高く,逆に高負荷時にGPUに渡すタスク量が多すぎてスループットがでないということがある.よって,CPU1コアのほうがスループットが高くなるような負荷であれば,CPUに任せる.一方,GPUに渡すタスク量が多過ぎないように,適当なところでGPUに渡すタスク量に上限を設ける.

上記のアイデアを実現するためのシステム構成として以下の図のようなシステムが提案・実装されています.

2013041715593

CPUコア数分のワーカスレッド(実験環境では12個)と,GPUの個数分のGPUインタフェーススレッドを用意します. 各ワーカスレッドは個別にInput queueをもち,GPUインタフェーススレッドはGPU queueをもちます. どちらのキューにもRSEやAESなどの暗号処理タスクが格納されます. ワーカスレッドは基本的に,I/Oイベントを処理(図のPushとPop)し,空いている時に暗号処理(図のProc)を実行します. さらに,キューに含まれるタスク数が多い時はGPU queueにInput queueの内容を移行させます. GPUインタフェーススレッドは,GPU queueを見て最も先頭にあるタスクの暗号アルゴリズムと同じ暗号アルゴリズムのタスクをまとめてGPUにオフロードします. (この辺,異なる暗号アルゴリズムであっても,GPUのリソースが余っていれば同時にGPUに処理させたりしないのか疑問.)

なお,キューに含まれるタスク数の多寡は,静的に設定した閾値により決定されます.値自体はベンチマークテスト時のconfigureで測定した上で設定されるようです.

以上のようなパイプライン処理で,高スループット低レイテンシを達成されています.

評価

SSLShaderの比較対象として,lighttpd with OpenSSLが使われています. クライアント7台からabコマンドを叩いて対象のSSLプロキシに負荷をかけています.

まず,スループットの評価ですが,クライアントからの同時接続数を変化させたときのSSL transactions / s (TPS) を測定したものになっています. RSA 1024bitで,SSLShaderがlighttpdの2〜2.5倍速く,RSA 2048bitで,約4〜6倍速くなっています.

20130417155633

次に,レイテンシ(レスポンスタイム)の評価です. 負荷が高いケースと低いケースで提案手法の効果をCDFで評価しています. 例えば,80%のときはコネクション100本のうちの80本はレイテンシ◯msで返せるということがわかります. 凡例の括弧内は(コネクション数,TPS)となっています.TPSはabコマンドに手を入れて一定のTPSでリクエストを投げられるようになっています.

低負荷時は両方とも,90%程度のコネクションが数ms程度程度で返せています. 高負荷時はSSLShaderのほうがレイテンシは小さくなっています.

20130417155634

以上より,特に高負荷時においてはSSLShaderの優位性が確認されています.

スライド

研究室の輪講用につくったスライドです.細かすぎると感じた内容についてはいつかとばしています. 読み違い・誤解などが含まれている可能性があります. 特にSSLの認証周りはサッパリなので結構適当です.

関連研究

GPUをネットワーク処理に応用するという研究として,ルータのルーティンやIPSecの暗号化処理をGPUで高速化するというものがあります. SSLShaderと同じ研究チームのようです.

PacketShader - GPU-accelerated Software Router