2014年1月31日金曜日

slapadd のログを syslogd でとる設定 ※slapd でも none を忘れずに

昨日に引き続き OpenLDAP ネタ。slapadd を実行するシェルスクリプトを書いていて、ログ出力がどうもうまくいかないので調べていた。

slapadd の man ページを参照すると -o オプションで syslogd に関する設定を色々指定できるみたいだ。該当部分はこんな記述になっている。

-o option[=value]
     Specify an option  with  a(n  optional)  value.   Possible  generic
     options/values are:

        syslog=<subsystems>  (see `-s' in slapd(8))
        syslog-level=<level> (see `-S' in slapd(8))
        syslog-user=<user>   (see `-l' in slapd(8))

        schema-check={yes|no}
        value-check={yes|no}

     The schema-check option toggles schema checking (default on);
     the value-check option toggles value checking (default off).
     The latter is incompatible with -q.
 

今回 syslogd 側 では daemon ファシリティでプライオリティが info 以上のログを出力するよう設定していたので、まずは

slapadd -o "syslog-level=INFO" -o "syslog-user=DAEMON" ・・・(以下略)
 

みたいな感じにやってみたけどログが出ない。いろいろ試行錯誤する中で、試しにそれまで付けていた -F オプションを外してみるとエラーがログに出た。どうやら -F オプションを指定するとデフォルトの slapd から出すログレベルの設定が消えて、何も syslog に出なくなるようだ。
そこで -o "syslog=<subsystems>" で改めて指定する。<subsystems> に指定できるログレベルについては昨日の記事を参照。

slapadd -o "syslog=stats,stats2,shell,parse,sync,none" -o "syslog-level=INFO" -o "syslog-user=DAEMON" ・・・(以下略)
 

これでログ出力できた。

なお、ログレベルについて一点気をつけたほうがいいことがある。それは none だ。各種サイトで調べてみても none ってなんか一見不要そうな雰囲気だから最初は付けてなかった。けれど、これは指定しておくことを強くおすすめする。

たとえば、slapd をシェルからコマンドラインで打つとシェルには出るエラーメッセージが syslogd に記録されないんでおかしいと思ってたら、none を含めたら記録されるようになった。読んで意味がわかるメッセージがログに記録されるのは重要。こんな有用なログをなぜ none って名前のログレベルにしたんだよ・・・。

ちなみに余談だけれど、上に引用した slapadd の man ページの -o オプションの部分には、syslogd へ出力するログのプライオリティを変更するための slapd の -S オプションについて言及があるのに、slapd の man ページの方には -S オプションについての説明がない(笑)。それでも -S オプションは slapd の方でもきちんと機能する。単に man ページヘの書き忘れかもしれないから、syslogd への出力プライオリティを変更するオプションとして覚えておくと便利だろう。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月30日木曜日

slapd の syslogd 向けログ出力とデバッグレベル

OpenLDAPでLDAPサーバを構築している途中で、slapdがうまく起動しないことがあった。その際、slapdがログを吐かないことに気づいた。

そもそも、OpenLDAPの場合、デフォルト設定のままではログが出力されない設定になっているらしい。変更すべき設定は syslogd のファシリティとデバッグレベル。

・syslogd のファシリティ
ファシリティとは、ログの分類のこと。syslogd で出力されるログには、アプリケーションが指定したファシリティとプライオリティが付いている。
ファシリティには次のようなものがある。

ファシリティ 対象のログ
auth(security) 認証サービスのメッセージ(現在はauthprivが推奨されている)
authpriv 認証サービス(カテゴリはauthと同じ。authとは出力結果が異なる)
cron cronのメッセージ
daemon デーモンのメッセージ
kern カーネルのメッセージ
lpr プリンタサービスのメッセージ
mail メールサービスのメッセージ
news ニュースサービスのメッセージ
syslog syslogのメッセージ
user ユーザープロセスのメッセージ
uucp uucp転送を行うプログラムのメッセージ
local0~7 アプリケーションに依存する

プライオリティは、ログの重要度を表す。次のようなものがある。

プライオリティ 説明
emerg 認証サービスのメッセージ(現在はauthprivが推奨されている)
alert 認証サービス(カテゴリはauthと同じ。authとは出力結果が異なる)
crit cronのメッセージ
err デーモンのメッセージ
warning カーネルのメッセージ
notice プリンタサービスのメッセージ
info メールサービスのメッセージ
debug ニュースサービスのメッセージ

どのファシリティのどのプライオリティのログを出力するかについては、/etc/syslog.conf ファイルに設定する。なお最近の Linux では syslogd ではなくその改良版である rsyslog が使われているかもしれないので、その場合 /etc/rsyslog.conf が設定ファイルになる。

設定は

ファシリティ.プライオリティ    ログ出力先

のように書く。例えばメールに関する全てのログを /var/log/maillog に出力する設定は

mail.*    /var/log/maillog

という感じになる。

で、話をOpenLDAPに戻すと、実はOpenLDAPのデフォルトのファシリティは local4 になっている。この local4 に対するログ出力設定がデフォルトでは存在しないため、デフォルト設定のままではログが出力されないのだ。

これに対処するには、syslogd側の設定を変更するlogd側の設定を変更する場合、ファシリティlocal4 に対する設定を追加すればいい。次のような感じになる。

local4.*    /var/log/ldap.log 

これを /etc/syslog.conf (または /etc/rsyslog.conf)に追加し、syslogデーモンを再起動すればよい。

なお、slapd側がsyslogdへログを出力する際のファシリティを変更することもできる。これは slapd 起動時の -l オプションで指定できる。LOCAL0〜LOCAL7, USER, DAEMON が指定可能。

もう一つ注意すべき点は、slapd が syslog へ出力する際のプライオリティは DEBUG になる点だ。このプライオリティを変更する方法は色々調べたが見つからなかった。(僕の環境では -S INFO を指定することでプライオリティを INFO に変更できたが、誰か正式な方法知ってたら教えてください。)

slapd が出力するログのレベルは、Configuration Backend を使っている場合 cn=config エントリの olcLogLevel に格納する。ログレベルは、次のようなものがある。

ログレベル
(10進数)
ログレベル
(16進数)
ログレベル
(文字列)
ログ出力内容
-1 - any すべてのログを出力
0 - - まったくログを出力しない
1 0x1 trace 内部の関数呼び出し
2 0x2 packets パケット操作
4 0x4 args 処理の詳細なトレース
8 0x8 cons コネクション管理
16 0x10 BER パケット送受信
32 0x20 filter 検索フィルタ処理
64 0x40 config 設定ファイルの処理
128 0x80 ACL アクセス制御リストの処理
256 0x100 stats ステータスログ(デフォルト)
512 0x200 stats2 ステータスログ2
1024 0x400 shell シェルバックエンドとの通信
2048 0x800 parse エントリの解析
16384 0x4000 sync syncrepl コンシューマ処理
32768 0x8000 none 設定に依存しない最低限の出力のみ

出力したいログレベルの数値の合計、またはログレベルの文字列をカンマ区切りで列挙する。デフォルトは 256 (stats) 。設定例としては

dn: cn=config
objectClass: olcGlobal
cn: config
olcLogLevel: trace,ACL

こんな感じ。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

参考ページ:
OpenLDAP ソフトウェア 2.3 管理者ガイド: slapd の実行
OpenLDAPによるディレクトリサーバ運用(3):ものいわぬOpenLDAPサーバのログ管理 (1/3) - @IT
Linux管理者への道(3):システム管理の基礎 syslogdの設定をマスターしよう (1/3) - @IT

2014年1月29日水曜日

UNIX/Linux系OSにおけるシグナル

昨日プロセスとシェルについて調べていく途中で、プロセス間で情報をやりとりする仕組みの一種のシグナルとはそもそも何なのかが気になって調べてみたのでまとめてみる。

↓こちらのサイトが素晴らしい解説なので一読をお勧めする。
http://www.nurs.or.jp/~sug/soft/super/signal.htm
下の文章はほぼ上記サイトの要約みたいなもんです。

・ハードウェア割り込み
キーボードからの入力など、予期せぬタイミングで処理をしなければならないイベントに対応するため、CPUには「ハードウェア割り込み」の機能がある。これはCPUのINTRピンがOFFになった時に、現在の処理を一時中断して別の処理へと飛び、完了後に元に戻ってくる機能である。ハードウェア割り込みがあるおかげで、プログラムは例えば一定間隔でキーボードの入力があったかチェックするようなことをせずに済む。

・ハードウェア割り込みとシグナル
このハードウェア割り込みの仕組みと同様の仕組みをOSにおいて実現したのがシグナルである。シグナルには対応する処理(シグナルハンドラ)を決めておくことができ、シグナルを受け取るとその処理が呼び出される。なお、UNIX系OSでは全てのシグナルにデフォルト処理が定めれらている。デフォルト処理を再定義して任意の処理に変更できる。

・シグナルのリエントラント問題とPOSIXシグナルの動作
システムコールで何かを待機中にシグナルを受け取ったり、シグナルハンドラの処理中に再度同じシグナルを受け取ることが考えられる。これらのパターンは予期しない結果を生む可能性がある。これをシグナルのリエントラント(再入)問題と呼ぶ。

そのためPOSIXの定めたシグナルの動作では、シグナルハンドラの処理中に別のシグナルを受け取ると、新しく受け取ったシグナルを保留状態にし、前のシグナルハンドラの処理が終わった段階で新しい方のシグナルを処理するようになっている。また、システムコールを実行中にシグナルを受け取った場合はデフォルトではシステムコールはエラーで終了するが、シグナルハンドラの処理完了後にシステムコールを継続するようにすることもできる。

・Linuxにおけるシグナルの一部とデフォルトの動作及び送信方法の例
SIGINT:プロセスの終了。シェルでCtrl+Cが押されると送信。
SIGTSTP:プロセスを一旦停止。シェルでCtrl+Zが押されると送信。
SIGCONT:プロセスを再開。シェルで「fg」や「bg」と叩いた時送信。
SIGQUIT:プロセスを終了してコアダンプ。シェルでCtrl+\が押されると送信。
SIGTERM:プロセスの終了。子プロセスがある場合、そのプロセスが子プロセスも終了させる。killコマンド実行時にデフォルトで送信。
SIGHUP:プロセスの終了(デーモンの場合再起動)。ログインシェルからログアウト時に送信。
SIGKILL:プロセスの強制終了。killコマンドで明示的に指定すれば送信できる。
上記の他にも多数ある。下の参考URLを参照。

こんなもんかな。シグナルについてまとめてみたけれど、よく考えられてると思う。シェルからの操作とか、killコマンドとか、なんとなくの理解のまま使ってたのでスッキリした。

2014年1月28日火曜日

シェルとプロセスとプログラムの呼び出し

結構長いことLinuxを使っている自分だけれども、アプリ開発の仕事をずっとやってきたために、OSの機能については疎いところがあったりする。

先日、プログラミング言語から別のプログラムを呼ぶときに「シェル経由で呼び出す」方法と「シェルを経由せずに呼び出す」方法がある、という話を聞いた時によくわからなかったので、調べてみた。

・プロセスの概念
まず、プロセスの概念について。言葉だけは何となく使っちゃてるけど。プロセスとは、プログラムを実行するときにメモリ上にロードしてIDを割り当てたもの。たとえ同じプログラムでも、別々のメモリ領域を与えてアドレス空間を分離することによって、互いに干渉せず同時に実行することが可能になる。

・プロセスの複製と親子関係
UNIX系OSにはfork()システムコールがあり、これはプロセスを環境変数や実行位置なども含め現在の状態をまるごとコピーしてIDを付け替えたプロセスを生成する。fork()を呼び出した側が親プロセスで、fork()によって生成されたプロセスが子プロセスとなる。fork()の戻り値として親プロセス側には子プロセスのプロセスIDが渡され、子プロセス側には0が渡されるので自分が親か子かを判別できる。

・子プロセスの置き換え
あるプログラムから別のプログラムを呼び出す場合、まずfork()システムコールで子プロセスを生成し、子プロセス側でexec()システムコールを呼び出す。exec()システムコールは、自分自身を別のプログラムに置き換える(一旦メモリ上のアドレス空間を破棄して作りなおす)。プロセスIDは維持されるので、親子関係は保たれる。

・プロセスの終了
プロセス内でexit()システムコールを呼び出すと、そのプロセスは終了するが、メモリ上に管理情報は残っている。親プロセスからwait()システムコールを呼ぶことによって親は子プロセスの終了ステータスを取得した上で子プロセスの管理情報を削除する。親プロセスがwait()を呼ぶタイミングは、子プロセスを生成してすぐか、あるいは子プロセスから終了シグナルを受け取った時点になる。

・プロセスツリー
UNIX系OSでは、プログラムの実行は fork() → exec() を利用して、次々と子プロセスを生成していくことで行われる。一番最初のプロセスは、カーネル自身が init というプロセスを生成し、全てのプロセスの親となる。init を頂点に全てのプロセスがツリー状に親子関係でつながっている。ちなみに、子プロセスの終了前に親プロセスでが終了した場合、子プロセスの親は自動的に init になる。この場合の子プロセス終了時にwait()を呼ぶ処理は init が担当することになる。

・シェルはプログラム実行時に何をしているのか?
シェルは、ユーザからプログラム実行のコマンドを受け付けると、自分自身をfork()によって複製し、子プロセス側でexec()を呼び出して指定されたプログラムを実行する。あとは、wait() を呼んで子プロセスの終了を待つ(フォアグラウンドジョブ)。あるいは、wait()をすぐには呼ばず、にユーザからのコマンド受付状態に戻るようにも指定できる(バックグラウンドジョブ)。この場合終了した子プロセスに対して wait() を呼ぶのはシェルが適宜行ってくれると思う(詳しい方いたら教えてください)。

・シェルを経由するかしないかの違いとは
要するに fork() → exec() (+ wait()の待機) の一連の流れができれば、あるプログラムから別のプログラムを呼び出すことができる。この処理をシェルに任せるのが「シェル経由で呼び出す」方法であり、独自に行うのが「シェルを経由せずに呼び出す」方法だということ。

だいたいこんなもんかな。上に書いていない知識も色々整理できたが(親プロセスが異常終了した場合の子プロセス=ゾンビプロセスについてとか)、その辺は下の参考URLを参照。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

※参考URL
http://fumiyas.github.io/2013/12/21/dont-use-shell.sh-advent-calendar.html
http://itpro.nikkeibp.co.jp/article/COLUMN/20080502/300565/?ST=oss&P=1
http://www.gadgety.net/shin/tips/unix/ipc/fork.html
http://www.fireproject.jp/feature/c-language/process/fork-wait.html

2014年1月27日月曜日

統一性へのこだわり

Linuxディストリビューションのパッケージに付いてくる設定ファイルなんかでも、たまにインデントでスペースとタブが入り交じっているのを見かけたりする。

でもこれは僕としては相当気になる(笑)。だから自分でカスタマイズするときは直してしまう。タブをスペースに全部置き換えるとか。

あるいは、人から引き継いだプロジェクトでタブとスペースがごちゃまぜになってる時などは一括で置換してしまう。Git でソースコードを管理しているなら、空白を除いて比較するコマンド

git diff -w

とか使えるわけだから、間違って空白以外の部分をいじってしまった時はすぐ分かるし、リスクも特に高くない。

さて、上記はインデントの話だけれど、例えば if 文の後のカッコの置き方とか変数の名前の付け方とか、そういったものを統一させようがさせまいがプログラムとしては正しく動作するだろう。しかし細かい部分での統一性を維持しようとすることが、結局バグの発生率も下げるし、メンテナンス性のいいコードになると思うんだ。

統一性へのこだわりっていうのは一種の美意識って側面もあるから、プログラミングを扱うITエンジニアには美的センスも求められると僕は勝手に思っている。一つ経験則として言えるのは、ITエンジニアとして仕事がよくできる人は、殆ど例外なく美的センスを持っている人だったってことだ。

いかにもガチガチに論理的そうなイメージのITエンジニアだけど、実は美的センスも仕事の成果に結びつく。これだからITエンジニアは面白いんだ。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月26日日曜日

面倒さを乗り越える能力はエンジニアの強み

他の人が面倒で諦めてしまうことでも、一歩一歩進めていける能力があるエンジニアは非常に強いと思う。

これは最近仕事をしていてよく感じることなんだけれども、結局のところIT系のエンジニアの仕事というのは、細かな作業の大量集合体みたいな部分があり、普通に客観的に考えれば大変面倒だと思ってしまう要素を多分に含んでいる。

その面倒さをいかに乗り越えていけるか、という能力がエンジニアの強みの1つなんじゃないかと思うわけだ。

面倒さを乗り越える能力というのは、例えば論理的な解決の道筋を考えて頭の中で構成し、その頭の中の解決策にそって一歩一歩地道な作業を進めていける能力、という風に表現できる。

ではどうやったらその能力が身につくかというと、そういう面倒な状況から逃げずに正面からガッツリ戦うしかないんではないかと思う。もちろん最初は上手くいかず、人の助けを借りる必要もあるかもしれない。でもその時に人に頼り切るのではなくて、自分の可能な限り自分のできることを、面倒でも億劫でもやっていくしか、能力を身につける方法はないだろう。

だから、僕は非常に面倒そうな問題に直面した時に、自分を鍛える絶好の機会だと思って喜ぶことにしている。そう考えれば結構楽しめるものだ。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月25日土曜日

シェルスクリプトの全引数リスト $*, "$*", $@, "$@" の違い

シェルスクリプトで全引数リストをまとめて扱う変数は「$*」と「$@」がある。色々なサイトで解説があるが、自分の理解のため実際にスクリプトを書いて実験してみた。

用意したスクリプトはこちら:

test.sh
#!/bin/sh

print_arguments() {
  echo "\$1: $1"
  echo "\$2: $2"
  echo "\$3: $3"
  echo ""
}

test_pattern() {
  echo '$*'
  print_arguments $*

  echo '"$*"'
  print_arguments "$*"

  echo '$@'
  print_arguments $@

  echo '"$@"'
  print_arguments "$@"
}

echo 'arguments: "arg1 arg2" "arg3"'
echo ''
test_pattern "arg1 arg2" "arg3"

これを実行すると、次の結果が得られる。

[user@localhost]~% sh ./test.sh
arguments: "arg1 arg2" "arg3"

$*
$1: arg1
$2: arg2
$3: arg3

"$*"
$1: arg1 arg2 arg3
$2: 
$3: 

$@
$1: arg1
$2: arg2
$3: arg3

"$@"
$1: arg1 arg2
$2: arg3
$3: 

ダブルクオーテーションで囲わない場合、$*と$@は同じで、元のダブルクオーテーションは無視される。

ダブルクオーテーションで囲った場合、"$*"は元のダブルクオーテーションを無視して全部を一括りに囲ったように動作するけど、"$@"はもとのダブルクオーテーションと同じように各引数をダブルクオーテーションで括ったものとして動作する。

言葉に書いて文章にしてみれば他のサイトに書いてあることと結局同じだけれど、こういうのは自分で実験してみると理解が深まるね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月24日金曜日

変数名・関数名などはなるべく手打ちしない

プログラミングをする上で最も初歩的かつ比較的よくあるバグの原因はスペルミスだと思う。納品後とか製品出荷後に判明する類のバグというよりも、開発の速度を遅らせる類のバグとして。

これを防ぐために普段僕が意識していることは記事のタイトルの通り「変数名・関数名などはなるべく手打ちしない」こと。変数名・関数名だけでなく定数名・クラス名・メソッド名などなど、要するにあらゆるシンボル文字列で言えること。

どういうことかというと、例え短い変数名だとしてもなるべくエディタやエディタや統合開発環境の補完機能は最大限活用する。例えば4文字の変数名でも3文字打って残りの1文字はあえて補完させる。(もちろん最初に打つ時は例外。)

その程度なら手打ちしても変わらないといえば確かにその通りなのだけれども、しかしこれは手間の問題ではなく習慣の問題なんだよね。ミスというのは自分で気づけない無意識的な行動だからミスなわけで、防ぐにはミスが発生しなような習慣をつける(=無意識的な行動を変える)必要がある。

些細な事だけれども、プログラミングにおいてコードの信頼性を上げるというのは、こういう小さなことの積み重ねではないかと思う。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月23日木曜日

シェルスクリプトで trap にシグナル 0 を指定する意味

slapdの起動スクリプト(/etc/init.d/slapd)をカスタマイズしている時に、
trap コマンドにシグナル 0 を指定している記述を見つけた。
該当箇所はこんな感じになっている。

start_ldap() {
  trap 'report_failure' 0
  log_daemon_msg "Starting OpenLDAP" "slapd"
  start_slapd
  trap "-" 0
  log_end_msg 0
}

trapの意味は第1引数に実行するコマンド、第2引数以降は捕捉するシグナルだけれども、POSIXの定義には0という値のシグナルはない。では0を指定している意味はなんだろう?
と思って更に調べると見つかった。
http://shellscript.sunone.me/signal_and_trap.html

どうやら、シェルスクリプトが終了するときのEXITシグナルを補足することで、シェルスクリプト終了時に必ず実行される処理を指定できるということらしい。そして trap の第2引数に 0 と書くと EXIT と書いたのと同じ意味になるということだ。

上記リンク先の解説によると
exit するルートが複数存在し、かつ終了時に共通の処理を行う必要があるシェルスクリプトにおいては非常に有効である。
とのことだ。
なお、EXITの他にもDEBUG、ERR、RETURNがあるようだ。下記URLを参照。
http://fumiyas.github.io/2013/12/05/trap-exit.sh-advent-calendar.html

ちなみに冒頭で引用したスクリプトで trap の第1引数に "-" を指定している部分は捕捉を無効化するという意味。使い方的には try 〜 catch 構文を連想するなぁ。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月22日水曜日

仮想マシン上のLinuxでディスク容量を拡張してLVMとして追加する

VMWare Fusion に Debian7 を入れて使っているんだけど、最初の見立てが甘かったため、ディスク容量が一杯になってしまった。

そこで、ハードディスクの容量を増やして空き領域に新たなパーティションを作成し、LVMとして追加することにした。LVMなら再度容量を追加したりするとき便利だし。

まあ仮想マシンだから新たにディスクを増やしてもいいんだけれど、そこはせっかくなので仮想マシンならではの方法をやってみよう、ということでディスクの容量追加にした。

ただ容量増加前の状態だと20GBの容量ほぼ全部を1つのパーティションとして使っていて(かつLVMにしていない)、ここにルート(/)以下をまるごと突っ込んでしまっているので、このパーティション自体の容量を増やそうとすると色々と面倒だ。

そこで最も容量を圧迫している /home 以下に、新たに作成したLVMパーティションを割り当てることにした(参考にしたのはここ)。

まず、VMWare の設定画面からハードディスクの容量を変更する(20GB -> 50GB)。スナップショットがあると変更できないので、必要なデータは事前にバックアップしたうえでスナップショットは消しておく。
そしてルート権限で fdisk コマンドを実行する。-lオプションをつけて現在の状態を確認。

[root@localhost]~# fdisk -l

Disk /dev/sda: 53.7 GB, 53687091200 bytes
255 heads, 63 sectors/track, 6527 cylinders, total 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000325f8

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048    40136703    20067328   83  Linux
/dev/sda2        40138750    41940991      901121    5  Extended
/dev/sda5        40138752    41940991      901120   82  Linux swap / Solaris

/dev/sdaは53.7GBと認識されてる。もちろん容量を増やしたから、現在のパーティションを全部合わせても空き領域がある。この空き領域に新しくパーティションを作成して/home用に使うことにする。その際、パーティションの開始セクタを空き領域の先頭に設定する。

そこで fdisk で続けて次の操作を行う。
・nコマンドを入力(パーティションを新規作成)
・パーティションタイプはデフォルト(プライマリ)でよいのでそのままEnter
・パーティション番号もデフォルト(この場合は3)でよいのでそのままEnter
・開始セクタは空き領域の先頭(この場合は41940992)を入力
・終了セクタは自動的に最終セクタがデフォルトになるのでそのままEnter

Command (m for help): n
Partition type:
   p   primary (1 primary, 1 extended, 2 free)
   l   logical (numbered from 5)
Select (default p): 
Using default response p
Partition number (1-4, default 3): 
Using default value 3
First sector (40136704-104857599, default 40136704): 41940992
Last sector, +sectors or +size{K,M,G} (41940992-104857599, default 104857599): 
Using default value 104857599

パーティションができたかpコマンドで確認。

Command (m for help): p

Disk /dev/sda: 53.7 GB, 53687091200 bytes
255 heads, 63 sectors/track, 6527 cylinders, total 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000325f8

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048    40136703    20067328   83  Linux
/dev/sda2        40138750    41940991      901121    5  Extended
/dev/sda3        41940992   104857599    31458304   83  Linux
/dev/sda5        40138752    41940991      901120   82  Linux swap / Solaris

/dev/sda3としてできている。

パーティションのタイプをtコマンドでLVMに変更する。
fdiskでの操作は
・コマンドは t を入力
・パーティション番号は先ほど作成した番号(3)を入力
・ヘックスコードはLVMを表す 8e を入力

[root@localhost]~# fdisk /dev/sda

Command (m for help): t
Partition number (1-5): 3
Hex code (type L to list codes): 8e
Changed system type of partition 3 to 8e (Linux LVM)

pコマンドで結果を確認する。

Command (m for help): p

Disk /dev/sda: 53.7 GB, 53687091200 bytes
255 heads, 63 sectors/track, 6527 cylinders, total 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000325f8

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048    40136703    20067328   83  Linux
/dev/sda2        40138750    41940991      901121    5  Extended
/dev/sda3        41940992   104857599    31458304   8e  Linux LVM
/dev/sda5        40138752    41940991      901120   82  Linux swap / Solaris

確認できたら、w コマンドでパーティションテーブルに書き込む。

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: デバイスもしくはリソースがビジー状態です.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

古いパーティションを使用中なので警告が出る。パーティションテーブルを有効化するために再起動する。

[root@localhost]~# reboot

再起動後、PVを作成しようと思ったらコマンドがなかったので lvm2 をインストール。

[root@localhost]~# apt-get install lvm2

コマンドを認識させるためログアウト&ログインしておく。そしてあらためてPVを作成。

[root@localhost]~# pvcreate /dev/sda3
  Writing physical volume data to disk "/dev/sda3"
  Physical volume "/dev/sda3" successfully created

VG作成、今回はPVを1つだけ使うことにする。

[root@localhost]~# vgcreate home1 /dev/sda3
  Volume group "home1" successfully created

LV作成、VG空き容量の全部を使うことにする。

[root@localhost]~# lvcreate -n home1-1 -l 100\%FREE home1
  Logical volume "home1-1" created

ext4としてフォーマット

[root@localhost]~# mkfs.ext4 /dev/home1/home1-1
mke2fs 1.42.5 (29-Jul-2012)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
1966080 inodes, 7864320 blocks
393216 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
240 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
 4096000

Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

/homeの中身を引っ越すため、一時的にマウントポイントを作成して新しく作成したパーティションをマウントし、コピーする。

[root@localhost]~# mkdir /mnt/home1-1
[root@localhost]~# mount /dev/home1/home1-1 /mnt/home1-1 
[root@localhost]~# cp -rp /home/* /mnt/home1-1

/etc/fstabを編集する。

[root@localhost]~# vim /etc/fstab

最終行に以下を追加して保存

/dev/home1/home1-1 /home ext4 defaults 1 2

最後に、コピー済みの/homeの中身を消して再起動

[root@localhost]~# rm -rf /home/*
[root@localhost]~# reboot

再起動後、dfで確認してみる。

[root@localhost]~# df -h
ファイルシス                                           サイズ  使用  残り 使用% マウント位置
rootfs                                                    19G  6.6G   12G   37% /
udev                                                      10M     0   10M    0% /dev
tmpfs                                                    397M  688K  396M    1% /run
/dev/disk/by-uuid/a1211e97-cb86-4aa7-8829-0700c8e26c1a    19G  6.6G   12G   37% /
tmpfs                                                    5.0M     0  5.0M    0% /run/lock
tmpfs                                                    969M  220K  969M    1% /run/shm
/dev/mapper/home1-home1--1                                30G  8.1G   20G   29% /home

/homeの容量が増えました。めでたしめでたし。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月21日火曜日

仮想マシン上のLinuxでファイルシステムをまるごとバックアップ

VMWare FusionにDebian7をインストールして使っているんだけれど、
ちょっとバックアップをとりたくなったのでやってみた。

まず仮想マシンの設定で、十分な容量のハードディスクを新規に作成して追加しておく。(今回は20GB)

そしたらルート権限で fdisk コマンドを実行。-lオプションをつけて現在の状態を確認。/dev/sdbが新たに追加したハードディスク。

[root@localhost]~# fdisk -l

Disk /dev/sda: 53.7 GB, 53687091200 bytes
255 heads, 63 sectors/track, 6527 cylinders, total 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000325f8

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048    40136703    20067328   83  Linux
/dev/sda2        40138750    41940991      901121    5  Extended
/dev/sda5        40138752    41940991      901120   82  Linux swap / Solaris

Disk /dev/sdb: 21.5 GB, 21474836480 bytes
255 heads, 63 sectors/track, 2610 cylinders, total 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000


Disk /dev/sdb doesn't contain a valid partition table

次に新しく追加したディスクをfdiskのオプションで渡して実行。
コマンド入力を求められるが、まずコマンド一覧を確認。

[root@localhost]~# fdisk /dev/sdb
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x2fce4abf.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

Command (m for help): m
Command action
   a   toggle a bootable flag
   b   edit bsd disklabel
   c   toggle the dos compatibility flag
   d   delete a partition
   l   list known partition types
   m   print this menu
   n   add a new partition
   o   create a new empty DOS partition table
   p   print the partition table
   q   quit without saving changes
   s   create a new empty Sun disklabel
   t   change a partition's system id
   u   change display/entry units
   v   verify the partition table
   w   write table to disk and exit
   x   extra functionality (experts only)


nコマンドで新規パーティションを作成する。いくつか質問されるが、
・パーティションタイプはプライマリ(p)
・パーティション番号は最初のパーティションなので1
・開始セクタはデフォルト(2048)
・終了セクタはバックアップ対象と同じセクタで作成する(40136703)

Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-41943039, default 2048): 2048
Last sector, +sectors or +size{K,M,G} (2048-41943039, default 41943039): 40136703

できたか確認。

Command (m for help): p

Disk /dev/sdb: 21.5 GB, 21474836480 bytes
255 heads, 63 sectors/track, 2610 cylinders, total 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xc3d14735

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    40136703    20067328   83  Linux

確認したらwコマンドで書き込む。

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
[root@localhost]~# 

シングルユーザモードに移行する。リードオンリーモードで / をリマウントする。

# telinit 1
[root@localhost]~# mount -r -n -o remount /

dd でバックアップ。

[root@localhost]~# dd  if=/dev/sda1 of=/dev/sdb1

終わったらリードオンリーから通常のマウントに戻す。

[root@localhost]~# mount -w -n -o remount /

これで完了。
バックアップから復元する時はddコマンドのifとofを逆にすればいい。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年1月20日月曜日

技術系の話題はこちらに投稿します

今まで技術系の話題もそれ以外の話題も区別せずにブログを書いてきたけれど、
今後は技術系の話題はここに書いていこうと思う。
よろしくお願いします。