HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

QueueやStackとして使うなら‥LinkedList vs ArrayDeque

頭がJava 1.4で止まっているプログラマの呟き(謎)‥

LinkedListをQueueとして使ったあるプログラム(何)を書いていて、QueueやStackとして使うならLinkedListよりArrayDequeがお勧めとアドバイスをもらったので、軽く検証してみた。

事前準備

ソースコードは以下のような感じ。

  • LinkedListAsQueue.java
import java.util.LinkedList;
import java.util.Queue;

public class LinkedListAsQueueTest {
    public static void main(String[] args) {
        Queue<String> q = new LinkedList<>();

        long S = System.currentTimeMillis();
        while (true) {
            q.offer("a");
            long E = System.currentTimeMillis();
            System.err.println("q=" + q.size() + ", " + (E - S) + "ms");
        }
    }
}
  • ArrayDequeAsQueue.java
import java.util.ArrayDeque;
import java.util.Queue;

public class ArrayDequeAsQueueTest {
    public static void main(String[] args) {
        Queue<String> q = new ArrayDeque<>();

        long S = System.currentTimeMillis();
        while (true) {
            q.offer("a");
            long E = System.currentTimeMillis();
            System.err.println("q=" + q.size() + ", " + (E - S) + "ms");
        }
    }
}
  • LinkedListAsStack.java
import java.util.LinkedList;
import java.util.Deque;

public class LinkedListAsStackTest {
    public static void main(String[] args) {
        Deque<String> q = new LinkedList<>();

        long S = System.currentTimeMillis();
        while (true) {
            q.push("a");
            long E = System.currentTimeMillis();
            System.err.println("q=" + q.size() + ", " + (E - S) + "ms");
        }
    }
}
  • ArrayDequeAsStack.java
import java.util.ArrayDeque;
import java.util.Deque;

public class ArrayDequeAsStackTest {
    public static void main(String[] args) {
        Deque<String> q = new ArrayDeque<>();

        long S = System.currentTimeMillis();
        while (true) {
            q.push("a");
            long E = System.currentTimeMillis();
            System.err.println("q=" + q.size() + ", " + (E - S) + "ms");
        }
    }
}

XxxAsStack.javaの方はDequeなので末尾からpush/popする使い方もありだが、わざと先頭に要素を追加する実装にしてある。

実行

長時間待つのが面倒なので、最大ヒープサイズを小さくして実行する。

$ java -Xmx8m <ClassName>

これでOutOfMemoryErrorが出るまで待って最終行を記録、ということを5回ずつ繰り返す。

結果

  • LinkedListAsQueue
q=328077, 6279ms
q=328112, 6165ms
q=328077, 6187ms
q=328077, 6463ms
q=328122, 6526ms
  • ArrayDequeAsQueue
q=524287, 6834ms
q=524287, 6766ms
q=524287, 6697ms
q=524287, 6732ms
q=524287, 6460ms
  • LinkedListAsStack
q=328111, 6309ms
q=328076, 6290ms
q=328085, 6378ms
q=328076, 6134ms
q=328076, 6326ms
  • ArrayDequeAsStack
q=524287, 6769ms
q=524287, 6576ms
q=524287, 6545ms
q=524287, 7081ms
q=524287, 6917ms

それぞれの平均をまとめると以下のような感じ。

要素の数 時間(ms) 要素の数/時間(ms)
LinkedListAsQueue 328093.0 6324.0 51.881
ArrayDequeAsQueue 524287.0 6697.8 78.277
LinkedListAsStack 328084.8 6287.4 52.181
ArrayDequeAsStack 524287.0 6777.6 77.356

確かに、メモリ使用量の観点からも速度の観点からもLinkedListよりArrayDequeの方がQueueやStackに向いていそう。速度的にはLinkedListの方が早いのかなと勝手に思っていたが違うのか‥ちょっとArrayDequeクラスのソースを覗いてみよう‥

文字列連結のパフォーマンス比較

そういえば計ったことなかったなぁ‥と思い立って、いくつかのパターンで計ってみたメモ。

実行環境は、自宅のXenServer上に載せた以下のVM環境。

ケース1:文字列連結演算子とダブルクォーテーションと

プログラムの全体は以下のような感じ。

n,test1,test2
<?php

for ($n = 10000; $n <= 50000; $n += 5000) {
set_time_limit(120);
    // test1
    $s1 = microtime(true);
    $str = '';
    for ($i = 0; $i < $n; ++$i) {
        $str = $str . 'a' . $i . 'b';
    }
    $e1 = microtime(true);

    // test2
    $s2 = microtime(true);
    $str = '';
    for ($i = 0; $i < $n; ++$i) {
        $str = "{$str}a{$i}b";
    }
    $e2 = microtime(true);

    printf("%d,%.3f,%.3f\n",
        $n, $e1 - $s1 , $e2 - $s2);
}

文字列を

        $str = $str . 'a' . $i . 'b';

で連結するか

        $str = "{$str}a{$i}b";

で連結するかの違い。

実行すると、以下のような出力が得られる。

n,test1,test2
10000,0.075,0.029
15000,0.228,0.065
20000,0.445,0.120
25000,0.873,0.199
30000,1.422,0.300
35000,2.168,0.451
40000,3.023,0.581
45000,2.802,0.739
50000,3.962,0.938

グラフにしてみるとこんな感じ。

f:id:hhelibex:20160609133252p:plain

ケース2:ダブルクォーテーションと複合演算子

プログラムの全体は以下のような感じ。

n,test2,test3,test4
<?php

for ($n = 10000; $n <= 100000; $n += 5000) {
set_time_limit(120);
    // test2
    $s2 = microtime(true);
    $str = '';
    for ($i = 0; $i < $n; ++$i) {
        $str = "{$str}a{$i}b";
    }
    $e2 = microtime(true);

    // test3
    $s3 = microtime(true);
    $str = '';
    for ($i = 0; $i < $n; ++$i) {
        $str .= 'a' . $i . 'b';
    }
    $e3 = microtime(true);

    // test4
    $s4 = microtime(true);
    $str = '';
    for ($i = 0; $i < $n; ++$i) {
        $str .= "a{$i}b";
    }
    $e4 = microtime(true);

    printf("%d,%.3f,%.3f,%.3f\n",
        $n, $e2 - $s2, $e3 - $s3, $e4 - $s4);
}

文字列を

        $str = "{$str}a{$i}b";
        $str .= 'a' . $i . 'b';
        $str .= "a{$i}b";

のように連結するという3パターン。1つ目は、ケース1で早かった方と同じ。

実行すると、以下のような出力が得られる。

n,test2,test3,test4
10000,0.016,0.001,0.001
15000,0.034,0.002,0.002
20000,0.069,0.003,0.003
25000,0.102,0.004,0.003
30000,0.149,0.004,0.004
35000,0.210,0.005,0.005
40000,0.281,0.006,0.005
45000,0.363,0.006,0.006
50000,0.455,0.007,0.007
55000,0.556,0.008,0.008
60000,0.668,0.009,0.008
65000,0.782,0.009,0.009
70000,0.916,0.010,0.009
75000,1.054,0.011,0.011
80000,1.212,0.011,0.011
85000,1.364,0.012,0.012
90000,1.534,0.013,0.012
95000,1.723,0.013,0.013
100000,1.915,0.014,0.014

‥圧倒的差である。一応グラフにしてみる。

f:id:hhelibex:20160609134310p:plain

test3のグラフは隠れて見えないが、test4の下に隠れている。

結局‥

  • ケース2の結果を見る限り、長い文字列$strが右辺に出てくるときの評価にコストがかかるのだろうか。
  • ケース1の結果から、同じ長い文字列が右辺に現れざるを得ない場合は、文字列連結演算子の処理の方がコストがかかるということか。

正直、ここまで差が出るとは思わなかったので、文字列連結処理を書くときには、これからは気を使うようにしようと思う。

PHPの識別子にアスキーコード0x7Fが使えることの検証

ということなので、実際に試してみた。

どうせなら1つのソースコードで完結させてやろうということで得られたコードが以下。

<?php

$name = "\x7F";
$expr = '$' . $name . '="b";var_dump($name, $' . $name . ');';
var_dump($expr);
eval($expr);

これを実行すると、以下のように出力される。(0x7Fの部分は□で表している)

string(27) "$□="b";var_dump($name, $□);"
string(1) "□"
string(1) "b"

ちなみに、識別子の先頭文字としては使えない数字を入れてみたコード:

<?php

$name = "1\x7F";
$expr = '$' . $name . '="b";var_dump($name, $' . $name . ');';
var_dump($expr);
eval($expr);

を実行すると、ちゃんとエラーになる。

string(29) "$1□="b";var_dump($name, $1□);"
PHP Parse error:  syntax error, unexpected T_LNUMBER, expecting T_VARIABLE or '$' in /home/kajino/foo.php(6) : eval()'d code on line 1

is_aとis_subclass_ofの違い

クラス名の文字列を渡して使用しようとしたときに罠にはまったのでメモ。

はまったのは、v5.3.9から追加されたというallow_stringパラメータのデフォルト値に関連するものだが、他にも違いがあるようなので、2パターンほど試してみる。

試した環境は以下の通り。

メソッドのマニュアルを読むと、以下のようになっている。

  • is_a
    • allow_stringのデフォルト値はFALSE、つまり検査対象に文字列を許さない
  • is_subclass_of
    • allow_stringのデフォルト値はTRUE、つまり検査対象に文字列を許す

つまり、CentOS 6(PHP 5.3.3)の環境下では上記の挙動しか選択できない。

テスト1:直系の子クラスを指定

コードは以下のようなもの。

<?php

class A{}
class B extends A{}

$b = new B();

var_dump(
    is_a($b, "A")
  , is_subclass_of($b, "A")
  , is_a("B", "A")
  , is_subclass_of("B", "A")
);

出力は以下。

bool(true)
bool(true)
bool(false)
bool(true)

is_a("B", "A")」のパターンだけ、検証対象のクラス名を表す文字列を指定できないのでfalseが返されている。

テスト2:親クラス自身を指定

コードは以下のようなもの。

<?php

class A{}

$a = new A();

var_dump(
    is_a($a, "A")
  , is_subclass_of($a, "A")
  , is_a("A", "A")
  , is_subclass_of("A", "A")
);

出力は以下。

bool(true)
bool(false)
bool(false)
bool(false)

is_subclass_of($a, "A")」で、確かに「サブクラス」ではないのでfalseが返されている。

is_a("A", "A")」でfalseが返されるのは、テスト1と同じ理由。

PostgreSQLサーバー用ディレクトリを暗号化ファイルシステムに置き換えてみる~論理ボリューム作成編

前置き

やりたいことは、前に書いた

と同じなのだが、

  • HDDを追加せずに、論理ボリュームlv_rootを分割して、片方を論理ボリュームlv_pgsqlとして暗号化ファイルシステムにする

という制約を課す(というか、元々やりたかったのはこっち)。

事前確認

lv_rootの情報を確認しておく。

# df -m
Filesystem           1M-blocks  Used Available Use% Mounted on
/dev/mapper/VolGroup-lv_root
                         14023  1005     12300   8% /
tmpfs                      372     0       372   0% /dev/shm
/dev/sda1                  477    57       395  13% /boot
# lvdisplay --units m /dev/mapper/VolGroup-lv_root
  --- Logical volume ---
  LV Path                /dev/VolGroup/lv_root
  LV Name                lv_root
  VG Name                VolGroup
  LV UUID                SHPvKC-d2fE-Rb2D-5lpT-eqly-QWAB-rGLf3z
  LV Write Access        read/write
  LV Creation host, time localhost.localdomain, 2015-11-07 11:53:33 +0900
  LV Status              available
  # open                 1
  LV Size                14376.00 MiB
  Current LE             3594
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0
   
# 

下準備

いずれにしてもPostgreSQLサーバーは一旦止めないといけないし、lv_rootを分割するにはOSを停止しないといけないので、以下を実行する。

# chkconfig postgresql off
# sync;sync;sync;shutdown -h now

OSをシャットダウンしたら、lv_rootを分割するために、インストールメディアを挿入してRescueモードで再起動する。

起動したら、以下の手順でシェルを起動する。

  • Rescue installed systemを選択してEnterキーを押す
  • Choose a LanguageEnglish
  • Keyboard Typejp106
  • Setup NetworkingNo
  • RescueSkip
  • shell Start shellを選択

lv_rootの分割

bash-4.1# lvm vgchange -a y
  2 logical volume(s) in volume group "VolGroup" now active
bash-4.1# fsck.ext4 -f /dev/mapper/VolGroup-lv_root
e2fsck 1.41.12 (17-May-2010)
Pass 1: Checking inodes blocks, and sizes
    (略)
bash-4.1# resize2fs /dev/mapper/VolGroup-lv_root 6184M
resize2fs 1.41.12 (17-May-2010)
Resizing the filesystem on /dev/mapper/VolGroup-lv_root to 1583104 (4k) blocks.
The filesystem on /dev/mapper/VolGroup-lv_root is now 1583104 blocks long.

bash-4.1# lvm lvreduce -L6184M /dev/mapper/VolGroup-lv_root
  WARNING: Reducing active logical volume to 6.04GiB
  THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you realy want to reduce lv_root? [y/n]: y
  Size of logical volume VolGroup/lv_root changed from 14.04 GiB (3594 extents) to 6.04 GiB (1546 extends).
  Logical volume lv_root successfully resized
bash-4.1# exit

メニューに戻ったら、インストールメディアを抜いた後、rebootを選択して再起動する。

空き領域の確認

ちゃんと空き領域が確保できたかどうかを確認する。

# lvdisplay --units m /dev/mapper/VolGroup-lv_root
  --- Logical volume ---
  LV Path                /dev/VolGroup/lv_root
  LV Name                lv_root
  VG Name                VolGroup
  LV UUID                SHPvKC-d2fE-Rb2D-5lpT-eqly-QWAB-rGLf3z
  LV Write Access        read/write
  LV Creation host, time localhost.localdomain, 2015-11-07 11:53:33 +0900
  LV Status              available
  # open                 1
  LV Size                6184.00 MiB
  Current LE             1546
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0
   
# vgdisplay --units m
  --- Volume group ---
  VG Name               VolGroup
  System ID             
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  4
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                2
  Open LV               2
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               15880.00 MiB
  PE Size               4.00 MiB
  Total PE              3970
  Alloc PE / Size       1922 / 7688.00 MiB
  Free  PE / Size       2048 / 8192.00 MiB
  VG UUID               n0127i-W87x-Y9GI-CAT9-60yD-1ZQz-J9KQ9b
   
# 

lv_rootが縮小され、8GiBの空き領域が確保された事が確認できる。

暗号化ファイルシステムの作成と自動接続

論理ボリュームの作成

PostgreSQL用のデータディレクトリなので、lv_pgsqlとして論理ボリュームを作成する。

# lvcreate -L 8192M -n lv_pgsql VolGroup
  Logical volume "lv_pgsql" created.
# lvdisplay --units m
  --- Logical volume ---
  LV Path                /dev/VolGroup/lv_root
  LV Name                lv_root
  VG Name                VolGroup
  LV UUID                SHPvKC-d2fE-Rb2D-5lpT-eqly-QWAB-rGLf3z
  LV Write Access        read/write
  LV Creation host, time localhost.localdomain, 2015-11-07 11:53:33 +0900
  LV Status              available
  # open                 1
  LV Size                6184.00 MiB
  Current LE             1546
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0
   
  --- Logical volume ---
  LV Path                /dev/VolGroup/lv_swap
  LV Name                lv_swap
  VG Name                VolGroup
  LV UUID                221tmz-bcIA-Tafg-kUvM-xxwv-8uID-qiJSpr
  LV Write Access        read/write
  LV Creation host, time localhost.localdomain, 2015-11-07 11:53:40 +0900
  LV Status              available
  # open                 1
  LV Size                1504.00 MiB
  Current LE             376
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:1
   
  --- Logical volume ---
  LV Path                /dev/VolGroup/lv_pgsql
  LV Name                lv_pgsql
  VG Name                VolGroup
  LV UUID                WrnmIg-daJ1-Xlnr-AJ0e-ciCZ-QeXT-GxaZSq
  LV Write Access        read/write
  LV Creation host, time CentOS6, 2015-11-07 13:54:04 +0900
  LV Status              available
  # open                 0
  LV Size                8192.00 MiB
  Current LE             2048
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:2
   
# 

暗号化ファイルシステムの作成

前回とほぼ同じだが再掲。

# cryptsetup luksFormat -h sha256 /dev/mapper/VolGroup-lv_pgsql

WARNING!
========
This will overwrite data on /dev/mapper/VolGroup-lv_pgsql irrevocably.

Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase: 
Verify passphrase: 
# cryptsetup luksOpen /dev/mapper/VolGroup-lv_pgsql lv_pgsql_crypt
Enter passphrase for /dev/mapper/VolGroup-lv_pgsql: 
# mkfs -t ext4 /dev/mapper/lv_pgsql_crypt
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
524288 inodes, 2096640 blocks
104832 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2147483648
64 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

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

This filesystem will be automatically checked every 30 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
# cryptsetup luksClose lv_pgsql_crypt
# 

試しにマウント

# cryptsetup luksOpen /dev/mapper/VolGroup-lv_pgsql lv_pgsql_crypt
Enter passphrase for /dev/mapper/VolGroup-lv_pgsql: 
# mount /dev/mapper/lv_pgsql_crypt /mnt/
# ls /mnt
lost+found
# umount /mnt
# cryptsetup luksClose lv_pgsql_crypt
# 

自動接続の設定

前回とほぼ同じだが再掲。

# dd if=/dev/random of=/etc/lvm/lvm.seckey bs=1 count=32
32+0 records in
32+0 records out
32 bytes (32 B) copied, 0.0398393 s, 0.8 kB/s
# chmod 400 /etc/lvm/lvm.seckey
# cryptsetup luksAddKey /dev/mapper/VolGroup-lv_pgsql /etc/lvm/lvm.seckey
Enter any passphrase: 
# cryptsetup --key-file /etc/lvm/lvm.seckey luksOpen /dev/mapper/VolGroup-lv_pgsql lv_pgsql_crypt
# cryptsetup luksDump /dev/mapper/VolGroup-lv_pgsql
(中略)
UUID:           6069746a-025f-4158-963b-c729fe047281
(中略)
# vi /etc/crypttab
+ lv_pgsql_crypt UUID=6069746a-025f-4158-963b-c729fe047281 /etc/lvm/lvm.seckey luks
# vi /etc/fstab
+ /dev/mapper/lv_pgsql_crypt /var/lib/pgsql ext4 defaults 1 1
# 

PostgreSQL用ディレクトリの移行

# mv /var/lib/pgsql /var/lib/pgsql_original.yyyy-mm-dd
# mkdir /var/lib/pgsql
# mount /var/lib/pgsql
# chown postgres:postgres /var/lib/pgsql
# chmod go-rwx /var/lib/pgsql
# ( cd /var/lib/pgsql_original.yyyy-mm-dd ; tar czf - . ) | ( cd /var/lib/pgsql ; tar xzf - )

PostgreSQLサーバーの起動

# service postgresql start
# chkconfig postgresql on

参考

文字とASCII値の変換

いつも関数名まで忘れて苦労するので、メモついでにおもちゃを作ってみた(謎)。

関係する関数は以下

おもちゃコードとして、文字列の独自エンコード/デコードする関数を作ってみた。

  • エンコード
    • 各文字のASCII値のビットを反転させた文字列を出力として返す
  • デコード

ASCII値での単なるrot1関数。可視化するためにbin2hexしているが。

<?php

function rot1_encode($str) {
    $res = '';

    $len = strlen($str);
    for ($i = 0; $i < $len ; ++$i) {
        $res .= chr(ord($str[$i])^0xff);
    }

    return bin2hex($res);
}
if (!function_exists('hex2bin')) {
function hex2bin($str) {
    $res = '';

    $len = strlen($str);
    for ($i = 0; $i < $len ; $i += 2) {
        $res .= pack("c", intval(substr($str, $i, 2), 16));
    }

    return $res;
}
}
function rot1_decode($str) {
    $str = hex2bin($str);

    $res = '';

    $len = strlen($str);
    for ($i = 0; $i < $len ; ++$i) {
        $res .= chr(ord($str[$i])^0xff);
    }

    return $res;
}

$strs = array(
    'こんにちは、ord()とchr()を使ったお遊びです。',
    'お遊び',
);

foreach ($strs as $str) {
    var_dump(rot1_encode($str));
    var_dump(rot1_decode(rot1_encode($str)));
}

これを実行すると以下のようになる。

$ php test.php
string(122) "1c7e6c1c7d6c1c7e541c7e5e1c7e501c7f7e908d9bd7d61c7e579c978dd7d61c7d6d1b42401c7e5c1c7e601c7e75167e751c7e4c1c7e581c7e661c7f7d"
string(61) "こんにちは、ord()とchr()を使ったお遊びです。"
string(18) "1c7e75167e751c7e4c"
string(9) "お遊び"
$ 

PostgreSQLサーバー用ディレクトリを暗号化ファイルシステムに置き換えてみる

前置き

とりあえず、以下の環境を前提としている。

で、暗号化されていない「/var/lib/pgsql」以下を、別HDDを追加して暗号化ファイルシステムとしてフォーマットしたものに置き換えてみようという話。(※別HDDでなくても、同じHDDに空き領域があり、別パーティションを作成できるケースも同様に対応可能)

作業前のHDDは以下のように「/dev/sda」の1本だけ存在する。

# fdisk -l

Disk /dev/sda: 8589 MB, 8589934592 bytes
255 heads, 63 sectors/track, 1044 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0006435d

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          64      512000   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2              64        1045     7875584   8e  Linux LVM


Disk /dev/mapper/VolGroup-lv_root: 5947 MB, 5947523072 bytes
255 heads, 63 sectors/track, 723 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000


Disk /dev/mapper/VolGroup-lv_swap: 2113 MB, 2113929216 bytes
255 heads, 63 sectors/track, 257 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

# 

下準備

とりあえず、PostgreSQLサーバーを一旦止めないといけないし、HDDを追加しないといけないのでOSも止めることになる(この辺は環境によるだろうが‥)。

# chkconfig postgresql off
# sync;sync;sync;shutdown -h now

OSをシャットダウンしたら、HDDを一つ追加して、OSを再起動する。

追加されたHDDは「/dev/sdb」。

# fdisk -l

Disk /dev/sda: 8589 MB, 8589934592 bytes
255 heads, 63 sectors/track, 1044 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0006435d

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          64      512000   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2              64        1045     7875584   8e  Linux LVM

Disk /dev/sdb: 8589 MB, 8589934592 bytes
255 heads, 63 sectors/track, 1044 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000


Disk /dev/mapper/VolGroup-lv_root: 5947 MB, 5947523072 bytes
255 heads, 63 sectors/track, 723 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000


Disk /dev/mapper/VolGroup-lv_swap: 2113 MB, 2113929216 bytes
255 heads, 63 sectors/track, 257 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

# 

暗号化ファイルシステムの作成と自動接続

作成

# cryptsetup luksFormat -h sha256 /dev/sdb

WARNING!
========
This will overwrite data on /dev/sdb irrevocably.

Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase: 
Verify passphrase: 
# cryptsetup luksOpen /dev/sdb sdb_crypt
Enter passphrase for /dev/sdb:
# mkfs -t ext4 /dev/mapper/sdb_crypt
# cryptsetup luksClose sdb_crypt

自動接続の設定

もちろん、これをやると、セキュリティリスクと手間とのトレードオフとなる。

  • 自動接続すると、パスフレーズを知らなくても、HDDを全部盗んでOSを起動すればデータが見えてしまう。
  • 自動接続しないと、luksOpenから、パスフレーズの入力、マウント、PostgreSQLサーバーの起動まで、全部手動でやることになる。
# dd if=/dev/random of=/etc/lvm/lvm.seckey bs=1 count=32
32+0 records in
32+0 records out
32 bytes (32 B) copied, 58.6724 s, 0.0 kB/s
# chmod 400 /etc/lvm/lvm.seckey
# cryptsetup luksAddKey /dev/sdb /etc/lvm/lvm.seckey
Enter any passphrase: 
# cryptsetup --key-file /etc/lvm/lvm.seckey luksOpen /dev/sdb sdb_crypt
# cryptsetup luksDump /dev/sdb
(中略)
UUID:           xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb
(中略)
# vi /etc/crypttab
+ sdb_crypt UUID=xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb /etc/lvm/lvm.seckey luks
# vi /etc/fstab
+ /dev/mapper/sdb_crypt /var/lib/pgsql ext4 errors=remount-ro 0 1
# 

PostgreSQL用ディレクトリの移行

# mv /var/lib/pgsql /var/lib/pgsql_original.yyyy-mm-dd
# mkdir /var/lib/pgsql
# chown postgres:postgres /var/lib/pgsql
# chmod go-rwx /var/lib/pgsql
# mount /var/lib/pgsql
# ( cd /var/lib/pgsql_original.yyyy-mm-dd ; tar czf - . ) | ( cd /var/lib/pgsql ; tar xzf - )

PostgreSQLサーバーの起動

# service postgresql start
# chkconfig postgresql on

psqlコマンドなどでPostgreSQLサーバーに接続し、データの取出しなどができることを確認する。

(余談1)念のため確認

OSを再起動したときに、自動接続されるかどうかを確認しておく。

# umount /var/lib/pgsql
# cryptsetup luksClose sdb_crypt
# sync;sync;sync;shutdown -r now
(再起動後‥)
# ls /var/lib/pgsql
backups  data  lost+found  pgstartup.log
# 

(余談2)暗号化ファイルシステムの取り外し

使用しているプロセスの停止

もちろん、今回の場合は、まずこのファイルシステムを使用しているPostgreSQLサーバーを停止する必要がある

# service postgresql stop
# chkconfig postgresql off

取り外し

自動接続の設定も併せて解除しておく必要がある。(あとでまた接続する場合は、各設定ファイルの内容は削除せずにコメントアウトしておく)

# umount /var/lib/pgsql
# cryptsetup luksClose sdb_crypt
# vi /etc/fstab
- /dev/mapper/sdb_crypt /var/lib/pgsql ext4 errors=remount-ro 0 1
# vi /etc/crypttab
- sdb_crypt UUID=xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb /etc/lvm/lvm.seckey luks

参考