ラベル Google App Engine for PHP の投稿を表示しています。 すべての投稿を表示
ラベル Google App Engine for PHP の投稿を表示しています。 すべての投稿を表示

2013年7月3日水曜日

39.Google App Engine for PHPでCakePHPを動かしてみた

このエントリーをはてなブックマークに追加




Ryo Yamasaki(@vierjp)です。

Google I/O 後に Limited Preview のアカウントをもらってから、
これまでGoogle App Engine for PHPに関して以下のブログを書いてきました。

28.Google App Engine for PHP (GAE/PHP) を早速試してみた
29.Google App Engine for PHPでWordPressを動かしてみた
30.Google App Engine for PHPにおけるポータビリティを考える
36.Google App Engine for PHPでWordPressを運用するためのプラグインが登場

私がPHP素人ということもありこれまでは主に「既存のPHPアプリをApp Engineで動かす」内容でしたが、
今回は「新規にアプリを作成する」ケースを想定して
GAE/PHP上でCakePHPというフレームワークを動かしてみようと思います



◯GAE/PHPで動かすフレームワークの条件

公式のIssue Trackerでのフレームワークについてのやりとりの中で、
Googleのエンジニアの以下の書き込みがありました。(本当はもっと細かいけど概要はこんな感じ)

1.GAEのファイルシステムはReadOnlyなので、一時ファイルはGoogle Cloud Storageに書き込む必要がある。
2.ロギングはsyslog関数を使っていること。(ファイルシステム内の指定ディレクトリへの出力はできない)

一つ目は#DevFestでも話にあった「ローカルに一時ファイルを書き出す既存のPHPフレームワークを動かすのは厳しいのではないか」と同じ内容ですね。

期待は薄かったのですが、今時フレームワークが使えない開発も厳しいと思うのでダメ元で試してみました。
PHPのフレームワークについて軽くググったところ、日本ではCakePHPが人気らしいのでCakePHPにしました。

結論から書くと、
「適切に設定をすることでApp Engine上でCakePHPを動作させることができた」
と言って良いのではないかと思います。 (全機能を試したわけではありませんが)



◯ローカル環境の構築

実はこれまでGAE/PHPに関してローカル環境で動かしたことがなかったのですが、
さすがに今回はエディタと本番環境でのテストだけではきつそうだったのでローカルに開発環境を構築しました。
以下はローカル開発環境のざっくりとした構築手順です。

・MySQLのインストール
Mac OS へのMySQLのインストール方法
こちらの手順に沿って行いました。

・MacPortsのインストール
MacPorts公式

冒頭の「“pkg” installers for Mountain Lion」のリンクからダウンロードできます。
Xcodeが入っている環境でインストール時にエラーが発生する場合はこちら

・PHPのインストール
App Engine公式ドキュメントより
sudo /opt/local/bin/port install php54-cgi php54-APC php54-calendar \
    php54-exif php54-gd php54-mysql php54-oauth php54-openssl php54-soap \
    php54-xdebug php54-xsl

・PHPStormのインストール (任意)
PHP IDE :: JetBrains PhpStorm (ダウンロードページ)
Getting Started with PhpStorm as Google App Engine PHP IDE (使い方)
最近人気のPHP用IDEだそうです。
最初から GAE/PHP に対応するプラグインが組み込まれていて、IDE上で GAE/PHP のデバッグ・デプロイが可能です。
有料ですが、最初の一ヶ月は無料で利用できます。



◯初期設定

・アプリのルートディレクトリを作成する
今回は「CakePHPTest3」という名前にしました。

・CakePHPのサイトから最新の安定版(現時点で2.3.6)のzipをダウンロードして展開する



・「CakePHPTest3」以下に全てコピーする

・app.yamlの設定

/app.yaml
application: [アプリケーションID]
version: [任意のバージョン名]
runtime: php
api_version: 1
threadsafe: true

handlers:
- url: /css
  static_dir: app/webroot/css

- url: /js
  static_dir: app/webroot/js

- url: /img
  static_dir: app/webroot/img

- url: /favicon.ico
  static_files: app/webroot/favicon.ico
  upload: app/webroot/favicon.ico

- url: /.*
  script: app/webroot/index.php

* 通常CakePHPでは apache の mod_rewrite でルーティングの設定を行うようですが、GAE/PHPではapp.yamlで行います。


・デフォルトタイムゾーンの設定
ルートディレクトリ直下に「php.ini」を作成して以下の1行を記述します。

/php.ini
date.timezone = Asia/Tokyo


・Security.salt と Security.cipherSeed の変更

/app/Config/core.php
/**
 * A random string used in security hashing methods.
 */
 Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');

/**
 * A random numeric string (digits only) used to encrypt/decrypt strings.
 */
 Configure::write('Security.cipherSeed', '76859309657453542496749683645');

上記はデフォルト値です。
この2つのセキュリティ的な値を適当に変更します。
「Security.salt」は文字列、「Security.cipherSeed」は数値です。


・App EngineのProduction環境とローカル環境で分岐するための処理を作成

データベースの接続先の切り替え等で使うのでまずはこれを作ります。

/app/Utility/AppEngine.php
<?php

class AppEngine {
    public static function isProduction() {
        if(isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'],'Google App Engine') !== false) {
            //syslog(LOG_WARNING, 'is Production.');
            return true;
        }else{
            //syslog(LOG_WARNING, 'is DevServer.');
            return false;
        }
    }
}

* 「$_SERVER['SERVER_SOFTWARE']」の値はProduction環境では「Google App Engine/1.8.1」、ローカル環境では「Development/2.0」になります。


・作成したUtilityを利用できるようにcore.phpの冒頭に下記を記述
/app/Config/core.php
App::uses('AppEngine', 'Utility');



・データベースの設定
最初から存在する「database.php.default」をコピーして「database.php」という名前で保存し、
下記の内容に書き換えます。

/app/Config/database.php
<?php
class DATABASE_CONFIG {

 public $default = NULL;

    public $prod = array(
        'datasource' => 'Database/Mysql',
        'persistent' => false,
        'unix_socket' => '/cloudsql/******:*****',
        'login' => 'cake_user',
        'password' => '********',
        'database' => 'cake_db',
        'encoding' => 'utf8',
    );

    public $dev = array(
        'datasource' => 'Database/Mysql',
        'persistent' => false,
        'host' => 'localhost',
        'login' => 'cake_user',
        'password' => '********',
        'database' => 'cake_db',
        'encoding' => 'utf8',
    );

 public $test = array(
  'datasource' => 'Database/Mysql',
  'persistent' => false,
  'host' => 'localhost',
  'login' => 'user',
  'password' => 'password',
  'database' => 'test_database_name',
  'encoding' => 'utf8',
 );

    function __construct(){
        if(AppEngine::isProduction()) {
            $this->default = $this->prod;
        }else{
            $this->default = $this->dev;
        }
    }
}


・ローカルのMySQLにDBを作成する

CREATE DATABASE IF NOT EXISTS cake_db;
CREATE USER 'cake_user'@'localhost' IDENTIFIED BY '********';
GRANT ALL PRIVILEGES ON cake_db.* TO 'cake_user'@'localhost';


・dev_serverを起動してブラウザでアクセスします

PHPStormから起動する場合、
初回起動時の設定画面でPHPのPathを入力する必要があります。
上記手順でMacPortsからインストールした場合は、
「Path to php-cgi executable」に「/opt/local/bin/php-cgi54」と設定すればOKです。


デフォルトのトップページはCakePHPの設定を確認するためのページが開きます。
(ここまでの手順もこのページにアクセスしながら設定を変えていくと、変える度に警告が消えていきます)

dev_serverは制限が緩く「ファイルシステムへの書き込み」ができてしまうので、下記画像のように問題なくページが開きます。

*制限が緩いのでローカル環境では「Your tmp directory is writable.」と表示されます。


しかし、App Engine上では「ファイルシステムへの書き込み」ができないためエラーになってしまいます。

デフォルトのCakePHPは下記画像のように、「cache」や「Log」を「tmp」ディレクトリ以下に出力します。
(「sessions」「tests」はデフォルト設定ではファイルが書き込まれないので問題なし)



*App EngineでPHPの「$_SESSION」を使った場合にはMemcacheに書き込むようになっています。

初期設定のままのCakePHPの場合、予想どおり一時ディレクトリへのキャッシュの書き込みでエラーになります。
また、ログもファイルに出力する設定なので、App Engineのログには出力されません。

GAE/PHPでは「ファイルI/OはCloud Storageに対して行うべし」というのが基本のようなので、
当初一時ディレクトリをCloud Storageにするため、index.phpに下記を追加しました。
define('TMP', "gs://[bucket名]/");

しかし、これは期待通りに動作しませんでした。
GAE/PHP上で「gs://〜〜」のPathを指定してファイルI/O関連の操作をすると透過的にCloud Storageの読み書きをするWrapperに処理が移譲されるのですが、
ディレクトリの存在チェック処理で「ディレクトリが存在しない」という扱いでエラーになってしまいます。
(事前にCloud Storage上にFolderを作成しておいてもダメ)


* CakePHPに限らず、既存のアプリのファイル出力先をCloud Storageに変更しただけではこういったケースは他にもあるかもしれません。

Issue Trackerでのやりとりに書かれていた通り一時ファイルとログ出力が問題になりました。
また、単純に書き込み先のPathをCloud Storageにすれば良い、というわけにもいかないようです。
次項からこれらについて対処していきます。



◯cacheの設定を変更する

前述の通りデフォルト設定のCakePHPは一時データを「tmp」ディレクトリ以下にファイルとして保存しますが、
一時データの保存領域としてMemcacheやAPC(後述)を利用するためのコードも用意されています。
また、パフォーマンス的にもMemcacheやAPCの方がファイルキャッシュより高速とされているそうです。

・bootstrap.phpの冒頭でキャッシュ設定を変更する

/app/Config/bootstrap.php
// Setup a 'default' cache configuration for use in the application.
//Cache::config('default', array('engine' => 'File'));
if(AppEngine::isProduction()) {
    Cache::config('default', array('engine' => 'Apc'));
}else{
    Cache::config('default', array('engine' => 'File'));
}

・core.phpの最後の方でキャッシュ設定を変更する
/app/Config/core.php
//$engine = 'File';
// in Production Apc, in Devserver File
if(AppEngine::isProduction()) {
    $engine = 'Apc';
}else{
    $engine = 'File';
}


ここではAPCを使うように変更しました。
* ローカル環境でもAPCを使えますが、開発時にはtmpディレクトリ内にファイルそのものが見えた方がわかりやすいかも、ということで分岐してみました。

再度デフォルトのトップページを開くと以下のようになります。

上から3つ目の項目が「The ApcEngine is being used for core caching.」になっているのがポイント。
前述のローカル環境で実行した結果のキャプチャと比べるとわかりますが、
変更前は「The FileEngine is being used for core caching.」となっていました。

*「Your tmp directory is NOT writable.」と警告が出ていますが、tmpディレクトリを使用しないので問題ありません。


・APC (Alternative PHP Cache)について
基本的には「PHPの中間コードのキャッシュや最適化を行う拡張モジュール」ですが、
MemcacheのようなKeyValue型のメモリキャッシュとしても使えるそうです。

APCはMemcacheと比べると以下のような特性があります。
 ・サーバーのローカルメモリを利用
 ・Memcacheより読み書きが高速
 ・キャッシュはサーバー間で共有されない
 ・サーバーを再起動するとキャッシュは消える

App Engineはアプリのバージョン名毎にインスタンスが別なので、
デプロイ後に古いコードのキャッシュが残ってしまうことはありません。
(同じバージョン名に対して上書きする形でデプロイした場合には一度インスタンスが全て停止するのでやはりキャッシュはクリアされます)


・APCではなくMemcacheを使う場合についても考えてみる
 ・App Engineでは頻繁にサーバーの起動・停止をするのが半ば前提なので、Memcacheの方がキャッシュを共有できる点では効率は良さそう。
 ・この場合デプロイ時にキャッシュをクリアしないと最新のコードや設定と内容がずれるだろうからそのタイミングでMemcacheのクリアが必要と思われる。
 ・複数バージョンを同時に操作した場合に競合しそう。(キャッシュのKeyのPrefixにバージョン名を追加する等で対応はできそう)
 ・データ量の多いサイトでDBのデータをMemcacheにキャッシュしている場合にはMemcacheが枯渇してCakePHPのキャッシュが活きづらい状況はあるかも。


今回は無難そうなAPCを使いましたが、
APCとMemcacheのどちらが良いかは実際に様々な条件で比較してみなければなんとも言えません。
システムの性質によって最適なキャッシュ方法が変わる可能性もありそうです。



◯ログの設定を変更する

Issue Trackerでのやりとりにある通り、ログの出力をsyslog関数を使って行うことでApp  Engineのログに記録する事ができます。

CakePHPではログの出力先(出力方法)も設定で変更できます。

・Syslogに出力するためのコードを取得する
最新の安定版(2.3.6)にはログをSyslogに出力するコードは含まれていませんが、
開発中のバージョンには「lib/Cake/Log/Engine/SyslogLog.php」という、Syslogに出力するためのコードが含まれています。

このファイルをGithubにあるCakePHP 2.4のbranchから取得して「app/Log/Engine/SyslogLog.php」にコピーします。(「app/Log/Engine/」ディレクトリも作成)

・bootstrap.phpを修正する

/app/Config/bootstrap.php
/**
 * Configures default file logging options
 */
//App::uses('CakeLog', 'Log');
//CakeLog::config('debug', array(
// 'engine' => 'FileLog',
// 'types' => array('notice', 'info', 'debug'),
// 'file' => 'debug',
//));
//CakeLog::config('error', array(
// 'engine' => 'FileLog',
// 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
// 'file' => 'error',
//));
if(AppEngine::isProduction()) {
    $engine = 'SyslogLog';
}else{
    $engine = 'FileLog';
}

App::uses('CakeLog', 'Log');
CakeLog::config('debug', array(
 'engine' => $engine,
 'types' => array('notice', 'info', 'debug'),
 'file' => 'debug',
));
CakeLog::config('error', array(
 'engine' => $engine,
 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
 'file' => 'error',
));

・php.iniの修正
この状態では以下の警告が出力されるので、php.iniに追記します
PHP Warning:  php_sapi_name() has been disabled for security reasons. It can be re-enabled by adding it to the google_app_engine.enable_functions ini variable in your applications php.ini

/php.ini
google_app_engine.enable_functions = "php_sapi_name, gc_enabled"
date.timezone = Asia/Tokyo

* 一行目を追加しています。

これでApp Engineのログにシステムログが出力されるようになります。



◯DebugKit pluginの設定をする

後回しにした「DebugKit plugin」も少し修正することで利用可能です。

・DebugiKitをGitリポジトリから取得する

・/app/Plugin/DebugKit ディレクトリを作成して、取得したファイルをコピー

・bootstrap.php に以下の記述を追加
/app/Config/bootstrap.php
CakePlugin::load('DebugKit');


・AppControlleを修正する
/app/Controller/AppController.php
class AppController extends Controller {
    public $components = array('DebugKit.Toolbar'); // ←この行を追加する
}

・ToolbarComponentの修正

このコードの391行目でCacheにFile Engineを使ってしまっているのでこちらもAPCに変えます。
/app/Plugin/DebugKit/Controller/Component/ToolbarComponent.php
 protected function _createCacheConfig() {
        if (Configure::read('Cache.disable') !== true) {
            $cache = array(
                'duration' => $this->cacheDuration,
                'engine' => 'Apc',
                'path' => CACHE
            );
            if (isset($this->settings['cache'])) {
                $cache = array_merge($cache, $this->settings['cache']);
            }
            Cache::config('debug_kit', $cache);
        }
    }


・app.yaml に以下を追記 (最後の「- url: /.*」より先に定義する)

/app.yaml
- url: /debug_kit/css
  static_dir: app/Plugin/DebugKit/webroot/css

- url: /debug_kit/js
  static_dir: app/Plugin/DebugKit/webroot/js

- url: /debug_kit/img
  static_dir: app/Plugin/DebugKit/webroot/img





◯環境変数の問題を解決する

一部の環境変数の値を期待どおりに取得できずに画面遷移において発生する問題を解決するため、以下の記述を追加する。

Issue Trackerにも似たような内容が挙がっています。
関連: Issue 9369: PHP $_SERVER['SCRIPT_NAME'] variable does not match the expected implementation

・index.php の最後の方に2行追加する
/app/webroot/index.php
App::uses('Dispatcher', 'Routing');

// ここから
$_SERVER['SCRIPT_FILENAME'] = __FILE__;
$_SERVER['PHP_SELF'] = 'webroot/index.php';
// ここまで

$Dispatcher = new Dispatcher();
$Dispatcher->dispatch(
 new CakeRequest(),
 new CakeResponse()
);

* そのうちIssueが修正されてこの部分の記述は不要になるかもしれません。




◯Sessionの保存場所を変える

以前に「30.Google App Engine for PHPにおけるポータビリティを考える」に書いたとおり、
現状GAE/PHPでは「$_SESSION」を使った場合のSessionデータの格納先は「Memcache」となっています。
そのためドキュメントには
・Memcacheのデータはクリアされるかもしれないのでそれによってセッション情報が失われる可能性がある。  
・長い期間Sessionを維持したければ、Cloud SQL等にセッション情報を保存した方が良い。
と書かれています。
この手順をしなくても一応CakePHPは動作しますが、ついでにこの対策もしてみます。


◯セッションをCloudSQLに保存する場合

CakePHPには「Sessionをデータベースに書き込むためのプログラム」が付属しているので、
DBにテーブルを用意した上でこの仕組みを使うようにCakePHPの設定を変更するだけで、
Sessionデータの格納場所を変更することができます。

・DBにSession格納用テーブルを作成する
USE cake_db;
CREATE TABLE IF NOT EXISTS cake_sessions (
  id varchar(255) NOT NULL,
  data text NOT NULL,
  expires int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

・core.phpを修正する
/app/Config/core.php
// Configure::write('Session', array(
//  'defaults' => 'php'
// ));

Configure::write('Session', array(
    'defaults' => 'database'
));


AppController.php を修正して'Session'を追加する
/app/Controller/AppController.php
public $components = array('Session','DebugKit.Toolbar');


◯MemcacheとCloudSQLを併用する場合

・前述の「Cloud SQLに保存する場合」と同様にテーブルを作成する

・「キャッシュとDBを併用するためのクラス」を作成する

/app/Model/Datasource/Session/ComboSession.php
<?php
App::uses('DatabaseSession', 'Model/Datasource/Session');

class ComboSession extends DatabaseSession implements CakeSessionHandlerInterface {
    public $cacheKey;

    public function __construct() {
        CakeLog::write('debug',"ComboSession#__construct");
        $this->cacheKey = Configure::read('Session.handler.cache');
        parent::__construct();
    }

    // セッションからデータ読み込み
    public function read($id) {
        CakeLog::write('debug',"ComboSession#read id:".$id);
        $result = Cache::read($id, $this->cacheKey);
        if ($result) {
            CakeLog::write('debug',"ComboSession#read result from cache result:".$result);
            return $result;
        }
        CakeLog::write('debug',"ComboSession#read result from db result:".$result);
        return parent::read($id);
    }

    // セッションへデータ書き込み
    public function write($id, $data) {
        CakeLog::write('debug',"ComboSession#write id:".$id." data:".$data);
        $result = Cache::write($id, $data, $this->cacheKey);
        CakeLog::write('debug',"ComboSession#write cache id:".$id);
        if ($result) {
            CakeLog::write('debug',"ComboSession#write db id:".$id);
            return parent::write($id, $data);
        }
        CakeLog::write('error',"ComboSession#write failed to write session:".$id);
        return false;
    }

    // セッションの破棄
    public function destroy($id) {
        CakeLog::write('debug',"ComboSession#destroy id:".$id);
        $result = Cache::delete($id, $this->cacheKey);
        if ($result) {
            return parent::destroy($id);
        }
        return false;
    }

    // 期限切れセッションの削除
    public function gc($expires = null) {
        return Cache::gc($this->cacheKey) && parent::gc($expires);
    }
}

・core.phpを修正する
/app/Config/core.php
// Configure::write('Session', array(
//  'defaults' => 'php'
// ));
    Configure::write('Session', array(
        'defaults' => 'database',
        'handler' => array(
            'engine' => 'ComboSession',
            'model' => 'Session',
            'cache' => 'sessionCache'
        )
    ));

    // セッション用Memcache設定の定義
    Cache::config('sessionCache',array(
        "engine" => "Memcache",
        "duration"=> 3600,
        "probability"=> 100,
        "compress" => false,
    ));

・前述の「Cloud SQLに保存する場合」と同様にAppController を修正して'Session'を追加する

コードは公式のサンプルに検証用のログを追加しただけです。
参照時にMemcacheにキャッシュがあればそれを利用し無ければDBを参照する、という処理になっているので、
パフォーマンスの向上と課金額の低下を期待できます。

公式のサンプルではAPCとDBの組み合わせにしていますが、
複数のサーバーが起動するのが(ほぼ)前提のApp EngineではWebサーバーのローカルメモリに書き込むAPCでセッション情報をキャッシュするのはまずいでしょうから、
MemcacheとDBの組み合わせにしました。
(スティッキーセッションではないのでどこのサーバーにリクエストが振り分けられるかわからない、よって全てのWebサーバーが共有する領域にキャッシュする必要がある)

キャッシュポリシーは名前をつけて複数設定できるので、セッションハンドラ専用のMemcacheの設定を追加してそれを利用しています。



◯ドットインストールのPHP入門に沿ってアプリを作ってみる

「一覧・登録・編集・削除」機能を持つシンプルなCRUD処理を行うWebアプリです。


ドットインストールの動画に沿って21回目の機能まで作って動作しました。
(その後のAjaxやコメント機能の追加はフレームワークの動作確認としては必要ないかな、という判断でここまで。ていうか力尽きました)

* このサンプルでは登録・更新・削除後に表示するメッセージの管理にSessionを使っています
 「setFlash」という関数を使った際にCakePHPが内部的に
 1.登録・更新・削除処理時にSessionにメッセージをセット
 2.一覧ページへリダイレクト
 3.Sessionからメッセージを取得して表示、Sessionから削除
 という処理をしているようです。


ドットインストールのPHP入門に沿って作ったサンプルアプリを以下に配置しました。
自由に読み書きしてもらって構いません。(投稿内容は常識の範囲でお願いします^-^;)
* 2013/11/10 追記 Cloud SQLの課金が地味に痛いので停止しました。

今回のサンプルアプリのコードはまるごとGithubに公開してあります。
https://github.com/vierjp/CakePHPTest3

* core.phpの「Security.salt と Security.cipherSeed」、database.phpの「接続先とパスワード」は伏せているので動作させる場合には書き換えてください。



◯まとめ

ドットインストールの動画の解説はおそらくCakePHPでのオーソドックスなCRUD処理のコーディング方法に沿っていると思いますので、
このサンプルの動作確認をもって「CakePHPが動いた」と言ってもいいんじゃないでしょうか。
もちろん全機能試したわけではないので「完全に動く」と断言まではできないのですが。

当初はフレームワークの選択肢が少ないか最悪利用が難しいのではと考えていただけに、
有名なフレームワークが動作したことでGAE/PHP上で動作させるアプリを新規に開発するにあたっての懸念が一つ減ったように感じています。

ついでに#Devfestの時には「mbstring が使えない」件も懸念されていましたが、
Google App Engine Ver.1.8.1のリリース時にmcrypt、iconv と一緒にこれも追加されています。

GAE/PHPでは拡張モジュールを自分で追加することはできませんが、
mbstringmcrypticonv は Issue Tracker に挙げられた要望が取り入れられた結果としてGAE/PHPに標準で導入されました。
「多くの人にとって価値があるような汎用的なモジュール」であれば、
要望を挙げることによって追加されるかもしれません。


今回試してみた感想として、
・JavaでDIをするように簡単にI/Oの対象を切り替える仕組みを最初から持っているCakePHPは良い
・ちょっとググれば日本語の情報が大量に見つかるという点でPHPという言語は良い(情報の得やすさはJava以上かも?)
・ローカル環境は制限が緩いのでフレームワークの基本動作や既存アプリは実際にデプロイして動作確認した方が良い
と思いました。


余談ですが、公式の Issue Trackerでのフレームワークや拡張モジュール関連の話題としては、
Phalcon Framework というCの拡張モジュールで書かれたPHP用の超高速フレームワークを入れてくれ」
というスレッドが盛り上がっているようです。
(現時点でスターの数はダントツ。盛り上がりすぎたのかコメント禁止ステータスになっていますがw)



◯参考リンク

Installing the PHP SDK on Mac OS X - Google App Engine — Google Developers
CakePHPインストール - CakePHPの使い方
Ayman R. Bedair: Google App Engine
Re: Anyone tried installing CakePHP on Google App Engine (since PHP now support)
CakePHP DebugKit の導入手順
APCとmemcachedの比較 : アシアルブログ
「PHP」の「PECL::APC」をキーバリュー型のメモリキャッシュとして使う。 – FlatLabs
セッション — CakePHP Cookbook v2.x documentation
[ステップアップ! CakePHP] キャッシュに memcached を使う | バシャログ。
CakePHP入門 (全32回) - プログラミングならドットインストール
Issue 9336:Install the Phalcon Framework PHP extension




このエントリーをはてなブックマークに追加

2013年6月14日金曜日

36.Google App Engine for PHPでWordPressを運用するためのプラグインが登場

このエントリーをはてなブックマークに追加


Ryo Yamasaki(@vierjp)です。

Google App Engine for PHP用のWordPressプラグインが公開されました。
WordPress › Google App Engine for WordPress « WordPress Plugins

このプラグインを追加することで、以下の改善がなされます。
ファイルのアップロードが可能になる
メールの送信が可能になる
・問題のあった一部のUIが修正される

以前に「Blog @vierjp : 29.Google App Engine for PHPでWordPressを動かしてみた」に書いた、
「画像のアップロードができない問題」が解決されるそうです。

これは素晴らしい!というわけで早速試してみました。



◯プラグインの導入

WorkPress本体の導入方法は
Blog @vierjp : 29.Google App Engine for PHPでWordPressを動かしてみた」を参照してください。

WordPress本体の導入までできていればプラグインの追加はとても簡単です。
1.「WordPress.org」からプラグインをダウンロードする

2. zipを展開する

3.展開したディレクトリ「google-app-engine」を「/wp-content/plugins/」に配置する

4.デプロイする
/Users/User/appengine_php_1_8_0/appcfg.py update -R --runtime=php . \
--noauth_local_webserver --oauth2

5.左のメニューからプラグイン一覧画面を開いて「Google App Engine for WordPress」を「有効化」する



6.「Settings」で「メールアドレス」と「ファイルを保存するGoogle Cloud Storageのbucket名」を設定する


◯画像をアップロードしてみる

以前は管理画面でアップロードする際にエラーになっていましたが、今度は問題なくアップロードできました。




・画像を追加して公開したエントリーがこれ。



実際にGoogle App Engine 上で動作しているWordPressは以下のURLで閲覧できます。
「WordPress on Google App Engine for PHP by vierjp」
* 2013/11/10 追記 Cloud SQLの課金が地味に痛いので停止しました。

修正はかなり面倒そうに思っていましたが、
本体のコードに手を入れずプラグインの追加だけで対応できてしまうのですね。
WordPressすごい。


これでApp Engine 上でWordPressを運用することが現実的になったのではないでしょうか。




このエントリーをはてなブックマークに追加

2013年5月24日金曜日

30.Google App Engine for PHPにおけるポータビリティを考える

このエントリーをはてなブックマークに追加


Ryo Yamasaki(@vierjp)です。

Google App Engine for PHPでオンプレミス向けのPHP既存アプリはどこまで動くか、
動かす際には何に注意すべきか、「前回行ったWordPressの動作確認」と「公式ドキュメント」を読んだまとめです。


GAE/PHPに関しては
・DatastoreではなくCloudSQLを推していること
・MemcacheのStubの関数の存在
・ドキュメントにWordPressの設置方法について書かれていること
などを見るに、
「既存アプリをそのままAppEngineで動かす」というケースをこれまでよりも強く意識しているように感じました。

これまでAppEngineはこのケースにおいて他のクラウドに遅れを取っていたように思いますが、
(PaaSではWindows Azureが早かった印象)
Cloud SQLやCloud Storage等の関連サービスの充実に伴い現実的になってきたと感じています。

その上でPHPの既存アプリをAppEngineに載せる際に問題になりそうな事を考えながらドキュメントを読んでみました。
と言ってもPHP素人なので考慮しきれていない事は多々あると思いますが。(わかりやすい予防線)



◯File入出力(Google Cloud Storage との連携)


・プログラム内で読み書きしたい場合

AppEngineはその制限上、ローカルのファイルシステムへのアクセスができません。
よって、ファイルの入出力は全てCloud Storageに対して行う必要があります。

・書き込み

$options = [ "gs" => [ "Content-Type" => "text/plain" ]];
$ctx = stream_context_create($options);
file_put_contents("gs://my_bucket/hello.txt", "Hello", 0, $ctx);

・書き込み(Stream)

$fp = fopen("gs://my_bucket/some_file.txt", "w");
fwrite($fp, "Hello");
fclose($fp);

関数的にはPHP標準のものだと思いますが、
Pathの指定方法がGoogle Cloud Storage特有なので
アプリ内でファイルの入出力をしている場合には修正が必要になるでしょう。
(デフォルトのディレクトリとして「gs://[my_bucket]」と指定できたら楽そうですが)


・ブラウザからアップロードしたファイルを保存したい場合

これは前回「29.Google App Engine for PHPでWordPressを動かしてみた」で困った内容です。
※ 2013/6/16追記 WordPressに関してはWordPress用のGoogleの公式プラグインが提供されました。
36.Google App Engine for PHP上でWordPressを運用するためのプラグインが登場


この場合はもう少し大変です。
Direct file uploads to your POST handler, without using the App Engine upload agent, are not supported and will fail.
と書いてある通り、
multipartでアップロードされたファイルの情報を「$_FILES」変数から直接参照することができません。
前回書いたようにダウンロードしてきたWordPressそのままのコードでは画像のUploadができませんでした。

この場合の対応方法は「Uploading Directly to Google Cloud Storage」に書いてあり、
以下のフローにする必要があります。

1.「createUploadUrl」関数でアップロード先のURLを生成してformのactionとして指定する。
2.「createUploadUrlで生成したURL」からPOSTされる「Handler」を作成する。
 「Handler」では一時ファイルの情報を「$_FILES」から取得することができるので、
  ここで「move_uploaded_file」関数を使ってCloud Storageに書き込む。(移動する)

アップロード時の処理のフローは以下のようになります。


元の処理内容によっては簡単に修正できるかもしれません。
CloudStorageTools::createUploadUrl('[元のアップロード先URL]', $options);
formのactionに指定するURLを上記のように生成すれば
「元のアップロード先URL(=Handler)」で一時ファイルの情報を「$_FILES」から取得でき、
その後「move_uploaded_file」で指定するPathをCloud Storageの形式にしてやれば「辻褄が合いそう」な気がします。


ただ、WordPressで修正方針を検討したところでは若干面倒そうでした。
WordPressは「元のアップロード先URL」でCookieから取得したユーザー情報に基づく権限チェック等を行なっています。
しかし、上図③の通り「uploadUrl」から「Handler」には「サーバー間でPOST」しているので、「Handler」ではブラウザが保持するCookieを取得できません。

そうであれば、ユーザー情報に基づく権限チェックは「Handlerがリクエストを受けたタイミング」ではなく
「createUploadUrl」でURLを生成するタイミングで行うことになるでしょうか。
「createUploadUrl」で生成されるURLは推測不可能な文字列を含んでいてかつ有効期間は10分間なので、
ここでチェックしていれば概ね安全そうな気がします。
(さらにHandlerにadmin権限つけて外部からアクセスできないようにできればさらに良い)

ただ、そのためには「元のアップロード先URL」で行なっているチェック処理を
ごっそり「アップロード画面表示時の処理」に移動する必要があります。

Javaの場合でも1分間(AppEngineのリクエストがタイムアウトする時間)でアップロードが終わらないような
サイズの大きいファイルをアップロードする際にはこの「createUploadUrl」を使ったアップロード方法が必要ですが、
この方法しか使えないとなると、既存アプリを修正する場合に元のアプリの作りや処理内容次第では意外と面倒な作業になるかもしれません。

処理フローのレベルで修正が必要になってしまう事を考えると、
ポータビリティの観点では1分制限があってもいいから透過的にやって欲しいなぁというのが正直な想いです。
今のままでは既存アプリのファイルアップロード機能は全て修正が必要になってしまうので。
WordPressを載せてみた感想的には「あとちょっとなのにもったいないなぁ」という気持ちです(´・ω・`)

参考に、上記の挙動を確認した際のログです。

・投稿画面を開いた際のログ(アクセス元IPアドレスが「61.201.***.***」でGET)

・uploadUrlからHandlerにPOSTした際のログ(アクセス元IPアドレスが「0.1.***.***」でPOST)

UserAgentはブラウザから送信された情報を引き継いでいるようなので
Cookieも全部送り直してくれれば辻褄が合いそうですが、それは行儀が悪いからしないのかな(´・ω・`)
それとも何かが間違っていて、もっと簡単な方法で修正できるのかな・・・?

 2013/6/14追記 WordPressに関してはこの問題を解決するプラグインが公開されました。
Blog @vierjp : 36.Google App Engine for PHP上でWordPressを運用するためのプラグインが登場


参考:
ファイル入出力 | PHP Labo
Google Cloud Storage PHP API Overview - Google App Engine — Google Developers



◯MySQLの利用 (Google Cloud SQL との連携)

Using Google Cloud SQL with App Engine PHP SDK - Google App Engine — Google Developers

・PDO
・mysql_connect
・msqli
を使用可能。

WordPressを試してみた限りではDB周りは特に問題無さそうでした。
(どちらかというとCloud SQLについてちゃんと調べた方が良いかも)


◯ログ出力

ググって見たところ、PHP標準のログ出力関数は以下の様なものがあるのでしょうか。
・error_log関数
・syslog関数

ドキュメントに書かれているのは「syslog関数」のみで
「error_log関数」については記載がありませんが、以下の挙動になります。

・log_test.php
<?php
error_log('##### 0 tset Message', 0);
error_log('##### 1 tset Message', 1);
error_log('##### 3 tset Message', 3, '/var/tmp/app.log');
//error_log('##### 3-2 tset Message', 3, 'gs://[bucket名]/app.log'); // エラー
error_log('##### 4 tset Message', 4);
echo "log_test";
?>

・出力されたログ

・ファイル名を指定しているケースはログ出力不可(代わりにWarning)
・Cloud StorageのPathを指定した場合はエラー
・それ以外はsyslogをErrorレベルで出力した場合と同様の結果
となりました。


参考:
unoh.github.com by unoh
Logs PHP API Overview - Google App Engine — Google Developers


◯Session

The PHP Runtime Environment - Google App Engine — Google Developersに書いてあるとおり、

・Sessionに保存

session_start();
$_SESSION['Foo'] = 'Bar';

・Sesionから取得

session_start();
print $_SESSION['Foo'];
という感じに普通に使えます。


しかし一点気になる記述がありました。
By default the App Engine runtime will use memcache to store session information using the MemcacheSessionHandler class.
(中略)
However data in App Engine memcache may be flushed periodically, meaning any session information will be lost.
For longer-lived sessions, it may be preferable to use an alternative storage service such as Cloud SQL.
・デフォルトではSession情報をMemcacheに保存している
・Memcacheのデータはクリアされるかもしれないのでそれによってセッション情報が失われる可能性がある。
・長い期間Sessionを維持したければ、Cloud SQL等にセッション情報を保存した方が良い。

Javaの場合はDatastoreにSessionデータが記録されていますが、
(Memcacheも併用していると聞いた事がある気もするけど、とにかく永続化されている)
PHPの場合はMemcacheにしか保存していないようです。
実際プログラム中でSessionを使ってもJavaと違ってDatastoreにSession情報を保存するためのEntityは生成されません。

大量のクライアントからのアクセスがあった場合にはSession情報が頻繁にMemcacheからクリアされてしまうかもしれません。
それが困る場合はCloud SQLを使った自前のSessionHandlerを作ってそれを使うべし、との事のようです。

ググってみたところではセッションをMYSQLに書き込む方法としては以下のサイトが見つかりました。
PHP/セッション管理 - がしまっくす
( 「MySQLを使ったセッション管理」-「session_mysql.php」の部分)

ただ、近年は自分がJavaでAppEngineのアプリを作成する場合、基本的にセッションを使わずステートレスに作っていたので、
設計次第ではセッションをがっつり使わなくてもそれほど困らないかと思います。
というか、AppEngineに限らず新規に作る場合には可能であればできるだけ使わない方が良いと思っています。

ただし、既存のシステムをAppEngine上に持ってくる場合にはこの点について留意した方が良いでしょう。


◯mbstring

※2013/6/16追記 Google App Engine 1.8.1で mbstringが追加されています。
http://phpinfo.vier-test.appspot.com/

気になるツイートを見かけたので少し試してみました。
phpinfo見たところ mbstring, Zend multibyteが off のようだけど
これらはPHPでマルチバイトを扱う際に必要なのでしょうか。
WordPressを触ったところではマルチバイト関連での問題は起きていませんが、
どういう場合に困るんでしょ。

ググって見よう見まねでphp.iniを書いたところ、
phpinfoで「zend.multibyte ON」にできました。(「Zend Multibyte Support」はdisabledのままですが)
mbstringについては今のところ有効にすることができていません。

・php.ini
;参考
;http://d.hatena.ne.jp/do_aki/20111208/1323315995
;http://wiki.ohgaki.net/index.php?PHP%2Ftips%2F%E6%97%A5%E6%9C%AC%E8%AA%9E%E7%92%B0%E5%A2%83php.ini%E8%A8%AD%E5%AE%9A
;http://www.phpbook.jp/install/phpini/index5.html

google_app_engine.enable_functions = "phpversion, phpinfo"
output_buffering = "On"

default_charset="UTF-8"
magic_quotes_gpc=off

[mbstring]
mbstring.language=Japanese
mbstring.internal_encoding=UTF-8
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII
mbstring.substitute_charactor=none
mbstring.strict_detection = Off
mbstring.input_encoding=pass
mbstring.output_encoding=pass

zend.multibyte = On
zend.script_encoding = UTF-8

・mbstring.php
<?php
  if ( extension_loaded('mbstring') ) {
    echo "mbstring is loaded";
  }else{
    echo "mbstring is not loaded"; //←こちらが出力される
  }
?>


他に「この設定を試してみろ」等あれば是非w


◯AppEngine固有サービスの対応状況

ドキュメントに書かれているのは以下の6つです。

Logs
Mail(送信、受信、バウンス)
Memcache
  Javaでは見たことがないstubの関数(呼び出すことはできるが何もしない)がいくつも用意されています
URL Fetch
Users
Task Queues(Pushのみ、Pullは無し)


以下のサービス等はまだ使えないようです。
・Channel API
・Images API
・Prospective Search
・Full Text Search
・Sockets
(他にもPHPにはまだ無いものがあります。正確に知りたい場合はJavaやPythonと比べてみましょう)

Datastore関連の機能もドキュメントにはありません。
(別サービスの「Cloud Datastore」はJSON API経由で使えると思います)


◯まとめ

環境の管理が不要なPaaSのAppEngineはIaaSやオンプレミス環境と比べると比較的制限は多く、
また、PHPはまだLimited Previewです。

と前置きした上で、

オンプレミス環境向けに作られた既存のPHPアプリをAppEngine上で動作させる場合には、
前述のファイルアップロードやSessionのように
「ドキュメントを読んだ上で自分が載せようとしているアプリについてその制限が問題とならないかどうか」
を検証する必要があるでしょう。

ファイル入出力についてはAPI的にはローカルのファイルシステムと同様に行うことができますが、
実際にはネットワークをまたがって別サーバーに対してファイルの読み書きをしているわけですから
Webサーバーのローカルのファイルシステムに対して読み書きする場合と比べて速度は低下するでしょう。
読み書きするファイルの使い道次第ではこれがパフォーマンス的な問題に繋がる事があるかもしれません。

また、プログラムからのファイルの出力先は必ずCloud Storageなので、
「アプリケーションのディレクトリ内に*.phpファイルを生成する仕組み」や、
「*.phpファイルを含むファイルをアップロードするような仕組み」
(WordPressのテーマを管理画面からアップロードする場合なんかはそうなのかな?)
に対応するのは難しいでしょう。


機能的な制限については「The PHP Runtime Environment」から読むのが良いと思います。
その上で各機能毎のページをチェックしてみましょう。
前回のWordPressの例のようにとりあえず載せて動かしてみて、
エラーになったら調べてみるというのも手っ取り早いかもしれません。


Cloud SQLもCloud Storageも無かった頃と比べると、
オンプレミス環境向けに作られた既存アプリを動かすための環境は徐々に整ってきています。
特にPHPはまだLimited Previewなので、今後の発展に期待したいと思います。(`・ω・´)


*ご意見・ご指摘等ありましたらコメントでもGoogle+のメッセージでもメールでも、ご連絡いただけましたら幸いです。




このエントリーをはてなブックマークに追加

2013年5月22日水曜日

29.Google App Engine for PHPでWordPressを動かしてみた

このエントリーをはてなブックマークに追加




Ryo Yamasaki(@vierjp)です。

前回「28.Google App Engine for PHP (GAE/PHP) を早速試してみた」というエントリーを書きましたが、
公式ドキュメントに「Running WordPress」という項目があったのでこちらも試してみました。

せっかく日本人が試すのだから、ということでWordpressも日本語版にしてみました。

※後述しますがかなり厳しい問題が一点未解決です
 2013/6/14追記 この問題を解決するWordPress用プラグインが公開されました。
Blog @vierjp : 36.Google App Engine for PHP上でWordPressを運用するためのプラグインが登場



◯構築手順


基本的に公式ドキュメントの通りです。

・WordPress 日本語版をダウンロードする

WordPress › 日本語
今回テストに使用したのは3.5.1です。
日本語版は公式(英語版)とは別に提供されています。
これだけ有名なプロダクトなのに、公式で多言語対応していないのがちょっと驚き。

WordPress › 日本語 « WordPress 日本語版について
(オリジナル英語版と日本語版との違い)


CloudSQLの準備

CloudSQLは一番小さい「D0」のインスタンスを現在無料で使う事ができます。

ただし、Billingは有効にしておく必要があります。
6月までは無料」とのことですが、
無料期間が終わると自動的に有料プランに移行して引き落としが始まる、というよくあるパターンなのでしょう。


* 古いUIから作ってしまいました


DBの初期設定


「Import data」から「gs://appengine-php/setup.sql 」をインポートするか、
「SQL Prompt」から以下を1行ずつ実行します。

CREATE DATABASE IF NOT EXISTS wordpress_db;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'wp_password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';



アプリの作成

・ディレクトリ構成

アプリケーションのディレクトリとして「wordpress-gae」を作成した上で、
以下の構成にしています。

wordpress-gae
├wordpress ←ダウンロードして展開したWordpressのディレクトリを丸ごと配置
|├wp-admin
|├wp-content
|├wp-includes
|├index.php
|├wp-config.php←以下の手順で修正
|└・・・
├app.yaml ←以下の手順で新規作成
├cron.yaml ←以下の手順で新規作成
└php.ini ←以下の手順で新規作成


・app.yaml
application: YOUR_PROJECT_ID
version: wordpress
runtime: php
api_version: 1
threadsafe: true

handlers:
- url: /(.*\.(htm$|html$|css$|js$))
  static_files: wordpress/\1
  upload: wordpress/(.*\.(htm$|html$|css$|js$))
  application_readable: true

- url: /wp-content/(.*\.(ico$|jpg$|png$|gif$))
  static_files: wordpress/wp-content/\1
  upload: wordpress/wp-content/(.*\.(ico$|jpg$|png$|gif$))
  application_readable: true

- url: /(.*\.(ico$|jpg$|png$|gif$))
  static_files: wordpress/\1
  upload: wordpress/(.*\.(ico$|jpg$|png$|gif$))

- url: /wp-admin/(.+)
  script: wordpress/wp-admin/\1
  secure: always

- url: /wp-admin/
  script: wordpress/wp-admin/index.php
  secure: always

- url: /wp-login.php
  script: wordpress/wp-login.php
  secure: always

- url: /wp-cron.php
  script: wordpress/wp-cron.php
  login: admin

- url: /xmlrpc.php
  script: wordpress/xmlrpc.php

- url: /wp-comments-post.php
  script: wordpress/wp-comments-post.php

- url: /(.+)?/?
  script: wordpress/index.php


上記をコピペして「YOUR_PROJECT_ID」を書き換えればOK。
公式ドキュメントの記述に「wp-comments-post.php」を追記しています。(後述)
https://developers.google.com/appengine/articles/wordpress

・php.ini
google_app_engine.enable_functions = "php_sapi_name, gc_enabled"

・cron.yaml
cron:
- description: wordpress cron tasks
  url: /wp-cron.php
  schedule: every 2 hours

・wp-config.phpの修正
/** WordPress のためのデータベース名(書き換える) */
define('DB_NAME', 'wordpress_db');

/** MySQL データベースのユーザー名(書き換える) */
define('DB_USER', 'wp_user');

/** MySQL データベースのパスワード(書き換える) */
define('DB_PASSWORD', '********');

/** MySQL のホスト名(書き換える) */
//define('DB_HOST', 'localhost');

if(isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'],'Google App Engine') !== false) {
  define('DB_HOST', ':/cloudsql/*****:*****');
}else{
  define('DB_HOST', 'localhost');
}

/** AppEngineのドキュメントに従って以下を追加 */

// Cronを無効化(WordPress自身による処理を無効にしてcron.yamlで直接呼び出すような感じ?)
define ('DISABLE_WP_CRON', 'true');

// プラグインやテーマのアップグレードを無効化
define ('DISALLOW_FILE_MODS', 'true');


DB_HOSTは自動的にローカル環境と本番環境で接続先を切り替えるように分岐しています。

・参考:WordPressのwp-config.phpで出来る事いろいろ - かちびと.net


デプロイ


前回同様アプリケーションのディレクトリ(wordpress-gae)で以下を実行します。

/Users/User/appengine_php_1_8_0/appcfg.py update -R --runtime=php .

デプロイ後に初めてアプリにアクセスすると「初期設定のためのウィザード」が表示されます。
特に問題なく設定は完了し、管理画面にログインできます。



◯正常動作を確認できた事

・初期設定ウィザード
・ブログの参照
・コメントの投稿
・記事の投稿・編集
・カテゴリの追加
・固定ページの追加
・テーマの追加
 追加したテーマは「New World
・テーマの「有効化」
・テーマの「カスタマイズ」
・ウィジェットの変更

 *コメントの投稿をするためにドキュメントからコピペしたapp.yamlに以下の記述を追加する必要がありました
- url: /wp-comments-post.php
  script: wordpress/wp-comments-post.php

◯正常動作を確認できなかった事

・テーマの「ライブプレビュー」
・画像のUpload
 これが大きな課題として残りました。
 AppEngineはローカルのファイルシステムにファイルを書き込みできないし、
 Cloud Storageの設定もしていないのだからまぁ当然かと思っていたら、
 少し根が深そうです。

 内部で「$_FILES」という変数を使っていて、
 本来ファイルをUploadした場合自動でファイルが一時ディレクトリに格納され、
 その後move_uploaded_file関数で任意の場所に移動する、という流れらしいですが、
 AppEngineの制限でローカルディレクトリへのファイルの書き込みを許可していないためか
 「$_FILES」の情報が空になってしまっています。

if ( isset( $_FILES["async-upload"] ) ) {
    syslog(LOG_INFO, "_FILES['async-upload'] is set.");
}else{
    syslog(LOG_INFO, "_FILES['async-upload'] is not set."); // ←こちらに分岐する
}

 画面上のエラーメッセージ
ファイルが空のようです。中身のあるファイルをアップロードしてください。このエラーは php.ini ファイルでアップロードができない設定になっているか、post_max_size が upload_max_filesize よりも小さく設定されているために発生している可能性もあります。

 にしたがって、php.iniで
post_max_size=5M
upload_max_filesize=2M

 と指定してみてもダメ。エラーコードも取得できず。
 何か見落としているのでしょうか・・・?(・ัω・ั)

 「PHP アップロード」でググるとどのページも「$_FILES」の説明をしているあたりおそらく定番だと思うので、
 もし現状サポートしていないなら
 内部的にCloud Storageを使うでもいいから「$_FILES」がそのまま動くようにAppEngine自身がサポートしないと
 既存のアプリを載せた際にファイルアップロード機能が動かないケースが多そうな気がします。

 ・参考:$_FILES - 初心者向けPHPナビ

 2013/6/14追記 この問題を解決するWordPress用プラグインが公開されました。
Blog @vierjp : 36.Google App Engine for PHP上でWordPressを運用するためのプラグインが登場



◯まとめ

日本語の投稿も特に問題なくそこそこ動くのですが、
重要な機能としてはファイル(画像)のUploadができませんでした。
この問題さえなければ実用レベルで使えそうな勢いだっただけに残念(´・ω・`)

PHPはまだLimited Previewなので、今後に期待したいと思います。
・・・私がPHPド素人なのが原因の可能性も十分にありますが(´・ω・`;)
PHPに詳しい方、$_FILESが空になる(PHP的な)原因に心当たりがあれば情報くださいませ。m(´・ω・`)m

* 2013/5/22追記 Google+で +Takashi Matsuo さんから教えていただきました。
https://developers.google.com/appengine/docs/php/googlestorage/overview
createUploadUrl  を使うようにしないとだめでしょうね。上記ドキュメントの最後に普通のファイルアップロードは失敗すると書いてあります。

「About Direct File Uploads」のところに
Direct file uploads to your POST handler, without using the App Engine upload agent, are not supported and will fail.
と書いてありました。
結論、AppEngine的な制限についての私の見落としでした。
Google Cloud Storageにアップロードするようにコードの修正が必要になりそうです。

* 2013/5/23追記 はてブで提案をいただいたので試してみました。

php.iniで
upload_tmp_dir=gs://[bucket名]
としてみましたが、残念ながらダメでした。(´・ω・`)
実際にこれができたらシンプルでいいなぁ。

ブログに晒すと情報もらえていいですね。ありがとうございます。(´▽`)


WordPressは以下に配置してあるので、興味があればアクセスしてみてください。
・WordPress on Google App Engine for PHP by vierjp
管理画面に入るにはIDとPassが必要になってしまいますが、記事の参照とコメントの投稿はできます。
* 6月になってCloud SQLが有料になったら止めるかも。
* 2013/11/10 追記 Cloud SQLの課金が地味に痛いので停止しました。



このエントリーをはてなブックマークに追加

2013年5月19日日曜日

28.Google App Engine for PHP (GAE/PHP) を早速試してみた

このエントリーをはてなブックマークに追加




Ryo Yamasaki(@vierjp)です。

Google I/Oでハンズオンに参加したおかげか、
すぐにGoogle App Engine for PHPのLimited Previewを使えるようにしてもらえたので早速試してみました。

◯Google App Engine for PHP Limited Preview申し込み


・Google App Engineのアカウントとアプリの作成


申し込みは自分のGoogleアカウントに紐付いている「AppEngineの"アプリ"」に対して行います。
そのため「AppEngineのアカウントが無い」または「アプリを作成していない」という場合には、
先にアカウントとアプリの作成を行う必要があります。

比較的新しめのところでは以下のサイトが参考になるかもしれません。
egashira.jp : 初めてのGoogle App Engine

アカウントの大量取得を防ぐためか
AppEngineのアカウント登録時には携帯の電話番号やメールアドレスを使った確認があるので、
手元に携帯電話を用意しておいてください。


・Limited Previewの申し込み


Google App Engine for PHP Limited Preview申し込みサイト


中央の緑色のボタン「Register an app for the Limited Preview」を押します。

するとGoogleアカウントの権限の認可を求められるので、許可してください。

・アプリ選択画面


アプリ選択画面に来たら「PHPを使えるようにしたいアプリ」を選択し、
・AppEngine for PHPで何をしたいのか
・MemcacheやURLfetchのようなAppEngineのAPIを使う予定はあるか
・WordpressやphpMyAdminのようなアプリを使う予定はあるか
などを記述します。

最後に「Request PHP for this application」ボタンを押して申し込みは完了です。

申し込み完了直後と、実際に利用できるようになった後にメールが届きます。
また、再度「Register an app for the Limited Preview」を押すと、
現在の状況を参照することができます。

利用できるようになると以下の画面になります。

・申し込み完了画面



◯SDKのダウンロード

Installing the PHP SDK - Google App Engine — Google Developers

手順に従って以下をインストールしてください。
・Python 2.7
・PHP 5.4
・App Engine PHP SDK


◯AppEngine上で「phpinfo()」を表示してみる

適当なディレクトリに以下の内容でファイルを作成します。

・app.yaml

application: vier-test

version: phpinfo

runtime: php

api_version: 1



handlers:

- url: /

  script: index.php



基本的には「runtime: php」の行以外はPython版と同じ書き方のようですね。

・index.php

<?php

  phpinfo();

?>

普通はyamlとphpファイルだけなのですが、
セキュリティ都合か、デフォルトでは「phpinfo()」を実行できないようになっています。
そのため以下の内容で「php.ini」ファイルを作成して同一ディレクトリに配置します。

・php.ini

google_app_engine.enable_functions = "phpversion, phpinfo"

これで一つのディレクトリに
「app.yaml」「index.php」「php.ini」の3つのファイルが存在する状態になります。


◯デプロイする


・デプロイ方法

前項で作成したアプリのディレクトリに移動して以下を実行します。
(「appcfg.py」は展開したSDKのディレクトリにあります)

/Users/User/appengine_php_1_8_0/appcfg.py update -R --runtime=php .

認証中にGoogleアカウントのIDとPassを聞かれます。
二段階認証を設定している場合には
アプリケーション固有のパスワード」を取得するか、
上のコマンドに「--noauth_local_webserver --oauth2」を追加して実行します。


デプロイ後の「Admin ConsoleのVersion画面」です。

言語が「php」になっているのが確認できます。


実際にAppEngine上に配置した状態は、以下のURLで確認できます。
http://phpinfo.vier-test.appspot.com/


「Server API AppEngine PHP Runtime SAPI」となっているのが確認できます。


◯セッションやハンズオンの話

・Datastoreの話

前回「27.Google I/Oで発表されたGoogle Cloud Platformの新機能」にも書いた通り、
現時点でのドキュメントには「Cloud SQLを使う方法」しか書いていませんが
一応セッションではDatastoreにアクセスするコードがありました。
また、新プロダクトの「Cloud Datastore」はJSON APIを使えるので、こちらはPHPからも利用できると思います。


・WordPressは動きそう

セッション内でWordPressの話があったので「Cloud SQL」と組み合わせることで動作するんじゃないでしょうか。

* 2013/05/21追記
コメントでneranさんから教えていただきましたがドキュメントに思いっきり書いてありますね。
Running WordPress - Google App Engine — Google Developers

* 2013/05/22追記
実際に試してみたので詳細については次の記事
29.Google App Engine for PHPでWordPressを動かしてみた」をどうぞ。


・phpMyAdminもAppEngine上で動くかも

ハンズオンでは、CloudSQLを操作するために「AppEngine上に配置したphpMyAdmin」を使用しました。



ハンズオン用に最初から用意されていたので、
完全に素で動くのか、いくらか修正が必要なのかはわかりませんが、
一応AppEngine上で動作させる事はできるようです。


以上、今回もサンフランシスコからでした。(´▽`)




このエントリーをはてなブックマークに追加

2013年5月17日金曜日

27.Google I/Oで発表されたGoogle Cloud Platformの新機能

このエントリーをはてなブックマークに追加





こんばんは。Ryo Yamasaki(@vierjp)です。

現在Google I/Oに参加するためサンフランシスコに来ています。
今は2日目の夜ですが、
Google Cloud Platformの変更点についてセッションを聞いて理解した限りで書こうと思います。
まだ検証もドキュメントの確認もちゃんとできていないので間違っている点もあるかもしれませんが予めご了承ください。
3日目のセッションを聞いた後や実際に検証した上で間違いに気がついたら訂正なり追加の記事なり書くと思います。

間違いに気がついた方はツッコミやご指摘いただけたら幸いです。


◯Google App Engine


◯PHP対応


https://developers.google.com/appengine/docs/php/

・概要
AppEngine上で使えるプログラミング言語として、
Python、Java、Goに続く第四の言語としてPHPが追加されました。
言語に関する要望の中では昔からPHPが一番多かったそうで、今回ついに対応となりました。


ドキュメント内ではデータアクセスとして書かれているのが
「Cloud SQL」と「Cloud Storage」のみで「Datastore」についての言及がありませんが、
セッション中のサンプルコードにはDatastoreへのアクセスっぽいコードが存在していました。
* 2013/5/23追記 セッションの動画を見直したら「Cloud Data Store」(後述)と言っていました。



* 2013/5/24追記 早速Limited Previewをもらえたので試してみました。以下の記事も参考にどうぞ。
28.Google App Engine for PHP (GAE/PHP) を早速試してみた
29.Google App Engine for PHPでWordPressを動かしてみた
30.Google App Engine for PHPにおけるポータビリティを考える
36.Google App Engine for PHPでWordPressを運用するためのプラグインが登場
39.Google App Engine for PHPでCakePHPを動かしてみた


◯Modulalized Applications (Servers?)



スライドによって名称が「Modulalized Applications」だったり「Servers」だったりしましたが、同じ物を指していると思います。

おそらくTrusted Tester中に「Servers」と呼ばれていた機能で、
一つの「アプリ」の中に複数の「コンポーネント」を定義できるものではないかと。
これは現在の「アプリケーション」と「バージョン」の概念の間に存在するような仕組み。
たぶんこんな感じ?(想像図です)

概念は既存の「backends」の位置づけに似ていますが、
・スケーリング
・デプロイ
・バージョニング
・パフォーマンス設定
を「コンポーネント」毎にできるようになります。
例えば、
インスタンスクラス(F1等)やインスタンス数等の設定は「コンポーネント」毎に設定できます。
と言っても既存のBackendsでも上記については一応できていたので、
もう少し細かく設定できたり制限がなくなるといった要素もあるのかもしれません。
セッションでは「3つのコンポーネントに分けているアプリ」の例もありましたが、
「Frontendとbackends」という既存の分け方を、より柔軟にするような趣旨かと思います。

これが正式リリースされた際に既存の「Frontend」「Backend」がどうなるかは少し気になります。
(既存のアプリのために何らかの形で残りそうには思いますが)

以下についてはアプリ毎に存在し、各コンポーネントから共有する形になります。
・Datastore
・Memcache
・TaskQueue

この機能について、あるセッションでは「Launched Today」と書いてあったり、
また別のセッションでは「Limited Preview」と書いてあったりで、
リリース時期がいまいち不明です。

ただ、これがリリースされたらさすがに管理画面の構成が変わるはずですが、
現状変わっていないのでおそらくまだ「Limited Preview」なのでしょう。


◯Mobile Backend Starter




「サーバーサイドのコードを一切書かずにサーバーと連携するAndroidアプリを作る」ためのAppEngineアプリ。
「Mobile Backend Starter」は「Platformの拡張」ではなく、「AppEngineのアプリケーション」として提供されます。

以前からこのブログで扱っている「Google Cloud Endpoints」も近い要素がありますが、これは本当にサーバーの知識が不要です。

・サーバー側アプリをデプロイをするための環境を構築する必要が無い
→eclipseをダウンロードして、アプリのソースコードを取得してデプロイする・・・という作業が必要ありません。
Webの画面上から「Mobile Backend Starter」のアプリを直接デプロイすることができます。

・OAuth2の認証をサポート
 (内部的にはCloud Endpointsを使用しています)

・サーバー側のコードを書く必要が無い
→DAOをAPIとして公開するような作りになっていて、クライアント側からクエリの内容を指定してクエリを実行します。
 (詳しく見ていないけどアクセスコントロールの仕組みはちゃんとあるらしい)

・ContinuousQuery
 ・サーバー側でクエリの結果に影響するデータが登録されると自動でクライアント側の画面に反映される
  ・クライアント側からクエリを実行した際にProspective Searchに端末IDとクエリの内容を登録する
  ・データの更新時にProspectiveSearchが呼び出される(登録内容にマッチする更新が行われたか、が確認される)
  ・ProspectiveSearchに登録した条件にマッチした場合、GCMを使ってクライアント側に通知する
  ・クライアント側はGCMのMessageを受け取ると、API経由でクエリを再実行して「最新のクエリ結果」を取得して描画する

・クライアント側を楽に実装するための仕組みも用意されているらしい。

大々的に好評されていませんでしたが実は4/20ぐらいにはリリースされていて、少しソースを読んでいました。
Mobile Solutions - Google Cloud Platform — Cloud Platform」で
「Try it now」ボタンを押して利用を開始する事ができます。

ソースコードも公開されているので、
ProspectiveSearchとGCMの利用方法のサンプルアプリとして見ても面白いと思います。



◯Cloud Datastore


https://developers.google.com/datastore/

Blog @vierjp : 33.Google Cloud Datastoreを試してみた 概要編 (1/3)」に詳細をまとめ直しています。

◯概要
位置づけとしてはAppEngineのDatastoreを単独のサービスとして切り出したもの。
現在「Preview Release」という扱い。
概要レベルでは機能も同じとのこと。
ただしプログラムからの呼び出し方は大きく異なる模様。

これまでDatastoreはAppEngineの各アプリと1:1で完全に紐付いていて、
そのアプリ以外からアクセスすることができなかったが、
Cloud Datastoreとして単独のサービスになった事で、
異なるAppEngineアプリや環境から呼び出すことができるようになった。

複数のAppEngineのアプリから同一のDatastoreにアクセスしたり、
逆に一つのAppEngineのアプリから複数のDatastoreにアクセスしたり、
Compute Engineからアクセスしたりもできる。
さらには別のクラウドやオンプレミスのサーバーからもアクセスできる。


◯Cloud DatastoreのAPI
基本的に既存のDatastoreと機能は同じだそうですが、
ドキュメントを見る限りアクセス方法は異なります。
Protocol Buffer形式でリクエストを投げる「Protocol Buffers API」か、
JSON形式でリクエストを投げる「JSON API」を使用します。
Protocol Buffers APIは現状JavaとPythonから利用できます。
(現時点でGO用・PHP用は用意されていません)

注意が必要なのは既存のDatastoreアクセスに使っている「Low Level API」とは異なるという事。
そのため現在利用している「Low Levle APIベースのフレームワーク」は
「Cloud Datastore」に対してそのままでは使えないのでしょう。
(API Proxyレベルで処理を差し替えて対応できたりしないだろうか)
* 2013/06/11 追記 AppEngine上のアプリからはこれまで通りに使えます。
詳しくは「Blog @vierjp : 33.Google Cloud Datastoreを試してみた 概要編 (1/3)」を参照。

JSON API経由なら言語を問わず利用する事ができるので、
node.jsのサーバーから利用する」ような事もできます。

JSON APIは既にAPIs Explorerに表示されています。
Discovery APIベースなので、多くの言語に対応するClient Libraryが公開されることでしょう。


◯ユースケース
- ケース1 AppEngineアプリのバックエンドとしてCompute Engineを使う場合

AppEngineのアプリが保存する大量データの処理にCompute Engineを使う。
データの保存領域に「Cloud Datastore」を使うことで、
時間のかかる大量データの処理をCompute Engine上のバッチから行う事ができる。
(Cloud DatastoreならAppEngineのアプリとCompute Engineの両方からアクセスできるので)

ComputeEngineを使っていわゆる「夜間バッチ」的な一括処理に使ったり、
DatastoreのデータをBigQueryに転送する前に加工したり、
という用途が思いつきます。

これまではAppEngineアプリの持つ大量データの処理は
「Shutdown対応」をした上でbackendsを使ったり、細かいたくさんのTaskに分散させて処理する等、
実装段階で手間をかけていましたが、それが軽減されるでしょう。


- ケース2 Compute Engine上にWebアプリを配置する場合
AppEngineを使わない場合でも、
例えばCompute Engine上にnode.jsのサーバーを立てて、
そのデータ保存領域としてCloud Datastoreを使うということもできます。

実際にI/O 3日目の「Code Labs」でこの説明が行われるようです。
「Getting Start」では「node.jsからGoogle Cloud Datastoreを使う方法」について書かれています。
Overview - Google Cloud Datastore — Google Developers


- ケース3
 AppEngine・ComputeEngine以外のクラウド環境からデータを操作する
 JSON APIがあるので基本的にどこからでも操作できるはずです。
 自社にあるオンプレミスのサーバーからアクセスすることもできますし、
 アプリのアップグレードに伴うデータの一括更新処理をローカル環境から実行するようなこともできるでしょう。
 (この場合ComputeEngineから動かすのと比べて速度が遅そうですが)


◯Preview期間中の制限
Preview期間中は、課金を有効にしている場合にも、
Read、Write、Small Operationそれぞれに対して
「10 million per day, no more than 500 per second」のlimitが設定されています。
(連絡すれば増やしてもらえるそうです)



◯Google Compute Engine



現地時間で15日についにGoogle Compute Engineが一般公開されたそうです。
以前と比べて変更されたのは以下のとおりです。

- Sub-hour Billing
 課金の時間単位が変わったそうです。
 最低課金額は10分単位だが、その後は一分単位で課金される、とのこと。

- Shared Core Instance
 →f1-micro,g1-smallの追加
  さらに安いインスタンスが追加されました。
  これまでの一番安いインスタンスが「n1-standard-1」の「$0.115/h」ですが、
  「f1-micro」は「$0.019/h」で格段にお安くなっています。
  実際にどのくらいのパフォーマンスがあるのかわかりませんが、
  メモリをそれほど必要としない長時間の処理に良いのでしょう。

- Advanced Routing
3日目のセッションで話されるそうですが未確認です。
* 2013/5/25追記 オンプレミス環境や他のクラウドとセキュアに繋ぐためのVPN的なもの・・・らしいです。

- Persistent Diskのサイズが最大10TBになった。
これまでの最大サイズ「1TB」から10倍の「10TB」になります。

参考:
Google Compute Engine Pricing (料金体系)



◯Google Cloud Storage (2013/5/23追記)

すっかり忘れてましたがセッション動画見直していたら新機能があったので追記。

◯Offline Disk Import (Limited Preview) 
https://developers.google.com/storage/docs/early-access?hl=ja

アップロードしたいファイルをハードディスクに入れてGoogleに送ると、
Google内のネットワークからUploadしてくれる
という究極手段。
(最初ネタかと思ったのは内緒^-^;)

Google/IO 初日の5/15にサービス開始。

・手順
1.Offline Disk ImportのInterest Formから申し込み
2.SATAのハードディスクをencfs(暗号化ファイルシステム)でフォーマットする
3.データをハードディスクにコピーする
4.Googleに郵送する
5.Googleが顧客が所有する新しいbucketにimportする(Googleの高速なネットワークを使って)
6.GoogleがHDDを郵送で返す

* 現時点ではアメリカ国内の顧客限定です
ただし、間違いなく今後数ヶ月の間に国際的に利用できるように拡大するし、
優先順位が上がるかもしれないので海外の顧客も「Interest Form」から連絡してください、とのこと。

料金は通常のGoogle Cloud Storageの料金がリクエスト回数, 帯域使用料,ストレージ使用料」に加えて
HDD一つ毎に「$80」加算されます。

* 2013/5/24追記 Google Cloud Storeのセッションに関して別途詳細記事を書きました。
31.Google Cloud Storageに大量データをアップロードする際のテクニック(Google I/O 2013)



◯Google BigQuery (2013/5/28追記)

IO報告会(#Devfest)で知ったので追記。

◯Google Analytics and AdSense Data Analysis in BigQuery
AdSense BigQuery Integration Guide - AdSense — Google Developers

・BigQueryでGoogle AdSenseのデータを分析可能になりました
・Google Analyticsのデータは2013年9月の公開を目指しているそうです

AdsenseにしてもAnalyticsにしても、
BigQueryを使ってこれらと自社で持っている別の情報とをJoinして分析できる事が魅力です。

Google BigQueryのセッションに関して別途詳細記事を書きました。
32.Google BigQueryでAnalyticsとAdSenseのデータを分析する(Google I/O 2013)


概要レベルですが、今日はここまでとしてまた近日中に続報を書こうと思います。

以上、 San Franciscoからでした!



◯TopGate社 Google I/O フィードバックセミナー (2013/6/11追記)

2013/5/31に行われた「TopGate社 Google I/O フィードバックセミナー」で
私がGoogle Cloud Platform担当として発表したスライドです。
Report of Google I/O 2013 Google Cloud Platform

こちらも併せてどうぞ!




このエントリーをはてなブックマークに追加