2016年サークルのサーバの障害まとめ

新年あけましておめでとうございます. 新年早々,障害の記事なんてって感じですが年越し前に書くつもりがこんな時期になっていました… 障害があったことは年超えても忘れずに今後にいかしていきましょうということで…

さて,本日の話題は去年にサークルで発生した大きめの障害(単純な再起動等では復旧しなかったもの)のまとめです. 2つとも人為障害でした…

事例1:パーミッションの設定忘れによる一部ファイル閲覧不可

下記が対応ログより抜粋

   nginxログ確認
    →下記出力を確認
> 2016/10/20 21:48:08 [crit] 35340#0: *6 open() "/var/lib/nginx/tmp/proxy/2/00/0000000002" failed (13: Permission denied) while reading upstream, client: 133.82.251.196, server: _, request: "GET /only/static/file_management_rule.pdf HTTP/1.1", upstream: "http://127.0.0.1:8082/only/static/file_management_rule.pdf", host: "densanken.com", referrer: "http://densanken.com/only/"
    
   /var/lib/nginx/配下の権限確認
    →nginxユーザが所有者であることを確認
> [root@main01 /]# ll /var/lib/nginx/
> 合計 0
> drwx------ 7 nginx nginx 73 10月 16 19:37 tmp

   /var/lib/nginx配下の所有者変更実施
    →正常に変更できたことを確認

nginxの実行ユーザをwebadminにしたのですが,"/var/lib/nginx/“の権限を変え忘れて死んでたという感じです. 対応ログではさくっとnginxのエラーログ見て対応してみたいに書いていますが, 最初はアプリケーション側の障害を疑ってました. 「推測するな.計測せよ.」,通信経路順におっていけっていう教訓です.

事例2: SSL化にともなってhttpとURLが記載されていた場所が死ぬ.

Webアプリケーションにhttpと書かれていた部分によって引き起こされました. 下手にhttp→httpsにリダイレクトしていたため,なぜか更新ができないという 一見,SSL証明書と関係ないバグのような感じで手間取りました.

最終更新日を確認するとSSL化した日付以降更新がないのでもしやと思ったらビンゴでした.

まとめ

今回の障害はサーバの設定を行う時に確認すべき項目が整備されてなかったことによるものでした. 面倒ですが,

  1. 設定変更したときに意図しない動作をするようにしていないか確認手順を設ける
  2. 機械的にテストできるものはテストする

ようにしましょう(といいつつしてないので今度メインで管理している後輩たちに投げたい)

Ansible+Dockerでシンプルにサークルのサーバを管理しはじめた話

どうも千葉大電子計算機研究会(以下CCS)、老害㌠いかろちゃんです。 CCS Advent Calendar 2016 の6日目の記事として 老害なので現役生を差し置いてサーバ管理をしちゃってる話を今日は書こうと思います。

サークルでサーバ管理をする上での問題点

大学サークルの特徴(少なくともCCSは)として下記があります。

  1. 人間の流動が激しい(役職は一年交代)
  2. 様々なWebアプリケーションがデプロイされる可能性がある
  3. 予算がその年のサークルの人数によって決まる
  4. 仕事でしているわけではないのでかけられる時間が限られる

したがって

  1. サーバを構築した人が連絡がつかず誰も構成を把握・対応できなくなる
  2. 環境がコンフリクトしてしまい最悪各種サービスが提供できなくなる
  3. いつIDCの変更を余儀なくされるかわからない
  4. 時間的制約で後回しにされがち

といった問題があります。

これらの問題を解決するために構成をシンプルにしてAnsible+Dockerでコード化をすすめてみたよというのが今日のお話。 今後CCSのWeb管になる人、他のサークル等で同様の悩みを抱えている人の参考になれば幸いです。

技術選択

クラウド

会室にサーバ機を置くのはネット回線を引く手間、盗難・火事等が怖いのでまず最初に却下としました。 クラウドとかVPSの候補としてはAWSGCP,IDCFクラウド、さくらVPS、ServersMan@VPSが候補に。 料金・拡張性を考えてIDCFクラウドとしました。 IDCFクラウドなら最低ワンコイン(オブジェクトストレージつかうともっとさげられますが...)からはじめられます。 (ServersMan@VPSは安いけど前使った感じあんまり印象が良くなくてお遊び以上には使えないなーっていう感じがあったので却下)

お金があるんだったらAWSとかGCPのPaaSをがんがんつかっていくというのがよい選択な気がします。

OS

現在はCentOS7を使っています。 これは最近のものがまぁまぁ動いてかつ枯れていて、サポート期間も長いため選択しました。 過去にCoreOSやFeodraつかってみましたが、CoreOSは小規模環境ではただただつらい、 Fedoraはサーバ機でする選択ではなかったですね...

構成管理

構成管理ツールのひとつであるAnsibleで構成管理を行っています。 Ansibleを使い構成をコード化することで(Ansibleがわかる人なら)誰でもサーバの構成を把握しやすくなりました。 また、将来のクラウド移転を行うことになった際もだいぶ楽なはずです。

Ansible特徴としてはエージェントレスであることYAMLで記述するため比較的設定がシンプルであることが挙げられます。 Chef-soloなんかでもいい気も一瞬しましたが、シンプルにできることはシンプルに済ませるというのが引き継ぎや作業のコスパを考えるといい気がしたのでAnsibleを選択しました。

Webアプリケーション

ここでコンテナ型仮想化技術のDockerです。 これでAnsibleを導入したのと同様の環境のコード化を手に入れ、 また環境がホスト・他のDockerコンテナと分離されるのでコンフリクトを気にする必要がなくなります。

Webサーバ

軽量・高速なWebサーバであるNginxを採用しました。 Nginxで静的サイトの配信及びWebアプリケーションへの振り分けを行っています。 各種環境に依存するサービス、特にWebアプリケーション(というか今はWebアプリケーションしかコンテナ化してない...)をコンテナ化しました。

監視

外形監視にはUptimeRobotを利用しています。 また,内部のリソース管理にはNagiosを利用しています。 Mackerelを使い始めました(詳細はこちらMackerelとUptimeRobotでサークルのサーバを監視している話 - まどろみの思考空間)

Nagiosを利用しているのはわたしが慣れているからという理由だけなのでZabbixでもMackerelでもなんでもいいと思います。

コードの管理

Ansibleのplaybook, DockerのDockerfile, そしてコンテンツ系ソースコードの管理はGitHubで行っています。 gitによるバージョン管理を行うことで構築でミスって障害を引き起こしても最悪直前の状態までもどせるという安心をえることができます。 学生ならアカデミックライセンスがつかえますのでプライベートリポジトリもつかえていい感じです。

実装

仮想マシンの立ち上げ・基本設定

IDCFはCloudStack APIに対応していますのでAnsibleのCloudStackモジュールを利用することで上位のファイアウォールの設定はもちろん、VMインスタンスを自動的にたてて基本的な設定をするといったことまで可能です。 これは下記を参考に実装しました。

AnsibleでCloudStackを操作する(基礎編:仮想マシン作成とプロビジョニング) - Qiita

ユーザはセオリー通りsudo権限を持つ管理者ユーザ、webコンテンツの管理者ユーザ(sudoは不可)を用意しました。 webコンテンツの管理者ユーザはDockerの各種コマンドが実行できるようにdockerグループに入れてあります。

SSL証明書の取得

世の中の常時SSL化の流れにしたがってCCSでもすべてのページを原則https化しています。

SSL証明書の取得にはLet's Encryptを利用しました。 Let's Encryptを使うと無料でSSL証明書を取得できます。 また,コマンドラインで取得,更新が可能ですので非常に自動化向きです。 certbotyumでいれて定期的に更新するようなcronを仕込めば完了です。 Ansible化するための注意点としてSSL証明書の取得のコマンドをそのまま叩いてしまうと冪等性が保証されないので --keep-until-expiringオプションを付ける必要がありました。

Nginx

これもAnsibleで管理です。 特に細かいチューニング等はしていません。 また,一時期動的にdockerコンテナにproxyする実装にしようかと思いましたが, 実装コストを考えて手動で設定ファイルを書き換えて各種コンテナにproxyさせるようにしています。 必要になったら実装すればよいでしょう。

監視

例のごとく監視設定,監視エージェント(NRPE)もAnsibleで管理しています。 監視項目で特にかわっている点としてはサーバにSWAPがないのでメモリ使用量監視にしているということでしょうか。 これには下記のプラグインを利用しています。

GitHub - koemu/nagios_plugin_check_memfreetotal: Checking overall memory free space (RAM + Swap) plugin for Nagios.

また,コンテナの起動監視もしたいと思っていて,現在こちらのプラグインを作成中です。

監視サーバは暫定的にわたしの個人契約しているのを利用しているのでそのうち移行したいと考えています。しました。 Mackerelに移行した記事はこちら(詳細はこちらMackerelとUptimeRobotでサークルのサーバを監視している話 - まどろみの思考空間)

コンテンツ系

先程説明したとおり,WebアプリケーションはDockerで管理しています。 基本的に揮発しても良いものはDockerfileにかいて、 データはVolumeでマウントするようにしています。 gitで管理はしているものの,基本的に手動でデプロイです。 ポート番号とかも手動管理... これはいけてないのでそのうち直したい...

実際に運用してみて

メリットとしては作った本人も構築当時の設定等をわすれてしまうもので, 何かあったときとりあえずGitHubをみて設定を確認できるってのは便利だなって思ってます。 今までは構築担当者が死んだらサーバの構成は闇の中でしたがそういったこともなくなりました。 また,教える時に余計コストが増えるかと思いきやAnsibleやDockerの概念さえ教えてしまえば、 細かいところに気をとられることなくざっくりと教えられる、ダブルチェックの手間が減る等 で時間的コストを大幅に減らすことに成功しました。 具体的には昔は構築は教えながらだと2,3日fullでしても終わらないとかでしたが 今は最短数秒,教えながらしても合計12時間程度の時間になりました。

今回サーバ構成で意識したこととしては、要求を満たしかつ凝りすぎないということです。 再利用性や柔軟性をかんがえたり、もっとストイックに全部Dockerで管理したり... ともっとやろうと思えば色々できたと思います。 ただそういうことをしてしまうとメリットをデメリットが上回ってしまいます。 また、シンプルなシステムのほうが壊れにくいし何かあっても対処しやすい。 そういったことを考えて妥協点を探るというのが特にサークルのサーバような 必要経費ではあるものの全力でそれに時間をかけているものではないといった場合に 必要なのではないかと思います(もちろんお金があるんだったら専門の会社とかにアウトソーシングとかもありなんでしょうけど...) 実は明らかな技術的負債も作り込んでしまっていますが、これはきっと後輩たちが解決してくれるでしょう(他人任せ

ただ,緊急で対応したいとかテストしたいとかのときになかなか面倒だなーっていうところはあるので そういったのに対応しやすいPlaybookとかかきたい(テスト環境をいい感じに整備することもふくめ)なあといった感じです。

まとめ

サークルのような低予算かつ人の流動が激しい環境ではAnsible+Docker等で出来る限り環境のコード化する, 構成をシンプルに保つことで幸せになれるかもというお話でした。

次は @littlebird514 君の予定です。よろしくおねがいいたします

Fedora22で突然dnfが使えなくなって解決した話

dnf updateするかーとおもっていつものように

sudo dnf update

とすると

Traceback (most recent call last):
  File "/bin/dnf", line 36, in <module>
    main.user_main(sys.argv[1:], exit_code=True)
  File "/usr/lib/python2.7/site-packages/dnf/cli/main.py", line 198, in user_main
    errcode = main(args)
  File "/usr/lib/python2.7/site-packages/dnf/cli/main.py", line 84, in main
    return _main(base, args)
  File "/usr/lib/python2.7/site-packages/dnf/cli/main.py", line 117, in _main
    cli.configure(map(ucd, args))
  File "/usr/lib/python2.7/site-packages/dnf/cli/cli.py", line 1006, in configure
    self.base.plugins.run_init(self.base, self)
  File "/usr/lib/python2.7/site-packages/dnf/plugin.py", line 104, in run_init
    plugin = p_cls(base, cli)
  File "/usr/lib/python2.7/site-packages/dnf-plugins/system_upgrade.py", line 359, in __init__
    super(SystemUpgradePlugin, self).__init__(base, cli)
TypeError: must be type, not None

とエラー。

調べてみると https://github.com/rpm-software-management/dnf-plugin-system-upgrade/commit/4bbbf1ec0c3f387249d30dd103ff4d002c17f574 に行き着いた。 Python2のユニコード周りのバグらしい...

Unfortunately, Fedora 21 and Fedora 22 don't have that

ほんとに不運だ...

ちゃんと修正されたソースコードはあるのでそれをDLして現状のものを置き換える(念の為バックアップもとっておく)

# cd /usr/lib/python2.7/site-packages/dnf-plugins/
# cp system_upgrade.py{,.bak}
# wget https://raw.githubusercontent.com/rpm-software-management/dnf-plugin-system-upgrade/4bbbf1ec0c3f387249d30dd103ff4d002c17f574/system_upgrade.py

これで解決した。

PyCon2015に参加してきました #PyConJP

1日目

Pragmatic Logging and Metrics

Pythonっぽい話かなと思ったら、わりとインフラよりのお話。 「fast/once/context」を実現するためにどうするか? まずはSentryの紹介。 アプリケーションの例外をSentryに送って、そこからSlack等へ配布。 fluentdに近いイメージ?

次にGrafanaとかいろいろ。 メトリクスを収集して表示的な感じ(いろいろあって把握しきれてなかった...)

そして、各種のログ出力をいい感じに整形して渡してくれるstructlogというのを書いたよという話。

Python と型ヒント (Type Hints)

Python3.5で導入された型ヒントについて。 大雑把にいうとイケメンではあるけれども、 それと同時に弊害・理想と現実という問題もあるというのも現実というような感じの内容。

プログラム全体が対応していないとあんまり意味がないっていうのはC++constみたいな感じだなって思った。

今後漸次的型付け(Gradual Typing)ってのは色々なところでいわれはじめそうな気がするので、 はやめに把握しておきたい。

組合せ最適化を体系的に知ってPythonで実行してみよう

タイトル通り。 組み合わせ最適化の紹介と実用例。 基本的にには解きたい問題をパターンに落とし込んでソルバに入れてあげれば答え出るねという感じ。

組み合わせ最適化問題は 標準問題と数理問題という二面から捉えることができる。 一般的に標準問題のほうが、早くとけることが多い。

わりと数学的なようで実用面結構ありそうだなという感触だった。 解くということに関しては、もうライブラリが充実しているので各アルゴリズムの特性とかを理解するのが良いのかなという感触。

なぜWeb画面自動テストはうまく行かないのか

Seleniumでいけそうだけど、現実問題(マルチブラウザ、パラメータ変更コスト、環境容易コスト)からうまくいかないのでラッパー作りましたという話。

環境に依存しないCoreと、あって状態(パラメータ)を記述するファイルというのはServerspecとかも そういうような感じになっていてWeb画面自動テストに限らずテストのフレームワーク作る際の定石なのかなという印象。

MinecraftPythonで遊ぶ

登壇者が若くて驚いた。 mcpiをつかってPythonからMinecraftをいじると楽しいよという話。

Minecraftで遊んだことないけど、こういう遊び方もあるのかと3000円払いたくなってきた。 発表の中でいわていた学習というコンテキストで語られる機会の多いmcpiだけど、そうじゃなくて楽しいからやってみる っていうのとても大事だなって思う。

いま求められるコミュニティの多様性と未来

コミュニティについてのパネルディスカッション。 色々と考えさせられた。

この中で入りやすいコミュニティ大事という話があったけど、 C++のコミュニティとかここらへん真逆なあれだよなっていう感じがした。

懇親会

ビールばっかり飲んでたw 前から会いたいなーっておもってたこくたんと話せてよかった。 Twitterと同じように面白い人だった。

2日目

PythonとPyCoRAMでお手軽にFPGAシステムを開発してみよう

物理デバイスをつくるプログラムが書くの大変なので、 Pythonラッパーを作ってみましたというお話。

FPGAはあまり興味わかなかったけど、 Python構文木をいじれるってのは面白いなって思った。

uWSGI/Dockerを利用したWebサービス運用事例

わたしはgunironユーザだったけど、 パフォーマンスも良さそうだし、機能豊富だしuWSGI使ってみようと思った。 uWSGIのReuse-Portがダウンタイムを最小にしていい感じにできるらしい。

Dockerはよく言われる壊れることを前提にする的な話だった。

セカイノカオ by チーム・カオ

発想がぶっ飛んでた。 こういうアートとITの融合みたいなのおもしろいなーって思う。

Rise of the Static Site Generator

WordPressみたいなCMSな動的サイトは確かに便利なんだけど、 重かったり、攻撃の対象になったりとか色々と問題が多い。 そこでStatic Site Generator(有名ドコロだとMT?)で解決しましょうという話。 Python製のPelicanが紹介されていた。

唯一聞き取れた英語セッションだったのでうれしかった。(もっと英語力高めたい....)

※あとでまた追記するかも

YAPC::Asia Tokyo 2015に行ってきました!#yapcasia

投稿おそくなりました。ブログを書くまでがYAPCです!

最初はYAPCの存在すらよく知らなかったのですがかるぱねぎらさん(@karupanerura)にチケットを譲っていただき行ってきました。

めっちゃPerl, Perlしているのかなーと思いきやそんなことはなく、IT系の人たちが集まるお祭りって感じでした。 以下気になったセッションの感想を書いていきます。

Managing Containers at Scale with CoreOS and Kubernetes

Kubernetesとは何者なのか? デモとともに紹介していくというセッション。 個人的に最近Dockerに興味を持っていてそろそろ複数のコンテナを扱いたいなーって思っていたので、 ちょうどタイミングのいいセッションでした。

ポイントとしてはKubernetesはスケジューラでありレプリケーションコントローラでPod(同一ホストで起動しなくてはならないDocker群)の数を一定に保つようにするということですかね。 死活監視をしているので、ホストサーバが死んだ場合自動的に最適な別ホストにDockerを立ち上げてくれます。 また、スケールアウトや逐次アップデート、いくつかだけアップデートなどもコマンドをひとつ叩くだけでよしなにやってくれます。 ただ、逆に突然別ホストに移動する可能性があり、死んだDockerは削除されてしまうのでKubernetesで管理するものは基本的にステートレスなDockerクラスタに適応する感じで、永続化したいものはnfsマウントするなりAWSだったらS3に預けるなりしないとダメとのこと。

うっかりをなくす技術

インフラエンジニアとしてお仕事をさせていただいてもらっているので、ミスを出来る限り減らしたいという理由からとてもためになるセッションでした。

人間だからミスをしてしまう。そのミスをいかに減らし、またミスっても大事に至らないようにするかというお話。 まず、意識レベルを高めるということ。指差し確認などが有効なよう。 また、ミスは勘違いから生まれるので、勘違いしないような設計が大事で、そのためにはどうするかというと人の視点にたって手順を作ったりコードを書くとよい。読みやすいコードについてはリーダブルコード読みましょう!というお話でした。また、どうしてもトリッキーな実装になってしまう時はコメントに残す、コメントと実装の乖離をなくすにはみんなでコードレビューが有効。

また、ハインリッヒの法則からnear missを減らせば重大なミスは減らせるが、そもそもnear missは気づきにくいのでどうやって見つけるかが重要。具体的にはGitHubのIssueにヒヤリハットの要因を集めてまとめておくと良いとのこと。

最後にコーディングする上で言語による制約をうまく使いましょうという話。副作用を閉じ込める関数型言語はつよい。

ISUCONの勝ち方

ISUCONで勝つために聞いてきました!

まず、そもそもなぜ早くしなくてはいけないのか? ユーザ的な面だと遅いとUXが下がり、また検索順位に影響するとのこと。 そして、お金的な面で当然速いほどサーバ台数を減らせるのではやいとコスト削減になる。

方針として紹介されていた最初の1時間は問題把握・方針の決定、最後の30分は再起動テストにあてるってのは、去年やっていたのであとはのこりの6:30でいかにチューニングするかですね... また、参考になったのは「決まったことはメモとして書き出す」というの。 計測した情報とかは書き出すようにしてましたが、することに関しては今からします宣言して作業程度だったので、これは実践しようと思いました。 また、レギュレーションを読めというのこれ大事ですね...しなくていいチューニングをしないように。  技術的な面だとまずMySQLでは「コストの高いクエリより実行回数がおおいクエリが効いてくることもある」というの。とりあえずスロークエリにすべて吐き出す設定でベンチまわすのもありかもですね。long_query_time=0でそれが実現できるらしいです。あとは「いつもB+木を」。secondery indexのリーフにはprimary keyがいて、そこからprimary keyの探索が走るというの知らなかったので意識していきたいです。あとはそもそもoffsetがなくなるようにKVSとかとの連携も重要ですね。 Frontだとh2oはやいというはなし聞いたのでちょっと試してみます。

MB-SystemのMBIOの構造

お久しぶりです。

研究関連でMB-Systemのソースコードをよんで修正する機会がありましたので、その時にキーワードとなったMBIOのしくみについてまとめます。

はじめに

MB-Systemとは「MB-System is an open source software package for the processing and display of bathymetry and backscatter imagery data derived from multibeam, interferometry, and sidescan sonars. This software is distributed freely (and for free) in the form of source code for Unix platforms. 」で要するに海底地形データの処理に使用するソフトウェアです。ということでかなりマニアックな領域、情報系とはおよそ関係ない領域のことなので書いてもあんまりじゅようないかなーと思いつつ、私自身どこかにまとめとかないと忘れそうなのでまとめておきます。

MBIOとは

一口に測深データといってもベンダ・機関・機器・versionによって使用しているフォーマットが大きくことなります。 そこでMB-Systemでは測深データを読み込んだ後MB-System独自のデータ形式に変換し統一的に扱えるようにしています。 そのデータ形式(構造体)と操作(関数)を提供するのがMBIOです。ライブラリ化されているので外部からも使えます。 MBIOを使うことで元のデータ形式に依存しない操作ができるようになるわけです。 もちろん各フォーマットへのエクスポートもサポートしています。

MBIOに新フォーマットを対応させる際の作法

今回わたしがやったのは修正ですので、新フォーマットに対応させるということはしていませんが、 調べる途中でだいたい見えてきたのでかいておきます。もしかしたら抜けがあるかもしれません。

src/mbio/配下にmbr.cというファイルを作り(は任意。フォーマット名にするといいかもしれません)、 対応させたいフォーマットからMBIOに変換するための関数を実装します。慣習的に下記のようにmbr_register*という名前になっているようです。

int mbr_register_*(int verbose, void *mbio_ptr,
                int *error);

ここでverboseは出力のレベル、mbio_ptrはMBIOの構造体へのディスクリプタerrorはエラーが起こった時の書き込み先のポインタが渡されます。 基本的にmbio_ptrに、がしがしデータとそれらを扱う関数をセットしていけばおkです。 他のフォーマットの実装を参考にしつつ実装すると楽かと思います。 また、実装しない関数はNULLを入れておくのが作法なようです(ある関数が使えるかどうかの判定が多くの場合NULLかどうかでされている)

つぎにmb_format.hを編集してフォーマットIDを割り当てます。 1000番台の値を割り当てとけばまず、衝突することはないでしょう。 mb_format.hに追記します。

#define MBF_* ID

これと先ほど作成したbr_register_*を紐付ける処理を書きます。 mb_format.cのmb_format_register関数の中に下記のようなelse ifの羅列があるので、そこに追記しましょう。

        else if (*format == MBF_*)
                {
                status = mbr_register_*(verbose, mbio_ptr, error);
                }

また同じくmb_format.cにmb_get_format関数というフォーマットを取得し返す関数があります。 これに新しく追加したフォーマットの判別処理を書きましょう。 例えば独自フォーマットの拡張子が.fooとすると下記のようになります。

        if (found == MB_NO)
            {
            if (strlen(filename) >= 5)
                i = strlen(filename) - 4;
            else
                i = 0;
            if ((suffix = strstr(&filename[i],".foo")) != NULL)
                suffix_len = 4;
            else if ((suffix = strstr(&filename[i],".FOO")) != NULL)
                suffix_len = 4;
            else
                suffix_len = 0;
            if (suffix_len == 4)
                {
                if (fileroot != NULL)
                    {
                    strncpy(fileroot, filename, strlen(filename)-suffix_len);
                    fileroot[strlen(filename)-suffix_len] = '\0';
                    }
                *format = MBF_*;
                found = MB_YES;
                }
            }

基本的に

            if ((suffix = strstr(&filename[i],".foo")) != NULL)

            else if ((suffix = strstr(&filename[i],".FOO")) != NULL)

                *format = MBF_*;

の部分を書き換えればよさ気な雰囲気です(基本的に拡張子しかみてないんですね...

以上をするとフォーマットを自動判別して適切な変換関数を呼び出しあとはよしなに扱ってくれます。

ISUCON4予選に参加しました

ということで昨日09/28にサークルの人(わたし、りょい君、kakira君)でチーム「☆(ゝω・)vキャピ」として参加しました。

当日まで

2回ほど勉強会を開きました。一回目は肝になりそうな部分をそれぞれがまとめて発表し勉強するという形式。 具体的には負荷とはそもそもなにか?(わたし),MySQLまわり(kakira君),memcachedまわり(りょい君)って感じにしました。これはわたし担当分は普段さわっているから、kakira君は競技プログラマなのでこのあたりデータ構造とかアルゴリズムとか関連付けて理解できそう、りょい君がこのメンバの中で一番Perlかけるので実装の際にしっているとつよいって感じで割り当てました。 AMIからの起動とか軽い動作確認などもこの日にしました。 そして、予選の方針として「推測するな計測せよ」というのを徹底していこうということ、 またそれとあわせ最初一時間はチューニングを始めずに統計をとる、ソースコードを読むなどして構造を理解最初のボトルネックを探す、そして最後の1時間は再起動したらうまくいかなくなり失格なんてことののないようチューニングをやめ確認をしているというふうにすることに。 また、選択した言語はのこり二人が読み書きできるということでPerlにすることに。(個人的にはPythonがよかった...)

二回目の勉強会ではISUCON3の過去問を解くという形。

開始直前

始まる前にチャットサーバとか、ディスプレイの設定(デュアルディスプレイにした)とかそういうことをしておきました。チャットサーバにはDevHubというものを使わせていただきました。これを採用した理由は普通にチャットできる部分とメモとしてマークダウンで書いておいておける場所がある点。チャットはURL貼るという用途に しか使いませんでしたが(普通の会話は口頭でしていた)、メモは色々わかったことなどを各々が書き込んで結構使いました。結構役に立ちました。↓予選後のスクリーンショット

f:id:ikaro1192:20140928173547p:plain

予選開始

とりあえずAMIを立ち上げベンチマークを走らせます。(10:11) 最初に決めたようにわたしはサーバ周り、りょい君、kakira君にはアプリケーションのソースコードを読んだり、サイト構造を把握してもらったりをお願いしました。

まず、psやnetstatからサーバで動いているプロセスリッスンしているポートを把握。前回までと大きく構成変わっているのはwebサーバデフォルトでhttpdではなくnginxであること、 memcachedが動いていないことでしょうか? そしてわたしは各種計測ツール導入。入れたものとしてはdstat,percona-toolkitなんかをいれました。 また、nginxでアクセスログの先頭に処理時間を表示するように設定。ついでに設定を見るとワーカーが1だったので(一時間はチューニングしないという話でしたが単純な変更で済むので)ワーカを4にして再起動。 MySQLについてもスロークエリを出力するように設定して再起動。また、今回はsupervisordがRuby実装ということで使うPerl実装に切り替えました。 ほんとはSSHもisuconユーザでログインできるように設定するはずだったのですが、なんか焦ってしまいできない...こんなところで時間潰すのももったいないので他の人には不便をしいますが(ほんと申し訳ない...)、とりあえず一旦やめ再度ベンチマークを走らせます。(10:44) 最初走らせた手応え、前回までの感じで大体workload8あたりがベストなのでこの時点でその程度の値にして実行。その結果スコアが3200ぐらいになり

となりました。やったね!暫定一位!このままいければよかったんだけど...

dstat,topの結果をみているとロードが最大3〜4前後であること、IOというよりCPUがネックになっていそうなこと、時々ポンとIOが来ること、やっぱりMySQLCPU使用率が高いことがw狩りました。また、ログ出力の関係からか常にディスクアクセス(書き込み)が発生しています。また、メモリは全然使い切れていないようなのでメモリにのせる実装が良さそうですねとか考えました。 スロークエリの結果からはなんかlogin処理であるクエリが殆どをしめているっぽいです。login_logというテーブルを参照しているのでログインまわりかなとか考えつつ。 また、ページごとのアクセス数の偏り、なんかをみてみても静的ファイルを除いてログインまわりに結構アクセスが来ています。↓

 5611 /
3087 /login
565 /mypage
2 /report
1 /tmUnblock.cgi

ソースコード解析しているkakira君からも該当クエリがめっちゃ効率わるいという話だったので、とりあえずここをチューニングしようという話に。 ソースコードが単純であることからmemcachedに全部のっければいいのでは?という話に。 これはメモリ上にのせるというよりも検索コストをさげるのとSQLでCPU使ってるのでKVSとしてO(1)でアクセスするという意味合いがつよいです。

ということでソースコード書き換え開始とその前にisuconユーザのホームディレクトリ配下をgit管理するようにしました。 kakira君がSQLソースコードをよみ大まかな設計をし、りょい君が実装していきます。わたしはそれにアルゴリズムまわりにちょっと口を出す程度。 わたしはnginxで静的ファイルを返すように変更したり、MySQLのパラメータを変えたりと細かなチューニングをしていきます。

とりあえずIPでの遮断の方を書き換えベンチを走らせると6000超えました。この調子でいけるか?ということでユーザロックのほうも実装開始。 これはいけるか?って思いましたがここからが... お昼も食べつつ13:30ぐらいに再度ベンチを走らせるものの/mypageがfailしまたreportとの付きあわせがうまく行かないらしく、スコアが送信されない状態... 送られれば100000越えてたのに...って感じでデバッグが開始されます。が、なかなかバグの原因がわからない。きっとmypageにアクセスできないはずのユーザがログインできているだろうと推測はできたのですが、手動でしらべるのは手間がかかる、またエラーログとしてページが予想されたものとことなりますだけでは情報がすくない... さらにPerlのデータ構造まわりの参照なのかスカラなのかハッシュあのかよくわからないというあれが発生し、さらに泥沼に... 改善したと思ったら今度はユーザロックをし続けているっぽい... 15:30頃一旦差し戻そうというはなしになり、スコアが3000に逆戻り... ソースコードをあらためて綺麗な心で書き換えはじめます。 うまくいかない...って感じですでに16:30。もう無理そうなので一旦諦めチューニングして別のところをチューニングし始めます。 ところでおなじBANなのになんでIPのほうはfailしてないのって話になりどうやらIPで遮断されたものは一回もないようなので、とりあえずそこのコードを単純にreturnするようにしました。これで6000程度に戻る。 あとはユーザのテーブルにインデックスをはったりして6500前後まで行きました。 もしかしてMySQLのデータを全部/tmp/shmにおけばいいのでは?とか考えて設定ファイル書き換えたらなんかMySQLが立ち上がらない... 設定ファイル差し戻してもダメ...結局これの解決をしていたら時間が来てしまいました...

反省点

  • 実装力低い...

これが大きかったです...とくにわたしがPerl読み書きできないので...また、コンテストの性質上コードを読んでいる時間が長くなるのですぐに実装できるより読みやすい言語のほうが良さそうです。次回参加する際はわたしもプログラミングまわりで力になれるようにしておきたいです。

*ダブルチェックをする

実は途中でわたしがsupervisordを再起動しとうとおもって、bashの履歴機能つかって再起動していたら実は複数回memcachedを再起動していてperlのエラーログがうまく出されないという事象がありまして、これは実サービスでやったらプロセスによってはとんでもないことになり非常に反省しております。また、今回でも余計に時間をかけさせてしまったというので申し訳ない。 再起動など影響のある作業するときはきちんとコマンドがあっているかチェックをする。声掛けだけではなく、ダブルチェックするなどを怠らないようにしないといけませんね。

*ダメそうだったら途中ですぐに諦める

loginまわりでかなり時間をとっていしまいました。 これは途中で方針変更できるはずなのに強行突破しとうとしたのは間違いでした。 MySQLのテーブル設計をきちんとする(一回でも成功しているかしていないかの判定があったのでそこのテーブルをわけるなど)、その上でクエリの改善をするなどで十分高速化できたはずで、その場合加点が0というわけではなく多少は加点があり、さらに別のポイントのチューニングにも時間をまわせました。

感想

今年が初参加でしたが、とてもおもしろかったです。 また、技術者として自分に足りない部分を再度確認できるよい機会でもありました。 問題自体は去年とはことなり、複数問題点が含まれているというより大きな問題がありそれを解決するって感じだったのかなって感じてます(もしかしたらたくさんある問題点をわたしたちがみつけられなかっただけなのかもしれませんが...)やっぱり計測大事ですね。 来年はもっとスコア伸ばしていきたいです。

参加者・運営の皆様お疲れ様でした!

追記

学生枠5位になんとか入り本戦出場できることになりました。 わたしは研究の関係で参加できないので残り二人に頑張ってもらいます。