まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

UNIX 系システムには cron(d) と呼ばれるジョブの実行デーモンが存在しています。簡単にいえば「指定した日時や時刻に特定のコマンドを実行させる仕組み」です。「毎日午前1時にデータベースのバックアップコマンドを実行する」とか、「1時間おきに特定のスクリプトを実行する」とか、そういうものです。バックアップや集計目的のジョブを自動実行させようとすると cron を使うことになると思います。

例えばですが、毎日午前0時になったら /usr/local/bin/xxx.sh を実行したい、という場合は以下の1行を /etc/crontab に加えます:
0 0 * * * /usr/local/bin/xxx.sh

最初の5要素は実行タイミングです。1つ目が分、2つ目は時、3つ目は日、4つ目は月、5つ目が曜日を指定します。上記例では分に0、時に0が指定されているので「毎日午前零時」を指定していることになります。そして6番目の要素が実行させるジョブです。 というわけで上記の1行は毎日午前0時になったら /usr/local/bin/xxx.sh を実行する、という指定をしていることになるわけです。この程度ならシンプルで分かりやすいですよね。


で、今回やりたかったことはこんな内容でした:
(1) 毎日午前零時になったら、
(2) あるディレクトリ(/usr/tmp/logs/)に移動して
(3) php backuplog.php YYYYMMDD 形式のジョブを実行する
(4) 上記の YYYYMMDD は前日を示す日付文字列

要は backuplog.php という、ログのバックアップを行うような PHP スクリプトが /usr/tmp/logs/ に用意されていて、それを自動実行したいのですが、パラメータとして前日の日付文字列を付けたい(前日分のログをバックアップしたいので、その日付をパラメータで渡す)ということです。例えば今日の日付が 20140314 だとしたら、20140313 という文字列を指定して実行したい、ということです。まあ、この要件自体はさほど珍しくはないかな、と。敢えて付けくわえると、ログファイルの場所の関係もあって、直接 /usr/tmp/logs/backuplog.php を実行するのではなく、最初に /usr/tmp/logs にカレントディレクトリを移してから実行したい、という点が要件にあります。まあ、でも、これも珍しい要件とは言えないのかな・・・

ただ実現できるまで結構ハマりました。いくつか隠れた落とし穴があるんです。この要件を実現する crontab の記述内容をすぐ&正確に言える人ってどのくらいいるんだろ・・・



要件のうち、問題になりそうなところを1つずつ解決していきましょう。まず毎日午前零時になったら、は上記で説明しているので問題ないです。

次にカレントディレクトリを移動してからコマンドを実行する、という点。端末上だと cd /usr/tmp/logs と php backuplog.php YYYYMMDD という2つのコマンドを実行することになるのですが、それをどのように1行で記述するか、という問題です。 ただ、これも実は答はシンプルで単に「;(セミコロン)で区切って記述するだけ」です(端末上でもこの書き方で連続実行ができます)。なので crontab の6つ目の要素は cd /usr/tmp/logs;php backuplog.php YYYYMMDD という書き方になります。

さて、ここからがちょっと難しくなります。前日を示す文字列パラメータをどうやって作成すればよいでしょう? これは date コマンドを使うことになります。UNIX の date コマンドはそのまま実行すると日付時刻を表示するのですが、パラメータによって対象日やフォーマットを変更することができます。まず普通に実行するとこんな感じに表示されます:
# date
2014年  3月 14日 金曜日 04:53:43 UTC

このフォーマットを YYYYMMDD 形式に変えるには + オプションで形式を指定します:
# date +%Y%m%d
20140314

いい感じですね、でもこれは今日の日付です。では「前日」をどのようにすればいいでしょうか? こちらは --date オプションで指定することができるようです:
# date --date '1 day ago' +%Y%m%d
20140313

目的の文字列が取得できました! 後はこの結果をコマンドのパラメータとして渡せればいいので、上記コマンドを `(バッククォート)で括ってこんな感じにすると、目的のコマンドが完成したことになります。試しにコマンドラインから実行すると期待通りの結果が得られます:
# php backuplog.php `date --date '1 day ago' +%Y%m%d`


ということは、全てあわせて crontab に追加する内容はこれでいい、ということに・・・
 0 0 * * * cd /usr/tmp/logs;php backuplog.php `date --date '1 day ago' +%Y%m%d`

・・・実はこれ、(惜しいけど)まだ間違ってます。crontab では % という文字にはエスケープが必要になるのでした。最終的な正解はこちらです:
 0 0 * * * cd /usr/tmp/logs;php backuplog.php `date --date '1 day ago' +"\%Y\%m\%d"`

スケジュールエージェントはデバッグが難しい・・・


 

AWS(Amazon Web Services) のストレージサービスである S3。特定条件の中で利用する限りは無料になるし、容量1GBに付き月10円弱で使えるし、これ単体で静的なウェブホスティングにも使えるので、ちょっとしたファイル置き場以上の便利さがあります。

最近、この S3 をバケット単位でマウントできるという s3fs というツールの存在を知りました。その紹介です。

そもそも S3 をマウントできると何がうれしいのか?単なるファイル置き場というだけでなく、自分が最近使う機会の多い WordPress をクラスタリング環境にする際にネットワークマウントは必須になっている、という背景もあり、自分の場合にはそういったネットワークマウントのできるパブリックなクラウドストレージは WordPress と併用する環境としても魅力的なのでした。


ここでちょっとだけ脱線を。WordPress 自体はデータを RDB(一般的には MySQL)内に格納するので、クラスタリング環境を構築する上で特別意識することはないように思えます。ただし、ファイルをアップロードして利用する場合だけは注意が必要です。WordPress のファイルアップロード(メディアアップロード)では、アップロードされたファイルはファイルシステム上に移動されて、WordPress 自身はそのファイルパスの情報だけを格li納します(つまりバイナリの情報はDBには格納しません)。

この挙動は WordPress が1台の HTTP サーバー環境で稼働する場合は大きな問題にはなりにくいと思っていますが、例えば2台の HTTP サーバー(A, B) でクラスタリング環境を構成していた場合で考えると問題が浮き彫りになります。

仮に2台のうちのサーバーA上で、このメディアアップロードが実行されたと仮定します。サーバーA はアップロードされたファイルを自分自身の /var/www/html/wordpress/wp-content/uploads/ 以下に移動します。仮にそのファイル名が /var/www/html/wordpress/wp-content/uploads/aaa.jpg となったとします。

上記のように WordPress ではバイナリの情報は DB に格納せず、そのファイルパス情報だけを DB に格納します。つまりアップロードされたファイルは /var/www/html/wordpress/wp-content/uploads/aaa.jpg に存在する、という情報だけを DB に記憶することになります。サーバーA 内ではこれで問題になることはありません。

ところがクラスタリングのもう一台であるサーバーBも同じDBを参照しているので、サーバーBからも「該当ファイルは /var/www/html/wordpress/wp-content/uploads/aaa.jpg に存在する」と見えてしまします。このファイルはサーバーA のファイルシステムには保存されていますが、サーバーB には存在していません。したがって例えばサーバーB 上の処理でこの画像を表示させようとすると「ファイルが存在しない」ことになってしまいます。クラスタリングの振り分けはユーザーがコントロールできるわけではないので、ユーザーによってファイルが見えたり見えなかったり、ということが起きてしまいます。

これを解決するにはディレクトリを共有するしかありません。上記例であれば /var/www/html/wordpress/wp-content/uploads/ 以下が実際にはネットワークフォルダになっていて、その同じ箇所をサーバーA/B 双方からマウントして参照する、という構成になっている必要があるのでした。そしてそのようなネットワークフォルダをどうやって用意・調達するか、という問題に直面することになるのでした。


少し脱線しましたが、こんな背景の中で「無料でも使える AWS S3 がマウントできる」というのはかなり魅力的な情報なのでした。早速試してみたので以下にその手順を紹介します:

まずは S3 側の準備をします。仮に "awsuser1" という名前のバケットが S3 上に作成済みであると仮定し、このバケットを共有する前提で紹介します:
2014031201


必要な設定は、このバケットに接続するための認証ユーザーの作成です。既に別の AWS サービス用などにユーザーを作成済みであればそのユーザーを流用しても構いませんが、ここでは S3 アクセス用のフル権限を持ったユーザーを新規に作成することにします。

AWS のサービス一覧から IAM を選択します:
2014031202


左ペインで Users を選択し、"Create New Users" ボタンをクリックします:
2014031203


(名前自体はあまり重要ではないので)適当な名前のユーザーを作成します:
2014031204


作成直後のダイアログの "Show User Security Credentials" をクリックして展開します:
2014031205


展開後に表示される "Access Key ID" と "Secret Access Key" 両方の値をメモするかダウンロードして、後から参照できるようにしておきます。記録できたら "Close Window" でこのダイアログを閉じます:
2014031206


次にこのユーザーに S3 へのアクセス権を付与します。左ペインで Users が選択された状態に戻ります。ここで作成したユーザーを選択し、右下のユーザー詳細情報ペインでは "Permissions" タブを選択します。その中の "Attach User Policy" ボタンをクリックします:
2014031207


アクセス権指定のダイアログが表示されます。Select Policy Template と書かれたリストの下の方に "Amazon S3 Full Access" という列があるので、この右の "Select" ボタンをクリックします:
2014031208


確認画面になるので "Apply Policy" をクリックします。これでこのユーザーは S3 へのフルアクセス権を持ったことになります:
2014031209




次はマウント元側の設定です。今作成した(S3 へのフルアクセス権を持った)ユーザーでマウントするための設定を行います。まずインターネットに接続された Linux を用意します。以下 CentOS を使っている前提で紹介しますが、ディストリビューションによる可不可は特にないと思っています(AWS のインスタンスでも、そうでなくてもできます)。

まず事前準備として必要そうなライブラリを導入しておきます:
# yum groupinstall "Development Tools"  (C/C++ コンパイル環境を導入)
# yum install openssl-devel (libcrypt 0.9 を導入) # yum install libcurl-devel (libcurl 7.0 を導入) # yum install libxml2-devel (libxml-2.0.2.6 を導入)

上記3ライブラリに加えて fuse 2.8.5 が必要になります。AWS の EC2 インスタンスからは普通に yum でインストールすることもできます(yum install fuse fuse-devel)が、yum でインストールできないケースも多いのでビルド手順を紹介します。
# cd /usr/local/src
# wget http://sourceforge.net/projects/fuse/files/fuse-2.X/2.8.5/fuse-2.8.5.tar.gz
# tar xzvf fuse-2.8.5.tar.gz
# cd fuse-2.8.5
# ./configure --prefix=/usr
# make
# make install
# ldconfig
# modprobe fuse

fuse までが導入できると s3fs のビルド条件がそろったことになります。以下の手順で s3fs をビルドします:
# cd /usr/local/src
# wget http://s3fs.googlecode.com/files/s3fs-1.61.tar.gz
# tar xzvf s3fs-1.61.tar.gz
# cd s3fs-1.61
# export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/lib64/pkgconfig/
# ./configure --prefix=/usr
# make
# make install

これで s3fs のインストールが完了しました。最後に S3 へアクセスするためのユーザーの設定を行います。/etc/passwd-s3fs というファイルを、上記ユーザー作成時にメモした Access Key ID と Secret Access Key を使って、以下のような内容で新規に作成します(カッコは不要です):
# echo '(Access Key ID):(Secret Access Key)' > /etc/passwd-s3fs

仮に Access Key ID が ABCD で Secret Access Key が wxyz だった場合、/etc/passwd-s3fs の中身は以下の一行になります:
ABCD:wxyz

このファイルは他者から参照できないようにする必要があるため、ファイルパーミッションを変更します:
# chmod 640 /etc/passwd-s3fs

マウント先のディレクトリを用意します。ここでは /mnt/s3 というディレクトリに S3 をマウントすることにします:
# mkdir /mnt/s3

これで全ての準備が整いました。S3 の awsuser1 というバケットを /mnt/s3/ にマウントするには以下のようなコマンドを実行します。バケット名やマウント先は環境に合わせて適宜変更してください:
# s3fs awsuser1 /mnt/s3/ -o allow_other,default_acl=public_read

結果はこんな感じになりました。S3 って容量としては 256TB も用意されてるんですねw:
2014031210


再起動時にもこのマウントを有効にするには、同じコマンドを手動で再発行するのではなく、 /etc/fstab に以下の1行を追加記述するのがいいと思います。これもバケット名やマウント先は環境に合わせて適宜変更してください:
s3fs#awsuser1 /mnt/s3 fuse allow_other,default_acl=public-read 0 0


こんな感じで S3 をファイルシステムにマウントできることがわかりました。後は WordPress をクラスタ環境で使う場合は、メディアアップロードのディレクトリをこのマウント先にしておけば、同じイメージをクローンしてもクローン先でも同じ S3 を参照することになるので上述のような問題を回避できることになります、たぶん。





 

前回からの続きです。

 IBM Power Development Platform で Power 版 RedHat Enterprise Linux サーバーのリソース予約が完了し、サーバーが Active になりました。その後の接続方法について紹介します。
2014030502


 このサーバーに接続するには VPN を使います。その詳しい内容は予約確認ページの真ん中辺りにある "Download Connection User Guide" ボタンをクリックしてダウンロードできる PDF に(英語で)記載されています:
2014030104

以下、その内容を簡単にかいつまんで日本語で紹介します。まずこのボタンの上部に書かれている "VPN appliance IP address", "User ID", "Initial / last reset VPN password" の3つの内容をメモしておきます。これが VPN 接続時に必要な入力項目になります。
2014030601


次に VPN 接続をするクライアント PC からブラウザを起動して、https://vlpvpn.centers.ihost.com/ を参照します。僕の場合は CentOS のマシンから行っているため CentOS でのスクリーンショットを紹介しますが、Windows や Mac からも同様の内容になると思います。

ログインフォームが表示されるので、先程メモした VPN 用 User ID とパスワードを入力します:
2014030602


"Welcome" メッセージが表示されたらログインは成功です。Continue ボタンで先に進みます:
2014030603

この後、ウィザード形式で接続する手続きが始まります。OS の種類が自動認識されていると思いますので、リンク(以下の例であれば "Linux_x86_64" と書かれた箇所)をクリックして処理を進めていきます:
2014030604

おそらくですが、ここから先は OS ごとに手続きが異なるはずです。種類によってはこのまま自動認識が成功して、ウィザード処理を進めていくだけでこのまま VPN までが完了することもあると思います。以下は僕の試した CentOS 環境での場合の説明です、参考程度に参照ください。

リンクをクリックすると、VPN クライアントをセットアップするためのスクリプトのダウンロードが始ります:
2014030604


ダウンロードが完了したら、そのファイルをシェルから実行してインストールします:
# sh vpnsetup.sh

インストールが完了したら、アプリケーションメニューから VPN クライアントを起動します:
2014030605


VPN クライアントが起動します。まず最初の1回目に必要な設定があるので、設定項目を開きます:
2014030606


デフォルト状態では VPN クライアントのセキュリティが強めに設定されています。このままだとこの環境での VPN サーバーに接続できないため、"Block connections to untrusted servers" についているチェックを外します。その上で Close ボタンでこの画面をクローズします:
2014030607


1つ前の画面に戻ります。改めて最初にメモした VPN サーバーの IP アドレス(おそらく 32.97.184.40 で固定)を入力して Connect ボタンをクリックします:
2014030608


先ほどチェックを外した設定に関する確認ダイアログが表示されます(チェックを外さないとこの画面も出ないはずです)。Untrusted なサーバーに対する接続を行うか? という確認です。 今回だけは接続するのであれば "Connect Anyway" を(次回も確認ダイアログがでます)、このサーバーを信頼するのであれば "Always Connect" を(次回からは確認ダイアログがでません)、このどちらかをクリックして接続します。"Keep Me Safe" を選択すると接続しません:
2014030609


ユーザー名とパスワードを聞かれるので、これも上でメモした VPN 用の User ID と Password を入力し、最後に Connect ボタンをクリックします:
2014030610


確認画面が表示されたら、Accept をクリックします。環境や設定によってはこの後に再度 Untrusted サーバーへの接続に関するダイアログが表示されるので "Connect Anyway" か "Always Connect" を選択します:
2014030611


VPN サーバーに正しく接続できると、画面内に VPN が接続できているようなマークが表示されます(以下は CentOS での例):
2014030612



これで VPN 環境が整って、IBM の仮想プライベートネットワークに接続できました。では稼働中の Power 版 RedHat Linux Enterprise サーバーへ接続してみます。

改めて PDP の Virtual Server Access 環境画面を参照して、予約したサーバー環境の IP アドレス、ユーザー名、パスワード、root パスワードを確認してメモしておきます(※上記で説明した VPN 用の ID & パスワードとは異なります。ID だけは同じかもしれません)。
2014030613


確認した IP アドレスに(指定されている)ユーザー名とパスワードで SSH で接続します。Windows であれば PuTTY などの SSH クライアントから接続します。Mac や Linux であれば以下のコマンドとパスワードで:
# ssh u00NNNNN(UserID)@172.29.XXX.XXX(IPアドレス)

ログインできると画面に IBM ロゴが表示されるようです。とうとう Power サーバー環境にログインできました!:
2014030614



で、実際にこのサーバーを使うために色々なセットアップが必要になりますが、このままでは何もできないので、まずは root になりましょう。su コマンドを実行して、メモしておいた root パスワードを指定します:
[u00NNNNN@172-29-XXX-XXX ~]$ su -
パスワード: (rootパスワードを入力)
[root@172-29-XXX-XXX ~]#

ここからはもうご自由に。でも一応最低限のセットアップの紹介をしておきます。

何はともあれ root パスワードを変更します:
[root@172-29-XXX-XXX ~]# passwd
Changing password for user root.
New password:(新しいパスワードを入力)
Retype new password:(同じパスワードを再入力)
passwd: all authentication tokens updated successfully.
本当に Power サーバーかどうか(笑)を確認してみました。本物だ、仮想CPU×4構成:
[root@172-29-XXX-XXX ~]# cat /proc/cpuinfo
processor      : 0
cpu            : POWER7 (architected), altivec supported
clock          : 3864.000000MHz
revision       : 2.1 (pvr 003f 0201)

processor      : 1
  :
  :

timebase       : 512000000
platform       : pSeries
model          : IBM,9179-MHB
machine        : CHRP IBM,9179-MHB

ちなみにセットアップは普通に yum が使えます。MySQL とか PHP とか Apache HTTPD とかについては、x86 版と違いを感じないほど普通に導入できます。

加えて、IBM 製のミドルウェアに関しては、DB2 や WAS(WebSphere Application Server), C/C++ コンパイラなど、いくつかのものは最初から /stage/middleware 以下にインストールメディアがマウントされて使えるようになっています。検証などで必要であれば、ここからインストールして使えるようです:
[root@172-29-XXX-XXX ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda4              29G  3.0G   24G  11% /
tmpfs                1003M     0 1003M   0% /dev/shm
/dev/sda2             463M   46M  394M  11% /boot
nfs.dal-ebis.ihost.com:/var/nfs/LINUX
                       25G   19G  5.1G  79% /stage/middleware

[root@172-29-XXX-XXX ~]# ls /stage/middleware
DB2   IDS       ParallelEnvironment  Rational  TDS  XL_C_C++
ESSL  netbeans  PESSL                SAAS      WAS  XL_Fortran

なお最初にセットアップされている Power 版 RHEL ですが、どうやらフルセットに近いものが用意されているようです。X Window(GNOME) も、日本語環境も、VNC サーバーまでも最初から含まれているようです。ただ VNC サーバーはインストールまではされていますが起動はしていません。最後にその設定および起動方法だけ紹介しておきます(ま、これも x86 版と比べて特に違いはないですけど・・)。

まず、これは必須ではありませんが、せっかく X Window 環境を使うので日本語設定にしておきましょう。/etc/sysconfig/i18n をエディタで開いて、1行目を
[root@172-29-XXX-XXX ~]LANG="ja_JP.UTF-8"

と変更しておきます。

次に /etc/sysconfig/vncservers を編集して、root ユーザーで 5901 番ポートにアクセスするような設定にしておきます:
  :
VNCSERVERS="1:root"
VNCSERVERARGS[1]="-geometry 1024x768 -nolisten tcp"
  :

また VNC 接続用のパスワードを設定しておきます:
# vncpasswd
Password:(入力内容は画面に表示されません)
Verify: (同じパスワードを入力)

この段階で一度 VNC サーバーを起動して、設定ファイルのひな型(~/.vnc/xstartup)を作っておきます:
# /etc/init.d/vncserver start

直前のコマンドで xstartup が作成されているはずなので、このファイルを編集します。環境変数 LANG を設定して、GNOME のセッションをそのまま VNC 環境でも使えるようにします:

# vi ~/.vnc/xstartup
#!/bin/sh

export LANG="ja_JP.UTF-8"(この一行を最初に挿入)
  :
  :
(最後の部分を編集)
# xterm -geometory 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" & (コメントアウト)
# twm & (コメントアウト)
gnome-session & (この一行を追加)

改めて新しい設定で VNC サーバーを再起動し、またシステム起動と同時に実行されるようにしておきます:

# /etc/init.d/vncserver restart
# chkconfig vncserver on

これで VNC サーバーの準備と起動ができました。 実際に X Window(GNOME) 環境にアクセスしてみます。VPN 接続をしたクライアントの環境に戻り、VNC Viewer などの VNC クライアントを実行します。アクセス先には Linux サーバーの IP アドレスと、最後に 5901 番ポートで接続するための :1 を付けてアクセスします:
2014030615


パスワードを聞かれたら VNC 用に設定したパスワードを入力します:
2014030616


Power 版 RHEL の GNOME 環境にアクセスできました! 普段 CentOS を使っていると RHEL のロゴに少し緊張します(笑)。 といっても、x86 版と比べてほとんど違いはなさそうですけどね・・・
2014030617

実際試してみると分かりますが、普通に日本語も使えます。FireFox まで導入済みなので、すぐにウェブ利用も可能になっています。

この環境が2週間無料で使えるのはいいですね。加えて Power 版 RHEL の出来がいいというか、ほとんど x86 版との違いを感じずに使えるのでストレスもほとんどありません。 仮想環境とはいえ Power CPU × 4 環境のマシンを使えるので、ビットコインの発掘とか x86 用に作ったウェブアプリケーションの Power 環境での動作検証とか、いろいろ使えそうです。


最後にこの環境の終了手順を紹介します。この環境は放っておいても2週間後には使えなくなってしまうのですが、その前にある時点の環境をスナップショットとして保存しておくと、次回の予約時に(Standard Image からではなく)そのスナップショットのイメージから再作成してもらう、ということもできるようです。そのスナップショットの取得方法を紹介します。

PDP の予約画面に戻ります。ここで Virtual Server Access を展開し、スナップショットを取得したいサーバーの予約番号部分を右クリックし、"Save Image" を選択します:
2014030618


すると以下のような画面が出てくるので、"Save image now"(今の状態で保存する)か、"Save image when reservation ends"(利用期間終了時に保存する)かを選びます。またその保存イメージの名前と、説明を記入し、最後に "Save image" ボタンをクリック、です:
2014030619

この方法で取得したイメージは、次回リソース予約時に "Standard image" ではなく "Saved Image" から作成するよう指定することで再利用が可能になっています。この方法で事実上2週間以上必要な検証作業についても(一時的な切断と、IPアドレス等の変更はありますが)作業を継続して行うことができるようです。


IBM PartnerWorld への企業契約が前提になるとはいえ、この環境が無料で使えるのって、かなりお得な気がするなー。

僕自身は AIX や IBM i(AS/400) にはあまり興味ないんですが、最初にダウンロードした PDF のガイドにはこれらの環境での VPN の利用方法なども書かれています。興味あるかたは是非挑戦してみてください。



 

このページのトップヘ