LCL Engineers' Blog

夜行バス比較なび(高速バス比較)・格安移動・バスとりっぷを運営する LCLの開発者ブログ

JOB管理をJenkinsからKuroko2へ移行しました

弊社では最近、JOB管理をJenkinsからKuroko2へ移行しました。

Kuroko2についての概要や導入方法は、以下の記事などが詳しいため割愛させて頂き、弊社での具体的な導入内容について紹介したいと思います。

導入の背景

元々、数百ほどあるバッチ処理をJenkinsで運用していました。Jenkinsから、各JOBサーバにsshして、JOBを起動するという単純な構成で運用していたため、下記の課題がありました。

  • 一つのホスト上で同時に起動されるJOB数を厳密に制御できず、CPU/メモリのリソースを逼迫してしまう
  • 夜間だけホスト増やすなど、ピークに応じた実行環境の増減に対応しづらい

Kuroko2は、ジョブ管理システムとして必要な「スケジュール、エラー通知、JOBの依存関係制御」等の機能に加え、Wokerを増やすことで動的にJOB起動数を制御できるため、上記課題を解決可能と考えました。

構成

Kuroko2の基本的なアーキテクチャは、以下の図のようになっています。(GitHubから抜粋)

f:id:lcl-engineer:20171029232828p:plain

弊社では、kuroko2-consoleとkuroko2用のDBは同インスタンス上に構築し、kuroko2-workerは各サービス毎のJOBサーバに導入しています。つまり、一つのconsoleで、複数サービスのJOBを横断して管理しています。

現時点のkuroko2(v0.4.2)では、権限制御機能がないため、メンバー毎に管理可能なJOBを制御したい場合は、kuroko2-consoleを分割する必要があります。

Queueの設計

弊社では、サービス・JOB種類別にQueueを分けています。例えば日中に定期的に稼働するJOBはQueue-A,夜間に集中的に稼働するJOBにはQueue-Bとしています。

あるJOBがCPUやメモリを極端に使用する場合、そのJOBだけでサーバリソースを圧迫してしまうことになるため、同じQueueに所属するJOBは、できるだけ似通った処理特性のOJOBにしています。

エラー通知

Kuroko2では、メールとチャット等(Webhook)に各種通知可能です。弊社では、メールは利用せず専用のチャットルームへ通知し、通知があればそこで都度チャットで確認をしています。若干、オオカミ少年化している通知もあるため、通知の精度向上に取り込んでいます。

タグのルール

Kuroko2では、JOBに複数のタグを設定でき、タグによる絞込ができます。当初は複数のタグを細かく付与していましたが、タグが増えすぎると絞込時に探しづらい状態になりました。結局、以下の2種類付与しておけば問題なく運用できています。

  • Queueの名称
  • JOBの分類

実行ログ

Kuroko2では、JOB内で標準出力したログは、管理コンソール上で確認ができます。ただし、リアルタイムではなくJOBが完了後に初めて確認できます。(ログが多いと途中までしか表示されません)

AWS CloudWatch Logsを利用することで、管理コンソール上でリアルタイムにログが確認できるようになりますが、ログの出力量が多いと、JOBの実行時間が遅くなってしまったため、弊社では採用を見送りました。

バックアップ

管理コンソール上で定義したJOBの情報等は、kuroko2のDBに格納されているため、mysqldumpを利用してバックアップを取得するようにしています。

ログの削除

現時点のKuroko2では、ログは自動的に削除されないため、弊社では定期的にログの削除を実施しています。kuroko2は以下のテーブルで構成されます。

mysql> show tables;
+---------------------------------+
| Tables_in_workerdb              |
+---------------------------------+
| kuroko2_admin_assignments       |
| kuroko2_ar_internal_metadata    |
| kuroko2_executions              |
| kuroko2_job_definition_tags     |
| kuroko2_job_definitions         |
| kuroko2_job_instances           |
| kuroko2_job_schedules           |
| kuroko2_job_suspend_schedules   |
| kuroko2_logs                    |
| kuroko2_memory_consumption_logs |
| kuroko2_memory_expectancies     |
| kuroko2_process_signals         |
| kuroko2_schema_migrations       |
| kuroko2_stars                   |
| kuroko2_tags                    |
| kuroko2_ticks                   |
| kuroko2_tokens                  |
| kuroko2_users                   |
| kuroko2_workers                 |
+---------------------------------+

kuroko2_job_instances,kuroko2_logsが、特にデータが貯まりやすいテーブルです。これらのテーブルの過去N日以前のデータを定期的に削除するようにしています。(オフィシャルな情報ではないので、自己責任で)

監視

Kuroko2が正常に動作しているかは、下記の観点で監視しています。

  • 管理コンソールURLを外形監視
  • Workflow,Schedulerのプロセス監視

また、JOBが滞りなく実行されているかは、以下のAPIを利用してWaiting JOB数を監視しています。

/v1/stats/waiting_execution

Waitingが増えた場合には、JOBスケジュールの見直しやWokeの追加(HOSTの追加)を検討します。

その他

最後に少しハマったところや、TIPS的なものを紹介します。

PostgreSQLでの稼働

Kuroko2の動作環境として、System requirementsには「MySQL >= 5.6」と記載がありますが、PostgreSQLの方が運用に慣れているため、最初はPostgreSQLで構築しておりました。ある程度は正しく動いていたのですが、細かいところでSQLエラーが発生していたため、結局はMySQLでの運用に切り替えました。

強制キャンセル

Workerの強制終了等の理由により、Kuroko2上でJOBが完了しないまま残りつづける場合がありました。

その場合は、「Job Instance Details」画面で、Backslashを押下することで、Force Cancelボタンが有効になり、Force CancelでJOBを強制終了させることができます。

f:id:lcl-engineer:20171029233213p:plain

参考) https://github.com/cookpad/kuroko2/issues/23

HOST追加時にWokerが認識されない

Kuroko2では、Host名+Queue名をキーとしてWokerを管理しているため、HOSTを追加した場合に、既に同名のQueueがある場合は、Host名を変更する必要がありました。

拡張機能

いくつかChromeの拡張機能を作っていますので、少し紹介します。

デフォルト入力

新しくJOBを作製する際、デフォルトで入力する項目はある程度決まっています。デフォルトボタン(下記の図のオレンジボタン)を用意して、デフォルト値を設定するようにしています。

f:id:lcl-engineer:20171029233228p:plain

スケジュール削除ミス防止

スケジュールのDeleteボタンを押すと、確認なく削除されるため、間違えて押下した際に、以前のスケジュールがわからなくなって困るケースがありました。ミス防止のため、拡張機能でダイアログを出すようにしています。

f:id:lcl-engineer:20171029233236p:plain

まとめ

JenkinsからKuroko2への移行することで、JOBサーバを柔軟に増減することが可能となりました。今後は、ECSやスポットフリートを利用して、よりJOBの効率化・コスト削減を実施していきたいと思います。

LCLではエンジニア・インターンを積極的に募集中です。興味のある方は採用ホームページからお気軽に、ご連絡よろしくお願いいたします。

iOSDC Japan 2017 day1 レポート

前夜祭を含んだ3日間で開催されるiOSの国内カンファレンス、iOSDCに参加してきました。

今回はその1日目(9/16)のレポートです。

今年のiOSDCは最大4つのメインセッションが並行で行われるということで、

前日から公開されていたタイムテーブルを見ながら、当日参加する予定を立てました。

タイムテーブル | iOSDC Japan 2017

レポート

オープニングは10時でしたが、急遽私用ができてしまい午後からの参加となりました。

ランチ前の時間に到着したのですが、ボランティアスタッフの方々が 終日受付対応をしてくださっていたので、スムーズに入場することができました。

ノベルティ

ノベルティは トートバッグ一杯に入っていました。 チケットのグレードが一段階上の「個人スポンサー」の方には更に扇子、ハンドタオル、 パーカー、チートシート下敷きがあったそうです。

f:id:lcl-engineer:20171031113640j:plain

日常的に使えるグッズばかりで助かります。

ランチ

iOSDCさんのツイート: “【ランチ手順】食べたいサンドイッチを選ぶよ!(3種類!)#iosdc https://t.co/W2abjyyjXJ”

サンドイッチ、サラダ、飲み物がそれぞれ複数種類用意されていました。 自分はタンドリーチキンのサンドとチリコンカンを選びましたが程良いピリ辛感でおいしかったです。

Build high performance and maintainable UI library

13:30~14:00 / Track A / Realm

Building High Performance and Testable UI component // Speaker Deck

要点

  • 高速なUIを作るには「計測」と「トレードオフ」が不可欠
    • 計測
      • 負荷の高い処理を減らす(原因を知る、速度を上げるテクニックを学ぶ)
    • トレードオフ
      • パフォーマンスを上げるに辺り、犠牲は付き物である。伴うトレードオフを知る。
      • 例:パフォーマンスを上げるために、コードが複雑になる
  • メンテナンスしやすい = テストしやすい
    • UIコンポーネントをテストするのは難しい
      • 内部状態が多く複雑 / 状態を変化させる要因が多い / 状態が相互作用を及ぼす / 正しい挙動が明確でない
    • Unit Testing vs UI Testing
      • UIテストはテスト対象に直接アクセスできない
      • 成功・失敗の基準が明確でない
    • テストの段階は大きく4つにわけられる
      • 内部状態、パラメータ、関数、出力結果
      • マッシブなUI(ViewController)は、各段階でイレギュラーで明確でない要素が複雑に絡み合っている
    • テストしやすい構造とは
      • データの流れを1方向にする
      • 状態をモデルに分離する
    • テストしやすい構造にすることで振る舞いをモックに置き換えれる
    • 薄いビュー(コントローラー)、分厚いモデル
  • Q/A
    • View側でイレギュラーなアクションが発生する場合は網羅的なテスト書くことを避けられない(使い分けが必要)

感想

弊社のiOSプロジェクトの「高速バス比較 – 国内の路線と最安値を検索するアプリ」でも 現在、安定性やパフォーマンスの向上を図るためにリファクタリングやUnit Test、UI Testの導入を行っています。

設計から見直したリファクタリングを経て、マッシブなVCから責務を細かく分割することができつつありますが、 テストとの兼ね合いを考えると構造が最適化できていない箇所もあるため、改めて見直したいと思いました。

モバイルアプリで困らないエラーハンドリングとロギングのベストプラクティス

14:20~14:35 / Track C / Mercari

モバイルアプリで困らないエラーハンドリングとロギングのベストプラクティス // Speaker Deck

エラーハンドリング、ロギングのやり方は多くの人が必要としている情報なのか、立ち見が出るほど人が集まっていた

要点

  • Crashlyticsで一番重要なこと
    • ログはクラッシュ後にアプリが再起動した時に送られる
    • application:didFinishLaunchingWithOptionsを無事に通過しないとダメ
  • application:didFinishLaunchingWithOptionsでしそうなこと
    • 各種フレームワークの初期化 / DBのマイグレーション / キャッシュのクリア
      • メインスレッドを長時間止めない
  • Crashlyticsには便利な付加情報がある
    • UserID、Email、名前などが追加可能
    • Key/Valueを追加可能(制限あり)
    • クラッシュしなくてもログを送ることが可能
  • iOS10から使えるようになった OSLog と組み合わせて自作ロガーを作る
    • コードはスライド参照
  • guard文のアンラップ時など、ありえない想定でもログレポートを吐かせるのが大事
    • 例えば、チームメンバーが変更した際など 将来的に発生する可能性もある

感想

標準のレポートに加えてログを渡す方法など参考になりました。

「ありえない想定でもログレポートを吐かせる」 、 これができていなかったので早い段階で各所に対応しようと思います。

短期間でやり遂げるための、大規模リニューアルの進め方

15:10~15:40 / Track A / Retty

短期間でやり遂げるための、 大規模リニューアルの進め方 // Speaker Deck

要点

  • 何故リニューアルをするか
    • 成長 / ピポット / 陳腐化 / リファクタリング / セキュリティ
  • どのようにリニューアルを進めるか
    • 徐々に / 一気に長期間 / 一気に短期間 / プロダクトを分けて
    • 各メリデメはスライドを参照
  • リニューアルあるある
    • 膨らむ仕様 / 足りない人手 / あとで増やされる人手 / 見積もりの不正確さ
      • 1日8時間コードを書けると思っている
      • 見えないタスクがあることを考慮しない
      • 仕様決定が遅れても工数調整しない
  • スコープを明確にする
    • 新規のはできるだけ増やさない
    • やらないを明確に
    • 決める人を明確に
  • Design Doc
    • プロダクト全体の仕様書のようなもの
    • 目次はスライドを参照
    • 今回のリニューアルに向けて、これを作るのに3ヶ月掛かった

感想

リニューアルあるあるについては、普段の業務の中でも気をつけたいことだと思いました。 特に 「1日8時間コードを書けると思っている」 は、普段から気をつけていても ふとした時に上のように単純計算をしてしまい、見積もりが合わないことが自分にもたまにあります。

Design Docについては、今後もし大きめのリニューアル(もしくは新規プロジェクト)に関わる機会があったら 実際に試してみようと思いました。

ディープリンクの設計と実装

16:00~16:30 / Track A / 一休.com(一休レストラン)

ディープリンクの設計と実装 - iOSDC2017 // Speaker Deck

要点

  • どういう用途で使うか
    • Webやメールからアプリ内の特定のページを開く
  • 他社アプリを開くには独自URLスキームをハードコーディングする必要がある
  • iOSでは似たような機能でUniversal Linksがある
    • Googleから開く
  • なぜディープリンクが求められるのか?
    • スライドを参照
  • 実装方法
  • アプリ内URLを設定すると便利なこと
    • PUSH通知からも開ける
    • アプリ内WebViewから開く画面を指定する
  • 悩むこと
    • タブバーを利用している場合どこから開くか
    • モーダルはユーザビリティがあまり良くない
    • ドリルダウンの再現(中間のページを考慮する)
    • 全画面に対応することはない
      • WebページのPVで判断
  • URLスキームの設計方法
    • path = host じゃないか(スライド参照)
  • Universal Linksお悩みポイント
    • Web側のPV計測が狂う
    • Webとアプリのビジュアルの統一性
      • 連続性を感じられるデザインにする
    • サブドメインで発動してしまう
  • Q/A
    • テストはルーターのみ行っている
      • それ以外は手動
  • その他
    • 自由なドリルダウンが発生するのは良くないのでdismissは遷移の事前にやっておいたほうが良さそう

感想

弊社のプロダクトもWebとiOS,Androidでディープリンクを採用可能なページがありますが、 まだアプリに一任するメリットが大きくないと考えているため、採用には至っていません。

しかし、どこかのタイミングで今後採用する可能性は大きくあるので その際はURLスキームの設計方法など活かしたいと思います。

LT

  • 第3の課金形態「寄付モデル」ってどうなの?
    • 寄付モデルにトライ&エラーした話
    • 当日の最優秀LT賞になっていた
  • 多次元宇宙と画面遷移
    • 宇宙の話からいつの間にか画面遷移について説明
  • iOSで利用できるデバイスファームのメリット・デメリットの紹介
    • デバイスファームの各サービス比較
  • ローカライズの苦しみに立ち向かう
    • デザインの段階で単位の書き方を控える(単数、複数、○位)などなど
  • クラス名に個人の名前を含めるとこうなる
    • 純粋に面白かった(盛り上がっていた)
    • Swiftを書いている内はクラスにプレフィックスを付ける文化には会わなさそう

終わりに

現在直面していることやこれから対応を考えていることについて、 詳しく話を聴くことができたのでとても参考になりました。

会場の環境もWi-Fi環境が用意されていたり、各所で案内板があったりと 快適且つ親切な環境でとても参加しやすかったです。

当日聴けなかったセッションについては、登壇者の方々がSpeaker Deckなどに スライドを公開してくださっているのでBlogやTwitterをチェックしていと思います。

パフォーマンス計測ツール:SpeedCurveの使い方

フロントエンドエンジニアの岡田です。今回は、パフォーマンス計測ツール:SpeedCurveをLCLでどのように使っているかお伝えします。 f:id:lcl-engineer:20170628054829p:plain

設定について

計測タイミング(Times)

計測タイミングは多いに越したことはないとは思いますが、コストとのバランスで、今のところ1日1〜2回計測をしています。 1日分のリリースが終わるタイミングとアクセスが増えるタイミングに計測をしています。

計測回数(Checks)

3回以上で任意の回数が設定できます。今のところ最低ラインの3チェックにしています。 3回チェックすることで、標準よりも早かったり遅かったりする結果が排除され、結果の中央値が選択されるそうです。 What is a check and how many do I need? | SpeedCurve Support

ブラウザ

LCLのサービスは、スマホユーザーが多いため、スマートフォン向けページのみ計測しています。(1URLをスマホ・PC等複数デバイスで計測することも可能です)
ブラウザは、Chromeのエミュレート環境でiPhone6のサイズで設定しています。 回線については、Newrelic Browser(RUM)と比べて値が近かった、4G設定で計測をしています。 Newrelicの結果では、実際には4Gよりもやや早いような印象でした。 f:id:lcl-engineer:20170619064716p:plain

料金プラン

Synthetic Pay-As-You-Go plan. にしています。

SpeedCurve | Pricing

1チェック $0.01で、月額$20〜 です。 1日1回計測 × 1計測3回チェック × 1ブラウザの場合、21ページまでなら$20です。(31日間の月の場合)

結果の見方について

Siteタブ

設定したタイミングで計測したデータが表示されます。 Start Render, SpeedIndex, Visually Complete の他、Backendの時間も確認できます。 f:id:lcl-engineer:20170628054829p:plain

グラフにはnoteを追加することができるので、パフォーマンスに関連しそうなリリースの履歴を入力しています。 noteはAPI経由でも追加できるので、設定すると便利かもしれません。

https://api.speedcurve.com/#add-a-note

BROWSER WATERFALL

ファイルの読み込まれる順番が確認できます。 f:id:lcl-engineer:20170619095505p:plain Chromeのデベロッパーツールなどでも確認はできますが、以下の点が見やすくて良いと思います。

  • レンダリングブロックしている要素がわかりやすい(斜線マークがブロッキング要素)
  • 要素をクリックすると詳細情報が表示される

参考 SpeedCurve | SpeedCurve Waterfall

他のツールとの連携

データベースへ保存

テスト結果は社内ツール用のデータベースにも保存しています。 以下のAPIを使ってデータを取得しています。
SpeedCurve v1 API

Re:dashで表示

データベースへ保存したデータは、Re:dashで表示しています。
以下のグラフは、複数ページの結果を表示したものです。 SpeedCurveでは表示できない形式ですが、このように重ねると、どのページが遅いのかがひと目でわかります。 f:id:lcl-engineer:20170628112059p:plain

chatworkへ通知

first byte, start render, speedindex, visually complete, Google PageSpeed Insightsスコア をchatworkへ通知しています。 通知を出すようにしてから、日々の変化に気づきやすくなり、また、担当者以外にも関心を持ってもらえるようになったことが良かったです。 f:id:lcl-engineer:20170628052734p:plain

以上です。 まだ使えていない機能もあるのですが、しばらくはこちらで計測を続けていこうと思います。