2012年12月10日月曜日

3TB ディスクから CentOS 6.3 をブート

バックアップ用に 3TB のディスク (WD30EZRX/K) を購入しました。

まずは、Windows 7 で採取した CrystalDiskInfo の結果です。
ZALMAN ZM-HE350 U3E というケースに格納し、ThinkPad T510 に eSATA 接続しています。WD30EZRX/K は NCQ に対応していますが、このケース経由だと NCQ が使えません (他のドライブでも確認・経験済み) 。その点はちょっと残念。

さて、CentOS 6.3 ならば、UEFI 非対応のマシン (ThinkPad T510 も勿論非対応) でも、3TB ディスクからブート可能という情報がありましたので、実際にやってみました。他のドライブが繋がっているとヤヤコシイので、一旦 3TB ディスク以外は外してインストールしました。その後に、内臓 SSD などを戻しています。また、ThinkPad の BIOS でブートオーダーを変更して起動しました。
# parted -s /dev/sdc print
Model: ATA WDC WD30EZRX-00D (scsi)
Disk /dev/sdc: 3001GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system     Name                  Flags
 1      1049kB  211MB   210MB   ext4                                  boot
 2      211MB   21.2GB  21.0GB  ext4
 3      21.2GB  23.3GB  2097MB  linux-swap(v1)
 4      23.3GB  3001GB  2977GB  ntfs            Basic data partition

# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sdc2             20158332   4737476  14396856  25% /
tmpfs                  3986236       260   3985976   1% /dev/shm
/dev/sdc1               198337     51449    136648  28% /boot

# ls -l /dev/root
lrwxrwxrwx. 1 root root 4 Dec  9 22:10 /dev/root -> sdc2

# uname -a
Linux xxxx 2.6.32-279.14.1.el6.i686 #1 SMP Tue Nov 6 21:05:14 UTC 2012 i686 i686 i386 GNU/Linux
このように、うまく動作しました。
Fedora 16 以降なら、grub2 を使って GPT ラベルを付与したディスクからブート可能とは知ってましたが、grub1 でも対応パッチがあるようで、CentOS 6.3 の grub に取り込まれていたようです。

2012年12月1日土曜日

中古 SSD の SecureErase

某ショップにて中古の Intel SSD 320 600G を入手しました。GB単価が安かった(約50円)ため、思わず衝動買い。

中古ということで、総書き込み量はそれなりかと思っていたのですが、初回接続時にWindows7からCrystalDiskInfoで見てみると、次のような具合でした。
注: DeLOCK 42492 という eSATA/USB3.0 両対応のケースを使ったのですが、そのケース経由だと NCQ が使えないようです。下の CentOS6 のデータは、ケースから取り出して eSATA-SATA ケーブルで接続した場合です。SATA 電源は USB から供給。DeLOCK ケースはちょっと残念。

次は、CentOS6 で起動後の smartctl および hdparm のデータです。
# uname -a
Linux xxx 2.6.32-279.14.1.el6.x86_64 #1 SMP Tue Nov 6 23:43:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

# dmesg | egrep 'ata5|sdc'
ata5: SATA max UDMA/133 irq_stat 0x00400040, connection status changed irq 29
ata5: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
ata5.00: ACPI cmd ef/02:00:00:00:00:a0 (SET FEATURES) succeeded
ata5.00: ACPI cmd f5/00:00:00:00:00:a0 (SECURITY FREEZE LOCK) filtered out
ata5.00: ATA-8: INTEL SSDSA2CW600G3, 4PC10362, max UDMA/133
ata5.00: 1172123568 sectors, multi 16: LBA48 NCQ (depth 31/32)
ata5.00: ACPI cmd ef/02:00:00:00:00:a0 (SET FEATURES) succeeded
ata5.00: ACPI cmd f5/00:00:00:00:00:a0 (SECURITY FREEZE LOCK) filtered out
ata5.00: configured for UDMA/133
ata5.00: configured for UDMA/133
ata5: EH complete
sd 5:0:0:0: [sdc] 1172123568 512-byte logical blocks: (600 GB/558 GiB)
sd 5:0:0:0: [sdc] Write Protect is off
sd 5:0:0:0: [sdc] Mode Sense: 00 3a 00 00
sd 5:0:0:0: [sdc] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
 sdc:
sd 5:0:0:0: [sdc] Attached SCSI disk

# smartctl -a /dev/sdc
smartctl 5.42 2011-10-20 r3458 [x86_64-linux-2.6.32-279.14.1.el6.x86_64] (local build)
Copyright (C) 2002-11 by Bruce Allen, http://smartmontools.sourceforge.net

=== START OF INFORMATION SECTION ===
Model Family:     Intel 320 Series SSDs
Device Model:     INTEL SSDSA2CW600G3
Serial Number:    CVPR??????????????
LU WWN Device Id: 5 001517 bb28?????
Firmware Version: 4PC10362
User Capacity:    600,127,266,816 bytes [600 GB]
Sector Size:      512 bytes logical/physical
Device is:        In smartctl database [for details use: -P show]
ATA Version is:   8
ATA Standard is:  ATA-8-ACS revision 4
Local Time is:    Fri Nov 30 22:03:19 2012 JST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled

Error SMART Thresholds Read failed: scsi error aborted command
Smartctl: SMART Read Thresholds failed.

=== START OF READ SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED

General SMART Values:
Offline data collection status:  (0x00) Offline data collection activity
     was never started.
     Auto Offline Data Collection: Disabled.
Self-test execution status:      (  17) The self-test routine was aborted by
     the host.
Total time to complete Offline 
data collection:   (    1) seconds.
Offline data collection
capabilities:     (0x75) SMART execute Offline immediate.
     No Auto Offline data collection support.
     Abort Offline collection upon new
     command.
     No Offline surface scan supported.
     Self-test supported.
     Conveyance Self-test supported.
     Selective Self-test supported.
SMART capabilities:            (0x0003) Saves SMART data before entering
     power-saving mode.
     Supports SMART auto save timer.
Error logging capability:        (0x01) Error logging supported.
     General Purpose Logging supported.
Short self-test routine 
recommended polling time:   (   1) minutes.
Extended self-test routine
recommended polling time:   (   1) minutes.
Conveyance self-test routine
recommended polling time:   (   1) minutes.
SCT capabilities:         (0x003d) SCT Status supported.
     SCT Error Recovery Control supported.
     SCT Feature Control supported.
     SCT Data Table supported.

SMART Attributes Data Structure revision number: 5
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
  3 Spin_Up_Time            0x0020   100   100   ---    Old_age   Offline      -       0
  4 Start_Stop_Count        0x0030   100   100   ---    Old_age   Offline      -       0
  5 Reallocated_Sector_Ct   0x0032   100   100   ---    Old_age   Always       -       0
  9 Power_On_Hours          0x0032   100   100   ---    Old_age   Always       -       1
 12 Power_Cycle_Count       0x0032   100   100   ---    Old_age   Always       -       5
170 Reserve_Block_Count     0x0033   100   100   ---    Pre-fail  Always       -       0
171 Program_Fail_Count      0x0032   100   100   ---    Old_age   Always       -       0
172 Erase_Fail_Count        0x0032   100   100   ---    Old_age   Always       -       0
183 Runtime_Bad_Block       0x0030   100   100   ---    Old_age   Offline      -       0
184 End-to-End_Error        0x0032   100   100   ---    Old_age   Always       -       0
187 Reported_Uncorrect      0x0032   100   100   ---    Old_age   Always       -       0
192 Unsafe_Shutdown_Count   0x0032   100   100   ---    Old_age   Always       -       1
199 UDMA_CRC_Error_Count    0x0030   100   100   ---    Old_age   Offline      -       0
225 Host_Writes_32MiB       0x0032   100   100   ---    Old_age   Always       -       17887
226 Workld_Media_Wear_Indic 0x0032   100   100   ---    Old_age   Always       -       20
227 Workld_Host_Reads_Perc  0x0032   100   100   ---    Old_age   Always       -       94
228 Workload_Minutes        0x0032   100   100   ---    Old_age   Always       -       116
232 Available_Reservd_Space 0x0033   100   100   ---    Pre-fail  Always       -       0
233 Media_Wearout_Indicator 0x0032   100   100   ---    Old_age   Always       -       0
241 Host_Writes_32MiB       0x0032   100   100   ---    Old_age   Always       -       17887
242 Host_Reads_32MiB        0x0032   100   100   ---    Old_age   Always       -       3

SMART Error Log Version: 1
No Errors Logged

SMART Self-test log structure revision number 1
No self-tests have been logged.  [To run self-tests, use: smartctl -t]


Note: selective self-test log revision number (0) not 1 implies that no selective self-test has ever been run
SMART Selective self-test log data structure revision number 0
Note: revision number not 1 implies that no selective self-test has ever been run
 SPAN  MIN_LBA  MAX_LBA  CURRENT_TEST_STATUS
    1        0        0  Not_testing
    2        0        0  Not_testing
    3        0        0  Not_testing
    4        0        0  Not_testing
    5        0        0  Not_testing
Selective self-test flags (0x0):
  After scanning selected spans, do NOT read-scan remainder of disk.
If Selective self-test is pending on power-up, resume after 0 minute delay.

# hdparm -I /dev/sdc

/dev/sdc:

ATA device, with non-removable media
 Model Number:       INTEL SSDSA2CW600G3                     
 Serial Number:      CVPR??????????????  
 Firmware Revision:  4PC10362
 Transport:          Serial, ATA8-AST, SATA 1.0a, SATA II Extensions, SATA Rev 2.5, SATA Rev 2.6
Standards:
 Used: unknown (minor revision code 0x0029) 
 Supported: 8 7 6 5 
 Likely used: 8
Configuration:
 Logical  max current
 cylinders 16383 16383
 heads  16 16
 sectors/track 63 63
 --
 CHS current addressable sectors:   16514064
 LBA    user addressable sectors:  268435455
 LBA48  user addressable sectors: 1172123568
 Logical  Sector size:                   512 bytes
 Physical Sector size:                   512 bytes
 device size with M = 1024*1024:      572325 MBytes
 device size with M = 1000*1000:      600127 MBytes (600 GB)
 cache/buffer size  = unknown
 Nominal Media Rotation Rate: Solid State Device
Capabilities:
 LBA, IORDY(can be disabled)
 Queue depth: 32
 Standby timer values: spec'd by Standard, no device specific minimum
 R/W multiple sector transfer: Max = 16 Current = 16
 DMA: mdma0 mdma1 mdma2 udma0 udma1 udma2 udma3 udma4 udma5 *udma6 
      Cycle time: min=120ns recommended=120ns
 PIO: pio0 pio1 pio2 pio3 pio4 
      Cycle time: no flow control=120ns  IORDY flow control=120ns
Commands/features:
 Enabled Supported:
    * SMART feature set
      Security Mode feature set
    * Power Management feature set
    * Write cache
    * Look-ahead
    * Host Protected Area feature set
    * WRITE_BUFFER command
    * READ_BUFFER command
    * NOP cmd
    * DOWNLOAD_MICROCODE
      SET_MAX security extension
    * 48-bit Address feature set
    * Device Configuration Overlay feature set
    * Mandatory FLUSH_CACHE
    * FLUSH_CACHE_EXT
    * SMART error logging
    * SMART self-test
    * General Purpose Logging feature set
    * WRITE_{DMA|MULTIPLE}_FUA_EXT
    * 64-bit World wide name
    * IDLE_IMMEDIATE with UNLOAD
    * WRITE_UNCORRECTABLE_EXT command
    * {READ,WRITE}_DMA_EXT_GPL commands
    * Segmented DOWNLOAD_MICROCODE
    * Gen1 signaling speed (1.5Gb/s)
    * Gen2 signaling speed (3.0Gb/s)
    * Native Command Queueing (NCQ)
    * Phy event counters
      Device-initiated interface power management
    * Software settings preservation
    * SMART Command Transport (SCT) feature set
    * SCT LBA Segment Access (AC2)
    * SCT Error Recovery Control (AC3)
    * SCT Features Control (AC4)
    * SCT Data Tables (AC5)
    * Data Set Management TRIM supported
    * reserved 69[5]
    * Deterministic read after TRIM
Security: 
 Master password revision code = 65534
  supported
 not enabled
 not locked
 not frozen
 not expired: security count
  supported: enhanced erase
 2min for SECURITY ERASE UNIT. 2min for ENHANCED SECURITY ERASE UNIT.
Logical Unit WWN Device Identifier: 5001517bb28?????
 NAA  : 5
 IEEE OUI : 001517
 Unique ID : bb28?????
Integrity word not set (found 0x0000, expected 0x4fa5)


おそらくは、ゼロ書き込みによるデータ消去後であろう?とは思いましたが、せっかくの機会 (中古SSDは初挑戦) なので、オールゼロになっているのか確認してみたところ、SSD 上に残っていた文字列は、次の3行のみでした。
Delete End
2012/09/21/11:46:19
某ショップの会社名
さて、それなら SecureErase を実施したほうが、性能上良いはずなので、実際やりました。
# cat /sys/block/sdc/device/model 
INTEL SSDSA2CW60

# hdparm --user-master u --security-set-pass password /dev/sdc
security_password="password"

/dev/sdc:
 Issuing SECURITY_SET_PASS command, password="password", user=user, mode=high
# hdparm --user-master u --security-erase-enhanced password /dev/sdc
security_password="password"

/dev/sdc:
 Issuing SECURITY_ERASE command, password="password", user=user
SecureErase は1分ぐらい (ちょっとよそ見している間) で終わってしまいました。

ちなみに、Win7 上の Intel SSD Toolbox も試してみたのですが、わたしの環境では、どうしても frozen 状態が解除できず、実行できませんでした。

ThinkPad T510 に eSATA 接続の環境ですが、Toolbox の指示に従って、eSATA の先の SSD 電源を落としたり、刺しなおしたりしてみましたが、ダメでした。それなのに、デュアルブートにしてある CentOS6 では、frozen にはならず、簡単に SecureErase できました。

■関連記事
SecureErase を実施してみた
SecureErase のために frozen 状態を解除する方法

2012年11月24日土曜日

ZVOL を KVM ゲストに割り当てる

CentOS 6 + ZFS on Linux 0.6.0-rc12 において、ZVOL を KVM ゲストに割り当てる実験をしてみました。

目当ては、スパースボリューム (sparse volume) です。次は、man zfs(8) からの抜粋です。
           Though not recommended, a "sparse volume" (also known as "thin pro-
           visioning") can be created by specifying the -s option to  the  zfs
           create  -V command, or by changing the reservation after the volume
           has been created. A "sparse volume" is a volume where the  reserva-
           tion is less then the volume size. Consequently, writes to a sparse
           volume can fail with ENOSPC when the pool is low on  space.  For  a
           sparse volume, changes to volsize are not reflected in the reserva-
           tion.
スパースボリュームを作成するには、zfs create -V の際、同時に -s オプションを指定します。
# zfs create -s -V 30G tank1/zvol
# ls -l /dev/zvol/tank1/zvol 
lrwxrwxrwx 1 root root 9 Nov 24 10:46 /dev/zvol/tank1/zvol -> ../../zd0
# ls -l /dev/zd0 
brw-rw---- 1 qemu qemu 230, 0 Nov 24 11:03 /dev/zd0
# grep zd0 /proc/partitions 
 230        0   31457280 zd0
この zvol を KVM ゲストに割り当てて、CentOS 6.3 をインストールした直後のディスク消費量は次の通りです。なお、インストールオプションには、Software Development Workstation を選択しています。
# zfs list
NAME         USED  AVAIL  REFER  MOUNTPOINT
tank1       5.25G  53.3G   156K  /tank1
tank1/zvol  5.24G  53.3G  5.24G  -
# zfs get all tank1/zvol
NAME        PROPERTY              VALUE                  SOURCE
tank1/zvol  type                  volume                 -
tank1/zvol  creation              Sat Nov 24 10:01 2012  -
tank1/zvol  used                  5.24G                  -
tank1/zvol  available             53.3G                  -
tank1/zvol  referenced            5.24G                  -
tank1/zvol  compressratio         1.00x                  -
tank1/zvol  reservation           none                   default
tank1/zvol  volsize               30G                    local
tank1/zvol  volblocksize          8K                     -
tank1/zvol  checksum              on                     default
tank1/zvol  compression           off                    default
tank1/zvol  readonly              off                    default
tank1/zvol  copies                1                      default
tank1/zvol  refreservation        none                   default
tank1/zvol  primarycache          all                    default
tank1/zvol  secondarycache        all                    default
tank1/zvol  usedbysnapshots       0                      -
tank1/zvol  usedbydataset         5.24G                  -
tank1/zvol  usedbychildren        0                      -
tank1/zvol  usedbyrefreservation  0                      -
tank1/zvol  logbias               latency                default
tank1/zvol  dedup                 off                    default
tank1/zvol  mlslabel              none                   default
tank1/zvol  sync                  standard               default
tank1/zvol  refcompressratio      1.00x                  -
tank1/zvol  written               5.24G                  -
ZFS プールの空きや KVM ゲストのディスク使用量を気にかける必要はありますが、こんな具合にディスク消費量を節約できました。もっとも、節約だけであれば、スパースファイルでも同じことであり、実際、ext4 上にスパースファイルとして同様にインストールすると、ほぼ同じ消費量になります。
# df -Th /
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/sda6     ext4     20G   16G  2.5G  87% /
# ls -lsh /var/lib/libvirt/images/cent6vm64.img 
5.2G -rw-------. 1 root root 12G Aug 19 15:47 /var/lib/libvirt/images/cent6vm64.img
ZVOL を使う利点は、ZFS の豊富な機能 (compression, dedup, secondarycache, スナップショット) をゲスト毎に使い分けられる点ではないかと思います。また、運用上、スパースファイル方式よりも分かり易い (zfs list で各ゲストへの分配状況を俯瞰できる) かなとも思います。

ちなみに、LVM2 にも同様の機能を組み込もうとして、開発途上の模様です。CentOS 6 の man lvcreate(8) より抜粋です。
       -T, --thin, --thinpool ThinPoolLogicalVolume{Name|Path}
              Creates  thin  pool  or thin logical volume or both.  Specifying
              the optional argument --size will cause the creation of the thin
              pool  logical volume.  Specifying the optional argument --virtu-
              alsize will cause the creation of the thin logical  volume  from
              given  thin  pool  volume.  Specifying both arguments will cause
              the creation of both thin pool and thin volume using this  pool.
              Requires  device mapper kernel driver for thin provisioning from
              kernel 3.2 or newer.  Note: Thin types are currently  considered
              Tech  Preview.   For  more information on Tech Preview features,
              visit: https://access.redhat.com/support/offerings/techpreview/.
LVM2 (device-mapper) を使うと、運用が煩雑になる印象があるのですが、おそらくは、ZVOL より性能は上 (レイヤ増によるオーバーヘッドが少ない) だろうと思いますし、将来の選択肢として、頭の片隅に。

2012-11-25追記
さらに、compression による効果を試しました。zvol の compression プロパティを on にしてから、ゲストの CentOS 6.3 をインストールした結果です。
# zfs set compression=on tank1/zvol
... このあとで、ゲストをインストール ...
# zfs list
NAME         USED  AVAIL  REFER  MOUNTPOINT
tank1       3.60G  55.0G   156K  /tank1
tank1/zvol  3.60G  55.0G  3.60G  -
# zfs get all tank1/zvol
NAME        PROPERTY              VALUE                  SOURCE
tank1/zvol  type                  volume                 -
tank1/zvol  creation              Sat Nov 24 14:38 2012  -
tank1/zvol  used                  3.60G                  -
tank1/zvol  available             55.0G                  -
tank1/zvol  referenced            3.60G                  -
tank1/zvol  compressratio         1.76x                  -
tank1/zvol  reservation           none                   default
tank1/zvol  volsize               30G                    local
tank1/zvol  volblocksize          8K                     -
tank1/zvol  checksum              on                     default
tank1/zvol  compression           on                     local
tank1/zvol  readonly              off                    default
tank1/zvol  copies                1                      default
tank1/zvol  refreservation        none                   default
tank1/zvol  primarycache          all                    default
tank1/zvol  secondarycache        all                    default
tank1/zvol  usedbysnapshots       0                      -
tank1/zvol  usedbydataset         3.60G                  -
tank1/zvol  usedbychildren        0                      -
tank1/zvol  usedbyrefreservation  0                      -
tank1/zvol  logbias               latency                default
tank1/zvol  dedup                 off                    default
tank1/zvol  mlslabel              none                   default
tank1/zvol  sync                  standard               default
tank1/zvol  refcompressratio      1.76x                  -
tank1/zvol  written               3.60G                  -
圧縮により 1.64G ほどディスク消費を節約できています。
compression プロパティは、いつでも変更可能であり、ゲストのインストール時やアップデート時のみ、compression=on にして、ゲストの通常運用時には、compressoin=off にしておくといった使い方も可能です。

2012-12-06追記
重複排除 (dedup) についても、試してみました。zvol01 および zvol02 を用意して、dedup=verify を設定してから、各 zvol をそれぞれ KVM ゲストに割り当てて、CentOS 6.3 をインストールした場合の結果です。
# grep tank1/zvol01 /etc/libvirt/qemu/cent6zvol01.xml 
      <source dev='/dev/zvol/tank1/zvol01'/>
# grep tank1/zvol02 /etc/libvirt/qemu/cent6zvol02.xml 
      <source dev='/dev/zvol/tank1/zvol02'/>
# zfs get compression
NAME          PROPERTY     VALUE     SOURCE
tank1         compression  off       default
tank1/zvol01  compression  off       default
tank1/zvol02  compression  off       default
# zfs get dedup
NAME          PROPERTY  VALUE          SOURCE
tank1         dedup     off            default
tank1/zvol01  dedup     verify         local
tank1/zvol02  dedup     verify         local
# zfs list
NAME           USED  AVAIL  REFER  MOUNTPOINT
tank1         11.5G  51.4G   156K  /tank1
tank1/zvol01  5.60G  51.4G  5.60G  -
tank1/zvol02  5.52G  51.4G  5.52G  -
# zpool list
NAME    SIZE  ALLOC   FREE    CAP  DEDUP  HEALTH  ALTROOT
tank1  59.5G  7.10G  52.4G    11%  1.68x  ONLINE  -
重複排除により 4G ほど削減できています。重複排除は zpool の括りで行われるようで、効果確認は zpool コマンドで行います。
dedup プロパティについても、いつでも変更可能なので、インストール時だけ dedup=verify にして、通常運用時は dedup=off という使い方も可能です。ここでは試しませんでしたが、compression=on との同時利用も可能です。

2012年11月23日金曜日

ZVOL の性能

CentOS 6 + ZFS on Linux 0.6.0-rc12 において、ZVOL (ZFS プールの一部をブロックデバイスとして export する機能= LVM 的な機能) を使用したいと思い、利用方法調査と性能測定をやってみました。測定環境は、以前と同様に ThinkPad T510 です。

まず、設定方法は次の通りです。
# zfs list
NAME    USED  AVAIL  REFER  MOUNTPOINT
tank1  2.07M  58.6G   156K  /tank1
# zfs create -V 30G tank1/zvol
# zfs get all tank1/zvol
NAME        PROPERTY              VALUE                  SOURCE
tank1/zvol  type                  volume                 -
tank1/zvol  creation              Fri Nov 23 12:14 2012  -
tank1/zvol  used                  30.9G                  -
tank1/zvol  available             58.6G                  -
tank1/zvol  referenced            72K                    -
tank1/zvol  compressratio         1.00x                  -
tank1/zvol  reservation           none                   default
tank1/zvol  volsize               30G                    local
tank1/zvol  volblocksize          8K                     -
tank1/zvol  checksum              on                     default
tank1/zvol  compression           off                    default
tank1/zvol  readonly              off                    default
tank1/zvol  copies                1                      default
tank1/zvol  refreservation        30.9G                  local
tank1/zvol  primarycache          all                    default
tank1/zvol  secondarycache        all                    default
tank1/zvol  usedbysnapshots       0                      -
tank1/zvol  usedbydataset         72K                    -
tank1/zvol  usedbychildren        0                      -
tank1/zvol  usedbyrefreservation  30.9G                  -
tank1/zvol  logbias               latency                default
tank1/zvol  dedup                 off                    default
tank1/zvol  mlslabel              none                   default
tank1/zvol  sync                  standard               default
tank1/zvol  refcompressratio      1.00x                  -
tank1/zvol  written               72K                    -
# ls -l /dev/zvol/tank1/zvol 
lrwxrwxrwx 1 root root 9 Nov 23 12:15 /dev/zvol/tank1/zvol -> ../../zd0
# ls -l /dev/zd0
brw-rw---- 1 root disk 230, 0 Nov 23 12:15 /dev/zd0
このように、/dev/zd0 が生成されます。

今回は、作成されたブロックデバイスを ext4 にして、性能測定を行いました。
# mkfs -t ext4 /dev/zvol/tank1/zvol 
mke2fs 1.41.12 (17-May-2010)
Discarding device blocks: done                            
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=2 blocks, Stripe width=2 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

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

This filesystem will be automatically checked every 24 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
# mount /dev/zvol/tank1/zvol /mnt_tank1_zvol/
# df /mnt_tank1_zvol/
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/zd0              30963708    176064  29214780   1% /mnt_tank1_zvol
いちおう、io scheduler を3種類 (cfq, noop, deadline) 試しましたが、deadline が最も良好でしたので、その結果のみ掲載します。
# echo deadline > /sys/block/sdd/queue/scheduler 
# cat /sys/block/sdd/queue/scheduler 
noop anticipatory [deadline] cfq 
# bonnie++ -u root -d /mnt_tank1_zvol/
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx         15464M   940  96 40404   4 19568   3  2630  60 68826   5  4712  63
Latency             14844us    4526ms    4292ms     400ms     417ms    6134us
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16 20098  18 +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++
Latency               108us     927us     473us     169us      25us      54us
1.96,1.96,xxxx,1,1353630877,15464M,,940,96,40404,4,19568,3,2630,60,68826,5,4712,63,16,,,,,20098,18,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++,14844us,4526ms,4292ms,400ms,417ms,6134us,108us,927us,473us,169us,25us,54us
ベースの ZFS プール(tank1)の性能に対して、シーケンシャル WRITE で、約25% ほどの性能低下です。

2012年11月11日日曜日

ZFS と io scheduler

CentOS 6 で ZFS on Linux を利用する場合に、io scheduler が性能にどのように影響するのか、少しだけ実験してみました。ZFS は自身で I/O スケジューリングを行うはずなので、Linux の io scheduler は邪魔ではないかと思ったもので。。

順番に cfq(デフォルト), noop, deadline の場合の bonnie++ のデータです。
# cat /sys/block/sdd/queue/scheduler 
noop anticipatory deadline [cfq]
# bonnie++ -u root -d /tank1
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx         15464M   144  99 60984  12 35507   9   345  90 95825  10 800.4  26
Latency               103ms     212ms    1351ms     751ms     547ms     556ms
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  9603  93 +++++ +++ 25418  97  7615  94 +++++ +++ 20001  97
Latency             37143us     734us     584us   86610us     101us    1078us
1.96,1.96,xxxx,1,1352637907,15464M,,144,99,60984,12,35507,9,345,90,95825,10,800.4,26,16,,,,,9603,93,+++++,+++,25418,97,7615,94,+++++,+++,20001,97,103ms,212ms,1351ms,751ms,547ms,556ms,37143us,734us,584us,86610us,101us,1078us
# echo noop > /sys/block/sdd/queue/scheduler 
# cat /sys/block/sdd/queue/scheduler 
[noop] anticipatory deadline cfq
# bonnie++ -u root -d /tank1
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx         15464M   149  99 53196  11 34046   9   360  92 105384  12 853.4  26
Latency             95853us     418ms    1455ms     596ms     374ms     561ms
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  9562  92 +++++ +++ 25809  96 10773  96 +++++ +++ 10272  98
Latency             35613us     299us     819us   49027us      97us     387us
1.96,1.96,xxxx,1,1352612707,15464M,,149,99,53196,11,34046,9,360,92,105384,12,853.4,26,16,,,,,9562,92,+++++,+++,25809,96,10773,96,+++++,+++,10272,98,95853us,418ms,1455ms,596ms,374ms,561ms,35613us,299us,819us,49027us,97us,387us
# echo deadline > /sys/block/sdd/queue/scheduler 
# cat /sys/block/sdd/queue/scheduler 
noop anticipatory [deadline] cfq 
# bonnie++ -u root -d /tank1
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx         15464M   148  99 54315  11 34406   9   355  91 105448  12 831.9  24
Latency               107ms     289ms    1232ms     689ms     318ms     621ms
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  9649  93 +++++ +++ 23925  85 16976  95 +++++ +++ 27138  97
Latency             35409us     735us     339us   54244us      25us     128us
1.96,1.96,xxxx,1,1352612140,15464M,,148,99,54315,11,34406,9,355,91,105448,12,831.9,24,16,,,,,9649,93,+++++,+++,23925,85,16976,95,+++++,+++,27138,97,107ms,289ms,1232ms,689ms,318ms,621ms,35409us,735us,339us,54244us,25us,128us
あちらを立てればこちらが立たず という感じです。

また、io scheduler をいじっても、FreeBSD 9.0 の ZFS 性能にはかなわないようです。
としても、CentOS 6 で ZFS を普通に使える (わたしの利用レベルでは安定しています) ことは、ありがたいことです。

最後に、この記事の掲載データは、FreeBSD 9.0 のデータを測ったのと同じマシン (ThinkPad T510)、同じボリューム (eSATA 接続の古い SAMSUNG製 SSD) のものです。

2012年10月28日日曜日

CentOS 6 と CentOS 5 で ZFS を共用する

CentOS 5 では、ZFS on Linux が使えない (ビルドできない) ので、zfs-fuse を使うよりないのですが、マルチブート環境で、CentOS 6 と共用したいため、そのための設定方法メモです。

zfs-fuse のほうが zpool および zfs のバージョンが低いので、CentOS 6 に ZFS on Linux を導入した環境で、次のようにバージョンを指定してストレージプールを作成すれば、マルチブートで共用できるようになります。
# zpool create -o ashift=12 -o version=23 -O version=4 tank1 /dev/disk/by-id/scsi-SATA_External_Disk_0AL25744_6000092__0_S-part1
# fdisk -u -l /dev/sdd | grep sdd1
/dev/sdd1            2048   125045423    62521688   83  Linux
ここで、ashift=12 は AFT 対応 HDD (物理セクタサイズが 4K の HDD) の場合に指定すると効果があるパラメータですが、zfs-fuse 付属の zpool コマンドでは指定できません。また、上記のようにパーティションを切っている場合は、先頭セクタ番号が 8 の倍数になるように調整しておく必要があります。

ベンチマーク結果を参考に載せておきます。
まず、CentOS 6 + ZFS on Linux 0.6.0-rc11 の場合です。
# uname -a
Linux xxxx 2.6.32-279.11.1.el6.x86_64 #1 SMP Tue Oct 16 15:57:10 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
# zpool get guid tank1
NAME   PROPERTY  VALUE  SOURCE
tank1  guid      4585092805656841166  local
# zpool status
  pool: tank1
 state: ONLINE
status: The pool is formatted using an older on-disk format.  The pool can
 still be used, but some features are unavailable.
action: Upgrade the pool using 'zpool upgrade'.  Once this is done, the
 pool will no longer be accessible on older software versions.
 scan: none requested
config:

 NAME                                                   STATE     READ WRITE CKSUM
 tank1                                                  ONLINE       0     0     0
   scsi-SATA_External_Disk_0AL25744_6000092__0_S-part1  ONLINE       0     0     0

errors: No known data errors
# bonnie++ -u root -d /tank1
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx         15464M   161  99 60368  12 35164   9   371  94 96024  10 807.8  26
Latency             99022us     443ms    1407ms     332ms     546ms    1067ms
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  9314  91 +++++ +++ 24604  97  9976  95 +++++ +++  9474  98
Latency             48790us     741us     505us   53719us     128us    1094us
1.96,1.96,xxxx,1,1350830603,15464M,,161,99,60368,12,35164,9,371,94,96024,10,807.8,26,16,,,,,9314,91,+++++,+++,24604,97,9976,95,+++++,+++,9474,98,99022us,443ms,1407ms,332ms,546ms,1067ms,48790us,741us,505us,53719us,128us,1094us

次に、CentOS 5 + zfs-fuse-0.6.9_p1-6.20100709git.el5.1 のデータです。
# uname -a
Linux xxxx 2.6.18-308.11.1.el5 #1 SMP Tue Jul 10 08:48:43 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux
# zpool get guid tank1
NAME   PROPERTY  VALUE  SOURCE
tank1  guid      4585092805656841166  default
# zpool status
  pool: tank1
 state: ONLINE
 scrub: none requested
config:

        NAME                                                              STATE     READ WRITE CKSUM
        tank1                                                             ONLINE       0     0     0
          disk/by-id/scsi-SATA_External_Disk_0AL25744_6000092__0_S-part1  ONLINE       0     0     0

errors: No known data errors
# bonnie++ -u root -d /tank1
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx         15424M    24  11 32019   5 24308   6  2379  98 103178   7 496.0   9
Latency               506ms    1381ms    1627ms   10573us     609ms     999ms
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  5648  12 11300  10  5469   7  5038  12 12881  14  7092  12
Latency             28879us    1700us    3875us   54328us    1760us    2040us
1.96,1.96,xxxx,1,1351439162,15424M,,24,11,32019,5,24308,6,2379,98,103178,7,496.0,9,16,,,,,5648,12,11300,10,5469,7,5038,12,12881,14,7092,12,506ms,1381ms,1627ms,10573us,609ms,999ms,28879us,1700us,3875us,54328us,1760us,2040us
使用機材は、ThinkPad T510 Core i7 M620 2.67G + SAMSUNGの2008年製造の古い SSD 64G (eSATA 接続) です。

2012-11-10追記
わたしの利用範囲では、ZFS on Linux の性能は十分であると感じますが、FreeBSD のほうが上だと聞きますので、試しに同じマシン(ThinkPad T510)にマルチブート環境を作って、同じボリュームを import して、bonnie++ で計測してみました。
# uname -a
FreeBSD xxxx 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Tue Jan  3 07:46:30 UTC 2012     root@farrell.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  amd64
# bonnie++ -u root -d /tank1
...
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
xxxx            16G   173  99 53620   9 33313   5   425  90 138720   7 876.5   8
Latency             46882us    4175ms    3542ms     513ms     590ms     782ms
Version  1.96       ------Sequential Create------ --------Random Create--------
xxxx                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16 29261  88 +++++ +++ 32398  97 23195  97 +++++ +++ 12962  98
Latency             13252us     132us     126us   26767us     102us    7513us
1.96,1.96,xxxx,1,1352526856,16G,,173,99,53620,9,33313,5,425,90,138720,7,876.5,8,16,,,,,29261,88,+++++,+++,32398,97,23195,97,+++++,+++,12962,98,46882us,4175ms,3542ms,513ms,590ms,782ms,13252us,132us,126us,26767us,102us,7513us
# zpool get all tank1
NAME   PROPERTY       VALUE       SOURCE
...
tank1  guid           4585092805656841166  local
...
# zpool status
  pool: tank1
 state: ONLINE
status: The pool is formatted using an older on-disk format.  The pool can
 still be used, but some features are unavailable.
action: Upgrade the pool using 'zpool upgrade'.  Once this is done, the
 pool will no longer be accessible on older software versions.
 scan: none requested
config:

 NAME        STATE     READ WRITE CKSUM
 tank1       ONLINE       0     0     0
   ada2s1    ONLINE       0     0     0

errors: No known data errors
確かに各数字を見比べてみると、全般的に FreeBSD 9.0 のほうが、少し性能が上に見えました。
もしも、NAS 専用サーバを作るのであれば、FreeBSD や FreeNAS というものは、良い選択肢なのかもなと思いました。

余談:FreeBSD は、過去何回かインストールした経験があったのですが、拡張パーティションにはインストールできないということを、すっかり忘れており(たぶん、以前同じようにハマったと思いました)、結構な手間・時間がかかってしまいました。たぶん、ほとんど起動しないだろうというのに。。
なお、FreeBSD 10 に向けて、ZFS の性能向上の取り組みがあるらしいので、リリースされたら、また測ってみたいなと思っています。

ZFS の L2ARC に zram を利用する

ZFS の L2ARC に zram を利用し始めたのですが、汎用的な zram の初期化スクリプトというのは無いため、自分のサーバ (CentOS 6.3) 向けに初期化スクリプトを書きました。
一から書くのも骨ですので、どなたかの参考になれば幸いです。
#!/bin/bash
#
# my_zram       This shell script takes care of starting and stopping
#               zram for swap and zfs's cache(L2ARC).
#
# chkconfig: 12345 90 99
# description:  initializing zram for my server
#
# config:

### BEGIN INIT INFO
# Provides: my_zram
# Required-Start: 
# Required-Stop: 
# Default-Stop: 0 1 2 3 6
# Short-Description: initializing zram
# Description:  initializing zram
#
### END INIT INFO

#
# Authors: luna2 <blue3waters at gmail dot com>
#
# This file is subject to the GNU General Public License.
# It comes with NO WARRANTY.
#

LOCKFILE=/var/lock/subsys/my_zram

case "$1" in
  start)
    [ ! -e /tank2/src ] && service zfs start
    modprobe zram num_devices=2
    if [ $? -eq 0 ] ; then
        echo $((2048*1024*1024)) > /sys/block/zram0/disksize
        echo  $((896*1024*1024)) > /sys/block/zram1/disksize
        #
        /sbin/zpool add tank2 cache /dev/zram0
        #
        mkswap /dev/zram1
        swapon -p 1 /dev/zram1
        #
        touch $LOCKFILE
    fi
    ;;
  stop)
    /sbin/zpool remove tank2 /dev/zram0
    /sbin/zfs umount -a
    service zfs stop
    rm -f $LOCKFILE
    ;;
  *)
    echo "Usage $0 start | stop "
    ;;
esac

ストレージプール tank2 に、2G の cache (L2ARC) を追加し、ついでに、スワップを 896M 用意しています。また、起動順は 90番 に設定しています。これは、samba と NFS の初期化順序を考慮した結果です。
# ls -1 /etc/rc5.d/S??{nfs,smb,zfs}
/etc/rc5.d/S01zfs
/etc/rc5.d/S30nfs
/etc/rc5.d/S91smb
デフォルトでは、上のように zfs の初期化が nfs よりも早いのですが、sharenfs を使うには、zfs の初期化を nfs より後ろにする必要があるため、chkconfig zfs off に設定して、zfs の起動は、my_zram の中から行うようにしています。

2012-10-29追記
zram の効用について、追記です。
わたしが ZFS サーバに使用しているマシンは、メモリを 4G 積んでいるのですが、古いチップセット (945G Express) であるため、3.3G までしか見えないという貧弱なものです。zram 等のチューニングを施す前の状態だと、2~3日使用すると、free コマンドで次のような値を示していました。
             total       used       free     shared    buffers     cached
Mem:       3410036    2876652     533384          0      65448     586916
-/+ buffers/cache:    2224288    1185748
Swap:      2046968     213948    1833020
200M ほどスワップ使用されていますが、この状態でも、わたしの用途では、そんなに困るわけでもなかったのですが、zram を適量使うことで (※この適量のバランスが難しかったのですが) 、ディスクスワップしないで済むようになりました。次が、チューニング後の値です。
             total       used       free     shared    buffers     cached
Mem:       3410036    3185076     224960          0      67944     616652
-/+ buffers/cache:    2500480     909556
Swap:      2964464     781364    2183100
ここで、Swap: used の値が 780M ほどを示していますが、これが zram 上に載っています。
# cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/md2                                partition       2046968 0       -1
/dev/zram1                              partition       917496  781352  1
これ、この通り。zram 利用前は md2 が使われていましたが、ディスクスワップせずに済んでいます。初期化スクリプト中で、swapon コマンドに -p 1 というオプションを与えていますが、これにより、md2 よりも zram1 が優先して使われます。
zram の圧縮状況は、/sys から読み取れます。
# cat /sys/block/zram1/mem_used_total
205156352
単純計算で 580M ほど、稼いだ結果になっています。
一方、ストレージプール tank2 に cache (L2ARC) として追加した zram0 のほうは次のような状況です。
# cat /sys/block/zram0/mem_used_total
784912384
zpool iostat -v から cache の使用状況がわかるのですが、上記のキャプチャの際には既に飽和していましたので、約1.3G ほど稼いだことになります。なお、tank2 には、各種 OSS のソースコードと CentOS などの iso イメージを格納しており、secondarycache プロパティを次のように設定しています。
# zfs get secondarycache tank2/src
NAME       PROPERTY        VALUE           SOURCE
tank2/src  secondarycache  all             local
# zfs get secondarycache tank2/isos
NAME        PROPERTY        VALUE           SOURCE
tank2/isos  secondarycache  metadata        local
ソースコードは中身もろとも、iso はメタデータのみ L2ARC 許可ということです。

説明が最後になりましたが、デフォルトの状態だと ARC への割り当て量が多すぎるようなので、次の設定により、1G に絞っています。
# cat /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=1073741824
デフォルトだと 約1.8G でした。/proc/spl/kstat/zfs/arcstats から確認可能です。

まとめますと、わたしのマシンのケースでは、
3.3G 物理メモリ
1G  ARC
2G  zram L2ARC
896M zram swap
という配分で、zram により 約1.9G ほどお得しています。計測が難しいですが、体感上は引っかかりが少なくなったような気がしています。

なお、上記の配分に至るまでは、何回か試行錯誤が必要でした。zram を使いすぎると、スラッシングになり逆効果になる場合もあるようでした。

2012-12-08追記
その後、利用していて、swap が合計 2G 近くに達する状況 (disk swap にはみ出た) があったため、zram swap を 2G に変更して使っています。変更後、2G 弱まで swap が成長しましたが、disk swap には達せずに、よい感じ。どなたかのご参考まで。

2013-06-02追記
illumos プロジェクトで、L2ARC の圧縮機能 (secondarycachecompress) を開発中のようです。メモまで。
http://wiki.illumos.org/display/illumos/L2ARC+Compression

2013-08-31追記
ZFS on Linux 0.6.2 がリリースされました。secondarycachecompress が導入されたようです。着実な進化に感謝です。
https://groups.google.com/a/zfsonlinux.org/forum/#!topic/zfs-announce/DOAuSF7kjsU
New Features:
...
* Added L2ARC compression from Illumos
ですけれど、プールバージョン 5000 にする必要がありますし、自分としては、まだしばらく zram を使おうかと思ってます。

2013-09-02追記
1台を 0.6.2 にアップデートしてみたのですが、secondarycachecompress というプロパティは見当たらず、経緯を確認したところ、illumos のほうで、プロパティにする必要が無いという判断になり、ZOL も追従したようです。ただし、モジュールオプション l2arc_nocompress で off にすることも可能にしたようです。
https://github.com/zfsonlinux/zfs/issues/1379
# rpm -q zfs
zfs-0.6.2-1.el6.x86_64
# modinfo -p zfs | grep l2arc_nocompress
l2arc_nocompress:Skip compressing L2ARC buffers
# cat /sys/module/zfs/parameters/l2arc_nocompress 
0
# uname -a
Linux xxxx 2.6.32-358.18.1.el6.x86_64 #1 SMP Wed Aug 28 17:19:38 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/redhat-release 
CentOS release 6.4 (Final)

2013-10-12追記
Fedora 19 のカーネルは、zram がオフになっているのですが、rpmfusion が提供している kmod-staging に zram が入っているので、こちらを利用すれば良いようです。ただし、カーネルバージョン依存なので、注意が必要です。Fedora から新しいカーネルがリリースされてから1~2週間くらい経たないと、対応する kmod-staging が出てこないので、/etc/yum.conf に exclude=kernel-* を指定しておいたほうがいいかも。
カーネルを再構築してもいいわけですが、面倒なので、メモでした。
なおカーネル 3.11 で、zswap が入ったので、zram + zswap を試したのですが、わたしの利用形態では効果を実感することはありませんでした。

2012年10月27日土曜日

dkms 対応の ZFS on Linux を CentOS 6.3 に導入する

ZFS on Linux 0.6.0-rc10 (Aug 14 2012 リリース) から dkms (dynamic kernel module support) 対応が行われたようで、少々乗り遅れていますが、CentOS 6.3 に dkms 対応版を導入しました。その手順メモです。

これまでは、非dkms対応の 0.6.0-rc8 をビルドして使っていましたので、まずは、こちらを一旦削除してリブートします。
# zfs umount -a
# yum remove zfs
# rpm -e zfs-modules
# yum remove spl
# yum remove spl-modules
# shutdown -r now

次に、基盤となる dkms をインストールします。こちらは、EPEL に収録されているものを利用しました。
# yum install dkms --enablerepo=epel
...
# rpm -qi dkms
Name        : dkms                         Relocations: (not relocatable)
Version     : 2.2.0.3                           Vendor: Fedora Project
Release     : 2.el6                         Build Date: Mon 11 Jun 2012 11:39:12 PM JST
Install Date: Sun 21 Oct 2012 04:56:02 PM JST      Build Host: x86-02.phx2.fedoraproject.org
Group       : System Environment/Base       Source RPM: dkms-2.2.0.3-2.el6.src.rpm
Size        : 215347                           License: GPLv2+
Signature   : RSA/8, Fri 15 Jun 2012 08:47:09 AM JST, Key ID 3b49df2a0608b895
Packager    : Fedora Project
URL         : http://linux.dell.com/dkms
Summary     : Dynamic Kernel Module Support Framework
Description :
This package contains the framework for the Dynamic
Kernel Module Support (DKMS) method for installing
module RPMS as originally developed by Dell.

そして、ZFS 本体をインストールします。途中、zfs をリビルドする際に、e2fsprogs-devel と libblkid-devel が依存エラーになったので、追加インストールしました。
# rpm -ivh spl-modules-dkms-0.6.0-rc11.noarch.rpm
# rpmbuild --rebuild spl-0.6.0-rc11.src.rpm
# rpm -Uvh /root/rpmbuild/RPMS/x86_64/spl-0.6.0-rc11.el6.x86_64.rpm
# rpm -Uvh zfs-modules-dkms-0.6.0-rc11.noarch.rpm
# yum install e2fsprogs-devel
# yum install libblkid-devel
# rpmbuild --rebuild zfs-0.6.0-rc11.src.rpm
# rpm -Uvh /root/rpmbuild/RPMS/x86_64/zfs-0.6.0-rc11.el6.x86_64.rpm
# rpm -Uvh /root/rpmbuild/RPMS/x86_64/zfs-devel-0.6.0-rc11.el6.x86_64.rpm
# rpm -Uvh /root/rpmbuild/RPMS/x86_64/zfs-dracut-0.6.0-rc11.el6.x86_64.rpm
# rpm -Uvh /root/rpmbuild/RPMS/x86_64/zfs-test-0.6.0-rc11.el6.x86_64.rpm
# shutdown -r now

各種モジュールがロードされていること、zfs 領域が見えることを確認して完了です。
# lsmod | grep zfs
zfs                  1104868  0 
zcommon                43286  1 zfs
znvpair                47487  2 zfs,zcommon
zavl                    6925  1 zfs
zunicode              323120  1 zfs
spl                   253420  5 zfs,zcommon,znvpair,zavl,zunicode
# zpool list
NAME    SIZE  ALLOC   FREE    CAP  DEDUP  HEALTH  ALTROOT
tank1  59.5G  1.02M  59.5G     0%  1.00x  ONLINE  -
# zfs list
NAME    USED  AVAIL  REFER  MOUNTPOINT
tank1   920K  58.6G   104K  /tank1

これで、新しいカーネルにアップデートする際、今までよりも手間が減ることになります。現在の dkms の状態を見ると、次のようになっています。
# dkms status
spl, 0.6.0, 2.6.32-279.11.1.el6.x86_64, x86_64: installed
zfs, 0.6.0, 2.6.32-279.11.1.el6.x86_64, x86_64: installed
spl, 0.6.0, 2.6.32-279.5.1.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
zfs, 0.6.0, 2.6.32-279.5.1.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
spl, 0.6.0, 2.6.32-279.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
zfs, 0.6.0, 2.6.32-279.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
現時点で最新の 2.6.32-279.11.1.el6 を使っていますが、残してある古いカーネルでも zfs が動く状態になっていることが確認できます。
そのうち、新しいカーネルが出てきたらアップデートして、追加レポートしたいと思います。

2012-11-07追記
本日、新しいカーネル 2.6.32-279.14.1.el6 が出てきましたので、早速インストールしました。
# uname -a
Linux xxxx 2.6.32-279.14.1.el6.x86_64 #1 SMP Tue Nov 6 23:43:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
# dkms status
spl, 0.6.0, 2.6.32-279.11.1.el6.x86_64, x86_64: installed
zfs, 0.6.0, 2.6.32-279.11.1.el6.x86_64, x86_64: installed
spl, 0.6.0, 2.6.32-279.14.1.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
zfs, 0.6.0, 2.6.32-279.14.1.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
spl, 0.6.0, 2.6.32-279.5.1.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
zfs, 0.6.0, 2.6.32-279.5.1.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
spl, 0.6.0, 2.6.32-279.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
zfs, 0.6.0, 2.6.32-279.el6.x86_64, x86_64: installed-weak from 2.6.32-279.11.1.el6.x86_64
# lsmod | grep zfs
zfs                  1104868  1 
zcommon                43286  1 zfs
znvpair                47487  2 zfs,zcommon
zavl                    6925  1 zfs
zunicode              323120  1 zfs
spl                   253420  5 zfs,zcommon,znvpair,zavl,zunicode
# zfs list
NAME    USED  AVAIL  REFER  MOUNTPOINT
tank1   932K  58.6G   104K  /tank1
このとおり、以前のように再ビルドすることなく、ZFS 領域が使えました。大変便利になったと思います。

2013-04-02追記
0.6.1 がリリースされ、CentOS 6 向けのバイナリの yum リポジトリが提供されたので、こちらを参照してください。

2013-06-29追記
モジュールをコンパイルした際の古いカーネル(上記の例なら 2.6.32-279.11.1.el6)を削除してしまうと、モジュールも削除されてしまい、新カーネル(2.6.32-279.14.1.el6)で ZFS が使えなくなってしまいます。
その場合、次のコマンドで再コンパイルすれば良いです。
# dkms install -m spl -v 0.6.1
# dkms install -m zfs -v 0.6.1

2012年9月5日水曜日

SecureErase のために frozen 状態を解除する方法

SecureErase を行う際、マシン環境によっては、frozen 状態のために SecureErase 実行不能な場合があります。
hdparm の man を読むと次のような記述があります。
       --security-freeze
              Freeze the drive´s security settings.  The drive does not accept
              any security commands until next power-on reset.  Use this func-
              tion in combination with --security-unlock to protect drive from
              any  attempt to set a new password. Can be used standalone, too.
              No other flags are permitted on the command line with this  one.
これをヒントに、ではサスペンド/レジュームすれば HDD の電源が一旦切れるのだから、frozen が解除できるのではないか?っと思い、やってみたら成功しました。
マシン環境は CentOS 6.3 + ThinkPad T510 です。


上がサスペンド前、下がレジューム後です。
CentOS 6 では、LiveCD および LiveDVD が用意されているので、そちらを利用するとよいかと思います。私が試した範囲では、NEC のノートPCでもうまく行きました。

■関連記事
SecureErase を実施してみた

2014-08-31追記
OS 依存ではないとは思いますが、CentOS 7 でも、サスペンド/レジュームで frozen 解除できました。ThinkPad T510 の 2nd-HDD-bay に搭載した HDD を消去しようとして確認。

2012年8月26日日曜日

SecureErase を実施してみた

こちらの記事のように、SecureErase により、不良セクタを回収して代替セクタを割り当てられるケースがあることを知りました。
そこで、
http://marosama.blogspot.jp/2010/03/linuxsecureerase.html
こちらの方の Linux で SecureErase を実施する手順を参考に、わたしもやってみた次第です。
昔、RAID1 を組んでいた IDE HDD 2台のうちの片方が異常になり、別の HDD に移行したことがあったのですが、その時の HDD 2台を死蔵しておりましたので、今回はその 2台に対して SecureErase を試しました。

最初に作業機材について、まとめますと、
・ThinkPad T510 OS:CentOS 6.3 x86_64
・センチュリー社
 NEW 裸族のお立ち台 USB3.0 & eSATA
 裸族のお立ち台のお立ち台 IDE to SATA 変換アダプター
という構成です。T510 の左側面標準装備の eSATA 接続で実施しました。

作業手順はもとの記事のほうに詳しく書いてありますが、いちおうわたしの行った手順を要約しますと、次のような具合です。
# cat /sys/block/sdXX/device/model  等により実施対象の sdXX を確認
                 ディスクが複数接続された環境での作業では十分注意必要
# hdparm -I /dev/sdXX で not frozen 確認
# hdparm --user-master u --security-set-pass password /dev/sdXX でパスワード設定
# hdparm --user-master u --security-erase password /dev/sdXX

作業を行った2台のHDDのうち、1台は不良セクタがあったのですが、SecureErase により除去できたようでした。まあ、今や時代遅れで使い道がほとんどないのですが。


この2枚は、作業完了後に Windows 上から HDDSCAN を行った結果です。
アクセス時間が遅い部分があり、まだら状になっているほう(上)が、RAID1 から外れたディスクだったと思います。

2012-08-29追記


CrystalDiskInfo のデータです。2番目のディスクのほうが注意になってます。
(補足:画像の掲載順序が逆なわけではないです)

2012-09-02追記
さらに別のディスクのデータです。このように、不良セクタが生じていましたが、、、
SecureErase 後には、ひとまず不良セクタが消えました。

掲載したのは、SecureErase が功を奏したデータばかりですが、もちろんのこと、ダメ(無理)だった HDD もありました。手持ち7台のうち2台は無理でした。

なお、壊れたと思っていた SSD にも SecureErase を実行してみたところ、不良部分が無くなりました。次の記事を参照ください。
ThinkPad X300 のSSDで不良セクタを発見

2012-09-29追記
クセになり、また別の死蔵ディスクもやってみました。少なくとも5年ぶりに動かしたところ、不良セクタが生じていましたが、、、
SecureErase 後には、不良部分が解消しました。

2012年8月21日火曜日

perl の print 文の書き方による効率の違い

おそらくは、perl に造詣の深い方には当たり前レベルかもしれませんが、print 文でいくつかの変数を出力する際に、カンマ(,)で区切ると若干効率が悪いようです。
#!/usr/bin/perl

$j = 0 ;
for ($i = 0; $i < 1000000; $i++) {
        $a = $j++ ;
        $b = $j++ ;
        $c = $j++ ;
        $d = $j++ ;

        print $a, $b, $c, $d, "\n" ;
}
と書くよりも、
#!/usr/bin/perl

$j = 0 ;
for ($i = 0; $i < 1000000; $i++) {
        $a = $j++ ;
        $b = $j++ ;
        $c = $j++ ;
        $d = $j++ ;

        print "$a$b$c$d\n" ;
}
としたほうが効率が良いです。
# diff -u perf_joint.pl perf_joint2.pl 
--- perf_joint.pl       2012-08-21 00:17:46.000000000 +0900
+++ perf_joint2.pl      2012-08-21 00:18:16.000000000 +0900
@@ -7,5 +7,5 @@
        $c = $j++ ;
        $d = $j++ ;
 
-       print $a, $b, $c, $d, "\n" ;
+       print "$a$b$c$d\n" ;
 }
# time ./perf_joint.pl | md5sum
8a80cd09e1b5bcad31bdf23d5738c21a  -

real    0m2.224s
user    0m0.230s
sys     0m1.990s
# time ./perf_joint2.pl | md5sum
8a80cd09e1b5bcad31bdf23d5738c21a  -

real    0m1.210s
user    0m0.050s
sys     0m1.160s
些細なことではありますが、チリも積もればという局面もあろうかと思います。

2012年8月4日土曜日

ZFS関連記事のリンク集

ZFSのリンク集


■ZFS-FUSE project
http://zfs-fuse.net/


■ZFS on Linux
http://zfsonlinux.org/


■FUSE経由でZFSを使う
http://sourceforge.jp/magazine/08/06/23/0154238

■L2ARC and Metadata Compressability
http://comments.gmane.org/gmane.linux.file-systems.zfs.user/4101


■ZFS Best Practices Guide
http://www.solarisinternals.com/wiki/index.php/ZFS_Best_Practices_Guide
ZIL は搭載物理メモリの 1/2 ぐらい。


■How to improve ZFS performance
http://icesquare.com/wordpress/how-to-improve-zfs-performance/


■Use scatter-gather lists for ARC buffers
https://github.com/zfsonlinux/zfs/issues/75
現状(現在:0.6.0-rc14)、vmalloc 依存になっていて、ext4 等と違い page cache を使えてない。zfs 領域から大量にファイル read しても、cached は増えない。


2012年7月31日火曜日

ftp を使ってディスク丸ごとバックアップ

最近、カーネルやOSSソース, ISO イメージの収集蓄積を行うのに、ZFS on Linux を利用しています。 メイン PC のディスクを丸ごとバックアップすることも利用目的の1つです。

しかし、ネット上にいくつも情報があるように、ZFS on Linux + NFS アクセスする場合、どうにも性能が出ないようです。
わたしの環境では、書き込みスループットが 1 MB/s を下回るくらいです。とても実用にはなりません。GigaBit Ether で接続しており、ftp ならば 50MB/s くらいは出る環境なのに、NFS だと全然遅くなってしまうのです。残念。バックアップ対象の PC のディスクは 180GB であり、圧縮して 100GB になったとしても、1 MB/s ですから、100000 秒もかかる計算です。どうにかできないものか・・・

ディスク丸ごとバックアップするというと、次のように dd で NFS 領域へ書き込みというのが、わたしのお決まりのパターンなのですが。。。
# dd if=/dev/sda bs=1M | lzop -c > /mnt_nfs/sda.dd.lzo

ftp ならスループット 50MB/s 出るのだし、なんとかして ftp に dd + lzop の出力を渡して転送できないの?・・・とサーチしてみると、なんと超簡単ではありませんか!無知でした。
ftp> bin
200 Type set to I
ftp> put |"dd if=/dev/sda1 bs=1M | lzop -c" sda1.dd.lzo
local: |dd if=/dev/sda1 bs=1M | lzop -c remote: sda1.dd.lzo
227 Entering Passive Mode (192,168,xxx,xxx,128,22).
150 Opening BINARY mode data connection for sda1.dd.lzo
196+1 records in
196+1 records out
205599744 bytes (206 MB) copied, 11.0407 seconds, 18.6 MB/s
226 Transfer complete.
90549924 bytes sent in 11 seconds (8e+03 Kbytes/s)
なんと、Linux (に限らずかな?) の ftp コマンドでは、put の第一引数の先頭に | を指定すれば、パイプラインからデータを読み取ってくれるのですね。作った人に感謝。

2012-08-01追記
上のコンソールは、VMware 上での実験結果ですが、実際 180GB のディスクバックアップを実行してみました。
[root@livedvd Desktop]# ftp mypc
Connected to mypc (10.xx.yyy.zzz).
220 (vsFTPd 2.2.2)
Name (mypc:centoslive): user01
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> bin
200 Switching to Binary mode.
ftp> put |"dd if=/dev/sda bs=1M | lzop -c" user01.sda.backup-2012-08-01.lzo
local: |dd if=/dev/sda bs=1M | lzop -c remote: user01.sda.backup-2012-08-01.lzo
227 Entering Passive Mode (10,xx,yyy,zzz,uuu,vvv).
150 Ok to send data.
171705+1 records in
171705+1 records out
180045766656 bytes (180 GB) copied, 3891.19 s, 46.3 MB/s
226 Transfer complete.
127003147717 bytes sent in 3.89e+03 secs (32638.60 Kbytes/sec)
対象の PC は、普段は Windows XP が動いているマシンなのですが、CentOS 6.3 LiveDVD を利用してバックアップを行いました。Live 状態では、ftp と lzop が未導入なので、yum でインストールする必要がありました。
LiveDVD は初めて使いましたが、こういったケースでは便利なものですね。

結果として、圧縮後の転送レートは 32MB/s で、約1時間でバックアップが完了しました。
これは、わたしとしては十分に満足できる結果です。

2012-08-03追記
1時間でバックアップできるなら十分かと思いましたが、多少工夫の余地がありましたので、追試を行いました。
まず、圧縮率が低い (180G:127G) のは、ファイルシステム (NTFS) の空き領域に過去に書き込んだデータの痕跡があるためと考えられるので、空き領域にオールゼロデータを書き込んでからバックアップを実行すれば、もっと時間短縮できそうでしたので、次のような Ruby スクリプトを使って、ゼロ書き込みを行いました。Linux なら dd if=/dev/zero of=zerofile とすればいいわけですが、Windows XP なので、一筆。
#!/usr/bin/env ruby
#
# Name: zerofill.rb
#

f = open("zerofile", "wb")

z = [0].pack("N")

@z4 = []

for i in 0...1024
        @z4.push(z)
end

z4096 = @z4.join('')

begin
        while true
                f.write(z4096)
        end
end
なお、Windows XP 上の Ruby スクリプト実行には、Rumix を利用しています。

加えて、前回の実験の際に top で挙動を見ていたら、dd でディスクデータ読み出しを行うと、buffers の値が搭載物理メモリサイズの限界まで上昇し、メモリ回収のため kswapd カーネルスレッドが頻繁に動いていました。
でも、この場合は無駄な動きと思えますので、dd に iflag=direct オプションをつけて実行してみました。結果は次の通り。
ftp> put |"dd if=/dev/sda iflag=direct bs=1M | lzop -1 -c" user01.sda.backup-2012-08-02.lzo
local: |dd if=/dev/sda iflag=direct bs=1M | lzop -1 -c remote: user01.sda.backup-2012-08-02.lzo
227 Entering Passive Mode (10,xx,yyy,zzz,uuu,vvv).
150 Ok to send data.
171705+1 records in
171705+1 records out
180045766656 bytes (180 GB) copied, 2297.55 s, 78.4 MB/s
226 Transfer complete.
40910990817 bytes sent in 2.3e+03 secs (17806.37 Kbytes/sec)
期待どおり、約38分で完了しましたし、バックアップファイルのサイズもコンパクトになりました。

2012年7月1日日曜日

2012年7月1日の うるう秒 調整時の ntpd の挙動観測データ

日本時間の 2012年7月1日午前9時 うるう秒 調整が実施されましたが、ntpd の補正がどのように振舞うのか、ntpq -np の出力を観測してみました。
CentOS 5 x86_64 + ntp-4.2.2p1-9.el5_3.2 (slew モード -x オプション) です。
                         remote           refid      st t when poll reach   delay   offset  jitter
                    ==============================================================================
2012-07-01 04:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  613 1024  377   12.831   -0.952  21.164
2012-07-01 04:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  363 1024  377   12.187   -1.422   0.422
2012-07-01 05:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  113 1024  377   11.911   -1.562   0.984
2012-07-01 05:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  887 1024  377   11.911   -1.562   0.485
2012-07-01 06:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  636 1024  377   13.371   -1.439   3.338
2012-07-01 06:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  389 1024  377   13.422   -1.389   2.102
2012-07-01 07:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  139 1024  377   10.875   -3.526   0.334
2012-07-01 07:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  916 1024  377   10.537   -4.184   0.992
2012-07-01 08:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  667 1024  377   11.885   -3.829   0.914
2012-07-01 08:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  419 1024  377   12.479   -2.801   0.037
2012-07-01 09:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  168 1024  377   11.131   -3.257  11.100
2012-07-01 09:32:18  219.xxx.yy.zz   192.168.u.vvv    2 u  942 1024  377   14.296  -1001.0 986.659
2012-07-01 10:02:18 +219.xxx.yy.zz   192.168.u.vvv    2 u  691 1024  377   11.954  -900.19 118.193
2012-07-01 10:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  443 1024  377   11.681  -795.11 100.385
2012-07-01 11:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  193 1024  377   16.832  -519.76  96.828
2012-07-01 11:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  969 1024  377   16.832  -519.76  83.788
2012-07-01 12:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  721 1024  377   12.516  -373.72  66.502
2012-07-01 12:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  474 1024  377   17.056  -246.34  51.731
2012-07-01 13:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  225 1024  377   15.727  -97.721  45.772
2012-07-01 13:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u 1000 1024  377   15.727  -97.721  30.503
2012-07-01 14:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  748 1024  377   12.229  -23.726  31.822
2012-07-01 14:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  501 1024  377   14.533   60.172   1.022
2012-07-01 15:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  251 1024  377   14.231   84.031  18.929
2012-07-01 15:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u    2 1024  377   13.800  124.631  17.261
2012-07-01 16:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  776 1024  377   13.407  156.855  14.962
2012-07-01 16:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  529 1024  377   10.603  166.262   7.274
2012-07-01 17:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  277 1024  377   12.336  185.132   9.301
2012-07-01 17:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u   28 1024  377   13.053  202.397   0.055
2012-07-01 18:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  805 1024  377   13.053  202.397  28.663
2012-07-01 18:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  555 1024  377   13.432  209.414   2.174
2012-07-01 19:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  308 1024  377   14.631  212.225   4.011
2012-07-01 19:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u   56 1024  377   13.349  214.017   0.057
2012-07-01 20:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  832 1024  377   14.411  213.960   1.213
2012-07-01 20:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  583 1024  377   13.615  203.817   6.336
2012-07-01 21:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  334 1024  377   17.122  197.009   2.029
2012-07-01 21:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u   88 1024  377   18.445  198.370  18.541
2012-07-01 22:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  862 1024  377   16.281  194.083  14.255
2012-07-01 22:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  613 1024  377   13.269  187.763  14.045
2012-07-01 23:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  365 1024  377   14.249  180.813   2.089
このデータを見ると、ずれが収まるまでには思いのほか時間がかかるようで、うるう秒調整が実施されてから14時間後の現在でも、上位 NTP サーバと180ミリ秒ほど開きがあります。
offset の値のプラス/マイナスについて、明確に記憶していませんでしたが、上記データから、その NTP サーバが提示する時刻が、サーバのシステム時刻より前である場合、マイナスということですね。
なので、今 +180 ミリってことは、上位 NTP サーバから 180 ミリ秒遅れているわけか。
この続きのデータは、また明日見てみます。

2012-07-02追記
昨晩の続きのデータは、次のようになってました。
                         remote           refid      st t when poll reach   delay   offset  jitter
                    ==============================================================================
2012-07-01 23:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  113 1024  377   16.669  175.530  42.274
2012-07-02 00:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  890 1024  377   27.000  167.411  50.393
2012-07-02 00:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  640 1024  377   10.899  164.112   2.428
2012-07-02 01:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  392 1024  377   17.415  154.429  12.125
2012-07-02 01:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  143 1024  377   13.487  144.749   3.697
2012-07-02 02:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  917 1024  377   12.823  135.426   5.625
2012-07-02 02:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  668 1024  377   12.132  131.629   5.259
2012-07-02 03:02:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  420 1024  377   11.726  120.271   3.557
2012-07-02 03:32:18 *219.xxx.yy.zz   192.168.u.vvv    2 u  170 1024  377   11.441  115.966   3.173
2012-07-02 04:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  944 1024  377   11.231  107.834   4.959
2012-07-02 04:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  184  512  377   13.227   90.511   6.811
2012-07-02 05:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  448  512  377   11.007   85.097   6.125
2012-07-02 05:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  199  512  377   10.028   65.927   4.114
2012-07-02 06:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  463  512  377   11.529   57.629   5.778
2012-07-02 06:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  726 1024  377   11.231   50.067   3.147
2012-07-02 07:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  479 1024  377   12.781   49.581   0.441
2012-07-02 07:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  227 1024  377   12.351   47.561   1.351
2012-07-02 08:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u 1002 1024  377   11.107   46.045   1.515
2012-07-02 08:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  751 1024  377   11.073   44.120  15.058
2012-07-02 09:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  505 1024  377   12.946   41.379   1.681
2012-07-02 09:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  256 1024  377   11.038   37.101   6.429
2012-07-02 10:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u    5 1024  377   12.444   37.810   0.185
2012-07-02 10:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  267  512  377   14.939   36.484  25.669
2012-07-02 11:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   12  128  377   10.509   21.750   6.330
2012-07-02 11:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   11  128  377   10.696   10.668   3.874
2012-07-02 12:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   15  256  377   13.222    5.393   5.583
2012-07-02 12:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  275  512  377   11.597    2.826   2.155
2012-07-02 13:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   24  512  377   11.602    4.860   0.627
2012-07-02 13:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  285 1024  377   12.522    4.768   3.272
2012-07-02 14:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   34 1024  377   12.450    6.383   1.396
2012-07-02 14:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  809 1024  377   12.383    7.734   0.046
2012-07-02 15:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  561 1024  377   13.710    6.254   2.814
2012-07-02 15:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  312 1024  377   13.091    9.562   0.215
2012-07-02 16:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   62 1024  377   12.187    8.944   1.301
2012-07-02 16:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u  838 1024  377   12.651   10.245   3.777
2012-07-02 17:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   80  512  377   11.899   10.865   0.806
2012-07-02 17:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   82  256  377   11.432    9.104   1.885
2012-07-02 18:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   87  128  377   10.328    6.771   0.873
2012-07-02 18:32:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   89  128  377   11.186    4.603   0.883
2012-07-02 19:02:16 *219.xxx.yy.zz   192.168.u.vvv    2 u   94  256  377   11.876    4.625   1.519
まだ、細かい波があるやうな感じですが、10 ミリ秒未満となった 12:00 あたりが収束だとすると、27時間要したことになります。
この結果をふまえると、step モードで正しく推移(時刻の逆行が1秒発生する)させたい気分になりますが、1秒戻りがどんな作用を及ぼすか、事前に完璧に調べあげることなど不可能であり、予期しないトラブルを避けるため、商用で使っているようなサーバでは、やはり slew モード (-x オプション) にしておいたほうが無難なのではないでしょうかね。くわばら、くわばら。



2015-02-01追記
前回の実施から2年半経過した今年1月はじめ、次回の うるう秒(閏秒)が 2015年7月1日(水) AM 9:00:00(日本時間)に実施されることが発表されました。
記憶されている方も多いものと思いますが、上記の記事を書いたまさしくその頃に、Linux カーネル内の うるう秒対応処理に新たな不具合が発見され、世界中の多数の Linux サーバでトラブルが発生しました。
知らずに、またしてもその罠にハマるのも残念ですので、微力ですが、その不具合について、情報追加しておきたいと思います。

まず、レッドハット社の公開情報は、次のとおりです。

https://bugzilla.redhat.com/show_bug.cgi?id=836803
https://rhn.redhat.com/errata/RHSA-2013-0496.html

読んでもらうとわかりますが、RHEL6/CentOS6 のカーネルに内在していた不具合であり、2.6.32-358.el6 で修正されています。ですので、もしも今現在も、古いカーネルを利用し続けているなら、単純には前記したバージョンより新しいカーネルにアップデートすれば良いことになります。

とは言うものの、世間には様々な事情により、カーネルアップデートが実施できない(許可してもらえない)ということも、ままあるものと思います。その場合はどうしたら良いのか?

まず、うるう秒の実施指示(Leap Indicator)は、NTP プロトコルによってもたらされる(通知される)わけですから、NTP クライアント(ntpd など)を動かしていないなら、うるう秒を知る由もないということが言えます。
勿論のこと、大部分のサーバでは ntpd を動かしていることでしょう。ですから、選択肢の1つとしては、うるう秒実施の前後だけ、一時的に ntpd を止めておくという運用方法が考えられます。ここで注意しなければならないのが、うるう秒の実施を通知する Leap Indicator のフラグは、うるう秒実施の 24 時間前からオンになるという点です。ntpd は、受信した Leap Indicator を Linux カーネルに伝達し、カーネルはうるう秒の実施準備を行ってしまいます。カーネルまで Leap Indicator が伝達されてしまうと、キャンセルするには再起動するしかなくなります。
したがって、もし ntpd を一時的に止める運用を行うのであれば、6月30日 AM 9:00(日本時間)より前に停止する必要があります。実際には、余裕を見て、例えば AM 8:00 に止めるなどというスケジュールになるものと思います。
上位 NTP サーバから通知される Leap Indicator の値は、次のコマンドで確認できます。
[root@hoge ~]# ntpq -c readvar
...
processor="x86_64", system="Linux/2.6.32-220.el6", leap=00,
...
leap=00 は通常状態であり、leap=01 だったらば、うるう秒挿入指示を受信済みであることを示します。
さて、当然ですが ntpd を止めたら時刻が少しは狂う(ずれる)ことになります。どれだけずれてしまうかは、マシン毎に個体差があるはずですが、まる1日 ntpd を止める程度なら、プラスマイナス数秒程度ではないかと思います。#これは、後日実験してみたいと思います。

プラスマイナス数秒の時刻ずれも許容できない場合はどうしたら良いのか?

ntpd を動かしたまま、Linux カーネルによる うるう秒調整動作を行わないようにするには、ntpd が、カーネルに Leap Indicator を伝達しないようにできれば良いです。そのパラメータは次のとおりです。/etc/ntp.conf の末尾に指定します。
...
server xxx.yyy.zzz.uuu  iburst
...
disable kernel
この設定を施してから、service ntpd restart を実行すればよいです。本当に disable になったかどうかは、次のコマンドで確認できます。
[root@hoge ~]# ntpdc -c sysinfo
...
system flags:         auth ntp kernel stats      ※正しく設定できれば、kernel が消えます
...
もっとも、slew モード(ntpd -xオプション付き)で動作している場合には、disable kernel がデフォルトになっているはずなので、設定を変更する前に、現在の動作状態を ntpdc コマンドで確認するとよいです。バージョンの古い ntpd は、slew モードでも enable kernel がデフォルトだったものがありますので、やはり、ntpdc コマンドで確認しておいたほうがよいと思います。
disable kernel に対応する処理部分は、次の箇所です。ntp-4.2.6p5-2.el6 より抜粋です。
...
    502         if (pll_control && kern_enable) {
...
    557                         if (sys_leap == LEAP_ADDSECOND)
    558                                 ntv.status |= STA_INS;
    559                         else if (sys_leap == LEAP_DELSECOND)
    560                                 ntv.status |= STA_DEL;
    561                 }
    562 
    563                 /*
    564                  * Pass the stuff to the kernel. If it squeals, turn off
    565                  * the pps. In any case, fetch the kernel offset,
    566                  * frequency and jitter.
    567                  */
    568                 if (ntp_adjtime(&ntv) == TIME_ERROR) {
    569                         if (!(ntv.status & STA_PPSSIGNAL))
    570                                 report_event(EVNT_KERN, NULL,
    571                                     "PPS no signal");
    572                 }
...
"ntpd/ntp_loopfilter.c"
disable kernel を設定すると、kern_enable の値がゼロになり、うるう秒の実施をカーネルに伝達しなくなります。
また、slew モードなら disable kernel になるという処理は、次の部分です。
...
    163         if (HAVE_OPT( SLEW )) {
    164                 clock_max = 600;
    165                 kern_enable = 0;
    166         }
...
"ntpd/cmd_args.c"

2016-12-11追記
次回の うるう秒挿入(日本時間2017年1月1日8時59分59秒)が迫ってますが、RHEL7 のカーネル不具合情報と修正版カーネルが公開されたので、追記しておきます。

http://rhn.redhat.com/errata/RHBA-2016-2862.html
https://access.redhat.com/solutions/2766351

カーネル 3.10.0-514.2.2.el7 で修正されたようですが、すぐにアップデートできないなら、ntpd の slew モードを使っておけばよいです。または、ntpd を一時的に止めておく方法も可能。ちなみに、今回のRHEL7のカーネル不具合(発症するとCPU100%)は、発生確率がけっこう低い (500回試して1回程度と記載されてます) ようです。

■関連記事
ntpd を1日止めた場合のズレはどの程度か?
うるう秒(閏秒)の際にcronが二重に実行されるかも?という都市伝説(?)について
2015年7月1日の うるう秒 調整時の ntpd の挙動観測データ
2017年1月1日の うるう秒 調整時の ntpd の挙動観測データ (記載予定)

2012年6月19日火曜日

第2水曜日にcronスケジュールしたい

第2水曜日および第3水曜日にcronスケジュールしたいと思ったのですが、調べてみるとcronで指定可能なパターンでは、スケジュールできないことを知りました。

http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=20020&forum=10

ZFS ファイルシステムの scrub 処理 (整合性チェック) を定期的に動かしたいのですが、zfs-fuse に標準で備わっている設定方法だと、毎週行うか否かの2択になっており、それはちょっと動かし過ぎと思いました。
RAIDZ と RAIDZ2 の2つのストレージプール (tank1, tank2) を作成して ZFS を利用していますが、tank1 は月1回、tank2 は2ヶ月に1回の頻度で scrub を動かしたいため、前述の URL の情報を参考に、自作スクリプトを作りました。どなたかの参考になれば幸いと思いますので、掲載します。
#!/bin/bash
#
# Name: my-zfs-fuse-scrub
#

TARGET=""
if [[ "$1" = 20??-??-?? ]] ; then
    TARGET="-d $1"
elif [ -n "$1" ] ; then
    echo "Usage: my-zfs-fuse-scrub [YYYY-MM-DD]" 1>&2
fi

MONTH=`date +%-m $TARGET`
DAY=`date +%-d $TARGET`
WEEKDAY=`LANG=C date +%a $TARGET`

get_W() {
    local D=$1
    local F L
    for W in 1 2 3 4 5
    do
        F=$((1+($W-1)*7))
        L=$((F+6))
        if let "$F <= $D && $D <= $L" ; then
            break
        fi
    done
}

test_get_W() {
    local D
    for D in `seq 1 31`
    do
        get_W $D
        printf "D=%-2d W=%d\n" $D $W
    done
}
##DEBUG## test_get_W && exit 1

get_W $DAY

if [ "$W,$WEEKDAY" = "2,Wed" ] ; then
    if [ -z "$TARGET" ] ; then
        /usr/bin/zpool scrub tank1
    else
        echo "DEBUG: zpool scrub tank1 run @ $1"
    fi
fi

if [ "$W,$WEEKDAY" = "3,Wed" ] && let "$MONTH%2 == 0" ; then
    if [ -z "$TARGET" ] ; then
        /usr/bin/zpool scrub tank2
    else
        echo "DEBUG: zpool scrub tank2 run @ $1"
    fi
fi

exit 0
これを、次のように crontab に登録することで、毎月第2水曜日 AM1:00 に tank1 の scrub を実行、偶数月の第3水曜日 AM1:00 に tank2 の scrub を実行するようにスケジューリングしました。
0 1 * * Wed /root/bin/my-zfs-fuse-scrub
なお、実行日を確認し易いように、デバッグ機能をつけてあります。
# cal 6 2012
      June 2012
Su Mo Tu We Th Fr Sa 
                1  2
 3  4  5  6  7  8  9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

# ./my-zfs-fuse-scrub 2012-06-13
DEBUG: zpool scrub tank1 run @ 2012-06-13
# ./my-zfs-fuse-scrub 2012-06-20
DEBUG: zpool scrub tank2 run @ 2012-06-20
# ./my-zfs-fuse-scrub 2012-06-27
#
# cal 7 2012
      July 2012
Su Mo Tu We Th Fr Sa 
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

# ./my-zfs-fuse-scrub 2012-07-11
DEBUG: zpool scrub tank1 run @ 2012-07-11
# ./my-zfs-fuse-scrub 2012-07-18
#
さあこれで、まずは、今晩深夜 1:00 に tank2 への scrub がちゃんと動くはず...何か間違いを犯していなければ。。

2012-06-23追記
ログを確認したところ、うまく行っていました。
# cat /var/log/cron
...
Jun 20 01:00:01 xxxx CROND[28792]: (root) CMD (/root/bin/my-zfs-fuse-scrub)
...
# zpool status

  pool: tank1
 state: ONLINE
 scrub: none requested
config:

...

errors: No known data errors

  pool: tank2
 state: ONLINE
 scrub: scrub completed after 0h42m with 0 errors on Wed Jun 20 01:42:50 2012
config:

...

errors: No known data errors
次は、7/11 (第2水曜) に tank1 の scrub が動き、7/18 (第3水曜日 && 奇数月) には tank2 への scrub が行われない、というふうにプログラムできたはずです。

2012-07-20追記
その後のログを確認したところ、意図した通りに動作していました。
# who -b
         system boot  2012-07-03 12:14
# uptime
 08:07:13 up 14 days, 19:53,  3 users,  load average: 0.12, 0.09, 0.09
# uname -a
Linux xxxx 2.6.32-220.13.1.el6.x86_64 #1 SMP Tue Apr 17 23:56:34 BST 2012 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/redhat-release 
CentOS release 6.3 (Final)
# grep zfs-fuse /var/log/cron
Jul  3 13:26:01 xxxx run-parts(/etc/cron.weekly)[12348]: starting 98-zfs-fuse-scrub
Jul  3 13:26:01 xxxx run-parts(/etc/cron.weekly)[12355]: finished 98-zfs-fuse-scrub
Jul  4 01:00:01 xxxx CROND[41538]: (root) CMD (/root/bin/my-zfs-fuse-scrub)
Jul 10 03:10:01 xxxx run-parts(/etc/cron.weekly)[30291]: starting 98-zfs-fuse-scrub
Jul 10 03:10:01 xxxx run-parts(/etc/cron.weekly)[30298]: finished 98-zfs-fuse-scrub
Jul 11 01:00:01 xxxx CROND[4220]: (root) CMD (/root/bin/my-zfs-fuse-scrub)
Jul 17 03:37:01 xxxx run-parts(/etc/cron.weekly)[43573]: starting 98-zfs-fuse-scrub
Jul 17 03:37:01 xxxx run-parts(/etc/cron.weekly)[43580]: finished 98-zfs-fuse-scrub
Jul 18 01:00:01 xxxx CROND[15841]: (root) CMD (/root/bin/my-zfs-fuse-scrub)
# zpool status | grep scrub:
 scrub: scrub completed after 1h13m with 0 errors on Wed Jul 11 02:13:35 2012
 scrub: none requested
# zpool status
  pool: tank1
 state: ONLINE
 scrub: scrub completed after 1h13m with 0 errors on Wed Jul 11 02:13:35 2012
config:

...

errors: No known data errors

  pool: tank2
 state: ONLINE
 scrub: none requested
config:

...

errors: No known data errors

これであとは、5年くらい運用・利用できたら、コスト(投資額+構築労力)に見合うかなと思います。

2015-02-10追記
備忘録。この記事を書いた当時は zfs-fuse を使ってたが、その後、ZFS on Linux に切り替えて継続利用中。
当時新品の HDD (ちなみに全て AFT の 2.5inch 7200rpm 750G)で組んで、2年半経過しましたが、今のところ不良セクタはありません。

2012年5月20日日曜日

Python でオプション解析

Python 初歩レベルな私ですが、自分用に Python 用のコマンドラインオプション解析テンプレートを作成しましたので、参考までに。。
#!/usr/bin/env python
#
# Name: getopt-template.py
#
import sys
import getopt

def usage_exit():
    sys.stderr.write("Usage: getopt-template.py [-a] [-d dir] item1 item2 ...\n")
    sys.exit(1)

#
# Options
#
print sys.argv  ####DEBUG

try:
    opts, argv = getopt.getopt(sys.argv[1:], "ad:h")
except getopt.GetoptError:
    usage_exit()

(opt_a, opt_d) = (0, None)
for (opt, optarg) in opts:
    if opt == "-a":
        opt_a = opt_a + 1
    elif opt == "-d":
        opt_d = optarg
    elif opt == "-h":
         usage_exit()
    else:
         usage_exit()

argc = len(argv)
print "argc =", argc    ####DEBUG
print argv              ####DEBUG
print "opt_a =", opt_a  ####DEBUG
print "opt_d =", opt_d  ####DEBUG
# ./getopt-template.py -aa -d dir item1 item2
['./getopt-template.py', '-aa', '-d', 'dir', 'item1', 'item2']
argc = 2
['item1', 'item2']
opt_a = 2
opt_d = dir
# ./getopt-template.py item1 -d dir
['./getopt-template.py', 'item1', '-d', 'dir']
argc = 3
['item1', '-d', 'dir']
opt_a = 0
opt_d = None
getopt.getopt の場合、上記のように -d dir を後置すると、期待したように解釈されません。
getopt.gnu_getopt を使えば後置が可能なのですが、Python 2.x 系で追加されたもののようで、古い Python 1.x 系では使えないようです。

参考
perl でオプション解析(getoptコマンド編)

2012年5月19日土曜日

bashスクリプトでatexit(3)相当を行う

C 言語だと、atexit() という関数がありますが、bash にも似た機能があることを知りました。

bash スクリプトが Ctrl+C で途中中断される場合を想定し、スクリプトの処理途中で生成される一時ファイルを削除(ゴミ掃除)したい場合があります。そんな時には、trap "rm ..." SIGINT という具合に書くのが定石かと思っていました。しかし、そのような書き方だと、スクリプト全体の終了時にも rm の記述をすることになります。次のような具合に・・・
#!/bin/bash
#
# Name: test_trap_SIGINT.bash
#

TEMPFILE=$(mktemp /tmp/.${0##*/}.$RANDOM$RANDOM.XXXXXX)
trap "echo trapped ; rm -f $TEMPFILE" SIGINT

echo $TEMPFILE

# 一時ファイルを使った処理を行う ... 
sleep 10 # このサンプルでは、スリープ
#

rm -f $TEMPFILE
exit 0
このサンプルの実行イメージは次のようになります。
途中中断しない場合
# ./test_trap_SIGINT.bash 
/tmp/.test_trap_SIGINT.bash.2085610279.7jIDoS
# ls -l /tmp/.test_trap_SIGINT.bash.2085610279.7jIDoS
ls: /tmp/.test_trap_SIGINT.bash.2085610279.7jIDoS: No such file or directory

途中で Ctrl+C した場合
# ./test_trap_SIGINT.bash 
/tmp/.test_trap_SIGINT.bash.579227843.ewVGq4
trapped
# ls -l /tmp/.test_trap_SIGINT.bash.579227843.ewVGq4
ls: /tmp/.test_trap_SIGINT.bash.579227843.ewVGq4: No such file or directory
最近、このパターンでスクリプトを書いていたら、一時ファイル生成後に途中終了 (exit 1) させたい条件が複数あり、いちいち rm を記述しました。
しかしハテ?もしかして atexit(3) のような機能はないものか?
っと、help を見てみますと、、、
# help trap
trap: trap [arg] [signal_spec ...] or trap -l
    The command ARG is to be read and executed when the shell receives
    signal(s) SIGNAL_SPEC.  If ARG is absent all specified signals are
    reset to their original values.  If ARG is the null string each
    SIGNAL_SPEC is ignored by the shell and by the commands it invokes.
    If a SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from
    the shell.  If a SIGNAL_SPEC is DEBUG, ARG is executed after every
    command.  If ARG is `-p' then the trap commands associated with
    each SIGNAL_SPEC are displayed.  If no arguments are supplied or if
    only `-p' is given, trap prints the list of commands associated with
    each signal number.  Each SIGNAL_SPEC is either a signal name in 
    or a signal number.  `trap -l' prints a list of signal names and their
    corresponding numbers.  Note that a signal can be sent to the shell
    with "kill -signal $$".
あるではないですか・・・さすが bash ですね。
なので、次のように書けます。
#!/bin/bash
#
# Name: test_trap_EXIT.bash
#

TEMPFILE=$(mktemp /tmp/.${0##*/}.$RANDOM$RANDOM.XXXXXX)
trap "echo trapped ; rm -f $TEMPFILE" EXIT

echo $TEMPFILE

# 一時ファイルを使った処理を行う ... 
sleep 10 # このサンプルでは、スリープ
#

exit 0
注意点としては、もし trap "トラップ内容" SIGINT EXIT と書くと、Ctrl+C 中断の際にトラップ内容が2回実行されることになります。

2012年4月15日日曜日

Pythonには++演算子は無い

前に、Ruby に ++ 演算子(インクリメント演算子)が無いことを書きましたが、Python にも無いということを、認識しました。
もっとも、Python の場合にはループの抽象度が高いようで、これまでの自分の経験上では、for (i=0; i<10; i++) のようなコードが必要なことは、皆無ですが。

2012年2月11日土曜日

perl でオプション解析(Getopt::Long編)

前に、Getopt::Std編getoptコマンド編 を書きましたが、同じオプションを複数回指定するパターンが使いたかったので、Getopt::Long についても調べました。
#!/usr/bin/perl
#
# GetOptions-template.pl
#

use Getopt::Long ;

sub usage_exit {
        print STDERR "Usage: getopts-template.pl [-a] [-b] [-d dir] item1 item2 ...\n" ;
        exit 1 ;
}

print "@ARGV\n" ;       ####DEBUG
{
        local $SIG{__WARN__} = \&usage_exit ;
        GetOptions( "a" => \$opt_a, "b+" => \$opt_b, "d=s" => \$opt_d, "h" => \$opt_h ) ;
        ### see man Getopt::Long(3pm)
}

if (defined $opt_h) {
        usage_exit ;
}

$argc = scalar @ARGV ;
print "\$argc=$argc\n" ;        ####DEBUG
print "\@ARGV=@ARGV\n" ;        ####DEBUG
print "\$opt_a=$opt_a\n" ;      ####DEBUG
print "\$opt_b=$opt_b\n" ;      ####DEBUG
print "\$opt_d=$opt_d\n" ;      ####DEBUG
オンラインマニュアル(man Getopt::Long(3pm))によると、プラス記号を使うと、同じオプションの複数回指定に対応できるとのことなのですが、実際に試してみると、、、
# ./GetOptions-template.pl -a -bb
-a -bb
Usage: getopts-template.pl [-a] [-b] [-d dir] item1 item2 ...

# ./GetOptions-template.pl -a -b -b
-a -b -b
$argc=0
@ARGV=
$opt_a=1
$opt_b=2
$opt_d=
残念ながら、複数回指定可能と言っても、このように -bb とは指定できず、-b -b という具合に指定する必要があるようです。コードは少し長くなりますが、getoptコマンドを使う方法なら対応できます。
人気ブログランキングへ にほんブログ村 IT技術ブログへ