この記事は CyberAgent Developers Advent Calendar 2016 の 17 日目の記事になります。16日目は No_oLimits さんの MySQL5.7でMHAとmysqlfailover試した でした。


みなさん、はじめまして。16年新卒入社の桐井です。
技術本部というところで、 Ameba の基盤システムを中心にインフラ業務に関わっています。
腰がいたいです。渋谷近辺でオススメの整体があれば教えてください。

今回は、私の配属後のミッションの 1 つである、 Ameba を支える画像配信システムの stat100 の構成刷新について書いてみたいと思います。長いです。
よろしくお願いします。

Ameba を支える画像配信システム “stat100”

アメブロやガールフレンド(仮) など、Ameba が提供するサービスやゲームで使われる画像配信システムのうち、静的ファイル配信に特化したものが stat100 です。

元々は、各サービスが持つ Web サーバから、アクセス頻度の高い静的コンテンツを分離し、負荷軽減やレスポンス向上を図る目的で構築されたものだと聞いています。
現在では様々なサービスが相乗りする社内 CDN 的なシステムとして使われており、サービス側で用意した素材画像などをそのままの形式で配信する用途で使用されています。画像変換などは行わない単純なコンテンツ配信システムです。

(なんで名前が 100 なのかには一応理由があるようなのですが、書き始めると長くなってしまいそうなので今回は割愛いたします)

例えば、https://ameba.jp/ のロゴ画像。これも stat100 で配信されていますね。

post_3572_00001

タイトルでは画像配信と書きましたが、 画像以外にも css や js 、アニメーション gif や mp4 などの比較的サイズの大きめな動画コンテンツも配信しています。静的コンテンツならなんでもありです、はい 😆

stat100 の旧構成と様々な問題点

新卒研修を終えて、基盤インフラを担当する今の部署に配属されたのが 2016 年の 6 月。その当時、私が stat100 を引き継いだときのサーバ構成はこんな感じでした👇

post_3572_00002(Web サーバに関しては、連番のホスト名に幾つか歯抜けがあったので、実際はもう少し台数が少なかったかと思います)

コンテンツ受付用サーバで受け取ったファイルを、横に並べた Web サーバに展開し、LB でロードバランスするという構成です。受付用サーバにファイルを配置すると、それを自動で検知し、中継サーバ → Web サーバという順に自動的に rsync が行われる仕組みです。

ファイルを配置して自動的に rsync が行われる仕組みには Lsyncd を使っています。
Lsyncd は Linux の inotify API を使い、ファイルの作成/変更/削除などのファイルシステムイベントを起因に rsync を自動実行するためのデーモンです。
コンテンツ受付用サーバと rsync 中継用サーバ上で Lsyncd を動作させ、一箇所にファイルを配置すると自動的に複数の Web サーバに展開される多段 rsync の構成が組まれていました。

この stat100 旧環境ですが、様々な問題を抱えておりました。
頻繁に発生していたのが、コンテンツをアップロードしてもすぐに参照できないという反映遅延問題
サービス側からコンテンツを受け取ってから、Web サーバにたどり着くまで 2 度 rsync を行う多段構成のため、コンテンツをアップロードしてから参照できるようになるまでに大幅な遅延が発生することがありました。

コンテンツ受付サーバ上では、5 分おきにヘルスチェック用ファイルを更新する cron を動かし、Web サーバ側の監視項目として、ヘルスチェック用ファイルの更新日時が 20 分以内に収まっているかをチェックしていました。大規模なサービスでは、数万ファイルが一度にアップロードされるケースもあり、コンテンツアップロードのジョブが動く時刻になると、監視サーバから全 Web サーバ分の同期遅延発生アラートが送られ携帯が5分ぐらいブーブー鳴ってる日々が続いておりました。

他にも、

  • Chef 等で構成管理されていない
    • lsyncd.conf.bk, lsyncd.conf.bk151217……
    • 出、出〜wwwww 手動管理故1台設定他相違奴〜wwwwwwww
  • ディスクの空き容量が 5GB のWeb サーバがいる
    • コレは単純にサーバに搭載のディスクが小さかった
    • 使用率 100% に達しサービスアウトを余儀なくされたサーバ達…… おつかれさまでした 🙏
  • コンテンツ受付サーバが SPOF
    • 死んだらサービス側からファイルアップロードができず新規投稿が出来ない
    • データバックアップ用のサブ機は一応いた

もはや我らの stat100 は風前の灯火、いつ完全崩壊してもおかしくない状況でした😇

構成を見直し再構築へ……

上記の問題に対処するため構成を見直し、以下のような構成で再構築を行いました👇

post_3572_00003新環境では、次の様にサーバの役割を割り当てています。

  • data node (dn)
    • サービス側からデータを受け取って置いておくための node
  • replica node (rp)
    • data node のデータをそっくりそのまま貰うので replica
    • nginx を立ち上げキャッシュサーバのバックエンドとして動く
  • cache
    • Varnish Cache によるキャッシュサーバ

以下で、今回の構成刷新で行った変更の重要なポイントについて紹介したいと思います。

参照面の Web サーバをキャッシュサーバの層に置き換えた

旧 stat100 の Web サーバのリソースの使用状況を見ると、ディスク I/O はほとんど無く、メモリも cache 領域での使用が多い状態でした。こうした状況から、Web サーバを丸ごとキャッシュサーバに置き換えることが出来るのではないかという案が生まれました。

キャッシュサーバ化することにより、外部からリクエストが来たタイミングで、バックエンドの Web サーバに取得しに行くだけで良いため、多段 rsync によって発生していた同期遅延の軽減が期待できます。

キャッシュサーバには、他の画像配信コンポーネントでも利用実績のある Varnish Cache を採用しています。

Lsyncd を使って Varnish にキャッシュパージリクエストを送る

キャッシュサーバ導入で問題になるのが、配信するファイルに何らかの変更が行われた場合です。ファイルに変更が加えられたタイミングでキャッシュパージをしないと、キャッシュ TTL の時間だけ変更が反映されないように見えてしまいます。また、ファイルを削除したり移動した場合も同様に、 TTL の時間だけ同じ URL で参照し続けることができてしまいます。

そのため、コンテンツ受付サーバ上でファイル追加/変更が行われた場合に、キャッシュサーバに対してキャッシュパージのリクエストを送る必要がありました。

Lsyncd の設定は Lua 言語によって記述されており、文字列操作やループなどのスクリプトを記述し、それをファイル操作の変更を検知したタイミングで実行することが可能です。この仕組みを利用し、Lsyncd 組み込みの Lua 言語を利用して Varnish へキャッシュパージのリクエストを送るよう設定を記述しています。

Lsyncd の設定

data node で動かしている Lsyncd の設定ファイルと、ハマったポイントについて解説いたします。/etc/lsyncd.conf のうち、キャッシュサーバで動作する Varnish へリクエストを送っている箇所を抜粋し、簡単にコメントを付加しました。data node から replica node へ rsync するための設定は、特別変わったコードは書いておりませんので、今回は割愛いたします。

ハマりポイント1: ディレクトリを mv した際に配下のファイルを個別にパージできない

ファイルや子ディレクトリを抱えたディレクトリを、移動したり名称変更した場合 (つまり mv した場合) には、そのディレクトリ配下のファイルに対してもキャッシュパージを行う必要があります。
(古いディレクトリ名の URL でキャッシュを参照できてしまうため)

Lsyncd の event.path フィールドで取得できる情報では、 mv されたディレクトリの path しかわからないため、ディレクトリ配下のファイルを個別に指定して PURGE リクエストを送ることはできませんでした。(ファイルシステムとしては、ディレクトリエントリの持つ名前だけ書き換えれば良いので、この挙動自体にはおかしな点は無いのですが…)

今回は Varnish の Ban 機能 を利用したワイルドカードパージでこの問題に対処しました。Lsyncd では、ディレクトリを mv すると、古い path で Delete イベントが発行され、新しい path で Create イベントが発行されます。event.type が Directory かつ Delete イベントの場合は、BAN の対象をワイルドカードで指定することで、対象ディレクトリ配下のキャッシュオブジェクトも含めたパージリクエストが行われるようにしています。

ハマりポイント2: 隠しファイルでも PURGE リクエストを送ってしまう

サービス側サーバから rsync でデータを受け取る際に、rsync の一時ファイルが作成され、それも変更として Lsyncd が検出してしまう問題がありました。

この問題に対しては、変更のあったファイルの path を調べ "/." が含まれている場合には sendPurge function を呼ばないようにすることで対処しました。

(rsync を行う場合は、この様に 1 から書く必要はなく exclude オプション の指定で同期対象から除外することが可能です)

数々の問題は改善されたのか?

様々な問題があった stat100 ですが、構成刷新によってそれらは改善されました!

多段 rsync の廃止によって同期遅延が軽減され、アラートが来ることもなくなりました。キャッシュサーバ 1 台の 2016 年 12 月分のメトリクスを確認してみると、なんとキャッシュヒット率 98% を超えを維持しております。

post_3572_00004

全体のサーバ台数も 45 台から 17 台に減り、メンテナンスのコストも下げることができたかと思います。現在では Chef による構成管理が導入され、全サーバで同じ変更が適用されるように改善されています。

まとめ

画像配信システム stat100 の構成刷新と、それを支える Lsyncd について紹介いたしました。Lsyncd をファイル同期以外の目的で使う場面はあまり無いと思いますが、何かの参考になれば幸いです。

また、 6 月の配属後から新卒メンターとして数々の相談に乗っていただいた @kakerukaeru さんに、この場を借りて感謝を述べさせていただきます。

Advent Calendar はまだ続きます!明日も是非ご覧くださいー!!
CyberAgent Developers Advent Calendar 2016

Kirii Yuki
2016年新卒入社のエンジニアです。Ameba の画像配信システムや基盤システムの運用、インフラ業務の自動化・効率化を担当しています。