| @ブログ

人気記事を確認できるようにしたという記事を以前書いている。もう 7 年も前のことのようだ。

調べればこういうログ集約・集計系のサービスはあるようだったが、個人ブログなのでなるべくお金はかけたくない。

しばらくの間は集計後のログを捨てていたが、 2 年半前からログローテートするタイミングで日付ごとのファイルを書き出し保存するようにしていた。日付ごとのログファイルは Amazon の S3 にアップロードしてさくらの VPS サーバーからは待避させている。現状、 2022 年の 6 月 13 日のログから S3 上にデータが残っているので、その日以後の日ごとの人気記事を確認できるようにした。こんな感じ。

日付の選択は最初 React のライブラリを使って作ろうかと思ったが、そういや最近のブラウザーは <input type="date" /> とすると結構ちゃんとしたカレンダー UI と日付選択の機能を提供してくれるので楽をすることにした。

max と min を指定すると選べない日付は非活性にされるし、無理に選択すると form を submit したときにバリデーションしてくれる。

選べない日付は非活性に
max と min を指定すると選べない日付は非活性にされる
バリデーション
選べない日付で無理に submit されたときにはバリデーションもしてくれる

ブログの閲覧者からしたらいつどの記事が読まれていたかなんて興味ないだろうけど、著者である自分には興味深い情報になる。セルフホストのブログだからできる機能だ。

| @ブログ

Rubbish

ブログで使ってる Amazon S3 のバケットの画像のほとんどをミスって空ファイルにしてしまった…。 S3 に上がっている画像、 Cache-Control ヘッダーが付与されていないのですべての画像ファイルに Cache-Control ヘッダーを付与しようとしての事故だった。ファイル一覧を取得して AWS SDK Ruby で Cache-Control だけ付与するつもりだったのにファイルそのものを空で上書きしてしまって無となった。

大した操作じゃないと思って事前にバックアップを取っていなかった& S3 に上げておけば安心だと思って日常のバックアップも行っていなかった。スーパーアホ。写真ならアップロードし直すことは可能だけど、キャプチャとか、アニメーション Gif とか、ブログの内容に合わせて OmniGraffle で描いた画像は基本的には元のファイルが残ってなくて元に戻すことができなかった…。

CDN に残っていたものとローカルでキャッシュされていたものを探したが、 500 ファイル以上が失われてしまった。つらい…。

画像の吹っ飛び、過去にも何回か起こっている。レンタルサーバーの障害で消えたパターンもあったけど、自分のミスで消してしまうパターンが多い。自分が一番信用ならないので画像はプロに管理してもらうのが一番だなと身にしみて思った…。とりあえず S3 バケットのバージョンコントロールを有効にしたいと思います…

追記

AWS SDK Ruby での正しい Cache-Control ヘッダー付与の仕方は以下だった。

object.copy_from(object, cache_control: 'max-age=2592000,s-maxage=31536000', metadata_directive: "REPLACE")

#copy_from の引数に medata_directive: "REPLACE" を渡す必要がある。

🙅🏻‍♀️🙅🏻‍♀️🙅🏻‍♀️以下は NG なので注意されたし ☠️☠️☠️

object.put(cache_control: 'max-age=2592000,s-maxage=31536000')

これやるとファイルの body が空になります 🈳🈳🈳

| @技術/プログラミング

関連記事に画像を表示するようにして喜んでいたが、先月の AWS の請求額を見てビックリ。普段の 15 倍くらいの金額になっていた。デイリーの利用料金を見ると関連記事に画像を表示するようになった日から高くなっている。

CloudFront 転送量

このブログの画像は S3 に置いてあって CloudFront から配信している。これまでたくさん写真を掲載しても特にコストは高くなかった( Route 53 の費用など含めても $3 くらい、転送量だけだと $1.5 くらいだった)のが、転送量だけで $30 オーバーになっていた。ブログのサーバー代は Adsense 広告と Amazon アフィリエイトでまかなうつもりでやっているので、これでは完全に赤字になってしまう。

なぜ高くなったのかというと関連記事にサムネイル画像を表示することで、 imageproxy から CloudFront へのアクセスが発生するようになったからのようだった。こんな感じ。

image-data-transfer-infrastructure-1.png

imageproxy にもキャッシュの仕組みはあるが、 CloudFront が返す Cache Control ヘッダーの内容を理解せず決め打ちの時間でキャッシュを Expire させるので効率が悪い。

恐らく以下のように画像関連のインフラは AWS に寄せるのが一番効率的だと思う。Amazon の優秀なエンジニアが作ってる CDN が一番前段に出てブラウザーからのリクエストに答えるのがもっとも効率的に画像を配信できると思う。

image-data-transfer-infrastructure-2.png

ただ個人のブログレベルでここまでやるのは割に合わない感じがしたのでとりあえずは以下のような構成にした。

image-data-transfer-infrastructure-3.png

Nginx の proxy cache を使う。

キャッシュ時間は長めにとって 30d にしておいた。

あわせてキャッシュの HIT 率を計測するようにした。ログに $upstream_cache_status を書き出すようにして、 awk で定期的に集計するようにした。こんな感じ。

cat log/access.log \
  | grep 'cache_hit:' | grep -v 'cache_hit:-' | cut -f16 | sort | uniq -c \
  | awk '{
      if ($2 ~ /HIT/) {
        hit = $1
      };
      if ($2 ~ /EXPIRED/) {
        expire = $1
      };
      if ($2 ~ /MISS/) {
        miss = $1
      };
      sum += $1
    } END {
      hit_rate = hit/sum*100;
      expired_rate = expire/sum*100;
      miss_rate = miss/sum*100;
      print "HIT\t"hit_rate"%\nEXPIRE\t"expired_rate"%\nMISS\t"miss_rate"%"
    }'

こいつを Lokka の Dashboard に表示させる。

キャッシュヒット率

加えて、 Google の以下の記事を参考に、画像の遅延読み込みを行うようにした。

とりあえずはこれで様子を見たい。いまのところ、ちょびっとずつ転送量は下がってきているような感じがする。もうちょい下げたいところ。

しかし、画像の配信で毎月 $30 もかかるようであれば自前で画像をホストするのは諦めて Flickr に金払って PRO プランを継続した方が安いなと思い始めてしまった…。 Google Photos でも良いが、 Exif がわからなくなるのと埋め込み用の画像を取得する作業(公開用のアルバムを作ってそこに埋め込みたい写真を入れていく必要がある)が面倒くさいので移行に踏み切れない。