最新のログもすぐクエリできる速くて容量無限の最強ログ基盤をRedshift Spectrumで作る

こんにちは。去年の今頃は Rust を書いていました。 インフラストラクチャー部データ基盤グループの id:koba789 です。

背景

クックパッドではデータ基盤の DBMS として Amazon Redshift を利用しています。 既存のデータ基盤について詳しいことは クックパッドのデータ活用基盤 - クックパッド開発者ブログ を参照してください。

今まで、ログは数時間に1度、定期実行ジョブで Redshift 内のテーブルにロードしていました。 ロードジョブの実行間隔が "数時間" と長めなのは、Redshift のトランザクションのコミットが遅いためです。 クックパッドでは数百ものログテーブルがあるため、仮に1分おきにすべてを取り込もうとすると秒間数回以上のコミットを行わなければなりません。 このような頻繁なコミットは Redshift 全体のパフォーマンスを悪化させてしまいます。

サービスの開発者はリリースした新機能の様子をすぐに確認したいものです。 にもかかわらず、ログがクエリできるようになるまで、最大で数時間も待たなければなりませんでした。 最悪の場合、その日のうちに確認することは叶わず、翌朝まで待たされることもありました。 これではサービスの改善を鈍化させてしまいます。

また、問題はもう一つありました。 当然ですが、ログは日々単調増加し、ストレージの容量を消費し続けます。 一方で、Redshift のストレージサイズはノード数に対して固定です。 ノードを追加すればストレージを増やせますが、ノードには CPU やメモリも付いているため、ストレージが欲しいだけの場合は割高です。 すなわち、ログにとって、Redshift のストレージは高価だったのです。

求められているもの

絶え間なく流れ続けるログを遅延なくロードし続けられるログ基盤が必要でした。 もちろん、ロードされたデータはなるべくすぐにクエリ可能になるべきです。

その上で、クエリの実行速度を犠牲にすることはできません。 クエリが遅くなれば夜間のバッチジョブの実行時間に影響が出ます。 そのため、従来の Redshift 内のテーブルへのクエリと同等かそれ以上のクエリ速度が求められていました。

そして最後に、願わくばストレージが安くて無限にスケールすることを。

この要件だけ見ると BigQuery を使えばいいのではないかと思われるかもしれません。 確かに BigQuery なら上記の要件は満たせるかもしれません。 しかし、Redshift にある PostgreSQL 互換の接続インターフェイスや AWS IAM との連携などの機能は BigQuery にはないため、Redshift を使うことで既に達成できているその他の要件が満たせなくなってしまいます。 また、大量のデータをクラウドプロバイダ間で日々転送しつづけることによって発生する追加の転送料も問題になります。

以上のような理由から、BigQuery に乗り換えるだけでは我々の理想は達成されないと判断しました。

Redshift Spectrum

まずはじめに、最強のログ基盤の一翼を担っている大切なコンポーネントである Redshift Spectrum を紹介します。

Redshift Spectrum は Redshift の機能のひとつです。 Redshift Spectrum を用いると、Redshift 内から S3 に置かれたデータを直接クエリすることができます。 にもかかわらず、Redshift 内のテーブルと JOIN することもできます。 つまり、S3 に置かれたデータを通常のテーブルと同様に扱うことができるのです。

Redshift Spectrum で読めるテーブル(以下、外部テーブル)の内容は Redshift へではなく、S3 に直接書き込みます。 そのため、トランザクションによる保護はなく、同一トランザクション内であっても、複数回の読み取りはそれぞれ別の結果を返す可能性があります。

しかし、今回はこの特性を逆手にとり、頻繁なロードを可能にしました。 Redshift の内部テーブルへの書き込みと違い、S3 への書き込みならコミットのパフォーマンスに悩まされることはない、という算段です。

そしてみなさんご存知のとおり、S3 は容量が事実上無限にスケールして容量単価も安価なストレージですので、最後の願いが叶えられることは言うまでもありません。

高速なクエリのために

Redshift Spectrum を活用するにあたって、高速なクエリを実現するために実践せねばならないプラクティスがいくつかあります。 これらのプラクティスは AWS の開発者ガイドを参考にしています。

Amazon Redshift Spectrum クエリパフォーマンスの向上 - Amazon Redshift

まず1つ目はパーティション化です。

Redshift Spectrum ではパーティション化をしないとクエリのたびにテーブルの全データをスキャンすることになってしまいます。 今回のケースでは、ログレコードの発生時刻の日付でパーティションを切ることにしました。 パーティション化によって、データオブジェクトの key はスキーマ名・テーブル名のあとに、さらに日付で分割され、以下のようなレイアウトになります。

  • hoge_schema.nanika_table/
    • dt=2018-08-09/
      • 001.parquet
      • 002.parquet
      • ...
    • dt=2018-08-10/
      • 008.parquet
      • 009.parquet
      • ....

2つ目は列指向フォーマットの利用です。

Redshift Spectrum では、CSV や改行区切りの JSON などの一般的な行指向のテキストファイルもクエリすることができますが、クエリをより効率的かつ高速にするには、列指向フォーマットの利用が有効です。 列指向フォーマットにも様々な種類がありますが、今回のケースでは Parquet を採用しました。

3つ目は、各データオブジェクトのサイズを64MB以上の均等なサイズに揃えることです。

あまりに小さなデータオブジェクトは I/O オーバーヘッドの割合を増加させたり、Parquet の圧縮率を低下させたりします。 また、データオブジェクトのサイズの偏りは分散処理の効率性に悪影響を及ぼします。

上記の記事には、以上の3つの他に、効率的なクエリの書き方についてのプラクティスも紹介されていますが、ログのロードではそれらは関係ないため、ファイルの配置に関する内容のみを取り上げています。

Overview

f:id:koba789:20181121111431p:plain

Prism は Redshift Spectrum にログをロードするために私が開発したソフトウェアです。 Prism は3つのコンポーネントからなります。

1つ目は Prism Stream です。 これは S3 に到着した JSON 形式のログオブジェクトを Parquet に変換するコンポーネントです。

2つ目は Prism Merge です。 これは Prism Stream が書き出した細切れのデータオブジェクトを適切なサイズに結合(マージ)するコンポーネントです。

3つ目は Prism Catalog です。 日付変更後に当日分のパーティションを作成したり、階層の切り替え(後述)を行ったりするコンポーネントです。

弊社のコンテナ基盤である Hako を用いてスポットインスタンス上にデプロイされています。 スポットインスタンスの急な停止に耐えるため、上記のコンポーネントのすべての処理は、途中で突然終了してもデータの欠落や不整合が発生しないように設計されています。 Prism は RDB の読み書きのみならず、S3 への書き込みなども行います。 そのため、データの整合性については単に DBMS のトランザクションに委ねるというわけにはいかず、ケースバイケースでのケアが必要になります。*1

このようにすべての処理を冪等ないしはアトミックに実装したことの嬉しい副作用として、ネットワークや S3 の不調などによって偶発的にエラーが起きても、リトライするだけで回復できるという点があります。 もっとも、勝手にリトライするので人間がそれを気にすることは稀ですが。

また、S3 に配置したログオブジェクトや各パーティションのメタデータの DB として PostgreSQL を用いていますが、ログオブジェクトの数に比例して行が増えてしまうテーブルはローテート可能な設計にするなど、スケーラビリティのための工夫をしています。

階層化されたパーティション

各パーティションは prefix を用いて S3 上で論理的に階層化されています。 階層は SMALL と MERGED に分かれており、Glue カタログにはパーティションごとに SMALL では prefix が、MERGED ではマニフェストファイルの key が登録されています。 つまり、Redshift Spectrum が読めるのはパーティションごとに SMALL 階層か MERGED 階層のどちらか一方のみです。 当日(最新)のパーティションは初期状態で SMALL 階層を参照しており、マージ処理後、Prism Catalog によって順次 MERGED 階層を参照するように切り替わっていきます。

SMALL 階層のデータは分単位で細切れになっているため、オブジェクトサイズが最適ではなかったり不揃いだったりしており、高速なクエリには向いていません。 一方 MERGED 階層のデータは適切なサイズに揃えられており、高速なクエリのためのプラクティスに沿っています。

これは、低レイテンシなロードが必要になる場面と、高速なクエリが必要になる場面の違いを踏まえた設計です。

  • 直近(1日程度)のデータについては低レイテンシである必要があるが、クエリは低速でよい
    • "直近のデータ" である時点で対象が小さいため、クエリの速度は問題にならない
  • より古いデータについてはクエリは高速でなければならないが、ロードのレイテンシは高くてよい
    • 「今さっき届いた昨日分のログ」をすぐに見たいということは稀である

SMALL 階層のように細切れのオブジェクトを並べるだけではクエリが低速になってしまいますが、適切なサイズに粒を揃えるために単にバッファリングしてしまってはレイテンシが大きくなってしまいます。 これは低レイテンシなロードと高速なクエリを両立するための設計です。

データの流れと階層切り替え

では当日のパーティションに書き込まれた当日分のデータの流れを追ってみましょう。

まず、到着したログオブジェクトは Prism Stream によって Parquet に変換され SMALL 階層に書き込まれます。 この時点で、Redshift から読むことが可能になります。

その裏で、SMALL 階層に書き込まれたログオブジェクトは定期的に Prism Merge によってマージされ、マージ後のデータオブジェクトは MERGED 階層へ書き込まれます。 ただしまだこの時点では Redshift から MERGED 階層のデータを読むことはできません。

日付が変わったあと、Prism Catalog は SMALL 階層にある分のデータがすべて MERGED 階層に揃ったことを確認します。 確認が取れると、Prism Catalog は MERGED 階層にあるオブジェクトの一覧をマニフェストファイルに書き出し、Glue カタログにその key を登録することで、階層の切り替えをします。 この処理が走ることによって初めて、パーティションの参照先が MERGED 階層に切り替わります。 もしも MERGED 階層に SMALL 階層と同じだけのデータが揃う前に切り替えてしまうと、それまで読めていたデータが一部減少することになってしまいます。

Out-of-Order Data

当日に到着した、当日分のデータの流れについて説明しましたが、現実にはログは大幅に遅れて届くこともあります。

例として、モバイルアプリの行動ログの場合を説明します。 ユーザーが通信状況の悪い環境でアプリを操作したとします。 すると、ログレコード自体はその場で生成されますが、ログを送出することに失敗します。 クックパッドのモバイルアプリではログの送出に Puree というライブラリを使っており*2、上記のように送出に失敗したログは一旦端末に保管され、次の送出のチャンスを待ちます。 ここでユーザーがアプリを終了させ、翌日になってから通信状況の良好な環境で再度起動したとします。 通信状況が回復したため、アプリは前日の行動ログを再送します。 するとログ基盤には到着時刻に対して発生時刻が1日前になっているレコードが到着します。 ほかにも様々な理由により、ログのレコードはバラバラの順序で到着します。

そのため、日付が変わろうと当該日の SMALL 階層への書き込みが止むことはありません。 つまり、Prism Catalog が確認をしているその最中にも Prism Stream が新たなログオブジェクトを SMALL 階層に書き込むかもしれないということです。 これでは Prism Catalog が "SMALL 階層にある分のデータがすべて MERGED 階層に揃ったことを確認" することができません。

この問題を解決するため、Prism では「締め」という概念を導入し、SMALL 階層を「締め」前に書き込む LIVE 階層と「締め」後に書き込む DELAYED 階層に分割しました。 そして、SMALL 階層のうち、Redshift Spectrum が読み取り可能な部分は LIVE 階層のみとしました。

  • hoge_schema.nanika_table/
    • live/ (LIVE 階層)
      • dt=2018-08-09/
        • 001.parquet
        • 002.parquet
        • ...
    • deleyed/ (DELAYED 階層)
      • dt=2018-08-09/
        • 010.parquet
        • 011.parquet
        • ...
    • merged/ (MERGED 階層)
      • dt=2018-08-09/
        • 001-007.parquet
        • 008-013.parquet
        • ...

「締め」の後では LIVE 階層のデータが増えないことが保証されるため、Prism Catalog が階層切り替えの判断をする際には、LIVE 階層のデータがすべて MERGED 階層に揃っているかどうかを安心して確認することができます。 LIVE 階層のデータが MERGED 階層に揃った後では、どのタイミングで切り替えてもデータの減少は起きませんので、LIVE 階層と MERGED 階層の比較だけもって階層切り替えをすることができます。

まとめ

最強のログ基盤を手に入れるために開発したソフトウェアと、その設計についてご紹介しました。 Redshift Spectrum のようなクラウドサービスはとても大きくて複雑なコンポーネントですが、それ自体の理解はもちろんのこと、自分たちの課題をじっと見つめ、ひとつひとつ丁寧にトレードオフを選択していくことで強力な武器となります。

Prism の各処理をいかにリトライ可能にしたかなど、まだまだ 自慢 解説したい内容は尽きないのですが、それについて書き始めるといつまで経っても本記事を公開できそうになかったため、ここで筆を置かせていただきました。

クックパッドでは絶対ジョブをリトライ可能にしたいエンジニアやデカいデータなんとかしたいエンジニアを募集しています。 Prism の設計や実装について興味があるという方はぜひともご応募ください。

*1:基本方針としては、なんらかアトミックな値の書き換えによってコミットとすることで Atomicity を作り込むというパターンですが、ここに記すには余白が狭すぎる

*2:良い感じにログを収集するライブラリ、Puree-Swiftをリリースしました - クックパッド開発者ブログ

Android版クックパッドアプリで採用している技術の現状確認 2018年版

目次

はじめに

技術部の門田( @_litmon_ )です。

Android版クックパッドアプリで採用している技術の現状確認 2015年版 から3年、Androidアプリ開発を取り巻く環境も大きく変わってきました。 本エントリでは、以前のエントリからこれまでにAndroid版クックパッドアプリにあった技術選択の推移や、現在の状況を記していきます。

技術選択に関する基本的な方針などは変わっていないので、前回のエントリ( Android版クックパッドアプリで採用している技術の現状確認 2015年版 )を参照ください。

技術選択の各論

開発環境

現在のクックパッドアプリの開発環境は以下のようになっています。

  • Android Gradle Plugin 3.2.0
  • Gradle 4.10
  • targetSdkVersion 28
  • compileSdkVersion 28
  • minSdkVersion 21
  • support library version 28.0.0 (AndroidX未対応)
  • 使用言語: Java, Kotlin 1.2.60

ここ一年で大きく変化したことといえば、やはり targetSdkVersion と minSdkVersion 、そしてKotlinの採用でしょう。 それぞれに関して1つずつ振り返っていきたいと思います。

targetSdkVersion

targetSdkVersionに関しては、Googleが昨年12月頃にtargetSdkVersion 26以下のアプリは今後リリースもしくはアップデートできなくなるという発表をしたことで、無理矢理にでも上げざるを得ない状況が各アプリにあったと思います。 クックパッドアプリも例に漏れず、今年の頭はまだtargetSdkVersion 23だったアプリが、今年の9月にようやく26を満たすことが出来るようになりました。

targetSdkVersionをこれだけ長い時間かけて上げていったのには理由がありました。

まず1つが、targetSdkVersion 24で入ったIntent, Bundleに含められるデータサイズ制限です。 クックパッドアプリは大量のデータをActivityのIntentに載せて画面遷移を行っていたり、savedInstanceStateに保持していたため、targetSdkVersionを24に上げた状態でAPI 24以上の端末でアプリを使用すると、画面遷移を行った瞬間にクラッシュするという事態が発生していました。 ここを直すために色々と手を尽くして、最終的に以下の対応をすることで大きな問題を起こすことなくアップデートすることが出来ました。(実際にはいくつか作業ミスもありバグ報告も来ていたが、すぐに収束した)

  • Activity遷移の際のデータ保持は可能な限り小さくなるよう、大きな箇所を洗い出して修正
  • savedInstanceStateのデータ保持はAndroid Architecture Component ViewModelを使うように変更

そしてもう1つが、targetSdkVersion 26で入ったバックグラウンド制限です。 クックパッドアプリでは、料理きろくという機能の中でJobSchedulerを使ってバックグラウンド処理を行っており、その他には特にバックグラウンド制限に引っかかるようなものはない、と作業当初は思っていたのですが、実はGoogle Cloud Messagingの実装が古く、BroadcastReceiver内でServiceを起動しており、見事にバックグラウンド制限に引っかかっていました。 また、Google Cloud Messagingも2019年4月までにはFirebase Cloud Messagingに置き換える必要があったため、これを機にFirebase Cloud Messagingへの移行を行いました。

ここでもまた、アプリ上で使っているFirebaseプロジェクトが現在GCMで使っているプロジェクトと一致していない、という問題もあったのですが、GCMは2019年4月以降プッシュ通知を送れない状況であることと、そのプロジェクトはGCM以外の用途では使っていなかったこと、またプッシュ通知を送信する基盤実装への修正も容易であったことを踏まえて、廃止されるまでは両方のプロジェクトにプッシュ通知を送信する、という対応で事なきを得ました。

さらに、ローカルプッシュ通知の実装も、AlarmManagerからBroadcastReceiverを経由してServiceを起動する流れになっており、こちらもバックグラウンド制限に引っかかっていました。こちらはJobSchedulerを使った実装に変更することで対応を行い、これらの作業によって無事にtargetSdkVersion 26に上げることが出来ました。

targetSdkVersion 27, 28に関しては、上で挙げたような大きな障害はなく、すんなりと上がって今は最新の環境で開発が行えている状況です。

minSdkVersion

minSdkVersionに関しては、以前ブログでも紹介があった通り、現在は21となっています。詳細は以下の記事を参照ください。 Androidアプリ の minSdkVersion を21にした話

これによって、開発環境のアップデートに対する作業がぐっとやりやすくなりました。 正直、この1年でここまでのアップデートを行えたのはこの取り組みがあったおかげだと思っています。

Kotlinの導入

Kotlinの導入に関しては、2017年のGoogle I/Oで公式にサポートすると発表した直後から導入したいというのは話していたのですが、クックパッドアプリに関しては導入するのにだいぶ時間がかかってしまいました。

その頃のクックパッドアプリの最大の問題として「どこになにを書けばいいかが分からない」というものがあり、長い間続いているプロジェクトでアーキテクチャなどもうまく導入できておらず、無法地帯なコードベースがあったため、この状態のままKotlinを導入しても問題が増えるだけだと感じていました。 そのため、どこからKotlinを導入するか、どういう風に進めるかを基盤チームで議論した結果、VIPERアーキテクチャを導入し各画面の実装をアーキテクチャに対応させながらKotlinを導入していくことが決まりました。

その間も、他のプロジェクトや社内で使うライブラリにはKotlinを使うことに特に制限はしていなかったため、Kotlin 100%のプロジェクトも中には存在します。 また、導入の際にはStyleGuideを定めたり、社内でKotlin勉強会を開いたりして知見を共有し合ったりしていました。

方針が決まってからしばらく別の施策によって手が止まっていたのですが、クックパッドアプリでも今年の3月頃から導入を開始していて、今ではプロジェクトの約20%のコードがKotlinに置き換わっています。

f:id:litmon:20181115172928p:plain f:id:litmon:20181115172946p:plain

Swiftに比べてまだまだ手を付けられていない状況ですが、手を付けられるところから少しずつKotlinコードへの変換とアーキテクチャの適用を行っています。

HTTP Client

以前の記事時点では、Cookpad APIとの通信にはVolleyを使用していました。 ですが、以前の記事でも述べていたとおり、Volley内部で使用されているApache HTTP ClientがDeprecatedになっていたため、昨年メインとなるCookpad APIとの通信層に使用するコアライブラリをOkHttp3に置き換えました。

Retrofitなどのラッパーライブラリを使うかは検討しましたが、Cookpad APIはGarageで作られているため、Garageのリクエスト方式に沿ったものがあったほうが良いだろうという判断をして、社内ライブラリとしてgarage-client-androidを作成して使っています。

以降の展望としては、API側の変化を伴うものになっていくのではないかなと予想しています。 現在の問題点として、Kotlin, SwiftなどのNull安全な型制約を持った言語とRESTFullなAPIは相性が悪く、返ってくる値が Nullable であるかどうかが分かりにくく、開発効率も下がるしバグが発生しやすいというものがあります。 また、モバイルアプリでは、一画面で複数のリソースが必要になることが多く、RESTFullなAPIだと複数のAPI通信を行う必要がある場合が発生し、扱いが難しいところがあります。

これを解決するために、社内の一部新規サービスではGraphQLを使用したAPI通信を始めていたり、gRPCやSwaggerなどの型制約を解決できるような仕組みを使えないか検討し始めていたりします。

Dependency Injection

以前の記事時点ではRoboGuiceを使用していました。 ですが、RoboGuiceは2016年8月の時点でサポートが終了し、別のDIライブラリへの移行を余儀なくされる形となっていました。 前回の記事でも言及していたとおり、社内ではDIライブラリを使うべきかどうかという議論がずっと繰り返されており、Kotlinを導入した際に同時に導入したVIPERアーキテクチャではDIライブラリを使用しない形で進めていこうという話もしていました。

しかし、現段階で使っている箇所に関しては簡単に剥がすことも出来ないため、2018年4月からは実装も薄くてRoboGuiceからの移行が比較的簡単だったToothpickというDIライブラリを導入しています。 導入しようとしていた4月時点ではまだproguardへの対応が行われている最中だったため、少し時期尚早だったかなと思っていたのですが、いまのところ大きな問題もなく運用出来ています。

ちなみに、クックパッドで最近リリースされたcookpadTVアプリに関しては、DIライブラリにはDagger2を使用しています。このあたりの技術選定は基本的に各サービスごとのエンジニアに任せるようにしています。

cookpadTV -クッキングLIVEアプリ-

Image Loader

画像読み込みには、以前はPicassoを使用していましたが、こちらも長らく開発が滞っていたため、昨年Glideに移行しました。 導入段階ではFacebook製のfrescoも検討していましたが、こちらは画像読み込みの際のインターフェースがPicassoと大きく離れていたり、画像のサイズを予め知っておく必要があったりと、移行作業に難ありだったため見送りました。

PicassoからGlideへの移行に関しては、インターフェースも非常に似通っているためほとんど技術的な問題は起きませんでしたが、リリース後「画像が読み込めない」というお問い合わせが多数寄せられ、とても頭を悩ませたことは記憶に新しいです。(結果的に、通信レイヤーで使用していたOkHttp3のバグだったことが判明し、OkHttp3のアップデートを行うことで解決しました)

Debugging

デバッグツールには、StethoHyperion-Androidを導入しています。

Sthethoは、通信層のコアライブラリをOkHttp3に置き換えたことにより、プロキシを介さずにアプリの通信履歴を見ることが出来て、非常に重宝しています。

また、Hyperionは、SharedPreferencesに保存されているデータの中身を見たり、View構造を探索したりと、非常に多機能で優秀な上に、拡張してデバッグ用のメニューを追加することも出来ます。 今までは、クックパッドアプリのサイドメニュー下にデバッグ版のみ表示されるツールメニューを用意していたのですが、Hyperion導入後はHyperionのメニューとしてデバッグメニューをまとめることが出来るようになり、デバッグ用の機能を管理するのがとても楽になりました。

Android Emulator on Jenkins

Jenkins CI上でのAndroidエミュレータの扱いに関してもここ数年で大きく動きがありました。 弊社のCI環境は、Amazon EC2インスタンス上に構築されたJenkinsを使用しており、以前はその上にARMエミュレータを起動してCI上でのInstrumentation Testの実行に使用していました。 しかし、やはり起動時間や実行時間がネックとなり、それらの改善を行うためにこれまで様々な取り組みをしてきました。各取り組みに関してはそれぞれブログがまとまっているので、そちらを御覧ください。

現在では、AndroidエミュレータはGenymotion Cloud(旧Genymotion On Demand)を使用していますが、上記記事内でも言及している通りGenymotion CloudではGoogle Playの機能を使うことが出来ません。 これに対して、クックパッドアプリでは新たにいくつかの方法を検討中です。例えば、Firebase TestLabを使ったInstrumentation Testの実行や、上記記事でも挙げたようなグローバルチームでいち早く導入されているAWS Bare Metalインスタンスを使用したAndroidエミュレータの構築、またはFirebase, AWSなどのデバイスファームの使用などが挙げられます。

CI環境に関してもその時々で要求されるものは移り変わっていくので、色々な選択肢を試しながら技術選択を行っています。

コードレビューbot

社内では、GitHub Enterprise上でのPullRequest駆動での開発が盛んですが、その中でもコードレビューを行う際にLintやfindbugsなどの静的解析ツールによる指摘を自動化しています。

以前は、社内で開発していたdokumiというツールを使っていたのですが、 dangerというオープンソースのツールに乗り換えました。 dokumiの設定はdokumi本体に含める必要があり、各プロジェクトの設定がツールに含まれる形になってしまい、ツールとプロジェクトの関係が密になりすぎるという問題がありました。

その点、dangerは各プロジェクトごとにRubyで設定ファイルを記述することで動作するし、プラグインを作成するのも容易だったため、複数のプロジェクトに簡単に導入できるという点が乗り換えたポイントでした。また個人的に、dokumiと違ってdangerのコメントは上書きされていくので、PullRequest上に指摘コメントが積み重なっていき読みづらくなることが減るところが気に入っています。

danger導入時に関する話は以下のブログ記事で詳細に書かれているため、よろしければ読んでみてください。 Android開発のコードレビューbotを乗り換えた話

リリースエンジニアリング

以前から、リリースに伴う作業を手作業ではなく機械によって自動化するために、fastlane/supplyを使ってリリース関連の作業を自動化しています。

弊社のCI環境はJenkinsなので、Jenkinsのgoogle-play-android-publisher-pluginを使用する選択や、Gradleプラグインのgradle-play-publisherなどを使用する選択も取れたのですが、その中でもfastlaneを使用している理由は以下の点が大きいかなと思っています。

  • fastlaneはiOSアプリでも積極的に使用されていて、内製のプラグインも開発されているなど、iOSとの仕組み共通化のためにも使える
  • Gradleプラグインとして採用するとビルドに影響が出てしまうので、ビルドとリリースフローは分離したい
  • Ruby製のツールなので、社内の開発リソースと一致しやすい(※個人的な意見です)
  • Groovy書きたくない(※あくまでも個人的な意見です)

最近では、fastlane/supplyを利用してアプリの自動リリースを行い、リリースフローの機械化・自動化も進めています。こちらに関しては、iOSでの取り組みが先行しているため、以下のブログ記事を参照ください。

クックパッドアプリはみんなが寝ている間にサブミットされる

また、来たる2019年2月のDroidKaigi 2019にて、「Google Play Consoleのリリーストラックを有効活用してリリースフローの最適化を行った話」というセッションが採択されたため、そこで詳細に話す予定です。ご興味のある方は足を運んでいただけると幸いです。

おわりに

いかがでしたでしょうか。思いつく限りの最近のクックパッドアプリの開発事情について書き記してみました。

近年のクックパッドアプリは、目新しいものを導入したというよりは今まで使っていたものがどんどんと使えなくなっていったためアップデートしていったという話が主になっていることがわかります。 とはいえ、新しい技術を試していないわけではなく、使えるものは積極的に取り入れていくし、色々な技術を試すための環境は充分に用意されています。

また、今回はクックパッドアプリの開発事情について書きましたが、最近は新規サービスもどんどんと生まれてきており、複数のアプリに対する仕組みの共通化なども積極的に行っています。

これからもいろんな技術を駆使してユーザーさんにすばやく価値を届けられるように改善を続けていくので、一緒に高まっていきたい人はぜひぜひお声がけください!

【開催レポ】Cookpad Tech Kitchen #19 R&Dにおけるサービス開発者の仕事

こんにちは。広報部のとくなり餃子大好き( id:tokunarigyozadaisuki )です。

2018年11月1日に、Cookpad Tech Kitchen #19 R&Dにおけるサービス開発者の仕事を開催いたしました。クックパッドでは、Cookpad Tech Kitchenを通して、技術やサービス開発に関する知見を定期的に発信しています。

f:id:tokunarigyozadaisuki:20181118162207j:plain
部長の原島が司会を務めました

第19回は弊社の研究開発部から5名が登壇致しました。研究開発部ではサービス開発に積極的に取り組んでおり、今回は機械学習、スマートスピーカーの開発、他社を巻き込んだプラットフォームの開発などについてお話しました。 本ブログを通して当日の様子をご来場いただけなかったみなさまにもお届けしたいと思います。

発表プログラム

「スマートスピーカー向けサービス開発者のお仕事」

はじめにお話いたしましたのは、2016年新卒としてクックパッドに入社し、研究開発部 スマートキッチングループでスマートスピーカー向けのサービス開発を行う山田です。

f:id:tokunarigyozadaisuki:20181118161809j:plain

Alexaスキル「クックパッド」は「アレクサ、クックパッドで大根のレシピを教えて」のように話しかけるだけで、「ぶり大根、おでん、大根ステーキ」のような食べ方からレシピを提案するスキルです。山田からは「アレクサごっこ」と呼んでいる一人がアレクサ役、もうひとりが利用者となり、会話をしながら開発を進めていったプロトタイピングなどをご紹介しました。

「クックパッドのスマートキッチン開発」

同じく、スマートキッチングループに所属する伊尾木からは、クックパッドが考えるスマートキッチンについてや、キッチン家電向けにクックパッドのレシピを提供するスマートキッチンサービス『OiCy』に関してお話をさせていただきました。

f:id:tokunarigyozadaisuki:20181118161633j:plain

OiCyは、クックパッドに投稿されたレシピを機器が読み取り可能な形式(MRR: Machine Readable Recipe)に変換して機器に提供するサービスです。クックパッドはレシピとキッチン家電が連携することで、料理をする人の悩みや負担が軽減され、毎日の料理が楽しくなる、そんなスマートキッチンを目指して日々開発をしております。

MRR化についてはこちらでも紹介しておりますので、ご覧ください。

「クックパッドにおけるCloud AutoML事例」

3番目の発表は、ソフトウェアエンジニアで機械学習グループに所属する林田です。 f:id:tokunarigyozadaisuki:20181118162222j:plain

料理が楽しくなるマルシェアプリ『Komerco-コメルコ-』という新規事業でAutoML使った事例の紹介させていただいた後、「これからのサービス開発における機械学習」と題して、機械学習エンジニアがいなくてもサービス開発の道具として機械学習をうまく使っていこう、という旨のお話をさせていただきました。

「機械学習を用いた見栄えのいい料理画像抽出をサービスに活かすための取り組み」

次に発表させていただきました、機械学習グループに所属する三條は、2018年新卒としてクックパッドに入社した一年目の社員!

f:id:tokunarigyozadaisuki:20181118162226j:plain

機械学習をサービスで活用するためにどう評価したかと、実験時とサービス時のモデル評価のギャップの解消について、実例を用いながらご紹介しました。

「クックパッドにおけるチャットボット開発 / Chatbot Development at Cookpad」

最後は、2017年にクックパッドに中途入社、機械学習グループに所属し、自然言語処理を用いてサービス開発に従事している Huy Van phu quangです。

f:id:tokunarigyozadaisuki:20181118161951j:plain

検索キーワードを考えるのを面倒に感じるユーザーや、新しいレシピに出会うことができないといった課題をどう発見していったか、チャットボットでサポートするためにどのような開発手法を取っているか(こちらでも「botごっこ」は大活躍!)サービスの再設計までのなどについてお話致しました。

付箋形式でお答えするQ&Aディスカッション

Cookpad Tech Kitchenでは参加者のみなさまからの質問を付箋で集めております。第20回では、各発表後の質疑応答の時間も含め、みなさまにたくさんのご質問をいただきました。ありがとうございました! 

シェフの手作り料理

イベントに参加してくださったみなさまにおもてなしの気持ちを込めて、シェフ手作りのごはんをご用意し、食べながら飲みながらカジュアルに発表を聞いていただけるように工夫しています。

f:id:tokunarigyozadaisuki:20181118162138j:plainf:id:tokunarigyozadaisuki:20181118162158j:plain

おわりに

クックパッドに研究開発部ができて2年が経ち、今回ご紹介したことの他にも様々な取り組みがありました。クックパッドにおける研究開発部の役割は「社内外の最新の研究成果にもとづくサービスの企画と開発」で、具体的には「食や料理、レシピに関する研究成果」です。これらのシーズとユーザーのニーズを紐付け、他部署と一緒にサービス開発に日々取り組んでいるところです。今後も様々な取り組みに挑戦すべく、一緒に取り組んでいただける方を募集しておりますので、ご興味がある方は採用ページを是非ご覧ください。ご応募をお待ちしています。

https://info.cookpad.com/careers

次回のCookpad Tech Kitchenは、11月28日(水)、クックパッドのマイクロサービスプラットフォームの現状 です! イベント情報についてはConnpassページについて随時更新予定です。イベント更新情報にご興味がある方は、ぜひメンバー登録をポチっとお願いします!

cookpad.connpass.com