TECH PLAY

株式会社メドレー

株式会社メドレー の技術ブログ

1359

はじめに こんにちは。コーポレートエンジニアの溝口です。 メドレーでは、今年 7 月に稟議ワークフローシステムを導入しました。 詳細は「システム概要」の章でご紹介しますが、システムの全体像としては以下のようになっております。 ワークフローシステムと聞かれたら、どんなシステムを思い浮かべますか? 申請者がシステムで申請すると、予め定められた承認者へ承認依頼がメールで通知され、申請内容の確認及び承認のためにシステムへログインする、という流れがよくあるワークフローシステムではないかなと思います。 我々はコーポレート部門として「徹底的に合理性を追求した組織基盤や、仕掛けづくりを行っていく」ことを目指しています。故に、メドレーの稟議ワークフローシステムにおいても 便利 で 合理的 なシステムを目指して開発を行いました。今回 ChatOps の概念を取り入れることで、一般的なワークフローシステムよりも洗練されたシステムを構築できたかなと思います。 本稿ではシステム概要及び、裏側の仕組みをご紹介していきます。 最後までお付き合いいただければ幸いです。 ChatOps とは ChatOps とは「チャットサービス(Chat)をベースとして、システム運用(Ops)を行う」という意味です。ざっくり書くと「システムから Chat へメッセージを飛ばし、次のアクションが同じ Chat 画面で開始できる」というものとなります。(下記フローはあくまで一例です) ChatOps には以下のメリットがあると考えています。 常に立ち上げているツールという共通インターフェースである インタラクティブなコミュニケーションにつながり、スピーディである 共有しやすく、記録に残しやすい 本稿では、詳しく説明はしませんが、興味がある方は事例等を解説しているサイトもあるので、是非探してみてください。 なぜ ChatOps なのか 稟議申請においては 承認者「これ値引きしてもらって ×× 円になったはずだけど、金額間違ってない?」 申請者「すいません、変更し忘れました。差戻しお願いします」 などのコミュニケーションが度々発生します。 通常のシステムであれば、確認事項がある際はシステム内のコミュニケーション機能を使う、もしくは、Chat に URL や稟議番号を転記して確認のためのコミュニケーションを取ることが想定されます。 メドレー内の業務コミュニケーションは Slack 上で殆ど完結しています。 Slack ではない他の場所で会話が発生すると情報が分散しますし、Slack に URL を転記するといった行為や、別システムへのログインなども非効率です。 そこで、 共通インターフェースの Chat を中心にシステム構築する= ChatOps を採用し、稟議ワークフローを構築してみようと考えました。結果、稟議ワークフローシステムの情報を Slack へ連携し、稟議におけるコミュニケーションは Slack に集約、承認行為も Slack 上で可能、というシステムを構築することができました。 システム概要 申請 申請者は TeamSpirit 上で稟議内容を記入し、稟議申請を行います。 TeamSpirit とは、勤怠管理や工数管理、経費精算などを管理できるクラウドサービスです。Salesforce をプラットフォームとして採用しており、アイデア次第でいろいろなカスタマイズが可能です。 Slack から申請できるようにするのが ChatOps のあるべき姿かもしれませんが、過去の申請からコピーしたい、申請種別ごとに入力する項目が異なる等の要件を考慮し、TeamSpirit から申請するように設計しました。申請の導線については、今後もよりよい仕組みに磨き上げていきたいと考えています。 承認 申請者が「稟議申請」ボタンを押下すると、Slack の稟議チャンネルに申請内容及び添付ファイルが自動投稿されます。 承認者は申請内容に問題がなければ、投稿に配置されているボタンを利用して承認・差戻しが行えます。 承認者は稟議ワークフローシステムへアクセスすることなく、Slack で承認行為が完結できます。稟議内容において確認事項がある場合には Slack の投稿スレッドで申請者と質疑応答のやり取りができ、承認・差戻しの判断に必要なコミュニケーションが行えます。 後続のアクション 承認後には、 ・申請者に承認 or 差戻し結果を Slack の DM(ダイレクトメッセージ)で通知する 後続の担当者へ Slack で通知する (法務押印などの)承認後タスクを作成し担当者に通知する 等、後続のアクションへつながっていく仕組みも用意しました。 システムの裏側 入力インターフェース 入力画面は、TeamSpirit で標準提供されている 稟議オブジェクト を利用しました。入力項目は標準で用意されているコンポーネントを利用し、メドレー独自で定義しています。承認プロセスを定義すれば、Slack を使わずに TeamSpirit のみでも運用は可能です。 Slack 通知 Salesforce の標準機能と Apex を用いた Script 処理を使って Slack 通知をしています。 Apex とは、Salesforce 内で利用するビジネスロジック用のオブジェクト指向のプログラミング言語(ほぼ Java)のことです。 Slack 通知までの大きな流れは以下です。 稟議申請ボタンを押したタイミングでステータス項目を「未申請」から「申請中」へ変更 プロセスビルダーにてステータス項目が「申請中」になったことを検知して Apex をコール Apex 内で申請情報や承認者情報の取得 Slack API をコールし、Slack へ投稿 1~4 のプロセスを詳しく見ていきます。 1. 稟議申請ボタンを押したタイミングでステータス項目を「未申請」から「申請中」へ変更 申請者が「稟議申請」ボタンを押したタイミングで承認プロセスを走らせます。 申請時のアクションとして、 ステータス「申請中」とします。ステータスが変わる毎に処理を走らせているので、ステータス定義は一つ肝になります。 2. プロセスビルダーにてステータス項目が「申請中」になったことを検知して Apex をコール プロセスビルダーを利用することで「稟議レコードを作成または編集したとき」に何らかの処理を実施することが可能です。今回は、ステータスが「申請中」になった場合に Apex をコールする、という処理にしています。 3. Apex 内で申請情報や承認者情報の取得 通知に必要な情報を揃えるため、Apex の処理では稟議オブジェクトの申請情報と合わせて次の承認者情報も取得しています。 String ownerId = p . OwnerId ; //申請者のユーザ名を取得 String applicant = [SELECT Username FROM User WHERE Id = : ownerId ]. Username ; //承認プロセスのレコード取得 String processInstanceId = [SELECT Id FROM ProcessInstance WHERE TargetObjectId = : p . Id ORDER BY CreatedDate DESC limit 1 ]. Id ; //承認者の ID を取得 String approveId = [SELECT OriginalActorId FROM ProcessInstanceWorkitem WHERE ProcessInstanceId = : processInstanceId ]. OriginalActorId ; 4. Slack API をコールし、Slack へ投稿 Apex が取得した情報をもとに、Slack に投稿します。 稟議内容を記載し、申請者・承認者に対してメンションされるようにユーザ名も記載します。 また、今回は承認者用にインタラクティブボタンを配置する必要があったので、 Block Kit を利用し、ボタン付きメッセージを作成しました。 { "text" : "hoge" , "blocks" : [ { "type" : "section" , "text" : { "type" : "mrkdwn" , "text" : "fuga" } }, ... { "type" : "actions" , "elements" : [ { "type" : "button" , "text" : { "type" : "plain_text" , "text" : "承認" }, "value" : "Approve" , "style" : "primary" }, { "type" : "button" , "text" : { "type" : "plain_text" , "text" : "差戻し" }, "value" : "Reject" , "style" : "danger" } ] } ] } TeamSpirit(Salesforce)→Slack への投稿は開発において苦労したポイントの一つです。 Slack からのアクション Slack の投稿に埋め込んでいるボタンがクリックされた際は、Lambda を経由して TeamSpirit(Salesforce)の RestAPI をコールし、承認処理を実行しています。 また承認後は、ボタンを「承認」スタンプに置き換えています。 開発を終えて 稟議ワークフローシステムを導入するにあたり、ChatOps の概念を取り入れ Slack に連携する業務システムを構築しました。 承認者からは「Slack で承認やコメントができ、社外からでもすぐに対応できるので便利」「Salesforce-Slack 連携は他にも活用できるので是非やっていこう」などのコメントをいただきました。また、承認後にもスレッドにて、「振込お願いします」「物品届きました」等のやりとりも行っており、情報が Slack に集約されていく狙い通りの運用になったかと思っています。 Chat サービスを利用している会社では、今回ご紹介した ChatOps は業務効率化するにあたり、有効な手法になるのではないでしょうか。もちろん、すべて Chat に連携すればよいというものでもなく、しっかり設計や運用検討を行う必要があります。 今後は ChatOps に限らず業務効率化につながるものはどんどんやっていきたいと考えています。 さいごに メドレーのコーポレート部門では「徹底的に合理性を追求した組織基盤や、仕掛けづくりを行っていく」ことを目指して、業務効率改善のための開発を推進しています。面白そう!と感じた方、メドレーでどんどん改善してみたい!と思っていただけた方は、ぜひ弊社採用ページからご応募お願いします! https://www.medley.jp/jobs/ 最後まで読んでいただきありがとうございました。
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 https://www.medley.jp/jobs/
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
はじめに はじめまして、メドレー新卒入社 2 年目の森川です。 インフラ経験がまだ 4 ヶ月ほどの未熟者ですが、 AWS 認定資格クラウドプラクティショナー の試験に合格することができました。上位の資格取得に向けて今後も勉強していきます。 先日私が担当させていただいた CloudFront のアラート改善について、問題の原因と対応方法を本記事で書かせていただきます。 よろしければお付き合いください。 背景と問題 弊社が運営しているプロダクトの一つ ジョブメドレー ではインフラ環境に AWS を利用しています。 監視には CloudWatch や Datadog などを使用しています。サービスの異常を検知するための設定のひとつに、CloudFront のエラーレスポンス増加を検知するためのアラート通知があります。 CloudFront が返すレスポンスのうち、特定の時間範囲の中で 4xx, 5xx 系のエラーを返した割合が閾値を超過したことを検知して、CloudWatch アラームから Lambda を通して Slack に通知を行っています。 ところが、ある頃を境に CloudFront での 4xx 系エラーレスポンスの発生割合が増加し、アラートの通知頻度が想定以上に高くなってしまいました。 原因 調査を行ったところ、刷新した社内システムにて以下 2 つの原因でアラートが発生していることが分かりました。 原因 1. 社外サービスからのアクセスでアラートが発生 CloudFront のログを確認したところ、社外サービス(Slack, Google スプレッドシートなど)からのアクセスに対してステータスコード 403 を返しているレスポンスログが数多く記録されていました。 これらのサービスに弊社の社内管理システムの URL がポストされると、プレビューを表示するためのリクエストが送信されますが、この時のリクエストが社外からのアクセスとして WAF で制限されていました。 インフラ刷新前から現在まで稼働している CloudFront のログも確認したところ、こちらでも同様のエラーレスポンスが発生していることが分かりました。しかし、エラー割合増加のアラートが頻発することは現在でもほとんどありません。 以前はジョブメドレーが持つシステム全体へのアクセスをひとつの CloudFront で処理していたため、アラート通知の割合として計算する際の母数が大きく、社外からのアクセスによるエラーが発生していても、その割合が閾値を超過することが少なかったからだと考えられます。 インフラ構成を刷新したことをきっかけに、これまで目立っていなかった社外からのアクセスという問題が表面化してきたのです。 原因 2. 利用者が少ない時間にエラーレートが高くなりアラートが発生 CloudWatch アラームでは、一定期間内でのレスポンスのうち、4xx, 5xx 系のエラーごとにその割合が閾値を超過したことを検知してアラートを発生させる設定としていました。 しかし、深夜など利用者が少ない時間に一度でもエラーが発生すると、その割合が跳ね上がってしまうことでアラート発生頻度が増加し、誤検知と言える状態になっていました。 以下の画像では、4xx 系エラーの割合が夜間に 100%となっている箇所が確認できます。(表示時間は UTC です) 対応方法 2 つの原因に対し、それぞれ対応を行いました。 対応 1. 特定の社外サービスからのアクセスをエラー検知の対象外とする 各サービスの設定により、プレビュー表示によるアクセスを停止させる選択肢が考えられます。しかし、該当するサービスすべてに設定を行うのは難しく、管理も複雑になりそうです。 そこで、特定の社外サービスからのアクセスを エラー検知の対象外とする 方針で対応を行いました。 ログのすべてを CloudWatch アラームの評価対象としていたために、誤検知と言えるアラートが発生しているのが現状です。したがって、評価させたいログだけに絞り CloudWatch で評価させることができれば解決が図れます。今回であれば、特定のユーザーエージェントや IP アドレスなどを除外して CloudWatch に渡すという処理が求められます。 その実現のため、今回新たに作成したのが Lambda の関数です。 S3 に CloudFront のログが保存されたことをトリガーに Lambda を起動させるように設定しました。 ログごとに記録されているリクエスト元のユーザーエージェントや IP アドレスなどを確認し、除外対象かどうかを判定します。 そうして選別を通過したログを今度はステータスコードの 5 つのクラス(1xx, 2xx, 3xx, 4xx, 5xx 系)ごとに振り分けます。 ただし、CloudFront ではステータスコードに 000 が入ることがあります。 https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ステータスコード 000 はアラートで検知したところで対応できることが特にないため、検知対象から除外する方針としました。 (S3 のログを直接確認すると 000 なのですが、Athena でログを確認すると 0 で表示されるため、少しハマりました) こういった意図しない値がステータスコードに含まれていた場合などを検知できるようにするため、5 つのクラス以外の値が含まれていた場合に UNKNOWN_STATUS_CODE なクラスとして分類するようにしました。 必要なものに絞ったログを 6 つのステータスパターンに分け、それぞれの件数を CloudWatch メトリクスへ PUT させます。 ここまでが Lambda の仕事となります。 各ステータスのログの件数を CloudWatch メトリクスで確認できるようになったので、レスポンス全体における 4xx, 5xx 系エラーの割合が算出できます。これを元に閾値を設定し、以前のようなアラートを作成することができました。 対応 2. CloudWatch アラームの検知ルールを調整する 利用者が少ない時間にエラーレートが高くなりアラートが発生する件については、 CloudWatch アラームの検知ルールを調整 することによって対応しました。 一定期間でのエラー数に閾値を定め、超過した際にアラートを通知するように変更しました。つまり、割合ではなく絶対数で判断させるようにしています。 以下の画像の緑色のグラフが新たな検知ルールで参照するものとなります。橙色で示しているのが 4xx 系エラーの割合ですが、これが 100%となっている箇所においても新たな検知ルールには反応していないことが分かります。 対応を終えて Lambda を用いた集計処理の作成と、アラートの検知ルールの調整を行うことで、CloudFront のエラー監視精度を向上させることができました。 以前は頻繁にアラートがあがっていましたが、対応後はすっかり落ち着きを見せています。 システムの安定稼働を実現するためにも、適切にアラートを検知できるように今後も改善を図っていきたいと思います。 今回の課題に対する解決手段としてはシンプルな対応であったかとは思いますが、私には実りの多い紆余曲折な経験となりました。 AWS の基本的なサービスの連携を学ぶことができたことに加え、新たに作成する AWS のサービスの課金額の試算や、実行計画を定めてからの実装など事前準備を意識して取り組むことができました。恵まれた環境の中、日々学ばせていただいております。 さいごに メドレーでは「医療ヘルスケアの未来をつくる」というミッションを掲げ、各プロダクトの開発・運営が進められています。 エンジニア・デザイナーをはじめ多くのポジションで新たなメンバーを募集しています。ご興味をお持ちいただけた方は、ぜひお気軽にお話しさせていただければと思います! ここまでお付き合いいただき、ありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 https://www.medley.jp/jobs/
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
こんにちは、第一開発グループの矢野です。ジョブメドレー開発エンジニアとして、主にバックエンドを担当しています。 直近では、ジョブメドレーが先月リリースした 「動画選考」 機能の開発プロジェクトに携わっており、動画ファイルのアップロード/配信環境の設計・実装を行っていました。 今回のブログでは、この「動画選考」機能の開発に利用した AWS Elemental MediaConvert サービスと、 hls.js という OSS ライブラリについて紹介したいと思います。 ジョブメドレーの「動画選考」機能 はじめに、今回リリースした「動画選考」機能について概要を紹介します。 新型コロナウイルス感染拡大によって、対面での面接に不安を感じたり、公共交通機関の利用が難しくなったりすることにより、満足な転職活動ができなくなっている方もいらっしゃるかと思います。 このような課題を解決するために、ジョブメドレーではリアルタイムにオンラインで面接を行う「WEB 面接」と、事業者があらかじめ設定した質問に対して応募者が動画で回答を送る「動画選考」の 2 つの機能を提供開始いたしました。 ref. WEB 面接・動画選考機能のリリースのお知らせ 動画選考(動画面接)は、近年増加傾向にあるオンライン選考の一種です。一般的に、求職者 / 就活生が PC ・スマートフォン等のカメラで、予め用意された設問に応じて動画を撮影し、企業に送ることで選考を行います。 ref. WEB 面接・動画選考とは? 実施の流れ、使用ツール、マナー、注意点などを徹底解説! 私たちジョブメドレーの動画選考では、事業所があらかじめ設定した質問に対して、求職者が回答動画を提出することができます。事業所も求職者も、動画で質問・回答を送ることで、書類だけでは伝わらない雰囲気や強みを相手に伝えることができます。 WEB 面接・動画選考機能のリリースのお知らせ 動画配信サービスの設計ポイント Web アプリでこのような動画配信サービスを開発する場合、「ユーザによる動画アップロード環境」と「ユーザへの動画の配信・再生環境」を提供する必要があります。 ジョブメドレーで扱う動画は一般公開されるものではなく、公開条件も複雑です。 よって今回は、この「動画アップロード/配信環境」を自サービス内に構築する方針をとり、以下のような動画まわりの設計ポイントについて検討・技術選定を行うことにしました。 (もちろん、要件によっては YouTube や、法人向け動画配信プラットフォームを契約した方が手軽な場合もあるかと思います) 動画の録画・撮影 サポートしたい動画ファイルのフォーマットをどうするか Web アプリ内に録画機能を設けるか 動画のアップロード(ストレージ) 動画ファイルのバリデーションで「動画ファイルの解析」を行うか 動画ファイルのアップロード先(ストレージ)をどこにするか 動画のエンコード 動画ファイルのエンコード形式(H.264、HLS 等)をどうするか 非同期エンコードの場合、ステータス検知・エラーハンドリングをどうするか 動画の配信(ダウンロード) 配信形式(ダウンロード/ストリーミング)をどうするか 暗号化をする場合、復号をどのように行うか 動画ファイルの公開方法(アクセス制限)をどうするか 動画の再生 Web ページ上で再生させるのか、その場合の表示・再生制御をどうするか ブラウザサポートをどこまでにするか、非対応・エラー時の制御をどうするか 今回は、上記の太字で記載した 「動画のエンコード」に MediaConvert を、 「動画の再生」に hls.js をそれぞれ採用しています。 各項の詳細は省きますが、全体を通して大まかに、以下のフローで「動画アップロード → エンコード(変換)→ 配信・再生」を実現することにしました。 ブラウザから Ajax で動画を S3 へアップロードする MediaConvert が動画を HLS 形式にエンコード(変換)する ブラウザで hls.js を使い動画を CloudFront からストリーミング形式で受信、再生する 今回はこの「動画アップロード → エンコード(変換)→ 配信・再生」に焦点を絞り、MediaConvert と hls.js をどのように使ったのかを紹介します。 MediaConvert による HLS エンコード AWS Elemental MediaConvert は、S3 との親和性が高いファイルベースの動画変換サービスです。自前で ffmpeg などを使って動画エンコードサーバを構築・管理することなく、スケーラブルな動画変換処理を手軽にシステムに組み込むことができます。 ref. AWS Elemental MediaConvert 料金は出力する動画の再生時間に応じた従量課金です。AWS コンソールから GUI ベースでエンコード設定を作成したり、ジョブ(エンコード処理)を登録することができます。 また、他 AWS サービス同様に API が提供されており、AWS CLI や各言語の SDK を使ってプログラムからエンコード処理を登録することができ、システム連携も容易です。 # CLI でエンコードジョブを登録する例 $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json 上記 CLI コマンドで下のようなエンコード設定を記載した JSON を使いジョブを作成すると、S3 上の動画ファイルをサクッとエンコードしてくれます。ジョブはキューイングされ、内部で並列処理されるため、大量のエンコード要求にも簡単に応じることができます。 { ... "Settings" : { "Inputs" : [ { # 入力元の S3 バケット上の動画ファイル key を指定 "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # 出力先の S3 バケット key を指定 "Destination" : "s3://testbucket/output" } }, # 動画・音声のエンコード設定を指定 # ここで品質レベル毎に振り分けた複数のファイルを出力したり # サムネイル jpg を作成したりすることも可能 "Outputs" : [ { "VideoDescription" : { … }, "AudioDescriptions" : { … } } ] } ] } } ref. AWSCLI を使用した AWSElemental MediaConvertCreateJob の例 エンコードが完了したジョブは、cron + SDK などで API を介して定期チェックする他に、CloudWatch Events によるイベント監視 → Lambda で処理するようなこともできます。 ref. AWS Elemental MediaConvert による CloudWatch イベント の使用 なぜ動画を再エンコードするのか 通常、ユーザからアップロードされる動画ファイルは、既に何らかのコーデックで圧縮され .mp4 や .mov などのコンテナフォーマットに変換されていることが殆どです。 しかし Web ページで <video> タグを使いこれら動画ファイルを再生しようとした場合、 「動画フォーマットにブラウザが非対応だと再生できない」 という環境依存問題があります。 ブラウザと動画フォーマットのサポート表 ref. HTML5 video > Browser support この問題に対応するため、多くの動画配信サービスでは、ユーザの動画を多くの環境で再生可能な MP4 コンテナフォーマット(H.264 + AAC コーデック)などの形式へ「再エンコード」しています。 ジョブメドレーの動画選考では上記目的に加えて、動画閲覧時の回線・端末負荷を抑える 「HTTP ストリーミング形式」 で動画を配信するために、アップロードされた動画を全て HLS 形式 にエンコードしています。 HLS - HTTP Live Streaming 形式 HLS は HTTP Live Streaming の略で、Apple 社の開発した規格です。HTTP ベースのストリーミング通信プロトコルで、細切れにした MP4 動画ファイルを分割ダウンロードさせることで動画のストリーミング配信を実現しています。 HLS 形式にエンコードされた動画は .ts という分割されたメディアファイル群と、 .m3u8 という、メディアファイルの取得先や秒数などを記載したテキストファイルで構成されます。 .m3u8 ファイルの例(マニフェストファイル、プレイリストファイルとも) #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS は他のストリーミング形式と比較して、ライブ配信 / VOD どちらにも対応可能なこと、対応ブラウザが多いこと、専用の配信サーバを使わずに配信可能なことなどから、近年の動画配信サービスで広く利用されています。 Web エンジニアの視点から見ても、 HTTP ベースなためキャッシュや HTTPS 暗号化など、既存 Web 技術と掛け合わせることが想像しやすく、扱いやすい印象でした。 MediaConvert の HLS エンコードジョブ設定 実際にプログラムから API 経由で HLS エンコードジョブを登録する際の設定 JSON は、以下のように GUI でジョブテンプレートを作成して確認することができます。 この「 JSON を表示」で、前述した CLI コマンド mediaconvert create-job --cli-input-json に渡せる JSON が表示されます。実装の際にはこちらを参考にしながら、 ユーザーガイド を参照して利用したい機能にあわせた設定を追加していくことをおすすめします。 注意点・つまづいたポイント 利用前に IAM で MediaConvert 用ロールの設定が必要です ステップ 3. IAM 権限の設定 AWS コンソールの Service Quotas > AWS サービス > AWS Elemental MediaConvert から確認できますが、エンコード並行処理の同時実行数上限は 20 になっています AWS ルートアカウント 1 つにつき 1 サービスが割当てられるので、これを増やしたい場合は申請が必要です エンコードジョブをキューイングする「キュー」を作成して、ジョブの登録時に選べるのですが、上記した「並行処理の同時実行数上限」はこの「キュー」毎に均等に振り分けられます 例えば「本番キュー」と「検証キュー」の 2 つのキューを作成した場合、それぞれの並行処理の同時実行数上限は 10 ずつになるので注意してください マニフェスト期間形式(Manifest duration format)に整数(INTEGER)を指定していると、iOS Safari で「動画の実際の再生時間と、再生プレイヤーのシークバーに表示される合計時間にズレが生じる」問題がありました 浮動小数点(FLOATING POINT)に変更することで対応しました、マニフェストファイルに出力される各 .ts ファイルの長さが、浮動小数点 → 整数に変換され切り上げられることでズレが生じているようでした hls.js による HLS 動画の再生制御 MediaConvert により HLS 形式にエンコードされた動画を、Web ブラウザで再生するために必要なのが、hls.js です。 ref. video-dev/hls.js 実は HLS によるストリーミング配信は、現状 Safari など限られたブラウザでしかネイティブでサポートされていません。 ref. https://caniuse.com/http-live-streaming この HLS 動画を Safari 以外の Google Chrome や IE11 などの主要ブラウザで再生可能にするため、hls.js が利用されています。内部的には、非対応ブラウザ環境において、ブラウザの MediaSource 拡張 を使って HLS 動画を再生する仕様になっています。 Video.js との比較 似たようなライブラリに Video.js というものもあり、導入を迷ったのですが … Video.js は UI もセットになった「 HLS に対応した再生プレイヤー」ライブラリ HLS 対応以外にも、字幕や章分けなど機能が豊富 hls.js はブラウザ標準の <video> タグで HLS に対応することだけを目的にした「 HLS クライアント」ライブラリ UI などはなく、動画再生プレイヤーはブラウザ標準のまま …と、上記のように hls.js の方がシンプルにやりたいことを実現できるため、今回は hls.js を採用しました。 GitHub のスター数は先発の Video.js の方が多いのですが、hls.js も開発は活発で、日本では グノシー さん、世界的には TED や Twitter でも採用されており、十分実績があるかと思います。 hls.js による実装 基本的には README の Getting Started の通りで実装できます。一部 README のサンプルコードから抜粋して解説すると… var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } 上記 Hls.isSupported() の分岐で、HLS をネイティブサポートしていないブラウザの処理を実装しています。 本来 <video> の src 属性にセットするべき .m3u8 ファイルの URL へ hls.loadSource() でアクセスさせ、クライアントから XHR リクエストを飛ばします。その後 hls.attachMedia() でインスタンスを DOM 上の <video> タグに紐づけています。 else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } 上記の分岐が iOS Safari など、HLS 動画をネイティブサポートしているブラウザ向けの処理です。単純に .m3u8 への URL を <video> タグの src へ付与しているだけですね。 (サンプルコードでは、マニフェストファイルのロード後に自動再生させるようになっているようです) 注意点・つまづいたポイント hls.js クライアントが取得する HLS 動画ファイル群は、CORS ヘッダで GET リクエストを許可された環境に設置する必要があります .m3u8 マニフェストファイルをアプリの API などから返却する場合、Content-Type を application/x-mpegURL にして渡す必要があります iOS Safari などの hls.js 非対応ブラウザ向けの実装を意識する必要があります hls.js による制御が複雑になるケースでは、同じような制御を hls.js 非対応ブラウザ向けに実装できるか?をイメージできないと手戻りが発生しそうです この他、フロントエンドでは <video> タグのブラウザ毎の挙動や、表示の違いに時間がかかりました。(ある程度予想はしていましたが、やはりメディアの取り扱いは難しい…) hls.js 自体は導入も手軽で、サクッと HLS 動画のマルチブラウザ対応が実現でき、とても使いやすかったです。@types も存在するので、TypeScript 環境でも難なく実装できました。 SSR や HLS + AES-128 の再生にも対応しているので、興味のある方は一度 公式ドキュメント を確認してみてください。 おわりに 従来、動画配信サービスを構築する場合、ffmpeg を載せたエンコードサーバや、ストリーミング配信サーバを別建てして、負荷に応じてスケールさせて…のような設計が必要だったかと思います。 今回、MediaConvert をはじめとした AWS サービスと hls.js を利用することで、手軽に、スケーラブルな動画エンコード/HTTP ストリーミング配信環境を構築することができました。 ジョブメドレーの動画選考はまだリリースしたばかりですので、今後反響を見ながら、さらなる改善を重ねていけたらと思います。最後までお読みいただきありがとうございました。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
アバター
株式会社メドレーのエンジニアの笹塚です。 私が開発を担当しているジョブメドレーで、先月 10 月 23 日に WEB 面接・動画選考をリリースしました。 job-medley.com WEB 面接、動画選考ともに、昨今の非対面での就職活動ニーズに応えるべく開発しました。 リリースは 2 つの機能を同時ですが、今回は WEB 面接の裏側に絞ってご紹介します。 WEB 面接概要 WEB 面接とは、リアルタイムで事業者様と求職者様が、オンライン面接を行うことができる機能です。 専用のアプリケーションは必要なく、PC、スマートフォンのブラウザから利用できます。 サービス選定 開発にあたり、いくつかの候補があがりましたが、最終的には自社内でも導入実績のある SkyWay を使用することにしました。 SkyWay とは WebRTC(Web Real Time Communication)を使用したオンラインのビデオ通話を、サービスに導入できるマルチプラットフォーム SDK です。 2020 年 11 月時点で、JavaScript SDK、iOS SDK、Android SDK が提供されています。 シグナリングサーバなどの WebRTC に必要となるインフラ構築が不要です 使用上限つきの無料プランもあります NTT コミュニケーションズが開発しています webrtc.ecl.ntt.com WEB 面接の対応ブラウザバージョン(2020 年 11 月時点) プラットフォーム 対応バージョン PC: Google Chrome バージョン 84 以上 PC: Microsoft Edge バージョン 84 以上 iOS: Safari iOS12 以上 Android: Google Chrome バージョン 85 以上 Andoid9 以上 SkyWay  JavaScript SDK の動作確認ブラウザ、WebRTC の対応状況、利用者の利用傾向から対応ブラウザのバージョンを上記のように設定しました。 webrtc.ecl.ntt.com SkyWay の接続モデル SkyWay でビデオ通話を実装する場合、2 種類の接続モデルから選びます。 SkyWay 電話モデル 電話のように 1 対 1 でのビデオ通話を想定したモデルです。 Peer インスタンス(シグナリングサーバによって発行された一意の PeerID を持つ)同士で接続します。 接続するためには、相手の PeerID が必要になります。 SkyWay ルームモデル 同一ルーム内の全ての Peer でビデオ通話するモデルです。 ルーム名を使用して参加します。相手の PeerID を知る必要はありません。 ルーム名は API キー毎に独立しています。 ルームの接続タイプはフルメッシュか SFU の 2 種類から選べます。 参加者全員へのチャットなどのデータ送信もできます。 ジョブメドレーの WEB 面接では、 今後の機能拡張を想定して、こちらのルームモデルを採用しました。 ルームの通信タイプ ルームに複数人が参加している場合、それぞれの通信をどう行うかを、メッシュと SFU から選ぶことができます。 フルメッシュ 画像引用元: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 全員が相互に通信を行います。人数が増えると、人数分端末のエンコード負荷と通信量が増加します。 SFU SFU の場合、上りの接続は 1 本になるので、メッシュよりも端末のエンコード負荷や、通信量の軽減が期待できます。 画像引用元: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 通信方式の違いは SkyWay が隠蔽してくれるので、joinRoom 時の mode を mesh から sfu に変更するだけで切り替わります。 peer . joinRoom ( roomName , { mode: "mesh か sfu を指定" , stream: mediaStream }); webrtc.ecl.ntt.com ジョブメドレーの WEB 面接では、面接参加人数を考慮して mesh を使用しています。 実装イメージ const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ルーム参加後に発生するイベント }); room . on ( "peerJoin" , ( peerId : string ) => { // ルームに誰か参加した場合に発生するイベント }); room . on ( "stream" , ( stream : RoomStream ) => { // stream を受けた場合に発生するイベント }); room . on ( "data" , ({ src , data }) => { // data を受けた場合に発生するイベント }); }); peer . on ( "error" , ( error : Error ) => { // エラー発生時に発生するイベント }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer を作成し peer.joinRoom() でルームに参加 room.stream イベントで他の参加者の stream を受け取る room.data() でチャットなど、データ送信もできる room.close() でルームから退出 が SkyWay の JavaScript SDK を使用した基本的な実装になります。 この実装に navigator.mediaDevices.getUserMedia() で取得した stream を joinRoom で渡す steam イベントで受け取った stream を video で再生する を追加すれば、オンラインでのビデオ通話が可能になります。 スマートフォン対応 PC と同様のコードでほぼ動作しましたが、iOS や Android 端末で、機種依存と思われる挙動の調査と対応に時間がかかりました。その一部を紹介します。 タブを移動すると映像が映らない 発生した問題 iOS12、iOS13 などで 複数のタブをひらいた場合に、映像の取得ができなくなる スクリーンロックからの復帰時に、映像が取得できなくなる という問題が起きました。iOS14 ではタブ切り替え時に映像を取得できるようになっていましたが、スクリーンロックからの復帰時は映像を取得できないままでした。 対応 この対応は visibilitychange イベントで、タブの切り替えと、スクリーンロックからの復帰時のイベントを拾い 取得済の stream の track を stop する stream を取り直す SkyWay の room.replaceStream() で、WebRTC で使用している stream を差し替える 以上の実装により、対応しました。 __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); イヤホンの操作で映像が止まる 発生した問題 iOS12、iOS13 では起こりませんでしたが、iOS14 でオンライン面接途中にイヤホンをスマートフォンから外すと、相手側の映像が止まるようになりました。 対応 それまでは、相手の映像と音声を再生するために、video タグに stream を渡して映像と音声を再生していましたが video : mute にして映像を再生 audio : 音声を再生 と、音声と再生を分けたところ、イヤホンを外しても停止することはなくなりました。 制約設定 getUserMedia で取得する MediaStream は、制約を設定することでデバイスの消費リソースを抑えることができます。 設定例: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); ↓ navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); 制約が設定可能かどうかの確認: getSupportedConstraints() で、対応している制約名を取得することができます。 const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 使用しているブラウザがその制約に対応しているかを確認し、対応している場合のみ設定を有効にします。 例えば、スマートフォンの場合に以下の設定をすれば、インカメラを使用し、フレームレートを 20 に抑え、320x320 の解像度に制限することができます。 const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; 指定した制限が必ず使用される保証はなく、機種依存の影響を受ける設定でもあるので、対象としている環境にあわせて検証と調整をする必要がある点には注意が必要です。 動作検証中に、機種依存と思われる挙動をした例を紹介します。 オプション 挙動 frameRate 指定すると一部 Android 端末で以下の挙動をした。 1. Android 端末で面接に参加する 2. iOS Safari で参加する 3. Android 側の映像と音声が Safari に送られない Safari で先に参加する場合には問題がない。 解像度指定 例: width: 320, height: 320 frameRate だけ指定していたときに上記の挙動をした Android、iOS Safari の組み合わせで問題が起こらなくなった。 解像度を指定するとインカメラではなく、リアカメラを使う Android 端末があった。 制約設定については、現在も調整中です。 参考情報 SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay のルーム機能を使用したデモ環境です。 GitHub にコードも公開されているので、ルーム を使用した場合の挙動と実装方法を確認できます。 開発をしていると、実装の問題なのか、SkyWay の SDK の仕様なのか、特定のデバイスで起こる問題なのかの切り分けに時間がかかるため、こちらの環境が参考になりました。 まとめ SkyWay が WebRTC とグループでのビデオ通話の実装を統合して提供してくれるため、開発時は、自社サービスとしての WEB 面接の機能に集中することができました。 スマートフォンのブラウザ対応と調査に時間がかかることもありますが、今後も利用者からのフィードバックを得ながら改善していきたいと思います。 メドレーでは、ニーズにあわせた新機能の開発にも力を入れています。多くの利用者に実際に使われるサービスの開発をしてみたいと思った方、ぜひお気軽にお話しましょう! www.medley.jp
アバター
株式会社メドレーのエンジニアの笹塚です。 私が開発を担当しているジョブメドレーで、先月 10 月 23 日に WEB 面接・動画選考をリリースしました。 job-medley.com WEB 面接、動画選考ともに、昨今の非対面での就職活動ニーズに応えるべく開発しました。 リリースは 2 つの機能を同時ですが、今回は WEB 面接の裏側に絞ってご紹介します。 WEB 面接概要 WEB 面接とは、リアルタイムで事業者様と求職者様が、オンライン面接を行うことができる機能です。 専用のアプリケーションは必要なく、PC、スマートフォンのブラウザから利用できます。 サービス選定 開発にあたり、いくつかの候補があがりましたが、最終的には自社内でも導入実績のある SkyWay を使用することにしました。 SkyWay とは WebRTC(Web Real Time Communication)を使用したオンラインのビデオ通話を、サービスに導入できるマルチプラットフォーム SDK です。 2020 年 11 月時点で、JavaScript SDK、iOS SDK、Android SDK が提供されています。 シグナリングサーバなどの WebRTC に必要となるインフラ構築が不要です 使用上限つきの無料プランもあります NTT コミュニケーションズが開発しています webrtc.ecl.ntt.com WEB 面接の対応ブラウザバージョン(2020 年 11 月時点) プラットフォーム 対応バージョン PC: Google Chrome バージョン 84 以上 PC: Microsoft Edge バージョン 84 以上 iOS: Safari iOS12 以上 Android: Google Chrome バージョン 85 以上 Andoid9 以上 SkyWay  JavaScript SDK の動作確認ブラウザ、WebRTC の対応状況、利用者の利用傾向から対応ブラウザのバージョンを上記のように設定しました。 webrtc.ecl.ntt.com SkyWay の接続モデル SkyWay でビデオ通話を実装する場合、2 種類の接続モデルから選びます。 SkyWay 電話モデル 電話のように 1 対 1 でのビデオ通話を想定したモデルです。 Peer インスタンス(シグナリングサーバによって発行された一意の PeerID を持つ)同士で接続します。 接続するためには、相手の PeerID が必要になります。 SkyWay ルームモデル 同一ルーム内の全ての Peer でビデオ通話するモデルです。 ルーム名を使用して参加します。相手の PeerID を知る必要はありません。 ルーム名は API キー毎に独立しています。 ルームの接続タイプはフルメッシュか SFU の 2 種類から選べます。 参加者全員へのチャットなどのデータ送信もできます。 ジョブメドレーの WEB 面接では、 今後の機能拡張を想定して、こちらのルームモデルを採用しました。 ルームの通信タイプ ルームに複数人が参加している場合、それぞれの通信をどう行うかを、メッシュと SFU から選ぶことができます。 フルメッシュ 画像引用元: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 全員が相互に通信を行います。人数が増えると、人数分端末のエンコード負荷と通信量が増加します。 SFU SFU の場合、上りの接続は 1 本になるので、メッシュよりも端末のエンコード負荷や、通信量の軽減が期待できます。 画像引用元: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 通信方式の違いは SkyWay が隠蔽してくれるので、joinRoom 時の mode を mesh から sfu に変更するだけで切り替わります。 peer . joinRoom ( roomName , { mode: "mesh か sfu を指定" , stream: mediaStream }); webrtc.ecl.ntt.com ジョブメドレーの WEB 面接では、面接参加人数を考慮して mesh を使用しています。 実装イメージ const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ルーム参加後に発生するイベント }); room . on ( "peerJoin" , ( peerId : string ) => { // ルームに誰か参加した場合に発生するイベント }); room . on ( "stream" , ( stream : RoomStream ) => { // stream を受けた場合に発生するイベント }); room . on ( "data" , ({ src , data }) => { // data を受けた場合に発生するイベント }); }); peer . on ( "error" , ( error : Error ) => { // エラー発生時に発生するイベント }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer を作成し peer.joinRoom() でルームに参加 room.stream イベントで他の参加者の stream を受け取る room.data() でチャットなど、データ送信もできる room.close() でルームから退出 が SkyWay の JavaScript SDK を使用した基本的な実装になります。 この実装に navigator.mediaDevices.getUserMedia() で取得した stream を joinRoom で渡す steam イベントで受け取った stream を video で再生する を追加すれば、オンラインでのビデオ通話が可能になります。 スマートフォン対応 PC と同様のコードでほぼ動作しましたが、iOS や Android 端末で、機種依存と思われる挙動の調査と対応に時間がかかりました。その一部を紹介します。 タブを移動すると映像が映らない 発生した問題 iOS12、iOS13 などで 複数のタブをひらいた場合に、映像の取得ができなくなる スクリーンロックからの復帰時に、映像が取得できなくなる という問題が起きました。iOS14 ではタブ切り替え時に映像を取得できるようになっていましたが、スクリーンロックからの復帰時は映像を取得できないままでした。 対応 この対応は visibilitychange イベントで、タブの切り替えと、スクリーンロックからの復帰時のイベントを拾い 取得済の stream の track を stop する stream を取り直す SkyWay の room.replaceStream() で、WebRTC で使用している stream を差し替える 以上の実装により、対応しました。 __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); イヤホンの操作で映像が止まる 発生した問題 iOS12、iOS13 では起こりませんでしたが、iOS14 でオンライン面接途中にイヤホンをスマートフォンから外すと、相手側の映像が止まるようになりました。 対応 それまでは、相手の映像と音声を再生するために、video タグに stream を渡して映像と音声を再生していましたが video : mute にして映像を再生 audio : 音声を再生 と、音声と再生を分けたところ、イヤホンを外しても停止することはなくなりました。 制約設定 getUserMedia で取得する MediaStream は、制約を設定することでデバイスの消費リソースを抑えることができます。 設定例: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); ↓ navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); 制約が設定可能かどうかの確認: getSupportedConstraints() で、対応している制約名を取得することができます。 const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 使用しているブラウザがその制約に対応しているかを確認し、対応している場合のみ設定を有効にします。 例えば、スマートフォンの場合に以下の設定をすれば、インカメラを使用し、フレームレートを 20 に抑え、320x320 の解像度に制限することができます。 const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; 指定した制限が必ず使用される保証はなく、機種依存の影響を受ける設定でもあるので、対象としている環境にあわせて検証と調整をする必要がある点には注意が必要です。 動作検証中に、機種依存と思われる挙動をした例を紹介します。 オプション 挙動 frameRate 指定すると一部 Android 端末で以下の挙動をした。 1. Android 端末で面接に参加する 2. iOS Safari で参加する 3. Android 側の映像と音声が Safari に送られない Safari で先に参加する場合には問題がない。 解像度指定 例: width: 320, height: 320 frameRate だけ指定していたときに上記の挙動をした Android、iOS Safari の組み合わせで問題が起こらなくなった。 解像度を指定するとインカメラではなく、リアカメラを使う Android 端末があった。 制約設定については、現在も調整中です。 参考情報 SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay のルーム機能を使用したデモ環境です。 GitHub にコードも公開されているので、ルーム を使用した場合の挙動と実装方法を確認できます。 開発をしていると、実装の問題なのか、SkyWay の SDK の仕様なのか、特定のデバイスで起こる問題なのかの切り分けに時間がかかるため、こちらの環境が参考になりました。 まとめ SkyWay が WebRTC とグループでのビデオ通話の実装を統合して提供してくれるため、開発時は、自社サービスとしての WEB 面接の機能に集中することができました。 スマートフォンのブラウザ対応と調査に時間がかかることもありますが、今後も利用者からのフィードバックを得ながら改善していきたいと思います。 メドレーでは、ニーズにあわせた新機能の開発にも力を入れています。多くの利用者に実際に使われるサービスの開発をしてみたいと思った方、ぜひお気軽にお話しましょう! www.medley.jp
アバター
株式会社メドレーのエンジニアの笹塚です。 私が開発を担当しているジョブメドレーで、先月 10 月 23 日に WEB 面接・動画選考をリリースしました。 job-medley.com WEB 面接、動画選考ともに、昨今の非対面での就職活動ニーズに応えるべく開発しました。 リリースは 2 つの機能を同時ですが、今回は WEB 面接の裏側に絞ってご紹介します。 WEB 面接概要 WEB 面接とは、リアルタイムで事業者様と求職者様が、オンライン面接を行うことができる機能です。 専用のアプリケーションは必要なく、PC、スマートフォンのブラウザから利用できます。 サービス選定 開発にあたり、いくつかの候補があがりましたが、最終的には自社内でも導入実績のある SkyWay を使用することにしました。 SkyWay とは WebRTC(Web Real Time Communication)を使用したオンラインのビデオ通話を、サービスに導入できるマルチプラットフォーム SDK です。 2020 年 11 月時点で、JavaScript SDK、iOS SDK、Android SDK が提供されています。 シグナリングサーバなどの WebRTC に必要となるインフラ構築が不要です 使用上限つきの無料プランもあります NTT コミュニケーションズが開発しています webrtc.ecl.ntt.com WEB 面接の対応ブラウザバージョン(2020 年 11 月時点) プラットフォーム 対応バージョン PC: Google Chrome バージョン 84 以上 PC: Microsoft Edge バージョン 84 以上 iOS: Safari iOS12 以上 Android: Google Chrome バージョン 85 以上 Andoid9 以上 SkyWay  JavaScript SDK の動作確認ブラウザ、WebRTC の対応状況、利用者の利用傾向から対応ブラウザのバージョンを上記のように設定しました。 webrtc.ecl.ntt.com SkyWay の接続モデル SkyWay でビデオ通話を実装する場合、2 種類の接続モデルから選びます。 SkyWay 電話モデル 電話のように 1 対 1 でのビデオ通話を想定したモデルです。 Peer インスタンス(シグナリングサーバによって発行された一意の PeerID を持つ)同士で接続します。 接続するためには、相手の PeerID が必要になります。 SkyWay ルームモデル 同一ルーム内の全ての Peer でビデオ通話するモデルです。 ルーム名を使用して参加します。相手の PeerID を知る必要はありません。 ルーム名は API キー毎に独立しています。 ルームの接続タイプはフルメッシュか SFU の 2 種類から選べます。 参加者全員へのチャットなどのデータ送信もできます。 ジョブメドレーの WEB 面接では、 今後の機能拡張を想定して、こちらのルームモデルを採用しました。 ルームの通信タイプ ルームに複数人が参加している場合、それぞれの通信をどう行うかを、メッシュと SFU から選ぶことができます。 フルメッシュ 画像引用元: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 全員が相互に通信を行います。人数が増えると、人数分端末のエンコード負荷と通信量が増加します。 SFU SFU の場合、上りの接続は 1 本になるので、メッシュよりも端末のエンコード負荷や、通信量の軽減が期待できます。 画像引用元: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 通信方式の違いは SkyWay が隠蔽してくれるので、joinRoom 時の mode を mesh から sfu に変更するだけで切り替わります。 peer . joinRoom ( roomName , { mode: "mesh か sfu を指定" , stream: mediaStream }); webrtc.ecl.ntt.com ジョブメドレーの WEB 面接では、面接参加人数を考慮して mesh を使用しています。 実装イメージ const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ルーム参加後に発生するイベント }); room . on ( "peerJoin" , ( peerId : string ) => { // ルームに誰か参加した場合に発生するイベント }); room . on ( "stream" , ( stream : RoomStream ) => { // stream を受けた場合に発生するイベント }); room . on ( "data" , ({ src , data }) => { // data を受けた場合に発生するイベント }); }); peer . on ( "error" , ( error : Error ) => { // エラー発生時に発生するイベント }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer を作成し peer.joinRoom() でルームに参加 room.stream イベントで他の参加者の stream を受け取る room.data() でチャットなど、データ送信もできる room.close() でルームから退出 が SkyWay の JavaScript SDK を使用した基本的な実装になります。 この実装に navigator.mediaDevices.getUserMedia() で取得した stream を joinRoom で渡す steam イベントで受け取った stream を video で再生する を追加すれば、オンラインでのビデオ通話が可能になります。 スマートフォン対応 PC と同様のコードでほぼ動作しましたが、iOS や Android 端末で、機種依存と思われる挙動の調査と対応に時間がかかりました。その一部を紹介します。 タブを移動すると映像が映らない 発生した問題 iOS12、iOS13 などで 複数のタブをひらいた場合に、映像の取得ができなくなる スクリーンロックからの復帰時に、映像が取得できなくなる という問題が起きました。iOS14 ではタブ切り替え時に映像を取得できるようになっていましたが、スクリーンロックからの復帰時は映像を取得できないままでした。 対応 この対応は visibilitychange イベントで、タブの切り替えと、スクリーンロックからの復帰時のイベントを拾い 取得済の stream の track を stop する stream を取り直す SkyWay の room.replaceStream() で、WebRTC で使用している stream を差し替える 以上の実装により、対応しました。 __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); イヤホンの操作で映像が止まる 発生した問題 iOS12、iOS13 では起こりませんでしたが、iOS14 でオンライン面接途中にイヤホンをスマートフォンから外すと、相手側の映像が止まるようになりました。 対応 それまでは、相手の映像と音声を再生するために、video タグに stream を渡して映像と音声を再生していましたが video : mute にして映像を再生 audio : 音声を再生 と、音声と再生を分けたところ、イヤホンを外しても停止することはなくなりました。 制約設定 getUserMedia で取得する MediaStream は、制約を設定することでデバイスの消費リソースを抑えることができます。 設定例: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); ↓ navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); 制約が設定可能かどうかの確認: getSupportedConstraints() で、対応している制約名を取得することができます。 const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 使用しているブラウザがその制約に対応しているかを確認し、対応している場合のみ設定を有効にします。 例えば、スマートフォンの場合に以下の設定をすれば、インカメラを使用し、フレームレートを 20 に抑え、320x320 の解像度に制限することができます。 const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; 指定した制限が必ず使用される保証はなく、機種依存の影響を受ける設定でもあるので、対象としている環境にあわせて検証と調整をする必要がある点には注意が必要です。 動作検証中に、機種依存と思われる挙動をした例を紹介します。 オプション 挙動 frameRate 指定すると一部 Android 端末で以下の挙動をした。 1. Android 端末で面接に参加する 2. iOS Safari で参加する 3. Android 側の映像と音声が Safari に送られない Safari で先に参加する場合には問題がない。 解像度指定 例: width: 320, height: 320 frameRate だけ指定していたときに上記の挙動をした Android、iOS Safari の組み合わせで問題が起こらなくなった。 解像度を指定するとインカメラではなく、リアカメラを使う Android 端末があった。 制約設定については、現在も調整中です。 参考情報 SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay のルーム機能を使用したデモ環境です。 GitHub にコードも公開されているので、ルーム を使用した場合の挙動と実装方法を確認できます。 開発をしていると、実装の問題なのか、SkyWay の SDK の仕様なのか、特定のデバイスで起こる問題なのかの切り分けに時間がかかるため、こちらの環境が参考になりました。 まとめ SkyWay が WebRTC とグループでのビデオ通話の実装を統合して提供してくれるため、開発時は、自社サービスとしての WEB 面接の機能に集中することができました。 スマートフォンのブラウザ対応と調査に時間がかかることもありますが、今後も利用者からのフィードバックを得ながら改善していきたいと思います。 メドレーでは、ニーズにあわせた新機能の開発にも力を入れています。多くの利用者に実際に使われるサービスの開発をしてみたいと思った方、ぜひお気軽にお話しましょう! www.medley.jp
アバター