Google App Engine & Goolge Cloud SQL(MySQL 5.5)& Python

色々あるPaaSで無料で試せる(http://http://server.kensapo.com/cloud.html)というとどうしても「Google App Engine」になってしまいます。しかし、DB的にビッグテーブルしかないとなると、相当これまでのコーディングスタイルを変えざるを得ないので、二の足を踏んでいたのですが、よく調べてみると、とっくにGoogle Cloud SQL(MySQL5.5)が使えるようになっているではありませんか(気づくのに遅すぎ!)。しかも2013年の6月まで無料です!

そこで今回、GoogleのTutorial(https://developers.google.com/appengine/training/cloud-sql/application_with_local_mysql?hl=ja)にあるGustbookを作成してみました、その時の備忘録です(というか、ほとんどTutorialそのまんまです)。

  1. Google App Engine
  2. Google APIs Console

以上の2つのサイトでそれぞれ、アプリケーションの登録、Google Cloud SQLの登録およびそれをどのアプリケーションの使うのかという設定を行います。

アプリの登録

Googleアカウント等を登録していない人はhttp://symfoware.blog68.fc2.com/blog-entry-252.htmlあたりを見て、まずは登録する必要があります。ちなみに、GoogleのアカウントとGoogle App Engineのアカウントは同じでOKですが、手続きは必要です。

Google Cloud SQLインスタンス作成

  • http://libro.tuyano.com/index3?id=1062003(→このサイトではbillingでチャージしなくてはならないようなことが書いていますが、現在はそのような必要はないようです)や本家のhttps://developers.google.com/cloud-sql/docs/before_you_begin?hl=jaに詳しく書いていますので詳細はこちらをご覧ください。概略は次のような感じです。
    • https://code.google.com/apis/consoleに行く
    • プロジェクトがない場合はプロジェクト名を指定して、プロジェクトを作成します。例えば「myApplication201303」など。
    • 左側画面の「service」をクリックして、右側の画面のサービスの画面からGoogle Cloud SQLをONにする
    • 左側の「Billing」を選択して、カードの登録をします。無料なのですが、登録だけは必要のようです。登録後数十分待ちます(数時間という人もいます)。私の場合は、何度か試しているうちに(すぐに反映すると勘違いしていたもので・涙)、反映されました。
    • 右側のGoogle Cloud SQLのメニューをクリックします。
    • 右の方にある「New Instance...」をクリックします。
      • Name:データベース名を入力。例えば「sqltest」。
      • Size:サイズは無料のD0を選択するのがポイントです。これが無料のサイズだからです(現在は赤い字で無料はD0だよ、って書いてあります)。
      • Replication Mode:データ書き出しのモード指定。とりあえず、Synchronousを選択。
      • Pricing Plan:D0を選んでいるので、選べないようになっています。
      • Authorized Application:上記で登録したGoogle App Engineで登録したアプリを選択(sqltestfirst)。
      • Create instanceをクリックして作成。結果、
      • その結果、そのGoogle Cloud SQLインスタンスID(上記の例だと)「myApplication201303::sqltest」となり、後にこれが必要になります。

開発環境の整備

とりあえずローカルでアプリ作成

CREATE USER 'hoge'@'localhost' IDENTIFIED BY 'fuga';
GRANT ALL PRIVILEGES ON *.* TO 'hoge'@'localhost' WITH GRANT OPTION;
create database & table
create database guestbook charset utf8;
use guestbook;
CREATE TABLE entries
  (id int not null auto_increment primary key,
   guest_name varchar(255),
   content varchar(255),
   created_at timestamp)
  ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into entries (guest_name, content) values ('chikkun', 'Myself');
  • ローカルのアプリを作成するディレクトリを適当に作成し、そのトップでapp.yamlという名前のファイルを作成します。
cd
mkdir sqltest
cd sqltest
emacs app.yaml
  • app.yamlの中身。application:には先程Google App Engineで登録したアプリ名を入れます。また、今回はpython2.7なのでそれを指定し、pythonスクリプトはmain.applicationとし、テンプレートにはjinja2を使います。
application: sqltestfirst
version: 1
api_version: 1
runtime: python27
threadsafe: true

handlers:
- url: /.*
  script: main.application

libraries:
- name: jinja2
  version: latest
  • main.pyをトップに作成
import os
import logging

from google.appengine.api import rdbms
from google.appengine.ext import webapp

import jinja2

template_path = os.path.join(os.path.dirname(__file__))

jinja2_env = jinja2.Environment(
    loader=jinja2.FileSystemLoader(template_path)
)

CLOUDSQL_INSTANCE = ''#デプロイの時に必要となる。
DATABASE_NAME = 'guestbook'
USER_NAME = 'hoge'
PASSWORD = 'fuga'


def get_connection():
    return rdbms.connect(instance=CLOUDSQL_INSTANCE, database=DATABASE_NAME, user=USER_NAME, password=PASSWORD, charset='utf8')

class MainHandler(webapp.RequestHandler):
    def get(self):
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute('SELECT guest_name, content, created_at FROM entries '
                       'ORDER BY created_at DESC limit 20')
        rows = cursor.fetchall()
        conn.close()
        template_values = {"rows": rows}
        template = jinja2_env.get_template('index.html')
        self.response.out.write(template.render(template_values))


class GuestBook(webapp.RequestHandler):
    def post(self):
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute('INSERT INTO entries (guest_name, content) '
                       'VALUES (%s, %s)',
                       (self.request.get('guest_name'),
                        self.request.get("content")))
        conn.commit()
        conn.close()
        self.redirect("/")


application = webapp.WSGIApplication(
    [
        ("/", MainHandler),
        ("/sign", GuestBook),
    ],
    debug=True
)
  • 「index.html」という名前の jinja2テンプレートをトップに作成。
<!DOCTYPE html>
<html>
<head>
<title>CloudSQL Tutorial</title>
</head>
<body>
<h2>Guestbook</h2>
<form action="sign" method="POST">
  Name: <input name="guest_name"><br>
  <textarea name="content" rows="3" cols="60"></textarea><br>
  <input type="submit" value="Sign Guestbook">
</form>
<hr>
{% for row in rows %}
  <p>{{ row[0]|e }} wrote:
    <blockquote>
      {{ row[1]|e }}
    </blockquote>
    at {{ row[2]|e }}
  </p>
  <hr>
{% endfor %}
  • 開発ウェブサーバの起動(mysqlのオプションは自分の環境で要変更)
展開ディレクトリ/google_appengine/dev_appserver.py --mysql_socket=/var/lib/mysql/mysql.sock
  1. アプリの確認

ローカルにあるアプリのデプロイ

  1. Google Cloud SQLインスタンスは作ったけれど、まだTableを作成していないので(ユーザなんかも)、上記のローカルで行ったSQLを行う。それには下のように、左のGoogle Cloud SQLSQL Promptタブの中のTextAreaで行う。

  • ローカルのapp.yaml内のCLOUDSQL_INSTANCEを実際のものに書き換えます。他もローカルと違う場合は変更します。
CLOUDSQL_INSTANCE = 'myApplication201303::sqltest'
DATABASE_NAME = 'guestbook'
USER_NAME = 'hoge'
PASSWORD = 'fuga'

Googleへデプロイ

appcfg.pyでデプロイします。

  • コマンドを叩く
cd ..
展開ディレクトリ/google_appengine/appcfg.py update guestbook/
  • メールアドレスとパスワードを入力。メールアドレスはGoogle App Engineを登録した際のGoogleのアカウント用のメールアドレスです。パスワードをも同様。

これで終了です。

http://sqltestfirst.appspot.com/

で確認できます(はず(^_^;)。