NTP サーバの同期設定方法

先日、保守しているサイトの本番サーバの時刻がずれていたので、ntp サーバの設定をしました。
yum list で確認すると、NTP サーバ自体は本番サーバ(以下、hoge サーバとします)にインストールされていたので、設定から。

/etc/ntp.conf を修正

  • NTP サーバの設定を追加します。
  • 日本標準時を提供している NTP サーバと時刻の同期をとるように設定します。
  • 外部の NTP サーバと、hoge サーバにある NTP サーバと同期をとる、という感じです。たぶん。
# 修正前
server 0.rhel.pool.ntp.org
server 1.rhel.pool.ntp.org
server 2.rhel.pool.ntp.org
# 修正後
server -4 ntp.nict.jp
server -4 ntp.jst.mfeed.ad.jp

hoge サーバ内の NTP サーバが起動しているか確認

  • 下記コマンドで確認できます。どっちをつかっても OK です。
    • /etc/init.d/ntpd status
    • ps aux | grep ntpd
ps aux | grep ntpd
# こんな感じででれば起動中
ntp       2675  0.0  0.8   4392  4392 ?        SLs   2010   0:07 ntpd -u ntp:ntp -p /var/run/ntpd.pid -g

NTP サーバが起動してたら一旦止める

  • 停止している状態ならこのコマンドは実行しなくて OK です。


/etc/init.d/ntpd stop

手動で時刻を合わせる

  • 30 分くらい時刻がずれていたので、手動で時刻を合わせてから、NTP サーバと同期をとるようにします。
    • NTP サーバ起動時に大幅に時刻がズレていると、NTP サーバが起動できないそうです。
    • ■NTPサーバーインストール に書いてありました。
ntpdate -b ntp.nict.jp
  • もし NTP サーバが起動中だった場合、下記のようなエラーになります。
21 Apr 13:01:44 ntpdate[9659]: the NTP socket is in use, exiting

NTP サーバを起動

  • 時刻を合わせたら、下記コマンドで NTP サーバを起動します。
/etc/init.d/ntpd start

少し待って、同期しているか確認

  • ntpq -p を実行して、参照同期中のサーバを表すアスタリスクが表示されていれば OK です。
>ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 *ntp-b2.nict.go. .NICT.           1 u    5   64  377    3.366   28.289   8.686
 +ntp2.jst.mfeed. 210.173.160.86   2 u    9   64  377    1.436   31.315   6.857
  LOCAL(0)        .LOCL.          10 l   12   64  377    0.000    0.000   0.001

【補足】ntpq -p の見方

  • * 参照同期中のサーバーを表している
  • + クロック誤り検査に合格したサーバー
  • " " 左に何も表示されない場合(空欄)、サーバーを参照していない

ntpd をサーバ起動時、自動起動するよう設定

  • 下記コマンドを実行すると、自動起動するようになります。
chkconfig ntpd on

自動起動設定になっているか確認

>chkconfig --list ntpd
ntpd            0:off   1:off   2:on    3:on    4:on    5:on    6:off

これで、本番サーバの時刻のズレは解消されました。
結構長い間放置してたので、終わって良かったです。

php-gd のインストールではまったこと

本番にはあるけどステージング環境に GD モジュールがない!という状況に陥ったので、インストールすることにしました。
(ステージング環境がちゃんと動き出したのが最近で、全部の機能が動くことを確認出来ていなくて、今日まで気付かなかった…。)

php-gd のインストール

yum を使う

色々回り道したけど、結局は yum を使って一発インストール。(迷走した記録は後述します。)
一応インストールする gd ライブラリを確認してから実行しました。

$ yum list | grep gd
$ yum install php-gd.i386
gd.ini の作成

php-gd を有効にするために、ini ファイルを作って設定します。
gd.ini は、共通にインクルードされる設定ファイルがある場所におきます。

$ cd /etc/php.d
$ vim gd.ini
## 下記を記載して保存 ###
extension=gd.so
apache をリロード

設定ファイルを追加したので、リロードしておきます。

$ apachectl graceful
設定できたか確認

gd がリストにあればインストールおよび設定完了です。

$ php -m
[PHP Modules]
gd
  :

迷走した記録(読まなくて OK)

結局作業自体は上記の手順で良かったのですが、それにいたるまでいろいろ迷走しました。
また同じようなことではまらないように、書いておきます。

imagecreatetruecolor() がない

最初は、下記のようなエラーログが出力されていました。調べてみると GD モジュールがない疑惑が生じました。

Fatal error: Call to undefined function imagecreatetruecolor() 
GD モジュールの確認

GD モジュールは、PHP 5.2.0 以上の場合標準でインストールされているとのことです。
じゃあ PHP のバージョンが低いの?と思って調べてみると、5.1.6 でした…。

$ php -v
 PHP 5.1.6 (cli) (built: Nov 29 2010 16:47:37)

念のため GD モジュールが存在するのか確認したけど、やっぱりない。
やっぱり別にインストールする必要がありました。
今まで確認する時って、画面に phpinfo(); とかで表示してたけど、コマンドで確認する方法を覚えました。

  • i オプション:phpinfo の実行結果をコマンドライン形式で表示する。
  • m オプション:読み込まれているモジュールの一覧を表示する。
# 実行はこんな感じで
$ php -i | grep gd
$ php -m | grep gd
GD モジュールのインストール

GDライブラリのインストール を参考にしてインストールすることにしました。
ファイルを wget で取得したところまでは良かったのですが、make 時にエラーが起こりました。

$ cd /usr/local/src
$ wget http://www.libgd.org/releases/gd-2.0.35.tar.gz
$ cd gd-2.0.35
$ ./configure
$ make
cd . && /bin/sh /usr/local/src/gd-2.0.35/config/missing --run aclocal-1.9 -I config
aclocal:configure.ac:64: warning: macro `AM_ICONV' not found in library
 cd . && /bin/sh /usr/local/src/gd-2.0.35/config/missing --run automake-1.9 --foreign
Makefile.am:18: Libtool library used but `LIBTOOL' is undefined
Makefile.am:18:
Makefile.am:18: The usual way to define `LIBTOOL' is to add `AC_PROG_LIBTOOL'
Makefile.am:18: to `configure.ac' and run `aclocal' and `autoconf' again.
make: *** [Makefile.in] Error 1
エラーの原因?

よくわからないんですが、libgdのmakeでエラー を見ると同じような状態になっている人がいました。
libiconv を入れれば動くのかな…?と思いつつ、もう嫌になってきました。
周りの人に助けを求めたら、「yum でインストールできるんじゃない?」といわれてショックをうけつつ、上記の方法でインストールしたのでした。

post-receive-email を使って、push 時にメールを飛ばす

今まで自作で作った微妙なメール送信スクリプトを使ってたんですが、post-receive-email というものがあると知って、hollyなblog:git post-receive-email を参考に、こっちに移行しました。
これだとソースの diff がメールで見れないけど、commit 時にもメールを飛ばすようにしてるし、そこでは diff 見れるから OK ということで。

環境は下記とします。

post-receive の準備

まず、コミット後に実行されるスクリプト(post-receive)を準備します。post-receive は hooks の下に post-receive.sample という形であるので、コピーして使います。

cd /var/git/sendmail.git/hooks
cp post-receive.sample post-receive

post-receive を編集

次にpost-receive の中身を編集します。post-receive-email のパスがコメントアウトしてあると思うので、コメントアウトを外して、パスを変更します。

### これを↓ ###
#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
### こうする↓ ###
. /usr/local/src/git-1.6.5.7/contrib/hooks/post-receive-email


メールが文字化けすることがあるので、文字コードの設定をしておきます。これは、post-receive-email を修正します。

### 197 行目あたりに追加
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"

### 684 行目あたりを編集
generate_email $oldrev $newrev $refname | nkf -w80 | send_mail

git config をいじる

このままだと、メールの送信先が設定されないので、git config で設定します。ついでに、prefix と差出人も設定しておきます。
複数共有リポジトリを作ったときに毎回設定するのが面倒なので、今回は global で設定しました。

git config --global hooks.mailinglist "XXXXXX@gmail.com"
git config --global hooks.emailprefix "[git commit]"
git config --global hooks.envelopesender "XXXXXX@gmail.com"

description を編集

もう一点。私は description の存在を知らなかったので、メールが飛ぶと「UNNAMED PROJECT」とかでちゃってました。
description は、/var/git/sendmail.git/description というファイルで、ここにプロジェクト名を設定できるみたいです。(post-receive-email を読んだら、description の設定がなかったら UNNAMED ... になるみたいでした。)
UNNAME は格好悪いので、description を編集。これでメールのタイトルや本文に UNNAMED ... が出なくなります。

#### 編集内容これだけ↓ ###
sendmail_repo

hooks 用の共有リポジトリを作成

複数共有リポジトリがあると、それぞれに hooks の設定をしなきゃいけないのがめんどくさかったので、hooks のリポジトリをつくってしまうことにしました。

共有リポジトリを作成してコピーします。
これで今後共有リポジトリを作成したら、hooks を hooks.git から clone して使うようにすればOK です。

cd /var/git
git init --bare hooks.git
### clone して sendmail.git の hooks たちをコピー ###
git clone hooks.git
cp -rp /var/git/sendmail.git/hooks/* /var/git/hooks
### git commit して git push する ###

Oracle メモ

最近 Oracle を使うようになりました。というか、ちゃんと SQL を書き始めたのが最近なので、忘れないようにいろいろメモ。

ラクルは空文字は null になる

ADD_MONTHS

  • ADD_MONTHS(<基準となる日付>, <加算する月数>)
    • 日付を加算する
    • 加算する月数をマイナスにすると、前の月が取得できる
    • ADD_YEARS みたいなのはないみたい
# 3ヵ月前と比較
AND TO_CHAR(UPD_DATE, 'YYYYMM') < TO_CHAR(ADD_MONTHS(SYSDATE, -3), 'YYYYMM')

CONCAT

  • CONCAT(文字列1, 文字列2)
  • セルのデータを結合できる
## TIME と TIME_CLASS がくっついて表示
SELECT CONCAT(TIME, TIME_CLASS) AS TIME FROM TIME_TABLE

START WITH

CASE

  • switch 文みたいなことができる。詳細はここ → CASE式のススメ
  • CASE 式の評価は、真になる WHEN 句が見つかった時点で打ち切られて、残りの WHEN 句は無視される

/etc/grub.conf の設定を削除して再起動したら起動できなかった話

普段、/etc の下のファイルなんてそうそういじったりするわけじゃないんですが。
ちなみに、起動できなくなってしまったのは GuestOS(CentOS 5.4)です。なので、HostOS から入ってなんとか起動できました。
発端は、以前 GuestOS の時計が狂いまくっていて、HostOS と時間の同期とるという設定をこのファイルに追加したためでした。
その設定をしたのが半年くらい前で、その時使用していた Redmine のチケットに「下記設定を追加」と書いていたので、すべて新規で追加したと思ってしまったんです。で、まるごと消しちゃったんです。(よく見れば分かりそうなのに…)
/etc の下を、etckeeper とかでちゃんとバージョン管理していなかったのも悪かったのです。反省。

# 設定を追加したとかかれていた部分↓
kernel /vmlinuz-2.6.18-164.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet divider=10 clocksource=acpi_pm


それで reboot かけたらいつまでたっても立ち上がらない…。HostOS からコンソールを開いて確認するとなんか変な画面で止まってる。おかしいなぁと思って調べてみると、「/etc/grub.confの設定に従い,Linuxカーネルが読み込まれます。」とか書いてあるサイトを発見。
そこでやっと気づきました。まるごと消しちゃいけなかったんだって。よく考えたら kernel 読み込むところ削除してるし。そりゃ起動しないよ。
7GNU GRUB を使用してシステムを起動する方法 を参考にすると、grub.conf に書かれている設定を一行ずつ実行していけばいいっぽい。でも、そのファイルに何が書かれていたのか分からない…終わった…。と絶望しかけたとき、そういえば他のプロジェクトで使ってるサーバも、バージョン一緒だったことを思い出しました。急いで接続し、その内容を起動しなくなった方のサーバで下記のように実行しました。

  • とりあえず、CentOS がこれと同じならそのまま使えます。
# 起動パーティションを指定
grub>root (hd0,0)
# Linuxカーネルを読み込む
grub>kernel /vmlinuz-2.6.18-164.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
# initrd を展開
grub>initrd /initrd-2.6.18-164.el5.img
# 読み込んだカーネルを使用し、システムを起動
grub>boot


成功!よかったよかった。冷や汗かきました。/etc の下をいじるときは、もっと慎重にならないとダメですね。あとバージョン管理の大切さが身にしみました。
今回はじめて initrd とかを知りました。こんなの今まで何も考えてなかったので、勉強になりました。
参考 : initrdとは

git-svn から git への移行方法

前回 git-svn を使って、Subversion のリポジトリと連携 の続きです。
git-svn を使っていたのですが、私が最初に設定をエレコマのリポジトリSubversion)の trunk 以下を取得するようにしていたんですね。でも、エレコマの開発者の方は、最新のソースを branch にコミットしたりしていて。そうすると、私たちのチームで開発中のソースと、最新のソースのマージが出来ないわけです。そんな状態で途方にくれていたら、エレコマの開発者の方が、リポジトリを Git に移行してくれました。
ただ、ここからが問題。ただエレコマのソースを取得するだけなら git clone で済みますが、すでに git-svn で取得したエレコマのソースに手を入れている状況です。エレコマのリポジトリSubversion の時は、変更を git-svn rebase で取得できましたが、今度はエレコマの Git のリポジトリから git pull して取得するようにしたいのです。でも、それってできるの?ということで行き詰まりました。
いろんな方法を試して、できたのが下記の手順です。

ローカルに取得しているリビジョン番号を指定して、master ブランチにマージする

git-svn で取得していた最新のリビジョンにあたる、エレコマの Git リポジトリのリビジョンを、最新ソース取得用のリポジトリ(/var/git/elecoma)にマージします。いきなりマージしてしまうとコンフリクトが起こりまくるので、一旦 git fetch してからマージを行います。
まず、master ブランチを エレコマの Git リポジトリと同様にする必要があるので、master ブランチに checkout してから作業を行います。

[root@hoge ~]# cd /var/git/elecoma
# master ブランチに移動
[root@hoge elecoma]# git checkout master
# エレコマの Git リポジトリから、ソースを取得
[root@hoge elecoma]# git fetch git://git.sourceforge.jp/gitroot/elecoma/elecoma.git master
# ローカルに取得しているリビジョン番号(git log とかで確認しておく)を指定してマージ
[root@hoge elecoma]# git merge 55be40069b3d3e837836d9260fbdb7d2c9be6ba2
Merge made by recursive.

FETCH_HEAD を master ブランチにマージする

ちなみに FETCH_HEAD とは、最後にフェッチされたブランチの先頭の簡略記法。フェッチ操作の直後にだけ有効。というものなので、先ほどフェッチしてきたものを、最新ソース取得用のリポジトリ(/var/git/elecoma)にマージする事になります。

[root@hoge elecoma]# git merge FETCH_HEAD
Merge made by recursive.
 app/controllers/cart_controller.rb       |    1 +
 config/locales/translation_ja.yml        |    3 +++
 spec/controllers/cart_controller_spec.rb |    8 +-------
 spec/fixtures/customers.yml              |    2 ++
 spec/fixtures/products.yml               |    5 +++++
 5 files changed, 12 insertions(+), 7 deletions(-)

最新になった master ブランチを dev ブランチに取り込む

今度は、私たちが開発している dev ブランチとのマージです。ここまでくればただ単に master ブランチと dev ブランチをマージするだけです。

# dev ブランチに移動
[root@hoge elecoma]# git checkout dev
[root@hoge elecoma]# git merge master
Auto-merging app/controllers/cart_controller.rb
Auto-merging config/locales/translation_ja.yml
Auto-merging spec/controllers/cart_controller_spec.rb
Auto-merging spec/fixtures/customers.yml
Auto-merging spec/fixtures/products.yml
Merge made by recursive.
 app/controllers/cart_controller.rb       |    1 +
 config/locales/translation_ja.yml        |    3 +++
 spec/controllers/cart_controller_spec.rb |    8 +-------
 spec/fixtures/customers.yml              |    1 +
 spec/fixtures/products.yml               |    5 +++++
 5 files changed, 11 insertions(+), 7 deletions(-)


これでコンフリクトなく、完了!長い道のりでした。ほんとに。

下記はメモです。今回参考になりました。

◆FETCH_HEAD
リモートリポジトリが使われている場合は、git fetch はフェッチしたブランチの先頭をすべて、.git/FETCH_HEAD に記録します。FETCH_HEAD は、最後にフェッチされたブランチの先頭の簡略記法であり、フェッチ操作の直後にだけ有効です。
ブランチに特定の名前をつけない匿名フェッチの場合でも、このシンボリック参照を使って git fetch されたコミットから HEAD を見つけることができます。

●git fetch
リモートリポジトリから、オブジェクトとそれに関連したメタデータを取得します。
●git pull
git fetch と似ていますが、それに加え、対応するブランチに変更をマージします。
●git push
オブジェクトとそれに関連したメタデータをリモートリポジトリに転送します。
●git ls-remote
リモート内の参照を表示します。

git-svn を使って、Subversion のリポジトリと連携

ちょっと前に エレコマ という EC サイト構築パッケージを利用して、ショッピングサイトを作ることになったのですが、エレコマのリポジトリSubversion でした。
私のいるプロジェクトで使おうと決めたバージョン管理システムは Git だったので、Subversion リポジトリと連携できる git-svn というコマンドを使ってソースを取得することにしました。

下記の環境で行います。

共有リポジトリの作成と、sourceforge から エレコマのソースを取得

hoge サーバ上に開発用の共有リポジトリ(bare なリポジトリ)と、エレコマの最新ソースを取得してマージするためのリポジトリそれぞれ作成します。
リポジトリの役割はまとめるとこんな感じです。

[root@hoge ~]# cd /var/git
# sourceforge にあるリポジトリから、trunk のみ取得するよう指定
# 自動的に elecoma というディレクトリ名で取得される
[root@hoge git]# git svn clone --prefix svn/ --trunk trunk -s http://svn.sourceforge.jp/svnroot/elecoma/
# 共有リポジトリを作成
[root@hoge git]# git init --bare elecoma.git
# 共有リポジトリの master ブランチに sourceforge から取得したソースを push
[root@hoge git]# cd elecoma
[root@hoge elecoma]# git push /var/git/elecoma.git master

開発用ブランチの作成

リポジトリには master の他に dev というブランチを作成することにします。私たちのプロジェクトで行う開発は dev ブランチで作業し、master ブランチについては 手を加えていないエレコマのソース、というように使い分けます。

# 共有リポジトリに移動して、dev ブランチを作成
[root@hoge elecoma]# cd ../elecoma.git
[root@hoge elecoma.git]# git branch dev
# elecoma リポジトリにも dev ブランチを作成し、dev ブランチにいるときは必ず dev へ push するよう設定
[root@hoge elecoma.git]# cd ../elecoma
[root@hoge elecoma]# git checkout --track origin/dev

エレコマの最新ソースを取得してマージするスクリプトを作成

エレコマは結構頻繁にアップデートされているので、sourceforge から最新のソースを取得して、開発中のソースとマージするスクリプトを作成しました。cron で毎日 0:00 に動くように設定します。
Subversion から git-svn で更新されたソースを取得するには、git svn rebase を使います。
ちなみに、バッチは勢いで書いたので、ログの出力内容とかいろいろひどいです。一応動きますが、コピーはオススメしません。

  • バッチ : /var/git/elecoma/tmp/update/update_elecoma.sh
#!/bin/sh
ROOT=/var/git
ELECOMA_REPO=$ROOT/elecoma.git
ELECOMA=$ROOT/elecoma
UPDATE=$ELECOMA/tmp/update
LOG=$UPDATE/logs

cd $ELECOMA
# 共有リポジトリの最新を取得
git pull $ELECOMA_REPO dev

# http://svn.sourceforge.jp/svnroot/elecoma/ から最新のソースを取得
git checkout master
git svn rebase
git push $ELECOMA_REPO master
git checkout dev
git merge master

if [ $? -eq 0 ]
then
        git push $ELECOMA_REPO dev
        if [ $? -eq 0 ]
        then
                echo 'finish'
        else
                # push に失敗した場合ログ出力
                echo "[`date +"%Y-%m-%dT%T"` #$$] INFO -- : 共有リポジトリ(elecoma.git)>への push に失敗しました。コンフリクトしている可能性があります。" >> $LOG/batch.log
                exit 1
        fi
else
        # merge に失敗した場合ログ出力
        echo "[`date +"%Y-%m-%dT%T"` #$$] INFO -- : master との merge に失敗しました。コン
フリクトしている可能性があります。" >> $LOG/batch.log
  • cron の設定方法
crontab -e
# vi でファイルを開くので、下記を記述して保存。cron のログも吐くよう設定。
PATH=/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 0 * * * /var/git/elecoma/tmp/update/update_elecoma.sh >> /var/git/elecoma/tmp/update/logs/cron.log 2>&1

ローカル環境での設定

hoge サーバに共有リポジトリを作成したので、ローカル環境で下記のようにリポジトリを clone します。

git clone ssh://yun_kichi@hoge.co.jp/var/git/elecoma.git elecoma
cd elecoma
git checkout --track origin/dev


この設定をしておけば、自分のローカルのブランチは常に dev を見るようになります。push するときも dev に push してくれるようになります。
次回は、git-svn 取得しているソースを、git に移行する手順を書こうと思います。