東京はもう古い、これからは京都

最近、京都市内に引っ越して生活クオリティがあがってる。家はオフィスのある烏丸御池からちょっと離れたところの、閑散としたところにある。 自炊活動を全て捨てていて、調理器具はおろか電子レンジもないし、お皿も箸もスプーンもない。 代わりに、隠れ家っぽいお店に行くのが好きなので、毎日いろんな店に行ってる。 そんなに高い店には行かないので、食費は思ったほどかかってない。

先週の休みは、昼過ぎに起きて、御所の近くまで散歩して、隠れ家っぽいカレーショップでさつまいもチキンカレー食べて、コンテッサチェアを観るために家具の店行って、隠れ家っぽいケーキ屋でアーモンドケーキとプリン買って帰って、部屋でプリン爆発させたりした。

コンテッサチェアについては以下のエントリが詳しい。

プリン、まれに爆発するので気をつける必要があるが、だいたい豊かな生活をしていると言える。

京都でおすすめの店

ランチ/ディナーで1000円前後のお店。飲み屋とラーメン屋は多すぎるので省いた。全部隠れ家っぽくて落ち着きのある店。

tomomii さんにすすめられたところ。京都らしく路地の奥まったところに立地してて、丁寧な感じの店の雰囲気がとてもよい。味も一口たべておっ、て思う感じ。

欧風カレーの有名な店。カレールーは土鍋に入って出てくる。インドカレーみたいなのがあまり好きじゃないので、こういうのが食べたかったと思うような味がする。

オフィスビルのすぐ裏にある。本が置いてある系のこじんまりした店。オーソドックスなチキンカレーがあって丁寧な味でおいしい。めっちゃ落ち着ける。

昭和っぽい喫茶店。見た目も味も特に変わってるところはないけど、なぜかおいしい。

swimy さんおすすめの店。町家そのままという感じで、こんなに落ち着きのある店と料理があるのかという感じ。とにかく丁寧。

冒頭に書いてたカレー屋さん。御所と裁判所が近くて、周辺の雰囲気がよい。さつまいものカレーとかがある。

とにかく分厚くてうまいタマゴサンドがある。ボリュームがあって、これでだいたい満腹になる。コロナのタマゴサンドとかいうやつで、わりとカジュアルに売り切れるっぽい。

1汁3菜形式で、健康的な定食が食べられる。めっちゃ静かというわけではなく、適度にざわついている感じ。

10時過ぎても空いてて便利。昔ながらの日本の洋食屋さんという感じで、どのメニューもボリュームが結構ある。デミグラスソースが美味しくて、たぶんコーヒーみたいなの入れて味の深みを出してる気がする。

Googleのオフィスの一角みたいな雰囲気の店。(Googleには行ったことがないのでなんとなくのイメージ)。たまごが乗っかったかわいい感じのカレーがでてくる。

ランチにたまに行ってる。牛バラ肉のワイン煮込みみたいなやつがとにかくうまい。

灯りを落としていて暗めな雰囲気。半個室風になっていて、ゆったりした気持ちになれる。多分、水出しコーヒーでいがらっぽくなくてスッキリした味。

おもちゃっぽい小物が大量においてある。吹き抜けの2階建てで、2階に陣取るとなんかよい。日替わりのオムライスがおいしい。超ミニサイズが選べるので、超小食な人にも安心。

京都でおすすめの仕事

ISUCONでNginxとMySQLをDocker化したときのパフォーマンス

現実的なWebサービス環境において、Docker化によるパフォーマンス低下がどの程度のものか調査するために、 ISUCON4 の予選問題のうち、Nginx と MySQL 部分を Docker 化してベンチマークをとってみた。 典型的なWebサービスシステムの3層構造(Proxy, App, DB)を構築し、ベンチマーカーにより高ワークロードを実現できるので、ISUCON の予選問題は適当な題材といえる。 Docker のパフォーマンスについて留意することは先日書いたエントリに全て書いてる。

上記のエントリを要約すると、Docker のパフォーマンスについて重要なこととは

  • storage-driver の選択 (AUFS or Device mapper or ...)
  • Volume の ON / OFF
    • AUFS などの差分ファイルシステムをバイパスするかしないか
  • Host networking の ON / OFF
    • NAPT(ポートマッピング) しないかするか

の3つであり、前者2つがブロックI/O集約なアプリケーション、後者がネットワークI/O集約なアプリケーションにおいて特に重要であることがわかった。 予選では、自チームはNginx, MySQL, memcached, Perl(Webアプリサーバ)を使っていたが、全部 Docker 化するのも面倒なので、上述のエントリの知見から、ブロックI/O集約な MySQL と ネットワークI/O集約な Nginx を Docker 化すれば十分だろうと考えた。

ベンチマーク条件

基本的に、ISUCON4予選のレギュレーションに則ってる。

インスタンスタイプ: m3.xlarge CPU: Xeon E5-2670 v2 @ 2.50GHz 4 vCPU メインメモリ: 16GB RAM ストレージ: EBS Magnetic volumes OS: Amazon Linux 3.14.19-17.43

自チームのISUCON4予選時の構成を基準にしている。(Docker化する都合上、UNIXドメインソケットを切ってあるので、スコアは多少落ちてる)

  • スコア 39982 (約 3000 req/s)
    • 試行ごとに +-1000 スコア程度の誤差はでる
  • 予選突破レベル
  • データは全部メモリに乗る
  • セッション情報などは memcached
  • Nginx で静的ファイルを返す
  • ネットワークスタック、Nginx, MySQL は普通のチューニング

f:id:y_uuki:20141124005422p:plain

ベンチマーク結果と考察

  • MySQL と Nginx 両方ので Host Networking on/off 比較
  • Volume の on/off 比較は MySQL のみ
  • benchmarker の workload 指定は全て 22 (一定)
  • Docker 1.2.0
  • Docker のリンク機能は使ってない
  • storage-driver には device-mapper を選択
    • 本当は AUFS と比較するべきだが、Amazon Linux では AUFS のインストールが面倒...

結果は以下のとおり。net=bridge は Host Networking を OFF、net=host は ON にしている状態である。

構成 スコア
default 39982
Nginx(net=bridge) 32368
Nginx(net=host) 38976
MySQL(net=bridge) 38346
MySQL(net=host) 40802
MySQL(net=host, no-volume) 39335

まず、Nginx(net=bridge) が20%ほど遅い。 これはNAPT処理のオーバヘッドによるものと推測できる。 一方で、MySQL(net=bridge) の場合はデフォルトとほとんど変わらないところをみると、リバースプロキシのようなネットワークI/O集約なワークロードのアプリケーションの場合、オーバヘッドがより大きいと言える。

次に、MySQL(net=host, no-volume)のスコアがデフォルトあまり変わらない。 http://yuuki.hatenablog.com/entry/docker-performance のエントリでは、I/O集約なアプリケーションでは Volume を使わないと、AUFS の場合、I/Oレイテンシが大きいという結論になった。 今回使った Device mapper だと、ブロックデバイスを束ねて層を作る実装なので、AUFSのようにそもそもI/O要求が各層を通過するオーバヘッドがない可能性もある。 とはいえ、今回は書き込みI/Oがそれほど多くなく、データもメモリに全てのっているので、ファイルシステム層をバイパスしてもあまり効果がなかったのかもしれない。

その他については、net=host だとNAPTオーバヘッドがないため、デフォルトとほぼ変わらないなど、予想どおりの結果だった。 ISUCONのような高負荷環境においても、オプションを適切に選べばDocker化により、パフォーマンスが落ちることはそれほどないということがわかった。

【補足】 docker-proxy について

Nginx(net=bridge) のとき、docker-proxy とかいうプロセスが top でみて CPU 45%消費してた。そりゃ遅いはずだ。

docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip x.x.x.x -container-port 80

docker-proxy は Docker 1.2.0 から導入されたアウトバンドトラフィックを各コンテナへルーティングするものらしい。

The Docker userland proxy that routes outbound traffic to your containers now has its own separate process (one process per connection). This greatly reduces the load on the daemon, which considerably increases stability and efficiency.

Announcing Docker 1.2.0 | Docker Blog

ポートマッピングするユーザランドプロセスがDocker本体と別プロセスで起動されるようになった。1コネクションあたり1プロセスとあるけど、HTTP keepalive 切ってあるベンチマーカーがいても1プロセスしか上がってなかったようなので、どちらかというと one process per port な気がする(要調査)。 Docker 1.2.0 以前ならばもっと遅かったということになる。 そもそも、ポートマッピングってユーザランドでやらないとダメなのか、このあたりはまだよくわかってない。

実装はこの辺。https://github.com/docker/docker/blob/master/daemon/networkdriver/portmapper/proxy.go

その他

この内容は 第5回 コンテナ型仮想化の情報交換会@大阪 - connpass でも発表させていただいた。 @tenforward さんの Linuxコンテナの基本と最新情報 (2014-11-14) // Speaker Deck がLinuxコンテナを理解する上で参考になった。

ちなみに、ISUCONは今年初出場して本戦で負けた。

チームメンバー、ならびに運営の皆様、本当におつかれさまでした。

Go言語の便利情報

Go

ここ1年ぐらい収集した便利 Go 言語情報を並べただけです。

オフィシャル

言語機能解説を中心にピックアップ。

入門系

言語の話

設計・ベストプラクティス系

Vim

Go で書かれたOSS

GitHubのGoプロダクトのスター数降順リスト

その他

CI-as-a-ServiceでGo言語プロジェクトの最新ビルドを継続的に提供する | SOTA

Channels Are Not Enough or Why Pipelining Is Not That Easy

Dockerは速いのか?Dockerのパフォーマンスについて重要なことは何か?

だいぶ前からDocker(Linuxコンテナ)のパフォーマンスについて、速いことは速いだろうけどどの程度速いのか、もし遅いことがあるなら何がパフォーマンスにとって重要なのか(AUFSが遅いとかそういうの)が気になっていたので、今回は

で紹介されていた Docker のパフォーマンス検証に関する IBM の Research Report を読んだ。Report の内容をベースに、Docker のパフォーマンスの勘所などをまとめてみた。 Report のタイトルは An Updated Performance Comparison of Virtual Machines and Linux Containers 。 GitHub にベンチマークコードと実験データが置いてあってちゃんとしてる。

前提

まず、VMとコンテナの歴史を振り返るのに知らぬはエンジニアの恥。今さら聞けない【コンテナ/仮想化技術】11選 - paiza開発日誌 も併せて読んでおくとよさそう。

次に、Etsukata blog: Docker を支える Linux Kernel の機能 (概要編)Dockerを支える技術 が Docker が使ってるカーネル周りの技術についてわかりやすいので、絶対読みたい。

特に、Docker のドライバ周りについて、exec driver、storage driver、Host Networking、Volume について軽く見ておくと良さそう。

概要と考察

Report 自体は、クラウド(IaaS)事業者視点で、コンテナ(Docker), VM(KVM), Native Linux(仮想化してない普通のLinux) の3者の性能を比較してる。 性能評価の観点は、プロセッサ(FLOPS)、メモリ帯域幅、メモリIOPS、ネットワーク帯域幅、ネットワークレイテンシ、ブロックデバイスの帯域幅、ブロックデバイスのIOPSなどとなっている。 LINPACK、 netperf や fio などを用いた OS のベンチマークだけでなく、実用的なアプリケーションとして MySQL と Redis についてもベンチマークされている。 自分の場合は、EC2 インスタンスまたはオンプレの Xen Domain U で Docker コンテナを動かすことになるだろうから、VM と コンテナの比較よりは、Native Linux とコンテナの比較に興味があった。

レポートの3章のグラフをみるとわかるが、結論から言うと、Docker のオーバヘッドをなるべく削減する構成にすれば、各評価において Docker は Native Linux と同等かやや劣る程度で、実用上それほど問題になることはなさそうだ。 CPU、メモリ周りとネットワークとブロックデバイスの帯域幅については同等である。 CPU/メモリ集約なアプリケーションとか動画配信サーバみたいな帯域幅集約なアプリケーションなら気にすることは特に何もない。

逆に、 MySQL と Redis のようなストレージまたはネットワークのIOPSが支配的なアプリケーションではパフォーマンスが劣化する。 原因は AUFS と NAPT で、NAPT のポート変換オーバヘッドと I/O 要求が AUFS の各ファイルシステム層を通過するオーバヘッドがある。 ただし、Docker の Host Networking とか Volume 機能を使って、前述のオーバヘッドをかなりの部分まで削減することができる。(それでも、MySQLについてはNativeと比べて多少性能が落ちる。レイテンシの僅かな差がスループットに影響を与えている。)ただしこれはコンテナのポータビリティとパフォーマンスのトレードオフになる。 例えば、NAPT を使わなくすると、コンテナが公開するポートとホストのポートがコンフリクトすることもあり、Dockerコンテナがどこでも動くというわけではなくなる。また、Volume 機能を使うと、Docker イメージにデータを書き出すことができないため、コンテナをデータごと他所に持って行きづらくなってしまう。

CPU、メモリ周りは通常用途にはそんなに気にするポイントなかったので、ブロックI/O or ネットワークI/O集約なアプリケーション(MySQL とか HAProxy)以外は自分が触ってるような環境の場合本番投入しても問題なさそうな印象を受けた。 MySQL や Redis についてはステートフルなので、そもそもインスタンスの増減や引っ越しがステートレスサーバほど気軽にはできないので、多少人間的なオペレーションが入ってもよい。 つまり、それほどポータビリティが重要なわけではない。実行環境はDockerなのでそのままで、データだけは Volume ディレクトリの内容をホストに書き出して別途コピーしておけばよい。 また、statefull なアプリケーションについては、同じアプリケーションを同じホストで動かさない運用にしておけば、ポート番号はウェルノウンポート決め打ちぐらいでよさそう。

スライド

細かい実験条件などはより詳しい内容についてはスライドまたはReport 本文を参照。

感想

スライドの最後にも書いてるけど、コンテナは隔離されたプロセスぐらいのイメージなので、VMに比べて速いに決まってるし、Native Linuxと遜色ないのもまぁそうかという感じ。AUFS 遅いというのもまぁそうかという感じで、Device Mapper とか Btrfs との比較が気になるところ。AUFS以外の storage driver についてComprehensive Overview of Storage Scalability in Docker | Red Hat Developer Blog が参考になる。(大量のコンテナの作成と削除を以下に速くできるかが指標となっているので、やっていることはだいぶ違う)

関係ないけど、Docker の本番投入の問題はパフォーマンス云々以上に、既存のワークフローにいかに組み込むかと問題が発生したときのデバッグまたは障害対応であるというのがここ半年くらいの認識になっている。

気になる参考文献

今回の Report は参考文献が豊富で、いくつか気になったので紹介しておく。

https://www.gronkulator.com/overhead.htmlhttps://www.gronkulator.com/overhead.html

Software Defined Boden: KVM and Docker LXC Benchmarking with OpenStack

はてなでは、Docker 好きな人とか、論文読んで知見を共有したり本番投入したい人も募集しています↓↓↓

株式会社はてなではインターネットで生活を楽しく豊かにしたいスタッフを募集しています
採用情報 - 株式会社はてな

Docker のクライアント用バイナリをビルドする

Docker はクライアントとサーバで同一のバイナリを使用する。 しかし、Docker の実行バイナリを古い Linux カーネル(CentOS 5系とか)上で実行する と、Segmentation Fault となる。 docker version とかそういう簡単なコマンドでさえ。

せめて古いカーネル上で Docker をクライアントとしてだけ動作させたい。 Docker のクライアントは docker デーモンに対して、HTTP の API 叩いているだけなので、Docker をクライアントとしてだけ動作させたければ、カーネルには依存しないはず。 そこで、DOCKER_CLIENTONLY 環境変数のフラグを立てて docker をビルドするとよい。 Mac 用のバイナリもきっとこうやって作成しているはず。

go get -v github.com/docker/docker && cd $GOPATH/src/github.com/docker/docker
git checkout release
./hack/vendor.sh
GOOS=linux GOARCH=amd64 DOCKER_CLIENTONLY=1 ./hack/make.sh dynbinary

DOCKER_CLIENTONLYについては下記のドキュメントにて発見した。

https://github.com/docker/docker/blob/release/hack/PACKAGERS.md

DOCKER_CLIENTONLY 以外にも DOCKER_BUILDTAGS を使ってグラフドライバの一部をバイナリから除外したりできるっぽい。

今のところ、Docker デーモンが起動しているホストを用意し、さらに全ホストに上記の docker クライアントバイナリを配布するようにしている。

なにがやりたいか

maakit とか percona toolkit など、様々な運用ツールを環境を整える手間なく、すぐ使いたい。もしくは使ってもらいたい。一般的なツールならまだいいけど、社内独自ツールだと環境を整えるだけでとにかくめんどくさい。

Docker デーモンとクライアントは異なるホスト間でAPI通信する前提なので、もちろん使えるツールには制約はある。 とはいえ、ツールを実行して標準出力をみたいとかだけならだいたい事足りる気がしている。 なにか成果物がクライアント側に必要な場合は、Docker コンテナ上での成果物はdocker cpコマンドで手元に持ってこれる。 もしくは Docker image 上の特定のファイルを手元に引っ張りたいだけなら nsenter が参考になる。 jpetazzo/nsenter · GitHub に書かれているインストール方法がとにかくかっこいい。

docker run --rm jpetazzo/nsenter cat /nsenter > /tmp/nsenter && chmod +x /tmp/nsenter

逆に今のところ、Docker クライアントと Docker デーモンが異なるホストにいる場合、Dockerfile の ADD でしか手元のファイルをコンテナに渡せない。(NFSとかファイルサーバを経由するとかがんばりようはある)

Moving a file from the host system to a container · Issue #905 · docker/docker · GitHub

ちょっと関係ないけど、Docker はクライアントとサーバでAPIのバージョンが等しくなければならない。 Docker 自体のバージョンが上がるとAPIのバージョンも上がる(常にAPIバージョンが上がってるかはわからない)ようで、Docker を大規模展開したあとのバージョンアップがちょっとめんどくさそう。