2010年10月13日水曜日

Amazon Web Servicesによるソーシャルアプリ運用ノウハウ大公開

Software Design 2010年11月号にて、第1特集の「Amazon事例,国内新サービスから学ぶクラウド時代のシステム管理」の3章、「Amazon Web Servicesによるソーシャルアプリ運用ノウハウ大公開」という記事を執筆しました。
弊社がAWSを使いはじめたきっかけ、運用のポイント等をまとめてあります。

また、第1特集の1、2章は弊社SREの石川が執筆してます。
こちらも弊社のAWS運用ノウハウをそのまま公開しています。

10月18日発売です!

2010年7月7日水曜日

第3回 AWS User Group - Japan 勉強会 で 発表します

本日開催の第3回 AWS User Group - Japan 勉強会で「AWSによるソーシャルアプリ運用事例」という題で、発表してきます。

gumiが運用しているソーシャルゲームの実際のサーバ構成やどうしてAWSを使うに至ったのかというような話をできればと思ってます。

2010年7月6日火曜日

Amazon RDS で肥大化したslow_logテーブルをクリアする方法

以前AmazonRDSでslow queryを出力するようにする方法にてRDSでslow_logを出力するように設定すると、mysqlデータベースのslow_logテーブルにログが保存されるようになるのですが、このテーブルに保存されたデータは、RDSで作成できるユーザーの権限では削除することができません。


mysql> delete from slow_log;
ERROR 1556 (HY000): You can't use locks with log tables.
mysql> truncate table slow_log;
ERROR 1044 (42000): Access denied for user 'xxxx'@'%' to database 'mysql'


このことはgeneral_logにも言えるのですが、
これに対して、Amazonから以下のストアドプロシージャが提供されています。


mysql.rds_rotate_general_log
mysql.rds_rotate_slow_log


この2つのストアドプロシージャは新規、既存のものを含めて全てのRDSインスタンスで使用可能で、以下のようにCALL文により実行できます。


CALL mysql.rds_rotate_general_log;
CALL mysql.rds_rotate_slow_log;


プロシージャの中身はこんな感じです。

mysql> show create procedure rds_rotate_slow_log \G
*************************** 1. row ***************************
Procedure: rds_rotate_slow_log
sql_mode:
Create Procedure: CREATE DEFINER=`rdsadmin`@`localhost` PROCEDURE `rds_rotate_slow_log`()
READS SQL DATA
DETERMINISTIC
BEGIN
CREATE TABLE IF NOT EXISTS mysql.slow_log2 LIKE mysql.slow_log;
DROP TABLE IF EXISTS mysql.slow_log_backup;
RENAME TABLE mysql.slow_log TO mysql.slow_log_backup, mysql.slow_log2 TO mysql.slow_log;
END
character_set_client: latin1
collation_connection: latin1_swedish_ci
Database Collation: latin1_swedish_ci


slow_logと同じスキーマのテーブル、slow_log2を作成し、既存のslow_logテーブルをslow_log_backupに、slow_log2をslow_logにリネームするという作業を行っています。
この際、slow_log_backupテーブルがすでに存在していればDROPしているので、この関数を2回呼ぶと、1回目にバックアップしたデータはなくなってしまうので注意が必要です。

また上記の2つのプロシージャの他にKILL、KILL QUERYを実行できる以下の2つも用意されています。

mysql.rds_kill
mysql.rds_kill_query

2010年6月30日水曜日

Python × Django × AWSで作るソーシャルアプリ~3日に1つアプリをリリースできた理由~

今日の19:30から「Python × Django × AWSで作るソーシャルアプリ~3日に1つアプリをリリースできた理由~」という題目で話をしてきます。


まだ空いてますので、お暇な方はぜひ!
http://atnd.org/events/4846

2010年6月14日月曜日

第2回 AWS User Group - Japan 勉強会 で LTします。

今日行われる
第2回 AWS User Group - Japan 勉強会 - 6/14(月)
で「RDS multi AZ 早速試しました」というタイトルで、RDSとMulti-AZについて話をしてきます。

Mult-AZ機能が搭載されて、実運用にも十分耐えれるようになったRDSについて、簡単に話をしようと思ってます。

2010年5月18日火曜日

もう止まらない。Amazon RDS Multi-AZ Deployments機能をリリース

本日、Amazon RDSの新機能、 Multi-AZ Deploymentsがリリースされました。

Release: Amazon Relational Database Service on 2010-05-17

これにより、RDSのダウンタイムがほぼ0になるという素敵な機能です!

Multi-AZ deploymentを有効にするとプライマリDBとは別のAvailability Zoneにホットスタンバイとしてレプリカが作成されます。
レプリカはプライマリDBと同期していて、プライマリDBが止まると自動的にレプリカにスイッチします。
フェイルオーバーの仕組みはシンプルで、CNAMEレコードを変更し、スタンバイDBに向けるだけです。

スタンバイDBに切りかわるタイミングは以下のとおりです。


  • Availabilyty Zoneが落ちた時

  • プライマリDBが落ちた時

  • DBのスケールを変更した時

  • ソフトウェアにパッチを当てている時



DBのバックアップ時に発生していたI/Oフリーズもなくなるようです。たぶんレプリカの方でバックアップを取るようになるのでしょう。

また、レプリカには直接アクセスすることができないので、Read Onlyなクエリをレプリカに逃がすというスケールアウト的な使い方はできません。

忘れてはならないコストですが、利用料は倍になります。
インスタンスが二つになるのでしょうがないと言えばしょうがないですね。


Multi-AZを適用する方法はすごく簡単です。

まずは以下から最新版のコマンドラインツールをダウンロードして使えるようにします。

Amazon RDS Command Line Toolkit

バージョンが1.1.004ならOKです。

$ rds-version
Relational Database Service CLI version 1.1.004 (API 2010-01-01)


あとは以下のコマンドを叩くだけで既存のインスタンスにMulti-AZ 機能を適用できます。

$ rds-modify-db-instance dbname --multi-az true

既存のインスタンスに適用すると、適用中に数分DBがダウンすると書かれているので、実行するタイミングには注意が必要です。

実際試してみたところ、このコマンドを実行するとインスタンスにPending MultAZフラグが立ちます。(rds-describe-db-instancesコマンドで確認できます。)
僕の環境ではコマンド実行から数時間経過しましたがまだPending状態のままです。
PendingからMulti-AZが有効の状態になるにはかなり時間がかかりそうです。

Pendingの状態でもDBには接続できているので、準備ができて、実際に適用するタイミングでちょっとばかりアクセスできなくなるんだと思います。

また、新規作成の際も--multi-azオプションを指定してやるだけみたいです。

これでますますRDSが手放せなくなりそうです。

2010年5月6日木曜日

RDS バックアップから復旧する方法

fooというRDSのインスタンスのバックアップからbarという新規インスタンスを作成する場合、以下のコマンドを実行する。


$ rds-restore-db-instance-to-point-in-time bar -s foo -l

-sでソースにする元のインスタンスの名前(ここではfoo)を指定し、-lで復元可能な最新の時刻のバックアップから復元することを指定する。

復元時刻を指定する場合は、-rオプションを使用する。
指定する時刻はUTC


$ rds-restore-db-instance-to-point-in-time bar -s foo -r 2010-05-04T09:20:00Z


リストア可能な最終時間はrds-describe-db-instancesに--show-longを指定することで確認できる。


$ rds-describe-db-instances --show-long --headers
DBINSTANCE,DBInstanceId,Created,Class,Engine,Storage,Master Username,Status,Endpoint Address,Port,AZ,Backup Retention,PendingBackupRetention,PendingClass,PendingCredentials,PendingStorage,DB Name,Mainte
nance Window,Backup Window,Latest Restorable Time
DBINSTANCE,foo,2010-02-22T10:07:04.933Z,db.m2.4xlarge,mysql5.1,20,owner,available,foo.xxxxxxx.us-east-1.rds.amazonaws.com,3306,us-east-1a,1,(nil),(nil),(nil),(nil),(nil),tue:05:00-mon:09:00,
19:00-21:00,2010-05-06T08:45:00Z


見づらいが、一番最後の2010-05-06T08:45:00Zが復元可能な最終時刻になる。

バックアップが行われるのは1日1回だけだが、バックアップ後のトランザクションが全部保存されているので、それを元に直近1分前程度の状態を復元可能。
このため、バックアップから時間が経過していればしているほど、復元する際に時間がかかる。
特にDBの更新が頻繁だと復元に時間がかかる。
150 updates/sec程度の更新量で5時にバックアップを行っていた場合、9時に復元したところ、1時間程度、19時頃復元したところ、5時間くらいかかった。

2010年3月29日月曜日

EC2のインスタンスにpingを通すためのセキュリティグループの設定


ec2-authorize -P icmp -t -1:-1 -s 0.0.0.0/0

2010年3月23日火曜日

Djangoのパーミッションテーブルを更新するスクリプト

Djangoでproxy modelというものを使って、ひとつのモデルを複数にわけて、
管理するようにした際に、片方のモデルがadminのユーザーパーミッション一覧に
表示されなくて困っていた。

ユーザーパーミッションのテーブルを更新する方法がないかと
調べてみるとこんなページを発見

http://www.djangosnippets.org/snippets/698/

これを参考にパーミッションを更新するスクリプトを書いてみた。


# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
from django.contrib.contenttypes.management import update_all_contenttypes
from django.contrib.auth.management import create_permissions
from django.db.models import get_apps


"""
パーミッションを更新する
"""
class Command(BaseCommand):
def handle(self, *args, **options):

# Add any missing content types
update_all_contenttypes()

# Add any missing permissions
for app in get_apps():
create_permissions(app, None, 2)



これをインストール済みのアプリのmanagement/commands/syncpermissions.pyとして保存すれば、以下のコマンドで更新できる。


$ python manage.py syncpermissions

2010年2月22日月曜日

gitでsubversionのようなショートカットを使えるようにする

重い腰をあげて「入門git」を読んでます。

今までsvkと同じような感じでgit commit -aばかり使ってたんですが、
ステージングエリアという概念を今さら知ったり、
ファイルの変更の一部だけコミットできるんだということを知ったり、
これもっと早く読んどけばよかったなーと、ちょっぴり後悔しています。


そんな中でgitコマンドのショートカットの設定方法が書いてあったのでさっそく設定しました。


$ git config --global alias.ci "commit"
$ git config --global alias.stat "status"
$ git config --global alias.co "checkout"


これで、git ci で git commitが、 git stat でgit statusが、git coでgit checkoutが実行できるようになりました。
快適。

2010年1月17日日曜日

Amazon ELBを5つ以上作る方法

ELBはデフォルトでは1アカウントあたり5つまでしか作れない。

それ以上作ろうとすると以下のように怒られる。


$ elb-create-lb test-lb --availability-zones us-east-1d --listener "protocol=HTTP, lb-port=80, instance-port=80"
elb-create-lb: Malformed input-Exceeded quota of account xxxxxxxxxx


5つ以上作るためには、アマゾンに申請しないといけない。
申請は下記のフォームから行える。

http://aws.amazon.com/contact-us/elb-request/

2009年12月15日火曜日

Shindig + Partuzaでキャッシュさせない設定

Shindig + Partuzaで開発するならキャッシュ機能をオフにしないと不便。
Shindig,Partuzaの両方ともキャッシュに関する設定があるので、両方ともオフにする。

Shindig (PHP版)

cache_timeを0に設定する。


$ cat /path/to/shindig/php/config/local.php
'PartuzaService',
'activity_service' => 'PartuzaService',
'app_data_service' => 'PartuzaService',
'messages_service' => 'PartuzaService',
'oauth_lookup_service' => 'PartuzaOAuthLookupService',
'extension_class_paths' => '/Library/WebServer/Documents/partuza/Shindig',
'cache_time' => 0,
);


Partuza


こちらもcache_timeを0に設定する。


$ cd /path/to/partuza/html/
$ diff -u config.php.org config.php
--- config.php.org 2009-12-15 12:27:12.000000000 +0900
+++ config.php 2009-12-15 12:25:25.000000000 +0900
@@ -20,7 +20,7 @@

$config = array(
// Language to use, used for gettext / setenv LC_ALL
-'language' => 'en_US',
+'language' => 'ja_JP',

// prefix of where partuza lives, empty means it's /
'web_prefix' => '',
@@ -58,7 +58,7 @@
// If you use CacheStorageMemcache as caching backend, change these to the memcache server settings
'cache_host' => 'localhost',
'cache_port' => 11211,
-'cache_time' => 24 * 60 * 60,
+'cache_time' => 0,

2009年12月14日月曜日

デフォルトのストレージエンジンをInnoDBにする

LeopardにdmgからインストールしたMySQLのデフォルトのストレージエンジンがMyISAMだってことに最近気付いた。
デフォルトのストレージエンジンを変更するにはmy.cnfでdefault-storage-engineを指定すればOK


$ sudo vi /etc/my.cnf
$ cat /etc/my.cnf
[mysqld]
default-storage-engine=InnoDB


mysqlを再起動して、実際にInnoDBになったか確認


$ mysql -uroot
mysql> create database foo;
mysql> use foo
mysql> create table bar(id int not null);
mysql> show table status bar ¥G
*************************** 1. row ***************************
Table: bar
Create Table: CREATE TABLE `bar` (
`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

2009年12月3日木曜日

MySQL 主キーの追加・削除

忘れるのでメモ

主キーの追加


ALTER TABLE テーブル名 ADD PRIMARY KEY (カラム名1[,カラム名2...]);


主キーの削除


ALTER TABLE テーブル名 DROP PRIMARY KEY


削除時下のようなエラーがでたら制約を疑う

OperationalError: (1025, "Error on rename of './foo/#sql-b6f_3958' to './foo/bar' (errno: 150)")


制約を確認


mysql> SHOW CREATE TABLE foo;

2009年11月24日火曜日

AmazonRDSでslow queryを出力するようにする方法

AmazonRDSは、デフォルトの設定ではslow queryを出力するようになっていない。
また、MySQLが動いているサーバにアクセスすることができないので、slow queryをログファイルに出力したとしてもそれを閲覧する術がない。
RDSでも採用されているMySQL 5.1からは、slow queryをテーブルに保存できるようになったので、それを使う。

方針

  • ログはテーブル(mysql.slow_log)に出力する。

  • 実行に1秒以上かかったクエリを記録する

  • 100行以上を読み込んだクエリを記録する



普通なら以下のオプションをmy.cnfに設定すればOK。


[mysqld]
slow_query_log = ON
long_query_time = 1.000
log_output = TABLE
min_examined_row_limit=100


RDSの場合は、db-parameter-groupを作成し、パラメータを編集し、db-instanceに適用する。
今回はacme-param-grpというdb-parameter-groupを作成し、acmeというdb-instanceに適用する。

db-parameter-groupを作成

$ rds-create-db-parameter-group acme-param-grp -e MySQL5.1 -d "parameter group for acme"


db-parameter-groupのパラメータを編集
(log_outputはデフォルトでTABLEになっているので設定しない。設定しようとしてもできない)


$ rds-modify-db-parameter-group acme-param-grp \
--parameters="name=slow_query_log, value=ON, method=immediate" \
--parameters="name=long_query_time, value=1, method=immediate" \
--parameters="name=min_examined_row_limit, value=100, method=immediate"


methodに指定できる値はimmediate もしくは pending-reboot

編集したパラメータの確認

$ rds-describe-db-parameters acme-param-grp --source user
DBPARAMETER long_query_time 1 user integer dynamic true
DBPARAMETER min_examined_row_limit 100 user integer dynamic true
DBPARAMETER slow_query_log 1 user boolean dynamic true


sourceに指定できる値はuser 、system、 engine-default

db-parameter-groupをdb-instanceに適用

$ rds-modify-db-instance acme --db-parameter-group-name=acme-param-grp


db-instanceを再起動

$ rds-reboot-db-instance acme


db-instanceにdb-parameter-groupが適用されているか確認

$ rds-describe-db-instances
DBINSTANCE acme 2009-11-04T10:59:28.860Z db.m1.large mysql5.1 100 xxxx available acme.xxxxxxx.us-east-1.rds.amazonaws.com 3306 us-east-1d 1
SECGROUP xxxx active
PARAMGRP acme-param-grp in-sync


PARAMGRPがacme-param-grpになっていて、ステータスがin-syncになっていればOK。

さらにmysqlにアクセスしパラメータが有効になっているか確認する。


mysql > show global variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | ON |
+----------------+-------+
1 row in set (0.00 sec)

mysql> show global variables like 'long_query_time';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
1 row in set (0.00 sec)

mysql> show global variables like 'min_examined_row_limit';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| min_examined_row_limit | 100 |
+------------------------+-------+
1 row in set (0.00 sec)

2009年11月20日金曜日

Python 今日覚えたこと

utf-8な文字列を内部で扱う形式にデコードする際、


unicode_str = utf8_data.decode('utf-8')

とするとutf8_dataにデコードできないバイト列があった場合、UnicodeDecodeErrorの例外をだす。


UnicodeDecodeError: 'utf8' codec can't decode byte 0xb4 in position 1: unexpected code byte


デコードできない文字列を削除してしまっていいのなら'ignore'を引数で渡してやればよい。


unicode_str = utf8_data.decode('utf-8', 'ignore')


さらにdecodeよりunicodeの方が高速らしい。


unicode_str = unicode(utf8_data, "utf-8", 'ignore')


エンコード、デコードの概念はPerlと同じなので理解しやすかった。

参考
http://snippets.hachinos.net/lang/python/user/piro_suke/3/
http://d.hatena.ne.jp/methane/20090816/1250433407

2009年11月18日水曜日

saveをオーバーライドする時はforce_insert、force_updateを渡す

Djangoでdjango.db.Modelsのsaveメソッドをオーバーライドする際


def save(self):


とすると、create()とget_or_create()で以下のようなエラーを吐いた。


TypeError: save() got an unexpected keyword argument 'force_insert'


saveにforce_insert,force_updateを渡すようにしないといけないらしい。
正解はこう


def save(self, force_insert=False, force_update=False):


http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges


create() and get_or_create() will never update existing objects ¶

In [8670] a change was made to both create() and get_or_create() that affects people in two ways:

Firstly, if you have a custom save() method on your model and it is going to be called using create() or get_or_create(), you should make sure you accept the force_insert parameter (best to accept force_update as well, just like django.db.models.base.Model.save()). You don't have to do anything with these parameters, just pass them through to the Model.save() method.

2009年11月17日火曜日

urllib2でgzipされたデータを扱う

ここに書いてあった。
http://diveintomark.org/projects/misc/httpgzip.py.txt

StringIOよりcStringIOのが高速でよいと聞いたのでそちらを使ってみる。
それ以外はまるパクリ。



import urllib2, gzip, cStringIO

def get(uri):
request = urllib2.Request(uri)
request.add_header("Accept-encoding", "gzip")
usock = urllib2.urlopen(request)
data = usock.read()
if usock.headers.get('content-encoding', None) == 'gzip':
data = gzip.GzipFile(fileobj=cStringIO.StringIO(data)).read()
return data

if __name__ == '__main__':
import sys
uri = sys.argv[1:] and sys.argv[1] or 'http://mixi.jp/'
print get(uri)

2009年11月13日金曜日

MySQL 外部キー制約の追加、削除

忘れるのでメモ

MySQL 5.1で確認した。

外部キー制約の確認

SHOW CREATE TABLE テーブル名;


show create table bbs_thread;


外部キー制約の追加

ALTER TABLE テーブル名 ADD FOREIGN KEY (制約を張りたいカラム) REFERENCES 張りたいテーブル(張りたいカラム);


alter table bbs_thread add foreign key (creator_id) references accounts_user(id);



外部キー制約の削除

ALTER TABLE テーブル名 DROP FOREIGN KEY 制約名;


ALTER TABLE bbs_thread drop foreign key creator_id_refs_id_75448b6c;

2009年11月11日水曜日

Leopardでcapistrano

Leopardにはデフォルトでcapistranoが入っているが、バージョンが古いので、更新する

$ sudo gem install capistrano

複数環境にデプロイできるように。capistrano-extをインストールする

$ sudo gem install capistrano-ext