WINGS ブログ
ITやWEBに関するブログを書いています。

低コストでネット繁盛店をつくる〜EC-CUBE編その3〜

投稿者:munenori-iwai | 2012年/07月/16日

こんにちは、いわいです。

先日のファーストサーバのデータ喪失のニュースを人ごとならぬ思いで見ておりました。日本経済新聞のHPには『クラウドに預けていたデータが、「雲」が消えるかのごとく消失してしまった。』などと揶揄した記事が掲載されていましたが、もし本当にEC-CUBEでショップを運営する我々のクライアントに同じような障害が発生した時、どのようなリカバリが行えるのでしょう。

明暗を分けたのはバックアップデータ

今回の一件で比較的早急に復旧を終えた企業と完全にデータをロストしてしまった企業の明暗を分けたのが、バックアップデータの有無でした。ネットショップの場合、バックアップすべき資源は大きく2種類に分けることができます。1つはファイルに記載された情報である“画像”や“プログラム”で、もう1つはデータベースに格納される商品や注文などの“データ”です。このうち、画像やプログラムは、比較的変更頻度が低いことと、編集者が意図して変更しない限り、勝手に内容が変わってしまうことなどないので、作業時に修正分のファイルをバックアップする運用にしておけば、必ずオリジナルが手元に残すことができます。しかし日々刻々と変化するデータベース上の商品や注文のデータは、サーバーからこまめにバックアップを取っていない限り、手元に残ってないことが多いのではないでしょうか。

たしかにEC-CUBEには、管理画面からデータベースのデータをバックアップする機能はあります。しかし担当者が画面操作する必要があるので、“こまめ”な手間がかかります。何とかこれを自動化する方法はないものでしょうか。

バッチジョブで自動バックアップ

コンピュータシステムには、オンラインプログラムとバッチプログラムという概念(詳しくはWikipedia)が存在します。前者はユーザーインターフェイス(つまり画面)が存在し、人がマウスやキーボードで操作を行うプログラムを指します。それに対しバッチプログラムというのは、文字を入力するためのインターフェイスを持たないプログラムで、通常はジョブスケジューラやジョブ管理プログラムから起動されます。こういったバッチプログラムが行う処理をバッチジョブ、複数のバッチジョブの構成するバッチジョブのかたまりをジョブネット言い、ITコンサルタント時代は、こういったシステムの構築を専門としていました。わき道に逸れたました。データベースのバックアップです。要はこのバッチジョブの仕組みを利用して、データベースのデータバックアップを自動化し、万一の自体に備えましょう。

というのが今回のテーマです。

データベースのオンラインバックアップは簡単じゃない

実際の店舗と違って、オンラインショップには閉店時間がありません。なので、今この瞬間にもお客さんは商品を見ているかも知れないですし、購入しようとしているかも知れません。データベースのバックアップを取ろうとした時、その一瞬にお客さんが購入ボタンをポチッと押した場合、バックアップデータにその注文情報は含まれるか含まれないか、どちらでしょう?この話題は奥が深いので、今回はこの辺で止めておきますが、オンラインショップのような365日×24時間ずっと休まないシステムの場合、バックアップの静止点(完全に止まっている瞬間)が問題になります。しかしここでは費用対効果の観点から、偶発するかも知れないデータ不整合はあまり気にせず、あくまでサーバー障害によるロストデータを回避することに主眼をおきます。そのため今回の方法では、完全な静止点を取ることができなく、万に一つバックアップには、整合しないデータが含まれる可能性がある点を理解ください。

Dropbox APIを使って、クラウドコンピューティング

せっかくデータバックアップしても、同じサーバー上にある限り、障害発生時にバックアップもろともロストしてしまう危険がなくなった訳ではありません。なので、バックアップデータをどこか別のロケーションに転送する仕組みが必要になります。もしこれが実現すれば、ロストデータの可能性は限りなく0%に近づきます。転送先のサーバーは色々選択肢があると思いますが、今回はDropboxというクラウドのサービスを使ってみることにします。理由は、ポピュラーですし、API(Application Program Interface)が公開されていて、プログラムからバックアップデータを送信するのに都合がいいからです。

今回のレシピです。

EC-CUBEはPHPで記述されていますので、今回のプログラム言語は同じPHPを使用します。データベースのバックアップには、MySqlのバックアップコマンドmysqldumpを使用します。DropboxのAPIにはPHPでの実装Dropbox for PHPを使用します。オープンソースを利用して、品質・機能をそのままにコストダウンをはかる弊社の方針にマッチする、なかなかの妙案であります。

バックアッププログラム:mysqldump
Dropbox送信:Dropbox for PHP
※PostgreSQLの場合、pg_dumpを利用するなど適宜読みかえてください。

ディレクトリ構成

バッチプログラムは、SC_Batch_Backup_Mysql_Ex.php と命名し、以下のディレクトリに配置することにします。Dropbox for PHP は、ライブラリをそのまま modules ディレクトリに格納します。

+data
   +class_extends
      +batch_extends
         -SC_Batch_Backup_Mysql_Ex.php
   +module
      +Dropbox

データベースに接続する設定情報は、config.php から取得するようにしたいので、やや冗長ですが、以下の1行を挿入します。

[php title=”SC_Batch_Backup_Mysql_Ex.php”]
require_once ‘../../../public_html/require.php’;
[/php]

データベースバックアップの実装

$bin_path は、mysqldumpにパスが通ってない環境で使用してください。※ソースコードのサンプルは、ITキヲスクさんの記事を参考にさせていただきました。

出力したSQLファイルをZipに圧縮しますが、ArchiveTarがインストールされていない環境の場合は、PEAR経由でArchiveTarをインストールするか、該当行をコメントアウトしてください。

[php title=”SC_Batch_Backup_Mysql_Ex.php”]
$bkup_name = date(‘Ymd’) . date(‘His’).’.sql’;
$achv_name = $bkup_name . ‘.tar.gz’;
$bin_path = ”;//Applications/XAMPP/xamppfiles/bin/’;

$oauthCache = dirname(__FILE__) . ‘/oauth.cache’;
if (!file_exists($oauthCache)) {
die("Run setup first to establish an oauth token!\n\n");
}

$command = $bin_path . "mysqldump " . DB_NAME
. " –default-character-set=binary "
. " –host=" . DB_SERVER
. " –user=" . DB_USER
. " –password=" . DB_PASSWORD
. " > $bkup_name";
system($command);

$tar = new Archive_Tar($achv_name, TRUE);
$zip = $tar->create($bkup_name);
[/php]

Dropbox API経由でファイル送信する下準備

Dropbox画面1

Dropbox for Developers のページ

Dropbox API 経由でファイル送信するには、まずDropbox側の設定を行う必要があります。

Dropbox APIのページから、アプリケーションがDropbox APIにアクセスする認証手続きを行い、トークンをファイルに保存します。トークンはアプリケーションがDropbox API にアクセスするごとに使用する許可証のようなものです。

プログラムからDropbox API にアクセスし、トークンを取得するところまでの手続きは、労力を割かず、Dropbox for PHP のテストモジュールをそのまま流用させてもらうことにします。今回も無償でライブラリを使わせていただき、省コストを実現しますので、ライブラリの開発者ならびに提供者の方に感謝しつつ、ありがたく使わせていただくことにします。


Dropbox画面2

①メニューからMy Apps を選択し、Create an App ボタンを押下

Dropbox画面3

②App nameとDescriptionを記載しAppを登録

Dropbox画面4

③App keyとApp secretを取得

②でAppを登録する際、Access levelをFull Dropboxにしたのは、App folderでは、ファイル送信が失敗してうまくゆかなかったからです。もしかしたらこちらの手順が正しくなかっただけかも知れませんので、セキュリティ観点からFull Dropboxが好ましくない場合は、試行錯誤いただければうまくゆくかも知れません。

③App keyとApp secretが入手できれば、いよいよプログラムを起動し、トークンを取得する手続きに入ってゆきます。ターミナル(コマンドプロンプト)から Dropbox for PHP のsetupを実行します。

[bash]
Dropbox-php/test/setup
Setting up OAuth info for the dropbox-php unit tests…

Choose one of the following to use for negotiating OAuth:
[1] Dropbox_OAuth_PHP (uses the PECL oauth extension)
[2] Dropbox_OAuth_PEAR (uses the PEAR HTTP_OAuth package)
[3] Dropbox_OAuth_Zend (uses the Zend_Oauth package)
[4] Dropbox_OAuth_Curl (uses the PECL curl extension)
[5] Dropbox_OAuth_Wordpress (uses the WordPress WP_Http class)

4 ←当方環境ではcURLを使用しました。
Using Dropbox_OAuth_Curl for OAuth negotiation!

To test the dropbox api, you need a consumer key & secret…

Please enter your CONSUMER KEY and press enter: ******** ←画面に表示されている値をコピー&ペースト
Please enter your CONSUMER SECRET and press enter: ******** ←画面に表示されている値をコピー&ペースト
Visit the following url in a browser, login if necessary, and click ‘Allow’:
https://www.dropbox.com/1/oauth/authorize?oauth_token=******** ←URLをコピーしてブラウザで開く
Press enter when this is done…
[/bash]

画面にURLが表示されたらコピーして、ブラウザからURLを開きます。次のような画面が表示されるはずですので、Dropbox側でアプリの接続を許可してあげてください。

Dropbox画面5

④URLを開くと確認画面が表示されるので許可ボタンを押下

Dropbox画面6

⑤成功しましたと表示されたらOK

画面に成功しましたと表示されれば、アクセスが許可された状態になりましたので、ターミナルに戻り、Enterキーを押下します。これで無事トークンが取得できました。

[bash]
Got an OAuth token, confirmed connection with dropbox api.
Now you can run tests with the `phpunit` command!
[/bash]

Dropbox API経由でファイル送信する実装

Dropbox API のした準備で無事トークンを取得することができたら、カレントディレクトリに “oauth.cache”というファイルができあがっていると思います。このファイルの中には、先ほどの下準備で使用したKeyとSecretとトークンが格納されていますので、バッチプログラムのディレクトリに設置してやります。また“rootca”なるCA証明書もAPIとのやり取りに必要ですので、Dropbox for PHP のtestsディレクトリのものをコピーしてきてやります。

こんな感じになればOKです。

+data
   +class_extends
      +batch_extends
         -SC_Batch_Backup_Mysql_Ex.php
         -rootca
         -oauth.cache

プログラムの実装はこのようになります。Dropbox for PHP のテスト用実装を参考に実装してあります。

[php title=”SC_Batch_Backup_Mysql_Ex.php”]
$oauthCache = dirname(__FILE__) . ‘/oauth.cache’;
if (!file_exists($oauthCache)) {
die("Run setup first to establish an oauth token!\n\n");
}

$stream = unserialize(file_get_contents($oauthCache));
$consumerKey = $stream[‘consumer’][‘key’];
$consumerSecret = $stream[‘consumer’][‘secret’];
$oauthClass = $stream[‘class’];
$tokens = $stream[‘tokens’];

$oauth = new $oauthClass($consumerKey, $consumerSecret);
$oauth->setToken($tokens);
$dropbox = new Dropbox_API($oauth);
$result = $dropbox->getAccountInfo();

if (isset($result[‘uid’])) {
$dropbox_dir = ‘MySqlBackup’;
$response = $dropbox->putFile("$dropbox_dir/$achv_name", $achv_name);
if ($response) {
unlink($bkup_name);
unlink($achv_name);
}
} else {
die("An error occurred, unable to confirm connection with dropbox api\n");
}
[/php]

最後に Dropbox ライブラリのプログラムを1箇所だけ書き変えます。私がGitから取得した時点のソース(Commited at 2012-06-16 15:28:51)では、CURLOPT_SSL_VERIFYPEER オプションが“true”になっていますので、これを“false”にします。PHPのリファレンスによると、CURLOPT_CAINFO オプションにてCA証明書を指定するときは、“false”にするとありますので、これでよいのだと思います。※今回の検証に使用したX-Serverでは、“true”では正しく動作せず、“false”にすることで正しく動作するようになりました。
また同じくリファレンスには、CURLOPT_CAINFO オプションの注記には、「(証明書を)絶対パスで記載しなければなりません。」と記載されているのですが、このような記載で正しく動作しました。まぁたまたまかも知れません。

[php gutter=”true” firstline=”84″ highlight=”86″ title=”data/module/Dropbox/OAuth/Curl.php”]
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO, "rootca");
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
[/php]

バッチジョブをCronに設定します。

ローカルPCで、バッチプログラムを実行し、無事、Dropboxにファイルが送信されたら、サーバーにアップロードし、バッチプログラムをテストしましょう。今回の検証に使用したX-Serverでは、数回のトライ&エラーで、テスト送信が成功しました。環境依存だと思いますが、前述のCURLOPT_CAINFO オプションに関しては、ローカルPC(Mac OSX)では、“true”のままで正常に動作しましたが、X-Serverでは動作しませんでした。あともう1点、バッチプログラムを起動するPHPのバージョンですが、/usr/bin/php5 を使用すると、PHP 5.1.6 で起動されてしまい、これだとcURLがエラーで終了してしまっていたため、PHPのバージョンを5.3.3 を使用するため、/usr/bin/php5.3 でバッチプログラムを起動するようにします。

/usr/bin/php5.3 /home/****/****/data/class_extends/batch_extends/SC_Batch_Backup_Mysql_Ex.php

きっとこれでちゃんと動いてくれると思います。

今日はここまで。ばいばい。

今回記事にしたプログラムソースを置いておきます。どうぞ。

SC_Batch_Backup_Mysql_Ex.php