はじめに
私は、2024年2月の一ヶ月間、「CA Tech JOB」というインターンシップに参加し、 「ABEMA」のコンテンツ配信チームに所属していました。京都大学電気電子工学科4年生のChunと申します。今回のインターンシップを通して担当したタスクは3つで、どれもサービスへの観点が少し違い、いろんな領域で最新の技術を学ぶことができました。本投稿では、私が担当したタスクを簡単に説明させていただきます。
タスクの選定
私が着手するタスクを選ぶところから始まりました。最初、メンターさんから何個か、重要度が高くオンボーディングしやすいタスクを提示して頂きました。その際、私は、今回のインターンシップを通して下記の目標がありました。
- Go言語を用いたアプリケーション開発
- Kubernetesを使ってマイクロサービスを管理する
- AWSのリソースを含む外部のサービスとの連携を行う
この目標を元にインターンシップ期間を考慮しつつ、メンターさんと相談して担当するタスクを決めました。インターンである私が、してみたかったことをできる限り反映してくれたので、非常にモチベーションが高かった一ヶ月でした。それでは、具体的に担当していたタスクについて説明させていただきます。
担当したタスク
- Grafanaにメトリクスを出力し、特定条件で自動的に通知をする。
ABEMAは多数のユーザーに、数多くの動画を配信しているので、その負荷をしっかり監視し、適切な対応をする必要があります。そのために、Grafanaというサービスを使用し、サービスのメトリクスを可視化しています。
Granfanaのダッシュボード例(https://grafana.com/)
Grafanaは上記の図のように必要なメトリクスを可視化することができ、カスタマイズ性が非常に高いです。加えて、トリガーを設定し、通知を発火することもできるようになっています。
今回は、ABEMAで使用しているCDNで使用している帯域がどれぐらいなのかを可視化し、特定条件を越えると通知するようなシステムを構築しました。既に情報をAPIから取得するところまで実装されていたので、自分のタスクとしては、下記の項目を対応しました
– 該当APIを定期的に呼び出す
– PrometheusのExporterで取得したメトリクスを出力する
– アプリケーションの終了シグナルやエラーハンドリング
– Prometheusで取得したメトリクスから、Grafanaのグラフを定義する
– カスタム通知を定義し、特定の条件にWebhookを通して通知を行う
ABEMAのサービスは多数のマイクロサービスがKubernetes上で動いており、メトリクス管理のための基盤がすでにありました。今回は、その基盤に自分が新しく作ったマイクロサービスを追加し、運用するところまで担当しました。GrafanaのアラートについてはCloud Platformチームが予め共通する基盤を作っていたので、私は閾値を調整し、デプロイするところで解決することができました。
- Elemental Linkを管理画面から再起動させる。
このタスクは、一部の伝送で使われるエンコーダーである、Elemental LinkをABEMA内部で使用している管理画面から再起動できるようにして、運用時の利便性を向上させることが目的です。
まず、Elemental Linkは以下のような装置です。
AWS Elemental Link HD (https://aws.amazon.com/jp/elemental-link/)
この装置は、スタジオで撮影される動画をエンコードし、クラウドに伝送する機能を持っており、常時稼働することを想定して作られています。
この装置をコントロールするためには、AWSのコンソールで操作する必要があるのですが、ABEMA内部の管理ツールに統合し、問題があった場合、運用担当者がツールから操作することで、エンジニアへのエスカレーションなく、問題解決することが目標です。
ABEMAではマイクロサービスの間の通信をgRPCという通信プロトコルで行っています。
実装のために、gRPCの仕様を定め、監視するサービスの
[フロントエンド] -> [バックエンド] -> [AWSのAPIを呼び出す]
の手順で実装していきました。gRPCの定義から、バックエンド、フロントエンドに渡るまでいろんな領域で実装することができたタスクでした。
具体的には、Elemental Link操作のために、AWS SDK for Go 内部の medialiveパッケージを用います。medialive内部の RebootInputDeviceWithContext を用いることで、Contextから認証情報を取得し、指定されたリソースに対するリクエストを送信することができます。
実装した画面
上記の画像の中にある [再起動] というボタンを追加し、それぞれの系統の情報をフロントエンドで取得します。この情報を元に、ボタンを押下し、表示される確認モーダルの中のボタンを押すことで、バックエンドにリクエストを送信します。
バックエンドでは、受け取った情報を元に、該当のElemental Linkに対して RebootInputDeviceWithContextを呼び出し、その結果を返す関数を実装しました。
この課題を通して、gRPCの書き方を含め、Goのテストコードやモックについて学べることができました。
- アクセスログの不具合調査
ABEMAで一部の動画を再生する際に、DRM技術を使用して視聴権限があるかを確認しています。
ABEMAでは、ユーザーがアクセスするライセンスプロキシサーバーからDRMライセンスサーバーにリバースプロキシをして、権限チェックのマイクロサービスに問い合わせる実装をしていました。この時に特定のクライアントで視聴権限を問い合わせる際にABEMA内部のコンテンツIDがアクセスログに残らず、不具合の調査などに時間がかかる問題がありました。このプロキシサーバーは BigQuery にアクセスログを出力していて、この一部のフィールドが適切に埋められないことが問題でした。
問題の修正のためには、DRMのどの流れで権限を確認しているのかを確認する必要がありました。一番の問題は、最初のリクエストの時点ではABEMA内部のコンテンツIDを知ることができず、リバースプロキシのレスポンス中でそのコンテンツIDを知ることができるので、その値をどう取り扱うかを検討する必要がありました。
現状のシステムでは、コンテンツを特定することを別サービスで行っており、以下の流れで権限チェックを行います。
[ユーザー] -> [ABEMAのコンテンツプロキシ] -> [権限チェックの別サービス] -> [ABEMAのコンテンツプロキシ] -> [別のコンテンツ配信のサービス]
この中で、最初のリクエストではコンテンツの識別子がなく、別サービスの返り値を元に特定することができます。
このような実装はhttputil.ReverseProxyを使用することで実装することができます。
ここで、DRMサーバーからコンテンツを取得するリバースプロキシの簡単な実装例を示しています。あらかじめ、リクエストに対し、Content構造体のインスタンスを生成しておきます。次に、リクエストのコンテキストにそのContentオブジェクトのポインタをrequestContextKey構造体をキーとして挿入しておきます。
このリクエストをリバースプロキシを使い、DRMサーバーに転送します。この際にDirectorを定義することで、必要なリクエストを生成、転送してくれます。
DRMサーバーから帰ってきた値をModifyResponseでカスタマイズすることができます。ここで、DRMコンテンツの特定ができるので、レスポンスの中にあるリクエストのコンテキストをrequestContextKey構造体をキーとして取得することができます。従って、ここから抽出した元のContentオブジェクトの中身を書き換える処理を行うことで、リバースプロキシ処理後の元のオブジェクトには正しいコンテンツが挿入されることが可能となります。
最後に、リバースプロキシの呼び出し側でリバースプロキシを呼び出す前のリクエストのコンテキストにContentオブジェクトのポインタを挿入しておくと、呼び出しが終わった時点では、そのオブジェクトにDRMサーバーから取得したコンテンツ情報が反映されているようになります。
一ヶ月の流れ
- 1週目
最初の週のタスクとしては、パソコンの受け取りおよび、環境構築でした。大体2日以内で開発環境のセットアップが終わります。開発環境(IDE)や仮想環境のインストール、Goのインストールなど、開発しやすい環境を作ります。次に、早速タスクをメンターさんと話し合い、決めます。タスクが決まったら、コードを読んでいくのですが、ABEMAでは多数のマイクロサービスが密接に動いているので、多数のレポジトリレベルのコードを理解しないと全体的な挙動がわかりません。そのために、ドキュメントが多数用意されているので、読んで理解をしていきます。
- 2週目
2週目からは本格的にタスクに着手し、実装をしていきます。わからないことや必要なことは毎日のメンターさんとの1on1を通して話し合います。また、人事面談も毎週設定されていたので、開発的な面以外の話し合いもできます。大体、2週目の後半あたりで最初のタスクが終わりました。
- 3週目
2つ目のタスクを実装していきました。また、新しいことが多かったので、チームの方々と連携しながら実装していきました。
- 4週目
3つ目のタスクをし、後半には、引き継ぎ資料の作成を含め、ドキュメントを残すことが多かったと思います。最後に、業務で使用していたパソコンを返却することでインターンシップは終了します。
考えることができたのでいろんな観点でサービスを見る力も持てるようになったと思いました。
学んだこと
本インターンシップを通して、ABEMA内部でどのように配信が行われているのかについて知ることができました。特に、10個を超える多数のマイクロサービスに携わっていたので、コードレベルまでシステムを把握できたのが非常に良かったと考えています。ここに加えて、スタジオを見学することもできたので、実際に番組を撮影している設備を直接みることができたので、現場とクラウドをつなぐ部分についても体験しながら学べることができました。
技術的でない部分の学び
[ランチ]
メンバーさんが出社日の9割ほどのほとんど毎日ランチを設定してくださりました。ランチでは、毎日色んなチームの方々やマネージャーさんと会話する機会があり、サービスに対する色んな観点を学ぶことができました。例えば、インフラチームでは、全体のチームが使う基盤を作ることが目標であるので、サービスをより安定的かつパフォーマンスを重視していました。その反面、フロントエンドチームはよりユーザー側で小さい部分のUI/UXなどを中心的に議論をしていました。このように、多数のチームとの交流ができたので私もサービスに対する
[メンターさんとの1on1]
メンターさんが出社していない日はオンラインで、出社された日には対面で1on1をしました。限られた時間で、メンターさんとどのようにコミュニケーションを取るかは非常に大事だと考えています。
特に、今回のインターンシップでは短い間に知らないことを作っていくことが目標だったので、その時間内に必要な情報を取得する必要がありました。最初は、知らないことがあっても、基本的に質問せず、私一人で解決しようと頑張りましたが、なかなかうまくいかず、時間経っていったので、方向性を変え、少し悩んでみて難しそうであれば、質問内容としてまとめ、優先順位を決めて1on1で質問するようにしました。本インターンシップを通して、知らないことを聞かなければならない時にどうコミュニケーションを取るべきか、何を質問するべきかについてより経験を得たと感じています。
[チーム会議]
コンテンツ配信チームでは、毎週週次ミーティングを行います。週次ミーティングでは、全チームメンバーが集まり、進捗確認やタスクの分配、進捗の困りことなどを話をしました。他のチームの方々のタスクは私のタスクとは直接影響があることはありませんでしたが、こうチーム全体が集まり、チーム全体の方向性を決めることは非常に重要で、リモート勤務をされている方々との
[Developer Meeting]
月に一回程度、開発フロアの全員が参加する開発のミーティングに参加する機会があり、参加しました。社長さんや役員の方々も参加し、私のチーム以外のビジネスのところも話していて、非常に面白かったです。エンジニア的な考え方に加え、サービスをビジネス的にどのように成長させていくかについて考えるきっかけとなりました。
最後に
総合的にこのインターン期間を通して、ABEMAのいろんなサービスに携わることができたと評価しています。また、動画配信という今まで抽象的に考えていたのが、実際にどのように動いていて、ユーザーに届けられているのかも知ることができたので、非常に有意義な一ヶ月だったと考えています。2月は休日も多く、最初の営業日が少なかったので、あっという間に過ぎてしまいました。その中で技術力を上げて非常に有意義に過ごせたのかなと思いました。チームの皆さんも非常に優しく、リードしてくださり、とても感謝しております。これから本インターンに参加される方には是非ともABEMAの高い技術力を体験し、そしてその中のABEMAのサービスの基盤であるコンテンツ配信チームにて配信についてチャレンジしてみることをおすすめします!
コンテンツ配信チームについて
コンテンツ配信チームは、ABEMAのユーザーに映像を配信するための基盤を開発しています。具体的には、納品ファイルやライブ映像をインターネットで配信可能な形式に変換し、セキュアでスケーラブルで高品質な配信を安定的に行うことをミッションとしています。加えて、配信現場のオペレーションを考慮して、ライブ映像をクラウドにインジェストするための運用やシステムアーキテクチャの設計を行うこともミッションにしています。これらのミッションにより、ABEMAのユーザーの皆さんが楽しむことができる高品質なコンテンツの配信を可能にしています。