読者です 読者をやめる 読者になる 読者になる

スケーラブル tail -f | grep on AWS

AdventCalendar2016 Go Lambda aws fluentd

こんにちは。組長こと @fujiwara (id:sfujiwara) です。

早速ですが皆さん、ログは見ていますか? 当然見ていますよね。tail -f で追いかけるとか大好きですよね。という前提で話を進めましょう。

この記事は Tech KAYAC Advent Calendar 2016 の24日目の記事です。

TL;DR

  • 多種多様なホストから発生するログの末尾を tail -f で追いかけたい
  • fluentd + ローカルファイルで行うのはちょっと面倒
  • Amazon Kinesis + Lambda を使ってスケーラブルな仕組みを考えてみた
  • kinesis-tailf というツールも作った

f:id:sfujiwara:20161222165753p:plain

既存の仕組みと問題点

techblog.kayac.com

弊社の Lobi とソーシャルゲームでは、この記事で紹介されたような Fluentd を活用したログ集約の仕組みを整備しています。この構成は快調に動いているのですが、多少の問題があります。

  • 集約サーバの構築、可用性維持が面倒
    • 特に流量が多いと、集約サーバがボトルネックになりがち
    • ファイルバッファを使用するため、インスタンスを使い捨てたりオートスケールしたりしづらい
  • 集約サーバを複数用意する場合、流れているログの末尾を tail -f で追いかけづらい

アラート検知や定型の集計作業は Norikra に送ってそこでクエリしたり、S3からRedshiftに投入してクエリしたりで行っていますが、障害発生時や動作確認時など、流れているログをなるべくタイムラグ少なく、アドホックに眺めたりgrepしたりしたい場合があります。

集約サーバは性能と可用性を持たせるために複数台構成にし、そこで Norikra, S3 などへ送るのとは別にローカルファイルに書き込みを行っているので、拙作の nssh というツールで全台に並列に ssh して tail -f を実行する、というオペレーションを行うことがたびたびありました。

これをもうちょっと何とかできないか、ということで、スケーラブルな tail -f | grep の仕組みを考えてみました。

Kinesis Stream に流す

まず、頼れる土管こと Amazon Kinesis Streams にすべてのログを投げ込むことにします。集約サーバを経由するとそこがボトルネックになるので、ログが発生するすべてのホストに Fluentd を動作させ、そこから fluent-plugin-kinesis を使って送ることにしましょう。@type kinesis_producer を使用すると、複数レコードを Kinesis Producer Library (KPL) を使ってまとめ送りできるので、流量を少ないレコード数に押さえ込めます。

<match log.**>
  @type kinesis_producer
  region ap-northeast-1
  stream_name log-stream
  flush_interval 1s
  include_time_key true
  include_tag_key true
</match>

なるべく即座に送り出したいので、flush_interval 1s としています。

kinesis-tailf で追尾する

github.com

Go製の拙作のツールです。指定した Kinesis Stream を追いかけて、流れてきたレコードの内容を標準出力に出力し続けます。つまり Kinesis に対する tail -f ですね。KPL でまとめられたレコードの展開も自動で行います。

$ kinesis-tailf -stream log-stream -lf

これで、すべてのログを ssh なしで手元のマシンで tail できるようになりました。あとは pipe でフィルタコマンドに通して加工するなりなんなりと。めでたしめでたし。

……といいたいところですが、これではすべてのログがネットワーク経由で kinesis-tailf を実行しているホストに流れ込んできてしまうことになります。本番環境では大量のログが発生しているため、すべてを手元で読み取りたいことは少なく、たとえば status=500 のログであったり、特定のIPアドレスからのリクエストだったりを事前にフィルタしたいことのほうが多いでしょう。(出先での障害対応時に全ログをテザリング回線で手元に持ってくるとか考えたくありませんし)

ということで、AWS上でスケーラブルに grep することを考えます。

Lambda で絞り込んで別の stream へ

AWS Lambda で、Kinesisから流れてきたデータを加工するコードを実行できます。

kinesis-tailf/lambda at master · fujiwara/kinesis-tailf · GitHub

kinesis-tailf の lambda ディレクトリ以下に Apex を利用したコードが置いてありますので、project.json を適当に以下のように用意して apex deploy し、Lambda とログが流れている Kinesis Stream を繋いでください。role には Kinesis への読み書き権限が必要です。

{
  "name": "kinesis-grep",
  "description": "",
  "memory": 128,
  "language": "go",
  "timeout": 5,
  "role": "arn:*****",
  "environment": {
    "MATCH": "\"status\":500",
    "STREAM_NAME": "filtered-log-stream"
  }
}
  • MATCH で指定された正規表現にマッチするレコードのみを
  • STREAM_NAME で指定された stream に流し直す

という動作を行います。あとはこの出力先 (filtered-log-stream) を kinesis-tailf で追い続ければよい、ということになります。 Kinesis Stream のシャード数に応じて動作する Lambda も増えるため、流量が多い (=シャードが多い) 場合でも問題なくスケールするでしょう。

  • KPL でまとめられたレコードの展開に対応しています
  • 流量が多い場合は memory 割り当てを大きくしないと追いつけないことがあります
    • LambdaのCPU性能はmemoryに比例するため
  • 必要ないときは入力元 stream との関連づけを外しておけば費用は掛かりません

ということで、できあがったものは以下のような概念図になりました。

f:id:sfujiwara:20161222165753p:plain

実際に1万メッセージ/sec以上のログを10シャードのstreamに送りつけて試してみたところ、Lambdaのメモリ512MBで問題なく動作しました。ログが発生してから kinesis-tailf に流れてくるまでのタイムラグは4秒程度 (fluentd, kinesis, lambda, kinesis-tailf でそれぞれ1秒ぐらいラグがある) で、実用上も問題ない程度だと思います。

WEB+DB PRESS Vol.94

WEB+DB PRESS Vol.94

  • 作者: 藤原俊一郎,朽木拓,八木俊広,吉田太一郎,うらがみ,のざきひろふみ,うさみけんた,水嶋淳貴,佐々木健一,柴崎優季,前島真一,伊藤直也,遠藤雅伸,ひげぽん,海野弘成,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2016/08/24
  • メディア: 大型本
  • この商品を含むブログを見る


12/1 から24日間にわたって更新してきた Tech KAYAC Advent Calendar 2016 もこれが最後の記事となります。バラエティーに富んだ記事をお楽しみいただけましたでしょうか。

カヤックではログを眺めるのが好きなエンジニアも、そうでないエンジニアも募集しています!

【Unity】カヤックのゲームを支えるビルドシステムについて

Unity UnityAdventCalendar2016

今回はios/androidビルド方法についての簡単な解説と、社内で動いているビルドシステムついて紹介したいと思います。

こんにちは、ソーシャルゲーム事業部所属エンジニアのぴーちんです!!宜しくお願いしますヾ(@⌒ー⌒@)ノ

この記事はカヤックUnityアドベントカレンダー2016の24日目の記事になります。

カヤックのゲームを支えるビルドシステムについて

platform毎の基本的なビルド方法について

OSや開発ツールのバージョンが上がる毎に少しずつ変わって行くので、詳細な方法などについては触れずに基本的な事だけ書いていきます。

カヤックで運用してる自動ビルドシステムを後ほど紹介するので、まずは事前知識を以下に簡単に書いていきます。

iOS

ビルドの準備

iOSアプリをビルドする為には、以下の準備が必要です。

  • Xcode
  • アプリ配布に使う配布用証明書
  • コード署名に使う開発用証明書
Xcodeでのビルド

UnityでiOSビルドを行った場合の生成物がXcodeのプロジェクトファイル( .xcodeproj )なので、その後に配布用のアプリケーションファイル( .ipa )をXcodeからビルドする必要があります。

Unityが生成するXcodeProjectについては公式マニュアルで詳しく説明されています。 docs.unity3d.com

アプリ配布に使う配布用証明書

社内向けとAppStore用の証明書があります。 それぞれ役割が違いますが、社内向けは開発用でAppStore用は本番用の証明書と覚えておくと良いでしょう。

ちなみにそれぞれの証明書には有効期限があり、証明書が期限切れになる事があります。 その場合、期限が切れてしまった証明書でビルドしたアプリに以下の様な異なる挙動が出ます。

  • 社内向け: アプリを起動できなくなる
  • AppStore: アプリは起動できるが、期限切れの状態でアプリをバージョンアップする事が出来ない

基本的に証明書の有効期限が近づくと、社内ではAdmin権限を持った人がアナウンスしてくれますが、証明書が必要になった時に期限切れしていると予想外のタスクが増えます。覚えておくと良いでしょう。

iOSの開発には複数の証明書が必要になります。詳細はAppleの開発社サポートを読むと良いでしょう。

証明書 - テクニカルサポート - Apple Developer

コード署名に使う開発用証明書

iOSのビルドを作って公開するには、開発用証明書で署名を行わなければなりません。

署名をする目的は、アプリの安全性を担保する事です。 アプリの署名をすると、このアプリが特定の開発元から提供されていて、最後に署名を行った時点から改変されてない事を保証できます。

また、コード署名を行わないとiOSデバイス上で動作しません。 そういった理由もあるのでゲームの開発時からiOSビルドをしたい場合に、この証明書は必要です。

詳細は公式の解説をお読みください。

コード署名 - テクニカルサポート - Apple Developer

Android

ビルドの準備

Androidアプリをビルドする為には、以下の準備が必要です。

  • AndroidSDK
  • アプリへ署名する為の証明書
AndroidSDK

androidアプリをビルドするには、開発者サイトで公開してるAndroidSDKやantなどのビルドツールが必要です

どちらもAndroidの開発者サイトにて公開されているのでDLできます。

現在SDKはAndroudStudioからinstallする様になっています。 なので、もし最新のAndroidSDKを入手した場合には、AndroidStudioからinstallしましょう。 install方法はも開発者サイトにチュートリアル動画があるのでご覧ください。

Android Studio のインストール | Android Studio

AndroidSDKやantを使用した詳細なビルド方法については、公式のマニュアルのAndroidのビルドプロセスをご覧ください。

docs.unity3d.com

アプリへ署名する為の証明書

AndroidもiOSと同様にアプリに署名を行わないとアプリをinstallする事ができないので、証明書が必要です。 署名をする理由もiOSと同じですが、証明書をplatformが管理せずに開発者が自由に発行可能な事はiOSと違う点です。

CLIから証明書を作成する手順は、開発者サイトのアプリケーションへの署名のページに説明があるのでご覧ください。

developer.android.com

また、UnityではEditorのGUIを利用して証明書を作る事が可能です。 Android Player Settingsのページ内にあるPublishing Settingsの項目で紹介されています。

docs.unity3d.com

社内で使われてるビルドシステムについて

githubのpull−requestにコメントしてビルドする機能

昨日のGitでUnityプロジェクトを管理するの記事でも紹介されていますが、社内で扱われてるビルドシステムではgithubのpull−requestに build とコメントをするだけでビルドを開始して、ビルドURLをコメントしてくれる様になっています。

f:id:pchin:20161222183827p:plain

githubのwebhookとjenkinsとalphawingを使って実現しています。

ちなみにalphawingとは、社内で使ってるアプリ配信サービスです。 以下の記事で紹介されています。 (『Android向け』とありますが、社内ではiOSAppの配信にも使用しています。)

techblog.kayac.com

githubのpull−requestにコメントしてコンパイルチェックする機能

プロジェクトによって異なりますすが、大体 compiletest とpull−requestにコメントするとjenkinsのビルドがUnityEditorを起動してコンパイルチェックをしてpull−requestにチェック結果を通知してくれます。

やってる事は、UnityEditorを起動してメニューにある Assets/Open C# Project をバッチモードで起動したUnityから開いて .slnファイルを更新して、Xamarinを起動してコンパイルのチェックをしています。

このプロセスを、チェックしたいScriptingDefineSymbolsのパターンの数だけ実行します。

まずはUnityのメニューにある Assets/Open C# Project を実行してUnityプロジェクトの .sln ファイルを最新の状態に更新します。

public static class TestUtils
{
    [MenuItem("Kayac/Open C# Project")]
    public static void OpenCSharpProject()
    {
        EditorApplication.ExecuteMenuItem("Assets/Open C# Project");
    }
}

そのあとで、Xamarinを起動してコンパイルを実行します。

/Applications/Xamarin Studio.app/Contents/MacOS/mdtool build

バッチモード

UnityはCLIから起動して、特定の関数を実行したりできる機能が提供されています。

例えば先ほど紹介した TestUtils. OpenCSharpProject を実行するには以下の様に書きます。今回はMacの Terminal.app から実行した例です。

/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode \
-executeMethod  TestUtils. OpenCSharpProject \ # 実行したい関数
-projectPath /path/to/unityProject/ \
-silent-crashes # バッチモード中にクラッシュしてもエラーレポートのWindowを出さないで終了する

詳細は公式マニュアルをご覧ください。 docs.unity3d.com

ScriptingDefineSymbols

C#にはプリプロセッサディレクティブという、コンパイル前にコンパイラなどに特別な命令を出す事ができる機能があります。 ScriptingDesineSymbolsは、その中でも条件付きコンパイルの機能で扱うシンボルの事を指します。

プリプロセッサディレクティブの詳細や、条件付きコンパイル以外の命令を知りたい場合には、『++C++; // 未確認飛行 C』にて、とても分かりやすく解説されているのでオススメです。

ufcpp.net

UnityはScriptDefineSymbolにシンボルを追加する事で、そのシンボルをプロジェクト全体に反映してくれます。 platform毎にPlayerSettingsのGUIから設定が可能です。 コードから設定する場合にはPlayerSettings.SetScriptingDefineSymbolsForGroupのAPIをご活用ください。

使用例

ScriptingDefineSymbolsについて更に詳しく知りたい場合には、公式マニュアルに設定例なども書いてありますのでご覧ください。

docs.unity3d.com

ちなみに過去にリリースしたプロジェクトでは、以下の様なScriptingDefineSymbolsを使用していました。

  • 3rd-party製SDKの有効設定(SDKをコンパイルするかの設定)
    • USE_GOOGLEANALYTICS...など
  • プッシュ通知機能の有効設定
    • PUSH_NTF_ENABLED
  • デバッグビルドか?(デバッグ用の機能もコンパイルするかの設定)
    • IS_DEBUG

3rd-party製SDKの有効設定(SDKをコンパイルするかの設定)

基本的に外部の機能はゲームと疎結合にしています。 理由は、以下の通りです。

  • 3rd-party製SDKの内容はサービス提供者のサーバーやDLLコードに隠蔽されてる場合が多いので、バグがあった場合にすぐ取り外したい
  • AppleやGoogleなどのplatformでアプリ内のルールや禁止事項が変更されて、禁止された処理をSDKで行っていた場合にすぐ取り外せないとアプリをアップデートできない

終わりに

如何でしたでしょうか?

ゲーム事業部のビルドシステムは基本的に技術基盤チームがメンテナンスしていますが、実際にビルドを動かしてるのはゲームタイトル毎なのでビルド処理のチューニングをしたり、ビルドが失敗した場合のログから問題を調査する能力はエンジニアとして働いていたら必ず役に立つと思います!

さて、本日でカヤックUnityアドベントカレンダー2016は終了ですが、また来年も引き続き技術ブログを更新していきたいと思います。

また、ソーシャルゲーム事業部所属では中途でも新卒でもエンジニアを募集していますので、今回のAdventCalenderを見て興味を持っていただけたら是非来てみてください、一緒にゲーム開発しましょう!

www.kayac.com

www.kayac.com

では、みなさん良いお年を!メリークリスマス☆*:.。. o(≧▽≦)o .。.:*☆