株式会社LIFULLのブログ - TECH PLAY

TECH PLAY

株式会社LIFULL

株式会社LIFULL の技術ブログ

全668件

こんにちは、LIFULL HOME'Sの売買領域でエンジニアチームのマネジメントを担当しています、長崎です。 ここ数年、LIFULL HOME'Sでは積極的に技術的負債解消に取り組んでおり、今回は私がマネジメントするチーム内でどのような取り組みをしているかをご紹介します。 技術的負債の解消はあらゆるサービスにおいて大きな問題となっており、すでに多くの事例が紹介されていますが、同じように我々の取り組みがどなたかの参考になれば幸いです。 LIFULL HOME'Sにおける技術的負債 これまでに下記エントリでも言及している通り、LIFULL HOME'Sの現行プロダクトは9年を超えて開発されています。 www.lifull.blog 上記エントリではフロントエンドに焦点を当てていますが、これだけ長く開発・保守されているプロダクトですので、技術的負債はアプリケーションレイヤー(PHP / Ruby)、インフラレイヤーと多岐・多層に渡って存在しています。 新機能の開発や既存機能の改修を行う際に、調査・テストの工数が膨らんでおり、サービス成長の大きな阻害要因、まさに負債となってしまっています。 事業開発部門における取り組み LIFULL HOME'Sの開発部門は大きく分けて2つに分かれています。 ビジネスサイドと密接にコミュニケーションしながら、エンドユーザーが触れるプロダクトを日々開発する事業開発部門と、 LIFULL HOME'Sに限らず、プロダクト・サービス全体の基盤システムを保守・改善する技術基盤部門です。 肥大化する調査・テスト工数を削減し、開発効率の向上を図るために、私の所属する事業開発部門では 工数の10%をリファクタリングに充てる というルールを作成し、組織全体として負債解消に取り組んでいます。 私のチームはアプリケーションレイヤーの開発を担当することが多く、下記のようなコードレベルでの負債に日々悩まされています。 使われているかわからないコード 依存関係が適切に分割されていないコード(デグレしやすいコード) 複雑度が高く、テストコードがないコード 私のチームでの取り組み 私のチームでは、上記コードレベルの負債をリファクタリングにより解消するべく、3つの取り組みを行っています。 コードの静的解析に基づいたリファクタリング 日々の改修における課題を基にした大規模リファクタリング リファクタリングを行うべき領域の可視化・優先度付け それぞれどんなことをしているのか、よいところと課題はなにか、をご紹介します。 コードの静的解析に基づいたリファクタリング LIFULL HOME'Sでは、 codeclimate というサービスを導入し、コードの静的解析を行っています。 この解析結果に基づいて、複雑度の高いメソッドや、重複したブロックを発見し、リファクタリングを実施します。 よいところ コードに対し、定量的な指標を持てる 一定のフィードバックを機械的に行える 課題 メンテナンス性のために分割されたクラス・メソッドであっても重複判定されてしまう コードの利用頻度や、改修頻度に紐づかない分析のため、改善効果による優先度がつけられない 静的解析の結果がすべて!としない運用が求められますが、lintツールなどと同様に定常的に活用することで、コードのベースレベルの向上や複雑度に対する意識づけが行える点が良いと思います。 日々の改修における課題を基にした大規模リファクタリング 新規実装当初にその時点でのユースケースから作ったクラスが、長年多数のメンバによって、そして多くの場合デリバリーを優先したプロジェクトにおいて、適切に拡張されずに if 文まみれになってしまうことはよくあることかと思います。 このような状態のコードを放置していると、改修の際に想定外のページ・サービスにおいてバグが発生し、以降デグレチェックに悩まされ続けます。 これを改善するため、現在のユースケースに合わせた大規模なリファクタリングを行っています。 よいところ 普段目を背けているところに向き合う機会になる 改めて見直すことで、現時点では不要になったコードを消すことができる ビジネス要求や想定される改修の質・量が想定できる状況で設計し直せる 課題 アーキテクチャレベルや、フレームワークの負債からは逃れられない リリース手順やタイミングなど、実行計画が難しい ステークホルダーが多くなりがちで、精神的に疲弊する 進めていると往々にして、「もはや作り直したほうが早いだろコレ」という気持ちに苛まれますが、サービスの保守をしながらできることをしていかねばと思い直す日々です。 リファクタリングを行うべき領域の可視化・優先度付け なんとか技術的負債を定量的に表現したいと思い、コードの変更行数とテストケース数(テストコードの行数)の計測を行っています。 現時点での1サンプルですが、変更行数が数行にもかかわらず、デグレチェック目的で数十URLをテストすることになる場合があれば、変更行数が数十行でもテストケースは数ケース程度で済む場合もあります。 前者のような機能は早急にリファクタリングしなければ、毎回大きなテスト工数がかかったり、誰にも意図がわからないデグレチェックが口伝で残り続けてしまいます。 よいところ リファクタリングの期待効果を見定めることができる 非エンジニアに改修の難易度を定量的に伝えることができ、リファクタリングの重要性を理解してもらいやすい 課題 変更行数は計測しやすい反面、プログラミング言語やフレームワーク、設計思想に左右されやすい テストケース数やテストコードの行数は、作成者によりばらつく 現在取り組んでいる指標で目的を果たせるのか、そしてベストなのかどうかはわかりませんが、なんとかリファクタリングの重要性を定量的に表現するべくチャレンジしていきます。 最後に サービス上の不要な機能を削除したり、「今」問題がないコード・システムを直すためにはステークホルダーの理解・協力が欠かせません。 一方で、「リファクタリング」や「技術的負債の解消」という非エンジニアからしたらよくわからないけどすごく大事そうな言葉を盾に、エンジニアが手段を目的化してビジネスに貢献できないことは絶対に避けなければいけません。 今回3つの取り組みを紹介させて頂きましたが、リファクタリングが「よくわからないけどすごく大事そうな言葉」じゃなくするために、ステークホルダーにしっかりと伝え続けることが一番大事な取り組みだと思います。
釣り気味タイトルで大変申し訳ございません。 プロダクトエンジニアリング部の島村です。 総会どうしていますか? みなさまが所属する会社・組織では総会は実施されておりますでしょうか? 部署やチームの結束を高め、メンバーが同じ方向を向くためには貴重な機会となる総会。 エンジニアリングマネージャーであれば、運営を行なったことがある方も少なくないと思います。 総会って難しい そんな総会ですが、下記のような問題が生じやすいと思います。 組織の階層ごとに総会があり、総会過多な状況になる どの総会でも似たような構成になりやすい 会を重ねるごとにネタが尽きてくる 私の所属する組織でも総会の実施を考えたのですが、上記のような問題が懸念され、頭を悩ませました。 総会の目的を考える そもそも何が何でも総会を実施する必要があるわけではありません。 総会を実施する上では、目的や総会を実施することで目指したい組織の姿を定義することが大切です。 今回の我々の組織のケースでは、所属する各グループ内での交流は盛んだが、グループ感での交流の機会には乏しく、そもそもお互いのことをよく知らない等、組織全体としての結束力には課題がありました。 そのため、総会を行うことで目指す姿を下記のように定義しました。 個々人の強みや詳しい領域がわかり、それをそれぞれの組織に還元できる状態 まずは総会を行うことで上記を目指し、ゆくゆくは、 グループの垣根を超えて助け合い、相乗効果を生み出せている状態 例えば何かサービスをつくるときにお互いの力を合わせ、よりよいものを作り出せる状態 のような状態に辿りつきたいと考えました。 OSTに着想を得る 上記を目指すための総会のコンテンツを考えはじめたのですが、そんな時にコンテンツ案として着想を得たのが、OST(Open Space Technology)というディスカッションの方法論です。 www.humanvalue.co.jp OSTは会議の参加者が議題を提案し、その議題に興味のある人が参加して議論を行う会議の方法です。 議題や議論の場の作り方が参加者に委ねられるため、より参加者が主体的、かつ能動的に議論を行えるのが特徴です。 どんな感じでやったか 今回我々は上記のOSTにインスパイアされつつも、初めての試みかつ、関係性が出来上がり切っていない組織での実施であったため、事前にいくつかのテーマを用意し、参加者に選んでもらう形式で行うことにしました。 (それもうOSTじゃないじゃんというツッコミはスルーします!) テーマですが、参加者にとって親みやすい、かつ個々人にとって学びがある・持って帰られるネタがあることを重視し、下記に5テーマを用意しました。 在宅勤務 レビュー 設計・見積もり 仕事を早く終える方法 チームビルディング LIFULLも現在は在宅勤務が多くなっており、総会もzoomでの開催となりました。 上記のテーマごとの部屋を用意し、参加者が入りたいテーマの部屋に入ってトークする形式で実施しました。 やってみてどうだったか やってみて良かった点、イマイチだった点・改善点は下記です。 良かった点 普段関わりのないメンバーでの交流ができた、話が参考になったという意見が参加者からも聞くことができた テーマ選定が日常業務の範疇だったため、初対面くらいの関係性でも議論がしやすかった 自分でテーマを選ぶことができたので、話が盛り上がりやすかった イマイチだった点・改善点 テーマにより人数にバラつきがあり、人数が少なすぎて解散した部屋もあった(5、6人がちょうどよさそう) 技術的な話題のところは知識差がある場合に同じ目線で議論をするのが難しかった テーマは事前発表されていた方が準備して臨めるので議論が深まりそうだった 総会実施後にはアンケートも実施しましたが、約80%がやってよかった、約95%がまたやりたいと回答しており、おおむね好評な結果が得られました。 一方で改善点も多く見えてきております。 目指す姿に近くためには継続性が重要と考えますが、継続していくには議論の質を高めていく工夫も必要と感じています。 まとめ 今回我々の組織で実施した総会について紹介させていただきました。 タイトルは釣り気味ですが、満足度が高かったのは本当なので、今後も改善を重ねながら継続していきたいと考えています。 上記のような形式でのディスカッションは総会以外のチームビルディングにも有用と思いますので、参考になれば幸いです。
こんにちは。LIFULLでエンジニアをしている中村優太です。 2020年4月に新卒で入社して、早くも4ヶ月、配属されて2ヶ月が経過致しました。 この記事では、配属までのLIFULL新卒エンジニア研修についてご紹介したいと思います。 はじめに 研修スケジュール プログラミングの基礎 個人開発演習 その他トピック 最後に はじめに LIFULLのエンジニアは2ヶ月間の新卒研修があります。 最初の2週間は全職種合同で会社のビジョンの理解や社会人の心構え、ビジネス基礎を学び、残りの1.5ヶ月間はエンジニア研修になります。 LIFULLの新卒エンジニア研修はプログラミングの基礎の基礎から始まります。 と言うのもLIFULLはとにかく経営理念『常に革進することで、より多くの人々が心からの「安心」と「喜び」を得られる社会の仕組みを創る』を大切にしています。 採用過程では技術力を評価の軸にするのではなく、経営理念に共感する価値観を持っているか?、それに伴う行動を自発的に起こしてきたか?が評価されている(のだと思います!)。 そのため新卒には、Web開発インターンをしていた人もいれば、Web開発は未経験と言う人もいます。 それどころか同期は、大学で生物を研究してた人、機械科出身、農学部出身とバックグラウンドも様々です!(ここもLIFULLらしい) 前置きが長くなりましたが、つまりは技術差があるのです! 新卒エンジニア全員がWeb開発の基礎と全体像を理解し、実装できる状態にしてから実務に入る。 そのためにエンジニア研修は1.5ヶ月に設定されています。 そして今年はみなさんご存知の通り、新型コロナウイルスの影響により入社初日から出社できないと言う状況でした。 LIFULLでは入社式もリモートで行いました。 当然エンジニア研修もフルリモートで行われました。 そんなLIFULLの新卒エンジニア研修について紹介していきます。 このブログを見ていただいた方、特に就職活動中の学生に入社後の様子やLIFULLの人となり、空気感が少しでも伝われば幸いです。 研修スケジュール 以下がざっくりとした研修期間のスケジュールです。()内には営業日を書いています。 04/10 - 05/01(15) プログラミングの基礎 Linux HTML, CSS, JavaScript PHP PHPフレームワーク(Laravel) 05/07 & 05/08(2) ソフトウェアテスト & セキュリティ研修 05/11 - 05/19(7) 個人開発演習 5/20 成果発表会 スケジュールを改めて振り返ってみると、ゴールデンウィークなどもあって営業日だけで言うと1ヶ月分もなかったんですね。 内容については後述してますが、25日間でやる内容にしてはかなりボリューミーです。 さらに今年はオンラインでの研修ということもあり、何かと模索しながらの研修で大変でした。 講師の方は僕たちの数倍大変だったと思います。。。 あくまで僕の例ですが、研修前と研修後のスキルマップはこんな感じです。 Web開発はほとんど未経験でしたが、研修を通して非常に幅広い技術に触れたと思います。 研修の最後の方にはスキルマップには記載されていない技術にもチャレンジしたので、めちゃくちゃ詰め込み教育ですね。 プログラミングの基礎 「はじめに」でも記述したようにLIFULLのエンジニア研修は基礎の基礎から始まります。 Linuxの生い立ちや、HTML,CSS,JavaScriptを使ったDOM操作などWeb実装未経験者にも1からわかる授業です。 ちなみに研修のスタイルとして、講義内容についてすでに理解している人は自習しててOKです。 あくまで目的はエンジニア基礎スキルの向上です。 PHPの研修の最後には、課題アプリケーションを2つ作成します。 課題が与えられ制限時間内にアプリケーションを作成します。 実装が終わると、レビューの時間が設けられており、2人分のコードに対してレビューを行います。 また自分のコードには同期2人と講師の方からレビューをもらえます。 (ちなみに講師の方は全員分のコードをレビューしてくれました。大変だ。。。) 学生時代の研究では、コードに対するレビューは経験することがなかったのでとても新鮮であり、同時にレビューで他人のコードを理解することの難しさを学ぶことができます。 配属されて2ヶ月ですが、先輩方のレビューを見ていて、レビュー能力はエンジニアにとっていかに大切な能力なのかをひしひしと感じてます。 社内には、ソフトウェアテストとセキュリティを専門に扱うグループがぞれぞれあり、そのグループの先輩社員から1日ずつ講義を受けます。 ソフトウェアテストでは実際にある仕様に対するテストケースを自分で考えます。 テストケースを考える上で必要な観点が必要最低限身につきます。 セキュリティ研修では、座学もありますが、実際に研修用サーバに対してSQLインジェクションやXSSを仕掛けます。 研修の最後にはCTF(Capture The Flag)という情報セキュリティのスキルを競い合うセキュリティコンテストを行います。 2人一組で課題に取り組みます。 研修用のWebサイトに対して、いろんな攻撃手法を試して、隠されたキーワードを見つけ出します。 これがまた楽しい!がしかし、できないと悔しい。。。 個人開発演習 個人開発演習はサービス開発における「企画、スケジューリング、要件定義、設計、実装(サーバサイド、フロントサイド)、テスト」の全てを1人でやりきります。 テーマは「チャレンジ&リスペクト」。 制約は「Laravelを使うこと」くらいです。 LIFULL内の多くのサービスにPHPが用いられていることからこの制約が設けられています。 みんな各々自分の作りたいサービスを作ります。 そのため特に授業などがあるわけではありません。 個人開発演習期間の1日のスケジュールは、「朝会→ 個人開発 →昼食→ 個人開発 →夕会→ 個人開発 →終了」です。 本当に1日中個人開発しています。 しかし、講師の方の計らいにより、基本的にZoomを繋げたまま、カメラをオンの状態で個人開発を行いました。 そうすることで、少し実装でつまづいた時や困った時に気軽に相談できるようになりました。 そしてその問題に対して、みんなで考え、調べ解決していきます。 あくまで個人開発ですが、お互いに何を実装したいのかを理解し、知見共有していくことでみんながより良いプロダクトを作れるような環境ができていたと思います。 また、テーマにあるチャレンジの取り組み方も人それぞれで、研修内容で習った内容を最大限プロダクトに落とし込んでアウトプットすることはもちろんですが、自分なりの技術的チャレンジを行います。 CSSフレームワークを使わずにあえてCSSをフルスクラッチで開発する人 PREACT x redisで超軽量ネイティブアプリを作る クリーンアーキテクチャ AI技術を取り入れたアプリケーションを3つ作る 研修中にクリーンアーキテクチャが同期内でブームになりました。 個人開発演習では同期2人が実際に「Laravel x クリーンアーキテクチャ」にチャレンジしていて大変そうだったのが印象に残ってます。 (そんな同期が書いたQiita記事です。ぜひご覧ください! https://qiita.com/Shiruba/items/b0754a5815e0583aa8ce ) 僕は「Vue.jsとDocker使ってみたい」という、いかにもミーハーかつ浅はかな考えで以下のような技術選定を行いました。 時間も限られている中で、少し幅広く手を出しすぎたかなという気がしています。 しかし、いろんな技術に触れることは新鮮で楽しいですね! 研修の最終日には成果発表会があります。 成果報告会はエンジニア全社員にZoomが公開されており、配属後のメンターやグループ長だけでなく、非常に多くの人が見にきてくれました。 みなさん活発に質問してくださり、すごく盛り上がりました! ちなみにLIFULLでは2年目にもエンジニア研修があり、チーム開発でプロダクトを作り上げます(手厚い😂 )。 噂によるとそちらの成果報告は厳しい質問も飛んでくるとか。。。頑張ろう! その他トピック 毎日の研修には日直が決められており、研修の最後に「日直スピーチ」があります。 自分の好きなもの、経験してきたことを好きなように話すコーナーです。 コンテンツは - 3Dモデリング - Deep Learning - 好きなキーボードについて語る - おすすめのVSCode拡張機能 - 競技プログラミングの魅力 - Haskelの魅力 みたいな感じです。 他にも講師の方による、監視技術やボトルネック調査の方法などを講義していただき、研修内容には含まれない実践的な内容も聞けました。 そして中でも僕が印象強く残っているのは、同期が話してくれた「DNAを使ってデータを保持する」という話です。 長くなりそうなので、詳細は省略しますが、DNAはアデニン、グアニン、シトシン、チミンの4種類の塩基が含まれており、この配列によって遺伝子が表現されているみたいです。 1つの塩基対で2ビットのデータを表しているということ! つまりここから導き出される結末はDNAはわずか1グラムで215ペタバイトのデータを保持できるということらしい! 衝撃的すぎますよね! 多種多様な話が聞けるのもメンバーのバックグラウンドが様々だからです! また、研修終わりにみんなでオンライン懇親会を行い、研修では話せないプライベートな話をして盛り上がりました🍺 最後に 配属されて2ヶ月が経過しましたが、実務でも「チャレンジ&リスペクト」が大事だなと思います。チャレンジする環境があり、それを周りが全力で応援する。 LIFULLの社風と言っていいと思います。 僕は同期とやってきたプロジェクトが新規事業提案制度(Switch: https://lifull.com/company/bctw_japan/) で入賞し、業務時間の20%を新規事業創出に費やしています。 新卒でいきなり新規事業に関われるのは、とてもありがたい機会だなと感じています。 また、その新規事業は社長室と呼ばれる新規事業を専門とするエンジニアや、ビジネスのプロフェッショナルがアドバイスをくれたり、時にチームのメンバーとして事業を推進してくれます。 まさに「チャレンジ&リスペクト」です。 僕自身も配属された部署で最大限価値を提供し、さらには今行ってるプロジェクトをLIFULLの子会社化するという目標を持って突き進みたいと思います! 最後までお読みいただきありがとうございました。 ぜひ、少しでもLIFULLって面白そうだなと思っていただけたら幸いです。 さらにLIFULLに入社して、みなさんと一緒に働けるのを心から楽しみにしております! では!
技術開発部の相馬です。好きな UI フレームワークは Svelte です。 私が現在所属しているグループでは、弊社のメイン事業である LIFULL HOME'S における開発効率の改善などを行っています。 今回は、LIFULL HOME'S の Web フロントエンド(以降はフロントエンドと表記します)開発環境を、Node.js の資産を用いて近代化した話(以降は近代化と表記します)をご紹介したいと思います。 目次 はじめに 近代化として取り組んだ内容 Sass の導入 Rollup の導入 Babel の導入 PJ を進める上で立ちはだかる問題点 全ファイルのコンパイル前後における動作担保問題 文字コード問題 CSS ハックをパースできない問題 デプロイサーバー(テスト)で本番デプロイできちゃう問題 学び npm ci の必要性 npm modules の更新作業 おわりに はじめに LIFULL HOME'S は弊社でもっとも開発が盛んなアプリケーション(Repository)です。 Commit 数や Contributor 数は恐らく弊社でもっとも多く、また開発の歴史も非常に長い(9年超)です。 アプリケーションの基本構成は PHP(Symfony + Twig) + jQuery で、サーバーサイドでテンプレートを組み立て HTML を構築し、クライアントサイドでは jQuery を使って補助的に DOM の操作などを行うような形です。 近代化を行うにあたり、当時の課題点としては大きく 2 種類ありました。 CSS/JS の minify/bundle は 初回の HTTP リクエスト時に PHP 側からランタイムで生成される 初回リクエストのレスポンスが非常に遅くなってしまう CSS/JS の依存関係の解決(ファイルの読み込み)は Twig 上で行なっており、JS などを単体で見た場合に依存関係が分かりづらい CSS/JS は開発者が書いたコードがそのまま(minify/bundle を除く)ブラウザーへと出荷される CSS vendor prefix 手作業がつらい URL 関数などで参照する画像のバージョン管理(cache-buster の手動付与)がつらい Hex などのデザイン周りの値が毎回ハードコーディングでつらく、たまに間違っててつらい JS 変数スコープがつらい 3rd party ライブラリの取り扱いが煩わしい JS のみで静的な依存解決ができない 近代化として取り組んだ内容 前述した課題を解決するため、実際に取り組んだ内容としては以下の通りです。 Sass の導入 前述していた CSS に対する課題は、Sass を導入することでほぼ全て解決できましたが、画像などの cache-buster のみ、ライブラリだけではどうにもならなかったため、対象サーバに内包している画像については Sass のビルドの事前に MD5 を計算した結果をファイルへと出力しておき、 Sass の JavaScript API を使って CSS の URL 関数をオーバーライドし、MD5 の計算結果のあるものは Hash を付与して URL 関数を組み立てる ということを Sass の build 内で行うことで解決しました。 sass-lang.com この対応によって、既存のコードを修正することなく、また開発者のメンタルモデルも据え置きでハッピーだねという作戦です。関数名を別の名前(URL_USE_CB のような)にして、CSS 側をリネームするかという議論もあったのですが、オーバーライドするデメリットがほぼ存在しないことと、別の関数に切り出した後の周知徹底/実装忘れの懸念を考慮した結果、URL を上書きするという結論に至りました。 また、テンプレート(Twig)側から参照される画像についても、Twig 上に cache-buster 用のカスタムタグを作成し、その中で参照される img 要素には PHP から例の MD5 の計算結果ファイルを参照し、自動で付与されるように実装を加えました。 input {% cachebuster %} < img src = "/img/xxxx.png" > < img src = "https://s3..../foo.png" > {% endcachebuster %} output < img src = "/img/xxxx.png?v=xxxxxxxxxxxx" > < img src = "https://s3..../foo.png" > また、開発補助として StyleLint/Autoprefixer も合わせて導入しています。 Rollup の導入 JavaScript の bundler には Rollup を選択しました。 選択した理由としては、将来的に module/nomodule を採用した build プロセスを検討していたため、実装当時では ESM 形式の output の選択肢を取ることができる唯一の bundler だったということと、plugin の書きやすさなどが大きな要因です。 philipwalton.com 実装したい機能が満たされているかの確認として、bundler の機能比較はこのサイトを参考にするのが良いと思います。 bundlers.tooling.report また、弊社では長年 joo というライブラリを使ってクラス宣言のようなことを行い、その宣言単位でモジュールと呼んで開発を行なっていたのですが、そのモジュール間の依存関係の解決は global(window)の名前空間を頼りに行なっていました。 github.com イメージです // module/A.js def().as( 'myapp.module.A' ). it.provides( { method: function () { ~~~~~~~ } } ); // module/B.js def().as( 'myapp.module.B' ). it.inherits( window .myapp.module.A). it.provides( { method: function () { this ._super(); ~~~~~~~ } } ); しかし、これらの JS ファイルの読み込みは Twig 側から行なっていたため、そのページ(ルーティング)から読み込まれる Twig をまず特定し、そこから辿るようにして JS が読み込まれているかどうかを判定するしかファイル特定の術がありませんでした。また、Twig 上で依存関係(JS の読み込み及びそれらの読み込み順番)を解決しているため、ページによっては依存するモジュールを読み込んでおらずランタイムでエラーになってしまうということが発生してしまっていました。 <!-- ./somepage/head/javascript.twig --> {{ minify_js([ "module/A.js", "module/B.js" ]) }} <!-- ./somepage2/head/javascript.twig --> {{ minify_js([ "module/B.js" <!-- 依存する A を読み込めていない --> ]) }} この問題に対して、Twig 側で依存関係の解決をするのをやめ、ESM の import/export を使用して JS 上で静的に依存関係の解決を行えるようにしました。 Babel の導入 JavaScript のコンパイラーとして Babel を導入しました。 前述した joo などは Class シンタックスによって、this のスコープ問題で bind や this の再代入などを行なっている記述はアロー関数に書き換え可能です。Optional chaining や Nullish coalescing などが良い例ですが、コードの記述量に関してもモダンな構文を採用する方が少なくなる傾向にあり、コード全体として可読性の向上にも繋がると思います。 また、導入時には基本的にモダンシンタックスのダウングレードコンパイルの役割しか持たせませんでした。 というのも、Babel を利用する場合 @babel/preset-env を併用して browserlist などでサポートブラウザを指定し、未サポートのメソッドの利用があった場合に Babel が core-js などから polyfill コードを読み込むのが一般的なセットアップだと思われますが、この自動判定の仕組みには限界があり「該当するメソッド呼び出しがあった際に透過的に追加する」という問題が発生してしまいます。今回の一件で具体例を挙げると Array.prototype.find と jQuery の $.find を見分けることが出来ずに不要な polyfill コードが読み込まれてしまっていました。 この挙動によって、後述する自動テストに影響が出てしまうため @babel/preset-env のオプションで利用する polyfill は、エントリーとなるようなファイルで手動読み込みするような設定にしました。 @babel/preset-env · Babel Babel 自体は Rollup の plugin として通すようにビルドを組みました。 PJ を進める上で立ちはだかる問題点 これらのツールを適応/導入すること自体はそこまで難易度の高いものではありません。 しかし今回取り組んだ環境には 9 年を超える"重み"が存在し、PJ を進めるには同時にこれらを解決する必要がありました。 全ファイルのコンパイル前後における動作担保問題 前提として、これらの導入/移行計画は全ファイルを一括で行う必要がありました。 CSS で約 700 ファイル、JS で約 1500 ファイルほど存在していたのですが、それぞれコンパイル前後でファイル内容に差分が発生することが分かり、リリースするためには「コンパイル前後で生じた差分によって現状の期待値とずれてしまうことがない」ということを全ページの全機能で確認する必要がありました。 正攻法でいくには圧倒的にテスト工数が足りないことが分かっていたため、ブラウザ上での動作確認は念頭に置いておらず、「コンパイル前後で構文上に差分がないこと」を確認するという方針を取りました。 最終的には AST の一致を測る自動テストで 99.9 % 以上を担保することができました。Babel によってどうしても変えられてしまう if 文表記がいくつかあったので、そこだけは等価性を証明できなかったため、手動での確認を行いました。 無事何事もなく済んだのですが、リリースのためにはこの「何事も起きないはず」ということを証明する必要があったというのが最初の小話です。 文字コード問題 歴史のあるサイトのため、中には UTF-8 ではないファイルがそれなりにありました。 基本的にコンパイルされ作成されるファイルは UTF-8 として吐き出されてしまうため、日本語などでコメントが記述されている箇所を特定し、事前に文字コードと内容を修正する必要がありました。 CSS ハックをパースできない問題 いわゆる CSS ハックな書き方は、仕様上正式な構文ではないため、Sass の parser がエラーとして扱ってしまいます。 これらは全て以前サポートしていた IE 向けの記述ばかりだったのですが、幸いなことに現在弊社では IE については 11 のみをサポートしているため、これらの記述は本来不要であったため、事前に手動で削除する対応を行いました。 デプロイサーバー(テスト)で本番デプロイできちゃう問題 以前の弊社のデプロイ方法は、Jenkins からリリーススクリプトを手動実行し、魔法のシェルスクリプトによって本番サーバへと展開されていました。(現在は k8s にのっているため当時のこのフローは存在しません。詳しくは下記リンク先の記事をご覧ください) www.lifull.blog その魔法のシェルが動いている環境が、見出しで言及しているデプロイサーバで、これが非常に厄介ものでした。 歴史的な理由により、いわゆるテスト環境内で閉じた状態で動作確認を行える環境がありませんでした。 デプロイサーバ(本番)とデプロイサーバ(テスト)の環境的な差分はほぼ存在せず、デプロイサーバ(本番)で動いているスクリプトを コメントアウト/修正 して動作確認をする必要がありました。 デプロイの向き先をミスしてしまうと、テスト中のソースで本番サーバに展開されてしまう可能性があり、ここでの動作確認がこの PJ でもっとも精神的負荷のある局面でした。 PJ チーム一丸となり、円陣を組みながらシェルを叩いた(比喩表現)のが功を奏し、本番デプロイは免れ無事にテストは完了できました。 学び 今回の近代化を通して、いくつか学びがあったので最後にまとめさせていただきます。何かに役立てば幸いです。 npm ci の必要性 リリース当初、モジュール更新は npm i コマンドでセットアップしていたため、実行タイミングによっては package-lock.json に差分が生じてしまう構成になっていました。 開発に関しては、都度マージすればいいんじゃないくらいの温度感の話なのですが、ある日社内のステージング環境の一つが突然更新が途絶えたタイミングがあり、調査した結果「定期的に git pull をしてソースを最新化」しているような構成だったため、 package-lock.json に差分が生じたタイミング移行の git pull が失敗してしまっていました。 前述したリリーススクリプトでは「git 上で差分が無いこと(ステージングに何も存在しないこと)」を確認するステップがあり、こちらも大慌てで修正した次第です。 package-lock.json から忠実に再現したい場合に npm ci の必要性を認識させられました。 npm modules の更新作業 開発タイミングやブランチのマージタイミングによって、作業環境の node_moudles が最新の状態ではないことはそれなりに発生すると思いますが、その状態でビルドなどを実行した場合は必須モジュールが存在しないためにエラーとなってしまいます。 この際 Node.js への理解などがある場合は各自で npm ci を再実行してもらえば良いのですが、多くの開発者が出入りしている Reposiroty では全員がそうとは限らず、エラーの問い合わせも何度か発生していました。 この問題に対して、node_modules の更新作業を自動化するスクリプトを、開発コマンドの前に発火させることで対応しました。 イメージ " predev ": " node ./ check - module - install . js " " dev ": " run - p dev :*" スクリプトの内容としては、 package-lock.json の中身と node_modules 配下のモジュールのバージョン確認をするシンプルなスクリプトで、完全に一致していれば何もせず、差分があったタイミングで npm ci を別のプロセスで実行するような内容です。 これを Github Packages として公開し、社内の他のプロジェクトでも利用できるようにしました。 おわりに 今回の PJ のように歴史あるアプリケーションの改善やリファクタリングにおいては、技術的課題に対して導入/解決する技術選定の問題と、それらを取りまく開発環境などにまつわる技術的問題が重なるケースがあり、場合によっては後者が進行を鈍化させてしまうこともあると思います。 また、開発環境の改善をする際は「それらを利用する開発者」のことを第一に考え、導入チームのバイアスがかかり過ぎないよう全体として最善の選択になるように心がけています。 今回のプロジェクトは約 3 ヶ月の期間で開発 2 名 + QA 2 名という体制でしたが、障害なく完遂できました。サポートしてくれた優秀なメンバーや様々な調整/依頼を快く引き受けてくれた多部署のメンバーに恵まれたことも、今回のような部署を跨いだ大きめなプロジェクトを成功させるための大きな要因の一つだと思っています。 これからも、緩やかではありますが、着実な改善を続けていきたいです。
こんにちは。プロダクトエンジニアリング部の中島です。 本稿(連載)では LIFULL HOME'S におけるフロントエンド技術スタックの刷新やリファクタリングの取り組みについて発信していこうと思います。 歴史 LIFULL HOME'SではバックエンドをSymfony2(php) + SinatraベースのBFF(ruby)、フロントエンドをjQueryといくつかのマイクロライブラリによって構築しています。 LIFULL HOME'Sの現在稼働しているサイトの歴史はPJ規模に対して存外古く、2010年末に開発をスタートして現在に至ります。 当時、フロントエンド側のフレームワークは今に比べると随分未成熟だった(初版のbackboneがリリースされたのがこの頃)こともあり、フレームワークの利用を採択するには至りませんでした。 とはいえ、多少の秩序がないことにはLIFULL HOME'S程度の規模感のサイトでも破綻は容易に想像できたため、いくつかの小さな設計とそれを補助するライブラリを組み込みました。 class-likeなオブジェクト設計とネームスペースを提供するJavaScript OOPライブラリ( joo )の導入 今でいうclass syntaxの機構を提供するもの UIパーツごとのViewオブジェクトの生成 Backbone.Viewに当たるもの Viewオブジェクトのイベントバインディング機構のルール化 Backbone.Viewの@eventsに当たるもの Viewオブジェクト間の相互作用を疎に実現するためのメディエイターライブラリ( pubsub.js )の導入 グローバル放出したBackbone.Eventsインスタンスのようなもの 初期設計時は特にBackboneを意識したつもりはありませんでしたが、結果としてこれらはBackboneの基礎的な設計と酷似したものとなりました。 LIFULL HOME'Sというそこまでリアクティブでないサイトにおいてはこの基礎的な設計は案外はまっており、いくつかの問題となるケースは抱えながらも、致命的な局面はさほどなかったように思います。 LIFULL HOME'Sの現行バージョンは最低限の物件検索だけを提供する形で初期リリースを迎え、その後9年は旧サイトからの移行と機能拡充・コンテンツ拡充・UIチューニングを進める日々でした。 これは裏を返せば、この長い期間の中でフロントエンドにおける技術投資は初期設計のみで、途中途中ではサイト全体に影響を与えるような大きな設計の見直しはほぼ為されてこなかったということでもあります。 この間に時代は移り変わり、Bundlerによるmodule機構の提供であったり、TranspilerによるモダンなSyntaxの提供であったり、UIを揃えるための様々なアプローチであったり世間では多くの技術課題に対する解決方法が提示されてきました。 近年のLIFULL HOME'Sは開発当初より随分とFatになり、もう個人的な努力で基礎的な設計に大きな変更を加えることは難しくなり、これらの導入も先送りにされ続けてきました。 しかしながら、開発現場からは日に日にモダンな開発環境を求める声も聞こえるようになったり、大きくなりすぎた現行サイトのフロントエンドパフォーマンスに限界を感じはじめたというのもあり、とうとう2019年末ごろからフロントエンドの開発における技術投資がなされることになりました。 刷新・リファクタリング作業の流れ Sassの導入 Rollup + Babel導入によるモダンシンタックス + モジュール機構の提供 フロントエンドのワーキンググループ結成 レンダリングパフォーマンス向上のための阻害要因の検出と排除 UIの統一性を加速させるためのStyleGuideの導入 テンプレートエンジンのPrecompilerの作成 JavaScriptフレームワークの導入 module化したUIパーツの提供 単に刷新やリファクタリングといっても組織的に活動しないとリスク管理的にニッチもサッチもいかないものと、ある程度のサポートがあれば個人の努力で改善できるものがあると捉えてプロジェクトチームを結成して行うものと、もっとフランクに個人のこうしていきたいを尊重し、レビューやテスト、活動時間の摂政等のサポートをして進めていくワーキンググループを作成してそれぞれで連携を取りながらアプローチしていくことにしました。 プロジェクトチームを結成して遂行したものとしてはSassの導入や Rollup/Babelの導入、テンプレートエンジンのPrecompilerの開発などの開発基盤的なところを整えることにフォーカスし、ワーキンググループサイドではサイトパフォーマンスのチューニングや、運用効率を高める再利用性の高いUI部品の提供などを行いました(現在進行形)。 それぞれの対応の詳細については以下のカテゴリリンク(随時追加)から発信していきます www.lifull.blog
Hi there, my name is Jye Ruey . I'm a Software Engineer in Test (SET) from LIFULL. This article is a translation of 自動システムテストツール「Bucky」OSS化までの道のり - LIFULL Creators Blog , which is written in Japanese by Rikiya Hikimochi . Introduction We SET group publish a test framework "Bucky" as an Open Source Software (OSS). github.com github.com Bucky was a internal tool, which was used for the automation testing. There are a lot of difficulties on the way to OSS. This article will share these difficulties and also share the knowledge we gain on the way. Introduction Why we decide to publish Bucky as an OSS The way to OSS 1. Refactoring 2. Adopting system testing 3. Register on RubyGems.org After release Doesn't prepare the operational procedures ruby version Release the Gem automatically At Last Why we decide to publish Bucky as an OSS "Make LIFULL engineer more visible" Although our main service "LIFULL HOME'S", a real estate and housing information aggregation web service, is well-known in Japan, LIFULL's engineers who make this service are still little-known. By publishing Bucky as an OSS, we suppose that it will make us become more famous. "Hope everyone can use it" Simply, we just hope more people can give Bucky a try. We use Bucky almost everyday and we think Bucky is so convenient. So we will like to share with everyone in the world. "For more feedback from outside, and improving it become more easier to use" Although Bucky is open now, there are still a lot of part can be improved. Because we want to make Bucky better, we will take the external feedback aggressively. And we are looking forward to getting the issue and Pull Requests. The way to OSS 1. Refactoring Because the OSS will release under our company's name, the code should be clean as much as we can. We refactor the code by using static analysis tool Rubocop and CodeClimate. Here are the steps: Integrate Code Climate and Github. And analyze the code and measure the coverage of the unit test. By the integration, create the issue detected by Code Climate on Github. Assign the issue to SET member, and fix refactor the code. Double check the code by using CodeClimate Cli on the local environment By using Code Climate, it is more easier to manage the issue and becomes more efficient in refactoring. Furthermore, Code Climate shows maintainability and coverage on the dashboard. It improves our motivation to refactor the code. CodeClimate rank the code from E to A. Maintainability transition of the repository. The badges also have a good looking. codeclimate.com 2. Adopting system testing Improving the whole quality. We adopt the system testing. To ensure the Bucky command operation and the execution of Bucky test script, which the unit testing can't. By introducing the test framework "Bats", we can make the output and status in Bash, as the expectation in the system testing. github.com The structure of system testing is as follow: 1.Github push triggers CircleCI, and run container as below. ・ container for test (Nginx) ・ selenium-standalone container ・ test execution container (Bucky) 2.With Bats, we can validate the execution output and exit code are same as expected operation. The system test script implement in here . 3. Register on RubyGems.org Making Bucky as a gem package maybe more easier to use. We register on RubyGems.org at the same time that OSS release. bucky-core | RubyGems.org | your community gem host After release Doesn't prepare the operational procedures We want to release as fast as possible, so the entire document didn't prepare well. This will lead some problem as bellow. Problem User don't know how to use it after install the gem package. We only got the simple procedures in README at that time. The way of using Bucky-management doesn't clear. Bucky-core is for test implementation and execution; Bucky-management is for test report. User don't know how to use it even though they read the README. Solution 1: Prepare article for Hands-on guild Quick start With Bucky-management Qiita blog hands-on article (Written in Japanese) Solution 2: Updating README structure Adding the setup step. Procedure of installation and test script implementation is listed in Setup. Procedure of executing of execution is listed in Usage. ■ Before - Bucky-Core - Overview - Feature - Set connection infomation for database - Usage - Install - Run test - Implement test code ~omit~ ■ After - Bucky-Core - Overview - Feature - Setup - Install - Implement test code - Set connecting information for database - Usage - Run test - Rerun test ~omit~ ruby version Because forgetting specific the required_ruby_version in gemspec, the required version is showing greater than v1.0.0 at that time. We fixed that in a hurry. Release the Gem automatically We also make the RubyGems release flow to automatically. The flow will trigger by the release tag on Github. And will execute the flow as follow. Change the version in gemspec Push into master branch Packaging Gem Release to RubyGem More detail is in the bellow article. (in Japanese) RubyGemsリリースを自動化した話 - Qiita At Last Even though the user is not an engineer, test script can be written easily in YAML format. Please have a try. More detail about the procedure, please read the README or the hands-on guide. There are still many parts can be improved. We will keep developing and looking forward the issue and Pull Request.
技術開発部の相原です。 以前にブログで書きましたが、LIFULLでは主要サービスのほぼ全てがKubernetesで稼働しています。 www.lifull.blog Kubernetesをアプリケーション実行基盤として本番運用するためにはデプロイやモニタリング・ログ、セキュリティなど考えることが多くどこから手を付ければよいか困ることがあるでしょう。 そこで今回は既に数年の運用実績のあるLIFULLのアプリケーション実行基盤で利用しているKubernetesエコシステムについて紹介します。 全て書くと数が膨大になるので今回はクラスタ周りを中心に、必要とするソフトウェアの数が多いモニタリング・ログまでとします。(それでも大作になりそうですが...) kubernetes/kops projectcalico/calico coredns/coredns node-local-dns kubernetes-sigs/aws-iam-authenticator kubernetes/autoscaler kubernetes-sigs/descheduler istio/istio prometheus/prometheus prometheus/alertmanager knative thanos-io/thanos grafana/grafana tricksterproxy/trickster DirectXMan12/k8s-prometheus-adapter kubernetes-sigs/metrics-server kubernetes/kube-state-metrics kubernetes/node-problem-detector prometheus/node_exporter kubecost/cost-model kaidotdev/kube-trivy-exporter fluent/fluentd kaidotdev/events-logger 最後に kubernetes/kops github.com kopsはAWSやOpenStackなどへのKubernetesクラスタ構築を行うクラスタ管理ツールです。 KubernetesのIn-place Upgradeにも対応していたり、最近はKubernetesのバージョンアップへの追従の速度が上がっていて、このエントリの執筆時点でKubernetes 1.17に対応していて開発も活発です。 LIFULLでは主にAWSが利用されているため、このkopsを利用してAWS上にKubernetesクラスタを構築しました。 当初はAWSにEKSが無かったためkopsで構築をしましたが、EKSのリリース後も既に後述の認証部分やクラスタのオートスケールを実現をしていてEKSへ移行する旨味が少ないと判断して現在も使い続けています。 kopsからkube-apiserverのAuditログの設定ができるので併せて有効にしてあります。 また、ノードに多くのワークロードをデプロイするとkubeletが稼働するためのリソースが不足してしまうことがあるため、 spec.kubelet.kubeReserved から必要なリソースをkubeletに確保しておくことを忘れないようにしましょう。 LIFULLではスポットインスタンスの活用も進んでいて、preemptiveなワークロードの多くはスポットインスタンスで稼働しています。 単にスポットインスタンスにデプロイしてしまうとAWS側によるスポットインスタンスの停止でPodが安全に終了できないため、スポットインスタンス停止のシグナルを受けてノードのDrainを実行する以下のスクリプトをDaemonSetで動かすことで対処しています。 #!/usr/bin/env bash INTERVAL = 1 # Spot instance terminates 2 min after notification # https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/spot-interruptions.html#spot-instance-termination-notices SPOT_LIFESPAN = 120 while true; do test " $( curl -sSL http:// 169 . 254 . 169 . 254 /latest/meta-data/spot/termination-time -o /dev/null -w ' %{http_code} ' ) " -eq 200 && break sleep $INTERVAL done ( sleep $(($SPOT_LIFESPAN - 30 )) && kubectl delete pod --field-selector =" spec.nodeName= $NODE_NAME ,metadata.namespace!=kube-system " --all-namespaces --wait =false ) & kubectl drain $NODE_NAME --force --ignore-daemonsets --delete-local-data sleep $SPOT_LIFESPAN NODE_NAMEはKubernetesのDownward APIによってデプロイされているノードの名前です。 projectcalico/calico github.com kopsは構築時にCNIを指定できるためそこでcalicoを採用しています。 calicoはBGPを用いたハイパフォーマンスなL3ネットワークを構築するためのCNIプラグインで、NetworkPolicyの利用も可能です。 kopsからAWS VPCというCNIプラグインを採用した場合、NetworkPolicyが利用できなかったり、PodがそれぞれVPC内のIPを使うためIPが枯渇する可能性があることにご注意ください。 coredns/coredns github.com 同様にDNSのコンポーネントについても選択できるためCoreDNSを採用しました。 CoreDNSはCNCFのGraduated Projectで、Kubernetes 1.13からはデフォルトでCoreDNSが利用されるようになりました。 kopsを用いてCoreDNSをインストールした場合、そのままではSIGTERMを適切に解釈せずRolling Update時にリクエストを取りこぼしてしまうため、 インストール時に以下のようにlameduckプラグインを有効にする必要があることにご注意ください。 $ kubectl get configmap -n kube-system coredns -o yaml | sed -r ' s/^(\s+)health$/\1health {\n\1 lameduck 5s\n\1}/ ' | kubectl apply -f - $ kubectl rollout restart -n kube-system deployment/coredns またkopsを利用してCoreDNSを有効にすると共にインストールされるcoredns-autoscalerについてもデフォルトだと適切にスケールしない可能性があるため調整を忘れないようにしてください node-local-dns github.com node-local-dnsはその名の通り、Nodeごとに名前解決の結果をキャッシュするためのコンポーネントです。 これが必要となる詳しい理由は以下にありますのでご参照ください。 kubernetes.io またクリティカルなところで言うと、kube-proxyのiptables実装を利用している際にUDPのEndpointの一覧の更新がうまくいかないケースがあるというものがあります。 kubernetes/proxier.go at 4505d5b182c85388627f1b5a648a2da377026871 · kubernetes/kubernetes · GitHub kube-dnsはそのまま使うとUDPで使うことになるためこの問題をはらんでおり、一度現象が発生してしまうとEndpointの更新が走るまで一部の名前解決のリクエストがblackholeされることになります。 node-local-dnsはこの問題に対しても有効に機能します。 Motivationの項にある通りiptablesによるDNATとConnection Trackingをskipすることができることに加え、node-local-dnsからkube-dnsへのリクエストはTCPにアップグレードされるため、少なくともkube-dnsに関してはこの問題が発生しなくなります。 ipvs実装はGAしたものの未だIssueが目立つ状況でお困りの方も多いと思いますので、node-local-dnsがお勧めです。 当然、名前解決のキャッシュによるパフォーマンス向上も期待することができます。 kubernetes-sigs/aws-iam-authenticator github.com こちらは旧heptio/authenticatorで、kube-apiserverに対するIAM Roleでの認証を実現するためのものです。 我々のKubernetesクラスタはMulti Tenantで運用しているため、各チームごとにIAM Roleを作成し権限の管理をしています。 kubernetes/autoscaler github.com kopsにはノードをオートスケールさせる機能がないため、リソースの確保量に応じてノードをスケールアウトさせるためにkubernetes/autoscalerを利用しています。 同様にリソース確保量の少ないノードをスケールインする機能も備えていて、スケールイン時にはDrainをして安全にワークロードを退避させることができます。 AWSでkubernetes/autoscalerを利用する際に注意しておかなければならないのは、AWSのAutoScalingGroupにはAvailability Zone間で台数を均等に保つための挙動があるということです。 この処理はAutoScalingGroup側で行われるため、ノードのDrainが行われずPodを安全に終了することができません。 これを防ぐためにはAvailabilityZoneごとにNodeGroupを分ける必要があります。 また前述のスポットインスタンスの活用に際してもスポットインスタンス用のNodeGroupを作成していて、kubernetes/autoscalerの持つexpanderという機能を利用してスポットインスタンスが優先的に利用されるように設定しています。 kubernetes-sigs/descheduler github.com 前述のkubernetes/autoscalerを利用してノードのスケールアウトをしていると、スケールアウト直後にノード間でリソース確保量にばらつきが出るという問題があります。 極端にリソース確保量の多いノードが存在している場合、そのノードでQoSの低いPodのEvictが起きる可能性が高くなってしまいます。 その問題を解決するのがdeschedulerです。 ポリシーを設定することで閾値を超えたノードからPodを対象のノードに移動させることができます。 RemoveDuplicates を有効にすることで同一ワークロードのPodが複数同じノードにある場合に退避させることもできますが、そういった制約を持たせたい場合は素直に antiAffinity を設定した方がいいため利用していません。 istio/istio github.com IstioはEnvoyをデータプレーンとして利用するサービスメッシュで、Kubernetesクラスタに対して優れたTraffic Managementや高いObservabilityを提供します。 Istioに関しては過去のエントリで触れているのでこちらも併せてご覧ください。 www.lifull.blog 我々のチームでは開発者がKubernetes Manifestを書く労力を削減するため、Kubernetesを利用するにあたってのプラクティスを盛り込んだManifestを生成するツールを提供しています。 このツールから生成されるManifestではIstioの利用が前提となっており、サービス間通信のCircuit BreakerやRetrying, Timeoutなどがデフォルトで有効化されるようになっていて、これにより多くのmicroservicesが安全に稼働するようになりました。 同様にIstioレイヤでのアクセスロギングも有効化されるようになっており、このツールによって開発者が意識することなくLIFULL共通のアクセスログフォーマットでログが吐けるようになっています。 アクセスログフォーマットが共通されたことでアプリケーション実行基盤として共通のログ分析を提供するにあたっても役立っています。 安くない運用コストを割いてIstioを導入しても実際に使われなければ絵に描いた餅となってしまいます。 このようにManifest生成ツールと組み合わせることで社内でIstioが広く利用される状況が作られました。 prometheus/prometheus github.com PrometheusはCNCFのGraduated Projectのモニタリングシステムです。 KubernetesのService Discoveryを使ってメトリクスを収集することができるためKubernetesとの親和性が高く、余計なエージェントを入れることなくクラスタの監視を開始することができます。 我々のチームでは新規開発プロジェクト向けのGitHubのTemplate Repositoryを提供していて、このTemplate Repositoryを利用してリポジトリを作成することでスムーズにKubernetesクラスタにアプリケーションをデプロイできるようになっています。 このTemplate RepositoryにはPrometheusのアラートのルールの自動生成の機能が備わっており、開発者が簡単にPrometheusでアラートの設定ができるようになっています。 また、Prometheusは単体で長期のメトリクスを保存することは想定されておらず、長期のメトリクスを保存するためには後述のThanosのようなソフトウェアを別途用意する必要がある点にご注意ください。 prometheus/alertmanager github.com AlertmanagerはPrometheusから送信されてきたアラートをSlackやWebhookなどを通して通知するためのコンポーネントです。 前述のアラートのルール自動生成時に適切なSeverityを付与するようにしていて、AlertmanagerではSeverityに応じて適切な頻度で適切なSlackチャンネルにアラートを通知するようになっています。 またAmazon SNSをイベントソースとしてLambda FunctionによるAmazon Connectを利用したオンコールシステムも開発していて、Alertmanagerと組み合わせることで必要に応じてオンコール担当者へ電話での通知も可能となっています。 knative github.com KnativeはIstioなどのサービスメッシュ上に構築する、いわゆるサーバレスワークロードを実行するためのソフトウェアです。 イベント駆動のワークロードや、前述のAlertmanagerのWebhookと連携させてアラートに伴うオペレーションの自動化に利用しています。 Kafkaを用いたChannelの永続化やメッセージングの提供も準備しているため、今後多くのワークロードで利用していく予定です。 thanos-io/thanos github.com ThanosはCNCFのSandbox ProjectのPrometheus向けLong Term Storageです。 Object Storageにメトリクスを格納することで長期のメトリクスを安価に保存できるという特徴を持ちます。 多くのクラウドプロバイダはS3やGCSのようなObject Storageを提供しているため単にそれらを利用するだけになります。 同種のソフトウェアの場合、自前で永続化されたストレージを用意する必要があったりCassandraやDynamoDBのようなデータストアを要求されますが、Thanosの場合は比較的セットアップが容易という点もメリットです。 ただし、Indexのキャッシュにmemcachedを利用できるものの同種のソフトウェアと比較して相対的にメトリクス取得の速度が遅くなるため、Thanos側でサポートしているShardingを利用しながら高速化に努める必要があります。 また、長期のメトリクスを用いたPrometheusのRuleの評価に関してもいい方法がなくこれも課題の一つです。 Long Term Storageであると共にPrometheusの冗長性の問題も解決していて、重複メトリクス除去の機能を備えているため複数台のPrometheusを立てて冗長化しつつ、Thanosを介してクエリすることで重複を除去して結果を返すことができます。 grafana/grafana github.com GrafanaはPrometheusなどをバックエンドとするVisualizationツールです。 先日リリースされたGrafana v7ではJaegerやZipkinといった分散トレーシングもバックエンドとして利用できるようになり、ログストレージにも同様に対応していることからモニタリングに関する一切の情報をGrafanaで表示することができるようになりました。 我々のチームでは開発者が簡単にダッシュボードを作成できるようgrafonnet-libというGrafanaコミュニティが提供しているライブラリをベースにJsonnetの整備も進めており、現在全てのダッシュボードがJsonnetで管理されています。 モニタリングのプラクティスに則ったダッシュボードのパネルを多く提供していて、開発者は必要なパネルをJonnnet形式で配置するだけで洗練されたダッシュボードを手に入れることができます。 tricksterproxy/trickster github.com TricksterはComcastが開発するPrometheusのような時系列データベースの前段に置くキャッシュサーバです。 前述のようにThanosは運用が簡単な一方でメトリクスの取得が遅いため、このTricksterを挟むことで高速化を図っています。 あらかじめよく使われるGrafanaのダッシュボードから叩かれるクエリのキャッシュを裏で生成しておくことで、ダッシュボード閲覧時の体験をよくするような工夫もしてきました。 DirectXMan12/k8s-prometheus-adapter github.com DirectXMan12/k8s-prometheus-adapterはKubernetesのHorizontal Pod AutoscalerでPrometheusのメトリクスを利用するための custom.metrics.k8s.io 実装です。 CPUの使用率でのスケールアウトが適さないケースは多くあり、そういったニーズに応えるべくPrometheusのメトリクスを利用できるようにしています。 例えば以前のエントリで紹介したGitHub ActionsのSelf-hosted Runnerもin-queueなJobの数をPrometheusのメトリクスとして公開しておいて、その値を元にスケールアウトさせています。 www.lifull.blog 実験的な試みとしてあるメトリクスの未来の予測値をもとにしたスケールアウトも始めていて、これも同様に時系列分析の結果をPrometheusに格納することで実現しているなど、こういった柔軟なスケールには必要不可欠なコンポーネントです。 kubernetes-sigs/metrics-server github.com metrics-serverは kubectl top やHPAを用いたCPUによるスケールをする際に必要なコンポーネントで、元々使われていたHeapsterというソフトウェアの後継です。 kubelet内のcAdvisorからメトリクスを収集してAPIを通してそのメトリクスをクラスタに対して公開する役目を持ちます。 Prometheusは直接cAdvisorからメトリクスを取得することができるのでHPAでCPUによるスケールをさせたい場合は前述のprometheus-adapterを用いることで不要にも思えますが、 kubectl top などでも利用されるため基本的には導入することになると思います。 Kubernetesのメトリクスパイプラインは少し複雑なのでこちらの資料がおすすめです。 github.com kubernetes/kube-state-metrics github.com kube-state-metricsはKubernetesのObject単位でのメトリクスを生成するコンポーネントです。 似たような名前のmetrics-serverとは似て非なるもので、metrics-serverはkubeletをデータソースとするのに対し、kube-state-metricsではkube-apiserverをデータソースとし、例えばPodのCPUのRequestsやDeploymentのReplicasなど、Kubernetes Resourceに関するメトリクスを公開します。 その点についてはドキュメントでも注記されています。 github.com HPAのMaxに達してしまっている、DeploymentのRolloutが止まっているなどといったアラートは運用する上で必須のため、こちらもmetrics-serverと同様に基本的には導入することになるでしょう。 kubernetes/node-problem-detector github.com node-problem-detectorはその名の通りNodeの異常を検知するためのPrometheus Exporterです。 基本的には /dev/kmsg をデータソースとしたKernelレベルでの異常の検知に利用され、例えば task xxx blocked for more than 120 seconds のようなハングやOOM Killingなどを検知することができます。 Prometheus向けにメトリクスも提供するため、既存のPrometheusによる監視にこういったKernelレベルのモニタリングを組み込むことができます。 prometheus/node_exporter github.com node_exporterはNode単位でのメトリクスを公開するためのPrometheus Exporterです。 Node全体でのCPUやネットワークの利用量に加えて、PSIに対応しているKernelであればCPUやメモリのPressureを公開することができます。 PSIとはLinux Kernelの機能であるPressure Stalled Informationの略で、詳しくは以下をご覧ください。 PSI - Pressure Stall Information — The Linux Kernel documentation これは有名なモニタリングの方法論USEメソッドで監視を推奨されているSaturation、つまりリソースの飽和状態を表すメトリクスで、PSIから取得したメトリクスを監視することでリソースのOvercommitによるパフォーマンスの低下に気づくことが可能となります。 参考 www.brendangregg.com kubecost/cost-model github.com kubecost/cost-modelはクラウドプロバイダのインスタンスタイプに応じたインスタンスの金額を公開するPrometheus Exporterです。 クラスタ上に存在するNodeの一覧を取得し、Nodeそれぞれのインスタンスタイプからインスタンスの料金、CPU1コアあたりメモリ1GBあたりの推定料金をメトリクスとして生成します。 これを用いて各アプリケーションのダッシュボードに推定の月次コストを表示するようにして、開発者に日々コストを意識してもらうようにしています。 金額はフィクションです。 kaidotdev/kube-trivy-exporter github.com kaidotdev/kube-trivy-exporterは拙作のクラスタ内に存在する全てのDocker Imageに対してaquasecurity/trivyを実行し、脆弱性情報を公開するPrometheus Exporterです。 社内のセキュリティエンジニアから要望を受けて開発し、申し訳程度にOSSとして公開しているものです。 www.lifull.blog これも同様に各アプリケーションのダッシュボードに脆弱性情報を表示するために利用していて、開発者が日頃から脆弱性を意識するような文化づくりに貢献しています。 fluent/fluentd github.com fluentdは言わずと知れたログコレクタで、具体的には fluentd-kubernetes-daemonset をデプロイしてNodeのログを集約しています。 LIFULLではログから得られたレスポンスタイムの統計情報をダッシュボードで閲覧するためにPrometheusのメトリクスとして公開したり、ログフォーマットの統一や各種変換処理をfluentdのレイヤで行なっているため、スケールさせたい単位でfluentdクラスタを分けてfluentd-kubernetes-daemonsetを入り口として多段のログパイプラインを構築しています。 ここでログが欠損するとクラスタ全体のログが失われてしまうため、bufferの設定には気を使う必要があります。 kaidotdev/events-logger github.com こちらも拙作のコンポーネントで、kaidotdev/events-loggerはKubernetesのEventsを単に標準出力するだけのものです。 もともと監視にDatadogを使っていたのでKubernetesのEventsをDatadogで確認することができていたのですが、Prometheusに移行してKubernetes Eventsを集約して監視することができなくなったため開発しました。 fluentd側でその情報をPrometheusにも公開していて、ログの監視と同じようにKubernetes Eventsを監視できるようにしています。 例えばContainerGCFailedといったNodeの異常を示すものや、大量のNodeNotReadyの発生に気付けるようアラートを仕込んでいます。 最後に このようにKubernetesをProduction Gradeで運用しようと思うと多くのソフトウェアの導入が必要となります。 一部はDatadogのようなサービスを利用することで置き換えることはできますが、これ以外にもデプロイやセキュリティ、内部統制など考えること多くあります。 導入をお考えの方には慎重な検討の手助けに、既に導入済みの方には少しでも参考になれば嬉しいです。
こんにちは。エンジニアの松尾です。普段はLIFULL HOME'Sの売買領域でエンジニアチームのマネジメントを担当しています。 私が所属している部署では定期的に登壇スタイルでの社内勉強会を開催しています。 直近の会を「 社内勉強会をオンラインでいい感じに開催する 」という点を念頭に開催したため、その構想からふりかえりまでをまとめます。 これからオンラインで勉強会を開催したいという方の参考になれば幸いです。 目的の設定 私の所属組織のエンジニアは「強い個人・最高のチームになることで、価値創造を加速させ続ける」という姿勢で日々の業務に取り組んでいます。このことばの前半部分を下記のように解釈し、勉強会の目的としました。 強い個人 : エンジニアとしてのテクニカルスキルを向上させること 最高のチーム : チーム内のコミュニケーションを活性化させ、コラボレーションを生み出すこと テクニカルスキル向上への影響の部分はテーマ選定・発表内容によって決まるところが大きく、今回の記事の本筋からは外れるため特に触れません。 運営の組織づくり 現組織での勉強会開催は3回目になります。当初はマネージャー主導のトップダウン形式でしたが、現在は有志のメンバー(新卒2、3年目のメンバーが大半)が主体となって運営をしています。 メンバーにはキックオフの際に前述の(割と大雑把な)目的を伝えていますが、実現方法や準備の進行はおまかせしています。 そのため、以降のお話は運営メンバーを見守っていた私が議論を思い出しながら整理した記録です。 実施形態の検討 勉強会当日は出社するのか 新型コロナウイルスへの対応状況は日々変化しています。 実は以前に企画した勉強会が土壇場でオンライン開催となったことがあり、その場でのふりかえりでは「参加者間でコミュニケーションがとれない」「次回は対面でやれたらいいね」という話をしていました。 とはいえ現状で一同に会することが難しいという状況が改善されるかは読めなかったため 「 じゃあ最初からオンライン前提の勉強会を作ろう 」という方向性にしました。 参加者間でコミュニケーションは取れるのか 社外での勉強会と比較して、今後も一緒に働いていく仲間としては「 その場限りではない継続的な関係を作っていきたい 」という思いが強く、実現のために ツール選定 と 場作り の2点を中心に議論をしました。 ツール選定 当日だけではなく後日にも活発な議論が生まれることを期待して、要件に合うものを探しました。今回は下記の4つのツールを活用しました。 📞 オンライン通話: Zoom 長時間繋いだままでも安定する 後から/参加できなかった社員も見られるように録画ができる 他のツールより部屋の分割(ブレイクアウト)が容易 ※結局使用せず 📩 登壇者への質問・参加者間の議論: Chatwork 議論のログが残る ※Zoomの録画時にチャットログが出力できることを知らず 事前に参加者に告知ができる 🗂 質疑のまとめ: Google Jamboard 議論の要点や盛り上がりをわかりやすく表現できる 複数人で編集ができる 📊 投票・アンケート: Google Form スムーズにフォームの配布と集計が行える 投票のログが残る 場作り 「質疑の短い時間では深い議論ができない」「登壇スタイルでは聴講者との間に距離ができる」という懸念があったため、 オフ会 という名称で、会が終わった後に別途セッションを設けることにしました。 登壇者に質疑で聞けなかったことやこぼれ話を聞くことを主目的に、ファシリを置かない・ご飯を食べていい という雑談ベースでの会話の場にしています。 開催当日 登壇発表 「LIFULL HOME'Sの実装の歴史から学ぶ『設計』」 というテーマで3人のスピーカーが登壇しました。 自部署内外から人が集まり、常時50名程度が参加している状態でした。※出入りは自由 「CSSの設計」の発表 オフ会 20名程度が集まり、登壇時には語られなかったこぼれ話を聞くことができました。 ※撮影しそこねたため写真は割愛 登壇時とオフ会の議論もあとから振り返られるように、質疑やコメントはチャット上だけではなくJamboardにもまとめています。 CTOが登壇した「現APIの設計」の議論の例 ふりかえり 参加者からのフィードバック 後日聴講者と登壇者の両方を対象にアンケートを取った結果から抜粋したコメントです。 横の繋がりを強化する大変良い取り組みだと感じました 顔出しはありのほうが発表はやりやすいですね! 家の回線の問題が…。 オフ会はもうちょい人がばらけてくれるとよかったなーと思います(T_T) オフ会参加はネタと時間の都合によりそうです! スキルアップ・コミュニケーションの場として一定の評価は得つつも、オンライン特有の問題や会の運営上の課題が見つかりました。 オンライン開催のメリット・デメリット 前述のアンケート結果も踏まえて、開催してみて感じたメリットやデメリットをまとめます。 運営 🙆‍♂️ 準備の手間が少なく済む 場所取りや会場設営が不要で、事前準備にかかる手間はオフラインと比べて大きく削減できました。 またオンライン通話ツールの録画機能が使えれば、撮影がRecボタンひとつで完結するため非常に楽ができます。 🙅‍♂️ オンライン特有のオペレーションが発生する 長時間/多人数で利用できる有料アカウントなどの確保 ビデオ撮影に関する事前アナウンス 聴講マナーの周知(基本はミュート・反応は大げさに など) 今回は有料アカウントは取得済みであり、アナウンスは事前に段取りを決めて勉強会開始時に行いました。 登壇者 🙆‍♂️ リアルタイムでコメントが見える/聞ける 発表時間にゆとりがある場合は聴講者から複数のコメントを吸い上げるなど、うまく活用できれば柔軟な発表ができそうです。 🙅‍♂️ 発表に対する反応が見えづらい ビデオOFFの参加者が多く、アイスブレイクや小ネタを挟んでテンポを掴むというところが難しいようでした。 Zoomであればリアクション機能を積極的に使用したり、ビデオをONにすることを推奨しておくことでもう少し改善できそうです。 また、登壇者のネットワーク環境が整っていないと場がグダグダになる可能性があるため、不安がある場合は事前対応を考えておく方が良さそうです。 聴講者 🙆‍♂️ 参加のハードルが低い 部屋のキャパシティが基本的にはないことと作業の片手間でも参加しやすいことがあり、気軽に参加できます。また資料や音声が手元で見られる/聞けるため、通常の勉強会より視聴しやすいという意見もありました。 🙅‍♂️ 対面での懇親会のような距離感は出すことは難しい 今回はオフ会というカジュアルな場を設けてみましたが、複数の議論を並列に進められないため、結局1対多の印象になってしまうのが難点でした。 まとめ 単に発表内容から学ぶ以上に発見の多い勉強会となりました。何より慣れないやり方に試行錯誤しつつも、事後アンケートでは多くのお褒めのことばを頂き一安心でした。 今の状況は逆にチャンスとも捉えられると思います。次回以降も毎回新しい工夫を取り入れながらスキルアップ・コミュニケーションを加速させていきたいと思います。
技術開発部の相原です。 今回は、2019年末にリリースされた GitHub ActionsのSelf-hosted runners をKubernetes上で動かして自動化に取り組んでいる事例を紹介します。 背景 LIFULLではプライベートネットワーク上に存在するRDBMSなどのリソースを利用したアプリケーションのテストを実行するといった用途で、古くからJenkinsが運用され続けてきています。 こういったテストの実行などはプライベートネットワークに疎通できないCircleCIやGitHub Actionsでは実行することができず、プライベートネットワーク上に構築されたJenkinsなどのサーバがリポジトリの更新を検知して実行する必要があります。 そのためLIFULLでもJenkinsを運用し続けてきたわけですが、ご多分に漏れずこれまでにバージョンアップやJenkins職人問題・スケーラビリティの問題に悩まされてきました。 そこで、我々はこれらの問題を解決すべくGitHub Actions Self-hosted runnersに着目しました。 GitHub Actions Self-hosted runnersとは、GitHub ActionsをGitHub hostedな環境ではなく自分たちのインフラで動かすための2019年末にリリースされたランタイムです。 これを利用してプライベートネットワーク上でGitHub Actionsを動かすことで前述のテストをJenkinsから移行することができ、Jenkinsサーバを撤廃できるのではないかと考えました。 GitHub Actions Self-hosted runners on Kubernetes 前回の LIFULLが主要サービスの(ほぼ)全てをKubernetesに移行するまで で紹介しましたが、現在LIFULLでは大部分のアプリケーションがKubernetes上で動いています。 当然そのKubernetesクラスタはプライベートネットワーク上で稼働しているため、以下のようなメリットを期待してGitHub Actions Self-hosted runnersをこのKubernetes上で稼働させることにしました。 日々の運用の余剰リソースを利用してrunnerを動かすことでのコストカット 待ち時間のないジョブ実行 柔軟なリソース確保 Kubernetesの宣言的なAPIを利用することによる脱Jenkins職人 しかし、Kubernetes上で動かすといっても公式からのサポートが受けられるわけではありません。 そこで、KubernetesのCustom Resource DefinitionsというKubernetesのAPIを拡張して任意のリソースを定義することのできる機能を利用してGitHub Actions Self-hosted runnersを動かすためのソフトウェアを余暇の時間で開発して導入しました。 github.com このソフトウェアを利用すると以下のようなKubernetes Manifestをデプロイするだけで任意のイメージのGitHub Actions Self-hosted runnersが指定したリポジトリに紐づけられて起動します。 apiVersion : github-actions-runner.kaidotdev.github.io/v1 kind : Runner metadata : name : example spec : image : ********.dkr.ecr.ap-northeast-1.amazonaws.com/homes repository : lifull/homes tokenSecretKeyRef : name : credentials key : TOKEN 本来、GitHub Actions Self-hosted runnersはGitHubから提供されているランタイムをサーバにインストールして利用することを前提とされています。 ref. セルフホストランナーの追加 そのためこれをコンテナとして動かすことを考えると、あらかじめこのランタイムがインストールされたイメージを用意して利用することになってしまいますが、アプリケーションのテストをrunnerに実行させたいといったケースで毎回そのアプリケーションのイメージを再ビルドしてランタイムをインストールするということは現実的ではありません。 そこでこの問題を解決するため、少々いびつではありますが kaidotdev/github-actions-runner-controller は以下のような動きをします。 github-actions-runner-controllerの動き これらの動きは全てコントローラの裏に隠されていて 、利用者は適当なイメージをKubernetes Manifestに書いてデプロイするだけでそれが透過的にrunner入りのイメージとして再ビルドされて起動するようになっています。 そのため、通常のアプリケーションのデリバリーパイプラインをそのまま利用してこのマニフェストをデプロイすることで、runnerのイメージを最新のアプリケーションに追従させることが可能となりました。 Spinnakerを利用している場合はイメージのタグをSpinnakerが勝手にいじってくれるのでそのままこのマニフェストを加えるだけですし、GitOpsなデリバリーパイプラインを使っている場合もタグ更新のワークフローを同様にこのマニフェストにも適用するといった感じです。 不自由なくGitHub Actions Self-hosted runnersを使うために必要な機能は大体備えているはずで、環境変数やファイルのマウント、コンテナリソースの定義に加えて kaidotdev/github-actions-exporter を利用してin-queueなジョブの数を公開しているため、カスタムメトリクスを利用したスケールアウトによってin-queueなジョブの数に応じたコンテナの増減も可能です。 github.com 詳しい使い方に関しては README をご覧ください。 GitHub Actions Self-hosted runners on Kubernetesによる自動化 こうしてLIFULLは誰もが自由にGitHub Actions Self-hosted runnersを起動できる環境を手に入れました。 GitHub ActionsはGitHubのあらゆるイベントを元にジョブを実行することができ、様々な自動化タスクへの適用が期待できます。 そして、誰もが自由にGitHub Actions Self-hosted runnersの導入が可能になったことによって、多くの開発者がそれぞれで気軽に自動化に取り組むことができるようになりました。 これは今までのJenkinsの運用や、クラウドサービスとしてのCircleCIやGitHub Actionsの利用では実現できないことでした。 我々のチームとしてもこの仕組みを利用して以下のような自動化に取り組んでいます。 アプリケーションのリリースパッケージでのテスト実行 アプリケーションのデプロイ コンピューティングリソースを利用する解析処理 クラウドサービスを利用すると待ち時間が長かったり利用できるコンピューティングリソースに制限があったりしますが、この仕組みに載ることで格安で待ち時間なく大量のコンピューティングリソースを使えるようになりました。 同じことを実現できるソフトウェアは多くありますが、Kubernetesの宣言的なAPIの利用によって簡単に自分たちのインフラで動かすことができ、GitHub Actionsという開発者に慣れ親しんだフォーマットで自動化タスクを記述できる安価でスケーラブルかつ柔軟なこの仕組みには一定の価値があると考えています。 自動化タスクにお悩みの方は試してみてはいかがでしょうか。
こんにちは!クリエイターの日運営委員の工藤です。 新型コロナウイルスの影響により弊社でも現在在宅勤務になっており、非接触のコミュニケーションの機会が増えてきました。社内でのミーティングも基本ビデオ通話になっております。 そんな中、社外ではclusterを利用したバーチャル空間でイベントなどが行われています。それに参加した社員から「社内でもやりましょう!」と声をかけていただき、他の社員にもこのような技術にも触れてほしいという思いから一緒に企画しました。 cluster.mu どんなイベント? イベントとしては、まずは誰でも気軽に参加できるように各自オリジナルのアバターを作り披露するというイベントを行いました。少し興味あるなーって方はデフォルトのアバターで見学も可能です。 バーチャル空間内のスクリーンにPDFを投影することができるので、各自自分のアバターの紹介をしてもらいました。 自分自身をバーチャル空間上で扱えるようにしたものもあったり かわいい女の子のアバターを作ったりもしていました。 それぞれ個性が溢れていて、おもしろかったです! これはどうやって作ったかも発表していて勉強にもなりました。 最後にイベントらしく集合写真も撮りました! 弊社受付スペースも再現 弊社2Fの受付スペースを再現したものをLIFULL Labの社員に1、2時間で作っていただき遊びました。 オレンジのオブジェクトを使って弊社ロゴを再現しようとしました。 みんな運んでますね! しかし、難しくでできませんでした、、、 さいごに エンジニアだけでなく企画など様々な職種の社員に参加していただけました。 イベントをやってみてバーチャル空間でもさまざまなイベントができそうだなと感じました。今回、何名もの社員がアバターを作成してくれたので、また違うバーチャル空間でのイベントも企画していければな思います。 弊社では、一緒に働くメンバーも募集しています。 recruit.lifull.com
こんにちは。LIFULLでデータアナリストをしている宮野です。 普段はサービス周りのデータ分析を行っているのですが、TVCMの効果検証を行う機会があり、その際CausalImpactという時系列因果推論フレームワークを使用したのでご紹介いたします。 【目次】 はじめに Pythonを用いたCausalImpact データの準備 効果検証 共変量の確認と選定 / 周期性(シーズナリティ)の付与 ①共変量 ②周期性(シーズナリティ) RのCausalImpactとの結果比較 RでのCausalImpact実装 PythonとRの検証結果比較 おわりに はじめに CausalImpactとは?  →Googleがリリースした時系列因果推論の"R"パッケージです。 そう。Rのパッケージです。当然Rを使って効果検証を行うのが通常だと思います。 なのですが、私自身Pythonを使用することが多く、どうせならPythonで分析できないかと調べたところ、有志が作成した"Pythonでも使えるCausalImpact"のライブラリを発見しました。 せっかくなのでこちらを使用してみたのですが、実装していく中で、非公式?故か日本語での参考記事があまり見当たりませんでした。そこで少しでもPython版を使用する方の参考になればと思い、こちらを執筆しました。 今回使用したPythonのライブラリはこちら。 dafiti/causalinpact: https://github.com/dafiti/causalimpact 参考として、記事後半でRのCausalImpactにおける検証結果との比較も行っています。 なお、この記事は「とりあえず手を動かして実行できる」ことを目的にしているため、難しい用語の使用はできるだけ避けました。同じ理由で、前提となる統計学や因果推論の知識も割愛しています。 ※実務で使用する場合は必ず有識者に相談やレビューをしてもらったうえで効果検証をしてください Pythonを用いたCausalImpact データの準備 今回、CausalImpactを使用して「 ある地域では、TVCMを放映したことによってサイト流入数は増加したか? 」という検証を行うものとします。 検証を行うためには目的変数となる"TVCM放映エリア"のサイト流入数と、共変量(≒説明変数)となる"非TVCM放映エリア"のサイト流入数のデータが必要になります。 これらを踏まえて、用意したデータの概要は以下の通りです。 データ取得期間:2019年10月1日〜2020年2月29日 TVCM放映期間:2020年1月8日〜 TVCM放映エリア(Aエリア):サイト流入数(目的変数) TVCM非放映エリア(B,C,Dエリア):サイト流入数(共変量≒説明変数) ※実際のデータを使用することはできないため、サンプルデータを用意しました Python版CausalImpactのインストール pip install pycausalimpact #必要なライブラリをインポートする import pandas as pd import numpy as np from statsmodels.tsa.arima_process import ArmaProcess from matplotlib import pyplot as plt from causalimpact import CausalImpact ##CausalImpactのライブラリ %matplotlib inline #データの読み込み(今回はexcel) df = pd.read_excel( 'sample.xlsx' ) #データの確認 df.head() データの読み込みが完了しました。 後述しますが、CausalImpactを実行する際にどの時点で介入があったのかindexで指定する必要があります。 今回はわかりやすいようにdateをindexに指定します。 #CausalImpactを実行する際、わかりやすいように日付データをindexに指定する df = df.set_index( 'date' ) df.head() これでCausalImpactを実行する準備ができました。 効果検証 実際に効果検証を行っていきます。 #TVCM放送前と後を指定する pre_period = [ '2019-10-01' , '2020-01-07' ]  #TVCM放映開始前 post_period = [ '2020-01-08' , '2020-02-29' ]  #TVCM放映開始後 #CausalImpactの実行 ci = CausalImpact(df, pre_period, post_period) ci.plot(figsize=( 22 , 20 )) 縦に引かれた黒の破線は、TVCM放映開始後である2020-01-08となります。 上段に図示された黒の実線は実績値、青の点線はTVCMを放映しなかった場合の予測値となります。 中段に図示されたものがTVCM放映の効果になりますが、2020年1月8日にTVCMを放映してからサイト流入数が増加したと言えそうです。 下段に図示されたものは増加したサイト流入数の累計値です。 結果のサマリーを確認するには以下を実行します。 #サマリーの確認 print (ci.summary()) Posterior Inference {Causal Impact} Average Cumulative Actual 285.22 15116.91 Prediction (s.d.) 206.92 (10.57) 10966.99 (559.96) 95% CI [186.82, 228.24] [9901.52, 12096.52] Absolute effect (s.d.) 78.3 (10.57) 4149.92 (559.96) 95% CI [56.99, 98.4] [3020.39, 5215.39] Relative effect (s.d.) 37.84% (5.11%) 37.84% (5.11%) 95% CI [27.54%, 47.56%] [27.54%, 47.56%] Posterior tail-area probability p: 0.0 Posterior prob. of a causal effect: 100.0% For more details run the command: print(impact.summary('report')) 共変量の確認と選定 / 周期性(シーズナリティ)の付与 ここからは少し踏み込んだ話をします。 ①共変量 今回のデータでは、共変量(≒説明変数)としてB,C,DのTVCM非放映エリアのデータがあります。 この共変量は予測モデルに必要となる大事な要素となります。 共変量がモデル作成にどれだけ使われている(モデルに影響している)のかを確認するために以下を実行します。 ci.trained_model.summary() P>|z| と coef に注目します。今回の結果だとエリアB,C,Dを使用して予測モデルを作成していそうです。 例えば coef が"0"になるような共変量があった場合、その共変量は予測モデル作成には使用されないようになっています。 そのため、使用された共変量を確認し、 "使用されなかった共変量を削除→新たな共変量を追加" という作業を繰り返すことで、モデル精度を高めることができそうです。 ②周期性(シーズナリティ) 次に周期性(シーズナリティ)について説明します。 時系列データには、天候や季節、曜日などによって数値が変わるような季節変動が含まれる場合があります。季節変動が含まれるデータの場合、より良い予測モデルにするためには季節変動を考慮する必要があります。 ここでは例として「平日より土日のほうが検索数が多いため1週間(7日間)ごとの周期性がありそう」と仮定します。 この周期性を考慮してCausalImpactを実行するには"nseasons"を追記することで実現できます。(デフォルトは1) #CausalImpactの実行 ci = CausalImpact(df, pre_period, post_period, nseasons=[{ 'period' : 7 }]) #7日間の周期性を考慮する ci.plot(figsize=( 22 , 20 )) これでPython版CausalImpactを用いた効果検証が一通りできました。 RのCausalImpactとの結果比較 同じデータを使用して、RでもCausalImpactを実装していきます。 ※Pythonのように日付データをindexに指定しませんがデータの中身は同じものです 最初に断っておきますが、あくまでPythonとの結果比較をするだけで「なぜ結果が違うのか」「どちらがいいのか」などについては触れないものとします。(むしろ詳しい人がいたら教えてください) RでのCausalImpact実装 #excelファイルを読み込むためのパッケージをインストール install.packages ( "openxlsx" ) #CausalImpactをインストール install.packages ( "CausalImpact" ) #ライブラリの読み込み library ( openxlsx ) library ( CausalImpact ) #データの読み込み(excel) df <- read.xlsx ( "sample_R.xlsx" ) #TVCM放送前と後を指定する(index番号) pre.period <- c ( 1 , 99 ) #TVCM放映開始前 post.period <- c ( 100 , 152 ) #TVCM放映開始後 #CausalImpactの実行とサマリーの表示 ci <- CausalImpact ( df , pre.period , post.period ) plot ( ci ) summary ( ci ) Posterior inference {CausalImpact} Average Cumulative Actual 285 15117 Prediction (s.d.) 207 (13) 10987 (673) 95% CI [182, 233] [9634, 12325] Absolute effect (s.d.) 78 (13) 4130 (673) 95% CI [53, 103] [2792, 5483] Relative effect (s.d.) 38% (6.1%) 38% (6.1%) 95% CI [25%, 50%] [25%, 50%] Posterior tail-area probability p: 0.001 Posterior prob. of a causal effect: 99.9% For more details, type: summary(impact, "report") 共変量の確認 plot ( ci $ model $ bsts.model , "coefficients" ) 周期性(シーズナリティ) ci <- CausalImpact ( df , pre.period , post.period , model.args = list ( nseasons = 7 )) #7日間の周期性を考慮する PythonとRの検証結果比較 PythonとRの検証結果(サマリーの内容)を比較してみます。 今回用意したサンプルデータではPythonとRの結果には差がなさそうです。 ※尚、今回のサンプルデータでのみ検証結果の比較を行っております。ご注意ください おわりに いかがでしたでしょうか? Python,Rどちらも簡単にCausalImpactを用いた効果検証が行えそうです。 今回のように、TVCM放映エリアと非放映エリアがある場合は共変量として非放映エリアを使用するのが良いと思います。 しかし、全国でTVCMを放映していた場合はどうでしょうか。 例えば、Google Trendsなどを利用して「1LDK」や「引越し」など、同じ業態の中でも検索トレンドがサイト流入数と似ているものを探し、共変量として使用してみるといいかもしれません。 地域にこだわらなくても、同質性が担保できれば問題ないと思います。 今回の検証では、TVCM非放映エリアがあったので簡単に共変量を見つけることができましたが、今後は上記のようなケースでも正しく効果検証ができるように自身のスキルを磨いていきたいと思います。 本記事の内容に齟齬などあればご教示いただけますと幸いです。
こんにちは!クリエイターの日運営委員の工藤です。 みなさんは「LIFULL Fab」をご存知ですか? 弊社は、Fabスペース(アナログ・デジタル工作機器が利用可能な工房)も運営しており、そこには3Dプリンターやレーザーカッター、ShopBotなど、クリエイターならテンションが上がること間違いなしの機器がたくさん揃っています! fab.lifull.com 今回はシルクスクリーン製版機を導入したのでみんなで使おう!ということで、「クリエイターの日」のイベントの一環として、 シルクスクリーン体験会を開催しました! ※クリエイターの日とは? 希望者が、3ヶ月ごとに最大7営業日を使って、好きなものを開発することができるLIFULLの制度です。 LIFULLでは、マーケティング能力や技術開発能力を高めてイノベーションを創造するため、通常業務の枠を離れて、新たな技術や手法に取り組む機会となっています。 以前はレーザーカッターでお菓子に彫刻するようなイベントを行いました。 www.lifull.blog どうやって印刷するの? まずはデータを製版機に送り、専用のスクリーンに製版します! ホームズくんの版ができました🤩 あとは、スキージー(ヘラ)でインクをのばすとできあがりです!! 作成中の参加者の方の様子 インクを乗せる場所はどこがいいか、真剣に考えています。 どこに製版されたのかわかりにくかったみたいです。マスキングテープなどで目印付けたほうがよさそうでした。 インクを乗せてみました。どれぐらいの量にすればいいのか、考えながら乗せていきます。 思ったよりたっぷり乗せるくらいがよさそうでした。 インクを伸ばす作業です。 速くのばすと図柄がかすれてしまったり、ゆっくり伸ばしすぎると図柄が潰れてしまったりするため、参加者のみなさんはこの作業に一番苦戦されたようでした。 これで完成です!みなさんどんな作品ができたでしょうか? どんなものができたの? 完成したものはこちらです!参加者のみなさんの個性が溢れるものができました。 保護猫の配信を行なっている方が、配信のノベルティとしてトートバッグを作ってくださいました! 猫ちゃんのロゴがとっても可愛いです! さっそく着てくれました!ご自身の名前をロゴ風にしたそうです! 普段も使えそうなデザインで素敵です! 細かい模様なため、非常に丁寧に作成されていました! とても綺麗な仕上がりになっています! ご家族の似顔絵をバッグにしてくれました! こちらは白いインクを使って、黒いバッグにもプリントしました。 弊社の役員が、ホームズくんのカバンを作ってくれました。 お気に入りになったようで、早速肩にかけています✨ さいごに 今回イベントを開催してみて、カバンやTシャツなど身近なものを題材とすることで、ものづくり以外の職種の方でもFabスペースやシルクスクリーンに興味を持っていただけました。 実際にシルクスクリーンを使ってみて、またオリジナルグッズを作ってみたいというお声を多くいただきました。 LIFULLでは、LIFULL Fabを使った様々なイベントを企画しています。 イベントの様子はFacebookで発信していますので、是非ご覧ください! https://www.facebook.com/lifullfab/ また、LIFULL Fab以外でもLIFULLでは様々なクリエイター向け社内イベントを開催しています。こちらも興味がある方は是非ご覧ください! www.lifull.blog recruit.lifull.com
こんにちは。Android開発チームのグループマネジメントを担当している衛藤です。 Android開発チームでは、不動産・住宅総合サイトのLIFULL HOME'S Androidアプリを開発しています。 play.google.com Android開発チームでは、2019年9月にスクラム開発を導入しました。 もともと2014年あたりにスクラム開発を行っていたのですが、今回再導入するまではウォーターフォール型で進めたり、アジャイル風に進めたりと様々でした。 前回は私自身メンバーとして稼働していましたが、今回は初スクラムマスターとしてチャレンジしました!! スクラム開発導入に至った背景や導入後の効果、遭遇した問題点と対応策などについてご紹介してみようと思います。 長い記事になりますので、時間を持て余しているときにでもゆっくり読んで頂けると嬉しいです。 導入の背景 スクラム開発を導入する以前は、以下のような体制で開発を進めていました。 プロジェクト単位でエンジニアをアサイン 見積りはアサインされたエンジニアの裁量で算出 必要があればMockを作成し、早い段階で軌道修正する 3ヶ月など長期に渡るプロジェクトは、中間レビュー会を開くなど、細かくマイルストンを設定 予めどのリリースバージョンに入れるかを確定し、そこに合わせて開発していく プロジェクト遅延の場合はリリース日を調整する、もしくは、間に合いそうな場合は無理やり対応 アプリのクラッシュバグや機能バグについては、個人の空き時間等を利用して対応(緊急性の高いもの以外) うまくいく場合はもちろん問題ないのですが、何か問題が発生するとすぐに遅延や遅くまでの残業が発生してしまい、心身ともに枯れ果ててしまうケースが度々発生していました。 また、バグ対応についても施策の優先度が上がってしまうと対応を後回しにしてしまい、品質が落ち始める事態となりました。 そこで、現在の開発体制から見直していこうと、以前やっていたスクラム開発を再び導入してみることにしたのがことの発端です。 スクラム開発とは Webや専門書籍にスクラム開発に関しての情報がたくさんあるため詳細は割愛しますが、1週間〜1ヶ月程度のスプリントというイテレーションを繰り返し、イテレーション毎に振り返りによる改善を実行することで、開発効率をチームとして上げていくアジャイル開発フレームワークの一つです。 一般的なウォーターフォール型とは違い、追加されたタスクはプロダクトバックログと言われるスタックに積まれていきます。 そのため終わりが多少見えづらくなるデメリットはありますが、その分品質を担保しつつ継続的に動作可能物をデリバリーすることが出来るため、問題の早期発見や起動修正など柔軟な対応ができます。 このように、スクラム開発は「短い期間で、最大限の成果をあげる」ことが可能になる開発手法です。 すべての現場にスクラムが合っているわけではないので、現状のプロジェクト体制やチームメンバーとも協議のうえ、選択すると良いのではないかと思います。 スプリントが一定期間でまわり続けるため、慣れない最初はスピード感を感じないかもしれません。いかし、一度慣れてくると高速に回る開発サイクル・成長するチーム・品質の担保を実感できます。 特にスクラム開発の醍醐味である「自律的なチーム」に育っていく様子も感じられるのは幸せな瞬間です。 それでは、LIFULL HOME'S Androidチームがスクラム開発を始めて変わったこと・問題点をどう対処してきたか、などを紹介していきたいと思います! まずはスクラム開発の一般的ルールに則って開始 スクラムルールの整理 チーム独自のルールを定めても良いのですが、まずは一般的なスクラム開発の手法に則って始めてみることにしました。改善する箇所は徐々にチーム独自のルールに変更していく予定です。 そこで、どのようなルールやイベントがあるかをチームで定めます。 インセプションデッキ スプリント スプリントプランニング デイリースクラム スプリントレビュー スプリントレトロスペクティブ 以下、順を追って簡単に説明していきます。詳細については、Webや専門書籍等を参考にしてみてください。 インセプションデッキ インセプションデッキとは、プロジェクト憲章とも呼ばれ、プロジェクトに対するビジョン・方向性や基本的な取り決めをチームやステークホルダー全員で定義する文書のようなものです。 取り決める内容はチームによって変わってくるかと思いますが、一般的には 我々がいる理由、何を目指しているのか プロダクトのニーズ・差別化ポイントを簡潔に やること・やらないことを明確にする(スコープの設定) などを決めます。 プロジェクトが進む上で、様々な問題によりチームが乱れてきたとき、インセプションデッキに立ち戻ることで、改めてチーム全体の方向性を再認識することができます。 また、「最初に作って終わり」ではなく、定期的に振り返って内容を精査することも大事と言えます。 LIFULLは、ビジョンを大切にする会社です。社員全員がビジョン・経営理念に共感し、そこから具体的なビジョンを最小組織単位で掲げています。 このビジョンは、部署メンバー全員で話し合いのうえ決めるため、インセプションデッキと同義とみなし今回は割愛しました。 lifull.com スクラムイベント スプリント スプリントとは、一定期間内にユーザーストーリーなどのタスクを実行し、動作可能物をデリバリーするイテレーションのことです。このため、スプリント期間をチームで定める必要があります。 もともと、Androidチームでは2週間単位でアプリのアップデートを行っていました。スクラム開発のスプリント推奨期間は1週間〜1ヶ月なので、そのまま2週間を1スプリント(=1イテレーション期間)として開発をしてくことにしました。 小規模なプロジェクトでは1週間でも可能かと思いますが、少しサイクルが早すぎて息苦しさを感じるかもしれません。逆に1ヶ月だとスプリントごとの改善効果が出るのが遅くなることもあるかもしれませんね。 チーム内で相談して最適なスプリント期間を定めるとよいと思います。試しにスプリントを回してみて、不都合があればスプリント期間を変えるのも一つの手段と言えます。 LIFULL HOME'S Androidチームでは、 月曜にスプリント開始、翌週の木曜日にスプリント完了、隔週金曜日はスプリントプランニング → 終わったら飲み会🍺 というスケジュールを組みました。(パーッと騒いで週末を迎えられる心理的安全性。でもだんだん飲みに行かなくなる・・・) スプリントプランニング 1回のスプリントで対応するタスクを定めます。スプリントに入る前のタスクはプロダクトバックログというスタックに積まれ、それが「すべて」の作業を意味します。 そのプロダクトバックログの中で優先度を付け、スプリント期間に収まる量のタスク(=チケット)を開始前に整える作業を行います。 スプリントプランニングで定めたタスクは、基本的にはすべて完了させます。すべて効率よく完了させるため、開発メンバーは声を掛け合ってどの作業を担当するか各自で定めて開発を行っていきます。 以前のように「この施策は●●さんにアサイン」ということはなく、施策単位で担当するか手分けして施策を完成させるかは開発者に委ねられています。 スプリント期間に収まるかどうかを判断するには、各チケットに工数が振られていないといけません。この見積作業もスプリントプランニングでチーム全員・または開発者全員で行うことが必要になります。 そして、スプリントプランニングは時にはかなり長い時間をかけることもあります。極稀にですが、3−4時間スプリントプランニングを行っていたこともありました。 なお、工数については後ほど触れますがストーリーポイントを導入しています。これにより相対的見積が可能になるとともに、全員で見積るため考慮漏れによる工数超過やその逆の超過見積りが減って精度が高まっていきます。 デイリースクラム 一般的なデイリースクラムに従い、以下の内容を15分以内で話します。 バーンダウンの確認 昨日やったこと 今日やること 問題・障害になっていること しかし、やっているうちに問題が出てきたため、後述の方法に改善をしています。 スプリントレビュー スプリントの最終日は成果物を全員で触り、見落としや致命的なバグがないかの確認をやっています。問題があれば、次のスプリントバックログに積んで対応を行います。 また、このスプリントレトロスペクティブは、テスト仕様書を伴わない探索的テストも兼ねており、機能テストでは発見しづらいバグやデグレードの発見にもつながっています。 各スプリントでは、毎回Buffer工数を設けており、スプリントレビューで発生したバグについては、そのBuffer内で出来る限り消化するようにしています。 スプリントレビューが終わり、masterブランチにマージされたものが「次期リリース可能状態」となりリリースフローに乗ることになります。 スプリントレトロスペクティブ スプリントが終わる度に、振り返り会を行います。 KPT方式で、Keep / Problem / Tryを話し合い、やりにくい点や問題点を早期発見し、具体的な改善策を実行することで次のスプリントに活かしていきます。 スプリントレトロスペクティブでは、改善策を話し合ったあとに、それを実行するアクション担当者のアサインまで決めてしまう運用でしたが、ここでもひとつ課題があり、改善が必要だったため後述にて改善策について触れています。 使用ツール 初めてスクラム開発を行う場合は、ホワイトボードやコルクボード等の物理的なボードを利用することも推奨されていますが、今回は弊社で利用しているJIRAにスクラム開発機能がサポートされいるため、そちらを利用することにしました。 JIRA スクラムボード プロダクトバックログ スプリントバックログ バーンダウンチャート ベロシティグラフ SpreadSheet (スプリントプランニング補助ツールとして) ストーリーポイント(SP)の導入 ストーリーポイント(以下、SP)と言われる見積手法を使っています。SPとは、単なる見積ではなく、あるタスクを基準(このタスクで"2時間"など)として、相対的に見積りをする手法です。 この見積を開発者全員で話し合い、スプリントプランニングでそのスプリントで対応するタスク量の目安を確定します。また、スプリント完了時に何SP完了したかをベロシティとして計測が可能になります。 SPは時間ではなく、あくまで単位、という考え方が一般的なようですが、Androidチームでは以下のような基準を定めています。 SP 換算 1 1時間以内で終わるタスク 2 2時間以内で終わるタスク 3 2時間〜4時間以内で終わるタスク 5 4時間〜6時間以内で終わるタスク( ≒ ミーティング時間を除いた1日の実稼働時間) 8 8時間以内で終わるタスク ( ≒ 残業すれば1日で終わる) 13 2日以内で終わるタスク このように、SPが大きくなるほど時間に幅を持たせることで、考慮漏れなどの事態が発生しても見積時間内に収まるように調整しやすくなります。 最大は13SPになっていますが、基本的にはこのレベルのSPになった場合は、更に細分化して小さいSPへと変換していきます。 13SPでは、まだ内容がしっかりと把握できていなかったり、見落としによるリスクが多いと考えているためです。 タスクを見積可能な状態で細分化し、一つ一つのタスクに対して一斉にSPを出し合います。(プランニングポーカー) そして、一番SPが少ない人・多い人で理由や考慮漏れなどが無いかの認識を合わせることで、驚くほど見積りの精度が上がります。 また、SPは決めた通りに動かなくといけないわけではなく、 予定よりも早く終わったら、前倒して次タスクに取り掛かる、または予定になかったリファクタなどを行う 予定よりも多くかかりそうなら早めにスクラムマスターに相談、タスクを再整理する という動き方をしています。 見積りは個人のレベルや経験年数などに応じて必ず差が出てきます。 特に個人間では認識に違いがなかったとしても、差が出てしまうのはそのためです。そのような場合はプランニングポーカーで出した数字の中間のSPを設定するようにしています。 このままでは個人間の差が出てしまうため、各人が持っている工数(稼働時間)に対して、係数を設定することで、誰が対応しても全体的には見積り通りに収まるような仕組みを導入しました。 以下、例を記しました。 1スプリント = 9day × 1日稼働6hours(ミーティング等の時間削除) = 54hours 開発者Aさん 係数:1.0 = 54hours 開発者Bさん 係数:1.2 = 64.8hours 開発者Cさん 係数:1.1 = 59.4hours 開発者Dさん 係数:0.9 = 48.6hours 係数を加味したトータル時間 = 226.8hours を該当スプリント期間の全稼働工数とする Buffer期間(スプリントレビュー後のバグ修正や不測な事態への考慮): 全体の1〜2割 = 226.8 * 0.2 = 45.36hours 全稼働工数 - Buffer = 226.8hours - 45.36hours = 181.44hours この場合、最終的な181.44hoursを全稼働と想定し、そこに収まるSPの時間合計をスプリントバックログに仕込みます。 もちろん、進行によっては早めに終わったり、Bufferが不要になる場合もあるので、そのときは予定になかったタスクを前倒したり、リファクタリングを行う時間に充当します。 早く終わったからといって早く帰るのではなく、その分最大限の付加価値を提供できるようにチームとして動くことが大切です。 この計算により、大幅に想定外の事態で遅延したり、バタついて付け焼き刃での対応を入れることによる品質低下を招くことがほぼなくなっています。 余談 チーム独自のルールとして、「バグやクラッシュ対応をする時間を必ず入れる」を徹底しています。 1スプリントごとに、5〜10SP程度の時間を必ず設け、品質向上に務めることにしました。平均して大体3-4のクラッシュを対応しています。 発生数や発生箇所によって優先度を付与し、高いものから順次スプリントバックログに予め積んでいくことで、品質向上につなげることが出来ます。 クラッシュやANR(Application Not Responding)はユーザーがアプリから離れてしまう原因に直接的につながってしまいます。 1クラッシュ等の些細なクラッシュも必ずチケット起票し、優先度を付与します。 Crashlyticsでクラッシュ状況の監視を続け、件数が上がってきた時点で優先度を付け替える作業も行うことで、「気付いたら大量クラッシュしていた」という事態も回避出来るようにしておきます。 この地道な作業により、以前と比較し確実に品質が上がってきたことが分かりました。具体的な結果については最後にまとめています。 スクラム開発を導入の結果 これまでに記載したとおり、開発方針や体制は事前にチーム内で合意を取り開発に着手しました。 導入して2〜3スプリント経ったあたりから、効果が目に見えて分かるようになります。 炎上案件がほぼなくなった(焦って修正してまたバグが発生する、などが極端に減る) 施策の優先度や差し込みタスクの調整がしやすくなった 品質が圧倒的にあがる 見積精度が高いので、スケジュール引き直しがほぼ発生しなくなった、など ただ、やっていく中で、やはり問題点は出てくるものです。そのような問題点はスプリントレトロスペクティブで洗い出し、次々に改善をしていきます。 このあとは、浮上してきた問題点や課題をどのようにクリアしていったかをご紹介します。 発生した問題・課題とその対処について 振り返りアクション事項の放置 スプリントレトロスペクティブは毎回行い、うまく行った点や問題点を深掘って話し合えるのはいいのですが、それで満足してしまうことがありました。 そうなってしまうと、せっかく決めたTRY項目が実施されずにまた次回以降の振り返りで課題にあがってしまうことになってしまいます。 そこで、週に1回開催しているチーム定例の最初に、アクション事項の進捗を確認する時間を設けました。 スプリントレトロスペクティブで出たTRY項目は残さず定例議事録に記すことで、対応を忘れてしまうことなく週単位で改善して行けるようになっています。 施策の優先順位、仕様追加時のルールが曖昧 優先度が高い施策からスプリントバックログに登録はしているものの、少し気が緩んでしまうとまた元の状態に戻ってしまいます。 その結果、「差し込みタスクとして無理やり工数にねじ込んで作業 → 焦ってしまうため品質が低下してしまう」という事態が発生してしまいます。 最初に決めたルールを厳格化し、 施策には優先度を必ず付ける。低・中・高だと優先度が被ることがあるため、番号で優先度を付ける スプリントバックログには、番号が優先度の番号で一番若いものから積んでいく 優先度が入れ替わった時点でスクラムマスターに相談、現時点のスプリントバックログの優先順位を入れ替える。工数が溢れたものは次回以降のスプリントバックログに移動 仕様追加時は、追加分のSPを見積り、Buffer時間で出来そうならそのまま対応。あまりにも大きい場合は優先度を入れ替える このようなルールを厳守することで、チーム内でバタつき混乱することがほぼなくなっています。 何より重要なのは、このルールをチームとして合意しておくことです。開発者が勝手にこのような行動すると認識の食い違いによるトラブルが発生してしまうため、ステークホルダー全員で握っておくことが大切だと思っています。 1スプリント = 1リリースの問題 当初は、もともとやっていた2週間サイクルのリリースと合わせてスプリントを回していたため、 1Sprint = 1リリース として開発をおこなっていました。 そのため、スプリント名もSprint_v1.2.0のようなバージョン名をもとに作成していました。 これでも十分スクラム開発の利点は発揮できるのですが、少しでもバタついてしまうと以下のようなことが発生してしまいます。 1施策でも考慮漏れや仕様追加によりスケジュールが伸びると、スプリントが破綻する 無理やりねじ込んで作業する スケジュールが伸びた場合は、次のスプリントも予定通りに開始できない 次のスプリントもバタついてしまい、負の連鎖に陥ってしまう そこで、スプリントとリリースの関係を次のように変更することをチームとして合意します。 Sprint = リリースではない リリースはこれまで通り2週間サイクル ただし、リリース時点でmaster branchにマージされているものだけがリリース対象 スプリントとリリースの切り離し このように、Sprintとリリースの関係を断ち切ることで、負の連鎖を解消できます。 ここでも重要なことは、チームとして方針に合意することです。全員の認識が合っていることで、施策調整がスムーズに進むようになりました。 そこで問題になるのがスプリント名です。これまでのようにリリースバージョンを付けるわけにはいきません。 スプリント名を考える 単純なことです。リリースバージョン名以外を付ければよいのです。ただ、普通に名前を付けるだけだとつまらないですよね。 コードネームのような形で以下の方針に決めました。 アルファベット順にその文字から始まる動物の名前をスプリント名とする こうしてみると、スプリント名を決めるイベントが楽しみになってきます。 ちなみにこれまでは Sprint_Alligator / Sprint_Butterfly / Sprint_Chicken / Sprint_Dingo / Sprint_Echidna / Sprint_Falcon / Sprint_Gekco ・・・ のような名前を付けてきました。 毎回スプリント名を決めるときは大いに盛り上がり、定期的な息抜きにもなるのでオススメです。 動物が終わったら次は何にしよう・・・笑 SPの精度測定 こちらはまだ試している途中です。 スプリント終了後、SPの見積がどれだけブレていたかを振り返る機会がありませんでした。 そこで、タスク終了時にざっくりとかかった時間を記録してもらい、ブレた理由や浮いた時間がどれだけ発生したかを観測できるようにしています。 スクラムイベントの形骸化 長く続けていると、やっていることが形骸化してしまい、気づかずに「なんとなく続けている」という状態になってしまいます。 そうなってしまうと、もはや続けている意味はありません。やめてしまうか、やり方を変えるかの2択になります。 今回はっきりしてきたものが「デイリースクラム」です。 前述の通り、 昨日やったこと 今日やること 問題になっていること を一人づつ共有するのですが、慣れてくると「昨日やったこと、共有やること」の単なる報告会になってしまっていました。 これではデイリースクラムをやる意味がありません。毎朝チャットに一人づつ書けばよいだけです。 もともとデイリースクラムを行うのが、日次で各自の作業状況を把握し、小さな問題だとしても報告、事態が大きくなる前に解消する、という目的があります。 そこで導入したのが「ファイブフィンガー」です。 有名なアジャイルの書籍である「 KAIZEN JOURNEY 」 でも紹介されていましたが、ファイブフィンガーとは、 1本:絶望的でどうしようもない 2本:不安がある、問題になりそうな種がある 3本:普通。可もなく不可もなく 4本:うまくやれているかも! 5本:絶大な自身がある!すばらしい! この基準に則り、デイリースクラムで全員が一斉に手を出してその日の状態を共有します。(一斉というのがミソです。バラバラと出すと先に出した人に合わせてしまう傾向があるため) そして、全員出揃ったら一番本数が少ない人からその理由を聞いていきます。 そのときに少しでも問題がありそうならその時点で調整を行い軌道修正をします。 4本以上の人にも話してもらうことで、チーム全体に安堵感を与えることにも繋がります。 ファイブフィンガーの導入により、問題になり得る小さな種を早期に発見し、大きな問題になる前に軌道修正も可能になり、ますます安定した開発チームとなっていきます。 スクラム開発を導入して変わったこと これまでに、スクラム開発の手法や問題点について紹介してきました。 導入後、どのようなチームに変わってきたかも触れてみたいと思います。 見積のブレが少なくなっていく スクラム開発導入に伴い、ストーリーポイントを開発者全員で出すようにしました。 最初の頃は、ブレもありバーンダウンが収束しないこともありましたが、最近では多くても見積りプラス・マイナス1時間程度に収まっています。 全員でタスクを見積るため、考慮漏れやいつもなら直前に発覚したであろう問題などを、なるべく事前に協議し解消することが出来るようになっています。 バーンダウンを意識するようになるチーム デイリースクラムでは、毎回バーンダウンを確認するようにしています。バーンダウンとは、タスクの見積りに対して日次でどの程度のタスクを消化する必要があるのかを示すグラフです。 参考までに、これまでに行ったスプリントで最初の頃と最近のバーンダウンを以下に貼ります。 最初の頃のバーンダウン(ナイアガラの滝) 最近のバーンダウン(理想線に追従) このように、最近ではキレイに理想線に従って進むことが出来ています。 スクラムを始めた当初は、チーム全員でバーンダウンを意識することが少なかったのですが、バーンダウンを毎日確認することで納期に関する意識付けにも繋がっています。 なくなる炎上PJ スクラム開発以前は、メンバー各自が施策にアサインされ、見積り・設計・開発を独自に進めていました。これにより個人作業による見積り時の見落としや追加作業によりリリース日に影響を及ぼすことがありました。 しかし、スクラム開発を導入し、全員の知見を見積り時に集合させることで遅延の割合が激変しました。 これにより、夜遅くまで作業し、さらに次の施策にまで影響が出るという負の連鎖の解消に繋がっています。 劇的に改善するCrashfree Rate アプリのクラッシュはユーザーにとってストレスの高いイベントです。あまりに多くクラッシュが発生するアプリはアンインストールにもつながるため注意が必要です。 弊社では、Crashlyticsを導入し品質を数値として監視しています。 安定した品質のプロダクトは、ユーザーにも安心感を与えます。開発しているアプリがどれだけの品質かを示す指標が、Crashlyticsで提供されている「Crashfree Rate」というものです。 これは、アクティブなユーザー・デバイスのうち、どの程度の割合がクラッシュせずに利用できているかを示しています。 Crashfree Rate また、全期間でクラッシュ数を比較すると、以前にくらべてかなり改善したことが確認できます。(下図の点線は前年度比較です) クラッシュ数の推移 過去90日間の比較で、Crashfree Rateは 0.5% 向上しました。直近3バージョンの比較では、 99.95% 前後を維持できています。 向上率としては小さいかもしれませんが、ある程度のDAUを抱えている状態で品質を維持し続けるのは大変難しいことです。 スクラム開発により、健全なチーム体制を整えることができ、皆が自律的な対応をとることで品質向上・維持も実現することが出来たのは非常に嬉しいことです。 (何よりメンバーのみなさん、ありがとうございました!!) まとめ 自律的チームへの第一歩 スクラム開発を導入し約半月が経過しました。プロジェクトの進行はスムーズになり、品質はこれまでの歴史の中でトップクラスの水準に達しようとしています。 「どうすれば課題をクリアできるのか」「共通のゴールを達成するためにどう動くべきか」をメンバー一人ひとりが考え行動した結果が出始めてきたのではないかと思っています。 単純に言われたタスクをこなすのではなく、チーム全員が共通のビジョン・ゴールに向かって突き進んでいくことが何よりも重要であると実感しました。 高い効果を実証できたので、今後もスクラム開発は続けていく予定です。しかし、現状で満足しているわけではありません。 長く続けていく中で、必ずやりにくい点や課題は浮上してくるはずです。そのような壁をチーム全員で解決し、高速に、高品質なプロダクトの開発を進めて行きたいと思っています。 長くなりましたが、最後までお読み頂きありがとうございました!! スクラム開発や、LIFULL HOME’S Androidアプリチームの体制について、少しでも興味を持って読んで頂けたなら嬉しいです! 最後に Androidエンジニア募集中です!! 最後に、LIFULL HOME'SのAndroid開発チームは、一緒にプロダクトを成長させていって頂けるメンバーを募集しております! 現チームメンバーと一緒に、最新技術をいち早く取り入れ、開発を楽しんで頂ける方、ぜひ以下の応募フォームよりエントリーくださいませ!! hrmos.co 「入社までは考えてないけど、社風や事業内容聞いてみたい…」という方向けに、カジュアル面談も実施しています(採用合否には関係ありません)。 カジュアル面談は以下からご応募ください! hrmos.co 一緒に働ける日を楽しみに、お待ちしております!!!
こんにちは!LIFULLのSoftware Engineer in Testグループ(通称:SETグループ)のヒキモチです。 皆さんはAIを活用したテスト自動化ツール(以下、AIテスト自動化ツール)のことを知っていますか? 先日、 Autify と TestCraft というツールを試させて頂く機会がありました。 その結果想像以上に良いツールだということが分かりました。 ここではそのAIテスト自動化ツールを検証しようとした理由や、我々がメインで使用しているテストスクリプトを書くタイプのテストツール Bucky との比較、AIテスト自動化ツールの使い所について考えましたので共有させて頂きたいと思います。 目次 目次 AIテスト自動化ツールに関する説明と導入を考えた理由 AIテスト自動化ツールについて 今ある問題点 実装前の知識・学習コスト 操作 検証 環境構築 メンテナンス どのように解決できるか 実装前の知識・学習コスト 環境構築 メンテナンス 実際に使ってみた感想 テストスクリプトを書くタイプのテスト自動化ツールとの比較 テスト実装 実装のしやすさ テスト実行(運用) テスト結果確認のしやすさ メンテナンスのしやすさ テストシナリオの管理 どのようなプロジェクトに向いているか まとめ AIテスト自動化ツールに関する説明と導入を考えた理由 AIテスト自動化ツールについて 自動E2Eテストに携わったことがある方なら分かると思いますが、運用をする中で面倒なのがテストスクリプトの メンテナンス です。 ページ内要素のDOMが変わったときなどにテストが動かなくなると、テストスクリプト側を修正する必要があります。これを メンテナンス と呼んでいます。 AIテスト自動化ツールはこのような面倒な工程を AIがサポートしてくれる 機能を持ったツールになります。 今ある問題点 今まで私達は社内の自動E2Eテストの導入はBuckyをメインに行っていました。 しかし、導入する上でいくつか障壁となるものがあります。 実装前の知識・学習コスト 環境構築 メンテナンス 実装前の知識・学習コスト Buckyもそうですが、テストスクリプトを書くタイプのテスト自動化ツールは実装前の知識として DOM要素の指定方法 の理解や操作や検証などの 命令 の学習が必要になります。 例えばヘッダーのこの部分をページ内の特定の要素をXPathという指定方法で書くとこのようになります。 //*[@id="top"]//*[@class="copy"] そしてBuckyでの操作・検証命令の例として以下のようなものがあります。 操作 go...指定したURLへ遷移する click...指定した要素をクリックする 検証 assert_text...指定した要素のテキストを検証する assert_title... ページタイトルを検証する これらを用いてYAMLで以下のように書くことでブラウザ操作を実現しています。 また、その他の操作や検証はこちらにまとまっています User Operation · lifull-dev/bucky-core Wiki · GitHub Verification · lifull-dev/bucky-core Wiki · GitHub テストスクリプトを書くタイプのテスト自動化ツールではこのようなことを 学習する必要 があります。 ちなみに弊社では導入時にその部署に対してBuckyのレクチャーを3時間程度かけて行っています。 環境構築 ローカルからの実行でもよいのですが、テスト実行用の環境を用意したほうが定期実行やトリガー実行などの面で便利です。 しかしながらテスト実行用のサーバを用意し、テスト実行用の環境を整えるのはかなり手間です。 そのため、弊チームでは Terraform を使うことで簡単に構築できるような工夫を行っていました。 メンテナンス 冒頭にも書きましたが、実際の運用に乗ったあとに手間なのがページ内要素の位置変更や文言の変更による テストスクリプト側の修正 です。 これはテストスクリプトを書くタイプのテストツールでは必ずといっていいほどついてまわるコストです。 どのように解決できるか 実装前の知識・学習コスト 基本的にSaaSとして提供されておりブラウザでの操作を録画して行われるので、ツールの操作のみ覚えれば先程挙げた知識や学習は必要ありません。 環境構築 基本的にSaaSとして提供されているので、テスト実行環境の構築を行う必要はありません。 メンテナンス AIが自動で要素の変更を検知し判断を仰いでくれます。 マニュアル部分は、その判断に対し決定を下すのみです。 実際に使ってみた感想 使って見た感想としては、 自動E2Eテストを実装したことがない人でも簡単にテストシナリオの作成ができる と感じられるような便利なものでした。 実際に他部署の方にも使ってもらい以下のような感想も頂きました。 直感的に操作ができてわかりやすい。 エンジニア以外でも使えそう。 学習コストが低い。 テストスクリプトを書くタイプのテスト自動化ツールとの比較 テストスクリプトを書くタイプのテスト自動化ツール(Bucky)とAIを用いたテスト自動化ツール(Autify、TestCraft)の比較をしてどのように工数を削減できるかを確認したいと思います。 運用までの流れとしては以下の図の様になります。 どちらのツールを使うにしろ、計画・設計に関しては同じ工数がかかるので、今回注目したい部分は 実装 と 実行(運用) の部分になります。 テスト実装 実装のしやすさ 評価 内容 Bucky   ◯   操作や検証をテストスクリプトで書く必要があります。 Autify   ◎   Chrome上で操作した内容を記録する形で進めます。 操作ごとにステップとして記録され、編集も簡単に行えます。 はじめかた - Autify User Guide TestCraft   ◎   TestCraftで用意されたコンテナ上でブラウザを起動し、自PCのブラウザから接続し操作します。 操作ごとにステップとして記録され、編集も簡単に行えます。 テスト実行(運用) テスト結果確認のしやすさ 評価 内容 Bucky   △   テストが失敗したときにメッセージでの判断+その結果が本当に正しいのか(デグレが発生したのか)を確認するために同じテストを再実行して確認する必要があります。 Autify   ◎   シナリオのステップごとにキャプチャが取られているので、再実行する手間もなく判断することができます。 TestCraft   ◎   Autifyと同様にシナリオのステップごとにキャプチャが取られているので、再実行する手間もなく判断することができます。 メンテナンスのしやすさ 評価 内容 Bucky   △   テストを再実行しながら、パーツの判別を行いテストスクリプトを書き換える必要があります。 Autify   ◎   パーツの改修に関してはユーザーのYes/Noのクリックのみで完結できる。 期待値の修正に関してはユーザーによる書き換えが必要です。 前回の実行と今回の実行のキャプチャを比較することができます。 TestCraft   ◎   パーツの改修に関してはAIが自動でテストスクリプトの修正を行います。 期待値の修正に関してはユーザーによる書き換えが必要です。 前回の実行と今回の実行のキャプチャを比較することができます。 テストシナリオの管理 評価 内容 Bucky   ◎   テストスクリプトの管理に関してはGitHubなどを利用した権限管理ができます。 シナリオ管理に関しては、複数のケースをまとめてスイートとして管理をすることや、プロジェクトごとにディレクトリを作成することで柔軟な管理ができます。 また、ラベル付けの機能があり実行するテストケースを指定できます。 Autify   ◯   現状ユーザー管理のみ可能で、細かい権限管理については検討中とのことです。 ロードマップ - Autify User Guide シナリオ管理に関してはスケジュール実行時にまとめ上げることができます。 タグ機能の追加検討もされているとのことです。 定期実行に関してはGUI上で簡単に設定できます。 TestCraft   ◯   ユーザー管理が可能で3つのロールがあり、ユーザに割り振ることができます。 シナリオ管理に関しては多階層で分けられています(suite→spec→flow)。 定期実行に関してはGUI上で簡単に設定できます。 どのようなプロジェクトに向いているか AIテスト自動化ツールはテストスクリプトを書くタイプのテスト自動化ツールに比べイニシャルコストはかなり抑えられますが、ランニングコストは高くなる傾向にありました。 また、AIテスト自動化ツールを使うことで一番効果がある部分は テスト実装とそれを運用段階に持っていくまでの工数削減 にあると感じました。 これらを踏まえて、以下のようなプロジェクトに向いていると考えました。 自動E2Eテストの導入段階にあるプロジェクト 自動テスト専門家がチーム内にいないプロジェクト あくまでこれはLIFULL SETグループの見解ですので、すべてのものに当てはまるわけではありません。 まとめ 今回初めて使ったAIを用いたテスト自動化ツールは素晴らしいものでした。 かなり使いやすさを考え作り込まれており、初めてツールを触る人でも簡単にテストを自動化できると感じました。 これからのSETチームとしてはこれらのツールのことも深く理解しながらも、 テストを進めて行く上でその開発チームや会社にとって何が ベストなテストアプローチ なのかを考え、 また、使う ツールの利点を最大限に活かせる ように開発チームを導くことも今後の役割として出てくるのかなと感じました。
こんにちは!プロダクトエンジニアリング部の山手です! 普段は、LIFULL HOME'SのiOS版アプリを開発しています。 1月も終わろうとしていますが、そんな中LIFULL HOME'Sに関わるエンジニアが集う大新年会 PEer Bash が初開催されました! 今回は、そんな社内向けイベントの様子をお送りいたします! 大新年会 PEer Bashとは LIFULL HOME'Sを支えるエンジニアは、プロダクトエンジニアリング部(Product Engineering部=PE部)という組織に所属しています。 プロダクトエンジニア部の総会時に「プロダクトエンジニア部として最高のチームになるためにやりたいこと」というテーマでアイディアを募集したところ、 業務上関わりの少ないグループメンバーとカジュアルな交流の場を持ちたい エンジニアが集まっているので、技術面での気軽なアウトプット&インプットをしたい という声が多く集まりました。 そこで、より良いチームを築き合うキッカケや新しいコミュニケーションの場になるような場を創るため、初の試みの「大新年会PEer Bash(PE部+Beer Bash)」を開催してみることにしました。 PEer Bashの模様 早速ですが、PEer Bashの模様をご紹介いたします! 今回のPEer Bash運営チームは、各部署から集まった有志のメンバーです。 それぞれ普段は異なるサービスを担当しているので、サービスの垣根を越えて集ったメンバーで企画を考えるのはとても新鮮でした。 メインテーマは「カジュアルな交流」。 第1弾ということもあり参加者の準備は少ないほうがよさそう、アンケートでみんなの意見を聞いてみよう、開催時間は2時間で区切ろう、などなど、運営チームで試行錯誤しながら準備をおこないました。 お酒やピザも揃い準備万端! コアタイムの終わりに業務を終えて立ち寄るのも良し、仕事が一区切りついたところで参加し始めるのも良し! それぞれの仕事の状況に合わせて自由に参加することができて、カジュアルに色々な人とコミュニケーションが取れるような会でした。 長沢翼CTOの乾杯の挨拶からPEer Bashスタートです! 部署関係なくエンジニア同士の会話が各テーブルで始まっていました。 各サービス担当者がプロダクトへの愛を語る座談コーナーやチーム制のライブコーディング大会なども盛りだくさんで、イベントは大いに盛り上がりました! あっという間の2時間でしたが、初開催のPEer Bashは無事終了しました! さいごに 今回が初開催のPEer Bashでしたが、各テーブルでの会話や催し物の盛り上がりからもLIFULL HOME'Sを支えるエンジニア同士が、カジュアルにコミュニケーションを取ることができるイベントにすることができたと感じています。 私自身も他の技術領域のエンジニアの方と何名かと話す機会になりましたが、自分が知らない知識を知る機会になったり、逆に自分が知っている知識を教える場面もありとても楽しく、刺激的なイベントでした! LIFULLでは、LIFULLを支えるエンジニアの仲間を募集中です! カジュアル面談も受け付けておりますので、ご興味がある方はぜひご覧いただきご応募ください。 hrmos.co hrmos.co
はじめまして!技術開発部セキュリティグループの花塚です。 LIFULL Creators Blogにセキュリティグループが登場するのは初めてですね。 セキュリティグループでは、脆弱性診断や脆弱性の調査、セキュリティツールの開発など、幅広い業務を行っています。 今回は、脆弱性可視化基盤を開発した話を紹介したいと思います! 主に開発の経緯から、技術的な構成、そして、これからのことについて、まとめています セキュリティに関わる人にとって、少しでも参考になれば嬉しいです。 目次 目次 目的と経緯 脆弱性情報をスムーズにエンジニアに届けたい 組織全体で継続的に脆弱性対応をしていく風土をつくりたい 脆弱性可視化基盤を支える技術 機能 脆弱性スキャン ダッシュボード 全体の構成図 脆弱性スキャンの仕組み EC2インスタンス GitHub Docker Image 工夫した点 オートスケールされたインスタンスの扱い Athenaのチューニング これからのこと 目的と経緯 まずは、どんな目的と経緯から脆弱性可視化を実施することになったのかについて紹介します。 脆弱性情報をスムーズにエンジニアに届けたい LIFULLでは、基本的にプロダクトにおける脆弱性対応は各部署に任せています。 そのため、どんな脆弱性がどのプロダクトに存在しているかが分かる仕組みはなく、セキュリティグループは日々脆弱性情報を監視し、エンジニアに対して注意喚起をするといったことしかできていませんでした。 もし、脆弱性情報を手に入れた時に、対応が必要なプロダクトがすぐに分かる仕組みがあれば、短時間で各部署に脆弱性対応を依頼することができますよね。 また攻撃コードが出回った時なども、よりスムーズに注意喚起できます。 「 セキュリティグループが収集した脆弱性情報をスムーズにプロダクトの開発者達に届ける仕組みを作りたい 」 そう思ったのが脆弱性可視化基盤を開発するきっかけの1つでした。 組織全体で継続的に脆弱性対応をしていく風土をつくりたい 継続的な脆弱性対応は、プロダクトを開発している企業の課題の1つです。 脆弱性対応の理想は、プロダクトを開発しているエンジニア自身が、脆弱性を定期的に確認し、対応することだと考えています。 しかし、多くの業務を抱える中で、脆弱性対応のために時間をあまり割くことができないのが現実だと思います。 時間の捻出以外にも「プロダクトにおける脆弱性をどのように確認するのか、優先して対応すべき脆弱性はどれか」などと、悩みは多いと思います。 脆弱性可視化基盤には、これらの課題や悩みを解決し、脆弱性対応に対するハードルを下げることで、継続的に脆弱性対応を促進していく意図があります。 脆弱性可視化基盤を支える技術 ここでは、脆弱性可視化基盤を支える技術について触れていきます。 機能 脆弱性可視化基盤の機能は、大きく2つあります。 脆弱性スキャン 以下を対象に脆弱性スキャンを行います。 EC2インスタンス GitHubリポジトリ Docker Image それぞれの仕組みについては、後半に詳しく説明します。 ダッシュボード 脆弱性スキャンの結果は、1つのダッシュボード上で閲覧することができます。 以下の条件で脆弱性の検索が可能です。 CVE ID 攻撃コードの有無 脆弱性のSeverity GitHubリポジトリ名 AWSアカウントID EC2インスタンスID Docker Image名 Namespace また、ダッシュボードにはMetabaseを使用しています。 MetabaseはUIがわかりやすく、とても使いやすいですよね。 github.com 閲覧を制限したいので、ダッシュボードは認証をかけています。 認証という面でもMetabaseは、あらかじめ機能(ldap認証など)を用意してくれるので便利です。 全体の構成図 省略しているものが多少ありますが、脆弱性可視化基盤の大まかな構成は以下のようになります。 これらの構成は、各脆弱性スキャンのパートから成り立っています。 脆弱性スキャンの仕組み ここからは、主要機能である脆弱性スキャンの仕組みについて説明していきます。 EC2インスタンス EC2インスタンスの脆弱性スキャンは、 AWS Systems Manager(以下SSM) & AWS Athena (以下Athena) & Vuls の組み合わせで実現しています。 LIFULLには多数のサービスが存在するので、AWSアカウントの数も100強ほど存在します。対象アカウントは多数あるので、手動で設定する必要がある場合は、なるべく時間をかけないことが設計時の要件でした。 EC2インスタンスの脆弱性スキャンではAmazon Inspectorなども考えていましたが、各インスタンスにエージェントをインストールする必要があり、AWSアカウントの数を考えると時間がかかりすぎると断念しました。 SSMを選んだ理由は、エージェントをインストールする必要がある点はAmazon Inspectorと同じですが、エージェントがプリインストールされているAMIが複数存在しており導入がしやすかったのが決め手です。 これらの構成で脆弱性スキャンをするためにはいくつかのステップがあります。 まずは、インベントリのリソースデータの同期です。 SSMには、マネージドインスタンスのインベントリ情報を定期的に収集してくれる機能があります。 可視化対象の各アカウントに、この設定をしてもらうことで、インベントリ情報を1アカウントのS3に集約することができます。 この設定でS3に集約したインベントリ情報は、Athenaを用いることで検索することができます。各インスタンスが、どのようなミドルウェアを使用しているか知りたいときに便利ですよね。 Assume RoleとSSMのSDKを組み合わせて、インベントリ情報を取得するアプローチもありますが、1つのアカウントに権限が一時的とはいえ集中するのは避けたかったのでSSMで集約した情報をAthenaで検索する形を採用しています。 さて、集約したインベントリ情報ですが、これらを整形しVuls Serverへと投げれば脆弱性スキャンが可能になります。 Vulsは、localスキャンやsshを用いたスキャンなど以外にもServerモードが実装されており、こちらを採用しています。 github.com 最終的に、AthenaのSDKを用いたプログラム上からインベントリ情報を取得し、整形した後Vuls Serverへ投げるまでが脆弱性スキャンの大まかな流れになります。 GitHub GitHubではGitHub Security Alertを使用しています。基本的に全リポジトリのSecurity AlertをONにし、APIを用いて取得しています。 こちらは特に変わったアプローチをとっているわけではないです。強いて言えばSecurity Alertで検知した脆弱性をExploitDBと突合して攻撃コードを検出しています。 Docker Image Docker Imageはコンテナの脆弱性スキャナーであるTrivyを使用していますが、そのまま使っているのではなくPrometheus Exporterとして実装したkube-trivy-exporterを使用しています。 以前のエントリで紹介されていましたが、現在LIFULLの(ほぼ)すべてのサービスがKubernetesで動いています。 詳細については以下を参照ください。 www.lifull.blog Kubernetesで動いているアプリケーションの監視にはPrometheusを使用しているので、Prometheus Exporterとして実装しています。 kube-trivy-exporterは社内のKubernatesに関わるエンジニアにコンテナの脆弱性について相談したところ開発してくれました。 github.com kube-trivy-exporterは定期的にクラスタ内をフルスキャンしオンメモリ上に結果を保存しています。最終的に結果はPrometheus Serverに集約されるので、好きなタイミングで取得しに行けばいいというわけです。 今後Kubernetesに移行するサービスは、自動的に脆弱性を見ることができるようになるので今後の活躍にも期待できますね。 工夫した点 オートスケールされたインスタンスの扱い S3に集約されたインベントリ情報の中には、オートスケールされたインスタンスの情報も存在します。 同じAutoScaingGroupのインスタンスをスキャンすると脆弱性が重複してしまったり、無駄にスキャン回数が増えるので、同じ「aws:autoscaling:groupName」の値を持つインスタンスを1つだけ選抜しています。 Athenaのチューニング Athenaを使用する場合は、料金や実行速度に注意が必要です。 読み込んだバイト数で料金がかかるため、なるべく読み込むサイズを減らしていかねばなりません。 インベントリのリソースデータの同期の設定で作成したAthenaのDatabaseはAWSアカウントIDなどでパーティションされていますので、それらをうまく使うことでより高速に、より低コストでクエリを実行することができます。 Athenaのベストプラクティスがありますので、興味のある方は以下を参照してください。 aws.amazon.com 他にも工夫している点はありますが、長くなるので割愛させてください。 VulsなどのOSSを使用しているので、そもそも大したコストはかかりませんが、クエリのチューニングやスキャン回数を減らすことで、脆弱性スキャンがかなり低コストで実現可能となっています。 これからのこと 改めて脆弱性が可視化されたことで、いろいろとやることが見えてきました。 残存する脆弱性の対応方針・検証など、やることはさまざまです。 また、Webアプリケーションのようなミドルウェア以外のレイヤーの脆弱性スキャンも徐々に対応を進めていきたいと思います。 既にLIFULLでは、AppScanで動的スキャンを実施(手動脆弱性診断もやります)していますが、そのような脆弱性スキャナーをデプロイパイプラインの一部とする仕組みは確立されていません。 脆弱性の検知を早め、よりセキュアな状態でプロダクトが世の中にリリースされるように目指していきます。 最後になりますが、これからもLIFULLのセキュリティグループでは、セキュリティに関わる誰かのために活動をアウプットしていきますので、よろしくお願いします! また、LIFULLではメンバーを募集しております! カジュアル面談もありますのでご興味ある方は是非ご参加ください! hrmos.co
こんにちは、株式会社LIFULLでエンジニアをしている塩澤です! 今回は、2020年1月28日(火)に開催した、 『Ltech#10 不動産・住宅情報サイト「LIFULL HOME'S」の中の人が語るAWS活用前線』 についてレポートします! Ltechとは Ltech(エルテック)とは、LIFULLがお送りする、技術欲をFULLにするイベントです。 特定の技術に偏らず、様々な技術の話を展開していく予定です。 今回でなんと、 #10 です!! 祝二桁開催!! 不動産・住宅情報サイト「LIFULL HOME'S」の中の人が語るAWS活用前線 記念すべき二桁開催となる今回のテーマは、 『不動産・住宅情報サイト「LIFULL HOME'S」の中の人が語るAWS活用前線』 です。 部署を問わず、様々な活用事例を話してもらいました! それでは各発表のレポートです。 Fargate/CloudWatch Eventsでバッチをサクサク作った話 【Ltech#10】Fargate/CloudWatch Eventsでバッチをサクサク作った話 from LIFULL Co., Ltd. www.slideshare.net 最初のLTは、LIFULLで動いているバッチの環境を改善したお話です。 LIFULLでは、日次バッチと不定期なイベント駆動のバッチが動いており、かつ利用するAPIのリポジトリにバッチ処理が同居している状況でした。 リポジトリにはこれ以上バッチを追加したくない... それを解決するために、FargateとCloudWatch Eventsを利用してバッチを起動する構成を組むことにしました。 結果として、APIとバッチのリポジトリが疎結合になることでメンテナンス性がアップし、バッチ用サーバの管理から解放されることに! 社内通貨のシステム構成 【Ltech#10】社内通貨のシステム構成 from LIFULL Co., Ltd. www.slideshare.net 続きまして、LIFULL社内で開発している社内通貨「LIFULL COIN」をAWS上に構築したお話です。 LIFULL COINでは、Quorumというエンタープライズ向けのブロックチェーン基盤を利用しています。 このブロックチェーンのノードの起動と停止をAWSをつかって実装しました! FargateのタスクでQuorumノードの起動・停止イベントをAWS EventBridgeに送信し、紐つけられたStep Functions Express Workflowsによって各種処理が実行される構成となっています。 自分はExpress Workflowsに触ってみたくなりました。 CloudFormation による LIFULL HOME'Sサイト問い合わせ情報登録APIの構築と管理 【Ltech#10】CloudFormation による LIFULL HOME'Sサイト問い合わせ情報登録APIの構築と管理 from LIFULL Co., Ltd. www.slideshare.net ここからは、LTではなく15分の発表となります。 まずはCloudFormationを使った、APIの構築と管理についてです。 SalesforceというCRMツールに情報を登録するためのAPIを作成する際に、全てのリソースをCloudFormationを使って構築・管理したお話です。 CloudFormationはリソースの設定や情報をコードで管理できかつ、そのコードから自動でリソースを作成してくれるサービスです。 APIの新規開発に当たって、手作業の運用コストの削減、環境の冪等性の確保、構成管理の属人化の防止などを目的としてCloudFormationの採用を決定しました。 CloudFormationのコード1つで全てを管理することもできましたが、APIの全てのリソースを管理するとなると肥大化しメンテナンス性の悪さを招いてしまうため、機能単位でテンプレートを分ける設計にしました。 また、あえてCloudFormationで管理しない部分も考慮する必要がありました。 最終的には、CloudFormationで全てのリソースが管理できるようになりました。 これによって、手作業の設定によるカオスな状態に陥ることなく、誰でも同じ環境を整える状態になりました。 このお話は自分も少しだけ関わっている(すでにCloudFormationで環境構築ができる状態でJoinしたので楽でした)ので、改めてCloudFormationのありがたさを感じました。 LIFULL HOME'S ネイティブアプリ用APIのデプロイを自動化する 【Ltech#10】LIFULL HOME'S ネイティブアプリ用APIのデプロイを自動化する from LIFULL Co., Ltd. www.slideshare.net 次は、ネイティブアプリ用APIのデプロイを自動化したお話の予定でしたが、自動化しようとして失敗したお話になります。 最終的には成功?しているのでご安心を! 今回のお話の対象となるAPIでは、個人環境で実行しているデプロイ用のスクリプトの複雑化や、個人環境への依存度の高さなどが課題となっていました。 そこで、AWSの各種サービスを駆使してデプロイを完全自動化を試みることに。 方針としては、GithubへのソースpushをトリガにSAMとCodePipelineなどを利用してデプロイフローを組むことにしました。 しかし、そこにはいくつもの壁が... Parameter StoreとLambdaを使った環境変数の取得処理の際に、都度発生してしまう通信をうまいこと回避する必要がありました。 同じAPI Gateway IDができてしまう問題。LambdaのエイリアスとAPI Gatewayのステージが上書きされてしまう。 それらを乗り越えた先に待ち受けた最大の壁は「既存のパスを消さないと違うテンプレートからデプロイできない」というものでした。 SAM template を独立させておく必要があったため起こってしまった問題です。 結果、Github Actions を使って自動化することに。 SAMにも限界があること、AWSで完結させる以外の選択肢もあるとのことでした。 失敗することが1番学びに繋がるかもしれませんね。 LIFULL HOME'S のAWSアカウントに SavingsPlans を導入した話 【Ltech#10】LIFULL HOME'S のAWSアカウントに Savings Plans を導⼊した話 from LIFULL Co., Ltd. www.slideshare.net いよいよトリです! 最後はLIFULL HOME'SのAWSアカウントにSavingsPlansを導入したお話です。 AWSで一番気になるところはやっぱりお金についてですよね。 SavingsPlans (SPs) はリザーブドインスタンス(RI)に代わる新しい料金プランです。 このSPsの導入についての発表となります。 SPsを利用したコスト最適化の第一歩目は、「SPs」を理解することです。 割引率は?適用される範囲は?インスタンスタイプによる違いは? 最適なプランを選ぶためにもまずは理解することから始めましょう。 次にプランの選択です。 コスト最適化の適用範囲が広ければ、割引率は下がってしまいます。 このトレードオフを考えて選びましょう。 そして、コミット金額を決めること。 今回は、Cost Explorerを利用した見積もりとCost Usages and Report (CUR) + Athena + Solverを利用した見積もりについて詳しく聞けました。 Athenaで出力したコストレポートに対してSolverというツールでコミット金額を算出します。 書ききれませんので、詳細についてはぜひ資料をご確認ください! 導入してどうなったかといえば、インスタンスタイプに関わらず最適化がなされ、割引率も高くなりました。 また、使いきれなかったコミット金額が発生したものの、他のアカウントに適用されることで無駄にはなりませんでした。 結論、SPsは神プランだった。とのことです。 懇親会の様子 最後に、登壇者と参加者の方を交えた懇親会です。 いつもの唐揚げです 今回はサラダもありました!いつもより彩りが豊かに笑 時間いっぱいまで盛り上がりました 最後に Ltech では、LIFULLエンジニアが中心となって皆様の技術欲を満たすよう実例を交えた勉強会を開催しています。 今後もLtechを積極的に開催していきますので、 ぜひ気になった方は、connpassでLIFULLのメンバー登録をよろしくお願いします! lifull.connpass.com
  ※この記事は LIFULL Advent Calender の20日目です  こんにちは! LIFULLでデータアナリストをしている竹澤( @Akira Takezawa )です.  今回は, LIFULLの データアナリストチーム の取り組みを紹介します.  本記事はデータ分析に興味がある方を対象に, 「マーケティングの実務で生かせる時系列分析」をテーマに執筆しました.  まず, なぜこの記事を書いたかを簡単に説明します.  近年, 機械学習やディープラーニングの台頭を筆頭に近年データ分析の手法は爆発的に増え続けています.  一方で実際のビジネスの現場で見えてくるのは, 「派手さや新しさのみに捉われず, 古今東西変わらず価値を提供し続けてきた分析手法こそ重要ではないか」というもう一つの側面です.  具体的には相関・回帰分析や検定などがそうですが, 同時に「 時系列分析 」もビジネスの世界で活用機会が多く, パワフルな威力を発揮すると私は考えています.  そこで今回は, TVCMの訴求やクリエイティブの評価をする際に, 時系列モデルを活用することで評価基準となるKPIを作成した事例について紹介します.  なお中盤では, PythonによるSARIMAXモデルの実装を記載しています. 【目次】 ①マーケティングにおける時系列分析の活用機会 目的: TVCMでユーザーのサイト訪問・利用を実現したい 課題: 訴求内容とクリエイティブの評価におけるボトルネック 解決策: 出稿予定のGRP量とKPIの関係をモデリングする ②Python/statsmodelsによるSARIMAXの活用事例 はじめに: SARIMAXモデルとは? データ準備: WAUと過去のCM配信GRPデータ モデル選択: 時系列データにおける確率過程 パラメータ推定: AICとGrid Searchによる推定 予測: 今期のGRPで予想されるKPIの期待値を算出 ③現在LIFULLではデータアナリストを積極採用中 データアナリストの役割: 自分たちの存在意義について LIFULLが抱えるデータの魅力: 量と多様性の観点から さいごに: データアナリスト(特にマネージャー)を募集中 ①マーケティングにおける時系列分析の活用機会 引用元: 千鳥がネタ6本!?スマートニュースの新コンテンツ『クーポンチャンネル』を紹介する新TVCMが2018年3月31日(土)より全国オンエア中!! 目的: TVCMでユーザーのサイト訪問・利用を実現したい  突然ですが読者の皆さん, 自分が好きなCMはありますか?  こちらの画像は弊社でも広告を出稿させて頂いている SmartNews さんの CM カットですが, 「TV画面の右側30%をサービスのUI(スマホ画面)が占める」という斬新なクリエイティブとなっています.  個人的にはかなり勇気のある挑戦だと考えており, ユーザーにとっての「わかりやすさ」を極限まで突き詰めた, 作り手のこだわりを強く感じるのですごく好きです.  スマホの登場以来, ますます生活が広告で溢れる現代で, 単にTVCMを見てもらっただけで認知を獲得できる時代は終わりました.  特に LIFULL HOME'S のような情報サービスの世界では, 市場を問わずプロダクト認知の獲得だけでなく, アクション喚起を狙ったCMへ大きくシフトチェンジしていることが上記の例のように確認できます.  当然LIFULLでもオフラインの広告によって, 実際にオンライン上のサービスを使ってもらえたか, あるいはアプリケーションをインストールして頂けたかに関心を寄せています.  今回はWebサイトやアプリサービスにおいて, ユーザーの「オンライン上でのアクション」までを狙ったTVCM活用を想定して話を進めます. なおその際のKPIとしては, CM出稿期間中の DAU や 指名検索 などが考えられるでしょう.  余談ですが, TVCMはマーケターにとって, 年間で打席に立てる回数が少ない割にコストは基本億単位と高額なため, 成功しても失敗しても事業に対するインパクトが大きく, 難易度が最も高い仕事の1つです.  だからこそ, 1回1回の訴求内容やクリエイティブに対して, 正しい評価指標を設定し, その結果を軸に細かなフィードバックを与えていく必要があります.   ※注意点として, あくまでTVCMの目的を「サービスの利用」に設定した時のみ, トラフィックの増減等の指標でクリエイティブを評価すべきです. 認知など目的次第で評価の仕方は変わるので, この方法が常に正解という訳ではありません. 課題: 訴求内容とクリエイティブの評価におけるボトルネック  ここではクリエイティブを評価する上での難所を例示するために, 実際にあったボトルネックを3つ紹介します. TVCMには GRP や放映時間帯などKPIに変化を与え得る変数が複数あり, 訴求など特定の変数による影響を定量化して切り出すことが難しい 定性情報である訴求内容やクリエイティブは, 定量化してKPIとの関係をモデリングすることが難しい KPIの値が, 季節効果やトレンドの影響を受けていることを考慮しなければならない   ※GRPは「延べ視聴率」と呼ばれ, CMの総表示回数を表す尺度と考えてください  少し具体的に, 順を追って説明していきます.  まず1. に関して. TVCMは通常, 大きく以下の5つの要素(変数)を変更・調整して毎回の出稿を行います. 訴求内容 (Who&What) クリエイティブ (How)※起用タレントや音楽etc... 放映GRP量≒出稿金額 (How much) 放映時間帯 (When) 放映都道府県 (Where)  上記の要素において, どの要素の変更がKPIに対してどれだけ影響を与えたかを個々に定量化して抜き出すことが困難なことは想像がつくでしょう.  次に2. に関して. そもそも訴求内容やクリエイティブ(起用タレントや構成)は定性的な情報かつ, 変数化(ダミー変数など)も難しい部類にあたるため, 統計モデリングが非常に難しくなります. 結果, KPIに対するクリエイティブの効果を直接定量化することができませんでした.  最後に3.に関して. 詳しくは後述しますが, 時系列モデルを選択するメリットそのものに関する話となります. まずは 季節効果 について.  一般的にどの商材・サービスにおいても繁忙期や閑散期があると思います. 弊社ではTVCMの出稿時期と繁忙期が重なり, KPIのリフトのうちどこまでがCMの影響なのかの判断が難しいという課題がありました.  例えば, LIFULL HOME'Sにおいて賃貸物件の検索は, 1月から3月の引っ越しシーズンが繁忙期にあたりアクセス数が急増します. 当然, 1,000GRPのTVCMを春先の3月に出稿した時と, 夏の8月に同じ量を出稿した時にKPIが取り得る値は全く異なります.  よって, 単純に線形回帰を使って毎回のGRPとKPIの関係性をモデル化しようとしても上手くいきません.  また, トレンド とは端的に言うと長期的な傾向やKPIのベースラインの変動を指します. 例えば, スマホの普及率が急増した5年間においては, 基本的には多くのWebサービスのユーザー数も右肩上がりに増加していったはずです.  こうしたマクロ要因による緩やかな数値の変化も考慮する事で, より正確な効果検証ができます.  ざっくりとですが以上がWebのトラフィック指標を評価基準として, CMのクリエイティブや訴求を効果検証する際に出てきた問題点でした. 解決策: 出稿予定のGRP量とKPIの関係をモデリングする  ここでは前述のボトルネックを踏まえ, 今回我々が採用した効果検証の方法について書きます.  まず検証手順の全体像としては, 以下の3ステップとなります. CMの変数としては「GRP」のみを説明変数に加えた, トレンド・季節調整付き時系列モデル を用いて未来のKPIが各週で取り得る基準値を予測する もし実際のKPIの結果が基準値を大きく上回った場合, 説明変数に加えなかった「訴求」や「クリエイティブ」が正の影響を与えたと仮定して評価する 最後に, 今回試したCMの変更点( 訴求や起用タレントの変更など )と, 過去のクリエイティブの「差分」を吟味し, 次回の継続事項・改善事項を決定する  要は, 実際の観測値から「訴求内容とクリエイティブ"以外の要素"」による効果を差し引くことで, 訴求とクリエイティブの効果を定量化して抜き出そうという発想です.  ただお察しの通り, 現時点で妥協した点がいくつかあります.  例えば, 「訴求内容」や「クリエイティブ」による効果(放映時間帯や放送エリアの変更も含む)は, 過去の出稿の結果を平均して考慮するとおおよそ一定になるとして割り切り, 誤差に吸収されるものとして扱っています.  しかし, 実際にKPIとなる指標が過去に取った値は, これらの影響を少なからず受けた結果生まれているはずです.  また本来であれば, CM出稿期間に同時に出稿した電車内の交通広告やYoutube動画などの影響も考慮するべきですが, 今回はそれらの出稿金額等を説明変数としてモデルに加えていない点においても不完全さが残ります.  一方で, 世の中の現象を単純化してモデリングする際は, 常に妥協がつきものです. ですので今回は一度モデルを構築し, どれだけ精度が出るか見てみようという判断に踏み切りました.  ここから先は, 時系列モデルであるSARIMAXのモデリングに移り, Pythonでの実装方法と共に簡潔にモデルの説明をします. ②Python/statsmodelsによるSARIMAXの活用事例 引用元: Forecasting with Python and Tableau   ※この記事では, 個々の統計用語の説明や数式については割愛します. また所々統計的な厳密さに欠ける記載があると思われ, その際は積極的に修正したいのでぜひコメントをお願いします. はじめに: SARIMAXモデルとは?  今回はタイトル通り, KPIの予測値を作るための時系列モデルとして SARIMAXモデル (季節調整済みARIMA + 外生変数) を採用しました.  SARIMAXモデルを直感的に理解するため, まずは SARIMAX = S+ARIMA+X と分解して説明します.  まず S+ARIMA の部分ですが, 代表的な時系列モデルである ARIMAモデル (自己回帰和分移動平均) に季節性などの周期成分を取り入れたものを SARIMA モデルと言います. そして残りの +X については, 回帰分析のように 外部変数 もモデルに組み込めることを表しています. 今回でいうとTVCMの出稿GRPを, X としてモデルに取り込みます.  SARIMAXモデルを採用することで, 前章であげた繁忙期などの「季節影響」や「トレンド影響」を加味したいといった要件を満たすことに繋がります. また広告費などの外部変数も複数追加できるため, 多くのマーケティングキャンペーンにおける予測や効果測定とも相性が良さそうです.  Pythonでは, statsmodels というライブラリからSARIMAXクラスを呼び出すことで, 数行のコードでモデリングできます. データ準備: WAUと過去のCM配信GRPデータ  今回はTVCMで伸ばしたい指標(KPI)を, サイトに訪問した週次のユニークユーザー数を表す WAU (Weekly Active User)と仮定して進めます.  実際のサービスデータを公開することはできないため, 今回は Google Trend を使用します. 具体的には直近5年分の自社サービスに関わる複数ワードの検索数をダウンロードし, その合計値に適当な数をかけることで擬似的なWAUデータを生成しました.  また, 説明変数に加えるGRPのデータには, 広告代理店のレポートを元に自社の過去のTVCM出稿実績を用います.  なお実装にあたっては, Jupyter Notebookを使います. それでは早速必要なデータを準備していきます.  まずは①WAUデータをロードします. 元データの単位はDailyですが, pandas.DataFrame.resample で 簡単に週単位にまとめる ことができます. # 必要なライブラリを事前にインストールしてください import pandas as pd import matplotlib.pyplot as plt import statsmodels.api as sm # Google Trendのデータから仮想WAUのデータを生成 wau = pd.read_csv( "wau_5years.csv" ) wau.DATE = pd.to_datetime(wau.DATE) wau.set_index( "DATE" , inplace= True ) wau = wau.resample( "W" ).sum() wau.head( 7 )  次に説明変数に使う②GRPデータをロードし, 同じく週単位にまとめます. こちらは当然公開することができないため, データを隠しています. # 自社のGRPや広告出稿金額データを用意する grp = pd.read_excel( "grp.xlsx" ) grp.DATE = pd.to_datetime(grp.DATE) grp.set_index( "DATE" , inplace= True ) grp = grp.resample( "W" ).sum() grp.head( 7 )  この際, モデルに入れるデータの日付の開始点や粒度(日次, 月次)を揃えないと後でエラーが出るのでご注意を. モデリングに必要なデータの準備ができたので, 一度WAUの原系列を可視化してみます. # トラフィック数を可視化 plt.rcParams[ 'figure.figsize' ] = 12 , 8 wau.plot(linewidth= 4 , color= "steelblue" )  この時点で, 経年でややデータが取り得る値のベースライン(トレンド)が上昇していることや, 特定の月のみ数値が高くなるパターン(周期性)が確認できます. 次節で詳しく見ていきます. モデル選択: 時系列データにおける確率過程  ここでは, 簡単にWAUデータが従う 生成過程 を確認していきます.  前提として, 統計モデルを構築するとはつまり, 「 観察対象となるデータが, 実は目に見えない内部構造やパターンに従って現れている 」と仮定することであり, そのため数式によって定式化することができます.  そこで今回は, 各観測点におけるWAUのデータが以下のような 確率過程 に従う仮説を持ちます.    WAU = 短期の自己相関 + 長期のトレンド + 季節効果 + CMのGRP + 誤差  なおこの仮説は, おおよそSARIMAXモデルが想定している確率過程にあたります.  次ではモデリングに入る前に, 時系列データ特有の EDA によって上記の仮説の妥当性を検証していきます. 1. 自己相関と季節効果の確認  一歩目として, 自己相関 の有無を確認していきます.  時系列モデルでは, 現在の観測値と過去の値の間に, なんらかの時間的な関係性が存在することを前提としています.  よって「自己相関がない( ホワイトノイズ )」, つまり100%ランダムに生成されるデータを, わざわざ時系列モデルとして扱う必要がありません.  そのためまず, WAUデータが自分の過去の値と相関関係があるかどうか, また同時に周期性を持つかどうかを調べます. # 自己相関係数と偏自己相関係数のコレログラムを出力 fig,ax = plt.subplots( 2 , 1 ,figsize=( 12 , 8 )) fig = sm.graphics.tsa.plot_acf(wau, lags= 100 , ax=ax[ 0 ], color= "darkgoldenrod" ) fig = sm.graphics.tsa.plot_pacf(wau, lags= 100 , ax=ax[ 1 ], color= "darkgoldenrod" ) plt.show()   ※上記はコレログラムといい, 横軸がラグ数, 縦軸が自己相関係数(または偏自己相関係数)をとります.   コレログラム から, ラグが大きくなるに連れて正の自己相関が緩やかに減衰していることが分かります. またかすかにですが, 52週毎の周期性も確認できます. 2. 定常性と単位根の確認  次に, 定常性(stationarity)と ARIMA モデルの前提となる単位根(および和分過程)の有無について確認していきます.  まず定常性とは, 時間が経過してもデータを生成する確率分布が変化しない性質のことです. 基本的に多くの時系列モデルは, データが定常性を持つことを前提としています.  定常性を持つ時系列データを 定常過程 , 持たないものを 非定常過程 と呼びます.  ただし, 原系列が非定常過程なデータに対しても, 例外的に時系列モデルを適応できる場合があります. それは前後の観測点の値で差分をとった階差系列が定常性を持つデータの場合です. 単位根過程や和分過程がそれに当たります.  より具体的には, 単位根(単位根過程)は1次の差分をとった階差系列が定常過程に従う時系列データを, 和分過程は任意のd次階差系列が定常過程となるものを指します.  ARIMAモデルは, 今回のようにトレンドを持つデータに対して適応することができます. ARIMAモデルでは, 内部でトレンドデータの差分を取ることで, 定常過程に変換しています.  それでは試しに, WAUデータの1次階差系列を見てみましょう. # wauの1次差分系列 plt.rcParams[ 'figure.figsize' ] = 12 , 8 wau.diff().plot(linewidth= 4 , color= "mediumseagreen" )  先ほどよりは定常過程に近い系列となりましたが, まだいくつかスパイクしている観測点が確認できます. 今回はこのスパイク部分を「季節要因」と「外部要因(CMのGRP)」の影響による動きとして説明できないかという仮説が, SARIMAXモデルを採用する背景となります.  念のため, 単位根の有無も調べてみます. 単位根の確認には, ADF検定 (Augmented Dickey-Fuller test)が用いられます. 今回は, 有意水準 5%として検定します. # 原系列に対するADF検定 results = sm.tsa.stattools.adfuller(wau[ "WAU" ]) print ( 'ADF Statistic: %f' % results[ 0 ]) print ( 'P-Value: %f' % results[ 1 ]) ADF Statistic: -2.467521 P-Value: 0.123578   P値>0.05 となりました. 単位根過程であるという帰無仮説を棄却できなかったため, WAUの原系列が単位根を持つ可能性があるとして次に進みます. 3. トレンド(と季節成分)の抽出  本来であれば, 季節成分は上記のコレログラムによって確認できますが, statsmodels にはデータからトレンド成分と季節成分を別々に抽出することができるメソッドが存在するので紹介します. # トレンド成分と季節成分, 残差を抽出する fig, axes = plt.subplots( 4 , 1 , sharex= True ) decomposition = sm.tsa.seasonal_decompose(wau, model = 'additive' ) decomposition.observed.plot(ax=axes[ 0 ], legend= False , color= 'r' ) axes[ 0 ].set_ylabel( 'Observed' ) decomposition.trend.plot(ax=axes[ 1 ], legend= False , color= 'g' ) axes[ 1 ].set_ylabel( 'Trend' ) decomposition.seasonal.plot(ax=axes[ 2 ], legend= False ) axes[ 2 ].set_ylabel( 'Seasonal' ) decomposition.resid.plot(ax=axes[ 3 ], legend= False , color= 'k' ) axes[ 3 ].set_ylabel( 'Residual' )  トレンド成分を表す Trend から, 長期でのアップトレンドを確認できます. また季節成分を表す Seasonal からは, 1~3月に数字が大きく跳ねるといった周期性が確認できます.  以上でWAUデータにSARIMAXモデルを適応する上で, "おおよそ"の妥当性を確認できました. 次は, SARIMAXモデルが持つパラメータの推定・選択をしていきます. パラメータ推定: AICとGrid Searchによる推定  目的変数となるデータが従う確率過程を仮定できたら, モデルが持つパラメータが決まればモデルの構築が完了します.  詳細は こちらの記事 にわかりやすくまとまっていますが, SARIMAXモデルのパラメータは全部で7つ(p, d, q, P, D, Q, s)あります. 今回sに関しては, データの観測点が週単位であるため, s=52(1年間が約52週)という周期を持つと決め打ちし, それ以外の6つを推定していきます.  今回は最尤推定を内部で用いる AIC (赤池情報量規準)と Grid Search (総当たり手法)を用いてパラメータ推定を行います.  まずは, 候補となるパラメータの組み合わせを全てListに格納します. なおこの際データの長さ(行数)によってパラメータの値に上限があるので気をつけて下さい. # 考えられるパラメータの組み合わせを全て作成 max_p = 2 max_d = 1 max_q = 1 max_sp = 1 max_sd = 1 max_sq = 1 params = [] for p in range ( 0 , max_p + 1 ): for d in range ( 0 , max_d + 1 ): for q in range ( 0 , max_q + 1 ): for sp in range ( 0 , max_sp + 1 ): for sd in range ( 0 , max_sd + 1 ): for sq in range ( 0 , max_sq + 1 ): params.append([p,d,q,sp,sd,sq]) params[: 3 ] Output: [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 2]] ..  次に, それぞれのパラメータの組み合わせに対するAICの値を算出してみます. 基本的にはAICが最小のモデルを選択すれば, 多くの場合良いモデルが選択できるとされています.  なお, statsmodels.tsa.statespace.SARIMAX では, exog の中に季節性やトレンド以外に含めたい外部変数(今回はGRP)を説明変数として追加することができます.  また, このライブラリは計算に時間がかかることが多いため, 組み合わせ数が多いパラメータ推定の際には, 並列処理を簡単に実行出来る Joblib というライブラリの併用をお勧めします. # 最終的に予測したいのは繁忙期である1~3月のWAUのため, テスト期間も繁忙期に合わせる wau_s = wau.head( 225 ) grp_s = grp.head( 225 ) # テストするため, 学習用とテスト用データの分割 y_train, y_test = wau_s.head( 210 ), wau_s.tail( 15 ) X_train, X_test = grp_s.head( 210 ), grp_s.tail( 15 ) # AIC計算用のメソッドを作成 def aic_calculater (param): aic = sm.tsa.statespace.SARIMAX( endog=y_train, exog=X_train, trend= "n" , freq= 'W' , order=(param[ 0 ], param[ 1 ], param[ 2 ]), seasonal_order=(param[ 3 ], param[ 4 ], param[ 5 ], 52 ), enforce_invertibility = False , enforce_stationarity = False ).fit().aic return aic # 並列計算: n_jobs=-1とするとCPUコア数の数だけ並列化される import joblib aic_list = joblib.Parallel(n_jobs=- 1 , verbose= 10 )([joblib.delayed(aic_calculater)(param) for param in params]) # AICが小さくなる順にのパラメータの組み合わせを並べ変え aic_df = pd.DataFrame({ "params" : params, "aic" : aic_list}) aic_df.sort_values( "aic" , inplace= True ) aic_df.head( 10 )  AICが小さい順に, パラメータ(p, d, q, P, D, Q)の組み合わせが出力されました. パラメータ推定の結果, (0, 1, 1, 1, 1, 1)の組み合わせを使用することにします.  また念のためモデルの精度を確認するために, テスト期間のGRPデータを説明変数に持たせたモデルが出力した予測値と, テスト用に分割しておいた実測値の誤差を比較してみます.  今回は, 誤差の評価指標として時系列データの誤差評価によく用いられる MAPE を使用します. # AICが最小のパラメータを当てはめる model = sm.tsa.statespace.SARIMAX( endog=y_train, exog=X_train, trend= "n" , freq= 'W' , order=( 0 , 1 , 1 ), seasonal_order=( 1 , 1 , 1 , 52 ), enforce_invertibility = False , enforce_stationarity = False ) results = model.fit() # テスト期間の予測値を出力する y_pred = results.get_prediction( start = pd.to_datetime( "2018-12-30" ), end = pd.to_datetime( "2019-04-07" ), exog = X_test, dynamic= False ) # 点推定での予測値と区間推定での予測値を取り出す pred_mean = y_pred.predicted_mean pred_ci = y_pred.conf_int(alpha = .05 ) # MAPEを計算する自作関数を定義 def mape (true, pred): act = list (true) preds = list (pred) total = 0 for i in range ( len (act)): ape = np.abs((act[i] - preds[i])/act[i]) total += ape mape = (total / len (act)) * 100 return mape # MAPEを算出 print (mape(y_test, pred_mean)) Output: 6.019151544589458  可視化することで, 予測値と実数値の誤差の様子を実際に確認してみます. # 予測値と実測値の折れ線グラフの描画 y_test.plot(label= 'observed' , linewidth= 4 ) pred_mean.plot(label= 'forecast' , alpha= .7 , color = "r" , linewidth= 4 ) # 区間予測の折れ線グラフを描画 plt.fill_between(pred_ci.index, pred_ci.iloc[:, 0 ], pred_ci.iloc[:, 1 ], color= 'r' , alpha= .2 )  精度をどれだけ求めるか次第ですが, それほど悪くない予測値の動きと判断しました.  最後にパラメータが確定した後, 残差が正規分布していることや自己相関の有無を確認しておく必要があります. もしモデルが妥当であれば, 残差(説明しきれない部分)はホワイトノイズになるためです. # 残差を確認する plot = results.plot_diagnostics()  左上から順に, 標準化された残差の系列, 残差のヒストグラム, 残差の正規Q-Qプロット, 残差のコレログラムとなります. どれもおおよそ問題なさそうですね.  MAPEによる誤差評価の部分を少し省略ましたが, これでモデルの構築(確率過程とパラメータの選択)が完了しました. 予測: 今期のGRPで予想されるKPIの期待値を算出  いよいよ, 2020年の引っ越しハイシーズンに合計●●GRPのTVCMの出稿した場合, WAUが取りうる値を算出してみます.  最終的にはCM放映期間中に各週で実際に観測されたWAUの値が, 算出された予測値をはるかに上回った場合, 今回の訴求内容またはクリエイティブがWAUのリフトに寄与した(過去との比較において)と仮定して評価します.  逆に誤差程度のリフトしかなかった場合は, 「訴求が弱かったのではないか」等の仮説を立て, 次回の出稿に向けて改善点を探す作業に入ります.  それでは, 未来のWAUを予測値を算出するにあたり, まずは未来のGRPデータを準備します. # 出稿予定のGRPデータをロードする future_grp = pd.read_csv( "future_grp.csv" ) future_grp.DATE = pd.to_datetime(future_grp.DATE) future_grp.set_index( "DATE" , inplace= True ) future_grp = future_grp.resample( "W" ).sum() future_grp.head( 15 )  先ほどMAPEが最小だったパラメータの組み合わせ(0, 1, 1, 1, 1, 1, 52)をモデルに当てはめ, 予測値を算出していきます. # 手元にある直近までのデータを全てモデルへ model = sm.tsa.statespace.SARIMAX( endog=wau, exog=grp, trend= "n" , freq= 'W' , order=( 0 , 1 , 1 ), seasonal_order=( 1 , 1 , 1 , 52 ), enforce_invertibility = False , enforce_stationarity = False ) results = model.fit() # 未来の予測値を算出 forecast = results.get_forecast(steps= len (future_grp), exog=future_grp) # 予測値を可視化 lower = forecast.conf_int()[ "lower WAU" ] upper = forecast.conf_int()[ "upper WAU" ] plt.plot(wau, label= 'original' , linewidth= 4 , color= "steelblue" ) plt.plot(forecast.predicted_mean, label= 'SARIMAX' , c= "orangered" , linewidth= 4 ) plt.fill_between( forecast.conf_int().index, lower, upper, color= 'mistyrose' ) plt.xlabel( 'DATE' ) plt.ylabel( 'WAU' ) plt.legend() plt.show()  濃い赤線が点推定による予測値であり, 青がこれまで実際に観測された値です. これで評価の基準値となる予測値を出力することができました.  最後にこの週次の予測値をcsvやexcelファイルとして出力したら, 効果検証に向けた準備は完了です. # 予測した評価基準値をファイルに落とす answer = pd.DataFrame(forecast.predicted_mean) answer.columns = [ "NQ" ] answer.round().to_csv( "未来のWAU予測値.csv" ) answer.round()  実際のTVCM放映期間はこの予測値を基準として, WeeklyでKPIの動向をモニタリングしていけば良いと思います.  以上でSARIMAXモデルの実装の説明を終わります, お疲れ様でした. ③現在LIFULLではデータアナリストを積極採用中 引用元: http://recruit.lifull.com/interview/ データアナリストの役割: 自分たちの存在意義について  今回, この記事を通して発信したかったことがもう1つあります. それは「データアナリスト」の価値についての考察です.  最近は改めて, マーケターやデータサイエンティストがいる中で, データアナリストの存在意義を考えさせられます. 私たちは本当に必要なのか?  結論から言うと, 我々には 「ビジネス課題」と「正しい分析手法」のマッチング を担う役割があると考えています. 他の職種との比較で表現すると, データアナリストはマーケターとデータサイエンティストのちょうど中間に立つような存在だとイメージしています.  具体的には, マーケター(または営業企画)はプロダクトのマーケティング課題を深く理解しています. 同じくデータサイエンティストも統計や機械学習の手法に知見が深く, またソースコードの実装を通してモデルやアルゴリズムに再現性を持たせるスキルも備えています.  ただ現状, 多くのビジネス現場ではこの課題と解決手法の距離が遠く, データ分析が本来のインパクトを残せないというジレンマがあるのではと考察します.  そもそもマーケターや営業企画のメンバーが自分たちが抱えるボトルネックを分析によってより良く解決できること自体を認識していなければ, 決してデータサイエンティストに声がかかることはありません. ただ手法がこれだけ複雑化・多様化した現在では, 手法の網羅・理解も簡単なことではありません.  そうした背景から特に事業会社では今, ビジネス課題を嗅ぎつけて自らボールを拾いに行けるアナリティクスプレイヤーにもある程度需要があるのではないかと考えています.  要は「いまの事業にはきっとこういう課題があるよね. この手法使えばもっと良い解決ができそうだから一緒にやりませんか?」 と分析者側から提案していくことで克服される課題もあると考えています.  実際, LIFULLのデータアナリストチームにはマーケターやセールスの経験があるメンバーが所属しています. 事業にインパクトを残すためにドメイン知識やビジネス課題の理解も重要視しており, そういった嗅覚という部分を大切にしています.  また海外に目を向けるとNetflixやAirbnbは, データ分析組織や職種が目的ベースでかなり細分化されています. 個人的にはAirbnbのData Scienceチームのリーダーの女性が書いた One Data Science Job Doesn’t Fit All という記事が印象的でした.   ※データサイエンティストについて少し補足すると, ML/DL などの徐々に結果を出している先端技術でしか, 解決できない課題や向上できる生産性の余白がビジネス現場には多大にあると思います. 逆に難易度がそれほど高くないタスクに彼らのリソースを当ててしまうのも, もったいない(オーバースペックという文脈で)気がするため, そうした意味でも分析職はもっと細分化されて良いと考えています. LIFULLが抱えるデータの魅力: 量と多様性の観点から  ここではLIFULLが所有するデータの魅力について紹介します. 自分はまだ入社して半年ですが, データアナリストとして日々働く中で「恵まれているな」と改めて感じる機会が多くあります.  まずデータの量に関して. 事業規模では最大のサービスLIFULL HOME'Sは, MAU/DAUといったトラフィック量も国内有数の規模があります.  また質に関しても, ユーザーデータ(to C)だけでなく, 不動産会社様などのクライアントデータ(to B), 日本全国の何千万件規模の物件をカバーする膨大なコンテンツデータがDBに存在します.  個人的には, いまBtoB・SaaS企業を中心にセールス・アナリティクスが盛り上がっているように, LIFULLでもto B領域のデータ活用に携われることも魅力的に感じています. また, 不動産検索領域におけるレコメンデーション(不動産物件のパーソナライズ)はまだ世界でも完成系がないため, 非常にやりがいのあるテーマです.  データ以外にも, 分析業務に必要なツールも大変充実しており, 普段の業務では以下を使用しています. データ抽出: Bigquery データ加工•前処理: Python/R データ可視化: Tableau コード/クエリ管理: Gitlab/Github ナレッジ/チケット管理: Confluence/JIRA  データ基盤の整備に関しては2年以上前から非常に力を入れており, データエンジニアの先輩方のおかげで, 現在はBigqueryを通して社内のほとんどのデータにアクセスできます.  会社としても「世界一のライフデータベース&ソリューション・カンパニー」をスローガンとして掲げており, データ活用の重要性に対するコンセンサスが全体で共有されているのもアナリストにとっては非常に働きやすい環境です. さいごに: データアナリスト(特にマネージャー)を募集中  タイトル通り, 現在LIFULLでは強力なデータ分析メンバーを新たに募集しています. (マネージャーポジションを担える方は特に) hrmos.co  LIFULLではデータアナリストは比較的新しい職種で, 現在は社内に自分たちの価値を伝えていくフェーズにいます. 新設チームだからこそ, 組織づくりと文化づくりにチャレンジできることは, 非常に魅力的なことだと考えています.  今期も「決めるを支える」をMissionに掲げ, 日々奮闘中です. 我々は, データ分析の価値は「意思決定」の質が向上することであると定義し, どれだけステークホルダーを巻き込み, 事業にインパクトを与えられるかを意識して活動しています.  こちらの記事を読み, 弊社のデータ分析組織の活動に興味を持っていただいた方は, ポジション問わず気軽に ご連絡 ください.   私 (takezawaakira@lifull.com)への個人メッセージでも構いませんので, まずはカジュアルに他のメンバーも含めてランチにでも行ければと思います.  長くなりましたが, ご一読ありがとうございました!! 謝辞  今回実際の業務でのモデリングと本記事の執筆にあたり, 弊社のデータサイエンティスト福富裕康さんからは多くの助言を賜りました. 厚く感謝を申し上げます.
はじめまして、LIFULL秘書の新垣です。 この記事は、 LIFULLアドベントカレンダー2 の22日目です。 クリエイターズブログなのに秘書?とお思いの方が多くいらっしゃると思います。私も若干不安ですが、現在2名を担当しており、いずれも主にその管掌がエンジニア組織のため、今回参加させていただくことになりました。 「秘書ってどんなことしているの?」と聞かれることも多く、謎に包まれている印象が強い職種。 普段あまり語る機会もないので、今回はメイン業務のひとつである スケジュール管理 を中心に、取り組みをご紹介できたらと思います。 スケジュール調整依頼数は月平均200件 LIFULLの秘書グループは、3名の秘書と1名のアシスタントの計4名が所属しており、私は下記2名を担当しています。 取締役執行役員(2013年~) CTO(2019年10月~) 1日あたり6~9件程の予定が入っており、それらを調整するのが秘書の役割です。 なかには定例会議もあるため、依頼件数は2名分合わせて月平均200件強くらいかと思います。 規程上必要な会議体や出張等は年間で計画しますが、直前で変更になることは日常茶飯事。また、事業や組織の状況に応じて都度動きも変わるため、スケジュールは毎日分刻みで変更しています。 秘書には、優先順位の判断力と柔軟性、そしてスピードが求められます。 コミュニケーションツールの多様化 また、数年ほど前までは電話とメールがコミュニケーションツールの主流でしたが、現在はChatworkやハングアウトなどのチャットツールやメッセンジャーも増え、社内外問わず連絡手段が多岐にわたっています。 連絡という行為が簡便になったことで、コミュニケーションのスピードは確実に早まっていますが、その一方で、情報が細切れで届いたり、情報の属人化が顕著になっています。 秘書グループでも 毎日膨大な量に対応するためのスピードが求められる一方で、 必要な情報が一回で手に入らず、やりとりの回数が増大(=手戻りが増えた) グループ内で代理対応がしづらくなってきた という声が増えました。 そのためこれらの不を解消すべく、気軽に取り組める改善として Googleフォームによる調整受付 を行ってみることにしました。 Googleフォームの活用で、情報取得とフォロー体制の基盤を 社内限りですが、役員単位で簡単な専用フォームを作成し運用しています。 項目は秘書がそのスケジュールを調整するために必要な情報ですが、相手の負担にならないよう 必要最低限の情報のみ 選択式 としました。 また、投稿された内容は、他秘書も閲覧できるところに保存しています。 普段見ることはないですが、何かあったときにすぐに情報の取得が可能です。 この方法を導入してから、 必要な情報を都度聞き返す手間が削減 他秘書が不在の際にグループ内で代理対応が可能(=役員や社員に迷惑がかからない) などの利点が生まれています。 また、スプレッドシートにデータが溜まることで 過去情報を遡る ことができたり、 調整未/済のタグ付で タスク管理ツール としても活用が可能です。 スケジュールを分析し、可処分時間を創り出す さらに、これらのスケジュールは月単位で分析し報告します。 <方法> Googleカレンダーに予定を登録する際、軸となるタグをつける 月単位でCSV(gas)に吐き出す 手作業で元データ修正(マクロ活用) ピポットテーブルでグラフ化 分析の軸は希望に応じてカスタマイズしていますが、おおよそ 内容の種別(意思決定、報告、ブレスト等) 管掌部門別 時間軸別(足下、未来) で、 どんな性質のMTGに どんな役割として参加したのか それは満足度高く、生産できたものだったか そうでなければ次はどんなアクションをしてみるか を一緒に考え、提案し、改善を繰り返します。 それなりに手動の部分も多いので、より良い方法を模索中です。 以上、スケジュールに関する取り組みについて一部ご紹介させていただきました。 ルーチン業務は極力自動化(に挑戦中) どの職種にもある程度のルーチン業務が存在します。 LIFULL秘書の場合、「管掌部署で取りまとめて提出」ということも多く、 2名分計6部門(直下マネージャー14名)が担当範囲になります。 なかには週や月単位で同じ内容を広報することもあるので、そのようなものは GoogleAppsScript(GAS) とChatwork APIを使って、通知を自動化 しています。 これは、弊社エンジニアが作ったスクリプトを活用しています。 また、よく聞かれる質問等は社内ポータルに掲載。メンバー紹介とともに秘書的tipsとして紹介しています。 おまけ:エンジニアの組織をフォローするにあたり心がけていること 最後に、クリエイターズブログということなので、これらの組織をフォローするにあたり心がけていることを考えてみました。 私の場合は、可能な限りイベントや各種活動を直接見に行くようにしています。 たとえば 1)社内椅子コン-ISUCON- (でこっそり差し入れを提供する) 2)Ltech (をこっそり見学をする) www.lifull.blog 3)AI戦略室成果展示会 (で、こっそり手書きの企画書を出してみる) 4)創民祭 (で、Vtuberのシークレットゲストになってみる) www.lifull.blog などです。 正直なにを話しているのかサッパリなことも多い(ごめんなさい)のですが、 職種柄なにも動かないと距離が遠くなってしまうので、 そこは近い存在でありたいと思い、そのようにしています。 また、その場で出てきたワードは他の場面でも出るので、生の情報に触れておくことは大事だと思っています。 最近は自分が動かなくても情報が入ってくる時代ですが、百聞は一見にしかず。 その情報は誰かの主観を通したものであることも多いので、大事にしつつも、自らその場にいき、場の空気含めて自分の感覚でも感じるようにしています。 と言いつつも、単に皆さんが楽しく発表したり開発したりしている姿を見ることが好きなのかもしれません。 LIFULL主催のイベントは数多く開催していますので、よければ皆様も気軽にお越しください。 また、弊社では仲間も絶賛募集中です。 カジュアル面談もおこなっていますので、ご興味のある方はぜひご連絡ください。 hrmos.co hrmos.co 2020年もクリエイターたちに助けてもらいながら、バックオフィスの小さなデジタル改革を進めていけたらと思います。
こんにちは!CX戦略部の堀内です。 今回は2019年12月17日(火)に開催された 「LIFULLが進めるデータドリブンマーケティング~その戦略と実践〜」 の開催レポートをお送りします! 本日のスピーカー  Twitter: https://twitter.com/noguchimasahit  Agenda Note: https://agenda-note.com/serialization/?contents_type=110   それでは、さっそくレポートします!   LIFULLのスローガンです。 「世界一のライフデータベース&ソリューション・カンパニーへ」 前半のライフデータベースについては野口、後半のソリューションについては 菅野が解説しました。どのような目的でデータを収集し、戦略に落とし込み、日々実践しているかを紹介します!   データ戦略とは <野口> データ戦略は企業・事業の成長を加速させるためにあります。 データ活用によって、売上増加、コスト削減や新規事業の創造を実現します。 そのための データ戦略として、データを「増やす」、「高める」、「使う」の 3つのポイントが重要となります。 次にその3つのポイントについて説明します。   ①データを「増やす」~複数サービスのデータ統合 LIFULLでは、住まい探しやリフォーム、引っ越しなどのあらゆるデータを貯めて、増やしています。 重要なのは、一つのサービスではなく、複数のサービスでユーザーを見ていくことです。 そうすることで、そのユーザーを多面的に見ることができ、ユーザーがどのような人なのか、何を求めているのかが、わかってきます。 そのために必要なものがID統合であり、部署を横断したり子会社も含めてID統合を進めています。今後もLIFULLで注力するポイントです。 ②データを「高める」~データ活用体制の整備 データを活用するために「基盤」と「人」の体制整備も必要になるため、 それぞれ社内整備を整えています。 ③データをデータを「使う」~マーケティングの成果向上 KGIである「売上」の要素を「集客×歩留まり×単価」に分解し、 更にそれらを利用シーンに置き換え、広告ではROAS、サイトではCVR、CRMではLTVなど、 それぞれを最大化させる施策を日々走らせています。 野口からは以上となります。 ソリューションとは LIFULLのスローガン 「世界一のライフデータベース&ソリューション・カンパニーへ」 後半は菅野から、 ソリューション の部分について解説します。 <菅野> ソリューションの2つの考え方 ① 事業やサービス単位としてのソリューション 各事業がテーマとして定めた社会課題を解決していくサービス群。100社規模での展開を目指します。 ② ユーザー単位としてのOne to Oneソリューション 各サービスが、より「一人ひとりのLIFE」に相応しい形で提供されることを目指します。 →今回は②のOne to Oneソリューションの仕組みづくりにポイントを定めて紹介します。   多チャネル展開に必要なマーケティングテクノロジー   メールや電話など多岐に渡るデータを収集しても、 「シナリオ設計」がしっかり構築されていなければ意味を成しません。 ただ単に、あらゆるチャネルからユーザーとコミュニケーションが取れるようにすることは容易いです。難しいのは、この状況でどのようなコミュニケーションのシナリオを考案するか?ということです。AIが優れているからといって、 流石に全体最適されたシナリオの自動生成はまだ先の未来の話です。 「誰に、なにをどう提供するか」の長い道のり(シナリオ)が細かく紹介されました。 (その場限りの資料でしたので、こちらでは割愛させていただきます。)   シナリオを死ぬほど作り込んでいて、疑問に思ったこと 真のOne to Oneソリューションを目指して 改めて、LIFULLのビジョンに向き合い、自問自答した過程が生々しく語られました。 「あらゆるLIFEを、FULLに。」 一人ひとり全く事情が違う住み替えの悩みに、One to Oneで応えています。 これまで絶対視してきたフレームワーク「カスタマージャーニー」は、 ユーザー行動のパターン化という、One to Oneとは矛盾する作業が 内在することに気づきました。   ■カスタマージャーニーの解体結果 「パターン化」して「汲み取る」 ■本来のOne to Oneマーケティング 「パターン化」ができないから「聞き取る」 LIFULLとしては、巷で推奨されてきたOne to Oneマーケティングという 幻想からむしろ脱却すべきです。 双方向コミュニケーションへの挑戦 あらゆるチャネルにおいて、 「双方向」の要素を組み込んでいく必要があります。 一方向性から双方向性へのシステム設計思想への転換が語られました。 最後に事例として紹介されたのが、双方向性の特性を最大限活用できる、 LINEのサービス事例です。   UXの流れとしては、性格診断のような楽しめるコンテンツをフックに、 「対話する土台」を作り、AIによる物件提案に繋げる流れとなります。 菅野からは以上です。 本日のお二人の資料はこちらでご覧いただけます。 <野口発表資料> 【LIFULL HOME'S事例トーク】LIFULLが進めるデータドリブンマーケティング~LIFULLのデータ戦略と事業構造改革 from LIFULL Co., Ltd. www.slideshare.net <菅野発表資料> 【LIFULL HOME'S事例トーク】LIFULLが進めるデータドリブンマーケティング~WEB/店舗/LINE・・多チャネルサービス展開に必要なLIFULLのマーケティングテクノロジーとは from LIFULL Co., Ltd. www.slideshare.net 参加者とのミートアップの様子 講演の質疑応答で聞き切れなかった質問などが登壇者へ投げかけられたり、 参加者間でも意見交換、交流が盛んに行われました。 最後に 本日はLIFULLが、どのような目的でデータを収集し、戦略に落とし込んみ、 日々施策を実践しているかを紹介ました。今後もLIFULではデータドリブンな マーケティングを進め、「あらゆるLIFEを、FULLに。」するために挑戦を続けます。 ご覧いただき、ありがとうございました。 現在、LIFULLではマーケターを募集しております! カジュアル面談も受付していますのでご興味ある方は是非ご参加ください! hrmos.co