2009年12月25日金曜日

postfixでVirtualアカウントの管理をMySQLで

どうも、俺@仕事中です。
今日はPostfix(2.5.5)のvirtual_mailbox_mapsについてです。
しかもそれをMySQLで管理しちゃおう!という話です。

そもそもvirtual_mailbox_mapsというのは、簡単に説明するとその名の通り
そのサーバのアカウントにないアカウント(仮想アカウント)のメールをメールボックスに保存するというものです。
今回はMySQLを利用しますので、postfix-2.5.5ソースをDLして、まず
# tar zxvf postfix-2.5.5.tar.gz
# cd postfix-2.5.5
# make makefiles AUXLIBS='-L/usr/local/lib -L/usr/local/mysql/lib/mysql -lssl -lcrypto -lmysqlclient -lz -lm' CCARGS='-DHAS_MYSQL -I/usr/local/mysql/include/mysql -DUSE_TLS -I/usr/local/include -DUSE_SASL_AUTH -DDEF_SERVER_SASL_TYPE=\"dovecot\"'
(パスは環境に合わせてね)のコマンドを打ちます。
ここでlibmysqlclient.soが見つからないぜ!的なエラーが出る場合は、
-L/usr/local/mysql/lib/mysqlのパスが正しいかどうか確認し、それも合っている場合は、
# vim /etc/ld.so.conf.d/mysql.conf
/usr/local/mysql/lib/mysql
を追加(パスは適宜合わせてね)。で、
# ldconfig -v
# make upgrade
でもう一度 make makefiles を打ちます。次に、
# make && make install(全てEnter)
でインストール完了です。
次に、main.cfの編集です。
# vim /etc/postfix/main.cf
fallback_transport = virtual # ローカルに存在しないアカウント宛の処理
virtual_mailbox_base = /usr/local/virtual # 適宜あわせてね
virtual_mailbox_maps = /etc/postfix/virtual_mailbox_mysql
virtual_uid_maps = static:10000
virtual_gid_maps = static:1000
を追加します。

続いてグループ、ユーザの作成。
# groupadd -g 10000 vgroup
# useradd -u 10000 -g vgroup -d /usr/local/virtual -s /sbin/nologin vuser


次は、virtual_mailbox_mysqlの設定。
# vim /etc/postfix/virtual_mailbox_mysql
hosts = localhost
dbname = mailsystem
user = mysql_user
query = SELECT concat(name,'/') FROM mail_account WHERE active=1 AND domain='%d' AND name='%u'

で、これをhash化します。
# postmap /etc/postfix/virtual_maibox_mysql


続いてMySQLの構築。今回はMySQLは自サーバで稼動しているものとします。
# mysql -h localhost
mysql> create database mailsystem;
mysql> create table mail_account (
id int not null auto_increment,
name varchar(128) not null default '', # virtual_accountの名前
password varchar(128) not null default '', # virtual_accountのPass
domain varchar(128) not null default '', # サーバドメイン
active tinyint(1) not null default 0, # 利用する/しないのフラグ
primary key (id)
);

INSERT INTO mail_account (name, password,domain,active) values ('username','password','mydomain.jp',1);
テーブル操作権限等は適宜指定してください。

では、postfix起動します。
# /usr/sbin/postfix start

これで、MySQLに入れたアカウント宛のメールであれば、/usr/local/virtual以下へ配送されます。

ちなみに、配送の確認をするには
# postmap -a "username@mydomain.jp" mysql:/etc/postfix/virtual_mailbox_mysql

とやって、対象の結果(今回で言うと username/)が返って来ればOKです。
何も表示されない場合は、何か間違えてます。

めでたし、めでたし!お(・∀・)め(・∀・)で(・∀・)と(・∀・)う!

2009年12月16日水曜日

PostgreSQL8.xで外部サーバから接続 めも

お久し、俺@仕事中です。

postgreSQL8.4.1を使ってて少しハマったのでメモφ(`д´)メモメモ...

タイトルの通り、外部サーバ<192.168.0.11>から自サーバ<192.168.0.10>(ローカル接続)のpostgreSQLへ接続しようとしてできませんでした。
 $ telnet 192.168.0.10 5432
Trying 192.168.0.10...
telnet: connect to address 192.168.0.10: Connection refused
これはpostgreSQLのバージョン8以降からpostgresql.confに設定漏れがあったことが原因でした。

# vim $PGDATA/postgresql.conf
------------------------------------------
#listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
これは、どのネットワークインターフェースアドレスで接続を許可するか、を設定する項目だそうです。
デフォルトでは「localhost」になっているので、自サーバからの接続のみ許可となっているので
listen_addresses = 'localhost,192.168.0.10'
とするとローカル接続の外部サーバから接続できます。
全て許可する場合は
listen_addresses = '*'
とします。

インターフェースのアドレスを確認するのは、おなじみの
# /sbin/ifconfig
でできますね。


以上でえええす。

2009年12月2日水曜日

iptablesの設定メモ

こんばんわ、俺@家で音楽鑑賞中です。

今年も残り1ヶ月切ってしまいましたね。
今年の元旦に神社で心に誓った目標の何割が達成できたでしょうか?
ちなみに僕は10の目標のうち4しか達成できませんでしたよ。・゚・(ノε`)・゚・。ウワーン


で、今日はiptablesの簡単な設定方法をメモします。
解説サイトは山のようにあるのですが、結構みなさん複雑な事書かれているので
チョチョイっと設定する人向けのメモです。

まずは設定の確認。
# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- localhost.localdomain anywhere
ACCEPT all -- 192.168.0.0/24 anywhere
ACCEPT tcp -- anywhere anywhere tcp dpt:http
ACCEPT tcp -- anywhere anywhere tcp dpt:omirr
ACCEPT tcp -- anywhere anywhere tcp dpt:smtp
ACCEPT tcp -- anywhere anywhere tcp dpt:ftp-data
ACCEPT tcp -- anywhere anywhere tcp dpt:ftp
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable

Chain FORWARD (policy ACCEPT)
target prot opt source destination
ACCEPT all -- localhost.localdomain anywhere
ACCEPT all -- 192.168.0.0/24 anywhere
ACCEPT all -- xxx.xxx.xxx.xx/28 anywhere
ACCEPT tcp -- anywhere anywhere tcp dpt:tproxy
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
一部のIPは伏せてあります。
僕のテストサーバ、思ったより色々設定されてありましたw誰だこんなんしたやつw

まぁ、大きく分けて3つあります。
「Chain INPUT」・・・入ってくるパケットに関するもの。
「Chain FORWARD」・・・転送されていくパケットに関するもの。
「Chain OUTPUT」・・・出力されるパケットに関するもの。

設定する順番は
1)ポリシーを設定
2)ルールを一旦削除(クリア)
3)ルールを設定
4)設定を保存
5)iptables再起動
です。

ではまず1)ポリシーを設定。
簡単なiptablesという事なのでINPUTは許可(ACCEPT)、FORWARDは破棄(DROP)、OUTPUTは許可(ACCEPT)と言う事にします。
FORWARDは滅多な事じゃないと使わない(ルータにするとか)ですし、OUTPUTはサーバからの出力なので上記の方法で良いと思います。
// 書式は iptables -P [チェイン] [ターゲット]
// チェインのポリシーを指定のターゲットに設定するという意。
# iptables -P INPUT ACCEPT
# iptables -P FORWARD DROP
# iptables -P OUTPUT ACCEPT
これでOK。

次に2)ルールを一旦削除します。現在の設定を変更せずに追加とかした人はココやらなくても良いです。
# iptables -F
OK。
これで一旦設定を見てみると、
# iptables -L
Chain INPUT (Policy ACCEPT)
target  prot opt source     destination

Chain FORWARD (Policy DROP)
target  prot opt source     destination

Chain OUTPUT (Policy ACCEPT)
target  prot opt source     destination
になるはずです。

次に3)ルールを設定。
// 書式は iptables -A [チェイン] -p [プロトコル] -s [送信元] -j [ターゲット] --dport [ポート範囲の指定]
// 他にオプションはたくさんあるけど、これくらい使えばとりあえずOK。
# iptables -A INPUT -p tcp --dport 80 -j ACCEPT
と書けば、80番ポートに対するtcpプロトコルは通すよ!という意味です。ちなみに、
# iptables -I INPUT 3 -p tcp --dport 80 -j ACCEPT
だと、INPUTチェインの上から3番目に、80番ポートに対するtcpプロトコルは通すルールを挿入!という意味です。
さらには、
# iptables -A INPUT -p tcp -s 192.168.0.0/28 --dport 80 -j ACCEPT
と書けば、INPUTチェインの80番ポートに対するtcpプロトコルの192.168.0.0/28からのパケットは通すぜ!という意味になります。
まぁ、こんな感じでどんどんと
# iptables -A INPUT -p tcp --dport 22 -j ACCEPT // ssh接続はACCEPT
# iptables -A INPUT -p tcp --dport 110 -j ACCEPT // POPはACCEPT
# iptables -A INPUT -p tcp --dport 25 -j ACCEPT // SMTPはACCEPT
# iptables -A INPUT -p icmp -j ACCEPT // PINGはACCEPT
# iptables -A INPUT -i lo -j ACCEPT // 自サーバからはACCEPT(-iはインターフェースの指定)
# iptables -P INPUT DROP // 他の接続は全てDROP
とこんな感じです。

次は4)設定を保存します。
# service iptables save
これはサーバ再起動されたら設定がリセットされてしまうので、保存しておくのです。

次に5)iptables再起動します。(念のために)
# service iptables restart
OK!こんなもんです(´゚艸゚)∴


ちなみにルールを設定する時に
# iptables -A 
と「-A」を多用しましたが、他にも
# iptables -I [チェイン] 番号
で挿入。
# iptables -R [チェイン] 番号
で書き換え(置換)
# iptables -D [チェイン] 番号
で削除ができますので、あしからず。

以上、おやすみー。

2009年11月27日金曜日

MySQLで最初に見るべきチューニング設定項目

どうも、俺@仕事中です。
わが社ではMySQL、PostgreSQLを良く使っている(というかほとんど)のですが、よくアクセス過多によるサーバチューニングをやっています。
今回はMySQLサーバで負荷対策を行う際の「まず最初にココの設定だけは要Check It Now」というのをメモします。

# vim /etc/my.cnf
または
# mysqladmin variables
または
mysql> show variables;
で、現在の設定の確認ができます。

-- key_buffer_size
mysql> show variables like '%key_buffer%';
MyISAMエンジンテーブルのインデックスを保存するメモリバッファのサイズ。
Key_read/Key_read_requestsでキーバッファミス率を算出し、0.01以下になるように。

-- max_allowed_packet
mysql> show variables like '%max_allowed%';
通信時の1パケット最大サイズ。画像データなど大容量のデータをMySQLへ保存する際には大きくする。

-- table_cache
mysql> show variables like '%table_cache%';
一度開いたMySQLのデータファイルポインタを保存しておくためのキャッシュ。二度目以降のテーブルへのアクセスを高速にする。つまりはサーバのディスクI/Oを減らせる。
mysql> show global status like '%Open_tables%'
の値が大きい場合はテーブルキャッシュの値を増やした方が良い場合がある。
※バージョン5.1.xからはtable_open_sizeに変わってるかも!?

-- sort_buffer_size
mysql> show variables like '%sort_buffer%';
ソートに利用されるメモリサイズ。Order ByとかGroup Byとかで使われる。もしsort_buffer_sizeが1Mでmax_connectionsが1000だった場合、メモリは最低でも1GB必要という事ですよ。

-- read_buffer_size
mysql> show variables like '%read_buffer%';
テーブル読み込み時に利用されるメモリサイズ。INDEXを使わずフルスキャンされる場合に利用。そもそもINDEXを見直すべきでは?!

-- read_rnd_buffer_size
mysql> show variables like '%read_rnd_buffer%';
テーブルをランダムに読み込む場合に使われるメモリサイズ。使用可能メモリより大きなテーブルでGroup By/Order Byをする際は大きめにしておく。ソート後のレコード読み取り速度を上げるかも。要はディスクI/Oの軽減。

-- myisam_sort_buffer_size
mysql> show variables like '%myisam_sort_buffer%';
REPAIR TABLE / CREATE INDEX / ALTER INDEX 時に利用されるメモリサイズ。かなり頻度は低い。

-- thread_cache_size
mysql> show variables like '%thread_cache%';
MySQLコネクションスレッドをプールする数。スレッドを破棄&生成するコストを下げる。

-- tmp_table_size
mysql> show variables like '%tmp_table_size%';
サブクエリなどで一時的に実行結果をテンポラリテーブルに保存されたときのメモリサイズ。このサイズを超えた場合、MySQLはディスクへ一時テーブルへ保存しディスクI/Oが発生してしまう。このサイズを指定した場合はmax_heap_table_sizeも同じ値に設定する。

-- query_cache_size
mysql> show variables like '%query_cache%';
一度発行したクエリ実行結果をキャッシュしておき、二回目以降はクエリを発行することなくキャッシュから結果を返す。もちろん更新クエリなどが走った後はキャッシュはクリアされる。ちなみに
mysql> FLUSH QUERY CACHE;
でキャッシュを強制的にクリアできる。

-- skip-name-resolve
これは有名すぎますがDNS逆引きをしない。この設定をした場合、GRANT構文などでユーザを登録する際にもIP指定にする必要がある。

my.cnfの設定は基本的に上記のものを見直します。
もちろん細かい設定をする場合は、もっと詳細な設定を見直す必要があるのであくまで参考程度に。

あとは、スローログを取得させる。
slow_query_log=mysql-slow // スローログを取得
logn_query_time=1 // 1秒以上かかったクエリを取得


OPTIMIZE TABLE [table_name]を定期的にかける。
mysql> OPTIMIZE TABLE table_name;
更新や削除などが頻繁に行われているテーブルに対してやると効果絶大。デフラグを解消させる。postgreSQLで言うところのvacuumみたいなもの。


あとは、サーバのメモリを増やすだとかサーバそのもののスペックを上げるだとか、アプリケーション側でのチューニングは必ず必要です。
特にスローログに出ているクエリは見直す余地がかなりあると思うので、そちらのチューニングも大切に!


以上でぇぇす。

2009年11月16日月曜日

linuxのssコマンドで利用中ポート一覧

こんちわ、俺@仕事中です。

ふと、ターミナル(zsh)で
[user]$ sl
と打ってしまって
correct:sl -> ss [n y a e]?
と表示されて知ったのですがLinuxには「ss」というコマンドがあるみたいです。
[user]$ whereis ss
ss: /usr/sbin/ss /usr/include/ss /usr/share/ss
見つからない人は、yumで一発インストールできます。

で、このコマンドなのですが、そのサーバが開いているポート一覧を表示してくれます。ちょうど
[user]$ netstat -tpln
したのと同じような感じです。

[user]$ ss -an
State    Recv-Q Send-Q    Local Address:Port    Peer Address:Port
LISTEN    0   0            *:3333           *:*
LISTEN    0   0            *:6666           *:*
LISTEN    0   0            *:21            *:*
LISTEN    0   0            *:22            *:*
LISTEN    0   0        127.0.0.1:5431           *:*
LISTEN    0   0            *:5432           *:*
ESTAB     0   0      192.168.60.215:22     192.168,.60.29:1515
ESTAB     0   0      192.168.60.215:22     192.168,.60.29:1764
と、こんな感じです。
オプションもnetstatと似てます。
[user]$ ss -h
Usage: ss [ OPTIONS ]
    ss [ OPTIONS] [ FILTER]
-n, --numeric don't resolve service names サービス名に変換せずにポート表示
-a, --all dispaly all sockets 全てのソケットを表示
-t, --tcp display only TCP sockets TCPプロトコルのソケットを表示
-u, --udp display only UDP sockets UDPプロトコルのソケットを表示
-4, --ipv4 display only IP version 4 sockets ipv4のIPでのみ表示
-6, --ipv6 display only IP version 6 sockets ipv6のIPでのみ表示
-r, --resolve resolve host names ホスト名を名前解決する
主なオプションはこんなもんでしょうか。

以上でーーす。

2009年11月11日水曜日

jqueryでポップアップウインドウ

どうも、俺@仕事中です。

mixiのPC版で絵文字入力するときに「絵文字」アイコンをクリックするとポップアップが出て絵文字入力ってありますよね。
今日はあんなポップアップの作り方の勉強です。

まず表示部分。
<html>
<title>
<script type="text/javascript" scr="js/jquery-1.3.2.js">
<!-- ドラッグできるように -->
<script type="text/javascript" scr="js/ui.core.js">
<!-- ドラッグできるように -->
<script type="text/javascript" scr="js/ui.draggable.js">
</title>
<body>
ポップアップ
<hr>

<!-- ポップアップ -->
<div id="popup">
ココにポップアップ表示
</div>
<!-- ポップアップ以外をクリックすれば、ポップアップが消えるように -->
<div id="delete"></div>

</body>
</html>
htmlはこんな感じにしておきます。
<div id="popup"></div>の中にポップアップさせたい内容を書きます。

次はCSS。
div#popup {
position: absolute; /* 移動できるように */
z-index: 99; /* z-indexは高く */
}
div#delete {
position: absolute; /* absoluteで! */
z-index: 0; /* div#popupより低く! */
display: none; /* 初期は非表示 */
}
CSSはこんな感じ。

ではjavascript部分(jQueryを使います)
$(function() {
// パレットをclickでbind()しておく
$("div#popup").bind("click", $(this), popup);

// パレット以外の部分をクリックすると消えるように
$("div#delete").bind("click", function() {
$("div#popup").hide(); // パレットを非表示
$(this).hide(); // 自身も非表示
});
});

function popup(e) {
// パレットを表示
$("div#popup").css({"top":e.pageY-100,"left":e.pageX+50}).show().draggable();

// パレット以外の部分をクリックすると消す部分(mask)の処理
var height = $(document).height();
var width = $(document).width();
$("div#delete").css({"width":width,"height":height,"top":0,"left":0});
$("div#delete").show();
}
こんな感じです。


簡単にサンプルを書きましたが、後はお好みで変えてやればOKです。
ちなみに↑のサンプルソースは動作確認していませんので、もし間違いがあれば連絡くだされ;;


以上でえぇぇぇす。

2009年11月10日火曜日

php5.3のクロージャー(無名関数)を使ってみる

どうも、俺@猛仕事中です。

新規案件にphp5.3.0を入れてみた。
せっかくなので5.3からの新機能「Clouser(クロージャー)」を使ってみる。

やりたい事は、絵文字データを内部コードに変換してDBへ保存し、それを表示する際に絵文字画像に変換して出力したいという事。
絵文字画像というのは、<img src="xxx">に変換するって事ね。
※絵文字内部コードは[[MG:001]]~[[MG:999]]のフォーマット
<?php
1 // プログラムは適当に書き換えています。
2 function convertToImg($text) {
3 // 変換テーブルを配列で取得
4 $emoji_map = file("emoji_map.txt");
5
6 $converted = preg_replace_callback("/\[\[MG:([0-9]{3})\]\]/",
7 function ($match) use ($emoji_map) {
8 if (isset($emoji_map[$match[1]])) {
9 return "<img src='./img/emoji/".$emoji_map[$match1]].".gif'>";
10 }
11 return $match[0];
12 },
13 $text
14 );
15
16 return $converted;
17 }
てな具合です。

preg_replace_callback()関数の7行目~10行目までで無名関数を使っています。
$text内で /\[\[MG:([0-9]{3})\]\]/ にマッチした最初の([0-9]{3})を、配列$emoji_mapのキーに指定しています。

使う事ないだろうと思っていた、preg_replace_callback()関数とクロージャーを使う事になるとは。。


以上、自分でいろいろやってみてねー。

2009年10月31日土曜日

Zend_Db_Adapter_Abstractを継承した拡張自作Adapter

どうも、俺@遊び帰宅です。
眠いっす。

Zend Frameworkを使ってWebアプリケーションを作ってて
「あー、DBへのクエリのログ取りたいなー」ってよくあると思います。
特にトランザクションの発生するクエリに関しては、Webプログラマとしては必需品だと思います。

方法はすべてのXXController内でクエリを発行する度に
<?php
// Zend_Db_Adapter_Abstractでクエリ発行
$adapter->query("INSERT INTO foo_table (column) VALUES ('value')");

// Zend_Db_Profiler
$profiler = $adapter->getProfiler();
$queries = $profiler->getQueryProfiles(Zend_Db_Profiler::INSERT);
if (is_array($queries)) {
foreach ($queries as $query) {
file_put_contents($filename, $query->getQuery());
}
}
と、簡単なサンプルを書くとこんな感じでしょうか。

ただ、これだとクエリを発行する度にZend_Db_Profilerを生成し、ログを取得しなければなりません。
面倒くさいです。

そんな面倒くさがりな貴方にはZend_Db_Adapter_Abstractを継承した自作クラスを作成することをオススメします。
例えばPostgreSQLを使っている場合は
<?php
/**
* Zend_Db_Adapter_Pdo_Pgsqlを継承した自作アダプター
*/
class MyPdoPgsql extends Zend_Db_Adapter_Pdo_Pgsql {
/**
* @Override
*/
public function insert($table, array $bind) {
// 略 Zend_Db_Adapter_Pdo_Pgsql::insert()と同じ処理

// Zend_Db_Profilerでログる
$profiler = $adapter->getProfiler();
$queries = $profiler->getQueryProfiles(Zend_Db_Profiler::INSERT);
if (is_array($queries)) {
foreach ($queries as $query) {
file_put_contents($filename, $query->getQuery());
}
}
}


/**
* @Override
*/
public function update($table, array $bind, $where) {
// 略 Zend_Db_Adapter_Pdo_Pgsql::update()と同じ処理

// Zend_Db_Profilerでログる
$profiler = $adapter->getProfiler();
$queries = $profiler->getQueryProfiles(Zend_Db_Profiler::UPDATE);
if (is_array($queries)) {
foreach ($queries as $query) {
file_put_contents($filename, $query->getQuery());
}
}
}

public function delete($table, $where = '') {
// 以下同じ
}
}
このようにinsert/update/deleteメソッドにログ処理を書いてZend_Db_Adapterの子クラスを作成します。

あとはindex.php内の処理でZend_Db_Adapter_Abstractを生成するときに
<?php
$db = new MyPdoPgsql($options);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
のようにデフォルトアダプターをセットしておけば、全てOK!!

基本的にZend FrameworkのクエリはZend_Db_Adapter_Abstractを経由して発行されるので、あとは自動でログを取ってくれます。
他にもトランザクションや例外処理も全てこの中に書けば、面倒なことを書かずに済みますね。


以上、面倒くさがり屋さんへ。

2009年10月22日木曜日

phpで三項演算子の注意点

どうも、俺@始業です。
別に暇なわけじゃないですよ。

今日はphpで三項演算子を利用した際の注意点をメモります。
三項演算子は例えば
<?php
$a = 1;
// 三項演算子
echo $a == 1 ? "$aは1です。" : "$aは1ではありません。";
と書くと、出力は
$aは1です。
となります。
書き方は
(式) ? 真の場合 : 偽の場合
です。
三項演算子は短く条件分岐を書くことができるので、phpでプログラムを書いた際のView側などで利用できます。

で、
三項演算子を入れ子(ネスト)にした場合、php三項演算子ならではの注意点があります。
<?php
$a = 1;
// 三項演算子
echo $a == 1 ? "$aは1です。" : $a == 2 ? "$aは2です。" : "$aは1でも2でもありません。";
とすると、プログラマーは
$aは1です。
と出力されると予想します。
ところがドッコイこの結果は
$aは2です。
と出力されてしまいます。危ないですねー。
理由はPHPマニュアル@三項演算子にあるように、phpの三項演算子は左から右へ評価の順が移っていくので、上の例でいくと
1)$a == 1 が評価されtrue
2)"$aは1です。"をechoしようとする
3)(1)で評価されたtrueが次の $a == 2で評価されtrue
4)"$aは2です。"をechoする。
という流れになるみたいです。たぶん、、。

要は(3)で$a == 2 が評価されるときに(1)で評価された結果(true)を持ち越してしまうみたいです。

というわけで、どうしても三項演算子を利用して入れ子をしたい場合は
<?php
echo $a == 1 ? "$aは1です。" : ($a == 2 ? "$aは2です。" : "$aは1でも2でもありません。");
と書きましょう。


以上ーー。

2009年10月7日水曜日

PostgreSQLでコマンドラインから直接クエリ実行

おはようございます、俺@朝礼後です。

今日はPostgreSQLでコマンドラインで直接クエリを実行する方法をメモります。
説明が難しいのですが、「コマンドラインから直接」というのは
$ psql foo_database
psql> SELECT * FROM bar;
というのではなく、
postgresqlサーバへログインせずにクエリ発行、結果取得という意味です。
(※内部的にはpostgresqlログインしてるのかもやけど、、、)


やり方は、
$ psql -c 'SELECT * FROM bar' foo_database
です。
ちなみに
$ psql -c 'SELECT * FROM bar' -o output_file foo_database
とやると、
output_fileへクエリ結果を出力します。

ついでにMySQLでは
$ mysql -e 'SELECT * FROM bar' foo_database
で直接クエリを実行できます。



以上どぇぇぇぇぇす。

2009年9月16日水曜日

MySQLのALTER TABLE構文のまとめ

こんばんわ、俺@家でまったり中です。
今日は、プログラマがよく使うであろうMySQL(ver5.x)のALTER TABLE構文についてメモします。
ALTER TABLE構文はMySQLのテーブルの構造を変更する時に使います。

1)カラムを追加
mysql> ALTER TABLE [テーブル名] ADD COLUMN [カラム名] [型] [制約] [FIRST | AFTER カラム名]
最後の[FIRST|AFTER カラム名]はなくてもOK。
例:
msql> ALTER TABLE table_name ADD COLUMN name VARCHAR(64) NOT NULL DEFAULT ''

2)カラムを削除
mysql> ALTER TABLE [テーブル名] DROP COLUMN [カラム名]

3)テーブル名変更
mysql> ALTER TABLE [テーブル名] RENAME TO [新テーブル名]

4)カラムの型を変更
mysql> ALTER TABLE [テーブル名] MODIFY [カラム名] [型] [制約]
または
mysql> ALTER TABLE [テーブル名] CHANGE COLUMN [カラム名] [新カラム名] [型] [制約]

5)カラム名を変更
mysql> ALTER TABLE [テーブル名] CHANGE COLUMN [カラム名] [新カラム名]

6)インデックスを追加
mysql> ALTER TABLE [テーブル名] ADD INDEX [インデックス名] ([カラム名,...])
または
mysql> CREATE INDEX [インデックス名] ON [テーブル名] ([カラム名,...])

7)インデックスを削除
mysql> ALTER TABLE [テーブル名] DROP INDEX [インデックス名]
または
mysql> DROP INDEX [インデックス名] ON [テーブル名]

8)AUTO_INCREMENTの値を変更
mysql> ALTER TABLE [テーブル名] AUTO_INCREMENT = [値]

9)テーブルの使用しているエンジン(ENGINE)を変更
mysql> ALTER TABLE [テーブル名] ENGINE = [ENGINE名]

10)テーブルのデフォルト文字セットを変更
mysql> ALTER TABLE [テーブル名] DEFAULT CHARSET = [文字コード名]

11)デフォルト値を変更
mysql> ALTER TABLE [テーブル名] ALTER COLUMN [カラム名] SET DEFAULT [デフォルト値]

12)デフォルト値を削除
mysql> ALTER TABLE [テーブル名] ALTER COLUMN [カラム名] DROP DEFAULT

詳しくはMySQL::5.1マニュアル ALTER TABLE構文をば。

以上どぇぇぇぇーす。

2009年9月9日水曜日

mmeasureのインストール

どうも、俺@仕事中、2度目の投稿です。
決して暇な訳ではありませんよ。

mmeasureのインストールについて覚書き。
インストールは至って簡単シンプルです。
まずはmmeasure projectからソースをダウンロード。2009年9月9日時点でのバージョンは1.0.7でした。

次に解凍
# tar zxvf mmeasure-1.0.7.tar.gz
出てきたディレクトリを/usr/local下(デフォルト)へ移動
# mv mmeasure/ /usr/local
# cd /usr/local/mmeasure


perlのモジュールをインストールします。
cpanについてはオデの日記@cpanモジュールを見てね。
# cpan install Socket
# cpan install DBI // ←MySQLを起動させてないとダメっぽい
# cpan install Jcode
# cpan install DBD::mysql // ←ココでずっこける
RRDsモジュールはrrdtoolをインストールすればOKの模様。
# wget 'http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.0.x/rrdtool-1.0.49.tar.gz'
# tar zxvf rrdtool-1.0.49.tar.gz
# rrdtool-1.0.49
# ./configure
# make
# make install // ←ついでにrrdtoolインスト
# cd /usr/local/src/rrdtool-1.0.x/perl-shared
# perl Makefile.PL
# make && make install
で、DBD::mysqlですが、何をどう頑張ってもインストールできませんでした。
同じくインストールできなかったけど、頑張ればインストできた人がいれば解決方法を教えて下さい。
とりえずyumで
# yum install perl-DBD-MySQL
で何とかインスト。

設定ファイルを修正
# cp mmeasure_template.conf mmeasure.conf
# vim mmeasure.conf
で、この設定ファイルを色々変更します。デフォルトの設定ファイルには間違いもあるので必ず修正します。
#------------------
# MySQL
#------------------

MYSQL_HOST=[MySQLのIP]
MYSQL_USER=mmeasure # あとでmmeasureユーザを追加します。
MYSQL_PASSWORD=password # 接続パスワード
MYSQL_PORT=3306
MYSQL_SOCKET=/tmp/mysql.sock
MYSQL_SLOW_QUERY_LOG="/usr/local/mysql/var/mysql-slow" # スローログのPATH。ない場合はコメントアウトで
#------------------
# Mail
#------------------

MAIL_HOST=foo.com # 自サーバホスト名
MAIL_FROM=info@foo.com # 実在するアカウントで!
MAIL_TO=bar@tohost.com
#MAIL_CC= # いらん!
#MAIL_BCC= # いらん!
MAIL_SUBJECT="alert" # メールの件名を適当に。
#------------------
# Pathes
#------------------

PATH_RRDTOOL="/usr/local/rrdtool-1.0.49/bin/rrdtool" # パスが違う場合は修正
#------------------
# Web setting
#------------------

WEBDIR="$PATH_MEASURE/web/images/graphs" # /imagesを追加!
WIDTH_SMALL="200" # 追加!
HEIGHT_SMALL="100" # 追加!
WIDHT_LARGE="400" # 追加!
HEIGHT_LARGE="100" # 追加!

次にMySQLサーバにmmeasureユーザの追加です。僕の環境ではローカル接続の別サーバにMySQLがあったので
# mysql -h 192.168.0.2 -u mysql
mysql> GRANT ALL ON *.* to mmeasure@192.168.0.1 indentified by 'password';
mysql> SET password FOR mmeasure@192.168.0.1 = old_password('password'); // ←MySQL4.1以前の互換
mysql> FLUSH PRIVILEGES;
でOK。一応接続テストもしておく。
# mysql -h 192.168.0.2 -u mmeasure -p
Enter password: パスワード入力


次に、MySQLのデータを取るために./mmeasure/daemon/data-mysql/read-data.plを修正します。ココはだいぶハマったのですが、クエリ回数、クエリ比率などを表示するために変更するべきです。
# vim ./mmeasure/daemon/data-mysql/read-data.pl
50 my %status = select_2column( $db, "show global status" ); # globalを追加

あと、僕の場合ですが、mmeasure.errでアラートメール送信時のエラーが出まくっていたのでメール送信処理部分をコメントアウトしました。アラートはNAGIOSとかで監視してるので、、、。
# vim ./mmeasure/daemon/daemon-mmeasure.pl
224 # alerter_sendmail( $conf{ 'MAIL_HOST' },
225 # $conf{ 'MAIL_TO' }, $conf{ 'MAIL_FROM' },
226 # $conf{ 'MAIL_SUBJECT' }, $alert_message,
227 # $conf{ 'MAIL_CC' }, $conf{ 'MAIL_BCC' } );
なんでエラーが出るのか謎です。時間があるときに調査します。分かる方いたら教えてください><
エラー内容
# vim ./mmeasure/daemon/log/mmeasure.err
52 RCPT command failure(Bad file descriptor)... at ../library/mailer.pl line 93.
53 print() on closed filehandle SH at ../library/mailer.pl line 127 (#1)
54 (W closed) The filehandle you're printing on got itself closed sometime
55 before now. Check your control flow.
56
57 readline() on closed filehandle SH at ../library/mailer.pl line 128 (#2)
58 (W closed) The filehandle you're reading from got itself closed sometime
59 before now. Check your control flow.
60
61 Use of uninitialized value in substitution (s///) at ../library/mailer.pl line
62 205 (#3)
63 (W uninitialized) An undefined value was used as if it were already
64 defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
65 To suppress this warning assign a defined value to your variables.
66
67 To help you figure out what was undefined, perl tells you what operation
68 you used the undefined value in. Note, however, that perl optimizes your
69 program and the operation displayed in the warning may not necessarily
70 appear literally in your program. For example, "that $foo" is
71 usually optimized into "that " . $foo, and the warning will refer to
72 the concatenation (.) operator, even though there is no . in your
73 program.
74
75 Use of uninitialized value in pattern match (m//) at ../library/mailer.pl line
76 130 (#3)
77 print() on closed filehandle SH at ../library/mailer.pl line 132 (#1)
78 DATA command failure(Bad file descriptor)... at ../library/mailer.pl line 134.
79 print() on closed filehandle SH at ../library/mailer.pl line 143 (#1)
80 readline() on closed filehandle SH at ../library/mailer.pl line 144 (#2)
81 Use of uninitialized value in pattern match (m//) at ../library/mailer.pl line
82 146 (#3)
83 print() on closed filehandle SH at ../library/mailer.pl line 148 (#1)
84 Body & header send failure(Bad file descriptor)... at ../library/mailer.pl line 150.
85 print() on closed filehandle SH at ../library/mailer.pl line 156 (#1)

次はApacheにAliasを作ります。
# vim /usr/local/apache2/conf/httpd.conf
Alias /mmeasure "/usr/local/mmeasure/web"

# /usr/local/apache2/bin/apachectl restart

ではデーモンの起動!
# ./mmeasure/daemon/mmeasure.sh start


以上~!あとはチューニングアドバイスを信じるも信じないもアナタ次第!

MySQLで再起動せずに設定変更

こんちわ、俺@仕事中です。
周知の事実すぎるかもしれませんが、MySQLの設定(通常はmy.cnfに書かれてあるもの)を再起動せずに変更する方法について。

mysql> SET GLOBAL <変数名>=<値>
または
mysql> SET @@global.<変数名>=<値>
で設定を変更できます。
例えば、query_cache_sizeを1MBに変更する場合
mysql> SET GLOBAL query_cache_size=1048576;

で変更できます。

変更後は必ずmy.cnfの値を変更する事を忘れずに!じゃないと、MySQLがいつか再起動した後元に戻っちゃいますよー。



以上どぇ~す。

2009年9月8日火曜日

Linuxで"-"ハイフンから始まるファイルの削除

どうも、俺@仕事中です。
今日は滅多に起こりえない"-"ハイフンから始まるファイルの削除について。

そもそもハイフンから始まるファイルを作成する事はないと思います。
$ touch -a
touch: file arguments missing
Try `touch --help' for more information.

ただ、vimでファイルを修正中などに
:w -a
などとタイプミスすると、「-a」というファイルが出来上がってしまいます。
$ ls -l
total 64
-rw-r--r-- 1 user group 0 Sep 8 10:29 -a

これを頑張って削除しよとしても
$ rm -a
rm: invalid option -- a
Try `rm --help' for more information.
$ rm "-a"
rm: invalid option -- a
Try `rm --help' for more information.
$ rm \-a
rm: invalid option -- a
Try `rm --help' for more information.
削除できません。

そんな時は"--"ハイフン二つ付けて
$ rm -- -a
とすれば削除できちゃうよ!
というお話。

2009年9月1日火曜日

MySQLのCASE構文

どうも、俺@残業中です。
忘れないために備忘録。

MySQLはCASE構文が使えるようになっています。
mysql> SELECT CASE WHEN 1=1 THEN 'true' ELSE 'FALSE' end;
+--------------------------------------------+
| case when 1=1 then 'true' else 'false' end |
+--------------------------------------------+
| true |
+--------------------------------------------+
1 row in set (0.00 sec)
となります。

構文は
CASE WHEN <条件式> THEN <真の場合> ELSE <偽の場合> END;
です。

応用して、例えば別テーブル(B)に自テーブル(A)を参照しているカラム(a_id)があり、BテーブルがAテーブルを参照しているデータを`refered`として取得する場合は
mysql> SELECT
CASE WHEN a_id>0 THEN 'refered' ELSE 'no refered' END
FROM A INNER JOIN B ON A.id=B.a_id;
で取得できます。

さらに拡張して、AテーブルのデータがBテーブルに参照されていた場合に、Aテーブルの`refered`カラムを`refered`、そうでない場合を`no refered`と更新したい場合は
mysql> UPDATE A INNER JOIN B ON A.id=B.a_id
SET refered= CASE WHEN a_id > 0 THEN 'refered' ELSE 'no refered' END;
のように、MySQLのUPDATE文+JOIN句でできます。

以上どえす。

2009年8月20日木曜日

MySQL5.1でfederatedエンジンを有効にする

こんにちわ、俺@仕事中です。
MySQL5.1.3のFederatedエンジンでハマりました(汗

MySQL5.1でfederatedエンジンを有効にするには、configureオプションで
# ./configure --with-plugins=max
または
# ./configure --with-plugins=federated
または
# ./configure --with-plugin-federated
としなければなりません。
詳しくは
# ./configure --help
で。

上記でインストールしたのですが、
# mysql
mysql> show engines;
+------------+---------+--------------+------+------------+
| Engine | Support | Transactions | XA | Savepoints |
+------------+---------+--------------+------+------------+
| MRG_MYISAM | YES | NO | NO | NO |
| CSV | YES | NO | NO | NO |
| FEDERATED | NO | NULL | NULL | NULL |
| MyISAM | DEFAULT | NO | NO | NO |
| MEMORY | YES | NO | NO | NO |
+------------+---------+--------------+------+------------+
5 rows in set (0.00 sec)
あれ?
mysql> show plugins;
+------------+----------+----------------+---------+---------+
| Name | Status | Type | Library | License |
+------------+----------+----------------+---------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| FEDERATED | DISABLED | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
+------------+----------+----------------+---------+---------+
7 rows in set (0.00 sec)
と、うんこな結果に。

configureオプションを色々と変えて試してみてもダメ。さらにMySQLはmakeに結構な時間がかかるので、いちいち試してもいられない。

という事で「ウンコー!」と叫びながら、mysqldを起動する際に
# service mysqld start --federated
とやると、federatedエンジンが有効になりました。

configureオプションは
# ./configure --with-plugins=max
または
# ./configure --with-plugins=federated
または
# ./configure --with-plugin-federated
でOKでし。

以上でええす。

2009年8月19日水曜日

opensslを使って秘密鍵と証明書の作成

どうも、俺@仕事中。今日3度目の投稿です。

linuxのopensslを使って秘密鍵とサーバ証明書を作成する場面に出くわしたのでメモります。

まず適当なディレクトリ作成
# mkdir /etc/ssl
# cd /etc/ssl


サーバの秘密鍵を作成
# openssl genrsa -out server.key 1024

署名要求書の作成
# openssl req -new -key server.key -out server.csr
----------
Country Name (2 letter code) [GB]:JP  // 国名
State or Province Name (full name) [Berkshire]:Osaka  // 県名
Locality Name (eg, city) [Newbury]:Osaka  // 市
Organization Name (eg, company) [My Company Ltd]:XX.company  // 会社名、所属組織名
Organizational Unit Name (eg, section) []:develop  // 会社の所属先
Common Name (eg, your name or your server's hostname) []:koexuka.blogspot.com  // サーバのホスト名 この値は超重要
Email Address []:info@koexuka.blogspot.com  // メアド
A challenge password []:  // パスワードいらなければそのままEnter
An optional company name []:  // これもEnter

通常はココで生成されたserver.csrをベリサインとかに送ってサイト証明書を発行してもらうのですが、今回はその処理も自サーバで行います。

では、自サーバが認証局となったつもりで認証局の秘密鍵を作成します。
# openssl genrsa -out ca.key 1024

認証局の証明書を作成します。
# openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
----------
Country Name (2 letter code) [GB]:JP
State or Province Name (full name) [Berkshire]:Osaka
Locality Name (eg, city) [Newbury]:Osaka
Organization Name (eg, company) [My Company Ltd]:XX.company
Organizational Unit Name (eg, section) []:develop
Common Name (eg, your name or your server's hostname) []:www.koexuka.blogspot.com
  // ココが上で要求証明書を作成したときのホスト名と違う必要があります
Email Address []:info@koexuka.blogsport.com

これで認証局側の鍵(ca.key)と証明書(ca.crt)ができました。

認証局となったつもりは終わって、通常のサーバに戻ります。
シリアルを作成します。
# echo 01 > serial.txt

認証局証明書付き自サーバ証明書の作成
# openssl x509 -CA ca.crt -CAkey ca.key -CAserial serial.txt -req -days 3650 -in server.scr -out server.crt
これで自サーバ証明書(server.crt)ができました。


めでたし、めでたし!

dovecotで'Support not compiled in for passdb drive 'pam''が出る

こんちわ、俺@また仕事中です。
dovecotを入れて起動させたら、タイトルの通り
Aug 19 17:02:01 dovecot: Info: Dovecot v1.2.4 starting up (core dumps disabled)
Aug 19 17:02:01 auth(default): Fatal: Support not compiled in for passdb driver 'pam'
Aug 19 17:02:01 dovecot: Fatal: Auth process died too early - shutting down
というエラーが発生しました。

OSはCentOS5.3
dovecotはdovecot-1.2.4です。

エラー内容は、適当和訳すると「passdbドライバーの'pam'は、コンパイルをサポートしていません?」みたいな感じか。

$ /usr/local/sbin/dovecot --build-options
と打ってみると
Build options: ioloop=epoll notify=inotify ipv6 openssl
Mail storages: cydir dbox maildir mbox raw shared
SQL drivers:
Passdb: checkpassword passwd passwd-file shadow
Userdb: nss passwd passwd-file prefetch static
とあったので、
$ vim /usr/local/etc/dovecot.conf
でdovecot.confを開き
#passdb pam {  // passdb pamの行をコメントアウト
passdb shadow {  // passdb shadowに書き換え
でちゃんと起動しました。
↑はコメントアウトだけでも良いですね。

英語力のなさを感じました。

`ls`コマンドでの色についてメモ

こんにちわ、俺@仕事中です。
今日はLinuxサーバのターミナルで
$ ls --color
した時の色の設定についてメモします。

色表示はデフォルトでは「/etc/DIR_COLORS」に書かれているものを読み込んで決めている(?)そうなのですが、たまにサーバによって「いつもと色がちゃうやないか!」て事があります。

気にしない人はそれでも良いのですが、
気になる人はログインユーザの~/.bashrc(または~/.tcshrc)に
$ vim ~/.bashrc
LS_COLORS='色の設定いろいろ'
export LS_COLORS
を書けばよかです。

LS_COLORSの内容は
$ dircolos -b
したものをコピペでOKです。
※-bオプションはBシェル用。-cオプションはCシェル用。

拡張子毎の色設定や、ファイルタイプ毎の色設定が可能です。
例えば、通常ファイルの色を白にする場合は
fi=00:
です。
ディレクトリの色を文字色黒、背景色緑にする場合は
di=30;42:
です。
拡張子tarの色を太い赤文字にする場合は
*.tar=01;31:
です。



以上でぇぇす。

2009年8月5日水曜日

sshでログインできない

俺@仕事中です。
今日はインストール直後のサーバにSSHログイン出来なかった場合の起こりうるミスについてメモります。
※実際おこりました、、。

サーバ側の/var/log/messagesに
# less /var/log/messages
Aug 5 11:14:17 data1 sshd(pam_unix)[15652]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=xxx-xxx-xxx-x.yyy.yy-yyyyy.yy.jp user=foouser
Aug 5 11:14:30 data1 sshd(pam_unix)[15654]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=xxx-xxx-xxx-x.yyy.yy-yyyyy.yy.jp user=foouser
Aug 5 11:14:56 data1 sshd(pam_unix)[15657]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=xxx-xxx-xxx-x.yyy.yy-yyyyy.yy.jp user=foouser

/var/log/secureに
# less /var/log/secure
Aug 5 11:14:17 data1 sshd[15652]: User foouser not allowed because not listed in AllowUsers
が出てる時。

# vim /etc/ssh/sshd_config
AllowUsers allowedusername foouser # ←foouserを追加
して

# service sshd reload
したらログインできます。

以上どぇぇぇす。

2009年8月4日火曜日

rsyncの設定(rsyncd.confなど)

どうも、俺@仕事中です。
今日は超基本中の基本ですが、rsyncの設定・使い方についてメモります。

rsyncはほとんどのLinuxに標準でインストールされている、ファイル転送用?コマンドです。
オプションも色々用意されていて、バックアップやミラーリングなどに便利です。
http://www.infoscience.co.jp/technical/rsync/

僕の利用しているLinuxディストリはCentOs4.7です。
rsyncコマンドはインストールされているものとします。

rsyncデーモン側(rsyncd)で/etc/rsyncd.confという設定ファイルを作成します。
# vim /etc/rsyncd.conf
----------------
hosts allow = 127.0.0.1 192.168.1.0/24 // ←許可するrsyncクライアントIP
hosts deny = * // ←拒否するrsyncクライアントIP *は全て
use chroot = false // ←chrootするかどうか。trueの場合は `path = /PATH`で指定したパスでchrootする
read only = false // ←read-onlyにするかどうか。trueの場合はクライアント側からのアップロードができない
[my_module_name] // ←モジュール名 モジュール毎に細かい設定をする場合
uid = nobody // ←モジュールでのユーザ名
gid = nobody // ←モジュールでのグループ名
path = / // ←デフォルトのパス
comment = this is comment. // ←コメント
で、rsyncdを再起動します。
# service xinetd restart


では、実際にrsyncコマンドを使う場合は
<サーバA>クライアントIP(192.168.1.1)
<サーバB>デーモンIP(192.168.1.2)

サーバA
$ rsync -avz /home/from/data/ 192.168.1.2::my_module_name/home/to/data/

でサーバA(/home/from/data)からサーバB(/home/to/data)へファイルが転送されます。
書式は
$ rsync [OPTION] 転送元PATH 転送先IP:[:モジュール名]転送先PATH
です。
ちなみにサーバAで
/home/from/data/とするのと/home/from/dataとするので動作が違うので要注意です。

/home/from/data/(スラッシュあり)の場合は、/data内のファイルだけが
/home/from/data(スラッシュなし)の場合は、/data内のファイルと/dataディレクトリ自体が
転送の対象となります。

以上どぇぇ~す。

2009年7月31日金曜日

Zend_Db_Tableでリレーションの作成

どうも俺@仕事中です。
相変わらずZendFramework使っています。多分11月くらいまで。。。
今日はZend_Db_Tableでリレーション(テーブル間の関連)を表す方法をメモします。

サンプルDB(MySQL)として、スレッド型掲示板を作ります。
CREATE TABLE user (
 id INTEGER AUTO_INCREMENT PRIMARY KEY,
 name
);
CREATE TABLE thread (
 id INTEGER AUTO_INCREMENT PRIMARY KEY,
 user_id INTEGER,
body TEXT
);
CREATE TABLE reply (
 id INTEGER AUTO_INCREMENT PRIMARY KEY,
 user_id INTEGER,
 thread_id ITNEGER,
 body TEXT
);

リレーションは大まかに
user←thread←reply
という感じです。
replyテーブルはuser_idを持ちます。

この場合のZend_Db_Tableは
<?php
class User extends Zend_Db_Table {
 protected $_dependentTables = array(
    "Thread","Reply"); // ThreadクラスとReplyクラスに参照されている
}
class Thread extends Zend_Db_Table {
 protected $_dependentTables = array(
    "Reply"); // Replyクラスに参照されている
 protected $_referenceMap = array(
    "User" => array( // Userクラスを参照している(`Usre`は何でも可)
        "columns" => array("user_id"), // user_idが外部キー
        "refTableClass" => "User", // 参照先クラス名はUser
        "refColumns" => array("id"), // 参照先のキーはid
        "onDelete" => self::RESTRICT, // Userが削除されてもThreadは削除しない
        "onUpdate" => self::CASCADE // Userが更新されたらThreadmo更新する
        )
    );
}
class Reply extends Zend_Db_Table {
 protected $_referenceMap = array(
    "User" => array(
        "columns" => array("user_id"),
        "refTableClass" => "User",
        "refColumns" => array("id"),
        "onDelete" => self::RESTRICT,
        "onUpdate" => self::CASCADE
        ),
    "Thread" => array(
        "columns" => array("thread_id"),
        "refTableClass" => "Thread",
        "refColumns" => array("id"),
        "onDelete" => self::CASCADE,
        "onUpdate" => self::CASCADE
        )
    );
}

となります。
要は
protected $_dependentTables = array();
で参照してきているクラス名
protected $_referenceMap = array();
で参照しているクラス名
を記す事になります。

ではUserオブジェクトから参照されているThread(またはReply)を取得するには
<?php
$thread_rowset = $user->findDependentRowset("Thread");
または
$thread_rowset = $user->findThread();
となります。

Relpyオブジェクトから参照しているUser(またはThread)を取得するには
<?php
$user = $reply->findParentRow("User");
または
$user = $reply->findParentUser();
となります。

ん~、ZendFrameworkなかなかイイねー。

2009年7月17日金曜日

jQueryでクリックされたエレメントの属性を取得する

どうも、俺@仕事中です。

jQuery+Ajaxを使ってインタラクティブなデザインのシステムを構築中なのですが、ちょっとハマったのでめも。

画面にテーブルとか使って、何かデータを一覧表示した場合に、クリックされた要素のvalue値やname属性の値やidの値とか取りたくて、取りたくて、、、取れなくて1時間悩みました。。。

例えば
<td><a href="foo" id="foo_id" value="foo_value">FOO</a></td>
<td><a href="bar" id="bar_id" value="bar_value">BAR</a></td>
のようなテーブルがあるとします。

これでjQueryで'FOO'をクリックした時に、その<a>タグに指定してあるid値やvalue値を取得したい!場合は
$("a").click(function() {
 id = $(this).attr("id"); // idの取得
 val = $(this).attr("value"); // valueの取得
});
という調べたらすぐ分かるような方法で取得できます。

調べ方ってとっても重要ですね!!
以上~~~~。

2009年7月16日木曜日

Zend_Db_SelectでWhere句を取得

どうも俺@残業中です。
引き続きZendFrameworkでアプリ開発を行っております。

Zend_Db_SelectというSQL文を自動生成してくれるクラスが用意されているのですが、
まぁSQLくらい直接書けよ。というツッコミは置いておいて、どうせなら用意されているクラスを利用したい+オブジェクティブに作りたいという事で使ってみました。
<?php $select = $myTable->select();
という感じで、Zend_Db_Selectオブジェトの生成はとても楽です。
ちなみに$myTableというのはZend_Db_Tableクラスを継承したクラスです。

Where句を作成するには
<?php $select->where("id = ?", $id);
のようにすると
WHERE id = $id 
というWhere句が作られます。

OrderBy句は
<?php $select->order("id DESC");
ORDER BY id DESC 
ができます。
超便利ですね。

あとはもう
<?php $select->query();
でZend_Db_Statementオブジェクトを取得しても良し
<?php $myTable->fetchAll($select);
でZend_Db_Rowsetオブジェクトを取得しても良し
<?php $dbAdapter->query($select->__toString());
でZend_Db_Statementオブジェクトを取得しても良し
<?php $dbAdapter->fetchAll($select);
などなど
使い道は無数にありますので、お好きなようにどうぞ。


で、このZend_Db_Selectを使って
「Where句だけ取りたい」
「OrderBy句だけ取りたい」
などあると思います。

その時は
<?php $select->getPart(Zend_Db_Select::WHERE);
でWhere句取得。
<?php $select->getPart(Zend_Db_Select::ORDER);
でOrderBy句取得。
などできちゃいます。
詳しくは、ZendFramework::Select オブジェクトの一部の取得で。


以上どぇーす。

2009年7月9日木曜日

lessコマンドのオプション

どうも俺@仕事中です。
今日はunixコマンド`less`についてメモります。

$ less filename
または
$ cat filename | less
とやると、1画面に収まりきらないファイル内容をページングのように見る事ができる超基本的なコマンドです。

lessコマンドにはいろんなオプションがありますが、普段あまりお目にかからないオプションを書いておきます。
$ less -m filename
画面に常に現在行のパーセンテージを表示

$ less -M filename
画面に常に現在行のパーセンテージと行数を表示

$ less -n filename
行数の計算をしない。大きなサイズのファイルをlessするとき重宝

$ less -N filename
行頭に行数を表示

$ less -r filename
バイナリデータをそのまま表示。日本語ファイルをlessするとき重宝

$ less -R filename
バイナリファイルでも強制的に表示。"xxx" may be a binary file. See it anyway?という質問がされなくなる。

以上でーす。

2009年7月8日水曜日

Zend_Db_Table_AbstractでZend_Cacheを利用して負荷軽減

どうも俺@ZendFrameworkを利用してWEBアプリを開発中です。

Zend_Db_Tableを継承してクラスを作成すれば、すでに様々なメソッドを持ったクラス(Bean)を作成することができます。
<?php
class MyBean extends Zend_Db_Table {
}

で、このZend_Db_Tableの生成は
<?php
$bean = new MyBean();
です。

<?php
var_dump($bean->info());
とすると、そのテーブルに関するメタデータ情報が連想配列で取得できます。

・name => テーブル名
・cols => テーブルのカラム名の配列
・primary => 主キーのカラム名の配列
・metadeta => カラム名とカラムに関する情報を関連付けた連想配列。discribeTable()と同じ内容
・rowClass => 行オブジェクト(Zend_Db_Table_Row)で使用するクラス名
・rowsetClass => 行セットオブジェクト(Zend_Db_Table_Rowset)で使用するクラス名
・referenceMap => このテーブルが参照する親テーブルの情報を含む連想配列
・dependentTables => このテーブルを参照しているテーブルのクラス名配列

で、このZend_Db_Tableですが、生成する度にdiscribetable()というメソッドが呼ばれテーブル情報を取得します。とても便利なのですが、都度DB接続が行われ非効率すぎるのでZend_Cacheを利用してデータをキャッシュさせておきます。
discribeTable()が走るタイミングは
・insert()
・find()
・info()
を呼んだ時だそうです。
※参照 Zend_Db_Table::メタデータのキャッシュ

<?php
$frontendOpt = array("automatic_serialization"=>true);
$backendOpt = array("cache_dir" => "/path/to/cache_dir");
$cache = Zend_Cache::factory("Core", "File", $frontendOpt, $backendOpt);
Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);

これを、index.phpなどに設定しておけばメタデータキャッシュを利用してくれます。

Zend_Cacheについては公式(Zend_Cache)を参照してね。
以上です。

2009年7月4日土曜日

ZendFramework + PostgreSQL で文字化けエラー

どうも俺です。
ZendFramework1.8+PostgreSQL8.3.7でPHP(5.2.6)アプリケーションを作成していたのですが、ハマったのでメモしておきます。

PostgreSQLは、データベース文字コードをSJISで作成できません。
なので携帯サイトでPHP(web)をSJISで作りたい場合は、PHP側の文字コードとPostgreSQL側の文字コードを変換しなければなりません。
一般的にはPostgreSQLは文字コードをEUC-JPかUTF8で作成します。
 $ createdb -E EUC-JP dbname


PHPではクエリを投げる前に
<?php
pg_set_client_encoding($connection, "SJIS");
とかやればイイのですが。。

ZendFrameworkでDB接続を行う場合は一般的に
<?php
$db = Zend_Db::factory("Pdo_Pgsql", array(
                   "host" => "localhost",
                   "dbname" => "dbname"));
のように使うと思います。(俺だけか?w)

この場合、全てのactionメソッドでDB接続をする度に
<?php
$db->query("SET NAMES 'SJIS'");
って処理させるのが面倒くさい!
<?php
Zend_Db::factory();
した際に自動でクライアント文字コードをSJISにしたい!
という事で2時間悩みました。。。

こんな場合はZend_Db::factory()の第2引数のパラメータに
<?php
$db = Zend_Db::factory("Pdo_Pgsql", array(
                   "host" => "localhost",
                   "dbname" => "dbname",
                   "charset" => "SJIS"));
とcharsetパラメータを追加すれば良いみたいです。

以上。・゚・(ノε`)・゚・。

2009年7月1日水曜日

Linuxリダイレクト処理めも

どうも俺です。
今日はLinuxの出力(標準出力、標準エラー出力)についてまとめておこうと思います。

まず、パイプについてですが、とあるコマンドの結果を別のコマンドへ引き継ぐ場合は
$ ls | grep foo
などとします。
これはlsした一覧からfooにマッチするものだけを表示します。

基本的に標準出力は
$ echo 'foo' > foo.log
で、'foo'をfoo.logに書き込みます。
追記書き込みの場合は
$ echo 'foo' >> foo.log
です。

標準出力と標準エラー出力を同じファイルへ書き出すには
csh tcshの場合
$ command >& foo.log
bashの場合
$ command > foo.log 2>&1
です。

標準出力と標準エラー出力を別々のファイルへ書き出すには
csh tcshの場合
$ (command > stdout.log) >& stderr.log
bashの場合
$ command > stdout.log 2> stderr.log
です。

標準出力だけファイルへ書き出して、標準エラー出力は書き出さない場合は
csh tcshの場合
$ (command > foo.log) >& /dev/null
bashの場合
$ command > foo.log 2> /dev/null
です。

とりえあずこれだけあれば、何とかなるはずです。
あとはみなさま調べてみてください。

以上。・゚・(ノε`)・゚・。

2009年6月22日月曜日

SQLiteで"unable to open database file"エラー

SQLite+PHPで簡単なアプリ作っていたら
「unable to open database file」というエラーが出ました。。
和訳すると「データベースファイルを開く事ができない」です。
エラーメッセージ内にあるパスには確かにデータベースファイル「XX.db」はあるのに。。

ということで、ファイルのパーミッションを変えて見ましたが変わらず。

ということで、ファイルの置かれたディレクトリのパーミッションを変えたらいけた!

以上どぇす。

2009年6月10日水曜日

MySQLでレプリケーション

Webアプリケーションを作成していて、MySQLサーバの負荷が高くなってきた場合、
・サーバスペックUP
・アプリケーションプログラム修正
・クエリ見直し
など色々対処方法はあるかと思います。
ま、一番言いのはサーバスペックUPだと思うのですが、どうしようもないくらい負荷が高くなった場合はMySQLサーバを複数台用意して、負荷を分散させましょう!
という事で、今日はMySQLの機能としてあるレプリケーション(Master-Slave)のやり方を簡単にメモ。

MySQLのレプリケーションはMaster側でバイナリログを取得していないといけないので
[Master]
# vim /etc/my.cnf
[mysqld]
server-id = 1 ←サーバIDはSlave側と重複しないように!
log-bin=mysql-bin ←バイナリログ取得
expire_logs_days=15 ←バイナリログ保存期間(任意)
binlog-do-db=[DB名] ←特定のデータベースのみバイナリログ取得(任意)
とします。
次にMasterに対しSlave側からアクセスできる新規ユーザを作成します。
[Master]
mysql> GRANT REPLICATION SLAVE ON *.* TO slave_user@192.168.0.2(Slave_IP);

では、Master側のデータをSlaveへ移動させます。
[Master]
mysql> FLUSH TABLES WITH READ LOCK;
でDBが更新されないようロックします。
Master側のバイナリログ状態を確認
[Master]
mysql> SHOW MASTER STATUS;
+-----------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-----------------+----------+--------------+------------------+
| mysql-bin.000001| 112 | | |
+-----------------+----------+--------------+------------------+
1 row in set (0.00 sec)
この「File」と「Position」の値はメモっておきます。コピー中にデータの更新があった場合にこのPositionからの同期をさせるためです。
MySQLのデータコピーをします。
[Master]
# tar cvf master.tar /usr/local/mysql/data/[DB名dir]

MySQLのロックを解除します。
[Master]
mysql> UNLOCK TABLES;


次にSlave側の設定です。
まず先ほど圧縮したMySQLのデータを展開します。
[Slave]
# cd /usr/local/mysql/data
# tar xvf master.tar
次にSlaveのmy.cnfの設定です。
[Slave]
# vim /etc/my.cnf
[mysqld]
server-id = 2 ←Master側と重複しないように!
read-only ←Slave側は更新しないように設定。SUPER権限を持つユーザのみが更新クエリを実行できる。
replicate-do-db = [DB名] ←指定したDBだけレプリケートする場合(任意)
Masterへ接続する設定を書いてもOK(?)です。
[Slave]
# vim /etc/my.cnf
master-host = 192.168.0.1 ←MasterIP
master-user = slave_user ←REPLICATION SLAVE権限を持つユーザ
と、my.cnfに書いても良いか、直接DBをイジった方が早い。
[Slave]
mysql> CHANGE MASTER TO
MASTER_HOST = '192.168.0.1',
MASTER_USER = 'slave_user',
MASTER_LOG_FILE = 'mysql-bin.000001', ←バイナリログ名
MASTER_LOG_POST = 112; ←先ほどメモったPositionの値
でOKです。
ではレプリケーション開始します。
[Slave]
mysql> SLAVE START;
ちなみに終了のときは
[Slave]
mysql> SLAVE STOP;
です。
めっちゃ簡単ですけど、もしSlaveが落ちて自動再起動とかなった場合にSLAVE STARTってっ誰がやるんだろ。。

2009年6月9日火曜日

MySQLを同一サーバ上に複数起動

どうも、俺です。
今日はMySQLを一つのサーバ内に複数起動する方法を超簡単にメモします。
OSはLinuxです。
そして、すでにデフォルトポート3306でのMySQLは起動しているものとします。
# netstat -tpln | grep mysql
tcp 0 0.0.0.0:3306 0.0.0.0:* LISTEN 30029/mysqld

まず、新しいmy.cnfを作成します。
# vim /etc/my.3307.cnf
[client]
port = 3307
socket = /tmp/mysql.3307.sock
[mysqld]
pid-file = /usr/local/mysql/var.3307/mysqld.3307.pid
datadir = /usr/local/mysql/var.3307
port = 3307
socket = /tmp/mysql.3307.sock
もう一つのMySQLはポート3307で起動するものとします。
次にmysql_install_dbを打ちます。
# mysql_install_db --defaults-file=/etc/my.3307.cnf --user=mysql
で/etc/my.3307.cnfを指定します。

では、MySQL起動します。
# /usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.3307.cnf & 
で起動できます。
このポート3307のMySQLへログインするには
# mysql --socket=/tmp/mysql.3307.sock
とソケットを指定すればOKです。

以上!!

2009年5月19日火曜日

vimインストールで'os_unix.c:45:30 error: selinux/selinux.h: No such file or directory'というエラーが出る

どうも俺です。
僕は新しいサーバが到着したらまず行うのがログインシェルの設定です。
次にvimのインストール(ソース)なのですが、make でこけまくったのでメモします。

OSはCentOS5.3。vimは7.2です。

まずソースをダウンロード
# wget 'ftp://ftp.vim.org/pub/vim/unix/vim-7.2.tar.bz2'
# wget 'ftp://ftp.vim.org/pub/vim/extra/vim-7.2-lang.tar.gz'
# wget 'ftp://ftp.vim.org/pub/vim/extra/vim-7.2-extra.tar.gz'
します。
次に解凍
# tar jxvf vim-7.2.tar.bz2
# tar zxvf vim-7.2-lang.tar.gz
# tar zxvf vim-7.2-extra.tar.gz
します。
次に僕はいつもパッチを当てます(※当てなくても良いと思う)
# cd vim72
# mkdir patches
# cd patches
# curl -O 'ftp://ftp.vim.org/pub/vim/patches/7.2/7.2.[001-182]'
... ここでパッチファイルをDLするのをしばし待つ ...
# cd ..
# cat patches/7.2.* | patch -p0
# ./configure --enable-multibyte --enable-xim --enable-fontset --with-features=big
# make && make install
なのですが、今回ここのmakeでずっこけました。
エラー内容は
os_unix.c:45:30: error: selinux/selinux.h: No such file or directory
os_unix.c: In function 'mch_copy_sec':
os_unix.c:2620: warning: implicit declaration of function 'is_selinux_enabled'
os_unix.c:2624: error: 'security_context_t' undeclared (first use in this function)
os_unix.c:2624: error: (Each undeclared identifier is reported only once
os_unix.c:2624: error: for each function it appears in.)....
というエラー文言がズラズラと、、。

「ん~面倒くさい、、、」となりました。
一応os_unix.cファイルなど眺めて見たものの長すぎるため5分で終了。
どうもselinux関連のエラーっぽいので、再度configureオプションを確かめる。
# ./configure --help | less
とすると
  --disable-selinux       Don't check for SELinux support.
「ん!」らしきオプションがあるじゃないか!
というわけでこのオプションを付けてmakeすればインストールできました。

深い原因調査はまた今度、または偉い人に聞いてみてください( ´艸`)

2009年5月12日火曜日

linuxで操作ログを残すscriptコマンド

あまり使わないかもしれませんが、ユーザの実行したコマンドの履歴は
$ history
で確認することが可能です。
これでも十分ですが、もう一つ
$ script log_filename.log
とすることで、log_filename.logにログを残すことが可能です。

ロギングを終了する場合は、
$ exit
または
$ Ctrl + D
で終了します。

2009年5月9日土曜日

svn(subversion)でtracが文字化け

subversionので利用されるtracはデフォルトでutf8で表示されます。
プログラムソースをsjisやeuc-jpで書いた場合、tracを表示すると文字化けが起こります。
これを解決するには
$ svn propset svn:mime 'text/plain; charset=euc-jp' *.php ←euc-jpの場合
$ svn commit -m "commit"
でOKです。

svnのコマンドの
$ svn proplist svn:mime xxx.php
とやればxxx.phpに設定されているプロパティを確認することができます。(-vオプションで詳細も表示)

2009年5月8日金曜日

iptablesで"ip_conntrack"エラーが出る場合

iptablesはパケットフィルタリングを行う便利な機能です。
設定したパケットを追跡するために
/proc/net/ip_conntrack
というファイルへパケット情報を記録、追跡します。

これがたまに大規模なシステムになると
# vim /var/log/messages
ip_conntrack: maximum limit of xxxx entries exceeded
というメッセージが表示される場合があります。

原因は、iptablesで追跡するパケットの上限数を超えてしまっているからです。
その上限数は
/proc/sys/net/ipv4/ip_conntrack_max
に書かれているのですが、システムで自動的に設定されています。

対処法は
# echo '100000' > /proc/sys/net/ipv4/ip_conntrack_max
と上限数を上げてあげるか、または
# vim /etc/sysctl.conf
net.ipv4.ip_conntrack_max = 100000
# sysctl -p
のようにしてあげるか、で大丈夫です。

/proc/sys/net/ipv4/ip_conntrack_maxの上限値は、搭載メモリーに依存するらしいので、増やしすぎには注意が必要です。

追跡中のip_conntrackの数を調べるには
# cat /proc/net/ip_conntrack | wc -l
で分かります。

大規模システムにもなると、外部システムとの連携などでptablesの監視パケットの数が膨大になるので注意が必要ですね。

2009年4月28日火曜日

MySQLで Host '...' is blocked エラー

どうも俺です。
先日とあるサーバのログにMySQLが吐き出したとおぼしき
Host '...' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
というエラーが出ていました。

これはMySQLが接続エラーを数回繰り返し出すと、そのホストからの接続を遮断するという機能によるものらしいです。
参照:http://dev.mysql.com/doc/refman/4.1/ja/blocked-host.html

この場合はログに書いてある通り
# mysqladmin flush-hosts
で解決できます。
デフォルトでは10回の接続エラーでなるようですが、変更する場合は
# mysqld_safe -O max_connect_errors=10000 &
を実行して変更できます。

パスワードなどの認証エラーも考えられますが、分かりにくいのがネットワーク関連のエラーで接続できない場合などです。
その場合は、値を増やしておく方が良いかもしれません。

2009年4月15日水曜日

mysqldumpで文字化けデータを頑張ってダンプ&リストア

どうも俺です。
昨日に引き続きMySQL関連のテーマをメモします。

mysqldumpでデータをダンプ&別サーバへリストアなんて事あると思います。
その時に文字化けで少しハマったのでメモしておきます。

色んなブログにも記されていますが、mysqldumpでデータを取得すると自動でutf8で取得されてしまいます。
利用しているデータベースがsjis(cp932)を利用しているとします。
mysql> show variables like '%char%';
+--------------------------+---------------------------------------+
| Variable_name | Value |
+--------------------------+---------------------------------------+
| character_set_client | cp932 |
| character_set_connection | cp932 |
| character_set_database | cp932 |
| character_set_filesystem | binary |
| character_set_results | cp932 |
| character_set_server | cp932 |
| character_set_system | utf8 |
| character_sets_dir | /usr/local/mysql/data/mysql/charsets/ |
+--------------------------+---------------------------------------+
この状態でダンプと取ります。
# mysqldump DATABASE_NAME > msyql.dump
これをこのままリストアすると
# mysql DATABASE_NAME < mysql.dmp
文字化けが原因でリストアできない場合があります。

こんな場合は使用している文字コードに関係なく
# mysqldump DATABASE_NAME --default-character-set=binary > mysql.dump
と --default-character-set=binary オプションを付けてダンプを取り
# mysql DATABASE_NAME --default-character-set=binary < mysql.dump
と --default-character-set=binary オプションを付けてリストアすれば大丈夫です。

2009年4月14日火曜日

mysqldump でスキーマ(テーブル構成)のみ取得

どうも僕です。
忘れやすいのでメモ。

mysqldumpで中のデータは要らないけどテーブル構成のみ取得したい場合。
# mysqldump DATABASE_NAME -d > schema.sql
または
# mysqldump DATABASE_NAME --no-data > schema.sql
です。

以上、終わり( ´艸`)

2009年4月3日金曜日

cpanモジュール

どうも。
muninの設定ついでにcpanについてもメモします。

cpanとはPerlモジュール群を置いているサイトのことで、cpanコマンドを使えば簡単にPerlモジュールをインストールできます。

# cpan install <モジュール名>
でもいいし、

# cpan
cpan> install <モジュール名>
でもOKです。
僕は仕事柄あまりPerlモジュールを利用することがないのですが、、。

Cpan::Miniというモジュールもあり、これはモジュール群を自分のローカルサーバへ落とす事ができます。

# cpan install Cpan::Mini
# minicpan -r http://ftp.cpan.jp/ -l ~/minicpan
これで1時間~半日かけてモジュール群を落としてきます。

ローカルへ落としたモジュールをインストールするには

# cpan
cpan> o conf urllist unshift ftp:///root/minicpan
cpan> o conf commit
でとりあえずローカルへ見に行くようになります。
あとは

# cpan
cpan> install <モジュール名>
でOK!

簡単ですね。
ちなみに、cpanで設定した項目をリセットするには

# cpan
cpan> o conf init
です。

muninインストールめも

どうも俺です。
今まで仕事でサーバ監視ツールはMRTGを使っていましたが、今回思いつきでmuninを使ってみようと思いインストールしたのでメモします。

既に数多くの解説サイトがあるのでそれらを参考にしていたのですが、ほとんどがyumでインストールだったのですが、muninの内容を理解するためにソースからインストールしました。

今回は監視対象サーバ(munin-node)と監視サーバ(munin)は同一サーバで行います。
監視対象サーバと監視サーバを分ける場合は、都度読み替えたり付け加えたりしてください。僕もいずれ監視対象サーバーを増やす予定です。

まずは、muninユーザとグループ作成
<監視サーバ/監視対象サーバ>
# groupadd -g 4949 munin
# useradd -u 4949 -g munin -s /sbin/nologin munin
なぜかグループIDとユーザIDは4949ですが、これはmuninが仕様するポートがデフォルトで4949だからだと思います。
気になる人はIDを指定しなくても問題ありません。

次にmuninはRRDToolを必要とするのでインストール。これもソースから。
<監視サーバ>
# wget 'http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.0.x/rrdtool-1.0.49.tar.gz'
# tar zxvf rrdtool-1.0.49.tar.gz
# rrdtool-1.0.49
# ./configure
# make
# make install
RRDToolは1.0.x系でないと正常に動作しません。(munin_1.2.6の場合)

次にCPANモジュールをインストール。
CPANとはPerlモジュールを入手できるサイトのこと。CPANサーバへつなぎモジュールインストールを自動化してくれます。
<監視サーバー>
# cpan
と打つと対話モードになり、初回は設定をあれこれ聞かれます。
基本全て「Enter」でOKですが、最後に接続サーバを選択するよう質問されるので、そこはきちんと選んでください。
僕は「Asia」→「Japan」→「ftp.ringo.gr.jp」というところを選択しました。

<監視サーバ>
# cpan install Time::HiRes
# cpan install HTML::Template
# cpan install Net::SNMP ←SNMP利用する場合
結構時間かかりますが気長に待ってください。

では、munin本体のインストールです。
<監視サーバ/監視対象サーバ>
# wget 'http://jaist.dl.sourceforge.net/sourceforge/munin/munin_1.2.6.tar.gz'
# tar zxvf munin_1.2.6.tar.gz
# cd munin-1.2.6
ここで初期設定を変更したい場合は、Makefile.configを適宜修正
とりあえずこのままデフォルト設定で進めます。
# make install-main ←監視サーバ
これで監視サーバ側のインストールは終わりです。後はMRTGのようにcronでデータを取得できればグラフができます。

まだ監視対象サーバの設定ができていないので、監視対象サーバの設定を行います。
まず、CPANモジュールのインストール。
<監視対象サーバ>
# cpan install Net::Server::Fork
※よくあると思うけど、もし監視対象サーバがローカル接続のみの場合、
cpanコマンドは使えないので、グローバルに出ているサーバへ
# cpan install Net::Server::Fork
してから、@NICにあるNew::Server::Forkモジュールをftpかなんかで落とします。
僕の環境の場合、
/usr/lib/perl5/site_perl/5.8.5/Net
以下のファイル・ディレクトリ全てを持っていきました。


次に、先ほどのmunin-1.2.6で
<監視対象サーバ>
# make install-node install-node-plugins
/etc/opt/munin/plugins内へプラグインへのシンボリックリンクを作成
# /opt/munin/sbin/munin-node-configure --shell | sh

munin-nodeの自動起動設定
<監視対象サーバ>
# cp ${MUNIN-SRC-DIR}/dists/redhat/munin-node.rc /etc/init.d/munin-node ←環境に合わせてファイルを変えてください。
# chkconfig --add munin-node
# chkconfig munin-node on
# service munin-node start
ここでエラーの出る場合は/etc/init.d/munin-nodeを修正
24行目:/usr/sbin/munin-node → /opt/munin/sbin/munin-node
ひとまずココで監視対象側の設定は終わりです。


では設定ファイルを書き換えます。
<監視サーバ>
# vim /etc/opt/munin/munin.conf ←監視サーバ用設定ファイル
特に問題なければそのままでOK。

<監視対象サーバ>
# vim /etc/opt/munin/munin-node.conf ←監視対象サーバ用設定ファイル
これも問題なければそのままでOK。

cron設定
<監視サーバ>
# crontab -e -u munin
*/5 * * * * /opt/munin/bin/munin-cron 2>&1
このcronの設定を行う前に一度手動でテストしたほうが良い。
# /opt/munin/bin/munin-cron
と打つと
Can't locate RRDs.pm in @INC (@INC contains: 
というエラーが出ました。
これはRPDs.pmが見つからない(${PATH}に見つからないと言う事だと思う)ので
# cd /usr/local/src/rrdtool-1.0.x/perl-shared
# perl Makefile.PL
# make && make install
でサクッと解決。

でまた
# /opt/munin/bin/munin-cron
と打つと、
「これはmuninユーザでやった方がエエで!rootでやるなら--force-rootオプション付けたほうがエエけど、あまりスマートちゃうやん」と怒られました。
とりあえずこれでOKです。
--force-rootオプションを付けてやると、コマンドは成功するのですが生成されるファイルの所有者がrootになってしまうので、munin-cronのcronをmuninユーザで設定していると問題です。ま、rootでcron動かせばいいんだろうけどね。。。

<監視対象サーバ>
# service munin-node restart


以上で、終わりです。
あとはApacheの設定を
<監視サーバ>
# vim /usr/local/apache2/conf/httpd.conf
Alias /munin /opt/munin/var/www ←追加

ブラウザで
http://localhost/munin
とすればグラフが表示されます。

グラフが生成されない場合は、munin.confとmunin-node.conf内に書いてある
-- munin.conf
dbdir データ保存先
htmldirhtml ファイル保存先
logdir ログ保存先
rundirlock ファイル保存先

-- munin-node.conf
log_file ログファイル名
pid_filepid ファイル名

で指定されたディレクトリの所有者をmunin:muninにしてください。
特にlog_file / pid_fileは見落としやすいです。

mysql系のnodeをmuninに追加する場合は、
# vim /etc/opt/munin/plugin-conf.d/plugins.conf
[mysql*]
env.mysqladmin /usr/local/mysql/bin/mysqladmin
と、mysqladminへのパスを書く。

apache系のnodeをmuninに追加する場合は、
# vim /usr/local/apache2/conf/httpd.conf
Include conf/extra/httpd-info.conf

# vim /usr/local/apache2/conf/extra/httpd-info.conf
<Location /server-status>
  SetHandler server-status
  Order deny,allow
  Deny from all
  Allow from 127.0.0.1 localhost
</Location>
ExtendedStatus On
を追加すればヨロシ。

postfix系の監視で
find: deferred: Permission denied
find: active: Permission denied
find: maildrop: Permission denied
find: incoming: Permission denied
find: corrupt: Permission denied
find: hold: Permission denied
のようなログが出る場合は、
# vim /etc/opt/munin/plugin-conf.d/plugins.conf
[postfix_mailqueue]
user root
を追加でOK。

2009年4月2日木曜日

linuxでのユーザ管理

俺です。

今日はLinuxでユーザーを追加、修正、削除する管理コマンドについてメモります。
OS:CentOS 4.3

1)ユーザーの追加

# useradd -d [ホームディレクトリ] -s [ログインシェル] -g [グループ] ユーザー名

2)ユーザー情報の確認

# finger ユーザー名

3)パスワード変更

# passwd ユーザー名

4)ユーザー情報まとめて変更

# usermod -d [ホームディレクトリ] -s [ログインシェル] -g [グループ] ユーザー名

5)ユーザー削除

# userdel -r ユーザー名
-rオプションはホームディレクトリごと削除


fingerコマンドとusermodコマンドはあまり知られてないかも(俺だけ?)なので要チェックです。

NAGIOS nrpeで「Could not complete SSL handshake」

仕事でNAGIOS+nrpeの設定をしていたとき少しハマりました。

NAGIOS監視サーバー側でnrpeを利用してエージェントサーバー側を監視しようとしたのですが、タイトルの通り
Could not complete SSL handshake
エラーが出て接続できない、、、。

nrpeのバージョンは2.0
SSLに原因があると思って、監視サーバー側の呼び出しコマンドに -n オプションを付けてSSL無効にしたが×。
エージェントサーバー側にも -n オプションを付けたが×。

エージェント側のログ(/var/log/messages)を見てみてると、
Host xxx.xxx.xxx.xxx is not allowed to talk to us!
というログが、、、。

原因は、、
nrpe.cfgファイルのallowed_hosts=の項目に、CIDR形式(xxx.xxx.xxx.0/28みたいな形式)で許可するIPアドレスを書いていました、、。
というか、それでOKだと思ってました、、、。

だって、そこに
I would highly recommend adding entries in your /etc/hosts.allow file to allow .....
て書いてあるから。。。

正解は、
allowd_hosts=127.0.0.1,xxx.xxx.xx.xxx,yyy.yyy.yyy.yyy(←許可するIPをカンマでつなぐ)
ちなみにエージェントサーバー側で
# /usr/local/nagios/bin/nrpe -c /usr/local/nagios/etc/nrpe.cfg -d
と打ってnrpeを起動する場合は、nrpe.cfgファイルのallowd_hosts=項目を見て許可IPを決めますが、デーモンに登録し
# /sbin/service nrpe start
みたいに起動する場合は、/etc/xinetd.d/nrpe(ファイル名は任意)内のonly_from=の項目が有効になります。

以上でーす。

再マウント

linuxにおいて、新たなデバイスをマウントする場合は

# mount [デバイス] [マウントポジション] -t [タイプ] -o [オプション,[オプション],,,]
でマウントします。

例えば

# mount tmpfs /tmpfs -t tmpfs -o rw,mode=777,size=512m
では、
/tmpfsへtmpfsファイルシステムを、ReadWrite、パーミッション0777、512MBでマウントします。


これを途中で1024MBへ変更したい場合は
一旦umountしたあとで、mountする方法もありますが

# mount tmpfs /tmpfs -t tmpfs -o rw,mode=777,size=1024m,remount
と、オプションにremountを付けてもOKです。

2009年2月28日土曜日

postgresqlのslow-log

基本的なことなのかも知れませんが、postgresql用スローログの取得方法についてメモ。
僕のテストサーバーでのPostgresqlバージョンは7.4.12です。

1)postgresql.confの設定(ソースからインストールした場合は/usr/local/pgsql/data下にあります)
syslog = 2 # range 0-2; 0=stdout; 1=both; 2=syslog
log_min_duration_statement = 100 # milliseconds
syslog_facility = 'LOCAL0' # この設定はなくてもOK?
を追加。(クエリ実行に100ミリ秒以上かかったクエリをsyslogへ出力。facilityはlocal0に設定)

2)/etc/syslog.confの設定
local0.* /var/log/postgres
を追加。(facility0を/var/log/postgresへログ出力)
*.info;mail.none;authpriv.none;cron.none /var/log/messages
をコメントアウトして
*.info;mail.none;authpriv.none;cron.none;local0.none /var/log/messages
を追加。(これがないと/var/log/messagesと/var/log/postgresの両方にログが出てしまう)

3)postgresqlとsyslogを再起動
# /sbin/service postgres reload
# /sbin/service syslog restart
以上で、実行に時間のかかるクエリのログが取得できます。
他にもpostgresql.confでいろいろ設定できるようなので、試してみる価値ありそうです。

これ試してみて、サーバーが重くなったら即時に設定を元に戻してpostgresqlとsyslogを再起動してください。

以上。・゚・(ノε`)・゚・。

fsockopen()でタイムアウト

よくWEBサイトで、広告バナーを広告サーバー(ADサーバー)から取得→表示という事が行われます。

その場合、phpなサイトではfsockopen()を使って、ソケット通信でバナーを取得する事が多いと思うのですがその場合のメモ。

※以下PHPマニュアルより
resource fsockopen ( string $hostname [, int $port [, int &$errno [, string &$errstr [, float $timeout ]]]] )
第5引数の float $timeout はソケット通信時のタイムアウト時間(秒)です。
で!僕はtimeout秒数は、設定した秒数ソケットの応答がなかった場合に接続を断念するものだと思っていました。

いや、そうなんですけど、言葉で説明むずかしいのですが、
1)ソケット接続完了
2)fputs()で情報を取得しに行く
3)応答がない、、、
4)timeout秒だけ頑張る
5)それでも応答がなければ諦める
↑のような動作だと思っていたのです。

調べてみると、実はそうではなくtimeout秒数は、ソケット接続を確立させるために待つ秒数であり、接続完了後に応答がなくて待つ秒数ではないみたいです。(実際そういう現象に遭遇しました、、><)

ソケットは接続確立されたが、ソケット接続先のサーバーから応答がない場合に切断したい場合の処理は

soket_set_timeout()と
stream_get_meta_data()を使え!
array stream_get_meta_data ( resource $stream )
既存の stream に関する情報を取得します。 ストリームは fopen() か、 fsockopen() か、pfsockopen() で 作成されたいずれのものも指定できます。 結果の配列は次のような項目を含みます。

timed_out (bool ) - 最後に fread() または fgets() でデータを待っている時にタイムアウトした場合 TRUE を返します。
この関数は、結果が配列で返ってくるのですがその中の'time_out'がtrueだった場合は応答時間切れのフラグです。

-- 以下サンプル
// ソケットでHOST:PORTに接続(TIMEOUT秒は頑張る)
if ($fp = @fsockopen(HOST, PORT, err_no, err_str, TIMEOUT)) {
  // 接続確立したら、タイムアウトは5秒に設定
  socket_set_timeout($fp, 5);
  // ソケットのメタデータを取得
  $socket_data_arr = stream_get_meta_data($fp);
  // ソケットでデータを送信
  fputs($fp, $hoge);
  // 応答があれば応答を取得
  $buf = "";
  // $socket_data_arr[time_out"]がtrueなら接続タイムアウト!
  while (!feof($fp) && !$socket_data_arr["time_out"]) {
    $buf .= fgets($fp, 1024);
  }
  fclose($fp);
}

以上。・゚・(ノε`)・゚・。

linuxのlocale設定メモ

イマイチLinuxについて分からないことが多いです。
例えばjavaで
public static void main(String[] args) {
System.out.println("ほげ");
}
と、EUC-JPで作成した場合に

1)まず、コンパイルで怒られた。
# javac Hoge.java
「コラー!!"??????"ってなんじゃー!?ASCIIコードマッピングにないでー!怒」
みたいな感じで。

2)だから、コンパイルにエンコードオプションをつける
# javac -encoding euc-jp Hoge.java
するとコンパイルできる。

3)じゃあ実行してみる。予想では標準出力(僕のテスト環境ではコンソール画面)に「ほげ」と出る予定。
しかし、出ない。というか文字化け(?)
# java Hoge
4)なんでだろぅ?と熟考したところ、あ!そういえば環境変数LANG=Cにしてたわ!ということで、LANG=ja_JP.euc_JPに!!

5)ところが僕のテストサーバー(ubuntu)は、ja_JP.euc_JPがないみたい、、、。UTF8しかない。
# locale -a | grep ja
ja_JP.utf8
6)ココらへんからだいぶ曖昧ですが、LANG=に存在しないロケール情報を登録してもLinux側で設定してくれないみたい?
# export LANG=foobartestest (←ありえないロケール)
# echo $LANG
foobartesttest
となるが、実際はデフォルトの(僕の場合はC)LANGで表示される。

7)という事で、ubuntuにEUC-JPとついでにSJISを入れてあげる。
# localedef -i ja_JP -f EUC-JP ja_JP.eucJP
# localedef -i ja_JP -f SJIS ja_JP.sjis
# locale -a | grep ja
ja_JP.eucjp
ja_JP.sjis
ja_JP.utf8
入った!

8)では、Linuxコンソールの文字コードをeucjpにして、もう一度
# java Hoge
ほげ
出た!!!!

とりあえず、そんな上記のような感じでjavaで日本語表示できました。

まー本来の目的はjavaで日本語表示することではなかったのですが、とりあえずLinuxの言語設定に関して少し知識が増えたかな?という感じでした。

apacheのカスタムエラーレスポンスについてメモ

Apacheの設定で、

ErrorDocument 404 http://外部URL/filename?param=value
のような事をしたい時ってあるよね。

いや、俺はありました。
ユーザーがリクエストしたファイル名とパラメータが何だったか?を取得する方法がないかな~?って調べてたらあった。

こんな時はApacheが自動的に

REDIRECT_HTTP_ACCEPT=*/*, image/gif, image/x-xbitmap, image/jpeg
REDIRECT_HTTP_USER_AGENT=Mozilla/1.1b2 (X11; I; HP-UX A.09.05 9000/712)
REDIRECT_PATH=.:/bin:/usr/local/bin:/etc
REDIRECT_QUERY_STRING=
REDIRECT_REMOTE_ADDR=121.345.78.123
REDIRECT_REMOTE_HOST=ooh.ahhh.com
REDIRECT_SERVER_NAME=crash.bang.edu
REDIRECT_SERVER_PORT=80
REDIRECT_SERVER_SOFTWARE=Apache/0.8.15
REDIRECT_URL=/cgi-bin/buggy.pl
という変数を作ってみれるみたいです。
だから、ErrorDocumentで指定したファイル内で
$_ENV["REDIRECT_XX"]ってやれば取得できる。
※外部URLへリダイレクトされた(ErrorDocumentが外部URL)場合は無理です。


「おおー!便利ー!」って思ったけど、よく考えたらそんな事しなくても簡単にphpで

$_SERVER["REQUEST_URI"]
で取れちゃうジャン。
ま、いつか使うかもね。

2009年2月4日水曜日

[Linux]openしているプロセス情報を表示する

どうも、俺です。

今扱っているWEBシステムで、セッションファイルとApacheのアクセスログを/tmpではなく/tmpfsとしてメモリ上に保存しています。

ある日/tmpfsが上限の900MBに達してしまい、「こりゃヤバイ!」ということで600MB程あるApacheのアクセスログをローテートさせたのですが、/tmpfsの使用容量はほとんど変わりません。
# df
Filesystem 1K-blocks Used   Available Use% Mounted on
/tmpfs    921600   873120 48480   95%  /tmpfs
実は、Apacheアクセスログをローテートさせた時に再起動も行っているのですが、
とある子プロセスがゾンビとなり、access_logへのポインタを保持し続けていたようです。
なので
# ls -l /tmpfs
としても出てこないのですが、古いaccess_logが/tmpfs上に残っていたようです。

# /usr/sbin/lsof | grep deleted
とすれば、本来であればあるはずのないaccess_logが出てきました。

あとはそのプロセスをkillして万事解決です。
しかし、なんでApacheの子プロセス残っちゃったのか調べないとな~。。。

amazon