エブリーエンジニアブログ エブリーエンジニアブログ

MAMADAYSのサービスとバックエンドシステムのお話

f:id:nanakookada:20210107145935p:plain

自己紹介

はじめまして && あけましておめでとうございます。MAMADAYS開発部長をやっている齊藤です。

開発部長という肩書きですが、マネージメント業務だけではなく、モバイルアプリ開発以外を守備範囲としたユーティリティプレイヤーもつとめ、若い子たちと一緒に草を生やす作業に勤しんでいます。

座右の銘は『枯れた技術の水平思考』です!

閑話休題

弊社オウンドメディアの every.thing にも、CTOのid:kajidaiとの対談が掲載されていますので、時間に余裕がある方は見ていただけると嬉しいです。

【前編】エブリーCTOが聞く!MAMADAYS開発部長のルーツは音楽!?ゲーム開発から組織づくりにも携わってきたこれまでの経歴とは | every.thing(エブリシング)| エブリーのこと、ぜんぶ

【後編】エブリーCTOが聞く!『MAMADAYS』を主力事業に成長させたい!と語るMAMADAYS開発部長の今後の展望 | every.thing(エブリシング)| エブリーのこと、ぜんぶ

はじめに

App/Webのリリースから1年余りが経過した MAMADAYS の、サービス全体像とバックエンド観点でのシステム紹介を行います。

個々の詳細は、後日、若者たちが書いてくれると思いますが、本記事で少しでも MAMADAYS の開発に興味を持っていただけると幸いです。

MAMADAYSのサービス全体像

mamadays:service

MAMADAYSでは『子育てを通じて喜びと幸せを感じられる社会に』をミッションに掲げ、出産・育児に関する課題を解決するため、プロダクト開発を行っています。

出産・育児の課題は多岐に渡るため、複数の機能を提供する多機能アプリとして展開しつつ、データを活用して1人1人に最適化した情報をお届けできるよう日々改善を重ねています。

corp.every.tv

MAMADAYSのバックエンドシステム全体像

基本的な部分は DELISH KITCHEN と同じような感じにしていますが、以下のようにいくつか異なっている点もあります。

  • コンテナオーケストレーションとして、AWS ECS ではなく Kubernetes (AWS EKS) を利用
  • CI/CDとして、CircleCIではなく github Actions や AWS CodeBuild を利用

リリース時期やサービス規模の違いによりそのタイミングで最適と思う選択であり、どの技術が優れているとかではないです。

利用サービス

mamadays-infra

MAMADAYSで利用している基盤や外部サービスは以下の通りです。

  • インフラ
    • AWS EKS
    • AWS Aurora
    • AWS S3
    • AWS CloudFront
    • AWS Route53
    • Elasticsearch
  • ログ/分析
    • fluentd
    • BigQuery
    • metabase
  • 開発/運用
    • github
    • github Actions
    • AWS CodeBuild
    • terraform
    • kustomize

APIサーバ

iOS/Android/WEBなど、クライアント向けに提供しているAPIサーバです。

DELISH KITCHEN 同様に MAMADAYS でも、Golang の軽量フレームワークである echo を利用しています。

テスト周りでは、私が元々 Rails 厨だったこともあり Rspec っぽい記述ができる GoConvey を利用し、カバレッジも Codecov で計測してたりします。

WEBシステム

mamadays:WEB

WEBにおけるMAMADAYSサービスを提供しています。

記事の入稿やお客様からのお問い合わせに対応するためのツール類も含め、MAMADAYSのWEB面は React のフレームワークであるNext.jsを利用しています。

リリース1年が経過した頃、開発メンバーが増えたこともあり TypeScript 化を行ったのですが、ものすごく手軽に対応できたので Next.js 様様です。 『もしかして:Nuxt.js』と毎回問われてしまうぐらいググラビリティが低く、検索するたびに悲しい気分になりますが、それはそれで愛おしいです。

検索システム

mamadays-search

記事/動画情報の検索を提供するサーバです。

検索エンジンには Elasticsearch を利用しています。

元々は使用したいプラグインが対応されていなかったため独自運用していたのですが、AWS Elasticsearch Service のバージョンも上がったので、マネージドに移行することを検討しています。

最後に

かなりざっくりとした内容となってしまいましたが、当たり前の技術を当たり前に、それでいて新しい風を取り込みながら MAMADAYS は開発・運用しています。

まだまだ成長途上のサービスですので、これからも世の中の出産・育児に対する課題に技術でどう解決していくかを考えながら取り組んでいこうと思います。

ここまでお読みいただき、ありがとうございました。

2020年の振り返りと2021年のこと

こんにちは、エブリーでCTOをやっている梶原と申します。

1年の始まりということで昨年の振り返りと今年の取り組みについて書きます。

はじめに、エブリーが提供している中でエンジニアが関わっているプロダクトをご紹介すると、 食の課題解決に取り組むレシピ動画サービスの「DELISH KITCHEN」と育児の課題解決に取り組むママ・パパの365日に役立つ情報やサービスを提供する「MAMADAYS」の2つのプロダクトを開発しています。

2020年の振り返り

開発組織について

まず自分自身の変化として、2020年の春くらいに当時見ていた事業を引き継いで開発業務に専念することになりました。そして開発組織については、当時、各事業部にぶら下がっていたエンジニアチームを、開発本部というエンジニア組織に統合することにしました。エンジニア全体での勉強会やエンジニアブログを開始したのも昨年です。結果、課題としてあったエンジニア間の連携の希薄化、エンジニアのキャリアサポートや適切なアサインメントは徐々に解消されつつありますが、まだまだこれからなので今年も課題の解消に取り組みます。

データに関する取り組み

これまではDELISH KITCHEN事業部のみに存在したデータエンジニアリングの組織を全社の横断組織として再編し、組織のミッションとして「DIKWモデル」の構築を掲げました。 データ基盤からBIツールを用いて各種分析業務は行っていましたが、中にはExcelやスプレッドシートで独自に管理、集計、分析されていたり、手動データを打ち込んでいるもの、データの定義が扱う人により違うものなど、属人的な扱いのデータが様々あり非効率な部分が多くありました。 これらの課題に対してデータ基盤への集約と集計のとりまとめと実施を行い、結果としてデータによる意思決定が格段に進みました。

アーキテクチャの再設計

DELISH KITCHENアプリはリリースから4年が経過して、新しくジョインするエンジニアの初期の学習コストや開発スピードが徐々に低下しつつあり、先んじて対策を打つという意思決定を行いリファクタリングとリアーキテクチャリングを開始しました。

2021年に向けて

データに関しては、集約のフェーズが終わり、次はデータの利活用のフェーズです。機械学習を用いたプロダクトの改善や事業者との取り組みをこれまで以上に推進していきます。プロダクト開発では、息の長いプロジェクトになるであろうリアーキテクチャプロジェクトを昨年に引き続き取り組んでいきます。

また今年は小売のデジタル化が更に進む1年になるであろうと考えています。これまでにもアプリ内のチラシ・クーポン機能による集客支援、スーパー内に設置したデジタルサイネージによる店内販促、ライブコマースによるオンライン購買活動支援など取り組んできました。今年はこれらのものをベースに小売様と協力して新しいユーザ体験を生み出すべくプロダクト開発をできたらと考えています。

2021年もよろしくお願いいたします!

DELISH KITCHEN iOSアプリ開発のCI環境について

DELISH KITCHEN iOSアプリ開発のCI環境について

f:id:nanakookada:20201211144038p:plain

はじめに

はじめまして。2020年4月にエブリーに新卒で入社した山口です。
iOSエンジニアとして入社後、DELISH KITCHENクライアントグループで、日々iOSアプリの改善や新機能開発の業務に関わっています。

さて、多くの方にご利用いただいているDELISH KITCHENのiOSアプリですが、日々の開発フローをできるだけ効率化させるためにCI環境を整備しています。

今回の記事では、実際にどんなCI環境が構築されているかをご紹介します。

構成

さっそくですが、こちらが全体図になります。

iOS-CI-ENV-OVERVIEW

フローとしてはこんな感じで、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って何が違うんですか?

f:id:kajidai:20201208174411j:plain

はじめに

弊社ではインフラは主にAWS(一部GCP)を利用しており、構成管理にansibleとterraformを使用しています。 新しく入社された方や、これまでインフラをあまり触ってこなかった方がこれらに触れる際、本件表題のような質問をされることが何度かありました。

私はこのたぐいの質問をされるたびにそれぞれの成り立ち、歴史的背景、ソフトウェアとしての実装と振る舞いの違い、そして弊社での使われ方について説明をしてきました。 大抵はそれで納得してその後は疑問なく使い分けができるようになっていく(ように見える)のですが、やはり中には腑に落ちないようであったり、使い分けに悩まれる様子がみられる方もいます。

説明している最中、自分でも「この説明で適切なのだろうか」「そもそもなぜこれらが混同されやすいのだろうか」と疑問に思うことがよくあり、良い機会ですので今回文章としてまとめました。

この文章が表題の疑問の回答になり、今後の学習のお役に立てれば幸いです。

作っただけでは動かない

ansibleやterraformの話をする前に、基礎の話から押さえておきましょう。 インフラエンジニアであったり、サービスの保守運用の経験がある方であれば「何を当たり前のことを」と思うかもしれませんが、アプリケーションは作っただけでは動きません

なぜでしょうか?「アプリケーションは完璧に仕様を満たしています!動かせばいいだけじゃないか!」そう思われますか?

そう、動かせばいいだけなのです!

しかしながら、アプリケーションを動かすためには、アプリケーションの他にもたくさんのものが必要になります。

それらは、普段手元のパソコンで開発していると当たり前のように存在しているために気づかないものであったり、インターネットを通してたくさんの人にサービスを届けるために必要なものであったり、様々です。 ひとまずこの文章内では、これらのことをひとまとめに「リソース」と呼ぶことにしましょう。インフラとはリソースの集合であると言えます。

リソースという言葉は色々な場面で色々な意味合いで用いられます。 ここでいう「リソース」はこの文章内ではこのように名前をつけて呼ぶ、という程度の意味で捉えてください。

リソースの例をあげていけばきりがないのですが、いくつか代表的なものを取り上げましょう。

コンピューター

あなたの手元にはコンピューターがありますか?いえ、答える必要はないですね。この文章を目にしているということは、そういうことですね。

当然のことですが、アプリケーションを動かすためにはコンピューターが必要です。

オンプレミスであれば機器を購入するかレンタルするかして、データセンターに配置しなければなりません。あるいはVPSを契約してもいいかもしれません。 もちろんAWSやGCPなどのクラウド環境で仮想コンピューターを用意しても良いでしょう。

ネットワーク

あなたのコンピューターは何かしらのネットワークに繋がっていますか?いえ、答える必要はないですね。この文章を目にしているということは、そういうことですね。

インターネットを通じてサービスを提供するのであれば、何かしらインターネットへの接続性が必要になります。 あるいは、既存のネットワーク内に配置するのであれば、多少のネットワーク構成の変更が必要になるかもしれません。 また、接続性を確保することと同様に、不必要な接続を遮断する設定も重要です。

このノリももう飽きてきませんか?私は飽きました。ですので、あとはまとめていくつか紹介しましょう。

ロードバランサー、DNS、TLS証明書……、etc

これらは必須というわけではありませんが、ほとんどの場合必要になるでしょう。

どれも購入・あるいは契約し、設定し、配置しなければなりません。

動かし続けるのはもっと大変

さてアプリケーションを動作させるために必要ないくつかのリソースを確保しました。アプリケーションは正しく動作し、目的を完全に達成しました。

素晴らしいことです!ではこれで全ては解決でしょうか?

そうならばどれほど良かったでしょう……。

f:id:hueless:20201208105626p:plain

ほとんどの場合、アプリケーションは変更され続けます。あなたは今アプリケーションを変更しました。ではそれをどのようにすれば反映できますか?

変更前のアプリケーションは動いていて、以前と変わりなく使われています。これをダウンタイムなく新しいものに置き換えるためにはどのような手順が必要でしょうか?

あるいは、あなたのアプリケーションは非常に人気が高く、多くのユーザーの要求を満たすために、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であればkustomizehelmを使っており、個々のコンテナはDockerfileで管理されているため、相対的にansibleの利用頻度は下がっています。

おわりに

以上、ansibleとterraformの違いについて、それぞれの特徴の説明と、弊社での使い分けについて解説をしました。

今回はそれぞれのツールの具体的な使い方やプラクティス、tipsなどの説明には踏み込めませんでしたが、いずれ機会があればそのあたりにも触れてみたいと思います。

DELISH KITCHEN WEB を構成する技術のお話

f:id:kajidai:20201130160617p:plain

DELISH KITCHEN WEBについて

はじめに

はじめまして。DELISH KITCHENバックエンドチームの梅木です。 DELISH KITCHENのバックエンドチームはアプリ向き合いとWEB向き合いのチームとで別れており、自分はWEB向き合いのチームに配属されています。 担当業務としては、DELISH KITCHENのWEBフロントの開発はもちろん、APIサーバーやインフラと、WEBサービスに関しての改修では境界を設けずに、開発・運用・監視を日々行なっています。

本日はDELISH KITCHENのWEBサービスのシステムについて、ご紹介できればと思います。

DELISH KITCHEN WEBで使われている技術スタック

DELISH KICTEHEN WEBで使用されている技術スタックはこちらになります。

delishkitchen 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サーバー
  • インフラ
    • 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を選びましたが、理由は下記となります。

  1. 当時のバックエンドチームには、バックエンドに強いメンバーがほとんどでwebフロント開発に長けているメンバーは多くなかったため、Nuxt.jsのような薄いフレームワークが取り掛かりやすいのではないかと考えた。
  2. バックエンドチームのメンバー内でVue.jsの開発経験者が一名在籍していた。
  3. 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について少しでも知っていただけたら幸いです。最後までお読みいただき、ありがとうございました。