tech.kayac.com

Lobiはメインの言語としてPerlを採用しています。 サーバーサイドで使用するコードは、Webアプリケーションから手動実行用のスクリプトまで、 ほとんどがPerlで書かれています。

languages.png

(なぜかPerl6のコードがあることになっていますが、さすがにまだ使ってません)

が、そこは適材適所。 Goの方が適していると判断した部分では積極的に利用しています。

Goの使いどころ

単機能を高いパフォーマンスで実現する必要がある場合はGoの出番です。 バイナリひとつを配置すれば動作するというポータビリティも魅力的です。 これらのツール・アプリは単独で実行され、一部はアプリケーションの要求に応じてその機能を提供します。

  • spam-filter
  • maintainer
  • gunfish
  • katsubushi
  • stretcher
  • rin
  • fluent-agent-hydra
  • nuko

それでは各ツール・アプリについて簡単に説明していきましょう。 なお、GithubリポジトリへのURLがないものは非公開のプロダクトとなっています。

spam-filter

Lobiの主たる機能はチャットコミュニティです。 ユーザーがメッセージを投稿するわけですが、その中にはスパムや荒らしも含まれています。 これらの投稿は単純なNGワードで防ぐことは困難です。 対策として、すべての投稿をGoで実装されたベイジアンフィルタに通してチェックしています。

spam-filter.png

スパムメッセージの教示は人間が行う必要が有るため、 spam-filterが判断に迷ったメッセージの蓄積と人力によるスパム判定を専用の管理画面から行なっています。 spam-filter自体は教示を受け付けるインターフェイスしか持たず、管理画面とは完全に独立しています。

Lobiに投稿されるすべての投稿をspam-filterが1プロセスでチェックしています。

maintainer

サービスを提供しているからにはいつでも安定稼働することが理想ですが、 やむをえずメンテナンスを実施することがあります。 メンテナンス中にもユーザーからのアクセスは発生します。 リバースプロキシとして使用しているnginxから静的ファイルを返せば事足り・・・ればいいのですが、 クエリパラメータなどでレスポンスのフォーマットを指定できるAPIがあるため、そうはいきません。 パラメータを解釈して適切なフォーマットでメンテナンス情報を返す必要があります。

関連するnginx.confの設定箇所を抜き出すと以下のようになります。


upstream maintainer {
    server 127.0.0.1:8090;
}

set $maintenance "false"

// 各種メンテ状態判定
if (...) {
    $maintenance = "true";
}

...

// メンテ有効であればmaintainerにreverse proxy
if ($maintenance = "true") {
    proxy_pass http://$maintainer;
    break;
}

ngx_http_lua_moduleを使ってluaで実装することもできますが、 使用言語をむやみに増やしてもメンテナンスコストが上がるだけなので Goで専用アプリケーションを書くことにしました。

平常時は1台のアプリケーションサーバーにつき数十のPerlプロセスがリクエストをさばいていますが、 メンテナンス時は同サーバー上のmaintainerが1プロセスでメンテナンス情報を返しています。

(そもそもこんな仕組みを用意しなくてもいいように、 API設計時にメンテナンス中のレスポンスフォーマットを統一のものとして定義しておくのが理想でしょう)

Gunfish

(公開準備中!)

iOSのpush通知はAnyEvent::APNSで送っていました。 メインのアプリケーションからHTTPで通知専用アプリケーションにリクエストを送り、 それを元に非同期でAPNsに通知内容を送信する、という仕組みになっていました。 しかし、昨年発表されたHTTP/2を使ったAPNs Provider APIには対応していません。

Goで書かれたpush通知送信ツールとしては、gaurunなどがありますが、 こちらも当時はAPNs Provider APIには対応していませんでした。 (本記事公開時点では対応したPull Rquestがマージされているようです)

APNs Provider APIに対応しつつ、Lobi内で使用していた既存のフォーマットに互換のあるAPNs通知送信ツールとして実装したのがGunfishです。

gunfish.png

Gunfishについて詳しくは以下の発表資料を参照下さい。

AnyEvent::APNsからGunfishに置き換えることで、 全ユーザーに対して送信する場合に300分程度かかっていた所要時間を90分程度まで短縮することができました。

katsubushi

https://github.com/kayac/go-katsubushi

katsubushiはTwiter社のsnowflakeのアルゴリズムを採用したid発番器です。 主にチャットIDの発番に使用しています。

タイムスタンプ、マシンID、シーケンスIDを元に発番するので、 各アプリケーションサーバーにマシンIDを一意に割り振れば 同期の必要なくユニークなIDを得ることができます。

daemonとして常駐し、idを必要とするアプリケーションとは memcachedプロトコルを使って通信を行います。 既存のプロトコルを流用し対応したライブラリを用いることで、 アプリケーション側のコード追加を最小限に抑えることができます。

katsubushi.png

stretcher

https://github.com/fujiwara/stretcher

stretcherはデプロイツールです。 特に初回のデプロイでは、依存するライブラリ等がある場合それらもインストールする必要がありますが、 Goで書いた場合はビルドして作られたバイナリを配置するだけなので非常にお手軽です。

stretcherを用いたデプロイについては以下のエントリーを参照下さい。

rin

https://github.com/fujiwara/rin

rinはAmazon SQSからの通知をトリガーにAmazon Redshiftにデータを投入するツールです。 Lobiではfluentdで集約したアクセスログをRedshiftに取り込むために使用しています。

rin.png

詳しくは以下を参照下さい。

fluent-agent-hydra

https://github.com/fujiwara/fluent-agent-hydra

fluent-agent-hydraはログファイルを監視してfluentdに送信するagentです。 Lobiでは一日に数十GBのログが生成されており、 これらすべてを各アプリケーションサーバーに配置されたfluent-agent-hydraがfluentdに送信しています。

fluent-agent-hydra.png

詳しくは以下を参照下さい。

nuko

おまけ。

nuko(ぬこ)はGoで書かれたSlack botです。 Go習得のためにいままでnodejs(hubot)で書かれていたbotを移植したもので、以下の機能があります。

  • Github issue番号の展開
  • レビュー依頼のランダム振り分け

シンプルなbotですがどれも開発に必須な機能ということでチーム内で日々利用されています。 レビュー依頼をbotに任せると、レビューさせちゃって申し訳ない感が薄れるのでおすすめ!

次回

今回はLobiで実際に使用しているGoプロダクトについて紹介しました。 次回はさらっと紹介したGunfishについて詳しく書こうと思います。

LobiではGoを書きたいエンジニアも募集しています!

Lobiチームの長田です。

今回はLobiで使用しているデータベースの構成・運用について紹介します。

TL;DR

  • メインのデータベースとしてMySQLを使用
  • 一般的なmaster-slave構成
  • HAProxyとMHAでDBのfailoverを自動化
  • HAProxyでslaveへの接続を分散・死活監視
  • MHAで障害時のfailover・ENIを付け替えてmaster切り替え

で、何が起こるの?

サービスが提供できなくなります。 ユーザーの認証情報等、サービス提供に必要不可欠なデータを管理しているため、一時的なサービス停止は免れません。 ユーザー体験的にもビジネス的にも、大変厳しい状態です。

この「一時的」な時間を可能な限り短くするために障害復旧の一次対応を自動化しています。 自動化のメリットとして、単純にサービス停止時間が短くなることはもちろん、 復旧処理を行う際の人的なエラーを未然に防ぐことで二次災害の可能性を減らすことができます。

それでは実際にLobiで採用されている自動化の手段について紹介していきましょう。

DB構成

LobiではサーバーインフラとしてAmazon Web Services(AWS)を利用しています。 Amazon EC2上でMySQLを動作させ、これをサービスのメインのデータベースとして使用しています。

一般的なmaster-slave構成を採用しており、 また一部のテーブルは水平分割されているため、master-slave構成が複数存在することになります。

アプリケーションからmasterへの接続はElastic Network Interface(ENI)を通して、 slaveへの接続はHAProxyを通して行っています。

db_cluster.png

slave障害発生時

正常時は以下のようになっています。 図中では省略していますが、HAProxyとMySQLの間はHTTPで接続を受け付けMySQLのチェックを行うヘルスチェッカーが仲介しています。

slave_failover_1.png

HAProxyが指定の間隔で行っているヘルスチェックが失敗すると、 そのホストには新規接続が回されなくなります。

slave_failover_2.png

アプリケーションはHAProxy通していれば常に正常なslaveに接続することができます。

slave_failover_3.png

slaveは負荷的に余裕を持った台数が稼働しているため、このままでもサービス運用は可能です。 この状態からさらにslaveがダウンすると余剰分がなくなるため迅速にに新しいslaveを追加する必要があります。 HAProxyによるfailoverはあくまでも一次対応の自動化です。

HAProxyにはmasterを含めた全てのデータベースがserverとして定義されています。 言い換えれば、HAProxyはどのデータベースがmasterかは感知しません。 ヘルスチェックを行う際にmasterかどうかを確認し、masterであればチェックに失敗するようにしています。 ヘルスチェックに失敗したserverには接続が発生しないため、masterはslaveとして接続を受けることはありません。


# HAProxyの設定例
listen  mysql-slave
        bind    0.0.0.0:3307
        mode    tcp
        balance roundrobin

        # MySQLが起動しているインスタンスの指定したポート(この例では13307番)にhttpでヘルスチェックをかける
        option httpchk

        # db01はmasterだがヘルスチェックが失敗するようになっているのでslaveへの接続が振り分けられることはない
        server db01 db-server01:3306 weight 100 check port 13307 inter 5000 rise 3 fall 3

        # アプリケーションからの接続は以下の2台に振り分けられる
        server db02 db-server02:3306 weight 100 check port 13307 inter 5000 rise 3 fall 3
        server db03 db-server03:3306 weight 100 check port 13307 inter 5000 rise 3 fall 3

DBホストに常駐しているヘルスチェッカを手動でダウンさせることで、安全にslaveを切り離すことができます。 ホストであるEC2インスタンスのScheduled Reboot対応(つらい)や、不具合発生時の調査などに利用できます。

httpによるヘルスチェックについては以下を参照ください。

master障害発生時

MHA managerが各master候補を監視しています。 この図ではDB01がmaster、02・03はslaveとして稼働しています。

master_failover_1.png

masterで障害が検知されると、MHA nodeによるbinlogの同期が行われます。

master_failover_2.png

binlogの同期が完了すると、ENIが新しいmasterに付け替える処理が行われます。 アプリケーションから見ると接続先は変わりません。

master_failover_3.png

もともとslaveとして稼働していたDB02ですが、 masterとして稼働し始めた時点でHAProxy用のヘルスチェックが失敗するようになり、 前述のslave障害発生時と同様にslaveとしての新規接続は振り分けられなくなります。

ENIの付け替えにはMHA::AWSを使用しています。 MHA::AWSは弊社藤原作のfailover支援ツールです。 詳しくは以下の記事を参照ください。

MHAのmaster_ip_failover_scriptにMHA::AWSが提供するコマンドを設定することで、 failover後にENIが新masterにつけ変わるようになっています。 slaveからmasterに役割が変わることによってHAProxy用のヘルスチェックが失敗するようになり、 slaveへの接続は新masterを除いた各slaveに振り分けられるようになります。 これによりアプリケーションの変更をすること無くmasterデータベースのfailoverを行うことができます。

以下はMHA managerの設定例です。


[server default]
secondary_check_script=masterha_secondary_check -s some_host --user=root --master_host=db-master
master_ip_failover_script=mhaws master_ip_failover --interface_id=eni-123*****
master_ip_online_change_script=mhaws master_ip_online_change --interface_id=eni-123*****
shutdown_script=mhaws shutdown --interface_id=eni-123*****

[server1]
hostname=db-01
candidate_master=1

[server2]
hostname=db-02
candidate_master=1

[server3]
hostname=db-03
candidate_master=1

MHAそのものについてはMHAのドキュメントを参照ください。

自動化最高!

影響範囲の大きい操作が自動化されていると安心感が段違いです。 導入には検証も含めてそれなりのコストがかかりますが、その価値は充分にあります。 便利なツールを提供してくれている作者の方々に感謝 :pray:

次回

Lobiで実際に使用しているgo製アプリ・ツールについて紹介します。

はじめに

どうも@Konboiです。

3月ももうすぐ終わり、あと今週末には2016年度新卒社員が入社してきますね!!

昨年の事を思い出すと、昨年の今頃 「メンターよろしく!」 って言われてワタワタしたのを覚えています。 ( もうちょっと前だった気もします)

ちょうど1年になるので、昨年の12月にAdvent Calendar 1日目に書いた

今年4月からメンターになってやったこと

のその後についてと追加で読んで参考になった本を紹介しようと思います。

今年4月からメンターになってやったこと のその後

今年4月からメンターになってやったこと は 入社後の4月から11月末までの事について書きました。

今回は 12月~3月末 までの事について書こうと思います。

とはいえ、12月頃になるとある程度どんなタスクを振ってもこなせるようにはなっていました。

なので、難しいタスクを与えてレベルアップを図るというよりは

  • 1個1個のタスクのスピード
  • 仕事の効率を上げる

を意識して欲しかったのでタスクをガンガン振りました。

ただ、インフラ周りは、まだ弱いという自覚があったので新しい機能で負荷試験を行った際には、一緒に負荷試験を行い何をやっているのか説明したり、zabbixのグラフを一緒に見たりなどはしました。

タスクをいきなりガンガン振り始め

「なんで俺ばっかり…」

と思われるとお互いにもったいないので

月1回行っている面談で

  • 先ほど書いた2点(スピード、効率)の事を意識して欲しい
  • 4月までの 3,4ヶ月はタスクを一杯振っていく
  • 新卒である期間中に失敗するなら失敗しちゃおう

というのを伝えました。

贔屓目に見ても、まぁまぁなタスク量がある中いい感じにこなしてくれたんじゃないかなと思います。

チーム内のディレクターから僕やもう1人の先輩エンジニアに来ていた質問も新卒氏にどんどんいくようになりチームメンバーからも頼られるようになってきているのではと思います。

参考になった本

無理・無意味から職場を救うマネジメントの基礎理論 18人の巨匠に学ぶ組織がイキイキする上下関係のつくり方

これは弊社人事からいいよとオススメされたので読んで見ました。

これからメンターになったり後輩が付く人などは1,2章がとても参考になると思います。

4月からメンターに

タイトルの通りなのですが、 4月からメンターをしていた新卒氏が2016年度新卒のメンターになることになりました。

自分は影ながら見守っていこうと思います。

育て方/教え方 は人それぞれなのでどうやって教えていくのかは、自分がこうやってたのか… という反省にもなるので非常に楽しみです。

最後に

カヤックではこんな感じで新卒で入社しても1年でぐぐぐ??っと成長できる環境とそれをサポートしてくれる先輩エンジニアがいるので興味がある方は是非こちらから応募お願いします!!

カヤック 新卒採用

また、俺ならもっと上手に教えられるぜ! こんな教え方もあるんだぜ!! と後輩エンジニアや周りのエンジニアを巻き込んで成長させてくれるエンジニアも絶賛募集中です!! どうぞよろしくお願いします!!

カヤック 中途採用