こんにちは、エブリーでCTOをやっている梶原と申します。 1年の始まりということで昨年の振り返りと今年の取り組みについて書きます。 はじめに、エブリーが提供している中でエンジニアが関わっているプロダクトをご紹介すると、 食の課題解決に取り組むレシピ動画サービスの「DELISH KITCHEN」と育児の課題解決に取り組むママ・パパの365日に役立つ情報やサービスを提供する「MAMADAYS」の2つのプロダクトを開発しています。 2020年の振り返り 開発組織について まず自分自身の変化として、2020年の春くらいに当時見ていた事業を引き継いで開発業務に専念することになりました。そして開発組織については、当時、各事業部にぶら下がっていたエンジニアチームを、開発本部というエンジニア組織に統合することにしました。エンジニア全体での勉強会やエンジニアブログを開始したのも昨年です。結果、課題としてあったエンジニア間の連携の希薄化、エンジニアのキャリアサポートや適切なアサインメントは徐々に解消されつつありますが、まだまだこれからなので今年も課題の解消に取り組みます。 データに関する取り組み これまではDELISH KITCHEN事業部のみに存在したデータエンジニアリングの組織を全社の横断組織として再編し、組織のミッションとして「DIKWモデル」の構築を掲げました。 データ基盤からBIツールを用いて各種分析業務は行っていましたが、中にはExcelやスプレッドシートで独自に管理、集計、分析されていたり、手動データを打ち込んでいるもの、データの定義が扱う人により違うものなど、属人的な扱いのデータが様々あり非効率な部分が多くありました。 これらの課題に対してデータ基盤への集約と集計のとりまとめと実施を行い、結果としてデータによる意思決定が格段に進みました。 アーキテクチャの再設計 DELISH KITCHENアプリはリリースから4年が経過して、新しくジョインするエンジニアの初期の学習コストや開発スピードが徐々に低下しつつあり、先んじて対策を打つという意思決定を行いリファクタリングとリアーキテクチャリングを開始しました。 2021年に向けて データに関しては、集約のフェーズが終わり、次はデータの利活用のフェーズです。機械学習を用いたプロダクトの改善や事業者との取り組みをこれまで以上に推進していきます。プロダクト開発では、息の長いプロジェクトになるであろうリアーキテクチャプロジェクトを昨年に引き続き取り組んでいきます。 また今年は小売のデジタル化が更に進む1年になるであろうと考えています。これまでにもアプリ内のチラシ・クーポン機能による集客支援、スーパー内に設置したデジタルサイネージによる店内販促、ライブコマースによるオンライン購買活動支援など取り組んできました。今年はこれらのものをベースに小売様と協力して新しいユーザ体験を生み出すべくプロダクト開発をできたらと考えています。 2021年もよろしくお願いいたします!
DELISH KITCHEN iOSアプリ開発のCI環境について はじめに はじめまして。2020年4月にエブリーに新卒で入社した山口です。 iOSエンジニアとして入社後、DELISH KITCHENクライアントグループで、日々iOSアプリの改善や新機能開発の業務に関わっています。 さて、多くの方にご利用いただいているDELISH KITCHENのiOSアプリですが、日々の開発フローをできるだけ効率化させるためにCI環境を整備しています。 今回の記事では、実際にどんなCI環境が構築されているかをご紹介します。 構成 さっそくですが、こちらが全体図になります。 フローとしてはこんな感じで、Githubのタグをトリガーにして、テストやAppStoreにipaを提出するまで(実際にApple側に提出する一歩手前まで)を自動化しています。 次に、それぞれの処理内容をざっくりと紹介します。 Makefile まずは運用しているMakefileの中身について大まかに紹介します。 特別変わっているところはないのですが、DELISH KITCHENのiOSアプリ開発では、必要なコマンド類はMakefileに集約してあります。 またCocoaPodsやfastlaneなどgem経由でインストールするものもBundlerを使用していて、 make install のようなコマンドでインストールできるようになっています。 特にfastlaneは bundle exec fastlane xxxx を都度書かなくても良いように必要な部分は、MakefileでWrapされています。 すべて make xxxx で必要な処理の流れを呼び出せるので、CircleCIなどでそのままできますし、例えばPCが変わっても開発構築を頑張らなくて良いです。 Github このCI環境では動作の起点となります。 開発者が、特定のプレフィックス付きのタグを切ったときや、特定のブランチにマージされた場合に、それがトリガーとなって次のCircleCIが動きます。 実際の運用では、このような感じで使っています。 developブランチにマージされた時 特定のプレフィックスがついたタグを切った時 例えば、 enterprise.{VERSION} のような感じ CircleCI Githubのトリガーをもとに実行され、タグのプレフィックスなどによって、予め定義されているそれぞれのジョブが実行されます。 ここで簡単な環境変数や使用するXcodeのバージョンが設定され、予め定義されているSTEPごとに実行しています。 実際に運用している config.yml では、workflowが4つ(Githubのトリガー分)存在しています。 すべて内部的にはmakeコマンドを叩くような構成で、テストからApp DistributionやTestFlightへのアップロードまで自動化されています。 また、毎度gemやCocoaPods、Carthageをフルで走らせると無駄な時間がかかってしまうため、前のデータをキャッシュすることでインストールの高速化を図る工夫をしたりしています。 fastlane fastlaneのことは皆さんご存知と思いますので具体的な話は省略しますが、運用でどう使用されているかを紹介します。 DELISH KITCHENの開発環境でも、Matchを利用した証明書の管理から、バージョン上げ、ビルド、テスト、アップロードまで利用しています。 プラグインとしては、 - versioning - firebase_app_distribution を使用しています。 どちらも名前の通りで、アプリのビルド番号や、バージョン情報をfastlane側から上げることができるプラグインと、App Distributionにビルドをアップロードすることができるプラグインです。 実際の運用では、デフォルトのAppStore Connect(TestFlight)へのアップロードと、プラグインを利用してApp Distributionへのアップロード。同時に、ビルド済みのアプリをGithubのリリースにアップロードするところまでをサポートしています。 このようにして、Githubでタグを切るところから実際にスマートフォン上で動ける環境になるまでを自動化させています。 まとめ 一度構築してしまえば、そのフローをすべて自動で走らせることが可能になるため、チーム開発や継続的なアップデートをするような環境では、開発の効率化にかなり貢献できます。また現在だと、fastlaneを始めとして、かなり有用なツールが揃っているので、CI環境を作りやすいように感じます。 この記事でDELISH KITCHENのiOSアプリ開発のCI環境を知っていただけましたら幸いです!
ansibleとterraformって何が違うんですか? ansibleとterraformって何が違うんですか? はじめに 作っただけでは動かない コンピューター ネットワーク ロードバランサー、DNS、TLS証明書……、etc 動かし続けるのはもっと大変 アプリケーションを作ることだけが仕事ではない ansibleとterraform IaC 自動化 均質化 明文化 履歴管理 IaCの2つのアプローチ 手続き型 宣言型 ansibleとterraform ansible terraform 使い分け おわりに はじめに 弊社ではインフラは主にAWS(一部GCP)を利用しており、構成管理にansibleとterraformを使用しています。 新しく入社された方や、これまでインフラをあまり触ってこなかった方がこれらに触れる際、本件表題のような質問をされることが何度かありました。 私はこのたぐいの質問をされるたびにそれぞれの成り立ち、歴史的背景、ソフトウェアとしての実装と振る舞いの違い、そして弊社での使われ方について説明をしてきました。 大抵はそれで納得してその後は疑問なく使い分けができるようになっていく(ように見える)のですが、やはり中には腑に落ちないようであったり、使い分けに悩まれる様子がみられる方もいます。 説明している最中、自分でも「この説明で適切なのだろうか」「そもそもなぜこれらが混同されやすいのだろうか」と疑問に思うことがよくあり、良い機会ですので今回文章としてまとめました。 この文章が表題の疑問の回答になり、今後の学習のお役に立てれば幸いです。 作っただけでは動かない ansibleやterraformの話をする前に、基礎の話から押さえておきましょう。 インフラエンジニアであったり、サービスの保守運用の経験がある方であれば「何を当たり前のことを」と思うかもしれませんが、 アプリケーションは作っただけでは動きません 。 なぜでしょうか?「アプリケーションは完璧に仕様を満たしています!動かせばいいだけじゃないか!」そう思われますか? そう、動かせばいいだけなのです! しかしながら、アプリケーションを動かすためには、アプリケーションの他にもたくさんのものが必要になります。 それらは、普段手元のパソコンで開発していると当たり前のように存在しているために気づかないものであったり、インターネットを通してたくさんの人にサービスを届けるために必要なものであったり、様々です。 ひとまずこの文章内では、これらのことをひとまとめに「リソース」と呼ぶことにしましょう。インフラとはリソースの集合であると言えます。 リソースという言葉は色々な場面で色々な意味合いで用いられます。 ここでいう「リソース」はこの文章内ではこのように名前をつけて呼ぶ、という程度の意味で捉えてください。 リソースの例をあげていけばきりがないのですが、いくつか代表的なものを取り上げましょう。 コンピューター あなたの手元にはコンピューターがありますか?いえ、答える必要はないですね。この文章を目にしているということは、そういうことですね。 当然のことですが、アプリケーションを動かすためにはコンピューターが必要です。 オンプレミスであれば機器を購入するかレンタルするかして、データセンターに配置しなければなりません。あるいはVPSを契約してもいいかもしれません。 もちろんAWSやGCPなどのクラウド環境で仮想コンピューターを用意しても良いでしょう。 ネットワーク あなたのコンピューターは何かしらのネットワークに繋がっていますか?いえ、答える必要はないですね。この文章を目にしているということは、そういうことですね。 インターネットを通じてサービスを提供するのであれば、何かしらインターネットへの接続性が必要になります。 あるいは、既存のネットワーク内に配置するのであれば、多少のネットワーク構成の変更が必要になるかもしれません。 また、接続性を確保することと同様に、不必要な接続を遮断する設定も重要です。 このノリももう飽きてきませんか?私は飽きました。ですので、あとはまとめていくつか紹介しましょう。 ロードバランサー、DNS、TLS証明書……、etc これらは必須というわけではありませんが、ほとんどの場合必要になるでしょう。 どれも購入・あるいは契約し、設定し、配置しなければなりません。 動かし続けるのはもっと大変 さてアプリケーションを動作させるために必要ないくつかのリソースを確保しました。アプリケーションは正しく動作し、目的を完全に達成しました。 素晴らしいことです!ではこれで全ては解決でしょうか? そうならばどれほど良かったでしょう……。 ほとんどの場合、アプリケーションは変更され続けます。あなたは今アプリケーションを変更しました。ではそれをどのようにすれば反映できますか? 変更前のアプリケーションは動いていて、以前と変わりなく使われています。これをダウンタイムなく新しいものに置き換えるためにはどのような手順が必要でしょうか? あるいは、あなたのアプリケーションは非常に人気が高く、多くのユーザーの要求を満たすために、100台のサーバで動いているかもしれません。 どのようにして100台のサーバに同時に、齟齬なく変更を反映させれば良いでしょうか? このようなシナリオはたくさん考えられます。もちろんありうる全てのシナリオを想定するのは不可能ですし、コストに見合いません。 しかしながら、日常的に発生するようなシナリオに対してはどのように対処するかを想定しておくべきでしょう。 アプリケーションを作ることだけが仕事ではない これまで紹介してきた通り、アプリケーションは作る場面だけでなく、実際に動作させる場面でもたくさんの考慮すべきこと、行うべきことがあります。 いうまでもなく、それは知識や経験、技術が必要な、専門的な作業です。 ansibleやterraformは、アプリケーションを動作させるために必要になるこれらの作業を錯誤なく、確実に、簡単に行えるようにするためのソフトウェアです。 ansibleとterraform ansibleやterraformはどのようにこのような作業の役に立つのでしょうか? それを説明するためにはまずInfrastructure as Code(以後IaCと省略)の概念を説明する必要があります。 なぜなら、ansibleとterraformはIaCを実現するためのツールだからです。 そもそも、Infrastructure(口語ではもっぱらインフラと省略されますので、この文章でも以降インフラと表記します)とは何でしょうか? 前項であげたリソースの例はどれもインフラを構成する要素ですが、他にもデータベースやミドルウェアなどを含む場合もあります。 あるいはCI環境などもインフラの一部だと捉える人もいるかもしれません。 はっきりとこういうものだ、という定義は難しいのですが、私は「アプリケーションを動作させるに足る環境」であると理解しています。 つまり サービス=アプリケーション+インフラ であると言えます。 IaC サービスを提供するためのインフラを準備する時、IaCを用いず従来通りの手法で行う場合、一般的にはドキュメントやチェックシートによって管理することになります。 小規模かつ変更の少ないインフラであればこのような手法でも十分に運用可能です。 しかし、インフラの規模が大きくなるにつれ、それぞれのリソースの相互作用が増え、構造が複雑になっていきます。 それだけでなく、管理するリソースが増えるということは、それだけ変更が必要になる頻度も上がります。 このような場合、ドキュメントやチェックシートによる管理は実際のリソースとの差異が生まれやすくなり、その差異を確認することも困難になっていきます。 この課題を解決するための手法がIaCです。 アプリケーションは何らかの表現形式で記述した コード によって表現されています。 IaCは、この考え方をインフラに応用した手法と言えます。 考え方としては単純で 「インフラを構築するためのアプリケーションを作り、それによってリソースの変更を人間の手ではなく、コンピューターにやらせよう。 インフラが正しく構築できるかどうかはそのアプリケーションのコードの正しさによって管理しよう」 というものです。 この手法によって、インフラ構築の自動化・均質化・明文化・履歴管理が可能になります。また、これらが可能になることにより再現性が非常に高く保てます。 自動化 従来手法では人間が手で行なっていた変更作業を、コンピュータが行うことで自動化が可能になります。 また、原則的には記述されたことと実際の差異が生まれないため、リソースの管理が容易になります。 均質化 従来手法ではドキュメントや実際の変更作業のクオリティ、読みやすさ・漏れ・ミスが担当の人間の経験に左右されていたのが、ある程度均質化されます。 もちろんコードであっても担当の人間によってクオリティは多少揺らぎは生まれますが、自然言語による文章表記よりもその幅は大きく減少します。 明文化 自然言語文章ではどうしても人間の解釈次第になってしまい、曖昧になってしまったり、当たり前に行うことなのでわざわざドキュメントに書かないというようなことがあります。 IaCでは書かれたコードの解釈も実行もコンピューターが行うため、曖昧さが排除され、インフラを構成するリソースのあるべき状態を明文化することが可能になります。 履歴管理 これは厳密にはIaCの特性というわけではないのですが、実際には改善することがほとんどです。 現代ではアプリケーションのコードは何かしらのバージョン管理システム(多くの場合gitでしょう)によって履歴管理が行われ、版ごとの差分や変更の意図などを記録することが可能になっています。 IaCではインフラを構成するリソースはコードによって表現されるので、従前より利用しているこのようなバージョン管理システムを利用することで、アプリケーションと同様に履歴を管理することが容易になります。 IaCの2つのアプローチ 現在、IaCには主に手続き型と宣言型の2つのアプローチがあります。 これはアプリケーションコードを記述する手法に手続き型・オブジェクト指向型・関数型のようにいくつかのアプローチがあるのと同じような状況です。 手続き型 手続き型はその名の通り、インフラを構築していく 手順 を記述していくアプローチです。 メリットは、単に手順をそのままコードに書き下していくような形になるため、最初の障壁が低く、初学者でも詰まりにくいことです。 また、コードも従来アプリケーション開発で用いられてきた汎用のプログラミング言語であることも多く、自由度が高いことも挙げられます。 デメリットは、冪等性の担保に対してのサポートが弱いことです。また、手順の積み重ねになるので、全体としての意図の見えにくくなる点や、大きな変更に弱いという点も挙げられます。 そのほか、自由度が高いが故にバグが入りうることもデメリットと言えるでしょう。 宣言型 宣言型はインフラを構成する リソースの状態 を記述していくアプローチです。現在の状態から宣言された状態への差分を自動的に埋めるように処理が行われます。 メリットは、最終的にリソースがあるべき状態を記述するため、バグが起こりにくく、冪等性も担保されやすい点や、全体としての意図が見えやすくなる点が挙げられます。 デメリットは、専用のDSLが用いられることが多いことや、手続き型より抽象的な記述になるため、最初の障壁が高いことが挙げられます。 また、手続き型よりも自由度が低いことが多く、アプリケーションコードを書きなれた人間からすると、フラストレーションを感じることが多いように思います。 ansibleとterraform さてこの時点までざっと5000字ほどあるのですが、ようやく本題です。みなさんこの文章の本題を覚えていますか? 私は忘れかけていました。なので改めて記載しますが、この文章は「ansibleとterraformって何が違うんですか?」という疑問の答えになることを目標に書かれています。 では具体的に何が違うのでしょうか? ここまでの文章を読んだのであればある程度想像がつくでしょうが、それぞれIaCを実現するアプローチが手続き型と宣言型であるという点が一番大きな差異であると言えます。 ansible ansibleは 手続き型 アプローチを採用しており、構成手順をplaybookと呼ばれるファイルにYAMLで記述するのが特徴です。 ansible自体はPythonで記述されており、プラグインもPythonで記述することができます。 類似の手続き型アプローチを採用したツールとして、chef、puppet、fabricなどがあります。これらの中でのansibleの特徴は、よりシンプルで扱いやすい点が挙げられます。 ただ、これらのツールでできることにはそれほど大きな違いはない印象です。各々の組織の要件にあったものを用いると良いでしょう terraform terraformは 宣言型 アプローチを採用しており、hclという独自言語でtfファイルにリソースを宣言するのが特徴です。 また、terraformはリソースの状態を管理するためにstateファイルというjson形式のファイルを用います。 このため、既存のリソースをterraformでの管理に切り替えるのには比較的手間がかかります。 リソースをterraformで管理したいのであれば、多少手間でも最初からterraformを使って定義することをお勧めします。 terraform自体はGoで記述されていますが、プラグイン(実際にはproviderやprovisionerと言います)はterraform本体のプロセスとgrpcで通信を行う別のプロセスとして実行されるため、各自好きな言語で記述することができます。 使い分け これまで述べてきたように、ansibleとterraformではIaCに対するアプローチが異なります。ansibleは 手順を自動化 し、terraformは 状態管理を自動化 します。 弊社では主にterraformをインフラの構築全般に使い、ansibleはサーバ単体の構築に用いています。 サーバ単体の構築は大抵の場合、リソース間の相互作用がそのサーバ単体の中で閉じられているため、全体像がそれほど複雑になりません。 また、サーバの中に配置したいものは一般的なミドルウェアであったり、あるいは自前で開発したバッチスクリプトであったり、多岐に渡るため、シェルスクリプトと一対一のように記述できるansibleが扱いやすく、採用の動機になります。 対して、VPCやセキュリティグループ、RDBなどはリソースごとに相互作用があり、どこかで定義した値を別の場所で使う、ということが多くあります。このようなシーンではterraformはうまく相互関係を解決してくれます。 また、稀にではありますがごく一時的に手作業でパラメータを変更してしまう場合もある(実際はこのような作業はあまりよくないですが、軽微な変更の検証など)ので、冪等性をツール側で担ってくれる点も採用理由の一つです。 弊社ではこのように使い分けをしていますが、近年はほとんどのサービスをECSかEKSの上に構築しており、サービスの管理はECSであれば ecs-deploy 、EKSであれば kustomize や helm を使っており、個々のコンテナはDockerfileで管理されているため、相対的にansibleの利用頻度は下がっています。 おわりに 以上、ansibleとterraformの違いについて、それぞれの特徴の説明と、弊社での使い分けについて解説をしました。 今回はそれぞれのツールの具体的な使い方やプラクティス、tipsなどの説明には踏み込めませんでしたが、いずれ機会があればそのあたりにも触れてみたいと思います。
DELISH KITCHEN WEBについて はじめに はじめまして。DELISH KITCHENバックエンドチームの梅木です。 DELISH KITCHENのバックエンドチームはアプリ向き合いとWEB向き合いのチームとで別れており、自分はWEB向き合いのチームに配属されています。 担当業務としては、DELISH KITCHENのWEBフロントの開発はもちろん、APIサーバーやインフラと、WEBサービスに関しての改修では境界を設けずに、開発・運用・監視を日々行なっています。 本日はDELISH KITCHENのWEBサービスのシステムについて、ご紹介できればと思います。 DELISH KITCHEN WEBで使われている技術スタック DELISH KICTEHEN WEBで使用されている技術スタックはこちらになります。 フロント Nuxt.js(Universal mode): 2.14 アプリケーションサーバー & BFF: express 4.x ランタイム: node 12.x コードフォーマッター: prettier / stylelint / eslint バリデーション: vee-validate 3.x テストフレームワーク: jest エラー監視: sentry ビデオプレイヤー: Videojs 状態管理: vuex ページ遷移: vue-router ページのメタ情報生成: vue-meta SSRレンダリング: vue-server-renderer APIサーバー Golang echo : 1.x インフラ AWS ECS AWS Route53 AWS Cognito AWS API Gateway AWS s3 AWS Lambda AWS CloudFront Elasticsearch ansible docker パッケージ依存関係解消ツール renovate ログ/分析 TreasureData redash 開発/運用ツール Github CircleCI Datadog terraform DELISH KITCHEN WEBでは、基本的にNuxt.jsのフレームワークのルールに従って開発しています。 vuex、vue-router、vue-metaやvue-server-rendererなどのライブラリも、最初からNuxt.jsの中に組み込まれているものを使っています。 Universal modeのNuxt.jsはBackend for Frontendとして機能するため、GolangのAPIサーバーとは別々で管理されており、Nuxt.jsアプリケーションだけを考えて開発を行うことができます。 Nuxt.jsとGolangのAPIサーバーをそれぞれdockerコンテナ化し、そのコンテナ化したマイクロサービスをAWS ECSでec2インスタンスにデプロイするという構成で運用しています。 他にも、メールアドレスログインでAWS Cognito、検索や絞り込みシステムでElasticsearch、レシピのサムネイルを縮小化しwebp拡張子で配信するシステムでAPI Gateway x Lambdaを組み合わせたサーバーレスアプリケーションを利用するなど、WEBチームでは、WEBフロント開発だけでは終わらずクラウドインフラを多く使い、裁量が広い範囲でWEBサービスの開発を行なっています。 Nuxt.jsを採用した理由 現在、DELISH KITCHEN WEBはNuxt.jsで構成されておりますが、Nuxt.jsで運用しているのは下記のような背景があります。 背景 DELISH KITCHEN WEBの1stリリースでは、 Riot.js というSPAライブラリで構築されていました。普通にブラウザで動かすSPAアプリケーションとしては何も問題なかったのですが、DELISH KITCHEN WEBはメディアサイトであるため、ユーザーだけではなくSEO対策としてクローラーのことも意識して開発する必要があります。クローラーがページにアクセスしたときは、ブラウザからアクセスされたときと同じjsが返却されるわけですが、当時のクローラーのレンダリングエンジンはjs解析にそこまで強くなく、クローラーにページの内容を読み取ってもらうことができませんでした。その為クローラーからのアクセスだった場合は静的なhtmlのページを返す必要があり、Express(nodejsのアプリケーションサーバー)がhtmlを生成してクローラーに読み取ってもらうという方針を取っていました。 問題 上記の背景により、Riot.js + Expressの構成でプロジェクト運用していましたが、ユーザー向けのブラウザで実行するコード(SPA用のコード)とクローラーに向けのコード(SSR用のコード)の2重管理をしていたので、特に新規ページや新規機能の開発、確認や運用コストが大きいという問題がありました。クローラーからのアクセスを考慮すると、クローラとユーザーごとに返却するHTMLを変えることが、SEO効果に悪い影響があるのか未知でした。そのような問題があるなか、事業側から新しい施策に打ち出したいと話があり、要件としてWEBサービスにメールアドレスログイン機能や課金機能が必要でした。コードが2重管理されているプロダクトでそのような大きい機能を無事に実装し運用できるかという不安がチーム内にありました。 他にも当時の構成について、下記のような意見もありました。 Riot.jsを本番運用しているプロダクトが少なく、開発で困ったときに参考にできる知見もあまりない。 当時のDELISH KITCHENは、ユーザーからのインタラクションでdomを表示/非表示の切り替えが発生する機会が少なく、リッチなアニメーションもないため、WEBサービス自体がブラウザでレンダリングさせるSPAアプリケーションで構成する必要がない。 ユーザーへはSPAである必要がなく、クローラーを考えるとSSRが必要 ということで、ユーザー側にもクローラーにも最初から同じコードからSSRで生成されたhtmlを返したほうがいいのではないかと考えました。大きな機能をWEBサービスに入れる前に、Universalアプリケーション(SSR + SPA)の開発ができる技術を使ってシステムリプレースをすることになりました。 検討 フレームワークレベルでUniversalアプリケーション開発が担保されている技術を検討したところ、Angular Universal / Next.js / Nuxt.jsの選択肢が出てきました。その選択肢の中からNuxt.jsを選びましたが、理由は下記となります。 当時のバックエンドチームには、バックエンドに強いメンバーがほとんどでwebフロント開発に長けているメンバーは多くなかったため、Nuxt.jsのような薄いフレームワークが取り掛かりやすいのではないかと考えた。 バックエンドチームのメンバー内でVue.jsの開発経験者が一名在籍していた。 Riot.jsのSFC(Single File Component)の作りが、Vue.jsでのコンポーネントと同じであるため、システムリプレースもしやすいのではないか。(template / script / styleとブロックを分ける構成がほぼ同じ。) 下はレシピのサムネイルとタイトルを表示するコンポーネントです。 Riot.jsのSFC <delish-recipe-item> <a href="/recipes/{ opts.recipe.id_str }"> <img riot-src="{ utils.resizeImg(opts.recipe.square_video.poster_url, opts.size) }" /> <div class="item__title-wrap"> <p class="item__title">{ getTitle() }</p> </div> </a> <style type="scss"> .item__title-wrap { text-align: left; flex: 1; margin-left: .75em; padding: 0 1em .5em 0; border-bottom: 1px solid @color5; } </style> <script> import utils from '../../misc/utils'; this.utils = utils; this.getTitle = () => { let str = this.opts.recipe.lead + this.opts.recipe.title; if (str.length < 29) { return str; } return str.slice(0, 27) + '...'; }; </script> </delish-recipe-item> Vue.jsのSFC <template> <div class="delish-recipe-item"> <nuxt-link :to="`/recipes/${recipe.id_str}`"> <img :src="utils.resizeImg(recipe.square_video.poster_url)" /> <div class="item__title-wrap"> <p class="item__title">{{ recipeTitle }}</p> </div> </nuxt-link> </div> </template> <script> import utils from '../../misc/utils'; export default { props: { recipe: { type: Object, required: true, } }, computed: { recipeTitle() { const str = this.recipe.lead + this.recipe.title; if (str.length < 29) { return str; } return str.slice(0, 27) + '...'; } } } </script> <style type="scss" scoped> .delish-recipe-item { .item__title-wrap { text-align: left; flex: 1; margin-left: .75em; padding: 0 1em .5em 0; border-bottom: 1px solid @color5; } } </style> 実際に移行してみてどうか? はじめに課題として上げた、ユーザ向けとクローラ向けのコード二重管理がなくなり、同じコンポーネントで一元管理できるようになったため、開発やQAのコスト削減を実現しました。 Nuxt.jsにリプレースしてから、SEO対策向けに多くの機能リリースを行ったことから、これらのコストを下げられたのは良かったと思っています。 Nuxt.js(Vue.js)の豊富なドキュメント、ライブラリや活発なコミュニティの恩恵を受けられ、開発して困る問題も大半は解決されました。多くの事例を参考にしながら開発をスムーズに行うことが出来ています。 追加で、webpackもNuxt.jsに組み込まれているため、ページ単位のjs分割やコンポーネントのdynamic importも可能となり、パフォーマンス対策もできています。 Nuxt.jsはフレームワークであり一定のルールに沿って開発するため、UIライブラリのRiot.jsと違い、チームメンバーの間で認識を揃えながら開発もできます。 最後に 今回はDELISH KITCHEN WEBで使われている技術スタックと弊社でNuxt.jsを採用した理由をまとめてみました。 DELISH KITCHEN バックエンドチームでは、WEBフロント開発だけではなく、APIサーバーやクラウドインフラの運用も行なっていきます。プロダクトを良くするのに必要であれば、自ら提案し新しい技術に触れられる環境です。 現在運用が安定してきた中、これからも新規機能をリリースしたり、テストやtypescript導入するなど開発改善を行うことも考えています。 このブログでDELISH KITCHEN WEBについて少しでも知っていただけたら幸いです。最後までお読みいただき、ありがとうございました。
自己紹介 はじめまして。DELISH KITCHENバックエンドチームのマネージャーをやっている内原です。 本日はDELISH KITCHENにおける、バックエンド観点でのシステム紹介を行います。この紹介によりDELISH KITCHENの開発に興味を持ってもらえると嬉しいです。 はじめに DELISH KITCHENのサービス全体像とバックエンドシステムの構成や仕様などを紹介します。 ご覧の通り、複数のマイクロサービスが様々なミドルウェアを利用しつつ、DELISH KITCHENサービスの提供を実現しています。 DELISH KITCHENのサービス全体像 DELISH KITCHENの一番主要な機能は、レシピ動画を提供することでお客様の料理体験をよりよいものにすることです。 これだけ聞くと、単に動画を配信しているだけのサービスのように思われるかもしれませんが、実際には料理にまつわる様々な事柄をサポートするシステムで、多くの機能を提供しています。 DELISH KITCHENを提供しているプラットフォーム 料理を作ろうと思った時や買い物をしている時、なんとなく流行りのレシピを知りたいと思ったときなど、色んなシチュエーションでご利用いただけるように、以下のプラットフォームでDELISH KITCHENを提供しています。 iOS/Android アプリ WEB 小売店でのデジタルサイネージ Amazon Echo DELISH KITCHENが提供しているサービス 単に料理を作る際に役立つだけではなく、特売情報をお知らせしたり買い物リストと連携することでお買い物をサポートします。 DELISH KITCHENが提供している主なサービスは以下の通りです。 人気、特集、新着、パーソナライズされたリコメンドなど、能動的なレシピ提案 カテゴリ内検索、キーワード検索など、受動的なレシピ提案 各地域の小売店特売情報と、特売商品を用いたレシピ提案 レシピのお気に入り管理 レシピのレビュー レシピ材料の買い物リスト管理 プレミアム会員向け機能やプレミアム会員限定レシピの提供 DELISH KITCHENポイントによるポイント交換、ポイントがもらえるクーポン機能 機種変更時や複数端末での同期用にメールアドレス/SNSによるログイン機構 プッシュ通知 DELISH KITCHENのバックエンドシステム全体像 複数のシステム構成 DELISH KITCHENは複数のマイクロサービスで構成されており、それぞれが別のシステムとして独立しています。 個々のシステムにおいては共通している部分も多いですが、提供している機能や使用しているサービスには差異があります。 共通の基盤/利用サービス DELISH KITCHENの各サービスで共通的に利用している基盤や外部サービスは以下の通りです。 Golang, echo インフラ AWS ECS AWS RDS AWS AutoScaling AWS ElastiCache AWS S3 AWS CloudFront AWS Route53 ログ/分析 fluentd redash TreasureData Databricks 開発/運用 github CircleCI terraform ansible 共通APIサーバ iOS/Android/WEB/サイネージなど、クライアント向けに提供しているAPIサーバです。お客様に対して提供される機能は、すべてこのAPIサーバを経由しています。 各種内部向けAPIサーバへの中継もここで行われます。 WEBシステム WEBにおけるDELISH KITCHENサービスを提供しています。 レシピなどのデータは共通APIサーバを参照しています。共通APIサーバと通信する構成であるため、BFF(Backends For Frontends)として稼働しています。 利用システム CloudFront Nuxt.js Node.js 検索システム レシピ情報の検索を提供するサーバです。 検索エンジンには Elasticsearch を利用しています。 特売情報システム 特売情報を提供するサーバです。 データとしては店舗、特売商品、チラシなどを扱っています。 別途、店舗側にて特売情報を入稿するためのtoC向けシステムも存在しますが、今回は省略します。 サイネージシステム 小売店舗に設置しているサイネージ端末が利用するサーバです。 個々のサイネージに対し、レシピ/広告を指定した配信設定が可能です。レシピ情報は共通APIサーバを参照しています。 別途、店舗側にて配信設定を管理するためのtoC向けシステムも存在しますが、今回は省略します。 ポイントシステム デリッシュポイントという、DELISH KITCHEN内でお得に使えるポイントを管理するサーバです。 ポイントを別商品に交換するには ドットマネー を利用しています。 クーポンシステム デリッシュポイントをもらうことができるクーポンサービスを提供するサーバです。 外部のクーポンサーバとやり取りをしています。 課金システム プレミアムサービスを利用可能とするため、ストア決済、キャリア決済を実現するサーバです。 プレミアムサービスはサブスクリプション形態であるため、アプリ外で購読状態を管理しています。 iOS In App Purchase Android Google Play Developer API (subscription) プッシュ通知システム アプリ向けにプッシュ通知を提供するサーバです。 サーバの実態としてはmercari社製の gaurun を利用しています。 最後に 上記のように、DELISH KITCHENは多くのシステムで構成されたサービスです。 開発、運用で使用しているインフラ技術、外部サービスも多岐に渡りますので、興味を持ってもらえると嬉しいです。 ここまでお読みいただき、ありがとうございました。
はじめに DELISH KITCHENでクライアントグループのマネージャーをやっている今井です。 クライアントチーム初のブログということで、DELISH KITCHENの Androidアプリの今の構成の話と、 アプリを作って4年が経とうとしてる中での変遷を紹介していきます。 なお、このブログは全体の構成の雰囲気を掴んでもらうことを目的にしておりますので、 細かい設計の話や採用技術の細かい特徴の紹介は割愛させていただきます。 それらはまた別の機会に紹介できればと思います。 現在の構成 ここでは現在のプロジェクトの構成とアプリで採用しているアーキテクチャの紹介をしていきます。 プロジェクトの構成 DELISH KITCHENのAndroidアプリではMultiModuleを採用しており、 大きく app core features の3種類のModuleに分かれています。 app いわゆるメインモジュールです。feature間をつなぐためのRouterの実装や、 ライブラリの初期化等を行っております。 歴史上の理由でここにもまだまだfeature要素が残ってしまっていますが、 徐々にfeatureモジュールに切り出しています。 core 各モジュールの共通で使う拡張関数やユーティリティクラスなどを入れています。 モジュール間の遷移に使ってるRouter等もここでInterfaceを定義しており、 実態はappモジュールで実装、DIで各モジュールに配布しています。 features 実際にfeaturesというモジュールがあるわけではなく、 機能ごとにfeatureモジュールとして分割しています。 各featureモジュールではcore以外のmoduleのimportを禁止しており、 強制的に参照不可にすることで依存をなるべく減らしています。 アーキテクチャ アーキテクチャとしてはMVVM+レイヤードアーキテクチャを採用しています。 図の通りですが、 Android Architecture Component (以下、AAC)を全面採用し、 MVVM+レイヤードアーキテクチャを構成しています。 ViewModelはそのままAACのViewModelを採用しており、 ViewModelからViewへのデータの公開はLiveData、 ViewからActionの受け付けはシンプルにメソッドコールにしています。 通信部分はKotlin Coroutineで繋いでいます。 この構成にいたるまでいろいろ変遷してきているので、 その一部を次で紹介します。 変遷 この4年間でKotlinがGoogleに正式にサポートされたり、AACやJetpackが登場したり Android界隈で大きくデファクトが変わってきました。 DELISH KITCHENのAndroidアプリではそれに対して比較的柔軟に採用技術を変更してきました。 その中で大きく変更があった3点を紹介します。 1. MonoModuleからMultiModuleへ コード量が増えてきてビルド時間が長くなってきたので検討し始めました。 副産物的な感じでしたが、参照をあえて出来なくすることで依存関係を意識せざるを得ない状況になり、チーム全体の依存に対する意識が向上しました。また、その事に頭を使わなくて良くなったのでとても良かったです。 分割し始めた当初はInstant AppやDynamic Feature Moduleも検討していました。 2. Android Architecture Componentの採用とRxの置き換え リリースから1年くらい経つと、機能が乗ってしまったいわゆるSuperActivity/Fragmentが出現し始めたので、 MVVMやFluxの導入を考えていたところAACが発表されたため即採用しました。 またAPI通信まわりは各レイヤーの繋ぎにとどまっていたRxをKotlinCoroutineとLiveDataに置き換えることで、 AACの恩恵をフルで受けれるような構成に変更しました。 3. Viewへのアクセス周りの変遷 ここが一番紆余曲折あったところです。 ざっくりと KotterKnife → Kotlin Android Extension → Databinding → ViewBinding と変更してきました。 初期はJavaの ButterKnife の流れで KotterKnife を採用してたものの、 型安全じゃないとかFragmentで使えないなどの理由から、Kotlin Android Extensionに移行しました。 その後すぐにnullableになってしまうのが辛くなったのと、xml上で値を設定できる便利さから DataBinding を採用しました。 DataBinding自体は全然問題なかったんですが、あまり双方向Bindingを生かす場面がなかったのと、 ロジックがxmlにも一部分散することでコードレビューが大変になったので、現在は ViewBinding を採用しています。 加えてViewBindingはDataBindingビルドが速かったり、型安全だったりと、しばらくはViewBindingで落ち着きそうです。 (おまけ) Dagger → Koin 人類にはDaggerは早かった(笑) っていうのもありますが、Daggerの機能をそんなに活かしきれていなかったのと、 誰でも比較的簡単にライブラリの挙動が理解できるように Koin に変更しました。 設計自体に影響が大きくあった訳ではないですが、コードの変更量が膨大で、 変更作業が一番辛かったのでおまけとして紹介させてください(笑)。 そんな中、今年出た DaggerHilt への変更を検討中だったりもします😇。 まとめ DELISH KITCHENのAndroidのアプリに関して少しでも知っていただけたら幸いです。 今回ご紹介した設計がベストだとは考えていませんし、まだまだ発展途上です。 アプリの規模にかかわらず、良いものであれば積極採用することはぶらさず、 開発してて楽しい環境を作りながら、より良いアプリをより早く提供できるように努めていければと思ってます。
はじめに はじめまして。2020年3月からエブリーでインターンをしている田村です。 私はデータエンジニア・データサイエンティストとしてインターンシップに参加し、データ関連部門に配属され、データ業務を担当しています。 今回は、エブリーにインターンとして入社し約4ヶ月間で感じたことをまとめていこうと思います。 目次 1.エブリーのインターンに参加した経緯 2.インターンを通して学んだこと 1.エブリーのインターンに参加した経緯 私は機械学習を主とする統計分野に興味があり大学院に進学したのですが、査読した論文を検証できるデータを保持するインターン先を探す中で、DELISH KITCHENを運営しているエブリーと出会いました。 面談の前にオフィスツアーをしたのですが、まず驚いたのは東京タワーの見える六本木グランドター38階のオフィスです。 食のメデイアを運営していることから、本格的なキッチンスタジオがあり、フードスタイリストの方が日々料理動画を撮影しています。 作業エリアは集中できる雰囲気なことや、CTOとのカジュアル面談時に大量のデータを扱うことが出来そうだと期待してインターンとして入社することに決めました。 2.インターンを通して学んだこと 私がデータエンジニアとしてインターンの業務を通じて学んだことを書いてみます。 ・DIKW まず、初日にデータチームのマネージャーに教えられたことはDIKWモデルというデータがどのように事業に貢献するかというモデルです。詳しい記事は こちら をご覧ください。 ・Arm Treasure Data / Databricks / Redashなどのデータプラットフォーム DELISH KITCHENのログデータの分析だけでも膨大な量で、これらのビッグデータを処理できる計算基盤が必須です。 所属しているデータ部門では、Databricksと呼ばれるApache Sparkをバックエンドとした分析プラットフォームを用いて、データクレンジングなどの前処理や機械学習の処理を実装しています。 また こちら の記事でもあるように、sql,scala,pythonなどの複数の言語を組み合わせて処理を書けるためこれらの言語の基本文法はマスターできました。学校ではPythonを利用した研究開発だけを実装しており、Python以外の言語に触れる機会が無かったのですが、SQLが特に重要だと教えられ、今では自分のやりたい事は大体実現できるようになりました。 インターン生でもエブリーの全てのデータに自由にアクセスでき、分析する基盤まであるのはとても良い環境だと思います。 ・他部門との連携 私の所属するデータ関連部門では、エブリーが運営している複数のメディアから発生するデータ分析依頼や要望を引き受けています。 「●●を知りたい」などの抽象的な課題を具体的な課題に落とし込んだ後、どんなデータを提供したら良いのか、どんな見せ方をしたらいいのかを考え、Redashのダッシュボードを提供したり統計的な手法で知見を伝えたりします。 他部門の業務を経験が浅いインターンが他部門の業務や課題を理解するのは難しいですが、エブリーではインターン生も他部門の議事録や組織図をconfleneceやカオナビで把握できます。 ・リモート環境 コロナの影響でエブリーでは3月からインターンも含めリモートワークを推奨しています。 私もリモートでのインターンは初めてで、移動時間がなくなったり服を選んだりする手間がなくなったりメリットが多いなと思いました。意思疎通が対面よりもスムーズにできないという問題がありますが、私のチームでは毎日30分程のミーティングを行い、課題の共有やコミニケーションを行うようにしています。 会議に参加できなくても録画しておけば後から見直せるので便利です。 最近では半年に1回の社員総会もリモートで行い こちら に詳しい記事があります。 最後に エブリーではデータ関連部門以外でもエンジニアのインターンを募集しています。 メディアの運営に携わってみたい、アプリの開発に携わってみたいという方は こちら をご覧ください。
はじめまして。2020年4月からエブリーに新卒で入社した伊藤です。 データエンジニア・データサイエンティストとしてデータ関連部門に所属し、日々データ業務に関わっています。 データ業務の1つである機械学習モデルの開発は、実験環境でモデルの精度を確認した上で本番環境に適用するプロセスを経ます。 今回は機械学習の実装で利用したMLflowとOptunaを組み合わせが、モデルの開発・運用・レビューで高いアジリティを提供できると分かり、導入事例として紹介できたらと思います。 取り組んだ問題 最近のデータ業務で、DELISH KITCHENユーザの過去の行動を分析し、ある特定のアクションが将来どの程度の確率で行われるかを機械学習で予測する、という問題に取り組みました。 ユーザがDELISH KITCHENをどのように使っているのか(レシピのお気に入り登録など)という情報は、集計された状態でデータウェアハウスに蓄積されています 1 。 まずはそれらを抽出し、予測を行うための特徴量データセットとして用意した後、パラメータの調整や特徴量の取り方などを試行錯誤しつつ、実験環境で機械学習モデルを構築しました。 大学の研究やデータコンペティションでは、多くの場合高い精度のモデルができたら一旦区切りとなりますが、企業で開発を行う場合にはこれを実際のビジネス施策でも使う必要があります。 そのためには、実験環境で行った「特徴量の抽出」→「モデルの学習」→「モデルの予測」→「最終結果の出力」という4つの段階が本番環境で1つのパイプラインとして自動化されており、かつ持続的に運用できる状態でなければなりません。 MLOps ここでMLOpsという概念を紹介したいと思います。 MLOpsとは機械学習のライフサイクルの管理やその環境を指す考え方で、機械学習(Machine Learning)と運用(Operations)を組み合わせた言葉です。 MLOpsの考え方に基づくと、上に挙げた4段階それぞれの品質を持続的に維持・改善しており、かつ全体としても1つのパイプラインとして運用できる環境の構築が重要になります 2 。 特に、ユーザ行動を使う今回のようなケースでは、予測したい行動データが日々更新され、実験では良い結果が出ていたモデルも時間が経つと「古い」モデルとなってしまうため、新しいデータを使ってモデルを定期的に更新していかなければなりません。 さらに、将来の研究でさらに良さそうなモデルが見つかった場合は過去のモデルと比較してより良いものをパイプラインに組み込めるような設計になっていると、より効果的な運用が可能になります。 このようなMLOps環境を実現するため、今回はMLflowとOptunaという2つのツールを組み合わせて活用しました。 MLflow MLflow 3 とはDatabricks社が開発した機械学習のライフサイクルを管理するためのプラットフォームです。 MLflowを使うと、学習結果の保存や過去の学習結果との比較が簡単に行えるようになります。 簡単な例としてロジスティック回帰によるアイリスデータセットの分類問題を考えると、実行環境によって設定は多少異なりますが、「ハイパーパラメータ」「予測精度」「学習モデル」の3つが対応した学習結果をおおよそ次のように登録できます。 from sklearn.linear_model import LogisticRegression import mlflow import mlflow.sklearn with mlflow.start_run(): lr_c = 0.1 model = LogisticRegression(C=lr_c).fit(x_train, y_train) score = model.score(x_test, y_test) mlflow.log_param( 'C' , lr_c) mlflow.log_metric( 'Mean Accuracy' , score) mlflow.sklearn.log_model( sk_model=model, artifact_path= 'mlflow_demo' , registered_model_name= 'lr_iris' ) 登録された学習モデルはMLflowを通してバージョン管理され、バージョンを指定したロードもできるため、新しいモデルと古いモデルとの性能比較を行ったり、時期や状況に応じたモデルの入れ替えも可能です。 それぞれのバージョンに対して、パラメータと予測精度が保存されています。 Optuna Optuna 4 は、Preferred Networks社が開発したハイパーパラメータチューニングのためのフレームワークです。 本来ハイパーパラメータチューニングは、パラメータの値を変更した学習結果を確認しながら最適値を見つける、非常に手間の掛かる作業です。 そのため、パラメータそれぞれに候補となる値を用意し、それらの組み合わせを全て試して最適なものを見つける、グリッドサーチと呼ばれる方法が主に使われていました。 しかし、グリッドサーチでチューニングの工程は自動化できるものの、パラメータの種類・候補が多いほど組み合わせが爆発的に増加するため探索に時間がかかってしまう、そもそも候補に含まれない値は探索できない、といった問題がありました。 Optunaでは、探索する値の候補を探索範囲として渡すことが可能です。 また、過去の探索結果に基づいて次に探索すべき範囲を判断するようなアルゴリズムが実装されているため、指定された範囲に含まれる値全てを試さずに、より確からしい最適解を導き出すことができます。 これにより、Optunaを使うとグリッドサーチよりも柔軟で効率的なハイパーパラメータチューニングが実現できます。 もう一度ロジスティック回帰によるアイリスデータセットの分類問題を考えます。 先ほどの例ではハイパーパラメータ C を決め打ちで設定していましたが、ここではOptunaを使い交差検証によるチューニングをしてみます。 from sklearn.linear_model import LogisticRegression from sklearn.model_selection import cross_val_score import optuna def objective (trial): lr_c = trial.suggest_loguniform( 'C' , 1e-4 , 1e2 ) model = LogisticRegression(C=lr_c) score = cross_val_score(model, x_train, y_train, cv= 3 ) accuracy = score.mean() return accuracy study = optuna.create_study(direction= 'maximize' ) study.optimize(objective, n_trials= 10 ) best_params = study.best_params 最適なパラメータは best_params として辞書形式で得られるので、その後は最適なパラメータでモデルを学習 → MLflowで結果を保存、という流れになります。 今回モデルの学習に用いるユーザ行動は時間的に変化するデータであるため、モデルの品質を持続させるには定期的に新しいデータで再学習する必要があります。 Optunaを使うとデータに応じて適切なハイパーパラメータを自動的に探索・決定できるようになり、 手動による介入を行わずに一定レベルの品質でモデルの再学習を行えます 5 。 MLOps環境の構築 それでは、MLOps環境をどのように構築したのかを紹介したいと思います。 エブリーではデータ分析基盤としてDatabricksを使っているため、Databricks notebookをベースに環境構築を行いました。 tech.every.tv まず、一連のパイプラインを「特徴量の抽出」「モデルの学習」「モデルの推論」「最終結果の出力」という4つのステップに分け、それぞれを順番に実行できるようなジョブを作成しました。 肝となるのが「モデルの学習」と「モデルの推論」のステップで、ここでMLflowとOptunaを活用します。 「モデルの学習」では、MLflowに登録されている最新の前処理モデルと予測モデルの2種類をロードし、検証用のデータを使ってこれらのモデル性能の検証を行います。 検証結果が良ければそのまま次のステップへ進みますが、もし検証結果が悪いようなら、再学習するためのタスクを呼び出してこれらのモデルをアップデートします。 モデルの再学習では、実験環境には存在しない本番環境の最新のデータを利用したハイパーパラメータチューニング、モデルの学習を行います。 チューニングするパラメータはOptunaに登録されており、探索はOptunaに実装されているアルゴリズムに従って行われるため、データに合わせた柔軟なチューニングが自動で行われます。 これにより、データが更新される度に必要となるハイパーパラメータの検証工程が減り、パイプラインの維持・改良をスムーズにできるようになります。 再学習後に得られたモデルはMLflowでバージョン管理し、必要に応じて過去のモデルとの比較もできるようにしました。 「モデルの推論」ではMLflowを通して最新のモデルをロードし、最新の行動データに対する予測を行います。 1つ前のステップでモデルの性能に関する検証を行っているため、ここで得られる予測は(完全ではないかもしれませんが)ある程度品質が保証されたものになります。 まとめ 以上、実験環境で機械学習モデルを作成するところからMLOps環境に昇華させるまでを、簡単に紹介させていただきました。 ここで紹介したパイプラインは現在バッチとして登録されており、定期的にモデルの更新とユーザ行動の予測が行われています。 現時点で運用を始めて数ヶ月が経ちましたが、現在も当初の予測精度付近で安定しており、構築した環境がモデルの品質維持に役立っているのでは、と考えています。 今回の業務を通して、MLflowとOptunaの有効性を改めて確認できたとともに、エブリーに入社するまではあまり経験してこなかった実運用環境の構築について新しく学べました。 今後は、MLOps環境を運用しつつ、新たなビジネス施策にも貢献できれば嬉しく思います。 ここまで読んでいただきありがとうございました。 https://speakerdeck.com/smdmts/databricks-and-spark-with-etl-and-visualization ↩ https://www.slideshare.net/databricks/mlflow-infrastructure-for-a-complete-machine-learning-life-cycle ↩ https://github.com/mlflow/mlflow/ ↩ https://github.com/optuna/optuna ↩ 時系列データを用いて交差検証を行う場合は、学習・予測データの分け方に注意する必要があります。 ↩
はじめに はじめまして。2020年4月にエブリーに新卒で入社した吉田です。 私はデータエンジニア・データサイエンティストとして入社し、データ関連部門に配属して日々データ業務に関わっています。 私達のチームでは、巨大なデータを集計・分析するための基盤としてDatabricks社の Databricks を使用しています。 今回の記事では、私達の業務におけるユースケースを交えながら、Databricksの利点や、活用方法を紹介できたらと思います。 Databricksとは DatabricksはApache Sparkベースの分析プラットフォームであり、データ分析の際に行う Data Sourceからデータを集約 分散処理によって高速にデータを整形 分析 可視化や集計したデータをData Lakeに保存 といった流れをDatabricks notebookを用いて記述し、Spark clusterを用いた高速な分散処理が可能です。 データ分析によく使われているツールであるJupyter notebookと同じような感覚でビックデータを扱え、インラインでの図表表示などにも対応しています。 またSpark clusterの設定やワークスペース、学習済み機械学習モデル、Jobの管理をWebUIを用いて行え、非常に扱いやすい分析プラットフォームです。 エブリーでDatabricksをどう使ってるか 私達のチームではデータ分析をする際に Arm Treasure DataやS3、BigQuery、Deltaからデータを抽出 データを加工し、Deltaに保存 Deltaから加工済みデータをTreasureDataに保存 TreasureDataをRedashから読み込み可視化 という流れを行っています。 この2の工程でDatabricksのnotebookを用いています。 SQLでは複雑なクエリになってしまう処理をDataFrameとして処理すると、コードを簡潔にでき可読性が上がります。 なぜDatabricksなのか Databricksを扱うにあたり便利な機能を紹介していきます。 豊富なData source DatabricksではS3上のcsvファイルやBigQuery、TreasureDataのデータ等をSparkを用いて高速に取り込めます。 特にDatabricks社が公開しているオープンソースのストレージレイヤーであるDelta Lakeを用いると、データへの信頼性とパフォーマンスが高まります。 Delta Lakeには ACIDトランザクションによりデータの整合性を確保 スキーマの適用によりデータの過不足や不正を検知 スナップショット作成によるロールバックや過去データの参照 Sparkの分散処理による高速な読みこみ といった機能が存在します。 これらの利点を、Spark Dataframeへの簡単なAPIによって使用できます。 // 書き込み sparkDataframe .write .format( "delta" ) .save( "delta/path" ) // 読み込み val sparkDataframe = spark.sql( """ select * from delta.`delta/path` """ ) ひとつのnotebook上でscala,python,sql等を混ぜて実行できる Databricksのnotebookではセルの先頭で %python や %scala といったマジックコマンドを記載すると、notebookに指定した言語とは別に、セル単位で使用する言語を選択できます。 pythonはデータを柔軟に扱え、データ分析において強力な言語ですが、型安全でない、scala等に比べて処理速度に劣るといった欠点が存在します。 そこでノートブック中で高速にしたい処理をscalaで処理し、分析部分をpythonで扱うといった運用が可能です。 履歴管理機能や同時編集機能が便利 Databricksにはnotebookの履歴を管理する機能や、複数人で同時に1つのnotebookを編集する機能が備えられています。 履歴の管理機能ですが、例えば notebookをリファクタリングしていたら重要なセルをまるごと消去してしまった データをいじくり回していたらnotebookがわけわからなくなった といった場合に、自動的に保存されている履歴から任意の履歴の復元が可能な機能です。 この機能はgitでの管理に関係なく履歴を保存してくれるため、わざわざgitで管理するほどでもない簡単な分析でも気軽に利用できるのが利点です。 次に複数人でのnotebook編集機能ですが、Databricksでは自分が見ているnotebookを他の人が閲覧している場合、閲覧しているユーザ名とそのユーザがどのセルを見ているかがセルに色がつき、わかるようになっています。 この機能を活用すると、複数人でnotebookを見ながら分析方法についての議論等が簡単に行なえます。 Gitによってバージョン管理が可能 DatabricksのnotebookではIpython notebookとは異なりセルの出力結果等がファイルに記載されないため、gitを用いたコードのバージョン管理が可能です。 これにより、一般的なコード開発と同じような コーディング P/Rを送る レビューしてもらう 修正 取り込み 運用 といった流れをデータ分析にも適用可能です。 そのため、クエリミスや不適切なデータ処理を可能な限りなくせ、また、より良い分析方法を協議できます。 それにより、信頼性の高いデータ分析を行えます。 また、gitホスティングサービスに連携したCircleCIを用いると、notebookのP/Rマージ時に本番環境への自動デプロイや、Job作成が行えます。 notebookをバッチスクリプトとして扱える Databricksで作成したnotebookはそのままDatabricks上でバッチJobとして登録できます。 使用するクラスタ構成や、実行するnotebook、スケジュールやリトライ回数等を簡単に設定できます。 notebookをバッチとして登録する利点として、 - 過去の実行結果がnotebookの出力としてセル単位で残る - セル単位で実行され結果が残るため修正が容易 というものがあります。 実行結果がセル単位で残るため、各セルごとの実行時間を比較してパフォーマンスが悪い処理を容易に特定可能で、パフォーマンス改善中の比較データとして過去の実装の結果を参照できます。 私達のチームでは、バッチのアラートをSlackに通知しており、アラート発生 -> セル単位の処理結果を見てエラー箇所を特定しリカバリという流れで安定したバッチ処理の管理を行えています。 まとめ 以上、エブリーでのDatabricksのユースケースと利点を簡単に紹介させていただきました。 Databricksを使用すると Spark clusterによるスケーラブルな計算基盤 Delta lakeによるデータの安全性 データ分析の信頼性 を確保できます。 こちらはあくまで一例ですが、Databricksの利点等を知っていただけたのであれば幸いです。
皆さん、こんにちは。株式会社エブリーは、『動画を通じてもっと楽しく、もっと充実した毎日に』をミッションに掲げた、『動画』『リアルデータ』『AI』のメディア企業です。 株式会社エブリーのエンジニア文化を紹介するために、技術ブログを発信していくことになりました。 はじめに 動画配信と言っても、動画編集サポートツール、動画エンコーディングインフラ、機械学習、ビッグデータ処理基盤、メディア運営をサポートする管理画面など、利用している技術は多岐に渡り『技術のるつぼ』と言っても過言ではありません。 採用技術 サーバサイド:Golang フロントエンド:Swift、Kotlin Webフロント:Nuxt、React、Angular データ基盤:Arm Treasure Data、BigQuery、Databricks インフラ:AWS、GCP プロジェクト管理:Asana、Confluence 対象ドメイン 料理、育児・子育て、ニュースエンタメ エブリーに在籍するエンジニア これまであまり外部に公開していなかったのですが、エブリーには個性的なキャリアを持つエンジニアが在籍しています。 AR黎明期にセカイカメラを作っていた人 金融系企業や教育系企業でCTOをやっていた人 ソーシャルゲーム企業の事業責任者をやっていた人 アドテク企業で爆速な配信サーバーを作っていた人 GAN(機械学習)でリンゴの画像生成をひたすら創っている人 エンジニアが『動画を通じてもっと楽しく、もっと充実した毎日に』を目指して、会社のミッションをどのように支えているか、紹介していきますのでぜひお楽しみに!