退職処理を可能な限り自動化する

技術部 SRE グループの id:itkq です。2019 夏アニメで一番好きな作品は Re:ステージ!ドリームデイズ♪ です。この記事では SRE が運用している退職処理の自動化について説明します。

退職処理とは

入社後に業務のための様々なアカウントを作成するのと反対に、退職時にはそれらのアカウントを無効化する必要があります。これを退職処理と呼んでいます。SRE が管轄している典型的な例では、SSO に対応していない SaaS のログインアカウント・AWS の IAM User・データベースの個人ログインユーザなどが該当します。これらのアカウントは社員によって要否が異なったり必要な権限が異なるため、入社時に一括で用意せず必要に応じて申請してもらう形をとっています。一方で退職時にはそれらのアカウントをすべて無効化する必要があります。 退職処理は繰り返され、自動化の余地のあるタスクです。また、SRE 以外でも退職処理を行うチームがあることは分かっていたため、退職処理の自動化のための共通の仕組みを考えることにしました。自動化のための第一歩として、退職のイベントを扱いやすい形で発生させる必要があります。

退職イベントを発生させる

クックパッドでは、ヘルプデスク1が社内 IT のアカウントを管理しており、退職時はヘルプデスクが退職者の Active Directory (以降 AD と略記) アカウントを無効化します。入社時のアカウントの作成は人事システム経由で自動化されている一方で、退職時は退職者によってタイミングなどが複雑であることがしばしばあるため、「人間による無効化」によって退職処理を開始させるようにしています。 AD とは別に、SSH や GitHub Enterprise ログインなどに使う目的で OpenLDAP によるアカウント管理も運用しています。これまでヘルプデスクと SRE でそれぞれ AD, OpenLDAP を運用してきましたが、手を取り合って AD に一本化する計画を進めています。移行期間中は AD と OpenLDAP 間で齟齬が起きることが予想されたため、AD と OpenLDAP 間で属性を同期し、パスワード変更時は AD と OpenLDAP 間で同時に変更させることで、将来の統合を楽にする目的の pasuwado というシステム (id:sora_h 作) が稼働しています。 pasuwado は AD と OpenLDAP 間の属性の同期をバッチ処理で行っています。AD のアカウントを無効化情報を pasuwado に管理させることは、本来の責務から外れすぎず、同じようなバッチ処理で実装できる見込みがあったたため、AD アカウントの無効化タイミングを保存しつつ退職イベントを扱いやすい形で発生させる機能を pasuwado に組み込みました。実運用では、誤って AD アカウントを無効化してしまうヒューマンエラーや、アカウントの無効化やリソースの削除を遅らせたい要求があることを考慮する必要があります。そこで「無効化が発見された直後」、「無効化を発見してから 3 日後」、… のようにいくつかのタイミングでイベントを発生させ、受け取る側では都合のいいようにフィルタする設計にしました。SRE では「無効化を発見してから 3 日後」のタイミングで退職が確定したとみなすようにしています。このイベントは pub/sub メッセージングサービスである Amazon SNS に送信します。次に示すのは SNS に送信されるメッセージの例です。detected_at は pasuwado のバッチが AD アカウントのサスペンドを発見した時刻、elapsed はサスペンドを発見してから経過したおおよその秒数です。

 {
    "name": "mana-shikimiya",
    "detected_at": "2019-04-23T00:00:00+09:00",
    "elapsed": 0
 }

退職イベントを受け取り自動処理する

退職イベントが送信される SNS を購読することで、自動化する退職処理のトリガーにすることができます。SNS を使うことで購読方法は選択肢の中から好きなものを選ぶことができ、また自動化処理同士を独立させられます。実際に自動化されている処理について例を挙げながら説明します。

例1: GitHub に issue を立て Slack に通知する

AD アカウント無効化直後のイベントを受け取ると、退職処理をまとめる GitHub リポジトリに issue を立てるジョブを実装しました。この issue は他の退職処理の結果をコメントしていき、スタックする用途のものです。また、誤って AD アカウントを無効化してしまったことに気づきやすいように Slack への通知も同時に行っています。 このジョブは barbeque2 ジョブとして実装しています。barbeque は ECS を基盤とする非同期ジョブ実行環境で、ジョブのリトライなどの管理を任せながら、SNS を購読し ECS で動作するジョブの実装を少ない手間で行うことができます。次の図は pasuwado から barbeque ジョブまでの動作フローです。

f:id:itkq:20191009220409p:plain
pasuwado から barbeque ジョブ実行までの動作フロー

例2: IAM User を削除する

AWS の IAM User は miam という IaC ツールで Git 管理しています。master ブランチが更新されると CI が走り AWS 上のリソースが変更されます。 無効化から 3 日後のイベントを受け取ると退職が確定したとみなし、IAM を管理するリポジトリをクローンし、退職者の IAM User を発見したらそれを削除する Pull Request を出しマージするジョブを同様に barbeque ジョブとして実装しました。この Pull Request は、例1 で述べた issue に紐づけています。

その他

上で挙げた例以外に、SRE が持つ稼働中の自動化ジョブは以下のものがあります。

  • MySQL 個人ログインユーザの削除
  • GitHub Enterprise アカウントのサスペンド
  • PagerDuty のユーザーをチームから削除
  • Amazon SNS Topic の個人 email subscription の削除
  • github.com のアカウントを cookpad organization から削除

また、他のチームの利用事例として、DWH チームによる Redshift の個人ログインユーザの削除・個人スキーマの削除があります。

自動化による SRE の退職処理運用の変化

退職処理の自動化によって、運用がどう変わったかについて説明します。

自動化以前

週次で SRE のうち一人がアサインされ、スプレッドシートに記録された退職者のアカウント情報を元に手動で処理していました。具体的には、SRE が管理するサービスのアカウントやリソースに該当アカウントがないかチェックし、見つかればそれを削除したり無効化してスプレッドシートに作業内容を記録していました。SRE 管轄のアカウントはそれなりに量があるため、場合によっては面倒な作業でした。

自動化以後

退職処理を伴う退職者が存在したかどうか週ベースで自動チェックし、存在した場合は SRE のうち一人がアサインされます。アサインされる issue には、退職処理済みのアカウントに対応する、例1 で述べた issue が紐付けられています。この issue には、自動処理の結果と自動化しきれず手動で行うべき処理が書かれており、これを手動で行ったら issue をクローズし、紐付けられたすべての issue をクローズしたら作業完了です。 元々あったスプレッドシートの運用は、作業内容を誰がいつ行ったかを記録する目的のものだったため、それが issue 上で行われるようになった現在では不要となりました。また、自動化できない処理とは例えば、SaaS のアカウントを無効化したいが無効化を行う API が無いなどです。いい感じの API が生えてくれることを願っています。

まとめ

SRE で運用している退職処理自動化の仕組みについて説明しました。汎用的な仕組みとして設計したため、この仕組みを活用して退職処理を自動化している SRE 以外のチームもあります。 繰り返される自動化可能なタスクを可能な限り自動化していくことにより、本質的な作業にかける時間を増やすことができます。この仕組みを導入してから 1 年以上が経過しており、自動化を実装する時間と比較しても退職処理にかける時間をだいぶ省けている実感があります。退職処理が面倒だと感じている方はこの記事で述べたような自動化の仕組みを検討してみてはいかがでしょうか。


  1. 通称。正式にはコーポレートエンジニアリング部サービスデスク・インフラグループ

  2. https://techlife.cookpad.com/entry/2016/09/09/235007 を参照

Ruby中間表現のバイナリ出力を改善する

Ruby 開発チームに4週間インターン生として参加いたしました、永山 (GitHub: NagayamaRyoga) です。 私は「Ruby中間表現のバイナリ出力の改善」という課題に取り組み、Railsアプリケーションのコンパイルキャッシュのサイズを70%以上削減することに成功しました。以下ではこの課題の概要とその成果について述べたいと思います。

InstructionSequenceの概要

まず、RubyVM 内で実行される命令の中間表現、InstructionSequence (以下 ISeq と省略) について簡単に説明します。

通常の Ruby プログラムは、以下のような手順で実行されます。

  1. ソースコードを構文解析し、抽象構文木を作る。
  2. 抽象構文木をコンパイルして、ISeq を作る。
  3. RubyVM (YARV) で ISeq を解釈し、実行する。

ISeq は、このように RubyVM で解釈される命令列に関する情報を含んだ一種の中間表現です。

ISeq に関する API は RubyVM::InstructionSequence としてその一部が外部に公開されているため、Ruby プログラムからも (ごく簡単な操作に限ってですが) 取り扱うことが可能です。

# 文字列をコンパイルして ISeq を得る
iseq = RubyVM::InstructionSequence.compile("p 42")

# 得られた ISeq を RubyVM で実行する
iseq.eval
# => 42

# ISeq に含まれている命令列を出力する
puts iseq.disasm
# => == disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,4)> (catch: FALSE)
#    0000 putself                                                          (   1)[Li]
#    0001 putobject                    42
#    0003 opt_send_without_block       <callinfo!mid:p, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
#    0006 leave

また、#to_binary メソッドを呼び出すことで ISeq をバイナリデータにシリアライズすることができます。

bin = iseq.to_binary
p bin
# => "YARB\x02\x00\x00\x00\a\x00\x00\x00D......"

もちろん、シリアライズされたバイナリデータから ISeq に戻すことも可能です。

iseq2 = RubyVM::InstructionSequence.load_from_binary(bin)
iseq2.eval
# => 42

コンパイルキャッシュとBootsnap

では、上の機能がどのように活用できるのかについて説明したいと思います。

先程も述べた通り、Ruby プログラムは実行されるたびにスクリプトファイルの構文解析が行われます。

  1. ソースコードを構文解析し、抽象構文木を作る。
  2. 抽象構文木をコンパイルして、ISeq を作る。
  3. RubyVM (YARV) で ISeq を解釈し、実行する。

しかし、スクリプトファイルが変更されていなけば、コンパイル結果として得られる ISeq が実行ごとに変化するようなことはありません。 同じ ISeq が得られるにも関わらず、構文解析やコンパイルが行われるのは冗長です。

特に、短時間に何回も実行されるようなプログラムや、多数のスクリプトファイルで構成される巨大なアプリケーションではコンパイル結果 (ISeq) をバイナリデータとしてキャッシュしておくとその起動速度を向上できるかもしれません。

Rails5.2以降ではデフォルトでプロジェクトにインストールされる Bootsnap という gem は、前項で説明した #to_binary メソッドを使って、スクリプトファイルのコンパイル結果を自動的に ./tmp/ 以下のディレクトリにキャッシュしてくれます。 Bootsnapはこの他にもautoloadしたファイルのパスなどをキャッシュすることでRailsプロジェクトの起動時間を50%〜70%程度縮めることに成功しています。 例えば、$ rails new によって生成されただけの空のRailsプロジェクトでは、Bootsnapによって起動時間が約65%短くなるのを確認できました。

課題

さて、この #to_binary ですが、その出力にはかなりの無駄があります。

iseq = RubyVM::InstructionSequence.compile("p 42")
p iseq.to_binary.length
# => 580

p 42というごく小さいコードから生成されたバイナリにも関わらず、その出力は 580byte という大きさになってしまいました (出力のサイズは環境によって異なります)。

当然、より大きいコードからは大きいバイナリが生成されます。 さきほどの空Railsプロジェクトであれば、Bootsnapが1632個の.rbファイルをキャッシュしており、そのキャッシュファイルの合計サイズは 32MB ほどになりました。

というわけで、本課題の目的はこの #to_binary の出力するバイナリのサイズを小さくすることです。 #to_binary の出力が小さくなると単純にストレージや転送時間の節約になるほか、Bootsnapがコンパイルキャッシュにアクセスする際のディスクアクセスが少なくなるため、Railsアプリケーションの起動時間が短くなることが期待されます。

方法

#to_binary の実装の大部分は Rubycompile.c に書かれています。

今回のインターンシップではこの実装を読みつつ、部分部分を書き換えていくことで徐々に出力のサイズを小さくしていきました。

特にバイナリサイズの削減に寄与した変更は主に以下の2つです。

1. 不要な構造体フィールドの出力の削除

従来の実装では ISeq の情報を格納した構造体の、本来出力する必要のないものや、常に同じ定数が出力されているフィールドなどが存在していました。 コードを読み解いて、それらを出力に含めないようにすることでバイナリのサイズを削減しました。

2. 整数値の符号化方法を変更

また、出力に含まれていたあらゆる整数値はほぼすべてが固定長で符号化され、4byteや8byteのデータ長で出力されていました。 しかし、出力される整数値はその出現頻度に大きな偏りがあり、多くが 01 などの少ないbit数で表現できる値です。 そこで、UTF-8を参考に可変長な整数の符号化方法を考え、導入することにしました。

0x0000000000000000 - 0x000000000000007f: 1byte | XXXXXXX1 |
0x0000000000000080 - 0x0000000000003fff: 2byte | XXXXXX10 | XXXXXXXX |
0x0000000000004000 - 0x00000000001fffff: 3byte | XXXXX100 | XXXXXXXX | XXXXXXXX |
0x0000000000020000 - 0x000000000fffffff: 4byte | XXXX1000 | XXXXXXXX | XXXXXXXX | XXXXXXXX |
...
0x0001000000000000 - 0x00ffffffffffffff: 8byte | 10000000 | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX |
0x0100000000000000 - 0xffffffffffffffff: 9byte | 00000000 | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX | XXXXXXXX |

この方法では、7bitで十分に表現できる値は1byteに、14bitで表現できる値は2byteに、というように符号化する整数の大きさによって必要なバイト長を変化させています。

UTF-8では1byte目の上位bitを使って後続のバイト数を表しているのに対して、この符号化方法では下位bitの連続する0bitの個数でバイト数を表現しています。 このような形式を採用した理由は、x86_64などの命令セットではbsftzcntといった命令を用いることで後続のバイト数が1命令で数えられるためです。

評価

これらの変更によって、バイナリの読み込み速度を損なうことなく #to_binary の出力のサイズを平均して 70%から75% 程度小さくすることに成功しました。 上記の空Railsプロジェクトでは、キャッシュファイルのサイズは合計 9.4MB (元の約30%) になりました。

f:id:NagayamaRyoga:20190926141101p:plain

その他の詳細なデータに関しては以下のチケットにまとめてあります。

https://bugs.ruby-lang.org/issues/16163

苦労した点

以下、今回の課題に取り組むにあたって苦労した点です。

プロジェクトの規模が大きいこと

Rubyは20年以上も継続して開発が続けられているプロジェクトであり、1万に近い個数のファイルによって構成されています。 特にC言語で記述されたソースコードの中には1ファイルが1万行を超えているものもあり、 (今回の実装に関連する部分はそのごくごく一部とは言え)処理の流れを把握するのが大変でした。

インターン期間の最初の1日は、ソースコードを読みながらバイナリデータを手でデコードし、おおよその処理の流れとデータ構造を理解していきました。

マルチプラットフォームなソフトウェアであること

Rubyは様々なOS、CPU、etcで実行される可能性のあるプログラムです。 そのため、どのような環境であっても正しく動作をするようにプログラムを書く必要がありますが、 C言語はその言語仕様の詳細 (例えば整数型のサイズと表現可能な数値の範囲、式の評価方法、評価結果など) の一部を"処理系定義"としています。

"処理系定義"の動作はコンパイラや環境によって異なる可能性があるため、 ある環境では動作をするが別の環境では動作しない、というようなことが起こらないように常に意識をする必要がありました。

まとめ

バイナリの読み込み速度を損なうことなく、そのサイズを70%以上も削減することに成功しました。 2019年12月リリース予定のRuby 2.7にこれらの変更が取り込まれ *1、実際のRailsアプリケーション上で動作するようになります。

世界的に有名なOSSに対して1ヶ月という短期間で貢献できたことは非常に貴重な経験になりました。 この場を借りて、メンターである笹田さんと遠藤さんに御礼を申し上げます。

2019 年度版:クックパッド x 広告領域の紹介

こんにちは。メディアプロダクト開発部の我妻謙樹です。サーバーサイドエンジニアとして、広告配信システムの開発・運用を担当しています。入社以来広告領域を担当するグループに所属しています。

クックパッドと広告

クックパッドでは、PS に次ぐ売上高を占める主力事業として、広告事業があります。

過去にも、"クックパッドの広告エンジニアは何をやっているのか"(公開日:2015-11-26)という記事が公開されたことがありますが、当時とは技術要素やチーム構成はもちろん、事業をめぐる環境が大きく変わっています。

しかし、上記記事でも述べられている、以下の原則は変わっていません。

クックパッドの広告は、昔から、ユーザさんと広告出稿企業さん、そして私たちクックパッドの3者ともが幸せになる形を模索し続けてきています。 クックパッドを通して、最終的には広告も「価値ある情報」としてユーザさんに届けば、それは広告単価の上昇にも繋がるからです。

広告は、広告を出稿してくださる企業の課題を解決するために存在し、ユーザーにその価値を届けるために存在しています。いわゆるアドネットワークを支える DSP/SSP を開発するのではなく、自社で配信システムを内製し、貴重なユーザーのデータを保持するからこそできる独自の広告事業の面白みが、クックパッドにはあります。

本記事を通して、クックパッドにおける広告事業、及びそれを支える私達の部・グループについて少しでも理解の一助となれば幸いです。

運用保守対象のサービス一覧

私達のグループでは、広告が入稿されてから配信されるまで一貫した自社システムを保守・運用しています。細かいシステムは多数ありますが、主要なコンポーネントのみを表示させたシステムアーキテクチャ概観は以下の図の通りです。

f:id:itiskj:20190913125033j:plain
msdev-system-overall
各システムについて、簡潔に紹介します。

Ad creative admin service

  • いわゆる「入稿」システム
  • 弊社の業務推進チームが、受注に従ってクリエイティブを入稿
  • 純広告とネットワーク広告の配信比率の調整、ターゲティングの設定、広告枠のリアルタイムレポートなど多機能

New ads delivery service

  • いわゆる「配信」システム
  • 新世代。クックパッド アプリケーション自体に内在されていた配信ロジックを別アプリケーションとして切り出したシステム
  • 初期は Rails アプリケーションだったが、機能の肥大化及びインフラコストの削減&開発速度の向上を目指し、一部の機能を Go に Microservices として切り出し

Legacy ads delivery service

  • いわゆる「配信」システム
  • 旧世代。初期は Cookpad アプリケーション自体に配信ロジックが内在されていた
  • 現在はほとんどの機能が新世代に移行済み、新規開発することは殆どない。旧バージョンアプリの互換性のために残していたが、物理削除含めて近日中に完全廃止予定。

Machine Learning services

  • いわゆる「最適化」システム
  • Ruby/Rails で実装された入稿システムにおいて、別言語ランタイムである Python と機械学習ライブラリを利用し、配信比率の最適化や在庫予測を行うため、AWS APIGateway + Lambda を利用した Microservices として実装されている

Logging (Lambda Architecture)

batch layer

  • いわゆる「DWH(Data Warehouse)」
  • すべてのログが格納されており、配信比率の最適化、レポーティング用集計など、入稿システムのあらゆる箇所で利用されている
  • サービスオーナーは DWH チーム
  • 業務推進チームやディレクターは、Tableau を利用しダッシュボードで可視化して業務に利用している

speed layer

  • サービスオーナーは我々のグループ
  • ストリーミングパイプラインによるリアルタイムログ基盤
  • 入稿システムにおけるリアルタイムレポートや、配信比率の最適化処理における精度向上に利用されている

Tracking service

  • いわゆる「DMP(Data Management Platform)」
  • サービスオーナーは DMP チーム
  • EAT(Extreme Audience Targeting) を始めとし、エリアターゲティングや検索キーワードターゲティングといった機能も提供している

技術スタック

先ほど紹介した各サービスで利用されている技術スタックは、以下の通りです。

f:id:itiskj:20190913125210j:plain
msdev-tech-stack

技術選定

前節で技術スタックについてご紹介しました。私達のチームでは、以下の図に表される評価軸に沿って、プロジェクトや事業の成果を達成するために最適な技術スタックを選択することを心がけています。

f:id:itiskj:20190913125235j:plain
msdev-tech-selection

「会社がこの技術を押すから」といった会社目線での観点や、「チームでこの技術を使っている人が多いから何となく・・・」といったチーム目線での観点、「この技術を使いたいから」といった技術的成長目線の観点だけで技術を選択することは有りません。以下の三点を総合的に判断することを心がけています。もちろん、技術選定に失敗したこともありますし、この評価軸が完璧ではありませんが、考える軸にはなります。

Tech - 技術的観点

技術自体の正しさを評価する軸です。例えば、ある課題に対して奇想天外な技術を選択することは、どれだけその技術を導入する難易度が高かったとしても、優れた設計では有りません。適切な「課題」に対して適切な技術を「解決」策として適用することこそが求められています。

そのために、数多くのミドルウェア、クラウドサービスに対しての知識を深め、少しでも引き出しを増やし、純粋に比較検討できるスキルが、技術選定に責任を持つテックリードに求められています。

その他にも、以下の観点を評価します。

  • 技術が開発されているエコシステムの発展性
  • 開発をサポートするツールの充実生
  • グローバル及び日本における技術トレンド
  • その技術を支える日本でのコミュニティ

Company - 会社的観点

次に、会社全体のその技術への関わり方を評価します。

例えば、クックパッドは Ruby/Rails をヘビーに利用する会社です。Ruby コミッターの方々も働いており、サポートも手厚いです。しかし、だからといって全てのサービスが Ruby であるべきか、Rails であるべきかというと違います。「技術的観点」および「チーム的観点」の比重を優先し、Ruby/Rails 以外の技術を選択することは往々にしてあります。

その他にも、以下の観点を評価します。

  • 会社のミッションに対する適合性
  • その技術を選択すると事業の成功にどれだけ貢献できる可能性が高いのか
  • 会社全体(他のチーム)で利用されている技術とは親和性が高いか
  • 会社のエンジニアカルチャーと適合するか
  • 採用の観点から、その技術を選択する優位性はあるか

Team - チーム的観点

最後に、チーム的観点から評価します。具体的には、部およびチームがどのような技術方針を持っているかという観点に加え、新卒からシニアメンバーまでそれぞれのメンバーの現在のスキルセットや目指すキャリアパスを考慮して総合的に判断します。

具体的には、以下の観点で評価します。

  • チームメンバーのその技術に対する成熟度
  • チームメンバの現在のスキルセットから想定できるその技術の吸収力
  • 各メンバーが目指すキャリアパスへの貢献具合
  • チームがスケールしたときに対応できる学習コストやサポート体制
  • その技術を選択することへのモチベーション

チーム体制

「領域:マーケティングサービス領域」を担当する部署が 4 つ存在しています。

f:id:itiskj:20190913125258j:plain
msdev-team

  • マーケティングサポート事業部
    • 国内におけるマーケティングサービスの企画、開発、運用及び営業に関する業務を担当する。営業グループの他、社内のデータを検証して新商品開発や営業提案資料を作成するデータチーム、日々の入稿作業やレポーティングを支える業務推進チームが所属
  • マーケティング企画制作部
    • マーケティングサービスの企画立案・制作・進行に関する業務を担当する。タイアップ広告を企業とともに作り上げる制作グループが所属している。
  • マーケティングプロダクト開発部
    • 国内に置ける広告事業及び企業向け事業に関する企画、商品開発に関する業務を担当する。主にディレクターが所属。
  • メディアプロダクト開発部
    • 国内の行ける企業向けプロダクト開発に関する業務を担当する。私達が所属している部署。

メディアプロダクト開発部

メディアプロダクト開発部では、以下の 3 つのグループから成立しています。9 割がエンジニアの組織です。広告領域を担当する私が所属しているのが、「マーケティングサービス開発グループ」通称 msdev です。

  • マーケティングサービス開発グループ(通称:msdev)
    • 広告領域を担当しています
  • プロダクト開発グループ(通称:pdev)
    • 動画領域を担当しています
  • プロダクトデザイングループ
    • デザイナーとディレクターが所属しています

プロダクト開発グループとの協同体制

プロダクト開発グループでは、動画領域を中心とし、数多くのサービスを開発しています。最近の開発については、以下の発表やブログが参考になるでしょう。

グループは違いますが、msdev と pdev は席も近く、部署も同じですので、頻繁にコミュニケーションがあります。プロジェクトによっては、片方のグループメンバーが片方のグループのプロジェクトを手伝う、といったこともあります。

これによって、広告領域に携わりながらも動画領域で利用されている技術に触れることができる、という大きなメリットがあります。例えば、私も広告領域のプロジェクトを担当する傍ら、過去に以下のプロジェクトに携わらせていただいたことがあります。

また、msdev/pdev それぞれのグループで勉強会を開催しています。もちろん横断して参加が可能です。過去には、以下のような内容で勉強会が開催されています。

  • 基礎技術詳解
  • AWS 各サービス詳解
    • DynamoDB. Parameter Store, API Gateway+Lambda, AWS IoT, CloudFront, etc.
  • 利用サービス詳解
    • terraform, Stripe, Tableau Desktop, etc.
  • カンファレンス参加報告
    • RubyKaigi 2019
  • 自分たちの保守運用するシステムのアーキテクチャ詳解
    • 動画配信サーバー, 広告配信サーバー, etc.

まとめ

広告領域は、技術的にチャレンジングな課題も多く、かつ事業の売上貢献に直結することが多い、非常にエキサイティングな領域です。また、アドネットワークではなく、自社の事業で専用の配信サーバーとユーザーデータを保持するからこその事業の面白さもあるため、事業開発に興味・関心が高い人にとっても活躍の可能性が大いにある場です。

メディアプロダクト開発部では、一緒に働いてくれるメンバーを募集しています。少しでも興味を持っていただけたら、以下からエントリーをしてください。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/