ラベル fuelphp の投稿を表示しています。 すべての投稿を表示
ラベル fuelphp の投稿を表示しています。 すべての投稿を表示

2012年12月11日火曜日

CDPってなんじゃ?(通知に関するオレオレパターンでCloudwatchからサイレンをならす)


CDP Advent Calendar 2012へ参加しました。

CDPのパターンリストを見ていると純粋に勉強になったり、知らずに実践していたパターンなどがあったり、見ていて楽しいです。

何を書くか迷いましたが、オレオレパターンを定義してみました。

普段運用などでCloudWatchからのアラートがよく飛び交っていますが、うっかり気づかなかったりすると困ることがあります。
SNSを使用した通知はインフラ内で使用されることが多いような気がしますが、HTTP Notificationを使用してアプリケーションサーバーからクライアントまで含めたプッシュ通知の可能性を探ってみたいと思います。

名前をつけるとしたらDeep Notificationでしょうか。
構成は以下の様なイメージです。



今回は例として、CloudWatchでアラートが発生したら、管理画面にプッシュしてWebAudioでサイレンを鳴らしてみたいと思います。

 まず、SNSに"sound"というトピックを立てます。




次にEC2インスタンス内で、HTTP通知先になるWEBアプリをつくります。
今回はFuelPHPで、/monitor/alert のアクションを通知先とします。

SNSのメール通知では購読確認はメールのリンクを踏めばOKですが、HTTP通知では登録後通知先のURLにSNSがアクセスし、その際のJSONパラメータに含まれる情報を元に、SNSの認証APIをリクエストするという方式のようです。

/monitor/alert のアクションで、以下のようにJSONリクエストからTopicArnとTokenを抽出し、SNSに購読確認を行うようにしておきます。

fuel/app/class/controller/monitor.php
     public function action_alert()
     {

          $sns = new AmazonSNS(array('key'=>'xxxxxxxxxxxxxxxxxx',                                               
                                      'secret'=>'xxxxxxxxxxxxxxxxxxxx'));
          $sns->set_region(AmazonSNS::REGION_APAC_NE1);
          $input = file_get_contents('php://input');
          $input =json_decode($input, true);

          if ($input["Type"] === 'SubscriptionConfirmation') {
               $response = $sns->confirm_subscription(
                                $input["TopicArn"], 
                                $input["Token"]);
          }
     }


次に、SNSの"sound"トピックで、購読者の登録を行います。
プロトコルはHTTP、エンドポイントは/monitor/alert アクションのURLとします。



すると、すぐにSNSがエンドポイントにアクセスし、認証が完了すると、以下のようにSubscriptin IDにsnsのarn値がセットされます。
これで認証が完了しました。

すでに認証が完了しているので、このコードは不要なのでコメントアウトし、実際に通知があったときの処理を書きます。
SNSから送信されたJSONデータをredisにPublishするようにします。

fuel/app/class/controller/monitor.php
     public function action_alert()
     {

/*
          $sns = new AmazonSNS(array('key'=>'xxxxxxxxxxxxxxxxxx',
                                     'secret'=>'xxxxxxxxxxxxxxxxxxxx'));
          $sns->set_region(AmazonSNS::REGION_APAC_NE1);
          $input = file_get_contents('php://input');
          $input =json_decode($input, true);

          if ($input["Type"] === 'SubscriptionConfirmation') {
               $response = $sns->confirm_subscription(
                              $input["TopicArn"], 
                              $input["Token"]);
          }
*/

          $input = file_get_contents('php://input');

          $redis = Redis::instance('default');
          $redis->publish('alert', $input);

          $this->template->title = 'Monitor » Alert';
          $this->template->content = View::forge('monitor/alert');
     }


次に、node.jsで、redis経由でSubscribeしたJSONデータをユーザーにプッシュするようにします。

node/server.js
var server = require('http').createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('server connected');
});
server.listen(3000);

var io = require('socket.io').listen(server);
var opts = {host:'127.0.0.1', port:6379};

var redis = require('redis');
var sub = redis.createClient(opts.port, opts.host);
sub.subscribe('alert');
sub.on("message", function(channel, message){
  io.sockets.emit('alert', JSON.parse(message));
});


また、アラート通知画面を別途用意し、alertイベントを受信したら情報を表示してWebAudioを鳴らすようにします。

/fuel/app/views/monitor/index.php
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="/assets/js/client.js" type="text/javascript"></script>

<div class="container">
<div class="hero-unit">
<span id="timestamp" style="color: red;"></span>
<h3 id="subject" style="color: red;"></h3>
<dl class="dl-horizontal" id="message"></dl>
<button class="btn" id="stop" type="button">Stop</button>
  </div>
</div>


/public/assets/js/client.js
$(function(){

  var context = new webkitAudioContext();
  var band = 24000;
  var buf = context.createBuffer(1, band, band);
  var data = buf.getChannelData(0);
  for (var i = 0;i < data.length;i++) {
        data[i] = i > band*0.5 ? 0 : ((i % 100) < 50 ? 1 : 0);
  }
  var src = context.createBufferSource();
  src.buffer = buf;

  $.getScript("http://"+location.hostname+":3000/socket.io/socket.io.js", function(){
    var socket = io.connect('http://'+location.hostname+':3000/');
    socket.on('connect', function() {
    });

    socket.on('alert', function(data){
      $("#timestamp").text(data.Timestamp);
      $("#subject").text(data.Subject);
      $("#message").empty();
      var msg = JSON.parse(data.Message);
     for(var p in msg){
        $("#message").append("<dt>"+p+"<dt><dd>"+msg[p]+"</dd>");
     }
      src.connect(context.destination);
      src.loop = true;
      src.noteOn(0);
    });

  });

  $("#stop").click(function(){
    src.disconnect();
  });
});



そして、試しにCloudWatchで監視対象のインスタンスの10%のCPUでアラートがでるようにしておきます。



それでは監視対象のインスタンスで無限ループを動かして、様子を見てみます。


10%を超えたラインでしばらくするとプッシュ通知され、サイレンが鳴りました!!

運用だけでなくバッチの完了通知やGrowlへの通知など、HTTP通知を使ってSNSをアプリケーション・サーバーのほうに一歩踏み込ませることで、通知に関する自由度が一気に高まり、運用効率などに効果があるのではないかと思います。

以上です。

2012年11月12日月曜日

redisってなんじゃ?(FuelPHPの管理画面からSocket.IOで全員にPush)

node.js、とりわけSocket.IOでは、ユーザー同士のリアルタイム通信が簡単にに行えることがわかってきました。
ですが、時には管理画面や、バッチの処理によってユーザーに何か通知をしたいことがあるかもしれません。
今度はredisを利用して、システム管理者とユーザーの間でリアルタイム通信を行なってみます。

以前紹介したFuelPHPもまた、キャッシュストアや、クライアントインターフェースとしてRedisをサポートしています。
そこで、FuelPHPで管理画面をつくり、ボタンを押すとチャット中のユーザーに管理者からのメッセージを表示させたいと思います。

チャットプログラムや構成は前々回の構成のまま、node1, node2という2つのサーバーがredisサーバーにつながってセッション共有されている状態とします。
今回は、以下のように、さらにadminサーバーを追加し、管理画面を置きます。

    +----------+       +---------+
    | admin    |       | redis   |
    |----------| pub   |---------|
    | fuelphp  +------->         |
    |          | sub   | redis   |
    | admin.js <-------+         |
    +-----+----+       +---------+
          |
          +-----------------+
          |      emit       |
    +-----v----+       +----v----+
    | node1    |       | node2   |
    |----------|       |---------|
    | chat.js  |       | chat.js |
    +----------+       +---------+

上図のように、fuelphpからredisにpublishしたものを同じadminサーバー上のsocket.ioでsubscribeし、チャットサーバーへブロードキャストするイメージです。
チャットサーバーが直接subscribeすることも可能ですが、そうすると、チャットサーバーの数だけsubscribe→emitが発生して、メッセージが重複してしまいます。subscribe→emit役は1つである必要があるので、adminサーバーに兼任させます。
なので、既存のチャットサーバーには手を加える必要がなく、adminサーバーだけ用意して実装すればいいわけです。

それでは実際に用意してみます。


まず必要なユーザーやライブラリを用意します。
# useradd appadmin
# passwd appadmin
# yum install -y vim git wget php  gcc gcc-c++ make
# curl get.fuelphp.com/oil | sh


FuelPHPの最新版(v1.4)では、タイムゾーンの指定が必至になったのでphp.iniで設定します。
# vim /etc/php.ini
---
date.timezone = Asia/Tokyo
--


次にアプリユーザーのホームディレクトリをApacheからアクセス可能にします。
# chmod 755 /home/appadmin


アプリを作成します。
publishボタンを配置する画面(index)とpublishボタンの押下(publish)の2つのアクションを用意します。
# su - appadmin
$ oil create app
$ cd app
$ oil g controller greeting index publish


次に、DocumentRootをFuelPHPのpublicディレクトリに向けます。
$ exit
# cd /var/www/ 
# mv html html.org
# ln -s /home/appadmin/app/public html


そして、Apacheの設定をシンボリックリンクをたどり、.htaccessを許可するように変更し、起動します。
# vim /etc/httpd/conf/httpd.conf
---

<Directory "/var/www/html">
~略~
    Options FollowSymLinks
~略~
    AllowOverride All
~略~
</Directory>

---
# /etc/init.d/httpd start 


次にFuelPHPのconfig/db.phpおよび、viewとcontrollerを以下のように実装します。

fuel/app/config/db.php
redisの接続先を指定します。チャットサーバーが参照しているのと同じサーバーに接続させるようにします。
$ vim fuel/app/config/db.php
<?php

/**
 * Use this file to override global defaults.
 *
 * See the individual environment DB configs for specific config information.
 */

return array(

    'redis' => array(
        'default' => array(
            'hostname' => '10.0.0.200',
            'port'     => 6379
        )
    ),

);


fuel/app/views/greeting/index.php
Publishボタンを配置します。
$ vim fuel/app/views/greeting/index.php
---
<p>Index</p>

<?php echo Form::open('greeting/publish'); ?>
<?php echo Form::submit('publish', 'publish'); ?>
<?php echo Form::close(); ?>
---


fuel/app/classes/controller/greeting.php
publishアクション内で、Redisクラスをインスタンス化しています。
ここでの引数defaultは、db.phpで設定したラベルを指定します。
そして、publishメソッドを呼び出すことにより、redisサーバーに「greeting」というチャンネルでpublishを行います。
$ vim fuel/app/classes/controller/greeting.php
---
<?php

class Controller_Greeting extends Controller_Template
{

    public function action_index()
    {
        $this->template->title = 'Greeting &raquo; Index';
        $this->template->content = View::forge('greeting/index');
    

    public function action_publish()
    {

        $redis = Redis::instance('default');
        $redis->publish('greeting', 'おはよう諸君!!');

        $this->template->title = 'Greeting &raquo; Publish';
        $this->template->content = View::forge('greeting/publish');
    }
}
---


ここまでで、管理画面自体は完成です。
以下のようにPublishボタンがあるだけのシンプルな画面になっています。




つづいて、同じadminサーバーに、node.jsを入れて動かします。
モジュールはforever, socket.io, redisを入れます。
socket.ioにもredisが含まれているのですが、requireのしやすさやバージョンが新しさなどのため、別途いれておきます。
# cd /usr/local/src
# wget http://nodejs.org/dist/v0.8.14/node-v0.8.14.tar.gz
# tar xzvf node-v0.8.14.tar.gz
# cd node-v0.8.14
# ./configure  
# make
# make install
# curl https://npmjs.org/install.sh | sh
# npm install -g socket.io
# npm install -g forever
# npm install -g redis


これでnodeのインストールができました。
それでは、subscribe→emit用にnodeのスクリプトを実装します。
# su - appadmin
$ mkdir -p /home/appadmin/admin/node
$ cd /home/appadmin/admin/node
$ vim admin.js
---

var server = require('http').createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('server connected');
});
server.listen(3001);

var io = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis');
var opts = {host:'10.0.0.200', port:6379};
io.set('store', new RedisStore({redisPub:opts, redisSub:opts, redisClient:opts}));

var redis = require('redis');
var sub = redis.createClient(opts.port, opts.host);
sub.subscribe('greeting');
sub.on("message", function(channel, message){
  io.sockets.emit('msg', {msg:message});
});

---

ポイントは、チャット用のスクリプトと同じようにredisサーバーをRedisStoreとしてstore登録することと、
それとは別にsubscribe用のredisクライアントを作り、「greeting」チャンネルをsubscribeし、受信したメッセージをチャット参加者全員にemitするようにするところです。

これを起動します。
$ forever start admin.js

それでは、実際に動かしてみましょう。
まず、前回と同様、node1とnode2のチャットウィンドウを開いて、適当にチャットしてみます。


それでは、ここで先程の管理画面でPublishボタンを押してみます。



そして、チャット画面をみてみます。


おお!メッセージが表示されました。
これで管理画面からのコントロールも可能になりました。

以上です。

2012年3月4日日曜日

FuelPHPってなんじゃ?(Eclipseとxamppでローカル開発編)

WindowsでFuelPHPを開発する方法をまとめてみました。
前回の記事で作成したように、FuelPHPのアプリケーションリポジトリは自前のGitlabでホストしているものとします。

今回はEclipseを利用します。


Eclipseの最新(Indigo)をインストール

Eclipseの最新のJ2EEをダウンロードします。
AptanaStudio3でも構いません。





アップデートサイトでEgitをインストール

Help > Install New Software...
から
Indigoのアップデートサイト
http://download.eclipse.org/releases/indigo
で
Collaboration > Eclipse Egit
を選択してインストール





SSHの公開鍵を作成

Preference > General > Network Connections > SSH2のGeneralタブでSSHのディレクトリを確認しておきます。
次に「Key Management」タブで「Generate RSA Key..」ボタンをクリックして、出力された内容をコピーしておきます。
また、「Save Private Key...」ボタンを押して、SSHのディレクトリに保存します。




公開鍵をGitlabに登録

GitlabのMy ProfileのKeysで、さきほどコピーした内容を適当なタイトルをつけて保存します。
Githubでも同様です。





Gitのユーザー設定

Preference > Team > Git > ConfigurationのUserSettingタブで、「New Entry...」ボタンをクリックし、以下の内容を入力します。
  • user.name:gitlabで登録したユーザー名
  • user.email:gitlabで登録したメールアドレス




Gitのクローン

EclipseのProject Explorerで右クリック、Import > Import... > Git > Projects From Git を選択します。


リポジトリソースでURIを選択します。


URIに該当するプロジェクトのgit@IPアドレス:Git名を入力します。
すると、Hostやリポジトリパスやユーザー名に自動的に各項目が入力されます。
そのまま「Next」ボタンをクリックします。



ブランチを選択します。ここではmasterしかないのでそのまま「Next」ボタンをクリックします。



自動的にGitのデフォルトディレクトリにダウロードするように設定されています。適宜変更しても構いません。
ここではC:\Users\memorycraft\git\bookstoreに保存されるように設定しました。
また、「Clone Submodules」にチェックを入れておきます。


「Next」ボタンをクリックすると、プロジェクトに関しての画面になるので、「Import existing project」を選択します。もしアプリケーションリポジトリがエクリプスプロジェクトでない場合は、「Import as general project」を選択して、後でPHPプロジェクトに変更するか、「Use the New Project wizard」を選択します。

また、Mac版Eclipseではプロジェクトウィザードを選択すると正常終了しなかったので、あらかじめアプリケーションリポジトリに.projectをコミットしておくとスムースに進むと思います。


「Next」ボタンを押すと、リポジトリに含まれているEclipseプロジェクトが表示されるので、そのまま「Finish」ボタンをクリックして、インポートを完了します。



すると、図のようにFuelPHPアプリがプロジェクトとして登録されているのが確認できました。




XAMPPのインストール

FuelPHPをローカルで実行できるようにするために、以下のリンクからXAMPPをインストールします。
http://www.apachefriends.org/jp/xampp-windows.html


インストールしたらApacheとMySQLを起動しておきます。



シンボリックリンクの作成

FuelPHPはpublicディレクトリ以外がDocumentRoot配下にあることはセキュリティ上好ましくないので、基本はpublicディレクトリのシンボリックリンクをDocumentRoot配下に配置するのですが、ショートカットはApacheからはシンボリックリンクだと認識されません。
そこで、Windowsでシンボリックリンクを作成できるツールを使用します。

コマンドプロンプトを管理者権限で実行します。



DocumentRootに移動し、mklinkコマンドでFuelPHPのpublicディレクトリのシンボリックリンクをbookstoreという名前で作成します。
C:\Windows\system32>cd c:\xampp\htdocs
c:\xampp\htdocs>mklink /d bookstore c:\Users\memorycraft\git\bookstore\public
bookstore <<===>> c:\Users\memorycraft\git\bookstore\public のシンボリック リン
クが作成されました


 すると、GUIでは以下のようにショートカットのようなアイコンでbookstoreという項目が作成されていることがわかります。




FuelのローカルDB設定

DB設定ですが、デフォルトではdevelopmentモードで動作します。
例えばdevelopmentモードのDB設定(fuel/app/config/development/db.php)で、localhostのDBを設定している場合はそのままdevelopmentモードでローカル実行すれば問題ありません。

また、開発サーバーでアプリケーションとは別のホストのDBを参照していて、ローカル実行ではXAMPPのMySQLを使用したい、もしくは、開発サーバーではlocalhostのDBを参照していて、ローカル実行ではXAMPPのDBを使わずに、開発サーバーのMySQLを利用したい場合などは、developmentモードとは別にローカルホスト用の環境設定をする必要があります。
その場合は、以下の手順で設定をおこないます。

  • development/db.phpとは別にlocalhost/db.phpを用意する
  • Windowsの環境設定でFUEL_ENVという名前でlocalhostを登録する
  • XAMPPのhttpd.confでSetEnv FUEL_ENV localhostと設定する

ここでは、developmentモードがlocalhostのDBなので、XAMPPのMySQLを使用することにします。


XAMPPのMySQLでデータベース作成

XAMPPのMySQLにデータベースとユーザーを作成します。
環境変数PATHにxampp¥mysql¥binを設定しておくとMySQLの実行に便利です。

mysql -u root
mysql> create database bookstore;
mysql> GRANT ALL PRIVILEGES ON bookstore.* TO 'bs_user'@localhost IDENTIFIED BY 'bs_pass';



oilコマンド

環境変数PATHにxampp¥phpを設定しておきます。
次に、oilコマンドで、Scaffoldをつくって、migrateしてみます。

cd C:¥Users¥memorycraft¥git¥bookstore
php oil generate scaffold book title:varchar[255] auther_id:int publisher_id:int
php oil refine migrate



ブラウザで確認


おお!無事動きました。



リモートリポジトリにcommit, push

いままで変更した分をローカルのリポジトリにコミットします。
まずプロジェクト自体を選択して、右クリック>Team > Add to Index



すると、変更ファイルがコミット候補になります。
次に、プロジェクトを選択して右クリック>Team > Commit ...


コミットダイアログが表示されるので、コメントを記入して、「Commit」ボタンをクリックします。
するとローカルのリポジトリに変更が反映されます。


次に、リモートリポジトリに反映するには、プロジェクトを選択して、右クリック > Team > Push to Upstreamを選択します。



すると、Gitlabのリポジトリツリーにも、変更が反映されました。



これで、WindowsでのFuelPHPの開発環境が整いました。
今回は以上です。



2012年2月23日木曜日

FuelPHPってなんじゃ?(Capistranoでデプロイ編)

今回はCapistranoでFuelPHPをデプロイしてみたいと思います。

構成は以下のとおりで、すべてEC2インスタンスです。また今回はDBに関しての説明は省きます。

  • repository(git): vvv.vvv.vvv.vvv
  • admin(capistrano): xxx.xxx.xxx.xxx
  • hoge1(web+app): yyy.yyy.yyy.yyy
  • hoge2(web+app): zzz.zzz.zzz.zzz

各インスタンスでのアクセス関係は以下の通りです。
admin→(deploy)→ hoge1,hoge2 → (git clone) → repotitory
└→ (git ls-remote) → repotitory

まず、各インスタンス間でアクセスできるように設定します。
adminからhoge1, hoge2, repositoryへ、またhoge1, hoge2からrepositoryへSSHできるようにセキュリティグループの設定を行なっておきます。

次にadminからhoge1,hoge2, repositoryへアクセスできるよう、adminの公開鍵をhoge1, hoge2, repositoryに保存します。

admin
# su - memorycraft
$ ssh-keygen -t rsa
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAslxvqb3fg7IA8qjrKbLanetaTcoESWznl9BrjmygSlUEaKlaqd5FRM1R4mGgJSHFOVV3rBArvRw1V14FW6QKaG6oVNBd6PVryXVWQufC0eSi+FBhkkdgISXUdpLApdx01ZxmMha9HvGl04Y+bSj0lz8oggfdBJrMAxGiUe3POy9dJA6Hw79FMVhBTcycxQ6aJ5PNxd/jOeiI/Npcwved/s7Y8DFwDTJAIz9uiCzdQrPmrM75gyrwis2I2kmeLANQ+d2pcsM8zZf7kglBTifmcegWTQkCkbu4WFO1BL8/JjaoWj7KIXFlHzstkPyn1duvbCBBmkR0HabbU+zNU4J/hw== memorycraft@ip-10-146-69-156
↑
この出力内容をコピー(1)


GitlabもしくはGithubへ(1)の公開鍵を登録します。GUIがない場合はgitユーザーの~/.ssh/authorized_keysに登録します。



hoge1, hoge2
# su - memorycraft
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/memorycraft/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/memorycraft/.ssh/id_rsa.
Your public key has been saved in /home/memorycraft/.ssh/id_rsa.pub.
The key fingerprint is:
b5:6d:1b:f9:e6:ee:c8:86:a3:a6:27:b8:d0:54:2d:ad memorycraft@ip-10-146-93-65
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|       o         |
|      o o .      |
|     . o . o .   |
|    . E S . =    |
|   o       . +   |
|  . ..     .. o  |
|   .. . o o..+   |
|    ...=.. ooo+  |
+-----------------+
$ vi ~/.ssh/authorized_keys
コピーした(1)をペースト
$ chmod 600 ~/.ssh/authorized_keys

次にhoge1, hoge2からrepositoryにSSHアクセスできるよう、hoge1, hoge2の公開鍵をrepositoryに登録します。

hoge1, hoge2
$ cat ~/.ssh/id_rsa.pub

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzOkvpSWfge0pzSsfbIkVpPZU8xZ+XXoDr+NhWCVVD4iC+F3bh69C90FSGbEDLF1hrgQGcaU6THSmXgozm0uPp0uP2vbjsFgxyklEEDhZrr7kBWIpVx5k2uhzp9xFI1Ijd+xXm1BW1/w1pSvuo0IAvpKMT0Pr5JA5x/1jvQ2JWNSMuZEp19bAQjGA1kgy82BbmX8bQxcnNMKaewiMAT8DrORBFH+yzagvKIcJDhi2/8z5Fp9P0Yxq1clLHEQBnU2g8918Zq5KMkRNnwQEqSrl1KrLVNpuTxh3IdqBXDPxlrdMYDfqdOAfH92f5Mq1cpThJJV2Kv+iej4bkUFcm/CGBQ== memorycraft@ip-10-146-93-65
↑
この出力内容をコピー(2)


capistranoのconfigで登録したリポジトリサーバーにこの内容(2)を登録します。
GitlabやGithubの場合は管理画面のSSH Keysで、gitだけの場合はgitユーザーの~/.ssh/authorized_keysに追加します。



必要なライブラリをインストールしておきます。
# yum install -y make openssh-clients gcc libxml2 libxml2-devel libxslt libxslt-devel python-devel wget readline-devel ncurses-devel gdbm-devel glibc-devel tcl-devel openssl-devel db4-devel byacc httpd gcc-c++ curl-devel openssl-devel zlib-devel httpd-devel apr-devel apr-util-devel sqlite-devel libicu-devel pcre-devel git-core python-setuptools python-devel libicu-devel python-setuptools python-devel libicu-devel git patch libtool php

またホスト登録のyes/noのダイアログを解消しておくため、それぞれ一度sshでログインを試みます。

admin
# su - memorycraft
$ ssh memorycraft@yyy.yyy.yyy.yyy
$ ssh memorycraft@zzz.zzz.zzz.zzz

hoge1, hoge2
#su - memorycraft
$ ssh memorycraft@vvv.vvv.vvv.vvv

ここでcapistrano用にディレクトリを作成し、capistranoディレクトリの中でcapifyコマンド実行します。
capifyはcapistranoのタスクなどを定義するためのひな形を作成するためのコマンドです。

admin
$ mkdir ~/capistrano
$ cd ~/capistrano
$ capify .
$ tree ~/capistrano
capistrano
|-- Capfile
`-- config
    `-- deploy.rb

作成されたCapfileを以下のように修正します。

load 'deploy' if respond_to?(:namespace)
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
load 'config/deploy' # remove this line to skip loading any of the default tasks


またconfig/deploy.rbを以下のように修正します。

set :application, "bookstore" #アプリケーション名
set :user, "memorycraft" #SSHアクセスする際のユーザー
set :use_sudo, false #sudoは使用しない

set :deploy_to, "/home/memorycraft/bookstore" #デプロイ先
set :scm, :git #今回はgitを使用
set :repository, "git@vvv.vvv.vvv.vvv:bookstore.git"  # gitリポジトリ
set :branch, "master" # ブランチはmasterを選択
set :git_enable_submodules, 1 #サブモジュールを使用するか
set :deploy_via, :remote_cache #

role :web, "yyy.yyy.yyy.yyy","zzz.zzz.zzz.zzz"

set :keep_releases, 3

namespace :deploy do
  task :start do ; end
  task :stop do ; end
  task :restart, :roles => :app, :except => { :no_release => true } do
  end
end


ここでcap deploy:setupを行います。
$ cd ~/capistrano
$ cap deploy:setup

するとhoge1, hoge2にデプロイ用のリリース管理ディレクトリが作成されます。capistranoはこのディレクトリを使用してデプロイの世代管理なども行うことができます。

hoge1, hoge2
$ tree bookstore/
bookstore/
├── releases
└── shared
    ├── log
    ├── pids
    └── system

これで準備ができたので、デプロイを行います。

admin
$ cd ~/capistrano
$ cap deploy

するとタスク処理の進捗結果が出力されます。
デフォルトのdeployタスクは、Railsアプリのデプロイを想定しているため、結果には、例えばgitサーバーのEIPの正引き逆引きの不一致やRailsに特化したjavascript, stylesheet, imagesなどのディレクトリの検索などのエラーが表示されていますが、最終行にfailedという項目が表示されていなければ全体的には成功しているようです。

hoge1, hoge2
ここではhttpdでホストするために、/var/www/html/bookstoreにリンクします。
capistranoはcurrentというディレクトリに最新のデプロイを保存しているので、そこをリンクターゲットにします。
# cd /home
# chmod 755 /home/memorycraft
# cd /var/www/html
# ln -s /home/memorycraft/bookstore/current/public bookstore

httpdでホストします。
# /etc/init.d/htpd start

hoge1,hoge2をブラウザ画面を見てみます。
ともにFuelPHPがホストされているのがわかります。





次に、開発環境でfuel/app/views/welcome/index.phpの中身を変えてpushします。
$ cd ~/bookstore
$ vi fuel/app/views/welcome/index.php
<h1>Welcome!</h1>
↓
<h1>Welcome!(version 2)</h1>

$ git add .
$ git commit -m 'change welcome message to version 2'
$ git push origin master


そして、deployします。
$ cd ~/capistrano
$ cap deploy

ブラウザで確認します。


きちんとデプロイされています。

次にこれをひとつ前のバージョンに戻してみます。
デプロイを前のバージョンに戻すには、deploy:rollbackタスクを実行します。

$ ~/capistrano
$ cap deploy:rollback



おお!、もとに戻っています!
これで、アプリケーションのデプロイコントロールがだいぶ楽になりました。
次回はcapistranoでマイグレーションを行いたいと思います。

今回は以上です。

2012年2月21日火曜日

FuelPHPってなんじゃ?(Git管理編)

以前FuelPHPのインストールと簡単なアプリの作成を行いましたが、
今回はFuelPHPを使ったアプリケーションを自分の管理するGitマスターリポジトリに登録してみたいと思います。

前回のGitlabのプロジェクト登録編では、すでにリポジトリに一度pushしました。そのように未取得の変更がマスタリポジトリ上に存在する場合はマージしてからpushする必要があります。その説明は別の機会にするとして、今回はリポジトリがまだ空の状態という想定で行います。

まずはoilコマンドで適当にプロジェクトを作成します。
oil create bookstore
cd bookstore/

oil createコマンドはFuelPHPを含めたアプリケーションひな形をGithubからcloneするので、Githubのgit管理下にあるため、まず.gitから始まるファイルを削除します。
rm -rf .git*

.gitが削除されたのでGithubリポジトリ管理外となりました。
ここで、あらためて自分のgit管理下におくため初期化を行います。
git init

また、FuelPHPのfuel/coreとfuel/packagesのみをGithubのFuelPHPのgit管理下に置き、このアプリケーションのサブモジュールとして登録します。
git submodule add git://github.com/fuel/core.git fuel/core/
git submodule add git://github.com/fuel/oil.git fuel/packages/oil
git submodule add git://github.com/fuel/auth.git fuel/packages/auth
git submodule add git://github.com/fuel/parser.git fuel/packages/parser
git submodule add git://github.com/fuel/orm.git fuel/packages/orm
git submodule add git://github.com/fuel/email.git fuel/packages/email

ここまで行ったらアプリケーションツリー全体を次回コミットに含めるためステージに上げます。
git add .

ここでは前回のGitlabサーバーをマスターリポジトリとするのでGitlabのユーザー情報を登録します。
git config --global user.name "miura"
git config --global user.email  "miura@cloudpack.jp"

まずローカルリポジトリにコミットします。
git commit -m 'reinitialize with submoduled'

ここで、Gitlabサーバーのgitリポジトリをoriginという名前でリポジトリ登録します。
この時点でgitサーバー側ではGitlabでプロジェクト登録してあるか、Gitlabを使用していない場合はgitのベアリポジトリを作成してある必要があります。
git remote add origin git@176.34.51.75:bookstore.git

今登録したoriginリポジトリのmasterブランチへpushします。
git push -u origin master

それでは反映しているかGitlabで確認してみます。



うまく登録されているようです。
これで、FuelPHPを利用したチーム開発もできるようになります。

今回は以上です。

2011年12月21日水曜日

FuelPHPってなんじゃ?(インストール編)

PHPのフレームワークは数々ありますが、どれも巨大すぎたり機能が足りなすぎたり、丁度よいモダンなフレームワークはなかなかありませんでした。
そこに登場したCodeIgniterは機能が豊富ですっきりとしている良質なモダンフレームワークとして人気なようです。

今回のFuelPHPはCodeIgniterの開発者なども参加しており、CodeIgniterや他のプロダクトのいいとこどりの期待のフレームワークだそうです。


主な特徴は
・MVC
・HMVC(画面要素の複数のMVCモジュールをひとつに纏めて1画面をつくれる)
・モジュール拡張可能
・セキュリティ対応(出力エンコーディング、入力フィルタ、XSS、CSRF、SQLインジェクション)
・豊富なコマンドライン
・ORMとSQLどちらもOK
・認証機構つき
・複数のビューテンプレート(smartyなど)に対応
・scaffoldできる
・migrateできる
・PHPUnitできる

こう見ると分かる通り、Railsから多くの影響を受けているようです。

では、さっそく触ってみます。

ここでは、
ドキュメントルートを/var/www/html
アプリベースを/opt/cloudpack/
として、blogアプリをつくってみたいと思います。


インストール
$ curl get.fuelphp.com/oil | sh
$ cd /opt/cloudpack/
$ oil create blog
これだけでひと通りのセットが揃います。

次にセキュリティのためにpublicフォルダだけをDocumentRootに移動します。
# mv blog/public /var/www/html/blog

そして、index.phpに書いてある、アプリパスを変更します。
# vi /var/www/html/blog/index.php
~
/**
 * Path to the application directory.
 */
define('APPPATH', realpath('/opt/cloudpack/blog/fuel/app/').DIRECTORY_SEPARATOR);

/**
 * Path to the default packages directory.
 */
define('PKGPATH', realpath('/opt/cloudpack/blog/fuel/packages/').DIRECTORY_SEPARATOR);

/**
 * The path to the framework core.
 */
define('COREPATH', realpath('/opt/cloudpack/blog/fuel/core/').DIRECTORY_SEPARATOR);


それでは画面をみてみます。



おー、Welcome画面が表示されてました。
とりあえず問題ないようです。



Scaffolding

次にデータベースの設定をします。(mysqlサーバーがインストールされ起動していることを前提とします)

vi /opt/cloudpack/blog/fuel/app/config/db.php
'default' => array(
                'type'        => 'mysql',
                'connection'  => array(
                        'hostname' => 'localhost',
                        'port' => '8889',
                        'database' => 'blog',
                        'username' => 'memorycraft',
                        'password' => '*********',
                        'persistent' => false,
                ),
                'identifier'   => '`',
                'table_prefix' => '',
                'charset'      => 'utf8',
                'caching'      => false,
                'profiling'    => false,
        ),

vi /opt/cloudpack/blog/fuel/app/config/development/db.php
'default' => array(
                'connection'  => array(
                        'dsn'        => 'mysql:host=localhost;dbname=blog',
                        'username'   => 'memorycraft',
                        'password'   => '***********',
                ),
        ),


mysqlでデータベースとユーザーを作成します。
mysql> create database blog;
mysql> GRANT ALL PRIVILEGES ON blog.* TO 'memorycraft@localhost'  IDENTIFIED BY '*******';


次に、scaffoldをつくってみます。
$ cd /opt/cloudpack/blog/
$ oil g scaffold blog title:string body:string tags:string created_at:date

そしてmigrate
$ oil refine migrate

これで、データベースblogにblogテーブルが作成されました。
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| blog               |
| mysql              |
| performance_schema |
| test               |
+--------------------+
5 rows in set (0.00 sec)


それでは画面をみてみます。



ブログの管理画面ができています。
入力して

登録すると、、、、



おおー!一覧に保存内容がひょうじされました。
railsと同じノリですね。

あくまでひな形ですが、このくらいのスピードでDB、画面ができあがります。
PHPはもうこれでいいんじゃないかなと思うくらいの可能性を感じます。

これから使い込んでみたいと思います。
以上です。