Home > Web+DB

Web+DB Archive

Docker ストレージドライバによる RHEL/CentOS 対応について

この記事の所要時間: 130

Shin x blog Advent Calendar 2013 の 8 日目です。

docker

これまで Docker を RHEL/CentOS で動かす際に懸案だったのは、AUFS への対応でした。RHEL/CentOS 6.x のカーネルは AUFS へ対応していないので、Docker を動かすには、AUFS 対応のカーネルを入れる必要がありました。

Docker 0.7 では、この対応としてストレージドライバという機構が採用されました。

ここでは、ストレージドライバによる RHEL/CentOS 対応について見てみます。

CentOS 6.5 で利用されているストレージドライバ

Docker 0.7 で採用されたストレージドライバは、Docker コンテナが利用するファイルシステムを選択する機構です。これにより、AUFS 以外のファイルシステムを利用ことが可能になっています。0.7 では、aufs / devicemapper / vfs の 3 つのドライバーが存在します。

この中で、RHEL/CentOS では devicemapper というドライバが利用されています。

どのドライバが利用されているかを確認するのには、docker info コマンドを実行します。

CentOS 6.5 で docker info コマンドで確認すると下記のようになります。Driver: の箇所が devicemapper と表示されており、これがドライバとして利用されているのが分かります。

[vagrant@localhost ~]$ sudo docker info
Containers: 47
Images: 24
Driver: devicemapper // <----
  Pool Name: docker-253:0-1835642-pool
  Data file: /var/lib/docker/devicemapper/devicemapper/data
  Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
  Data Space Used: 1790.1 Mb
  Data Space Total: 102400.0 Mb
  Metadata Space Used: 4.7 Mb
  Metadata Space Total: 2048.0 Mb

ちなみに ubuntu 上で docker info を実行すると、こちらは aufs と表示されます。

vagrant@precise64:~$ sudo docker info
Containers: 2
Images: 1
Driver: aufs
 Root Dir: /var/lib/docker/aufs
  Dirs: 5
  WARNING: No swap limit support

AUFS 以外のストレージドライバ採用の経緯

AUFS 以外のストレージドライバとして何を利用するかの経緯については、community.redhat.com にエントリがあります。

Adventures in Dockerland | Open Source Community

まず、AUFS について。AUFS は、現在の Ubuntu カーネルでは非推奨となっていて、いずれは削除されるようです。

このため、RHEL/CentOS 対応を行うかどうかに関わらず、AUFS 以外のストレージドライバへの対応が必要だったように思います。

そして、AUFS に変わるコピーオンライトなストレージとして、下記の 4 つが検討されていました。

  • overlayfs
  • btrfs
  • lvm snapshots
  • lvm thin provisioning

結果的には、最後の lvm thin provisioning が採用されており、devicemapper ドライバとして実装されています。こうして RHEL/CentOS でも Docker が利用できるようになりました。

件のエントリでは、それぞれの方法についての考察なども書かれていますので、気になる方は読んで見てください。

CentOS 6.4 で Docker を動かす

Docker 0.7 で対応した lvm thin provisioning は、RHEL/CentOS 6.4 からサポートされているので、CentOS 6.4 環境でも Docker が動きそうです。

そこで、Vagrant 上の CentOS 6.4 でも試してみました。インストール方法などは前エントリのままです。

$ cat /etc/redhat-release
CentOS release 6.4 (Final)

$ sudo docker run -i -t centos /bin/bash
bash-4.1#

$ sudo docker info
Containers: 0
Images: 0
Driver: devicemapper
  Pool Name: docker-253:0-262375-pool
  Data file: /var/lib/docker/devicemapper/devicemapper/data
  Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
  Data Space Used: 291.5 Mb
  Data Space Total: 102400.0 Mb
  Metadata Space Used: 0.7 Mb
  Metadata Space Total: 2048.0 Mb

ちゃんと動いてますね。docker info コマンドでは、Driver は、ちゃんと devicemapper になっています。

CentOS 6.3 で Docker を動かない?

では逆に動かないところも見てみたいので、次は CentOS 6.3 でも試してみました。

適当な Box ファイルが手元に無かったので、AWS Marketplace にある CentOS 6.3 の AMI を使いました。

# cat /etc/redhat-release
CentOS release 6.3 (Final)

# docker run -i -t centos /bin/bash
bash-4.1#

# docker info
Containers: 9
Images: 2
Driver: devicemapper
  Pool Name: docker-202:64-9417-pool
  Data file: /var/lib/docker/devicemapper/devicemapper/data
  Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
  Data Space Used: 858.7 Mb
  Data Space Total: 102400.0 Mb
  Metadata Space Used: 1.7 Mb
  Metadata Space Total: 2048.0 Mb

あれ?ちゃんと動いていますね。Driver も devicemapper になっています。コンテナ内で yum など使っていくつかパッケージをインストールしたり、docker commit でイメージを作ったりしましたが、正常に動いているようでした。

調べてみると、lvm thin provisioning は正式採用されたのは 6.4 ですが、6.3 の段階で Technology Preview として実装されていたようです。なるほど。

LVM の Thin Provisioning 型 LV とスナップショット利用

6.2 なら、Driver: vfs となり、違った挙動が見れるのかもしれません。

利用するストレージドライバの決定

Docker が環境に応じてどのようにストレージドライバを決定しているかを見てみました。

利用するストレージドライバの決定は、graphdriver/driver.go にある New メソッドにて行われます。下記が該当ソースです。

まず、環境変数やオプションによるドライバ指定を処理します。すでにドライバが指定されていれば、そのドライバを利用します。

その後に priority 変数の順に利用するドライバを決定します。それぞれのドライバが利用できるかチェックして、利用できるならば、それを使います。priority は、下記のように定義されているので、aufs / devicemapper / vfs の順でチェックが行われます。

このように通常、ストレージドライバは自動で決定されます。

一方、環境変数や -s オプションを使うことで利用するドライバを指定することも可能です。

下記では、docker コマンドに -s オプションに vfs を指定して、docker デーモンを起動しています。docker info を見ると vfs がドライバとして利用されていることが分かります。

$ sudo service docker stop # docker を一旦停止
$ sudo docker -d -s vfs # docker コマンドから、docker デーモンを起動

$ sudo docker info
Containers: 0
Images: 0
Driver: vfs

さいごに

Docker 0.7 でサポートされたストレージドライバによる RHEL/CentOS サポートについて見てみました。

コピーオンライトなストレージは、Docker における重要な技術なので、今後もより良い環境を求めて、他のファイルシステムへの対応が進みそうです。

ここで見たようにストレージドライバに関しては RHEL/CentOS 6.4 環境でも動いているので、6.5 環境がまだ無い人は試してみて下さい。

参考

  • コメント (Close): 0
  • トラックバック (Close): 0

CentOS 6.5 に Docker をインストールしてみた

この記事の所要時間: 330

Shin x blog Advent Calendar 2013 の 7 日目です。

docker

いよいよ CentOS 6.5 から Docker がサポートされたということで、実際にインストールしてみました。

Vagrant で CentOS 6.5 環境

Vagrant で CentOS 6.5 環境を作ります。( CentOS 6.5 環境がある場合は不要です。)

CentOS 6.5 の Box ファイルには、Opscode が提供している Bento プロジェクトのものを利用しました。

$ vagrant init opscode-centos65 http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box
$ vagrant up
$ vagrant ssh

Docker インストール

では、CentOS に Docker をインストールします。

Docker は、EPEL リポジトリで配布されているので、追加しておきます。あとは yum で、docker-io パッケージを入れるだけです。docker の起動設定なども行っていきます。

[vagrant@localhost ~]$ rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
[vagrant@localhost ~]$ yum -y install docker-io
[vagrant@localhost ~]$ chkconfig docker on
[vagrant@localhost ~]$ service docker start

Docker で Hello World

ではさっそく Docker を試してみましょう。Docker 公式の centos イメージを使って、コンテナ内で echo を実行してみます。

[vagrant@localhost ~]$ sudo docker run centos /bin/echo "Hello World"
Hello World

centos イメージのバージョンを見てみました。まだ 6.4 のようです。

[vagrant@localhost ~]$ sudo docker run centos /bin/echo /etc/redhat-release
CentOS release 6.4 (Final)

/bin/bash を実行すれば、コンテナ内でシェルが起動できます。

[vagrant@localhost ~]$ sudo docker run -i -t centos /bin/bash
bash-4.1#

Vagrantfile

CentOS 6.5 で Docker をインストールする Vagrantfile を書いています。ご参考まで。

さいごに

ついに CentOS で Docker が来ましたね。これで Docker 探索が捗ります:D

ちなみに AWS でも試そうとしたのですが、Amazon Linux ではインストールはできたもの動作が不安定でした(たまたまかもですが)。CentOS は公式も AMI はまだ 6.4 のままだったので、まだ試していません。

あ、公式サイトでも RHEL/CentOS のインストール手順が掲載されましたね。(昨晩は無かった。。。)

Requirements and Installation on Red Hat Enterprise Linux / CentOS – Docker Documentation

参考

Docker Documentation – Docker Documentation
Docker: Linuxコンテナを使ってアプリケーションの配置を支援する
仮想環境構築に docker を使う – apatheia.info

  • コメント (Close): 0
  • トラックバック (Close): 0

とある CMS を使ったサイトに Varnish を導入した話

この記事の所要時間: 741

Shin x blog Advent Calendar 2013 の 6 日目です。

varnishcache-logo

とあるサイトに、Varnish をリバースプロキシとして導入して、半年が経過しました。

導入した経緯やその効果など書いてみたいと思います。

Varnish とは

Varnish は、HTTP アクセラレータです。Web サーバのリバースプロキシとして動作し、キャッシュを生かして高いパフォーマンスを発揮するのが特徴です。また、VCL という独自の設定言語を持ち、これにより状況に応じた設定を柔軟に行うことができます。

導入の経緯

このサイトでは LAMP 構成の CMS を利用しており、インフラには AWS を利用しています。基本、閲覧が中心なのですが、イベント時に多数のアクセスがあります。S3 や CloudFront も検討したのですが、コンテンツを数分おきに更新する必要があるので今回は採用しませんでした。

下記が構成のイメージです。なお図はかなり簡略化しており、実際の構成とは異なります。

before

こちらでパフォーマンスの問題が発生したので、CMS が生成したページ全体をキャッシュとして高速に返すよう Varnish Cache を導入しました。

after

当初、nginx の proxy_cache を試したのですが、同一 URL へ多数のリクエストが来ている状況でキャッシュのライフタイムが切れると、バックエンドへ同じ URL に対して複数のリクエストが飛んでしまい、PHP アプリケーション側に負荷がかかるという現象があったので、今回は取りやめました。

Varnish では、同一 URL に対して複数のリクエストがあっても、バックエンドへは 1 リクエストしか飛ばないようになっています。

導入

Varnish Cache は、m1.large インスタンスの Amazon Linux サーバに yum からインストールしました。

$ sudo rpm --nosignature -ivh http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm
$ sudo yum -y install varnish

もし varnish のプロセスが落ちた場合に自動で再起動するように monit も入れています。varnish は、monit から起動しています。

$ sudo yum install monit
$ sudo chkconfig monit on

$ sudo vim /etc/monit.d/varnish
check process varnish with pidfile /var/run/varnishd.pid
start program = "/etc/init.d/varnish start"
stop program = "/etc/init.d/varnish stop"

$ sudo service monit start

監視

死活、性能監視として Zabbix を採用していたので、Varnish サーバも Zabbix で監視しています。

Zabbix のテンプレートは下記を利用しています。

rdvn / zabbix-templates

効果

Varnish 導入の効果はてきめんでした。

ELB 経由で来たリクエストは全て Varnish を経由しており、キャッシュが有効な期間(3分で設定しています)は、キャッシュを返すだけなので非常に高速です。

またキャッシュが切れた場合も、バックエンドへは 1 URL に対して 1 リクエストしか来ないため、バックエンドへの大きな負荷にはなりません。(ある特定の数ページのみが際立ってアクセスされるサイトなので)

導入前は、ピーク時にはバックエンドの Web サーバが応答を返せない場合があったのですが、それも一切無くなりました。

はっきりいって、下手に CMS を細々チューニングするよりも遥かに効果があります。導入後も安定して動いており、Varnish 起因の問題も発生していません。

参考の数値として、270,000 req/m のリクエストが来た時(Varnish 上位にある ELB の RequestCount)、Varnishサーバ(m1.large)の CPU 利用率は 56% 程度でした。その際の Client requests received(client_req)は 5,000 弱でした。外部からの閲覧に関して遅延など異常はありませんでした。

VCL

Varnish の特徴として挙げられるのが VCL = Varnish configuation Language による設定です。

VCL は小さな DSL となっており、プログラミングのように設定を書くことができます。イメージとしては nginx の設定をよりコードっぽくした感じですね。

例えば、下記は UserAgent を見て、iPhone が含まれていたら、http://sp.example.com/ へリダイレクトさせる VCL です。

if (req.url == "/") {
  if (req.http.user-agent ~ "(?i)iPhone") {
    error 750 "Moved Temporarily";
  }
}

sub vcl_error {
  if (obj.status == 750) {
    set obj.http.location = "http://sp.example.com/";
    set obj.status = 302;
    return (deliver);
  }
}

参考

構築にあたって参考にしたサイトです。柔軟に設定できるので、色々と調べながらの調整になりました。

さいごに

Varnish は、手軽に導入できるわりに簡単にパフォーマンスを向上させることができます。とくに CMS のような閲覧が中心となるアプリケーションには大きな効果を発揮します。

CMS 自体にもキャッシュ機構などは付いていますが、それよりの上位でキャッシュできるので、バックエンドが負荷に耐えられない構成でもうまく吸収することができます。

個人的には VCL がプログラムっぽく書けるのが設定しやすくて好きです。

アクセス過多でパフォーマンスに問題が出た際に使える切り札として覚えておくと、いつか役立つ日が来るやもしれません。

公式サイトのドキュメントが英語なので、日本語でのまとまった情報があればと思うのですが、詳しい方(チラチラッ、Kindle とかでどうでしょうか 😀

おまけ Vagrantfile

簡単に Varnish を試せるように Vagrantfile を作りました。

vagrant up して、http://192.168.33.50/sample.php にアクセスすると現在日時が表示されます。こちらがバックエンドアプリケーションの想定ですので、リロードする度に表示される日時は変わっていきます。

Varnish は 6081 port で待ち受けているので、http://192.168.33.50:6081/sample.php にアクセスするとその時点でのページがキャッシュされます。リロードするとキャッシュが返され、日時が変更されていないことが分かるでしょう。

shin1x1/vagrant-varnish

$ git clone https://github.com/shin1x1/vagrant-varnish
$ vagrant up
  • コメント (Close): 0
  • トラックバック (Close): 0

PostgreSQL INSERT で外部キーの被参照テーブルに ROW SHARE ロックがかかる

この記事の所要時間: 350

postgresql

PostgreSQL で、トランザクション処理中にあるテーブルに INSERT 文を発行すると、並列で実行している別処理にて、そのテーブルの外部キーの被参照テーブルへのEXCLUSIVE ロックが取得できない現象があったのでメモ。

再現

検証用にテーブルを2つ(products, orders)を用意します。orders テーブルの product_no には products テーブルへの外部キーを設定します。

CREATE TABLE products (
  product_no integer PRIMARY KEY,
  name text,
  price numeric
);

CREATE TABLE orders (
  order_id serial PRIMARY KEY,
  product_no integer REFERENCES products (product_no),
  quantity integer
);

products テーブルにレコードを登録しておきます。

test=> INSERT INTO products VALUES(1, 'item1', 1000);

トランザクションを開始して orders テーブルへ INSERT 文を実行します。トランザクションは実行中のままにしておきます。

test=> BEGIN;
BEGIN
test=> INSERT INTO orders(product_no, quantity) VALUES(1, 1);
INSERT 0 1

別ターミナルを開いて、psql を実行します。こちらで products テーブルの EXCLUSIVE ロックを取得しようとするとロック待ちになります。

% psql test
test=> BEGIN;
BEGIN
test=> LOCK TABLE orders IN EXCLUSIVE MODE;

pg_locks でロック状態を確認すると、products テーブルに ROW SHARE ロックがかかっていることが分かります。これが EXCLUSIVE ロックに競合しているために、ロック待ちが発生しました。

% psql test
test=> SELECT relname,pg_locks.mode FROM pg_locks INNER JOIN pg_class ON pg_class.oid=pg_locks.relation WHERE mode<>'AccessShareLock';
   relname   |       mode
-------------+------------------
 orders      | RowExclusiveLock
 products    | RowShareLock
(2 rows)

PostgreSQL のドキュメントを見ると ROW SHARE ロックは、SHARE ROW EXCLUSIVE ロック以下であれば競合しないので、SHARE ROW EXCLUSIVE MODE に変更するとロックを取得することができました。

% psql test
test=> BEGIN;
BEGIN
test=> LOCK TABLE orders IN SHARE ROW EXCLUSIVE MODE;
LOCK TABLE

INSERT 以外では、UPDATE 文で外部キーを更新(上記例では orders.product_no を更新)した場合も同様に外部キーの被参照テーブル(products)に ROW SHARE ロックがかかりました。

前にも

実際に INSERT や UPDATE を実行しているテーブルへロックがかかるのは容易に想像できるのですが、外部キーの被参照テーブルだとうっかり見落としがちなので注意が必要ですね。

実はこの現象を調べてたところ、7 年前に自分で書いたエントリが見つかりました(PHP4 + PostgreSQL7.4!)。すっかり忘れていた内容だったので書いてて良かったのですが、同じとこで躓くのが成長していないというかアホというか;-p

参照

制約 | PostgreSQL9.2.4文書
明示的ロック | PostgreSQL9.2.4文書

  • コメント (Close): 0
  • トラックバック (Close): 0

FireMobileSimulator でタブ毎に端末選択を有効にする

この記事の所要時間: 029

FireMobileSimulator でタブ毎に端末選択を有効にする方法です。

firemobilesimulator

単にチェック入れるだけです。Facebook で教えて頂いたのですが、こんな機能あったんですね。。。

調べてみると2011年には対応していたようです。

これまで全然気づかず、携帯サイトのテストは Firefox、スマートフォンのテストは Chrome 、と別ブラウザでやったり、別タブで開いていた Gmail や Facebook がいつの間にかおかしくなったりしてました。

こりゃ便利。

  • コメント (Close): 0
  • トラックバック (Close): 0

nginx で Too many open files エラーに対処する

この記事の所要時間: 754

nginx では1プロセスで多くのアクセスを捌くので、アクセス数が増えるとToo many open filesエラーが発生することがあります。
ここでは対処法と調べた内容を残しておきます。

1. fs.file-max の確認

まず fs.file-max の値を確認しておきます。fs.file-max は、システム全体でのファイルディスクリプタの上限数となっており、この値以上のファイルディスクリプタは確保することができません。
現在設定されている値は以下で確認できます。

$ cat /proc/sys/fs/file-max
167488

通常は上記の値で問題無いと思いますが、もしこの値が不足しているようなら設定値を更新します。

$ sudo -s
# echo 320000 > /proc/sys/fs/file-max
# cat /proc/sys/fs/file-max
320000

再起動時に有効となるように /etc/sysctl.conf に記述しておきます。

$ sudo vim /etc/sysctl.conf
fs.file-max=320000

2. worker_rlimit_nofile の指定

次はプロセス毎のファイルディスクリプタ上限数を増やします。
nginx ではプロセス毎のファイルディスクリプタ上限数を指定する設定項目 worker_rlimit_nofile が用意されているので、これを記述します。設定する値は worker_connections の3〜4倍くらいが良いでしょう。

$ sudo vim /etc/nginx/nginx.conf

worker_rlimit_nofile  4096;

(snip)

events {
      worker_connections  1024;
}

設定変更後は nginx をリスタートしておきます。

$ sudo /etc/init.d/nginx restart

設定値が反映されているか確認しておきます。ワーカープロセスのプロセスID で limits を確認します。下記では「Max open files」の行がこれに当たるのですが、Soft Limit と Hard Limit 双方とも worker_rlimit_nofile で指定した値が設定されていることが分かります。

$ ps ax | grep nginx | grep worker
17064 ?        S      0:00 nginx: worker process                   

$ cat /proc/17604/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            10485760             unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             8191                 8191                 processes 
Max open files            4096                 4096                 files     
Max locked memory         32768                32768                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       8191                 8191                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0   

プロセス毎のファイルディスクリプタ上限数を設定する方法として、ulimit コマンドや /etc/security/limits.conf による記述などがあるのですが、これらは環境によってはデーモン起動では適用されなかったり、起動ファイルに記述する必要があるなど注意が必要です。

nginx ではワーカープロセス初期化処理にて worker_rlimit_nofile で指定した値をsetrlimit(2) で設定するので、ulimit などで指定された値は上書きされます。(ソフトリミット、ハードリミット両方)

参照

おまけ

調べている中で色々と試したのでメモ。
もし同じようなことを試すなら、壊しても問題無い環境で行ないましょう。(私は AWS で EC2 インスタンス(AmazonLinux)を起動して試しました。) 

fs.file-max を 0 にする。

$ sudo -s
# echo 0 > /proc/sys/fs/file-max
# cat /proc/sys/fs/file-max
0
  • 元のシェルは動作している
# ls -a
. ..
  • 一般ユーザに戻るとだめ
# exit
$ ls -la
-bash: start_pipeline: pgrp pipe: Too many open files in system
-bash: /bin/ls: Too many open files in system
  • SSH でログインできない

ローカルから別コネクションでSSHでログインしようとしたのですが、ダメでした。

% ssh ec2-user@xxx.xxx.xxx.xxx
ssh_exchange_identification: Connection closed by remote host

結局、Management Console からインスタンスを stop & start することに。。。

  • /var/log/messages にログが記録される。

再起動後に /var/log/messages を見ると下記のようなログが記録されていました。

Feb 11 13:36:51 ip-10-121-3-15 kernel: [  221.357976] VFS: file-max limit 0 reached

nginx で worker_rlimit_nofile が適用される箇所

nginx が worker_rlimit_nofile で指定した値をカーネルに設定する箇所をソースコードから探してみました。(nginx1.2.7)
src/os/unix/ngx_process_cycle.c の861行目付近でこの処理が行われています。root でマスタープロセスを実行している場合は、この箇所ではまだ root ユーザで動作しているので、ソフトリミット、ハードリミット共に worker_rlimit_nofile で指定した値が設定されています。

  • コメント (Close): 0
  • トラックバック (Close): 0

Webシステム開発に便利な7つのツール

この記事の所要時間: 98

Webシステム開発で使っている便利なツールをあげてみました。

あらためて社内の開発環境を見直す機会があったので、使っているツールを並べてみました。こうして見ると色々なツールを使って開発をしていますね。わりと定番系なものが多いですが、良かったら参考にどうぞ。

1. Apache / PostgreSQL / PHP

Mac OS X に MacPorts でインストールしたApache / PostgreSQL / PHP 環境を使って開発をしています。

PHPは5.3、PostgreSQLは8.4 or 9.0です。

ただ旧バージョンのPHPを使ったり、Linux でなければ動かないモジュールを使うこともあるので、その際は社内のCentOSサーバにSSHで入って開発したりもします。

Vimを使ってるので、SSHで入ればどのサーバでも開発できるのは利点ですね。

Mac を使い出して、しばらくはこれで良かったのですが、たまにLinux環境が欲しい時があったり、社内スタッフと開発環境を揃えることを考えると、vmwareでLAPP環境を構築しておいて、それをみんなで共有するほうが良いかもしれません。

2. Vim

開発エディタにはVimを使っています。

昔はWindowsのPeggyを使っていたのですが、サーバにSSHで入ってメンテナンスする時にVimでもたもたするのがいやだったのと、何よりVimで颯爽とコードを書くのがかっこ良さそうだったのでメインで使うようになりました。

シェルの延長上で使えて、起動にストレスが無いですね。基本コマンドは手に馴染んでいるので、純粋に「書く」という行為については文句無いです。IDE並とは言わないですが、拡張していけば補完なんかもそれなりに効きます。

一度覚えておけば、Unix系を使う限りは下手すれば一生ものになるので楽できますね。

ただ絶対にVimじゃなきゃイヤだということも無くて、最近はNetBeansも気になったりもしてます。(Vimプラグインとかあるみたいだし)

http://www.vim.org/

3. xdebug

xdebug

PHPで開発している人にはおなじみのxdebugを入れてます。

インストールするだけで、PHPのエラーメッセージを見やすくしたり、スタックトレースを表示してくれたり、var_dump()した時が見やすくなったりで開発する時には嬉しい機能満載です。

例えば、PHPエラーが見やすくなって、スタックトレースが表示されたり。

xdebug_php_error

例えば、var_dump()で変数をダンプすると見やすくなってたり。

xdebug_var_dump_array

オブジェクトなら、インスタンス変数のアクセス権も表示してくれます。

xdebug_var_dump_object

他にも実行コードがトレースできたり、コードカバレッジを計測できたりと、とても重宝しています。

xdebug は pecl コマンドでインストールできます。

$ sudo pecl install xdebug

もちろん yum でもインストールすることもできます。

$ sudo yum install php-pecl-xdebug

PHPで開発する人ならぜひ開発環境に入れてみてください。

xdebug

4. Firefox

Firefox ブラウザ無料ダウンロード

Webを見るときのブラウザはChromeとFirefoxを併用していますが、やはりWebシステム開発に使うならFirefoxがメインです。

理由はアドオンが充実していることです。もちろんChromeでも同様のことができると思うのですが、使い慣れているということとFirefoxにしか無いアドオンがあるのが大きいですね。

豊富なアドオンの中でも特によく使うものをあげてみました。

4-1. Firebug

言うまでもなく定番のツールですね。

表示されているHTMLタグを確認したり、CSSを修正したり、JavaScriptのデバッグに使ったりと機能満載なのですが、Webシステム開発という意味では「ネット」が便利です。

ここを開くとブラウザからWebサーバへどのようなHTTP通信を行っているかが分かります。あるページを読み込むと画像やCSSが次々と読み込まれるのが分かります。

firebug_1

さらに各行をクリックするとヘッダを含む、リクエスト、レスポンスの内容が確認できます。

firebug_2

POSTの内容も見やすいです。

firebug_3

Firebug :: Add-ons for Firefox

4-2. Selenium IDE

自動ブラウザテストツールです。

Selenium IDEを使うとブラウザでの操作、入力を記録することができ、何度でも同じ操作を繰り返すことができます。さらにページやURL、Cookieなどの内容が想定されている値になっているかを検証することができます。

特に複雑な入力を伴うような購入フォームやユーザ登録などは、操作を記録してテストケースを作成しておくと、開発途中で動作確認するときに同じ操作を人がするのではなくツールにさせることができて、効率的です。

操作さえ慣れればかなり適用範囲の広いツールなので、Webシステムを開発する人でまだ使っていない人は一度試してみてください。

Selenium IDE

4-3. FireMobileSimulator

携帯電話対応のWebシステム開発では欠かせないツールです。

このツールを使うと擬似的に各キャリア、各機種の携帯電話からのアクセスを行うことができます。膨大な機種データが登録されており、新機種への対応も随時行われています。

画面サイズや絵文字表示などもシミュレートしてくれるので表示についても確認できます。(もちろん最終的には実機テストが必要ですが)

FireMobileSimulator.org

5. SimpleTest / PHPUnit

PHPでユニットテストを行うツールです。

SimpleTestはCakePHPで開発するときに使っています。それ以外で開発するときはPHPUnitを使うようにしています。

ユニットテストの効能については様々な情報があり、あえてここで触れる必要はないと思いますが、個人的に感じているメリットが2つあります。

5-1. 安心感を得る

テストを書いて、いつでもテストを実行できる環境を作っておくとそのコードが想定どおりに動く安心感が得られます。リファクタリングなどでコードを書き換える時もそうですが、開発が終了して何ヶ月か経った後でもそのコードが確実に動くことが保証できます。(もちろんテストを書いた範囲においては、ですが)

あと他人(数ヶ月後の自分を含む)が書いたコードがどのような動作を想定したものかを掴むことができます。

こうした書いたコードに対する安心感を得られるのが大きいです。

5-2. テストしやすいコード=独立性の高いコードを書くようになる

ユニットテストを書きだすと分かるのですが、メソッド内で様々なクラスと密結合になっていて様々な処理を詰め込んでいるとテストが書きづらくなります。

テストを書く段でそういった密結合を解きほぐしていって、メソッド毎の処理を小さな単に分けていくと、テストが書きやすくなり、自然と独立性の高い実装となっていきます。こうした独立性の高い実装はその役割がはっきりしているので再利用が容易となります。

独立性の高いコード、疎結合なコードを書くための訓練といういう意味でもテストコードを書くというのは効果があると思います。

とはいえ、やはりテストを書くのが面倒な場面はあるので、今はいかに効率的に(手間をかけずに)テストが書けるかを模索しています。

SimpleTest – Unit Testing for PHP
PHPUnit Manual

6. Redmine

社内の開発タスク管理にはRedmineを使ってます。

3,4年くらい前にBTSを導入しようとして、TracやMantisなども検討したのですが、複数プロジェクトが管理できるのとGUIが取っ付きやすそうだったので、Redmineを導入しました。

まだ今の使い方が正解かどうかは分からないですが、ざっくりとしたタスク単位でチケットを発行しています。チケットには仕様の詳細までは書いていないことの方が多くて、実際にアサインする時に打ち合わせで説明しています。

このあたりをどう活用しているのかは他の人と情報共有したいところですね。

Redmineの機能としては、やっぱりGitやSubversionなどのSCMと連携できて、コミットとチケットを関連付けられるのが便利ですね。チケットに対する変更差分をブラウザでさっと確認できます。

SCMを初めて使う人にとっても、commit / push した内容がブラウザで確認できるので分かりやすいみたいです。

http://redmine.jp/

7. Git / Subversion

ソースコードは、Git / Subversion で管理しています。

SCMはCVSから使いはじめました。その頃は一人で開発をやっていましたが、変更履歴や差分が取れるし、開発環境とテストサーバなど複数の箇所で同じソースが簡単に取得できるので、それ以来利用しています。(当時使っていたPeggy ProにCVS連携が付いていたのも一つのきっかけになりました。)

それからSubversionを使い出して、CVSのソースはSubversionに移行しました。スタッフが入って、複数人での開発になってからはさらにツールの重要性が増しました。誰がどう変更したかを間違いなく把握できるので、自分が書いたコードという意識も高まりますし、疑問点があってもすぐに書いた本人に聞くことができます。

そういえば、Redmineを使い出したのもSubversion連携があったというのもありますね。

そして昨年くらいからGitを使い出しました。まだベストプラクティスが決まらず、本来の分散リポジトリという部分は生かせていないですが、基本操作は浸透してきたので、そろそろ色々と模索していこうと思います。

Git – Fast Version Control System
Apache Subversion

ツールの提供者に感謝!

こうしてあらためて見ると普段多くのツールを活用して開発を行っていることが分かります。

ここで挙げたツールはいずれもオープンソースで配布されているものであり、無償で利用することができます。当然ながこうしたツールは誰かが開発しているもので、さらにテストをしたり、マニュアルを書いたり、それを翻訳する人がいたり、と多くの人の力で提供されています。

今後も感謝の気持ちを忘れず、どんどん活用していきたいですね。

nginx+php-fpmをyumでインストールして、WordPress/CakePHPを動かす設定

この記事の所要時間: 4242

www.1×1.jpの環境をApache+mod_phpな環境から、nginx+php-fpmな環境へ移行しました。

さくらVPSのCentOS5.5環境にnginx+php-fpmをyumでインストールして、CakePHPとWordPressを動かす設定を行いました。

このエントリでは導入ということで、インストールから、とりあえず動作するところまでをご紹介します。

0. 構成

nginx+php-fpm環境にCakePHPとWordPressをインストールします。

それぞれ以下のURLでアクセスできるようにします。

http://www.1×1.jp/ -> CakePHP

http://www.1×1.jp/blog/ -> WordPress

1. インストール

nginxとphp-fpmだとソースからインストールするパターンが多いですが、ここではyumでインストールします。

1-1. remiリポジトリを登録

centosリポジトリにはnginx、php-fpmが含まれていないので、epel / remiリポジトリを登録します。

$ sudo wget http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
$ sudo wget http://rpms.famillecollet.com/enterprise/remi-release-5.rpm
$ sudo rpm -Uvh remi-release-5*.rpm epel-release-5*.rpm

1-2. yumでインストール

nginxとphp-fpmをyumでインストールします。–enablerepo=remi で1-1で登録したremiリポジトリを指定することを忘れないようにしましょう。

他にphp-mbstringやphp-cliなんかもいれてますが、このあたりはお好みで。

$ sudo yum -y install nginx php-cli php-fpm php-mbstring --enablerepo=remi

2011/05/16現在インストールされるバージョンは以下です。

$ /usr/sbin/nginx -V
nginx version: nginx/0.8.54
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-50)
TLS SNI support disabled
configure arguments: --user=nginx --group=nginx --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/subsys/nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module --with-http_perl_module --with-mail --with-file-aio --with-mail_ssl_module --with-ipv6 --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'
$ /usr/sbin/php-fpm -v
PHP 5.3.6 (fpm-fcgi) (built: Apr 15 2011 18:13:37)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

2. nginxの設定

nginxの設定を行います。yumでインストールすると設定ファイルは /etc/nginx 以下に配置されます。/etc/nginx/nginx.confがメインの設定ファイルになっているのでこれを編集します。

Apacheの設定を知っていれば何となく理解できるかと思いますが、リクエストURIがlocationディレクティブにマッチすれば該当ブロックの設定が適用されます。

2-1. バージョン情報の出力をoff

HTTPレスポンスヘッダやエラーページでnginxのバージョン情報を出力しないようにします。

server_tokens     off; 

2-2. gzipをon

gzipをonにします。

    gzip  on;

2-3. /blogをWordPressに

/blogというURLに対してWordPress用の設定を行っています。

内容はなんとなく分かるかと思いますが、前半のブロックでApacheでいうmod_rewriteのような設定を行っています。これにより存在しないディレクトリ、ファイルへのアクセスがあった場合は/blog/index.php?q=PATHへURLをリライトします。

後半のブロックがphp-fpm(FastCGI)の設定です。^/blog/.+\.php$にマッチするリクエストであれば、php-fpmへリクエストを送信します。

        # wordpress
        location /blog {
            alias   /path/to/wordpress;
            index  index.php;

            if (!-e $request_filename) {
                rewrite ^/blog(.+)$  /blog/index.php?q=$1 last;
                break;
            }
        }
        location ~ ^/blog/.+\.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info ^/blog(.+\.php)(.*)$;
            fastcgi_param  SCRIPT_FILENAME  /path/to/wordpress$fastcgi_script_name;
            fastcgi_intercept_errors on;
            include        fastcgi_params;
        }

2-4. /blog以外のURLはCakePHPに

/blog以外のURLに対してCakePHP用の設定を行っています。

2-3と同じく、前半のブロックでURLリライトの設定を行っています。存在しないディレクトリ、ファイルへのアクセスがあった場合は/index.php?url=PATHへURLをリライトします。

後半のブロックでは、\.php$にマッチするリクエストであれば、php-fpmへリクエストを送信します。

        # cakephp
        location / {
            root   /path/to/cakephp/app/webroot;
            index  index.php index.html index.htm;

            if (!-e $request_filename) {
                rewrite ^(.+)$  /index.php?url=$1 last;
                break;
            }
        }
        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /path/to/cakephp/app/webroot$fastcgi_script_name;
            fastcgi_intercept_errors on;
            include        fastcgi_params;
        }

2-5. .htaccess / .git / .svnはアクセス禁止に

.htaccess / .git / .svnはアクセスできないようにしています。

        location ~ (\.ht|\.git|\.svn) {
            deny  all;
        }

2-6. /etc/nginx/nginx.conf

変更した /etc/nginx/nginx.conf は以下です。

#######################################################################
#
# This is the main Nginx configuration file.  
#
# More information about the configuration options is available on 
#   * the English wiki - http://wiki.nginx.org/Main
#   * the Russian documentation - http://sysoev.ru/nginx/
#
#######################################################################

#----------------------------------------------------------------------
# Main Module - directives that cover basic functionality
#
#   http://wiki.nginx.org/NginxHttpMainModule
#
#----------------------------------------------------------------------

user              nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
#error_log  /var/log/nginx/error.log  info;

pid        /var/run/nginx.pid;


#----------------------------------------------------------------------
# Events Module 
#
#   http://wiki.nginx.org/NginxHttpEventsModule
#
#----------------------------------------------------------------------

events {
    worker_connections  1024;
}


#----------------------------------------------------------------------
# HTTP Core Module
#
#   http://wiki.nginx.org/NginxHttpCoreModule 
#
#----------------------------------------------------------------------

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    server_tokens     off;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;
    
    server {
        listen       80;
        server_name  _;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        # wordpress
        location /blog {
            alias   /path/to/wordpress;
            index  index.php;

            if (!-e $request_filename) {
                rewrite ^/blog(.+)$  /blog/index.php?q=$1 last;
                break;
            }
        }
        location ~ ^/blog/.+\.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info ^/blog(.+\.php)(.*)$;
            fastcgi_param  SCRIPT_FILENAME  /path/to/wordpress$fastcgi_script_name;
            fastcgi_intercept_errors on;
            include        fastcgi_params;
        }

        # cakephp
        location / {
            root   /path/to/cakephp/app/webroot;
            index  index.php index.html index.htm;

            if (!-e $request_filename) {
                rewrite ^(.+)$  /index.php?url=$1 last;
                break;
            }
        }
        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /path/to/cakephp/app/webroot$fastcgi_script_name;
            fastcgi_intercept_errors on;
            include        fastcgi_params;
        }

        location ~ (\.ht|\.git|\.svn) {
            deny  all;
        }
    }

    # Load config files from the /etc/nginx/conf.d directory
    include /etc/nginx/conf.d/*.conf;
}

3. php-fpmの設定

次にphp-fpmを設定します。 yumでインストールすると設定ファイルが/etc/php-fpm.confと/etc/php-fpm.d/www.confに配置されます。

php-fpm.confはとりあえずデフォルトのままで、www.confを編集します。変更するのは4箇所です。

3-1. 実行ユーザ

php-fpmの実行ユーザをnginxと同一にしておきます。

user = nginx

3-2. 実行グループ

実行ユーザと同じく実行グループもnginxと同一にしておきます。

group = nginx

3-3. プロセス生成方法

php-fpmの実行プロセス生成方法を指定します。動的にプロセス数を増減するならdynamicを、設定したプロセス数に固定するならstaticを指定します。

ここでは利用するリソースが把握しやすいstaticにします。

pm = static

3-4. 生成するプロセス数

生成するphp-fpmプロセス数を指定します。ここでは10プロセスを生成します。

他にpm.start_servers / pm.min_spare_servers/ pm.max_spare_serversといったプロセス数に関する設定がありますが、pm=staticの場合は影響ありません。

pm.max_children = 10

3-5. expose_phpをoffに

HTTPレスポンスヘッダに含まれるPHPバージョン情報を出力しないようにexpose_phpをoffにしています。

このようにPHP関連の設定はwww.confで記述することもできます。(もちろんphp.iniで設定しても良いです。)

php_admin_flag[expose_php] = off

3-6. /etc/nginx/nginx.conf

; Start a new pool named 'www'.
[www]

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9000

; Set listen(2) backlog. A value of '-1' means unlimited.
; Default Value: -1
;listen.backlog = -1

; List of ipv4 addresses of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
listen.allowed_clients = 127.0.0.1

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
;                 mode is set to 0666
;listen.owner = nobody
;listen.group = nobody
;listen.mode = 0666

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
; RPM: apache Choosed to be able to access some dir as httpd
user = nginx
; RPM: Keep a group allowed to write in log dir.
group = nginx

; Choose how the process manager will control the number of child processes.
; Possible Values:
;   static  - a fixed number (pm.max_children) of child processes;
;   dynamic - the number of child processes are set dynamically based on the
;             following directives:
;             pm.max_children      - the maximum number of children that can
;                                    be alive at the same time.
;             pm.start_servers     - the number of children created on startup.
;             pm.min_spare_servers - the minimum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is less than this
;                                    number then some children will be created.
;             pm.max_spare_servers - the maximum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is greater than this
;                                    number then some children will be killed.
; Note: This value is mandatory.
pm = static

; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes to be created when pm is set to 'dynamic'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI.
; Note: Used when pm is set to either 'static' or 'dynamic'
; Note: This value is mandatory.
pm.max_children = 10

; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 5

; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 5

; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 35

; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
;pm.max_requests = 500

; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. By default, the status page shows the following
; information:
;   accepted conn    - the number of request accepted by the pool;
;   pool             - the name of the pool;
;   process manager  - static or dynamic;
;   idle processes   - the number of idle processes;
;   active processes - the number of active processes;
;   total processes  - the number of idle + active processes.
; The values of 'idle processes', 'active processes' and 'total processes' are
; updated each second. The value of 'accepted conn' is updated in real time.
; Example output:
;   accepted conn:   12073
;   pool:             www
;   process manager:  static
;   idle processes:   35
;   active processes: 65
;   total processes:  100
; By default the status page output is formatted as text/plain. Passing either
; 'html' or 'json' as a query string will return the corresponding output
; syntax. Example:
;   http://www.foo.bar/status
;   http://www.foo.bar/status?json
;   http://www.foo.bar/status?html
; Note: The value must start with a leading slash (/). The value can be
;       anything, but it may not be a good idea to use the .php extension or it
;       may conflict with a real PHP file.
; Default Value: not set
;pm.status_path = /status

; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
;       anything, but it may not be a good idea to use the .php extension or it
;       may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping

; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong

; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_terminate_timeout = 0

; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_slowlog_timeout = 0

; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
slowlog = /var/log/php-fpm/www-slow.log

; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024

; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0

; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: chrooting is a great security feature and should be used whenever
;       possible. However, all PHP paths will be relative to the chroot
;       (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot =

; Chdir to this directory at the start. This value must be an absolute path.
; Default Value: current directory or / when chroot
;chdir = /var/www

; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Default Value: no
;catch_workers_output = yes

; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp

; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
;   php_value/php_flag             - you can set classic ini defines which can
;                                    be overwritten from PHP call 'ini_set'.
;   php_admin_value/php_admin_flag - these directives won't be overwritten by
;                                     PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.

; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.

; Default Value: nothing is defined by default except the values in php.ini and
;                specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_flag[expose_php] = off
;php_admin_value[memory_limit] = 32M

4. nginx / php-fpmの起動

nginxとphp-fpmを起動します。

$ sudo /etc/init.d/nginx start
$ sudo /etc/init.d/php-fpm start

起動に成功すれば、0.0.0.0:80と127.0.0.1:9000がLISTENになっているはずです。

$ netstat -ltn | grep -E '(80|9000)'
tcp        0      0 127.0.0.1:9000              0.0.0.0:*                   LISTEN      
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN   

サーバ起動時に自動実行されるようにchkconfigで登録しておきます。

$ sudo /sbin/chkconfig nginx on
$ sudo /sbin/chkconfig php-fpm on

あとはCakePHP / WordPressをインストールすればokです。

5. 実際に使ってみて

Apache+mod_phpからの移行ということで基本Apache感覚で触りだしたわけですが、つまづいたというか気になったことを。

5-1. Aliasはaliasで

ApacheのAliasのようなことをやりたい場合、locationディレクティブでURIを指定して、そのブロック内ではrootではなく、aliasにてディレクトリを指定します。

        location /blog {
            alias   /path/to/wordpress;
            index  index.php;
        }

5-2. .htaccessは使えない

CakePHPでもWordPressでも使われている.htaccessですが、nginxでは利用できません(反映されません)。

nginxには.htaccessのようにサーバが設定ファイルを再読込することなく設定を動的に変更する方法はありません。

.htaccessで指定するような、アクセス制御、mod_rewrite、Basic認証等はnginx.confなどの設定ファイルにて記述し、nginxを再起動(再読込)を実行して設定ファイルを反映します。

# nginx 再起動
$ sudo /etc/init.d/nginx restart

# nginx 設定ファイル再読込
$ sudo /etc/init.d/nginx reload

5-3. Digest認証がない

nginxにはDigest認証を実現するモジュールは無いようです。以前はDigest認証を利用している箇所があったのですが、Basic認証は利用できるのでHTTPS+Basic認証で代用しました。

5-4. PHP関連の設定を変更したら

php.iniやwww.confでPHP関連の設定を変更した場合、php-fpmを再起動(再読込)します。nginxを再起動したところでphp-fpmには反映されませんのでご注意を。

# php-fpm 再起動
$ sudo /etc/init.d/php-fpm restart

# php-fpm 設定ファイル再読込
$ sudo /etc/init.d/php-fpm reload

5-5. rewrite対象URLにアクセスして画面が真っ白に

ここで設定した内容では、http://example.com/foo/bar といったファイルが存在しないURLへアクセスすると /index.php?url=URLというURLへrewriteして処理を行います。

こういった状況で画面が真っ白になるという現象がありました。

結局、原因はnginx.confの設定で、rewriteしたPHPスクリプトがSCRIPT_FILENAME で指定したパスに存在しない(アクセスできない)とこういった現象になるようです。

nginx、php-fpm どちらのログにも何も出力されなかったのではじめは嵌りました。もし同じ現象になった際はnginx.confのSCRIPT_FILENAMEの設定を確認してみて下さい。

nginxをはじめるのにオススメの本

これからnginxをはじめるなら「ハイパフォーマンスHTTPサーバ Nginx入門」がおすすめです。

現時点(2011/05/19)で日本語で書かれたnginx解説本がこれしか無いということもありますが、nginxをこれから利用するにとっては分かりやすく解説されています。

ただ、いきなりシェルコマンドやLinuxファイルシステム、システム管理ツールの解説が60ページほど続くので、この部分だけを立ち読みして「こりゃダメだ」と判断しないように注意して下さい:D このあと30ページほどnginxをインストールする内容があって、ようやく本編がはじまります。その後はしっかりとnginxについて解説されていますのでご安心を。

各ディレクティブの設定については公式WikiをはじめWebに情報があるのですが、設定の基本的な思想や細かなTipsなどは書籍にまとまっていると理解しやすいですね。php-fpmとの連携やApacheとの連携(リバースプロキシ)、Apacheからの移行なども簡単に解説されているので、Apahce+mod_phpから移行しようかなという方は一度手にとってみてください。

Google Docs のスプレッドシートに表データをコピペする

この記事の所要時間: 211

Google Docs のスプレッドシートに表データをコピペする方法です。

Google Docs 便利ですね。Excel に比べると多少操作性や機能は劣りますが、どこからでも共有できるというのはそれを補って余りあります。

そんな便利なスプレッドシートなのですが、コピーしたCSVを表形式でスプレッドシートに貼る機能がありません。

もちろんCSVをファイルに書き出してインポートすれば良いのですが、今画面上に表示しているCSVをそのままコピペしたい時ってありませんか。

例えば、以下のようなCSVをコピペしてスプレッドシートに貼り付けると、1行が1つのセルに文字列として格納されてしまいます。

1,原田哲也,TZM250
2,加藤大治郎,NSR250
3,青山博一,RS250RW

タブ区切りならいける

何とかならないかなーと試してみると、なぜかタブ区切りだと各セルに値を入れることができました!

先のCSVをタブ区切り(TSV)に変更して、スプレッドシートに貼り付けると、

1    原田哲也    TZM250
2    加藤大治郎 NSR250
3    青山博一    RS250RW

ばっちりいけます。

ただし貼り付けはショートカットキーで

これ不思議なのですが、マウスの右クリックから「貼り付け」を選択するとセルに値が入りません。

ショートカットキーの command+v(Windowsなら、ctrl+v)でペーストすれば ok です。

psqlの検索結果をスプレッドシートに貼り付け

そもそもなんでこれをやりたかったかというと、psqlで検索した結果をスプレッドシートに貼り付けたかったから。

psqlで検索結果をTSVにするのは以下。

$ psql db_name
db_name=> \a   // クエリ結果の整形オフ
db_name=> \t    // 検索結果のみ出力
db_name=> \pset fieldsep '\t'  // 列の区切り文字をタブに(カンマにすれば CSV に)

db_name=> select id,name,code from users order by id; // 検索クエリ
1     原田哲也     TZM250
2     加藤大治郎     NSR250
3     青山博一     RS250RW

あとはこの結果をコピーして、スプレッドシートに貼り付けるだけ。あー便利。

iPhone/iPadのスクリーンショットを共有する「iScreenShot」をリリースしました

この記事の所要時間: 421

iPhone(iPod touch)、iPadのスクリーンショットを投稿して、みんなで見て楽しむサービス「iScreenShot」をリリースしました。

iScreenShot
=> iPhone/iPadのスクリーショットを共有 | iScreenShot

機能の詳細は、実際にサイトにアクセスして頂くのが分かりやすいので(ヘルプもありますし)、ここではサイトを作る経緯までや公開してからについて、ざくっと書いてみます。

構築した経緯

iPhone 3GSを昨年(2009年)の夏に購入したのですが、アプリを入れてカスタムし出した頃から、他人のホーム画面にとても興味が出てきました。どうにかして色々な人のホーム画面が見れないかな、ということでこのサービスを考えました。

ただ、アイデアとしてはあったのですが、なかなか手が付けられずに結局そのまま放置されることになりました。

そして今年の春、iPadが発売され、さらにiPhone4(iOS4)が発表されました。デバイスがというよりOSのバージョンアップにより、当時よりホーム画面のカスタマイズの自由度が広がりました。つまりこれまで以上に多様なホーム画面が見られるわけです。

これは作るしかないでしょ、ということで公開にするにいたりました。

公開して

まだβ版ということで、8/1夜と翌日昼にこっそりTwitterにだけお知らせをpostしました。

TwitterでのRTやblog等でのご紹介で、少しづつですがスクリーンショットが投稿されはじめました。実際に投稿されたスクリーンショットを見ると、それぞれに個性があって面白いですね。知人がiPhoneを持っていてもホーム画面をじっくり見る機会はあまり無いので、こうやって色々なホーム画面が見られるのは想定していたより楽しいですね:D

使った方、見た方のコメントでも「面白そう」「人のホーム見たいよね」という話があって、作って良かったなーと思っています。

早速、iPhone/iPadのblogでお馴染みのtobu iPhoneさんととはてブニュースさんにも取り上げて頂けました!はじめは知っている人でボチボチ投稿してもらえれば良いかなと思っていたのですが、一気に色々な方に投稿して頂けるようになりました。ありがとうございます。やっぱり、すごい。

お気に入りのホーム画面

どんなスクリーンショットがあるかは見るのが一番なので、投稿されたスクリーンショットの中で自分のお気に入りのものをご紹介します。

=> お気に入り | iScreenShot

まずは公開早々に投稿頂いたitayanさんのホーム画面。

青系の背景とアイコンできれいにまとまっていますね。色合いを合わせるように意識してアイコンを並べているとのこと。なるほど。

ご自慢のホーム画面をキャプチャーしてみました。 | iScreenShot

次にユニークなホーム画面を投稿されているfool4rootさんのホーム画面。

アイコンを全面に置かずにあえて背景を見せるのがポイントですね。しかしFlashって:D

Flash 対応しました :-P | iScreenShot

そして今のいちおしがこれ!

Kzh_09Jpさんのホーム画面です。

はじめて見た時に驚いて、さっそくコメント入れました:D iOS4のホームボタン2度押しによるアイコングレーアウトを上手く利用して背景を浮かせています。これはすごい。ぜひ実機で次元が現れるところを見てみたいです。

ホームボタンを連続二度押したときに現れるこのショットが何気に気に入っ... | iScreenShot

あとおまけで、自分が投稿したものを。

文字アイコンを並べて作ってみました。この画面を2ページ以降に用意しておいて、相手に「ほら、スライドさせて」といって、見せると効果ありそうです:D

@crema おたんじょうび、おめでとうございます! | iScreenShot

ホーム画面ではなく、ロック中の画面です。iPhone使っている人ならいつも見るバッテリ表示の電池から液漏れです:D これは実際に人に見せると反応が良いです。

バッテリーから液漏れ | iScreenShot

ほかにもいろいろなスクリーンショットがあるのでサイトで見てみて下さい。

ホーム画面は楽しい!

投稿されたスクリーンショットを見て、あらためて思うのはやっぱりiPhone/iPadは画面が美しいですね。並べてみると惚れ惚れします。また、その美しい画面をそのままスクリーンショットで保存できる機能を標準で用意しているのは、さすがAppleだなーと思います。

これまで、それほどホーム画面の体裁を意識していなかったのですが、色々な人のホーム画面を見ると俄然やる気がでて来ますね。

見て楽しい、投稿して楽しい、というようにiPhone/iPadユーザ(+気になる人)が楽しめるサービスになればなあ、と思っています。ホーム画面コンテストとかやると楽しそうなので、企画も色々とやっていきますね。

どうですか?あなたのご自慢のスクリーンショットも投稿したくなってきましたか?

=> iPhone/iPadのスクリーショットを共有 | iScreenShot

ホーム > Web+DB

検索
フィード
メタ情報

Return to page top