試行錯誤のおと

日々の試行錯誤した結果です。失敗することが多い記録、それだけでっす!

ISUCON6 にインフラエンジニアとして参加して優勝した!!

f:id:kizkoh:20161024092622j:plainISUCON6 にインフラエンジニアとして参加して優勝してきたので、なにやったかとか感想とか書いてく。 (僕はアプリを触っていない。アプリの話は他の人が書いてくれるはず。)

※ @methane さんが会社ブログに投稿されたので記事を更新

dsas.blog.klab.org

会社で出場してチーム名は「この技術部には問題がある!」、メンバは @methane @mecha_g3 さんと僕。 ちなみに @methane さんと @mecha_g3 さんは兄弟だけど、僕だけ他人です(笑)。

チームメンバ役割

本戦に向けて、会社でチーム練習してたので本戦の役割もできてた。

@methane さんが全体の構成を見てチーム全体をまとめる役割、 @mecha_g3 さんがアプリ、 僕がインフラの役割。@methane さんが戦略をたてて、各自自分で改善できるところは改善してく感じで進めることができた。なので、自分は結局アプリのコードを読まなかったけど、やるべきことはできたのでチーム構成最高だった!!

当日の朝

自分は朝に余裕を持ちたいので、当日は 6 時起床。前日は特になにもせずに寝ることだけに集中してた。

チームでは一番に到着したので、席確保して @methane さん、 @mecha_g3 さんがくるまで twitter 見ながらだらだら過ごす。落ち着きが大事。

競技開始

他のメンバにレギュレーションや構成を見てもらっている間にコードや公開鍵をデプロイしたり、オペレーションに必要なツールやリソースモニタリングに必要なパッケージを流し込んでいったりする。 今回は他のサーバ上からオペレーションできるよう踏台サーバとして Azure 外にサーバを置いていて、アプリ担当のメンバが本戦サーバにログインせずともアプリをデプロイしたりログを収集したりできるスクリプトを整備していた。 さらにその準備をしている片手間で初期状態のベンチをかける。

3500 点くらい。

ここでスタートの準備が整ったので戦略会議をする。

フロントは https で nodejs(react) のアプリケーションが待ち構えていて、バックエンドにアプリ、その後ろに MySQL がある構成だった。 nginx を(ちゃんとした http サーバを立てたくて)使いたいので、フロントを nginx で受けて nodejs とアプリに nginx 分けようという戦略になって、これが最終提出時の構成にもなった。 初期実装は php で動いてて、プロセスに apache がいてこれを切り替えようとして systemctl list-units するも apache2.service がなくて焦るが、ps で見ると -DFOREGROUND で動いていたので、 あぁ docker で動いているんだなと思って docker ps したら起動中のコンテナ一覧が表示されてホッとした(レギュレーションのスライド読んでなかった)。 起動中のコンテナ一覧には node や MySQL があって、各サービスも docker-compose の docker で動いていた。 これらは Linux ネットワークスタックの処理をいくつか通過するためネットワークパフォーマンスが悪くなるし、何よりも問題が発生したときに追うのが面倒という理由で外だししていくことにした。 @methane さんに nodejs と MySQL のインストールを手伝ってもらいながら、 docker exec しつつ中身を一通り調査しながら systemd のサービスを作成していった。

すべてのコンテナを潰し、 nginx で 443 を listen するようにして、ベンチかけたらアクセスログHTTP/2.0 が記録されてたので予想通り!!

9500 点くらい。

ここで 12 時半くらいだったと思う。

/api へのアクセスはアプリだったのでアプリに飛ばしたり、静的ファイルの配信を nginx で gzip_static を使って配信するようにしたり、 /api/streamproxy_buffering を off にして 14 時くらい。

16000 点くらい。

たぶん、ここまでアプリのコードは触られていなかったんじゃないかと思う(うろ覚え)。 だいぶ落ち着いてきたのでアクセスログの集計スクリプトを整備していく。

@mecha_g3 さんがデータをオンメモリ化されたり、 @methane さんに img/SVG を go で返すようにしてもらったりして、設定変更した nginx のリバプロ先を tcpdump で念のため確認してた。

39000 点くらい。

余裕もできて、16 時になったので複数台に展開したりするための準備を進めていく。 sysctl.conf や別のインスタンスMySQL を用意したり、 node を動かす準備をしていく。

17 時になって最終構成。

        +  +------------------------------------+-------------------+-------------------+
        |  |                        listen 8081 |       listen 8081 |        isten 8081 |
        |  |                                    |                   |                   |
+isu01----------+   +isu02+---------+   +isu03+---------+   +isu04+---------+   +isu05+---------+
|       |  |    |   |               |   |       |       |   |       |       |   |       |       |
|  +----v--+-+  |   |  +---------+  |   |  +----v----+  |   |  +----v----+  |   |  +----v----+  |
|  |  nginx  |  | +---->  mysql  |  |   |  | nodejs  |  |   |  | nodejs  |  |   |  | nodejs  |  |
|  +----+----+  | | |  +---------+  |   |  +----+----+  |   |  +----+----+  |   |  +----+----+  |
|       |       | | |               |   |       |       |   |       |       |   |       |       |
|  +----v----+  | | +---------------+   +---------------+   +---------------+   +---------------+
|  |   app   +----+                             |                   |                   |
|  +----^----+  |                               |                   |                   |
|       |       |                               |                   |                   |
+---------------+                               |                   |                   |
        |listen 8080                            |                   |                   |
        +---------------------------------------+-------------------+-------------------+

ここからのスコアはこんな様子。

f:id:kizkoh:20161024042237p:plain

isu03, isu04, isu05 で nodejs を動かしているんだけど、これを 2 プロセスにするとなぜかスコアが大幅に落ちて 3万点 ぐらいになってしまって、ボトルネックの調査に行き詰まる。 結局、ボトルネックが不明だったので、 1 プロセスに戻した状態で最高記録を叩き出した。 その後、インスタンスの再起動試験をやっていくつかベンチをかけてみたけれど、 8 万点を越えることができなかったのが不完全燃焼。 なぜ、スコアが下がってしまったのか今でも分からない(停止 -> 起動ではなくて、再起動してたんだけど Azure のインスタンスガチャ?)。

蓋を開ければ優勝できてたんだけど、最高点数で表彰されなかったのが悔しかった。

最後にボトルネックになっていたのはネットワークだったんだろうなーと今は思ってる。 けれども、これを早々と見つけていても構成を 17 時の時点で構成を変えるのは難しかっただろうなとも思っていて、早くも来年の課題が見えてきて来年が楽しみになってる。

技術的トピック

直接スコアにコミットしたのは docker を潰すところと nginx の設定をガリガリやったところ。

docker はいつか来るだろうなと思っていたので、割とやることは自明で気持ちよく潰せたので楽しかった。 docker-compose は触ったことがなかったけど(今だと swarm でやることになるのかな?)、 docker-compose がどんな役割かは知っていたので、勘で設定ファイルを systemd 化していくことはできた。

http2 も来るだろうと思っていて let's encrypt の準備をしていた。 ドメインが割り当てられることはやっぱりなかったけれど、 nginx.conf の雛形を放り込んだら http2 をちゃんと喋ってくれたので、よかった。

あとはオペレーションのはなし。

今回はサーバが 5 台用意されていて、 2 年前に出場したときよりも 2 台増えていてオペレーション力が求められた。 けれども、基本的にサーバが何台増えようともできることは同一にしておく必要があって、以下のようにホストやプライベート IP のリストをつくっておいて利用するのが簡単でハマりどころも少なくていい。

ISUIPS="10.6.1.5 10.6.1.6 10.6.1.7 10.6.1.8"
for i in ${ISUIPS[@]}; do echo $i; ssh $i 'hostname'; done
for i in ${ISUIPS[@]}; do echo $i; rsync -n -avRiK /etc/systemd/system/react.service $i:/; done
for i in ${ISUIPS[@]}; do echo $i; rsync -avRiK /etc/systemd/system/react.service $i:/; done

echo -n ${ISUIPS[@]} | xargs -d' ' -P${#ISUIPS[@]} -I {} ssh -qt {} 'sudo journalctl -f -u isu-go'
echo -n ${ISUIPS[@]} | xargs -d' ' -P${#ISUIPS[@]} -I {} ssh -qt {} 'sudo tail -F /var/log/nginx/access.log'

for i in ${IP のリスト} みたいな書き方は普段のオペレーションでも使っていて練習ではなくて業務のノウハウ的なもの。 ホスト名の扱いに慣れていれば fabric や pdsh みたいなものを使わなくても ssh や xargs といった標準のツールで十分オペレーションできるし戦える。 こんなのを毎回打つ人として本戦ではインフラエンジニアまたはオペレーションエンジニアが必要なのかなあと思う(ミスるとクリティカルだし)。

感想

初めて出場した ISUCON は ISUCON4 だった。入社 1 年目で、そのときも @methane さんと出場して 5 位。 昨年はアプリ担当で出て go で出場するも、少しスコアを上げただけで予選敗退した。 このときはインフラ担当がいなくて、アプリとインフラを自分だけでやってたので本当つらかった思いがある。

そして、今回。予選はいつもなら各自で戦うんだけど今回は予選からチームで戦って役割を分担した。 予選は nginx.conf の設定をするだけであまり活躍する場はなかったけれど、本戦はインフラエンジニアがかなり活躍できて本当に楽しかった。 一方、途中でオペミスして、ファイルをふっ飛ばしてしまうこともあり @methane さんに ansible をトレースして助けていただいたり、構成の変更を練ってもらったり本当に感謝。

記事だとめっちゃ綺麗にうまくいったようにまとめているけど、実際は準備してた道具がほとんど封印された状態。 mysql の sys スキーマや nginx の webdav, image-filter, mruby, グラフィカルなリソースモニタリングの準備していたけど予想を上回る内容の問題で ISUCON って感じ! HTML5 の SSE や react など聞いたことはあるけど触れたことのない技術が問題として出題されて、どんな技術にもより貪欲でありたいと思わされた。 僕にとって ISUCON は自分自身のベンチマークだと思っていて自分がどれだけ成長できているかを実感できる素晴しい機会なので、ともあれ来年も今後もずっと挑戦していきたい。

最後に運営の皆様、参加者の皆様、会社の皆様、お忙しい中サポート & 応援ありがとうございました。

https://bit.ly/isucon6 からギフトできるようですので是非!!

Hatena Engineer Seminar #6 〜インフラ編〜 @ Tokyo に参加してきました

(最初文で書いてたけど、読みづらかったのでメモに置き換えた〜)

個人的には勉強会に参加したらやっぱり、記事を書くべきだと思ってる。 参加したくても様々な事情で参加できなかった人のためになるし、感想を書くと発表された側、発表を聞いた側のモチベーションにもなると考えてる!

内容と感想

hatena.connpass.com

LT も面白かったけど、メモなしで聞く方を重視したので記事で触れるのは通常のトーク 3 つ。

id: wtatsuru さん: はてなのログ運用のこれまでとこれから

  • 前提となるシステム構成
    Reverse Proxy -> Application Server -> DB
  • ログの種類
    アクセスログ、エラーログ: Reverse Proxy が書き出し
    アプリケーションログ: Application Server が書き出し
    ほとんどのサービスで同じような構成をとっているのでログの処理も同じ
    • アクセスログの運用・分析
      フォーマットは LTSV
      ログの保存は rsyslog でリモートに転送
      ローカルから s3 にも保存して二重化していて、しばらく日が経過したら glacier に保存している
      SSD 上のサーバにも転送して、 SSD 上のサーバで分析、レスポンスタイム、レイテンシを計算
      発展して EMR 上で分析、 EMR はクラスタの初期化が遅いので今は google big query を検証中(embulk を挟む)
    • アプリケーションログの運用
      構造化ログ(json) の出力、すべてのサービスで特定の場所に出力
      fluentd で中央の aggregator に出力、集約の精度はベストエフォート、完全に保証しない
      他に s3 に保存、mackerel に転送、 elasticache に転送
  • 今後
    syslog からの脱却、開発者以外も活用できる仕組み、行動ログ基盤

rsyslog を廃止する課題を挙げられていたのだけれど、弊社では journald を使ったログ転送の仕組みを試行錯誤していて、この課題の解決には journald のログ転送あるいは自作のログ転送ツールを利用されたりするのかなあと思った。 また、行動ログを取りたいという話をされていて、懇親会で話していると行動ログを取るんだったら時系列 DB を利用しないといけないねという話になった。 時系列 DB は http://druid.io/ というものがでてきていて、いい感じ(?)らしい。

id: hagihala さん: はてなのサーバプロビジョニングの話(仮)

  • プロビジョニング
    chef, AMI の運用について
    今回の話は Configuration, Bootstrapping、 Orchestration の話はない
    サーバ環境は最近専用サーバ(さくらさん)、他に DC, Amazon VPC
    物理サーバのプロビジョニングは物理層はさくらさん、PXEブート、 Xen domU 上の仮想サーバは EC2 と同じ扱い
    ツールは chef、 chef 昔、本よんだくらいで全然わからないのでなかなかついていけない
    はてな内部でも動きが抽象的だったりして chef の評判はよくないらしい
    問題はたくさんある、テストが回せていない、コードを外部に出せていない
    AMI は debian のものを元にすべての元になる AMI を作成している、使っている技術は open-vz, packer

あと Chef を使っていて、問題はたくさんあってテストが回せていない、コードを外部に使えないところなど、プロビジョニングツールあるあるみたいな知見が共有されていてよかった。 懇親会で若手インフラのメンバと話していたのだけれど、プロビジョニングツールを使ってセットアップの設定を書いていると、テストがそれと被るようになってテストを書くモチベーションが下がるというものあるあるらしい。

プロビジョニングするには何か理由があってされているはずなので、プロビジョニングを意識するに至った理由やツールを選択された理由があれば聞きたかった。 最近、実戦的に Ansible を使って一部プロビジョニングをしているのだけれど、仕事で Ansible を導入したのには下記に書くような理由があって、 Chef を導入された理由があるようなら知りたかったなあと思うなど。

  • Ansible を採用した理由
    • 動作に必要な Python はどのディストリにも大体システム標準で入っている
    • Python で実装されているのでいざとなれば読めばいいし読みやすい
    • べき等性が保証されたコードを書くことができる

id: hagihala さん: MySQL運用とらぶるすとーり〜3

  • テーブル行数制限のはなし
    The table 'relword' is full のエラーログ
    不要なデータを削除して延命
    DB は Mysql-4.0.25(?) (Senna, MyISAM)
    --with-big-tables が configure に見当たらない
    テーブルの最大行数制限に引っかかる
    MySQL 4.0 -> 5.1 へのアップデート
    はまりどころ共有(Timestamp とか)
    4.0 -> 5.0 -> 5.1 のレプリケーション
    参照系のみ 5.1 をみるようにして互換を参照
  • binlog 破損から救いを求めた話
    Master の Disk full
    古い binlog を削除するとレプリケーションエラーが発生
    mysqlbinlog コマンドで binlog を確認しようとすると binlog が開けない
    各 slave の binlog のポジションが同じ
    master の binlog が壊れて、壊れた binlog を読もうとして失敗していた
    binlog のバイナリを hexdump で読む..バイナリを見ても壊れている
    backup のバイナリログポジションを壊れているポジションを飛ばして修正
    バイナリログは追記形式なので途中で壊れたとしても後続は壊れない

普段、 RDS しか運用していなくてレプリケーション遅延やログバッファサイズやその他パラメタのパフォーマンスチューニングしか MySQL を調査したことがなかったのでこの手の話は新鮮でとても面白く聞けた。

イベントを開催してくださった皆様、発表者の皆様、ありがとうございました!!

Kyoto.なんか #2 で Rust の実践的な話について発表してきました

タイムラインをみていると流れてきたので、なんか会社休みだし、なんか実家にいるので Kyoto.なんか #2 で インフラエンジニアのための Rust というタイトルで発表してきた。

atnd.org

今回、発表で話しきれなかったことやうまくいかなかったことを、ブログ記事に発表内容をまとめた。

※ 発表資料はあとでリンク貼ります。

モチベーション

趣味で Rust を書いていて、 Rust に対するモチベーションをあげたかった話。

Rust というと一般にシステムプログラミング言語で、変数や式の所有権を付与することで安全性や高速性が謳われている。 Hacker News や Reddit を眺めているとほぼ毎日のように Rust の話があって、みんな OS や DNS サーバを実装したり、知見を共有し合っていて楽しそうだなあと思っていた。 趣味のプロダクトだけではなくて、エンタープライズでも一部のコンポーネントとして使っている事例が CloudFlare や Dropbox からもあがっていて、すごくホットな雰囲気も伝わってくる。 一方で国内の Rust 事情はというと、あまり情報が出回っていなくて、そもそも Rust の実践的な話を聞かないなあと感じていたのが今回の話につながっている。

最近、業務で苦労していることを絡めて Rust で netmap を使ったネットワークパケットの作成、解析として将来を見据えた話題になればなあと思って話をしてきた。

実践的な Rust の使い方を目指して

最近、 AWS を使って運用している案件で高トラフィックな要件の案件があって、負荷試験なんかやるとボトルネックが掴みにくくなっていて困っている。 大抵は EC2 インスタンスのリソースが限界になることが多くて、インスタンスタイプごとの性能がこういった形で共有されているのだけど、帯域はともかく m3.large なんかだと PPS は限界に到達してしまう事案が発生している。

qiita.com

問題に対して、今のところは数の暴力で殴って解決していて落ち着いている。 けれども、それではコストがかさむし、AWS だからといって無限にスケールアウトできるわけでもない。 さらに、パケットの処理能力がボトルネックとなっているほどネットワークが高負荷な場合、パケットキャプチャしようとすればパケットを取りこぼす可能性がある。 パケットキャプチャで取りこぼすようだと、何かネットワークまわりで問題が発生したときに調査がしづらくなるため見過ごせない。

そうした課題は以前から社内でも懸念に上がっていて、 netmap や Intel DPDK といった技術は注目していて情報は追っていた。 Rust で netmap や Intel DPDK を利用して何かできれば実践的な話になるんじゃないかなあということで、Rust から netmap を使ってみた。

Rust で packet を作成 & 解析する

Rust には libpnet という Crate (Rust のライブラリ) があって、汎用的なプロトコルスタックを持っていてパケットを作成 & 解析するのに使える。

github.com

標準では扱うレイヤによって OS のネットワークスタックを使うようなんだけど、その気になれば自前でネットワークスタックを libpnet 上で作ることもできる。 リポジトリexamples にはパケットキャプチャのサンプルプログラムが含まれていて、 lo を指定してパケットキャプチャしてみると以下のような結果を得ることができる。

./target/debug/pnet-study-general lo
[lo]: TCP Packet: 127.0.0.1:52472 > 127.0.0.1:6600; length: 39
[lo]: TCP Packet: 127.0.0.1:6600 > 127.0.0.1:52472; length: 269
[lo]: TCP Packet: 127.0.0.1:52472 > 127.0.0.1:6600; length: 32
[lo]: TCP Packet: 127.0.0.1:52472 > 127.0.0.1:6600; length: 44

コードも 200 行程度しかなくて、簡潔な記述でかけることがわかると思う。 パケットキャプチャと逆の要領でパケットを作成することもできて、各レイヤの処理に精通していなくても必要なヘッダやパラメタが分かるところが嬉しい。

さらに libpnet では様々なデータリンク層インターフェイスがサポートされていて、 以下のリンクから見れる。

https://github.com/libpnet/libpnet/tree/master/src/datalink

libpnet がサポートするデータリンク層インターフェイスには中には netmap も含まれていて、今回はこれを利用した。

試してみたのは ICMP echo-request と ICMP echo-reply の簡単な例。 netmap の環境を用意するには netmap をサポートしたデバイスを持っていなかったため、docker を利用して veth インターフェイスを作成し環境を切り分けて、 veth 上で netmap を試してみた*1。 ICMP echo-request を送信して、受け取ったパケットを解析して標準出力に出力した後に ICMP echo-reply を返し、返したパケットも解析して標準出力に出力している。

root@6485c836a3ff:/# /netmap-ping eth0 172.18.0.1
64 bytes from 172.18.0.1: icmp_seq=0 ttl=64 time=
64 bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=
64 bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=
debug # ./pnet-study-pong vethf90b4d8
[vethf90b4d8]: ICMP echo request 172.17.0.2 -> 172.18.0.1 (seq=0, id=70)
[vethf90b4d8]: ICMP echo reply 172.18.0.1 -> 172.17.0.2 (seq=0, id=70)
[vethf90b4d8]: ICMP echo request 172.17.0.2 -> 172.18.0.1 (seq=1, id=70)
[vethf90b4d8]: ICMP echo reply 172.18.0.1 -> 172.17.0.2 (seq=1, id=70)
[vethf90b4d8]: ICMP echo request 172.17.0.2 -> 172.18.0.1 (seq=2, id=70)

OS のネットワークスタックを使うと、 ICMP のヘッダとデータ部分を作成するだけでいいんだろうけれど、 netmap を使うと当然ながら OS のネットワークスタックを使えない。 そのため、今のところパケットを全部自前で作成しないといけないところが少ししんどいところ。

ちなみに、今回利用した、コードは以下のリンクから

netmap-ping の方は需要ありそうだと思っていて、これからも機能を加えていきたいなぁと思っているので、アップデートしていきたい。

まとめ

発表初めに会場で Rust を書いたことがある人を聞いたら結構、多くて驚いた。 # たぶん、若手エンジニアが多かったからだろうな〜。若手エンジニアが多いからこそ、 Rust について話そうと思ったんだけども。

Rust を使ったモチベーションとして、インフラエンジニア向けな話だったと思うのでそこは刺さりにくいのよなあと思ったので少し反省。 けれども id:y_uuki さんや id:moznion くんには刺さったようで、発表後や懇親会ではずっと喋っていてて、一定手応えはあったと感じることができたので良かった。

コードを書いていて、思ったのが libpnet のプロトコルスタックを使うと簡単にパケットを作成できるので libpnet は優秀だなあと感じた。 使っていて思ったのはパケットを作成するというよりはどちらかというと、受け取ったパケットを一部書換えてキャプチャしたり、転送するのに向いているなと思ったので、ロードバランサやホワイトボックススイッチとかの実装に向いているんじゃないかなあと思った。 そういったこれからも発展していくものに関わっているところで Rust は将来明るい技術だと思うし、学ぶモチベーションが上がるし、プロダクション投入に向けて高めていきたい。

最後になりましたが、こうした発表の機会を設けてくださり、ありがとうございました!! > id:hakobe932, id:hitode909

*1:発表した時点ではうまく疎通できなかったんだけど、 veth だから実際は一つのインタフェイスしかないわけで..普通に考えてコンテナの内側も外側も netmap じゃないとダメだよねといオチだった