ECサイトにおけるAmazon S3を使った静的コンテンツの配信(前編)
皆さん、こんにちは。最近、花粉症の飲み薬を飲み始めた、技術部の磯崎です。
現在、弊社でも情報発信に力を入れるべく、幾つか取り組みを始めています。その中で、今回私は技術部のトップバッターとしてブログを書くことになりました。そこで、あるECサイトの開発・運用において、静的コンテンツの配信に関連して発生したトラブルの事例を(ちょっと長くなりすぎるので)前編/後編に分けてご紹介したいと思います。もし今後同じようなシステム構成を検討される方がいらっしゃいましたら、つまずくことがないように参考になれば幸いです。
まずは前編として、そのECサイトのシステムの概要をご説明します。次回の後編では、実際に発生したトラブルについてご紹介します。
システムの概要
では早速、このECサイトのシステム構成についてご紹介します。俗に「LAMP」と呼ばれる、よくあるWEB-DBシステムです。
- ・ Linux (CentOS 6)
- ・ Apache HTTP Server 2.2
- ・ MySQL Server 5.5
- ・ PHP 5.4
このECサイトはAmazon Web Services(AWS)の東京リージョンに構築しています。
幾つか特徴を挙げてみます。
- ・パソコンやスマートフォンからのアクセスは、ロードバランサー(Elastic Load Balancing)がWebサーバーに分散
- ・Webサーバーは、Amazon EC2のインスタンスを複数台で運用(2?4台
- ・MySQLサーバーは、Amazon RDSのマルチAZ配置(Multi-AZ Deployment)で冗長化
- ・店舗運営側の各種検索やデータ出力処理は、同じくRDSに読込専用データベース(Read Replica)を設けて負荷を分離
- ・キャンペーン等の静的コンテンツや商品画像などの大量の画像ファイルはAmazon S3から配信
- ・ アプリケーションはPHP 5.4とCakePHP 2で開発
少し話が逸れますが、プロジェクトの運用面にも触れておきますと、日々このようなかたちで業務を進めています。
- ・ プロジェクトの課題やタスクはRedmineを使ってお客さまやメンバーと共有
- ・ソフトウェアのバージョン管理はGitHubで(一部Subversionもあり)
- ・Jenkinsによる自動ビルドとその結果はSkypeでメンバーへ通知
- ・サーバーの構成はChef(chef-soloとknife-solo)で管理
- ・アプリケーションのデプロイはCapistranoで実施
なお、弊社内でもプロジェクト(チーム)が異なると、進め方も少し異なっているのが現状です。
静的コンテンツの配信
先ほど述べたように、キャンペーン用の静的コンテンツや商品画像などの大量の画像ファイルは、WebサーバーであるEC2インスタンスからは配信せず、S3から配信しています。(一部を除く) WebサーバーであるEC2インスタンスはサイト訪問者などのアクセス負荷に応じて増減することを想定しており、Webサーバーの台数に関係なく静的コンテンツを配信できるようにするために、このような構成になりました。 なお、これはAWSクラウドデザインパターンで紹介されている「Web Storageパターンに当たります。
具体的には、次の仕組みで実現しています。
- 店舗運営専用のLinuxサーバー上でlsyncd(常駐プログラム)が静的コンテンツ(ファイル)の変更状況を監視
- 店舗運営担当者はWebブラウザーやWinSCPなどのソフトウェアを使って静的コンテンツをアップロード
- 同Linuxサーバー上で静的コンテンツが追加・変更されると、lsyncdがそれを検知し、自動的にs3cmdを実行
- s3cmdは指定された静的コンテンツをS3にアップロード
- Webサーバー上のCakePHPアプリケーションは、商品画像などのURLを自動的にS3のURLに変更
- ECサイト訪問者のWebブラウザーは、商品画像などはS3からダウンロードして表示
当初はs3fsも候補に挙がったのですが、開発スケジュール等の制約もあり、lsyncdとs3cmdの組み合わせを採用しました。
Amazon S3
ここでご説明するまでもなく、ストレージサービスとしてご存じの方も多いと思います。特に、イレブンナイン(99.999999999%)といわれる堅牢性がポイントとして注目されたりします。しかし、このECサイトにおいては、S3に配置する静的コンテンツはLinuxサーバー上のファイルのコピーであるため、その重要性はあまり高くなく、安定して配信できるかがより重要になります。簡単に言いますと、S3を「ディスクの容量制限がないWebサーバー」として捉えています。
ここでS3の特徴を幾つか挙げてみます。
- ・1個のファイルの最大サイズは5TB * 保存できるファイルの個数は無制限
- ・可用性は99.99%
- ・堅牢性は99.999999999%(低冗長化ストレージオプションを使うと99.99%に下がりますが、料金は安くなります)
- ・ピーク時最大85万リクエスト/秒(これだけ聞いても規模がよくわかりませんが…)
正確には、S3では「ファイル」とは呼ばず、「オブジェクト」と呼びます。
参考)
- ・Amazon S3 (クラウドストレージサービス Amazon Simple Storage Service) | アマゾン ウェブ サービス(AWS 日本語)
- ・S3 -ほぼ週刊AWSマイスターシリーズ第2回-
- ・Amazon S3による静的Webサイトホスティング
現在までのS3からの静的コンテンツの配信において、特に不具合は生じていません。
s3cmd
s3cmdをコマンドとしてを実行することで、任意のファイルをS3にアップロードしたり、その逆としてS3からダウンロードが行えます。また、s3cmdのsyncという内部コマンドを使うと、Linuxサーバー上のファイルとS3上のオブジェクトの同期を取ることができます。
参考)
設定ファイルの作成
S3にアクセスするために、アクセスキーIDとシークレットアクセスキーが必要です。次のコマンドを実行することで、対話的に設定ファイル「.s3cfg」を作成することができます。
s3cmd --configure
ローカルファイルのアップロード
ローカルファイルをS3にアップロードするときはこのようなコマンドを実行します。
s3cmd -P put /path/to/file s3://bucket/path/to/folder/ s3cmd -P -r put /path/to/directory s3://bucket/path/to/folder/
- ・「-P」オプションは、一般公開状態としてINTERNETからのアクセス許可を意味します。
- ・「-r」オプションは、ディレクトリを再帰的にアップロードするときに指定します。
S3上のオブジェクトまたはフォルダーの削除
指定したオブジェクトまたはフォルダーをS3上から削除します。
s3cmd del s3://bucket/path/to/folder/object s3cmd -r del s3://bucket/path/to/folder/
- ・「-r」オプションは、S3上のフォルダーを削除するときに指定します。
ローカルディレクトリを基準とした同期
ローカルディレクトリを基準として、S3に対してアップロードと削除を行います。その逆も可能です。後編で触れるlsyncdがダウンしたときなどの障害復旧時に手動で同期することもあります。
s3cmd -P --delete-removed sync /path/to/directory s3://bucket/path/to/folder/
- ・「-P」オプションは、一般公開状態としてINTERNETからのアクセス許可を意味します。<\/li>
- ・「–delete-removed」オプションは、ローカルに存在しないオブジェクトやフォルダーを削除するときに指定します。
なお、ローカルディレクトリのパスの末尾に「/」を付けるか否かによって同期対象がそのディレクトリ内か、またはそのディレクトリそのものであるかが変わるため、注意が必要です。
ローカルディレクトリのパスの末尾に「/」を付けたとき
s3cmd -P --delete-removed sync /path/to/directory s3://bucket/path/to/folder/
ローカルファイル | S3のオブジェクト |
— | — |
/path/to/directory/file | s3://bucket/path/to/folder/directory/file |
ローカルディレクトリのパスの末尾に「/」を付けなかったとき
s3cmd -P --delete-removed sync /path/to/directory/ s3://bucket/path/to/folder/
ローカルファイル | S3のオブジェクト |
— | — |
/path/to/directory/file | s3://bucket/path/to/folder/file |
低冗長化ストレージオプションの有効化
このECサイトではS3の低冗長化ストレージ(Reduced Redundancy Storage/RRS)オプションを有効にしています。前述のとおり、同オプションを有効にすることで堅牢性は下がりますが、料金が安くなります。設定ファイルにおいて、次の項目の値を変更しています。
reduced_redundancy = True
lsyncd
lsyncdはデーモンとして常駐するプログラムです。Linuxカーネルに組み込まれているinotifyと呼ばれる機能を利用します。lsyncdは、設定ファイルに記述したディレクトリ内で発生するファイルシステムの変更を検知し、任意のコマンドを実行できます。
Linuxカーネルの機能を利用するため、inotifyに関連する設定変更は /etc/sysctl.conf や /proc ディレクトリ内の該当ファイルで行います。
Linuxカーネルのパラメーター | /procのパス | デフォルト値 | 実際の設定値 | 説明 |
— | — | — | — | — |
fs.inotify.max_queued_events |
/proc/sys/fs/inotify/ max_queued_events |
16384 | 32768 | キューに入れられるイベント数の上限 |
fs.inotify.max_user_instances |
/proc/sys/fs/inotify/ max_user_instances |
128 | 128 | 1ユーザーが生成できるinotifyインスタンスの上限 |
fs.inotify.max_user_watches |
/proc/sys/fs/inotify/ max_user_watches |
8192 | 2000000 | 1ユーザーが監視できる上限 |
settings { logfile = "/var/log/lsyncd/lsyncd.log", statusFile = "/var/log/lsyncd/lsyncd.stat", statusIntervall = 1, }<br /><br />s3sync = { maxProcesses = 10, onCreate = "s3cmd -c /root/.s3cfg -P -r put ^sourcePathname ^targetPath", onModify = "s3cmd -c /root/.s3cfg -P -r put ^sourcePathname ^targetPath", onDelete = "s3cmd -c /root/.s3cfg -r del ^targetPath", onMove = "s3cmd -c /root/.s3cfg -r del ^o.targetPath; s3cmd -c /root/.s3cfg -P -r put ^d.sourcePathname ^d.targetPath", }<br /><br />sync { s3sync, source = "/path/to/application/shared/css", target = "s3://bucket/css", } sync { s3sync, source = "/path/to/application/shared/img", target = "s3://bucket/img", } sync { s3sync, source = "/path/to/application/shared/js", target = "s3://bucket/js", }
補足)
- ・監視対象ファイルが大量にあるときは監視が始まるまでに時間が掛かってしまうため、onStartupイベントは設定していません。
- ・監視対象ディレクトリ内でファイルを移動したときに不具合が発生したため、onMoveイベント発生時に実行するコマンドが変則的な指定になっています。
参考)
CakePHPアプリケーションでの対応
CakePHPでは、Viewをサポートする役目としてヘルパーと呼ばれる機能(関数群)があります。 動的にHTMLを出力する際はHtmlHelperと呼ばれるヘルパーを使うのですが、このヘルパーをベースに少し手を加え、imgタグの画像のURLを自動的にS3のURLに置き換えるようにしています。 なお、コーディングされたHTMLの中でimgタグからヘルパーへの置き換えがなされていない箇所は、スケジュールの都合もあり、S3にアクセスを振り分けていません。
ちょっと長くなりましたが、前編はここまでです。 続きは次回に。またすぐにお会いしましょう!