#えむけーろぐ

間違った事を書いていたらやさしく教えてください

26歳になった

f:id:mktakuyax:20220406202059j:plain

Twitterの風船

今日で26歳になった。

昔は記念日といえば自分の誕生日くらいだったけど、今は結婚記念日や家族の誕生日、ゆるふわPodcast○周年記念日など1年に記念日がたくさんある。いい話。

 

Podcastを毎週配信するようになってから、すっかりブログの投稿も減ってしまった。アウトプットしたい欲みたいなのがPodcastで発散されているからだろう。

とはいえこのブログ、実は2019年から毎月更新を達成しているようで、途絶えさせないために月末にブログネタを捻り出すという生活を続けている。

今月は誕生月なので、この誕生日です記事を出せばノルマクリアだ。

 

ブログを一番書いていたのは17歳とか18歳くらいの頃だったから、このはてなブログの記事編集画面を開いていると当時の気持ちを思い出してエモい。

というわけで、この1年間とてもお世話になりました。次の1年もどうぞよろしくお願いします。

Cloudflare経由でMP3ファイルを配信すると、一部のPodcastクライアントでバグる

3行で

  • Cloudflare経由でMP3ファイルを配信すると、一部のPodcastクライアントでバグることがある。
  • どうも、ID3タグの情報が欠損してしまっているのが原因なようだ。(未確定)
  • そもそもCloudflare CDNで音声ファイル配信は規約違反っぽいのでやめよう。

はじめに

友人たちと趣味でやっている「ゆるふわPodcast」では、WebサイトやRSSフィード、そして音声ファイルの配信をすべて自前でやっている。自作のRuby on RailsアプリケーションにWebサイトとフィードの配信をやらせて、フィード記載の音声配信用URLを踏むとキャッシュの効いたURLにリダイレクトされ、MP3ファイルがダウンロードされる、という具合だ。

もともと音声ファイルの配信だけはSoundCloudにやらせていた(フィードにSoundCloudの音声ファイルのURLを載せていた)のだが、コスト削減やロギング等の理由により自前での音声配信に切り替えた。*1

f:id:mktakuyax:20220309010324p:plain

Cloudflare経由での音声配信 概略図

Cloudflare経由配信での不具合

自前配信に切り替えた当初は、CDNにCloudflareを利用していた。

ところが、Cloudflare経由での音声配信に切り替えてから、下記のような事象がリスナーから報告されるようになった。

  • 2〜3分再生したと思ったら突然最初に戻ってしまう。(iOS Podcast App、Google Podcast、Podcast Addict)
  • シークバーが無くなってしまう。(Google Podcast)

上記に加え、下記の事象も自分の手元で確認した。(すべてiOS Podcast App)

  • そもそもエラーが出て再生できない。
  • 倍速再生が出来ない。
  • チャプター情報を閲覧出来ない。
f:id:mktakuyax:20220308233046p:plain
f:id:mktakuyax:20220308233042p:plain
左側:エラー、右側:本来はエピソードメモの下部にチャプターがあるはずなのに無い

厄介なのが、すべてのPodcastクライアントで起こるわけではないということだ。僕は普段Overcastを使っていたのだがそちらでは再現せず、この問題に気づくことが出来なかった。また、同じApple製Podcastクライアントでも、iOS版では起こるがmacOS版では起こっていないように見えた。

ゆるふわPodcastのリスナーが使っているPodcastクライアントアプリは、今回問題となっているもの(AppleCoreMedia(たぶんiOS / macOSのPodcast App), Podcast, PodcastAddict, GooglePodcasts)等で4割を占めている。つまり半数近くのリスナーが正しくエピソードを再生できていない可能性がある、ということになる。

正直言ってこれは大問題だ。

f:id:mktakuyax:20220308000407p:plain

2022年3月1日〜3月8日にエピソードをダウンロードしたPodcastクライアント内訳(User-Agentベース)

調査

ファイルのチェックサム比較

原因調査の第一歩として、手元の編集後のファイルと、CDN経由でダウンロードしたファイルの同一性をチェックすることにした。

手元にあったEP126のファイル(編集後のファイル)を「local.mp3」、CDN経由でダウンロードしたファイルを「from_server.mp3」とし、SHA-256ハッシュを比較した。

もちろん、両者のSHA-256ハッシュは一致している。

f:id:mktakuyax:20220308233620p:plain

手元のファイル・CDN経由でダウンロードしたファイルそれぞれのSHA-256チェックサム

ffprobeコマンドを使ってID3タグ(タイトルやチャプターなどのメタデータ)を確認してみても、特に相違はない。チェックサム同じなのだから当然だが。

f:id:mktakuyax:20220308235607p:plain

ffprobeコマンドで表示したMP3ファイルのメタデータ

Cast Feed Validator

次に、フィードがおかしい可能性を考え、何らかのvalidatorにかけてみることを考えた。

当初は一般的なRSSフィードのvalidatorにでもかけてみようと思ったが、Podcastのフィードに特化した「Cast Feed Validator」というものを見つけたのでそちらを利用してみることに。

www.castfeedvalidator.com

結果、2点ほど気になる結果が出てきた。

まず、Server Testsの結果。どうも、CloudflareはPodcastの配信において問題を引き起こす可能性があるらしい。

f:id:mktakuyax:20220308234538p:plain

次に、Media Testsの結果。実際にMP3ファイルをダウンロードして調べてくれるようだが、ID3タグを読み込めないというエラーが出ていた。

f:id:mktakuyax:20220308234645p:plain

Podcastの配信にCloudflareを使っていると問題が起こる可能性があり、実際にID3タグが読み込めないという問題が発生している、ということになる。

実際にCloudflare経由でダウンロードしても手元のファイルとまったく同じであることが確認出来ているのに、一体なぜ……。

検証

エッジキャッシュをOFFにする → 解決せず

音声ファイルの配信はRailsのActive Storageを利用して実現している。

https://yuru28.com/rails/active_storage/blobs/proxy/hogehoge-slug/podcast-epXXX.mp3

上記のようなURLにアクセスがあると、Railsが裏でS3にアクセスしてファイルをダウンロードし、Railsがユーザーにファイルを配信している。Cloudflare側では、このURLをエッジ側でキャッシュするようにルールを設定している。

このルール側でのキャッシュをOFFにして、毎回BYPASSさせる(= オリジンからファイルが配信される)ようにしてみた。

f:id:mktakuyax:20220309000808p:plain

エッジ側でのキャッシュをOFFにしてみる

結果、改善せず。つまり、エッジに保存されているキャッシュが腐っている(何らかの理由でID3タグが欠損してしまっている)などではなく、Cloudflareを経由することそのものが良くないようだ。

CloudFront経由で配信してみる → 解決!

Cast Feed Validatorの言うことを信じることにして、別のCDN経由での配信を試してみることにした。

Cloudflare以外でお手軽に使えるCDNといえば、Amazon CloudFrontだろう。Webサイト・フィードと音声ファイル配信を同じドメインでやっている都合上、Herokuの前段にいきなりCloudFrontを置くというのは出来なかったので、音声ファイルが保存されているS3をオリジンとしたCloudFrontディストリビューションを作成し、フィード記載のURL(yuru28.com)から別ドメイン(cdn-ls.yuru28.com)で提供しているCloudFrontディストリビューションのURLにリダイレクトするようにしてみた。

f:id:mktakuyax:20220309002812j:plain

CloudFront経由での音声配信 概略図

結果、問題が解決した。

問題が報告されていたPodcastクライアントで問題が起こらなくなったし、Cast Feed ValidatorでもID3タグ関連のエラーが出なくなった。

f:id:mktakuyax:20220309003409p:plain

CloudFront経由での音声配信にすると、ID3タグ関連のテストが通るようになった。

フィードの配信はCloudflare経由なので、依然Server TestsではCloudflareを使っている旨の警告が出ているが、音声配信そのものがCloudflareでなければ問題ないようだ。

f:id:mktakuyax:20220309003531p:plain

フィードをCloudflareで配信しているのでServer Testsで警告が出ているが、問題なし

結論

以上より、下記のような結論に至った。

  • Cloudflare経由でMP3ファイルを配信すると、一部PodcastアプリでID3タグが読み込めず、音声の再生に問題が発生する。
  • curlコマンド等で直接ダウンロードしても問題がないように見える。

無料で使えるので嬉しいCDNなのだが、今回のような問題を踏んでしまうため少なくとも音声配信はCloudFrontを使っていこうと思う。

そもそも

同じような問題を踏んだ人がいないかとググっている中で、そもそもCloudflareの利用規約では非HTMLコンテンツの提供に関して制限があることを知った。

2.8 Limitation on Serving Non-HTML Content

(前略)Use of the Services for serving video or a disproportionate percentage of pictures, audio files, or other non-HTML content is prohibited, unless purchased separately as part of a Paid Service or expressly allowed under our Supplemental Terms for a specific Service. (後略)

Self-Serve Subscription Agreement | Cloudflare

ここでいう「a specific Service」は、Cloudflare Workersなどのサービスを指すようで、少なくとも通常のキャッシュサーバとしてのCDNには含まれていない。

2022年現在、Webサイトを作れば多少の画像や動画、音声は登場すると思う。が、それらが主体のWebサイトに使ってはいけないようだ。もちろん、Podcastの配信システムの主体は音声ファイルだ。

ここまで、技術的な問題によってPodcastの配信にCloudflareを使わない方が良いという話をしてきたが、そもそも利用規約的にも良くないということが判明した。

ProやBusiness、Enterpriseなど有料プランなら別途可能になるかもしれないが、少なくともFreeプランではやめたほうが良さそうだ。

 

*1:詳細は先日の吉祥寺.pmで発表した「Podcast配信システムを自作したら捗った話」を参照。

独自ドメインでメールを受け取りたいだけなら、iCloud+のカスタムメールドメイン機能がいいかも

結論、独自ドメインでメールを受け取りたいだけならiCloud+のカスタムメールドメイン機能を使うと良い。

Apple MusicやiCloudストレージなど、ある程度Appleのサービスを使い倒している人ならだいたい入っていると思う。

support.apple.com

こんな感じでiCloudメールのエイリアスとして独自ドメインのメールアドレスを作ることが出来る。

f:id:mktakuyax:20220306151126p:plain

 

メールはメインのiCloudメールの受信ボックスに届く。iCloudメールなんて普段見ないよ、という人は普段使いのメールアドレスに転送することも出来る。

f:id:mktakuyax:20220306151241p:plain

 

ドメインを持っていることの証明として、webmasterやadminみたいな特定のメールアドレスにメールを送って記載されているリンクを踏んで……というやり方を要求される時があるが、そんなときはこれで十分なのではと思う。

G Suite無償版難民かつApple信者な方はぜひ。

『Web配信の技術』を読んだ

出版されてすぐ買ったものの、積読本になっていた『Web配信の技術』を読んだのでメモ。

この表紙を見て「キャッシュ周り、特にCDNの本かな」という期待をして購入した。もちろんCDNへの言及も多いものの、特に前半はCDN以前にWeb配信という領域でHTTPの仕様を理解しつつ当たり前のこと当たり前にやりましょう、という話が多く書いてあった印象。例えばgzip圧縮ちゃんとしましょう、とか不必要にデカいアセットを配信するのはやめましょう、とか。各種HTTPヘッダ(VaryとかAccept-Encodingとか、もちろんCache-Controlとか)の役割などについてもRFCを参照しながら解説されていた。no-cacheは「キャッシュをするな」という意味ではないですよー、とかそういうところも。リバースプロキシやCDNをうまく使う話は後半に寄っていた気がする。

本の中でも書かれていたが、「Web配信」というとどうしてもYouTubeやSpotifyなどのように大きなファイルの配信を必要とするWebサイトや、ソシャゲやメディアなどtoC寄りのサービスのほうを想像してしまいがち。またこの手のサイトは時間帯によってアクセス数の大小が激しかったり、バズると跳ねがちなので配信頑張らなきゃいけないのは自明。でも実際はそれだけではなくて、toBのWebサービスも立派なWeb配信である。今どき画像を使わないWebサービスなんてそうそう無いし、そもそもHTMLを吐き出すのだって裏側のDBや外部サービスにアクセスして何かしらの処理をして生成するんだからコストは掛かる。そのため、この本で書いてあるようなWeb配信をちゃんとやるプラクティスは役に立つ。

今どきはアクセスがそんなに多くないtoBのサービスでもEC2やS3をそのままインターネットに露出させずCloudFrontを挟んでいると思うが、それはなぜなのか。オリジンが太平洋を跨いだUSにあるHerokuでも、直アクセスするよりCDNを挟んだほうが日本からの遅延が少ないのだが、それはなぜなのか。本書を読めば、これらの疑問にそれなりの根拠を持って答えられるようになると思う。

あと僕みたいなRails中心でやってきたWebエンジニアは、HTTPのキャッシュまわりの意識が低い傾向にある気がしていて、静的コンテンツはとりあえずキャッシュさせるものの、動的コンテンツは一切キャッシュしない*1ケースが多いと思う。何も考えずにRailsでコードを書くと多分そうなる。本書では「動的コンテンツをキャッシュさせる意義と方法」や、「動的コンテンツ中心のWebサイトでCDNを通す意義」についても触れられていて、その辺をちゃんとやっていこうという気持ちにもなる。

最後の方の章では、CDNの役割が単なるキャッシュサーバではないということにも触れられていた。CDNの本質は世界中のネットワーク品質が良く、ユーザに近いところにエッジサーバがあることであり、キャッシュはCDNが得意ないくつかのことのひとつに過ぎない。キャリア的にtoBのサービスを作ることが多くCDN無くてもいいんじゃないと言われがちかもしれない自分にとって、この視点を獲得できたのは大きい。

 

積ん読だったこの本を再度手にとった動機はPodcastの音声配信まわりの改善*2だが、本業の仕事の方でも十分役立てられそうな内容が多くてよかった。

*1:Railsのコントローラーから返すレスポンスは基本 Cache-Control: max-age=0, private, must-revalidate なはず

*2:趣味でやっている「ゆるふわPodcast」の音声配信はすべて自作している。参考: Podcast配信システムを自作したら捗った話 - SpeakerDeck

吉祥寺.pm 28でPodcastの音声配信について発表してきた #kichijojipm

吉祥寺.pmというイベントでゆるふわPodcastの音声配信システムについて発表してきた。

kichijojipm.connpass.com

Podcastは、Web上にMP3ファイルとそのURLが記載されたRSSフィードを置いておけば成立する。それをちゃんとやろうとすると結構大変そうだなぁということで、2019年のPodcast開始時からストレージと音声配信はSoundCloudに任せていた(WebサイトとRSSフィードは自前で作っていた)のだが、4年目に突入するこのタイミングでアナリティクスの弱さをはじめとする自由度の低さと金銭・作業コストが気になり始めたので自作することにした。

配信システムが大方完成し公開できそうなことだしどこかで成果発表でもしたいな、というタイミングで、たまたま過去にもお世話になった*1吉祥寺.pmが開催予定で枠も空いていたので登壇申し込みをした、という感じ。

スライドは下記の通り。

speakerdeck.com

他の方々の発表も前回同様ガチ技術ネタからゆるいものまで様々で楽しかった。

それなりにプライベートの時間を使って組み上げたものだったので、その成果を発表できる場があるのはとても嬉しい。聞いていただいたみなさん、場を作ってくれた運営者様ありがとうございました。

*1:2020年6月のイベントでもLTさせて頂いた

一人でアドベントカレンダーをやるのは大変

2018年から毎年やっている一人アドベントカレンダーですが、去年・今年と2年連続で完走することが出来なかった。

去年も今年も、なんだかんだ忙しく記事を書こうとならなかったのが敗因だ。生活スタイルが変わったりして、インターネットより現実世界に比重を置くようになったということなのかもしれない。

あと、Podcastを毎週配信するようになったのも大きいかもしれない。日記としてのブログに書くような話は、Podcastでやってしまっているのだ。

そんなわけで、一人アドベントカレンダーは今年でやめにしようと思う。

おもしろがってくれたみなさんありがとうございました。

魚焼きグリル

※この記事は mktakuya Advent Calendar 2021 14日目の記事です。

 

今住んでいる賃貸マンションには魚焼きグリルが付いているのだが、入居からまだ一度も使っていない。洗うのがめちゃくちゃ大変そうだからである。

実際に魚焼きグリルを使ったことはないのだが、今の家に引っ越してくるとき母親にそう言われたのでビビってしまっているのが実情だ。

しかし、考えてみれば今は食洗機があるので使ってみても良いかもしれない。

このあたりの記事を読んで予習しておくか。

www.hokkaido-gas.co.jp