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 を大規模展開したあとのバージョンアップがちょっとめんどくさそう。

tmux + ssh + Mackerel API を組み合わせたとにかくモダンなサーバオペレーション

冗長化させたホストやスケールアウトさせたホストなどの同じサーバ構成をもつホストグループや、あるサービスに所属するホスト全てに同時にsshして同時に操作したいことがある。 複数のホストに同時ログインするツールとして cssh があるけど、毎回複数のホスト名をチマチマ入力したり、すぐに古くなるホスト一覧ファイルを手元に持ちたくない。Immutable Infrastructure 時代にはそぐわない。Immutable Infrastructure 時代にはホスト名なんて毎日変化するし誰も覚えてない。サーバ管理ツール上のグループ名を使ってグループ配下のホストに同時にsshしたい。 あと、cssh は個人的に挙動がなんか微妙なので、代わりに tmux と ssh を組み合わせている。 cssh はマスタとかスレーブとか気持ちはわかるけど、複数ウィンドウ操作は使い慣れたターミナルマルチプレクサを使いたい。

Clusterssh demo - YouTube

もう1台1台丁寧に ssh したり、毎回複数のホスト名をチマチマ指定して cssh する時代は終わった。 (タイトルに Mackerel と書いてるけど、半分以上は Mackerel に依存しない話です。)

デモ

http://i.imgur.com/Bvj9pes.gif

tssh

tmux を使って、複数ホストに同時 ssh ログインして同時にオペレーションできる。

dennishafemann/tmux-cssh · GitHub を使ってもよいし、下記のようなスクリプトを使ってもよい。 挙動はほぼ同じで、tmux を使うと30行足らずで実現できるがすごい。

https://github.com/y-uuki/opstools/blob/master/bin/tssh

#!/bin/bash

hosts=("$@")
session_name="tmux-ssh-$$"

tmux start-server

is_first="true"
for host in ${hosts[@]}; do
    cmd="ssh $SSH_OPTION $USER@$host"
    if [ "${is_first}" == "true" ]; then
        tmux new-session -d -s $session_name "$cmd"

        is_first="false"
    else
        tmux split-window  -t $session_name "$cmd"
        tmux select-layout -t $session_name tiled 1>/dev/null
    fi
done

tmux set-window-option -t $session_name synchronize-panes on
tmux select-pane -t 0
tmux attach-session -t $session_name

Usage

$ tssh blogapp001.domain blogapp002.domain blogapp003.domain ...
$ tssh blogapp00{1,2,3}.domain # ditto

このあと、だいたい top 叩くか ログファイルを tail したりする。

おまけとして、特定のホストのみを操作したいときは tmux の synchronize-panes と ペイン移動を使う。 ~/.tmux.conf に下記の設定を書いておいて、 + g でペインの同期をオフにして、操作したいホストに割り当てられたペインに移動して操作するとよさそう。

bind-key g setw synchronize-panes

tssh だけでも十分便利だが、Mackerel の RESTful API を使うことで、ホスト名または IP アドレスに依存しないオペレーションができる。、

Mackerel

Mackerel: A Revolutionary New Kind of Application Performance Management

サーバ管理ツール Mackerel では、ホストをロールで管理する。 具体的には、サービス名とロール名の組で、Saba-Blog::proxy や Saba-Blog::app、Saba-Blog::db みたいな感じ。

Mackerel はモニタリングだけでなく、RESTful API を使ったサーバ管理の自動化も促進しようとしている。

API仕様(v0) - Mackerel ヘルプ

今回は、ロールに所属するホスト一覧が取りたいので下記のような curl と jq を組み合わせたワンライナーを使う。 これくらいの用途なら API クライアントを使うまでもない。

$ curl -s -H "X-Api-Key:<apikey>" -X GET 'https://mackerel.io/api/v0/hosts.js?service=Saba-Blog&role=app' | jq -a -M -r ".hosts[].name
blogapp001.domain
blogapp002.domain
blogapp003.domain

毎回ワンライナーを叩くのもだるいのでスクリプトにする。~/bin/role に以下のようなスクリプトを置いてる。

#!/bin/bash

if [[ $1 == '' ]]; then
  echo "requried service name"
  exit 1
fi
if [[ $2 == '' ]]; then
  echo "requried role name"
  exit 1
fi

curl -s -H "X-Api-Key:<apikey>" -X GET "https://mackerel.io/api/v0/hosts.json?service=$1&role=$2" | jq -a -M -r ".hosts[].name

上記 role コマンドにサービス名とロール名を指定してやれば簡単にホスト一覧を取得できる。

$ role Saba-Blog app
blogapp001.domain
blogapp002.domain
blogapp003.domain

role コマンドの他に service コマンドも置いてる。さっきとほとんど同じ。

#!/bin/bash

if [[ $1 == '' ]]; then
  echo "requried service name"
  exit 1
fi
curl -s -H "X-Api-Key:<apikey>" -X GET 'https://mackerel.io/api/v0/hosts.js?service=$1' | jq -a -M -r ".hosts[].name
service Saba-Blog
blogproxy001.domain
blogproxy002.domain
blogproxy003.domain
blogapp001.domain
blogapp002.domain
blogapp003.domain
...

tssh + Mackerel

$ tssh `role Saba-Blog app`

これでなんでもできる。 Zabbix や Datadog など他のサーバ管理ツール/サービスを使っても、もちろん同じことはできるだろうけど、Mackerel はホストのグルーピングの指針が定まっているので、グルーピング方針に迷うことが少ないと思う。

社内でこの手の連携は昔からやってて、とにかく便利で毎日使ってる。

↓ とにかくモダンなサーバ管理、とにかくモダンなサーバ管理ツールを作ることに興味がある人ははてなで一緒に働きましょう!!!!

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

【悲報 5年後はおっさん】25歳になった

今日で25歳になった。 ここ数年Facbeookとかいうやつのせいで誕生日の印象が悪くなってるのはさておき、この1年はずっとサバッとか言ってた。 修了に失敗して社会人になったはずが、あまり生活に変化はない。 技術には集中できるようになった。 今の仕事はわりと楽しい。 こわいこわいとかいいながらスイッチオーバしたり、イミュータブルなんとかのゴミ掃除とかしたり、Chefに順番どおり実行してほしいとか祈願してたり、スラッシングしてるサーバみてシバ感あるとか言ってた。 5年後は30歳とか思うと未来がない。 25歳、もっとちゃんとしてると思ったが実体はこんなものだ。 とにかくおっさんになりたくない。 せめておっさん感のないおっさんになりたい。

ウィッシュリスト

追記

おっさんと年齢に関する話、下書きのまま放置されてた。

おっさんになりたくない話

最近、おっさんについての関心が高まっている。

まず、motemen さんが書いたおじさんパターン集っていう名エントリを引用する。

  • その面白そうな話、私も参加していいよね?なぜなら私は無条件に受け入れられているからおじさん(闖入おじさん) #おじさんパターン
  • 後出し難癖おじさん #おじさんパターン
  • 困難は成長のチャンス!だから君たちに成長の機会をあげようおじさん (成長おじさん) #おじさんパターン
  • あらゆる事案に一般論コメントおじさん #おじさんパターン
  • 俺ってあらゆることに精通してるじゃん?だから力になるよおじさん (精通おじさん) #おじさんパターン

他にも、

  • 古いものに固執して新しいものを根拠なく否定したがるおっさん。
  • 酒の場で酒を強要してくるおっさん。
  • 社会では通用しないとか言ってくるおっさん。

とかいろいろな最悪のおっさんがいる。 (ここでいうおっさんというのは、年齢的に30歳男性とか40歳男性とかそういうのではなくて、精神のあり方とか人の接し方がおっさんであるというぐらいの意味。)

そういうおっさんがいるのは怖くなくて、本当に怖いのは、自分がおっさんになってないかどうか。 そこで、おっさんパターンが役に立つわけだけど、どうすればおっさんにならないですむか考え続けてることになって後ろ向きでよくないし、おっさんパターンにあてはまらなかったからといってそれがよいかどうかはわからない。 オブジェクト指向のデザインパターンみたいなこれはよいパターンみたいなのがあるとよいと思っていて、ロールモデルとして尊敬できる人の真似をするのがきっと効果的。

なんでもいいからとにかくおっさんになりたくない。

Facebookの数千台規模のmemcached運用について

Linuxのブロックデバイスレベルで実現するrsyncより高速な差分バックアップについて - ゆううきブログの続きとして、Facebook の memcached 運用に関する論文を読んだ。 タイトルなどは以下の通り。 NSDI はネットワークシステムに関する(多分)準トップレベルのカンファレンス。

  • Scaling Memcache at Facebook

Facebook のキャッシュシステムがどのようにスケールしてきたか、各スケール規模の勘所は何かについて書かれた論文だった。 内容はかなり盛りだくさんで、基本的なデータベースクエリキャッシュ戦略から、マルチリージョン分散の話まで多岐に渡る。 memcached に依存しない話も多いので、memcached というよりは、超大規模キャッシュシステムの運用例として読むのがよさそう。

論文概要

memcached はよく知られたシンプルなインメモリキャッシュシステムである。 論文では、memcached を基本単位として、世界最大のソーシャル・ネットワークを支える分散KVSをどのように構築し、スケールさせたかについて書かれている。 Facebook のキャッシュシステムは、秒間数十億リクエストを捌いていて、10億人を超えるユーザにリッチな体験を届けるために、数兆個のアイテムをキャッシュに保持している。

システムは、以下の4段階でスケールしてきた。 - 1. 数台のmemcachedサーバ - 2. 多数のmemcachedサーバを含むシングルmemcachedクラスタ - 3. マルチmemcachedクラスタ - 4. マルチリージョン

1. 数台のmemcachedサーバ

  • demand-filled look-aside cache 方式
  • 並列 memcache set 問題 (stale sets)
  • Thundering Herd 問題
  • memcache プロトコルの拡張 "leases"

2. シングルクラスタ

  • consistent-hashing
  • TCP incast congestion 問題
  • Layer 7でスライディングウィンドウでフロー制御
  • get リクエストは UDP にしてパケット減らす
  • 独自ルータを挟んでコネクション永続化

3. マルチクラスタ

  • フロントエンドクラスタ + ストレージクラスタ
  • 一貫性の保持
  • 全クラスタのキャッシュ無効化処理
  • SQL文に無効化キーを埋め込んでおきて、MySQLのコミットログをtailして埋め込みキーを無効化するデーモン

4. マルチリージョン

  • (フロントエンドクラスタ + ストレージクラスタ) x リージョン
  • リージョン間レプリ遅延を考慮して、キャッシュミス時にマスタDBをreadするかスレーブDBをreadするかマーカーをつける

Conclusion

  • キャッシュと永続ストレージを分離して、独立してスケールさせる
  • モニタリング、デバッギング、オペレーション効率を改善する機能はパフォーマンスと同じくらい重要
  • ロジックは stateless なクライアントに置くほうが混乱しない
  • システムは新機能の段階的なロールアウトとロールバックをサポートしなければならない
  • Simplicity is vital.

スライド

より具体的な内容はスライド参照。 相当はしょっているので、詳細な内容は論文参照。 はしょった内容については、後日ブログにするかも。

関連記事

感想

TCP incast問題を回避するために、TCPのスライディングウィンドウ的なロジックをアプリレイヤで実装していたりして、OSレイヤでの解決方法を大規模運用にあてはめてスケールさせているのが印象的だった。 仕事でそのロジックを実装するリソースを割けない、もしくはそもそもそんなスケール必要がないとかはあるにしても、話としては結構おもしろかった。 あと、memcached 自体には機能を追加せずに、クライアントサイドにロジックを入れていたのも気になるところだった。 Facebook はストレージにHBaseを使っている話 (The Underlying Technology of Messages)もあって、こっちはミドルウェアにロジックをもたせてたりするので、このへんの方針の違いも気になる。

関連記事にも載せてるけど、 https://www.facebook.com/publications で Facebook の出してる論文とか読めるので、いろいろ勉強になりそう。

ヘビーなGraphite運用について

"Monitoring Casual Talks #6"に参加して、「ヘビーなGraphite運用」についてしゃべってきた。 Graphite ここ数ヶ月ずっと運用してて、そこそこのリクエスト数さばけるようになるまでと冗長化の話をだいたいしてた。 Graphiteのパフォーマンス・チューニングで結構苦労してて、@sonots さんが発表されてた"GrowthForecast/RRDtool チューニング秘伝の書" がすごい参考になった。(Graphite(whisper) のデータ構造と RRdtool のデータ構造はよく似ている。) fadvise(2)とか知らなかった。 ぜひ試してみたい。

スライド

結構口頭で補足してた。Graphite 秘伝の書書きたい。

感想

今回の勉強会、かなり刺さる内容が揃っててよかった。 形式が30~100人くらいいて、数人の発表者がプレゼンするっていう感じじゃなくて、14人くらいで全員発表とかいう形式だったので、カジュアルに議論とかできてよかった。 あと、結構 Mackerel について言及していただいて関係者としては緊張感あった。 Nagios とか Zabbix の話は意外と少なめで Sensu とかあと、Docker のメトリクス収集とか、Auto Scale時のモニタリング方法とかの議論があって、みんなモダンなことにチャレンジにしててすごい。 また参加したい。

関係ないけど、今週 Docker 1.0 がリリースされて、Dockerそのもの + 周辺ツールが充実してきた感があるので、今回と同じような形式の Docker Casual とかあったらよさそう。

会場の提供/準備をしていただいた @takus さん、@sonotsさん ありがとうございました。