前回、やっとこさPostgreSQLにテスト用のデータベースとテストデータを用意することができた。今回はお待ちかね、PHPからの接続である。これまで作り上げてきたテスト環境は、WebサーバーとDBサーバーが同一ホスト上に収まっている。その為、DBサーバーたるPostgreSQLへの接続は、localhost(127.0.0.1または ::1)への接続と、その他のNIC(ネットワークインターフェースカード)に割り当てられたIPアドレスへの接続の二通りの経路が考えられる。デフォルトではローカルからの接続しかできないので、いろいろと設定を変えてやる必要がありそうだ。両方の経路から接続OKな状態を考えてみる。
1.とりあえず、PHPから接続してみてどんなエラーが出るか見てみよう
なにせPostgreSQLを扱うのは初めてなので、PHPから接続してみて、一つ一つエラーを取り除いていく事にしてみようと思う。どんなときにどんなエラーを吐くのか見ておくことも今後の役に立つだろう。まずは、データベースtestdbに接続し、テーブルtesttbの中身を取ってくるという単純なPHPを書いてみた。
test.php
# vi /var/www/html/test.php
<?php
$connect = pg_connect("host=localhost dbname=testdb user=test01 password=パスワード");
$get_data = pg_query( $connect , "SELECT * FROM testtb );
$count = pg_num_rows($get_data);
echo $count;
for ( $i = 0; $i < $count; $i++ ) {
$rows = pg_fetch_row($get_data,$i);
echo "<pre>";
print_r($rows);
echo "</pre>";
}
pg_close($connect);
?>
このtest.phpに、ウェブブラウザのアドレスバーに http://テストサーバ/test.php と打ち込んでアクセスする。pg_connect()関数で必要なパラメータを渡してデータベースtestsc.testdbにアクセスしている。パラメータを”パラメータ名=値”という形で渡すのがナウい構文である。パラメータ名を明記しない古い構文は今後推奨されないので、周りの古い構文を使ってる人に教えてあげてほしい。そして、pg_query()関数でSQLを投げて結果を受け取る。pg_query()関数は、昔はpg_exec()関数と言われていたもので、pg_exec()関数は今後使えなくなるので早めにpg_query()関数に乗り換えるがよかろう。その後、pg_num_rows()関数で取得した行数を確認し、pg_fetch_row()関数でSQLの実行結果を配列に格納している。とりあえずのテスト用なのでエラーハンドリングなどは一切していない。差し当たっては動作確認が第一目的なのだ。テストPHPはできる限りシンプルな方が良い。
2.ローカルホストアドレスへの接続
PHPのエラーはnginxのerror.logに吐き出される。tailコマンドで表示させておくとリアルタイムに書き込まれた内容が見えるので便利。えいやっとtest.phpにアクセスしてみるとこんなエラーたでた。
# tail -f /var/log/nginx/error.log
~省略~
2013/03/28 23:53:11 [error] 1101#0: *22 FastCGI sent in stderr: "PHP message: PHP Warning: pg_connect(): Unable to connect to PostgreSQL server: FATAL: ユーザ"test01"のIdent認証に失敗しました in /var/www/html/test.php on line 3
~省略~
test,phpの3行目で早くもエラーが出たと怒られている。FATALのエラーだ。尋常ではない怒られ方だ。pg_connect()関数で接続できなかったようだ。予想通りの展開である。なぜか、test01ユーザーでIdent認証をしようとして失敗したらしい。test01は前回、パスワード認証するように設定したので、Ident認証しようとしてもダメだ。これは、pg_hba.confファイルで認証方法を正しく設定してやる必要がある。
# vi /var/lib/pgsql/9.2/data/pg_hba.conf
~省略~
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
#local all all peer
local all all password
# IPv4 local connections:
#host all all 127.0.0.1/32 ident
host all all 192.168.0.1/24 password
host all all 127.0.0.1/32 password
# IPv6 local connections:
#host all all ::1/128 ident
host all all ::1/128 password
~省略~
緑色の行が前回、ローカルからの接続をパスワード認証に変更した行である。黄色の行が今回、Ident認証の行をコメントアウトして、パスワード認証とする為に追加した行である。192.168.0.1/24のセグメントからのアクセスと、ローカルIP127.0.0.1/32からのアクセスをパスワード認証とした。ちなみに、今回IPv6は使用していないが、おいおい全環境をIPv6とする実験を行おうと思っているので、先駆けてIPv6のローカルアドレス::1/128もパスワード認証として置いた。これでPostgreSQLを再起動して再度、アクセスしてみる。
# tail -f /var/log/nginx/error.log
~省略~
2013/03/29 15:10:07 [error] 1093#0: *81 FastCGI sent in stderr: "PHP message: PHP Warning: pg_query(): Query failed: ERROR: リレーション"testtb"は存在しません
LINE 1: SELECT * FROM testtb
^ in /var/www/html/test.php on line 4
~省略~
今度は接続まではできたようだ。一行進んで4行目で怒られている。前回、SETコマンドでserch_pathにスキーマtestscを追加したが、SETコマンドはセッション限りの設定なので日を改めた今回は、その設定は消えてなくなっている。postgresql.confでserch_pathにスキーマtestscを追加してもよいが、今回はスキーマ修飾子をつけることで対応したい。test.phpを修正する。
# vi /var/www/html/test.php
~省略~
$get_data = pg_query( $connect , "SELECT * FROM testsc.testtb" );
~省略~
これで再度ウェブブラウザからtest.phpを叩いてみると、ようやくデータベースから情報と撮ってくることに成功した。以下のような出力結果を得ることができた。
10
Array
(
[0] => 1
[1] => 1行目だよ。一行目ですとも。
)
Array
(
[0] => 2
[1] => 2行目だよ。二行目ですとも。
)
Array
(
[0] => 3
[1] => 3行目だよ。三行目ですとも。
)
Array
(
[0] => 4
[1] => 4行目だよ。四行目ですとも。
)
Array
(
[0] => 5
[1] => 5行目だよ。五行目ですとも。
)
Array
(
[0] => 6
[1] => 6行目だよ。六行目ですとも。
)
Array
(
[0] => 7
[1] => 7行目だよ。七行目ですとも。
)
Array
(
[0] => 8
[1] => 8行目だよ。八行目ですとも。
)
Array
(
[0] => 9
[1] => 9行目だよ。九行目ですとも。
)
Array
(
[0] => 10
[1] => 10行目だよ。十行目ですとも。
)
ちゃんと10行取得し、それぞれを配列に格納している。やった。localhostへの接続はこれでOKだ。
3.ホスト名で解決されるIPアドレスへの接続
次は、ホスト名で解決されるIPアドレスに対しての接続をしてみよう。test.phpの接続先をlocalhostから、ホスト名に修正する。
# vi /var/www/html/test.php
~省略~
$connect = pg_connect("host=ホスト名 dbname=testdb user=test01 password=パスワード");
~省略~
この状態で再びブラウザからtest.phpへアクセスしてみる。と同時にログを確認。
# tail -f /var/log/nginx/error.log
~省略~
2013/03/29 00:51:10 [error] 4311#0: *11 FastCGI sent in stderr: "PHP message: PHP Warning: pg_connect(): Unable to connect to PostgreSQL server: could not connect to server: Connection refused
Is the server running on host "ホスト名" (192.168.0.3) and accepting
TCP/IP connections on port 5432? in /var/www/html/test.php on line 3
~省略~
わお、怒られた。ログの中に登場する192.168.0.3というのはテストサーバーのホスト名で解決されるIPアドレスである。Port5432に対するTCP/IPの接続に失敗したので、接続先のホスト上でPostgreSQLサーバーが稼働しているか確認しろ、というエラーメッセージである。PostgreSQLではデフォルトではPort5432を使う。なるほど、ポートを解放してあげなければいけなかった。CentOSではiptablesというファイアーウォールさんがポートの制限をしているので、Port5432への接続を許可してみる。
# vi /etc/sysconfig/iptables
~省略~
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 5432 -j ACCEPT
~省略~
#
# /etc/init.d/iptables restart
iptables: ファイアウォールルールを消去中: [ OK ]
iptables: チェインをポリシー ACCEPT へ設定中filter [ OK ]
iptables: モジュールを取り外し中: [ OK ]
iptables: ファイアウォールルールを適用中: [ OK ]
#
#
#
これで再度チャレンジ。ログを確認する。
# tail -f /var/log/nginx/error.log
~省略~
2013/03/29 00:57:56 [error] 4311#0: *21 FastCGI sent in stderr: "PHP message: PHP Warning: pg_connect(): Unable to connect to PostgreSQL server: could not connect to server: Connection refused
Is the server running on host "ホスト名" (192.168.0.3) and accepting
TCP/IP connections on port 5432? in /var/www/html/test.php on line 5
~省略~
状況が変わっていない。相変わらずTCP/IPの接続に失敗している。そもそも、Port5432でしているのだろうか。netstatコマンドをちょっと覗いてみよう。
# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 192.168.0.3:80 192.168.0.4:60808 ESTABLISHED
tcp 0 0 192.168.0.3:22 192.168.0.4:55214 ESTABLISHED
tcp 0 0 127.0.0.1:8080 127.0.0.1:50480 TIME_WAIT
tcp 0 0 127.0.0.1:9000 127.0.0.1:37695 TIME_WAIT
tcp 0 0 192.168.0.3:22 192.168.0.4:49796 ESTABLISHED
tcp 0 0 :::22 :::* LISTEN
tcp 0 0 ::1:5432 :::* LISTEN
tcp 0 0 ::1:25 :::* LISTEN
udp 0 0 ::1:59943 ::1:59943 ESTABLISHED
Active UNIX domain sockets (servers and established)
~省略~
衝撃の事実発覚である。IPv4のローカルホストアドレス127.0.0.1とIPv6のローカルホストアドレス::1でしかPort5432でリッスンしてないではないか!IPアドレス192.168.0.3ではPort22(ssh)と、Port80(webサーバ)を使用中であるが、Port5432はリッスンしてない。0.0.0.0(どこでもいいよというエントリ)でPort5432がリッスンしていると思っていたが、どうやらPostgreSQLでは接続を受け付けるネットワークインターフェースを制限できるらしい。どこでその制限を設定しているかというと、設定ファイルpostgresql.confのlisten_addressesに記述されている。
# vi /var/lib/pgsql/9.2/data/postgresql.conf
~省略~
#listen_addresses = 'localhost' # what IP address(es) to listen on;
listen_addresses = '*'
~省略~
#
# /etc/init.d/postgresql-9.2 restart
postgresql-9.2 サービスを停止中: [ OK ]
postgresql-9.2 サービスを開始中: [ OK ]
#
#
デフォルトでは、listen_addressesがlocalhostとなっているのでコメントアウトし、代わりに何でもありのワイルドカード”*”を入れてやる。これでどのネットワークインターフェースでも接続を受け入れるようになったはずだ。もう一度netstatコマンドをのぞいてみよう。
# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 192.168.0.3:22 192.168.0.4:61031 ESTABLISHED
tcp 0 0 :::22 :::* LISTEN
tcp 0 0 :::5432 :::* LISTEN
tcp 0 0 ::1:25 :::* LISTEN
udp 0 0 ::1:47725 ::1:47725 ESTABLISHED
Active UNIX domain sockets (servers and established)
~省略~
0.0.0.0(どこでもいいよの意味)でPort5432がリッスンし始めた。この状態でもう一度、test.phpへアクセスしてみると再びブラウザ上に次のように表示された。
10
Array
(
[0] => 1
[1] => 1行目だよ。一行目ですとも。
)
Array
(
[0] => 2
[1] => 2行目だよ。二行目ですとも。
)
Array
(
[0] => 3
[1] => 3行目だよ。三行目ですとも。
)
Array
(
[0] => 4
[1] => 4行目だよ。四行目ですとも。
)
Array
(
[0] => 5
[1] => 5行目だよ。五行目ですとも。
)
Array
(
[0] => 6
[1] => 6行目だよ。六行目ですとも。
)
Array
(
[0] => 7
[1] => 7行目だよ。七行目ですとも。
)
Array
(
[0] => 8
[1] => 8行目だよ。八行目ですとも。
)
Array
(
[0] => 9
[1] => 9行目だよ。九行目ですとも。
)
Array
(
[0] => 10
[1] => 10行目だよ。十行目ですとも。
)
やった!でけた。これで一応、PHPからアクセスできるDBサーバーをPostgreSQLで建てることができた。定期メンテナンスの仕組みや、パフォーマンスチューニングなどやることはいろいろありそうだが、それはまた、別のお話。たのしいWebサービスのアイデアが思いついたら、どんどん作っちゃおうという体制になった。しばらくはのんびりとアイデアを練ってみたい。