Alligator Swamp

技術メモ

ISUCON9予選にk02で参加して惨敗しました #isucon

いつものメンバーでISUCONに参加して惨敗しました。いつもどおりインフラ面を担当。
最大スコアは3,120で、最終結果は fail でした。

事前にやったこと

  • チームで集まって打ち合わせとISUCON8予選を軽く解く
  • Prometheus + Grafana を使うことに決めたので、使用方法を調査
  • 当日やることの手順を scrapbox にまとめる (分析ツールのインストールや使用方法など)

当日やったこと

10:00-10:30

  • マニュアルを読んで気になった点をまとめる
  • テーブルのCREATE文やレコード数、レコード内容を scarpbox にメモ
  • categories が更新されないことに気づく

10:30-11:00

  • アプリケーションを触って動作確認
  • 決済が動かず悩んだが、pyament-service の url が開発用になってないことに気づく
  • QRコードを読み込み、発送完了に出来たことを喜ぶ

11:00-11:30

  • kataribe 入れた
  • hostnamectl で各サーバの名前をわかりやすくした
  • MySQL を 別サーバに移し始める

11:30-12:30

  • Alibaba Cloud のセキュリティグループをいじって 3306 ポートを許可する
  • MySQL の bind-address を 0.0.0.0 にして動き始める
  • ベンチを実行して、MySQL の slowlog と Nginx log の分析
  • DBサーバのCPUが200%近かったので、DBがボトルネックと判断

12:30-13:30

  • お昼ごはんを買いに行く
  • 作戦会議

13:30-14:30

  • デプロイしてログの分析を何度か行う
  • 合間に Prometheus の node-exporter と mysql-exporter を入れた

14:30-15:30

  • そろそろ campaign の値を変えておきたいねということで 1 にする
    • リクエスト傾向が変わり、Appサーバにボトルネックが移る
    • もっと早く試しておけばと後悔
  • Too many open files が出たので、systemd の LimitNOFILE の設定を nginx, isucari, mysql に設定
  • MySQL の max connections を増やした
  • shippment service が APIGatewayError(502) で死に始める

16:00-17:00

  • shipment service の status 遷移を見て、initial, wait_pickup, done は api 叩かなくて良いのではという話をする
  • App 二台構成にしようとして、画像の配信でエラーになったので戻す

17:00-18:10

18:10

  • おわり

反省点

  • 各種ツールのインストールが遅すぎた。スクリプト組むなり ansible 使うなりして最速で終わらせるべきだった。
  • campaign の値を変えるのが遅すぎた。単に負荷が上がるだけだと勘違いしていたのがそもそもの間違い。
  • 外部サービスのチェックが甘かった。業務だと気を使うのに、ISUCONでは何も問題なく動くと信頼しきっていた...。
  • App2台の構成にできなかった。画像の共有どうしようか、WebDavか、とか考えていたが、そもそも重い処理だけ振り分ければよかった。
  • アプリの調査に時間を使いすぎて、インフラの改善が疎かになった。インフラ担当なので、インフラの改善をもっとすべきだった。

次回に向けて

具体的な反省点が見つかったので、次回は改善して挑みたい。悔しい。

ISUCON8本戦にk02で参加し惨敗しました(15位)

f:id:waniji:20181023001847p:plain 初の本戦出場、スコアは5,124で順位は30組中15位でした。
シェアボタンを有効に出来ず勝負の土俵に立てなかったので、非常に悔しいです。

当日やったこと

[10:00 - 10:30]

  • ポータルサイトにログインしてレギュレーションを読む
  • 初期状態でベンチ実行
  • isuconユーザーに公開鍵認証でログインできるようにする
  • 全台でmysqldumpを実行

[10:30 - 11:00]

  • アプリケーションを操作して挙動を確かめる

[11:30 - 12:00]

  • サーバの情報をKibelaにまとめる
  • ベンチ前後のDBデータ件数などをKibelaにまとめる

[12:00 - 13:00]

  • ランチ兼ねて作戦会議
  • ここからアプリ担当(kyokomi,yasu)とインフラ担当(waniji)に分担

[13:00 - 13:30]

  • MySQLをDockerから切り離して別サーバに構築

[13:30 - 14:30]

  • MySQLのスロークエリを有効にしてmysqldumpslowで分析
  • SHOW CREATE TABLEの結果をKibelaにまとめる

[14:30 - 15:00]

  • NginxをDockerから切り離す

[15:00 - 16:00]

  • Nginxで静的ファイルを配信したが、何故かレイアウトが崩れてしまい戻す
  • ここで大幅なロス

[16:00 - 17:00]

  • sendfile, tcp_nopush, keepalive_timeout の設定を追加
  • Nginx+Appが1台、Appのみが2台、DBが1台の構成にする
  • 再起動用の設定を全台に行う

[17:00 - 18:00]

  • シェアボタンを有効にするもエラー多発で戻す

良かった点

  • Kibelaに情報をまとめた
    • Slackだと流れてしまう情報が見やすく便利だった
  • 各種計測ツールをスムーズに導入することが出来た
    • 事前練習の賜物

悪かった点

  • WEBサーバ3台+DBサーバ1台という構成にとらわれすぎた
    • 想定解法がMySQLのmaster/slaveだったが考えつかなかった
  • CPUに余裕があることに対し早めに疑問を持てなかった
    • 早々にシェアボタンを有効にしてボトルネックを調査すればよかった
  • シェアボタンの部分開放が思いつかなかった
    • 業務で10%開放のケースはよくあるのに何故思いつかなかったのか...
  • パーティションまで手が回らなかった
    • ここは気付いて後回しにしてしまったがゆえに悔しい...
  • 1つ1つの作業が遅かった
    • インフラ側の作業をもっと早く終わらせるべきだった
    • 日々の鍛錬が足りない

最後に

問題、会場、運営、どれをとっても素晴らしく、有意義な時間を過ごすことが出来ました。
次回があるならば必ずリベンジします。悔しい。 f:id:waniji:20181020193432j:plain f:id:waniji:20181020194230j:plain f:id:waniji:20181023002133j:plain

ISUCON8にk02で参加し予選突破しました

k02 (kyokomi, yasu, waniji) で参加するのは4回目で、今回は初の予選突破をしました。とても嬉しい。スコアは 39,282 で、528組中16位でした。

準備や当日やったことはチームメンバーの記事が詳しいので、そちらを見てください。

ここでは自分がやったことや反省点などをまとめておこうと思います

事前準備

  • ホワイトボードのある会場を確保
  • 外部ディスプレイ確保

いつも作業している環境が一番捗るため、外部ディスプレイをまず確保しました。また、お互いの画面を見ながらコミュニケーションをしたいので、横並びで席を配置しました。非常に気持ちよく作業出来たので、自分が一番貢献したのはこの部分かもしれません。

当日

  • レギュレーションとマニュアルの読み込み
  • アプリケーションを実際に動かしてみる
  • サーバ内で起動しているミドルウェア等々の把握
  • DBスキーマソースコード、initialize処理の把握

まずはサービス内容の理解に力を入れました。過去参加した時の反省会で、サービス理解が足りなかったという意見が毎回出ていたため、しっかりと時間を使いました。結果として、修正ミスがあったときにも素早く挽回が出来ていたので、初手としては成功でした。

  • h2oはほぼ手を加えない判断をした

Nginxに置き換える案も出たのですが、今回はここがポイントではないと判断し、ほぼ何もしませんでした。

  • MariaDBを別サーバで起動するようにした

ベンチマーク時のDB負荷がかなり高かったため、別サーバで動かすようにしました。そこまで苦戦することなく作業できたので一安心。

  • スロークエリログを出して改善案を出した

今回は Go を選択したのですが、バリバリかけるのが kyokomi のみだったので、自分と yasu はクエリのチューニングにまわりました。実装を任せきりにしてしまったため、分担としてはあまり良くなかったのかなと反省しています。

反省点

  • 作業分担があまり良くなかった

自分は、デプロイ改善やログの分析などに回ったほうが良かったなと思いました。ついついソースコードを読んで改善出来る箇所を探してしまうのですが、もっとメンバーを信頼して、サポート側に振ったほうが良かったなと反省しています。

  • 事前準備が足りなかった

もっとあんちょこを用意すべきでした。ISUCON終了時はモチベーションも上がり色々と必要なものが思い浮かぶのですが、一年経つと一切合切忘れてしまうので、次からは気をつけたいです。

感想

毎回参加して思うのですが、ISUCONは本当に楽しいです。運営の方々、ありがとうございます。念願の本戦出場なので、万全の体制で挑みたいです。

YAPC::Asia Tokyo 2015 に行ってきた

1日目(+懇親会)、2日目と参加してきた。 参加したトークは以下。

同時通訳はトーク動画には入らないと思ったので、迷った時は海外ゲストのトークを聞きに行った。同時通訳最高でした。後はPerl6のトークも優先した。

Larry Wall氏のトークで、Perl6のリリース時期が2015年のクリスマスと発言されたのもあって、Perl6に俄然興味が出てきた。Jonathan Worthington氏の、Perl6の並行/並列/非同期処理の話もとても興味深かった。

コミケに行かなかったので手に入れられなかった「雅なPerl入門第3版」がありがたいことに頒布されていたので購入。こちらにもPerl6入門の章があったので、リリース前に入門してみようと思う。

懇親会では、今までは基本的に知り合いと話すのがメインだったが、今回は初めて合う人と話をすることが多く、内容も充実していて、とても有意義だった。

EC2上のログをtd-agent使ってCloudWatch Logsに投げようとしたらSSL認証エラーになった話

EC2上のログを、td-agentfluent-plugin-cloudwatch-logsを使ってCloudWatch Logsに送信しようとしたところ、SSL認証エラーが発生したので、エラーの解決方法を調べたのでまとめてみた。間違っている箇所があれば指摘ください!

before_shutdown failed error="SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed"

tl;dr

EC2でtd-agent使ってCloudWatch Logsにログを送信する場合は、/etc/sysconfig/td-agentに以下を追記すると上手くいくはず。

export SSL_CERT_FILE=/etc/pki/tls/cert.pem

バージョン情報

  • Amazon Linux AMI 2015.03
  • td-agent 2.2.0-0
  • fluent-plugin-cloudwatch-logs 0.0.7

エラー解消までの道のり

使用している証明書の確認してみる

td-agentに同梱されているrubyがどの証明書を使用しているかを確認。

$ /opt/td-agent/embedded/bin/ruby -r openssl -e 'p OpenSSL::X509::DEFAULT_CERT_FILE'
"/opt/td-agent/embedded/ssl/cert.pem"

td-agentに同梱されている証明書を使用しているようだ。

EC2組み込みの証明書を使用してみる

EC2の証明書は以下のパスにある。

/etc/pki/tls/cert.pem

OpenSSL::X509::DEFAULT_CERT_FILE_ENVで指定される文字列名で環境変数を指定すると、rubyが使用する証明書を変更できるのでそれを使う。

$ ruby -r openssl -e 'p OpenSSL::X509::DEFAULT_CERT_FILE_ENV'
"SSL_CERT_FILE"

td-agentはinitスクリプト内で/etc/sysconfig/td-agentを読み込むので、そこに以下の設定を書く

export SSL_CERT_FILE=/etc/pki/tls/cert.pem

td-agentを再起動して確認

service td-agent restart

td-agentを実行すると正常にログを送信することが出来た。

その他調べたこと

aws-sdk-rubyに証明書がバンドルされていないのか

aws-sdk-v1ではバンドルされていたが、aws-sdk-v2からバンドルしなくなった。

なぜaws-sdk-v2から証明書がバンドルされなくなったのか

証明書をバンドルすることで、SDKがセキュリティ上の懸念と責任を持つことになるので、望ましくないとのこと。(英語読み間違えてたらすみません)

なぜfluent-plugin-s3ではエラーにならないのか

fluent-plugin-s3はaws-sdk-v1を使用しているため。

fluent-plugin-cloudwatch-logsもaws-sdk-v1を使用すればよいのでは

aws-sdk-v2にしかCloudWatch Logsの実装は組み込まれてなくて、v1にバックポートもする予定ないらしい。

Amazon Linux以外のOSだとどうなの?

手元のMacでCentOS6.6の環境を作って試したけど、認証エラーが発生した。Amazon Linux以外では、aws-sdk-v1にバンドルされてる証明書を使わないとダメかもしれない。

CloudWatch Logs Agentはなんで大丈夫なの?

CloueWatch Logs AgentとはAWS公式のログ収集エージェント。内部的にはaws-cli(Python製)が使われている。aws-clibotocoreというAWS Interfaceライブラリを使用しており、それが証明書をバンドルしているので問題ない。

2014年振り返り

各月ざっくり

1月

  • AsakusaSatellite導入した
  • 各種通知をAsakusaSatelliteに流す対応をする

2月

3月

  • Perl 5.8.8から5.18.2への移行対応
  • CartonとかApp::FatPackerとか試す
  • Coroを使って非同期処理を書く
  • でんぱ組.incの曲に何故かハマる

4月

5月

6月

7月

  • Test::mysqldの素晴らしさを理解する
  • メインPCをMacBookAirに変える
  • スクフェスで初URを引いた
  • SQL::Translator::Diffを試したけどOracle対応してなかったので泣いた
  • 帰省した

8月

  • A Tour of Goを完走
  • beatmania IIDXを頻繁にプレイした
  • SlackにHubotを住まわせた
  • Travis CIを初めて使う
  • Herokuを初めて使う
  • MySQL5.1を5.6にアップグレードする対応してた
  • YAPC::Asia 2014に参加

9月

  • ゲーム実況やたらと見てた
  • 風来のシレンを一時期やってた
  • 引越し準備に追われる
  • ISUCON4に参加(予選敗退)
  • iPhone6に機種変更する
  • 第二子誕生

10月

  • 引っ越し
  • Data::Section::Simpleが便利なことに気づく
  • 古のサーバ移行に苦しむ
  • Chiba.pm#5に参加
  • Apache(httpd)と戯れる
  • BrainWarsにハマる
  • PerlCasual#6に参加
  • boot2docker入門

11月

  • DockerHubでAutomation Build
  • Net::POP3を使って一部のメールをHipChatに通知させた
  • VagrantとChef使って環境構築をコード化
  • Coverallsを初めて使う

12月

  • Time::Piece::Iteratorというモジュール書いた
  • Advent Calendarに初参加
  • Serverspecを導入
  • Circle CIとDocker使って自動テスト

ブログ

17記事を投稿。前年に比べると倍に増えてるけど、まだまだアウトプットが足りない。
Perlの記事が比較的多かったかな〜。

読書

f:id:waniji:20150103104612p:plain

http://booklog.jp/users/waniji/chart/2014/total

技術書1冊、ラノベ3冊、その他1冊。壊滅的。どげんかせんといかん。

Pocket

f:id:waniji:20150103104549p:plain

年末時点でPocketの残り記事数は70。追加した記事が2,655件、読んだ記事が2,721件だった。
積み記事は消化出来てるのでいい調子だけど、もうちょっと減らしたい。

まとめ

2014年は育児の年だったなぁ。第2子も生まれて更に賑やかになった。
技術に関して下半期はそこそこ頑張れてる印象。ISUCONに参加したり、仲間内でもくもく会したり。まさか業務でDocker使うことになるとは思わなかったけど、趣味で触っててよかったと思えた。
自分の自由時間が少なくなったことに慣れてきたので、来年は有効活用したい。

2015年

業務でRubyを使用することが決まったので、2015年はRubyかな〜。Golangは入門しかしてないので本格的に使いたい。
後は基礎を固めたい。何するにも基礎が固まってると飲み込みが早い気がするので。
それと英語......。

ある範囲内の日付時刻を、指定間隔で反復するTime::Piece::Iteratorというモジュールを作った

この記事はPerl Advent Calendar 2014の18日目の記事です。

17日目の記事はid:kfly8さんの間接オブジェクト記法のアンチパターンでした。

Time::Piece::Iterator

Time::Piece::Iterator

指定した範囲内の日付時刻を、指定した間隔で反復するモジュールが欲しかったのですが、見つからなかったので作成しました。(他にあれば教えて下さい!)
日付とか年月を引数に取るバッチを、特定の期間分(半年とか)実行したい時に使ってます。

使い方

Time::Pieceオブジェクトを2つ渡すと、その範囲内の日付時刻を返してくれます。

use Time::Piece;
use Time::Piece::Iterator;

my $iterator = day_iterator(
    localtime->strptime('2014/01/01', '%Y/%m/%d'),
    localtime->strptime('2014/01/03', '%Y/%m/%d'),
);

while( my $t = $iterator->next ) {
    print $t->ymd, "\n";
}
2014-01-01
2014-01-02
2014-01-03

間隔はdayだけではなくsecond/minute/hour/week/month/yearを用意しています。
例えば、今年の各月最初の平日を知りたい場合はこんなかんじです。

use Time::Piece;
use Time::Piece::Iterator;
use Time::Seconds;
use Calendar::Japanese::Holiday;

sub is_weekday {
    my $t = shift;
    my $is_holiday = isHoliday( $t->year, $t->mon, $t->mday, 1 );
    if( $is_holiday || $t->wday == 1 || $t->wday == 7 ) {
        return 0;
    }
    return 1;
}

my $from = localtime->strptime('2014-01-01', '%Y-%m-%d');
my $to = $from->add_years(1) - ONE_DAY;

my $iterator = month_iterator( $from, $to );
while( my $t = $iterator->next ) {
    while( !is_weekday($t) ) {
        $t += ONE_DAY;
    }
    print $t->ymd, "\n";
}

用意されている間隔では要件が満たせない場合は、自分で設定することが出来ます。
以下の例は、間隔が3週間の場合です。

my $iterator = custom_iterator(
    $from, $to,
    sub {
        my ($t, $sign) = @_;
        return $t + ( $sign * 3 * ONE_WEEK );
    }
);

第1引数が現在の日付時刻、第2引数が符号(plus or minus)です。過去に遡ることも出来るので、符号を受け取れるようにしています。

注意点

month_iteratorとyear_iteratorはTime::Pieceのadd_monthsとadd_yearsを使っているので、月末の扱いに注意する必要があります。

今後

無限iteratorを可能にする、Time::Piece::Plusも扱えるようにする、などを考えています。

あと、CPANには上げていないのですが、使っていただける方がいれば上げたいなと思っています。

明日

19日目はid:y_uukiさんです!