#えむけーろぐ

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

GitHub ActionsのWorkflowをWeb UIから削除する

ちょっと試しに実行したりしたあと、ファイル削除したGitHub ActionsのWorkflowがいつまでもWeb上(リポジトリのActionsタブ)に残ってしまうのを消したい。

結論、そのワークフローに紐づくすべての実行履歴(Workflow run)を削除すれば良い。

削除したいWorkflowを開いて、すべてのworkflow runを削除する。たくさんあると大変そう。

すべてのworkflow runsの削除が完了すると、Workflows一覧に出てこなくなる。スッキリ。

 

ただの日記(免許更新、献血、映画、ゲーム)

ただの日記。Podcastで話そうと思うけど忘れそうなので。

 

2016年、高専5年生の夏に免許を取ってから6回目の誕生月。無事に無事故無違反でこれを迎えることが出来たので、優良運転者講習に行ってきた。

2019年の初回更新者講習のときは鮫洲まで行ったのだが、今はもう横浜市民なので二俣川へ。どこだよそれ。

www.police.pref.kanagawa.jp

鮫洲・調布などは置いておいて、こういう施設はだいたい変な場所にあるのが相場なもんだが、それでもなんでこんなところに……という感じだった。しかも、神奈川県全域をこの一箇所で受け持っているらしい。つまり、平日に地元の警察署に行けるとかそういう人でない限り、ほぼ東京な川崎に住んでいようが、ほぼ静岡な湯河原町に住んでいようが、みんな日曜の朝っぱらにこんなところまでこなきゃいけないというわけだ。

といろいろと文句を書いたけど、着いてから免許を受け取るまでの体験はなかなか良かった。役所も免許センターも選挙もワクチン接種も、有象無象の人間をいい感じに捌いていくオペレーションを考える人は本当に天才だと思うし、それを実際に回す現場の人たちもすごい。

前回の初回更新者講習は2時間で、事故の悲惨さを煽る系のビデオを見せられて気が滅入ってしまったものだったが、優良運転者講習は30分で15分のビデオを見た後に最近の道交法改正ダイジェストみたいな説明をされるだけで終わった。ビデオも若気の至りで人生大変なことに……系のものではなくむしろ「こっちがルールを守っていても路上はワンダーランドなので最悪相手がルール違反してきても対応できるような運転をしよう!」という話だった。

 

免許の更新が終わって少し時間があるなあと思っていたら、免許センターの目の前に献血ルームがあったので行ってみることに。免許の更新はだいたい半日〜1日潰す覚悟で来る人が多いと思うので、その目の前にセンターを献血ルームを作るというのは賢い戦略な気がする。

実は献血は初体験だったりする。高専にも献血バスが出張しに来ていたのだが、わりとひ弱なので血を抜かれるとすぐ体調悪くしてしまうかなぁという先入観 + 貧血疑惑(数年の時を経て貧血ではなく偏頭痛と判明)によりパスしていたのだった。

ちょっとくらい具合悪くなるかなと思ったけど、蓋を開けてみたら何の問題もなく。血も薄いのではなんて思ってたけど事前検査によると十分濃いらしい。健康に感謝。

タダジュース・タダお菓子やタダハーゲンダッツを頂いて、退散。どうやら献血ルームごとに色々と体験に差があるらしいので、また今度違うところに行ってみよう。

 

あとはまぁ先日公開されたファンタスティック・ビーストとダンブルドアの秘密を観に行ったり、夜に友人たちとゲームしたり、いろいろ。

ファンタスティック・ビーストはなんとなく3部作くらいで終わるのかなと思っていたが、ハリー・ポッター本編のように7〜8作くらいやるのかね?

 

休日ってだいたいひとつかふたつのことしかしないんだけど、今日は1日に4つも活動をしたので満足度が高く、わざわざ日記を書くに至ったのであった。

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させて頂いた