2015年12月4日金曜日

SuperMicro Update Managerを試してみる

SuperMicro社が提供している有償ツールのSuperMicro Update Manager(SUM)を試してみました。このツールを利用するとIPMIと同じ感覚でBIOS設定の確認や変更、BIOSファームウエアの更新が可能です。

SuperMicro Update Manager

SUM紹介ベージ

https://www.supermicro.com/solutions/SMS_SUM.cfm

Linux起動中にBIOSの設定を確認したくなることありませんか。Windows環境のマシンであればそのようなツールもあるよう気もしますが、Linux上となるとあまり聞かないと思います。以前、Linux上でEFI変数を利用する方法を試してみたのですが、設定らしきものはバイナリデータになっていて詳細不明で、設定の変更はブートデバイスの優先順位くらいしか変更できませんでした。SuperMicro社が提供しているSuperMicro Update Managerを利用すると簡単に(同社のMB限定ですが)OS起動中にBIOSの設定を確認したり変更したりすることが可能になります。

SUMの機能などの詳細な説明は上記のWebページに詳しく紹介されていますのでそちらを参照ください。今回はBIOS周りの機能を試してみました。

SUMではツールを利用して以下のことが可能です。
  • BIOS設定の取得
    • 現在の設定やデフォルトの設定などをテキストファイルベースで確認できます。
  • BIOS設定の変更
    • テキストファイルベースで設定の変更ができます。ただし変更を反映するには再起動が必要です。
  • BIOSファームウェアのアップデート
    • BIOSをアップデートをそのままの環境で実行できます(DOS環境はいりません)。ただしアップデートを反映するには再起動が必要です。
また対象のサーバにアクセスする方法として二つの方法あります。
  • リモート(Out-band)でアクセスする。
    • 別のマシンでSUMを実行してネットワーク経由で対象サーバにアクセスする
  • ローカル(In-band)でアクセスする。
    • 対象サーバ上で起動しているOS(Llnux)上でSUMを実行して自分自身にアクセスする。
IPMIと同様に実際にBIOSの設定を取得したり変更したりするのを担っているのはMB上に搭載されているコントローラです。したがって、リモートでアクセスする場合には対象サーバのOSには制限ありません。OSが起動していない状態や、通電はされていてサーバの電源は入っていない状態でもリモートアクセスであれば実行可能です。

SUMのダウンロードとインストール

SUMはソフトウェア上記のWebページのリンク先からダウンロードできますがアクセスキーが必要です。また利用にはライセンスを購入する必要があります。ただ、上記ページから評価用のライセンスを申請することができ、試せる機能も購入した場合のものとかわらないようです。評価用のライセンスの申請にはMBの型番とBMC(IPMI)のMACアドレスを合わせて申請する必要があります。申請は(英語ですが)Webのフォームに必要事項を記入して送信するとメールでライセンスとソフトウエアのダウンロード用のアクセスキーが送られてきます。評価用のライセンスはすぐに発行されるようですのでお手もとのSuperMicro社製のMBが対応していれば試してみるものもよいのではないでしょうか。

対応システム

SUMはSuperMicro社製のX10世代のMBあるいは一部のX9世代のMBを搭載したシステムで利用できます。X10というのはだいたいHaswell世代のMB、X9の一部というのはRomleyチップセットを搭載した2ソケットのMBのことです。

対応BMC(IPMI)/BIOS

SUMでBIOSをアップデートしたいのにそのためにBIOSをアップデートしないといけないというのはなんか本末転倒ですが、MBのBMC/BIOSがSUMでのアクセスに対応していないといけません。最近の製品であれば問題ないと思われますが、MBのBMC(IPMI)およびBIOSのバージョン以下の通りある必要があるようです。

リモート(Out-Band)アクセスでの利用に必要なファームウェア

BMC Version

PlatformBMC Version
X9 ATEN platform   (SMT_X9)3.14 or later
X10 ATEN platform (SMT_X10)1.52 or later
X9 AMI platform     (SMM_X9)2.32 or later

BIOS Version

SystemBIOS Version
X9 Romley and X10 Denlow systems2.0 or later
X10 Grantley systems1.0 or later

 

ローカル(In-Band)アクセスの利用に必要なファームウェア

BIOS Version

SystemBIOS Version
X9 Romley and X10 Denlow systems3.0 or later
X10 Grantley systems1.0 or later
 
またLlnux上でローカルに実行する際にBIOSによっては専用のドライバ(カーネルモジュール)が必要になるようです。今回自分がためした環境では特にBIOS/BMCのアップデートやカーネルモジュールは必要ありませんでした。

ライセンス

SUMを利用するには(有償の)のライセンスが必要です。はじめにライセンスキーを利用してMBのコントローラをactivateする必要があります。SUMのソフトウェアの実行自体には特に制限はないようですが、ライセンスキーで activateしないとコントローラがSUMからのアクセスを受け付けてくれないようです。このようにライセンスはシステム(MB)1つにつき1つ必要です。

SUMの実行環境

SUMのソフトウェアにはLinux版とWindows版がありますが、どちらも64bit用しかないようです。またSUMをつかってリモートでアクセスするためには以下のJAVAのランタイム環境が必要です。
  • OPEN JDK/Oracle JRE 1.7.0以降
ローカルで実行する場合にはJava環境は必要ないようです。今回はOPEN JDK 1.7/Oracle JRE 1.8で動作を確認しています。

SUMのダウンロードとインストール

上記のページにあるリンク先からアクセスキーを使ってSUMのソフトウェアをダウンロードできます。展開するとsumコマンドがあるのでそれを実行すればOKです。インストールする際には展開したディレクトリをそのまま適当な場所にコピーすればいいと思います。コマンド本体はsumバイナリですが、リモートでアクセスする場合は同じディレクトリある付属のjarファイルが必要があります。

SUMのコマンドの形式

SUMのコマンドの形式は次の通りです。コマンド名で各動作を指定します。

# sum -i <ターゲットのIPMIアドレス> -u <ユーザ名> -p <パスワード> -c <コマンド名> [ --file <入出力ファイル> ]
アドレスはIPMIのアドレス、ユーザ名とパスワードもIPMIのそれと一緒です。ローカルで実行する場合はIPMIのアドレス,ユーザ名,パスワードを省略して-c <コマンド名>から始めればOKです。IPアドレスを改行で区切ったリストファイルを作成してそれを-l <リストファイル名>でアドレスのかわりに指定することによって同時に複数のターゲットに対してコマンドを実行することも可能です。
基本的にはコマンドはリモートでもローカルでもどちらでも実行可能です。また、SUMのコマンドをローカルで実行する場合はroot権限が必要です。

ランセンスのアクティベーション

ますシステムのコントローラをランセンスをつかってアクティベーションする必要があります。

$ ./sum -i 192.168.0.101 -u ADMIN -p ADMIN -c ActivateProductKey --key XXXX-XXXX-XXXX-XXXX-XXXX-XXXX 
Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64) Copyright©2015 Super Micro Computer, Inc. All rights reserved Product key activated for 192.168.0.101

これでSUMが使えるようになりました。アクティベーションしないと基本的に全ての機能は利用できません。CheckOOBSupportコマンドでリモートでのコマンドのサポート状況を確認できます。

./sum -i 192.168.0.101 -u ADMIN -p ADMIN -i -c  CheckOOBSupport
Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64)
Copyright©2015 Super Micro Computer, Inc. All rights reserved

[KEY]
Node Product Key Activated.......OOB

[BMC]
BMC FW Version...................03.19
BMC Supports OOB BIOS Config.....Yes
BMC Supports OOB DMI Edit........Yes

[BIOS]
BIOS Board ID....................0821
BIOS Build Date..................2015/8/13
BIOS Supports OOB BIOS Config....Yes
BIOS Supports OOB DMI Edit.......Yes

BIOS設定の取得

早速BIOSの設定を取得してみましょう。コマンドはGetCurrentBiosCfgTextFile(長い!)です。ちなみにコマンド名の大文字小文字は区別されないので全部小文字でも大丈夫です。

# time sum -i 192.168.0.101 -u ADMIN -p ADMIN -c GetCurrentBiosCfgTextFile --file config.txt
Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64)
Copyright©2015 Super Micro Computer, Inc. All rights reserved


File "config.txt" is created.

real    0m15.509s
user    0m14.721s
sys     0m0.800s

timeで時間を測定してみましたがリモートで大体16-20秒くらいでした。ローカルで実行してもそんなに変わりませんが、体感リモートの方がかかる時間が短かったような気がします。--fileで指定したファイルにテキストして次のような形で出力されます(長いので割愛します)

#Please refer to SUM User's guide '4.1 Format of BIOS Settings Text File' for usage.

[Advanced|Boot Feature]
Quiet Boot=01                           // Please enter the value in 2 hexadecimal digits. Default value is <<<01>>>       
AddOn ROM Display Mode=01               // *01 (Force BIOS), 00 (Keep Current)       
Bootup NumLock State=01                 // *01 (On), 00 (Off)       
Wait For "F1" If Error=01               // 00 (Disabled), *01 (Enabled)       
INT19 Trap Response=01                  // *01 (Immediate), 00 (Postponed)       
Re-try Boot=00                          // *00 (Disabled), 01 (Legacy Boot), 02 (EFI Boot)       
Watch Dog Function=00                   // *00 (Disabled), 01 (Enabled)       
Power Button Function=01                // *01 (Instant Off), 00 (4 Seconds Override)       
Restore on AC Power Loss=02             // 00 (Stay Off), 01 (Power On), *02 (Last State)       

[Advanced|CPU Configuration]
Clock Spread Spectrum=00                // Please enter the value in 2 hexadecimal digits. Default value is <<<00>>>       
Hyper-Threading (ALL)=00                // 01 (Disable), *00 (Enable)       
Cores Enabled=00                        // Please enter the value in 2 hexadecimal digits

(以下省略)

これはただのテキストファイルですので、BIOSの設定をdiffで比較したり、gitなどにあげて管理したりする事が可能です。便利ですね。

BIOS設定の変更

BIOS設定の変更は先ほど出力した設定のテキストファイルを編集して再度読み込ませる形で行います。編集の方法はテキストファイルのコメントみれば直感的に分かるとおもいますが"="のあとの数字を変更しておこないます。例えば最初の部分を以下のように編集します。

$ cp config.txt new_config.txt
$ vi new_config
$ diff config.txt new_config.txt
4c4
< Quiet Boot=01                           // Please enter the value in 2 hexadecimal digits. Default value is <<<01>>>       
---
> Quiet Boot=00                           // Please enter the value in 2 hexadecimal digits. Default value is <<<01>>>       

変更する箇所以外の設定は削除してもしなくても問題ありません。削除しても設定の変更にかかる時間は変わらないようです。変更する場所のみを残す場合その設定の項目([ ]で囲まれている行)も一緒に残しておく必要があります。今回は設定を変更しない部分もそのままに単純に取得した設定ファイルに手を加えるだけにしています。


これの新しい設定ファイルをChangeBiosCfgコマンドで指定して変更します。
# time sum -i 192.168.0.101 -u ADMIN -p ADMIN -c ChangeBiosCfg --file new_config.txt
Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64)
Copyright©2015 Super Micro Computer, Inc. All rights reserved



Status: Start updating the BIOS configuration for 192.168.0.101

************************************WARNING*************************************
    Do not remove AC power from the server.
********************************************************************************

Status: The BIOS configuration is updated for 192.168.0.101

Note: You have to reboot or power up the system for the changes to take effect


real    0m17.279s
user    0m16.581s
sys     0m0.876s

時間を計ってみましたが大体20秒前後で終了しました。
上にログにもでていますが設定の変更の反映は再起動されるまで行われません。それまでは設定は今まで通りです。
サーバが通電だけれされていて起動していない状態でもBIOSの設定の変更は可能ですがその場合は起動するまで反映されません。
逆に言えば複数あるサーバに対してまとめて変更だけを実行しておいて、それぞれのサーバの再起動は可能なタイミングで別個に実行するみたいな運用も可能です。
ただ、変更はサーバが通電されていないと保持されないようです。設定は変更したものの(再)起動しないまま電源ケーブルを抜いてしまった場合などは変更が消えてキャンセルされてしまうようです。
忘れずに(再)起動するようにしましょう。

ちなみに許容される設定値以外を指定した場合など、設定に不備がある場合にはエラーになって変更されません。ご安心を。

Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64)
Copyright©2015 Super Micro Computer, Inc. All rights reserved


********************************<<<<<ERROR>>>>>*********************************

ExitCode             = 60
Description          = Invalid BIOS configuration text file
Program Error Code   = 2.18
Error message:
    In file fail_config.txt, line 2:
    [Advanced|Boot Feature]Quiet Boot
        Value <<<02>>> has no option.

********************************************************************************

BIOSファームウェアのアップデート

SUMをつかってBIOSのファームウェアをアップデートすることが可能です。BIOSファームウェアのアップデートなんてCriticalな作業をネットワーク越しでするってのはどうなの?という意見はあるとおもいますがとにかく可能です。
もちろんLinux上でローカルにSUMを実行してアップデートすることもできます。
コマンドはUpdateBiosです。ファイルとしてSuperMicroから配布されているBIOSファイルのバイナリイメージを直接指定します。

# unzip X10DRW5_813.zip
# ls
FLASH.BAT                    SMCIPMITool.properties       afudosu.smc                  choice.smc                   tmp.txt                     
Readme for X10 AMI BIOS.txt  X10DRW5.813                  bios_update.log              fdt.smc

# time ./sum -i 192.168.0.101 -u ADMIN -p ADMIN -c updatebios --file X10DRW5.813
Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64)
Copyright©2015 Super Micro Computer, Inc. All rights reserved
Status: Start updating BIOS for 192.168.0.101

************************************WARNING*************************************
    Do not remove AC power from the server.
********************************************************************************

..................................................
..................................................
..................................................
..................................................
...........
Status: BIOS is updated for 192.168.0.101

Note: You have to reboot or power up the system for the changes to take effect


real    3m41.213s
user    0m13.745s
sys     0m1.016s

リモートで4分弱ほどかかりました。最初にイメージファイルを転送しているので多分途中でネットワークが切れても大丈夫だと思いますが確認していません。
例によって再起動するまでは変更は反映されす、ファームウェアも古いままです。BIOSの設定同様、起動がかかる前に電源を完全に落としてしまうとキャンセルされてしまうのではないかと思われますがこれも確認していません。

ローカルでも実行してみました。この場合リモートに比べてかなり時間がかなりかかったのと、再起動を強制するオプションをつけないと実行できませんでした。
他にオプションを指定するなどして調整できたのかもしれませんが調べていません。

# time ./sum -c updatebios --file X10DRW5.813 --reboot
Supermicro Update Manager (for UEFI BIOS) 1.4.2 (2015/09/23) (x86_64)
Copyright©2015 Super Micro Computer, Inc. All rights reserved
sh: 1: cannot create /proc/sys/kernel/nmi_watchdog: Directory nonexistent
Reading BIOS flash ..................... (100%)
Checking BIOS ID ...
Checking ME Firmware ...
Comparing FDT for ROM file and flash.... (98%)
FDT is same, Update BIOS and ME(exclude FDT) regions....
sh: 1: cannot create /proc/sys/kernel/nmi_watchdog: Directory nonexistent
Reading BIOS flash ..................... (100%)
Checking BIOS ID ...
Checking BIOS ID ...
Writing BIOS flash ..................... (100%)
Verifying BIOS flash ................... (100%)
Checking ME Firmware ...
Putting ME data to BIOS ................ (98%)
Writing ME region in BIOS flash ...
 - FDT won't be updated when ME is not in Manufacturing mode!!
   BIOS upgrade continues...
 - Updated Recovery Loader to OPRx
 - Updated FPT, MFSB, FTPR and MFS
 - ME Entire Image done
WARNING:Must power cycle the system for the changes to take effect!


Broadcast message from root@wheezy64 (pts/0) (Fri Nov 27 15:14:59 2015):

The system is going down for reboot NOW!
                        
real    18m18.135s
user    2m30.510s
sys     5m30.040s

18分以上かかってしまいました。--rebootオプションをつけないとエラーになって実行できませんでした。この場合ファームウェアのアップデート完了後、強制的に再起動されます。

リモートでのアップデートに抵抗がなければかなり簡単にアップデートが可能なのではないでしょうか。ローカルだと時間がかかってしまったり、強制的に再起動になってしまうのがちょっと使いにくい印象ですが、 DOS環境を立ち上げてアップデートするよりもずっと簡単にアップデートが可能だと思います。

WebインターフェースでのBIOSアップデート

最近のSuperMicro社せいのMBではIPMIのWebインターフェース(IPMIのアドレスにhttpでアクセスすると利用できる)上でもBIOSのファームウェアのアップデートが可能なようです。今までもBMC(IPMI)のファームウェアのアップデートはWebインターフェースで可能でしたが、それと同じ要領でBIOSのファームウェアもアップデートできます。
その際にライセンスが必要になりますが、そのライセンスはSUMのライセンスと一緒です。おそらく、SUMでアクティベートしてあればWebインターフェスでのアップデートも利用できるようになっているとおもいます。

そのほかの機能

SUMではこれらBIOS関連以外にもBMC(IPMI)の設定の取得や変更、DMI情報の取得と変更およびAsset情報の取得なども可能です。
これらの機能は従来のツールでも可能でオマケ的なものもありますが、DMI情報を変更できるのはちょっと便利な気がします。また機会があったら試してみたいです。

まとめ

SUMを使うことによって大量にあるサーバを簡単に同一のBIOS設定にすることが可能で、いつでも設定を確認できます。
設定の確認、変更に利用するテキストファイルは可読性が高く直感的に扱えます。またテキストファイルなのでgit上などで共有する上も差分を簡単に取得でき便利です。
BIOSのファームウェアのアップデートはそう頻繁に実行するものでもないとはおもいますが、このような仕組みがあればいざいうとき便利だと思います。

Serfを試してみました。

非集中型のクラスタ管理ツールのSerfを触ってみました。クラスタ管理というと何かいろいろ大変そうなイメージですが、Serfを利用すると簡単にサーバの死活監視や情報の収集が可能になります。

Serfについて

Serfの本家サイト:https://www.serfdom.io/

Serf is a decentralized solution for cluster membership, failure detection,and orchestration. Lightweght and highly available.

本家のサイトのトップページに書かれているようにserfは軽量で高可用性の非集中型のクラスタ管理ツールです。
  • インストールは簡単。
    •  serfの静的バイナリを各サーバにコピーするだけOK。
  • マスターサーバのような全体を管理しているノードはない
    • 全てのノードで実行されているserfは平等。
  • ゴシッププロトコルを利用したノード間のメッセージングツール
    • ノード間でお互いに通信して情報を全体に伝播させる。
  • 各ノードで実行されているserf agent が情報のやりとりをする。
    • クラスタ内のノードの増減についてのイベント
    • クラスタ内のノードの生死についてのイベント
    • ユーザ独自のイベントを通知することも可能
  • イベントをトリガにして任意の処理(スクリプト)を実行できる。
    • これによっていろいろ応用が効くようになる。

    例えば、クラスタ内のノードの死活監視やノードが追加/停止したときに自動でロードバランサの設定(バランス先)を変更するなどの使い方が考えられます。
    また複数のアプリケーションを組み合わせて利用する場合に全体の統制をとるのに利用できます。元々そのような管理の仕組みのないアプリケーションの組み合わせでも全体を統制するような仕組みを構築できます。

      インストール

      公式サイトに(静的)バイナリで配布されているのでそれをダウンロードしてきて展開し適当なパスにコピーすればOKです。今回は/usr/local/binに置く事にしました。

      # wget https://releases.hashicorp.com/serf/0.6.4/serf_0.6.4_linux_amd64.zip
      # unzip  0.6.4_linux_amd64.zip
      # cp serf /usr/local/bin

      Serfではこの基本的にserfコマンドのみを利用し、serf <コマンド名> [ 引数 ...]という形式で複数の機能を実行します。

      実行方法

      今回はVagrant+Virtualboxの仮想環境で検証していますが、実マシンはもちろんコンテナ環境でも問題ないです。とりあえず、debian01,debian02という名前の2つの仮想サーバを立ち上げます。サーバのアドレスは次のようになっています。
      debian01 : 192.168.0.101
      debian02 : 192.168.0.102
      debian03 : 192.168.0.103 (後ほど出てきます)
      まだ以下のログでサーバ毎の時間がずれていますが気にしないでください(すみません)

      まずそれぞれの仮想サーバでserf agentを実行します。コマンドはそのまま実行するとforegroundでの実行になります。今回はログをそのまま表示するためにforegroundで実行していますが、実際に利用する場合はbackgroundでの実行になると思います。

      debian01上で

      root@debian01:# serf agent -node=debian01 -bind=192.168.0.101
      ==> Starting Serf agent...
      ==> Starting Serf agent RPC...
      ==> Serf agent running!
               Node name: 'debian01'
               Bind addr: '192.168.0.101:7946'
                RPC addr: '127.0.0.1:7373'
               Encrypted: false
                Snapshot: false
                 Profile: lan

      ==> Log data will now stream in as it occurs:

          2015/12/02 10:15:41 [INFO] agent: Serf agent starting
          2015/12/02 10:15:41 [INFO] serf: EventMemberJoin: debian01 192.168.0.101
          2015/12/02 10:15:42 [INFO] agent: Received event: member-join

      同様なことをdebian02上でも実行します。

       

      ノードのクラスタへの参加

      agentを実行した段階ではまだお互いに"自分のクラスタ"に属しているだけの状態です。それぞれのサーバでメンバーを確認してみると自分自身しかいません。

      root@debian01:~# serf members
      debian01  192.168.0.101:7946  alive

      root@debian02:~# serf members
      debian02  192.168.0.102:7946  alive

      どちらかのノードをもう片方のクラスタに参加させることにより同じクラスタに属して通信が可能な状態になります。debian02をdebian01のクラスタに参加させてみます。このことはdebian01をdebian02のクラスタに参加させることと同じです。debian02には自分のクラスタにdebian01が参加したというイベントが通知されます。

      root@debian02:~# serf join 192.168.0.101
      Successfully joined cluster by contacting 1 nodes.
      root@debian02:~# serf members
      debian02  192.168.0.102:7946  alive 
      debian01  192.168.0.101:7946  alive

      debian01上で確認してみてもdebian02が参加していることがわかります。

      root@debian01:~# serf members
      debian01  192.168.0.101:7946  alive 
      debian02  192.168.0.102:7946  alive

      またそれぞれのノードにノードが追加されたことが通知されています。
      debian01側

      2015/12/02 10:43:49 [INFO] serf: EventMemberJoin: debian02 192.168.0.102
      2015/12/02 10:43:50 [INFO] agent: Received event: member-join

      debian02側

      2015/12/02 10:39:16 [INFO] serf: EventMemberJoin: debian01 192.168.0.101
      2015/12/02 10:39:16 [INFO] agent: joined: 1 nodes
      2015/12/02 10:39:17 [INFO] agent: Received event: member-join
      さらにクラスタにノードを追加する場合にはノードのいずれかのメンバのクラスタに追加すればOKです。例えば3番目のノードdebian03立ち上げてをdebian02のクラスタに参加させてみましょう。次のようにserf agentの起動時にノードを指定して同時にjoinすることも可能です。

      debian03上で

      root@debian03:~# serf agent --node=debian03 --bind=192.168.0.103 --join=192.168.0.102
      ==> Starting Serf agent...
      ==> Starting Serf agent RPC...
      ==> Serf agent running!
               Node name: 'debian03'
               Bind addr: '192.168.0.103:7946'
                RPC addr: '127.0.0.1:7373'
               Encrypted: false
                Snapshot: false
                 Profile: lan
      ==> Joining cluster...(replay: false)
          Join completed. Synced with 1 initial agents

      ==> Log data will now stream in as it occurs:

          2015/12/02 10:50:11 [INFO] agent: Serf agent starting
          2015/12/02 10:50:11 [INFO] serf: EventMemberJoin: debian03 192.168.0.103
          2015/12/02 10:50:11 [INFO] agent: joining: [192.168.0.102] replay: false
          2015/12/02 10:50:11 [INFO] serf: EventMemberJoin: debian01 192.168.0.101
          2015/12/02 10:50:11 [INFO] serf: EventMemberJoin: debian02 192.168.0.102
          2015/12/02 10:50:11 [INFO] agent: joined: 1 nodes
          2015/12/02 10:50:12 [INFO] agent: Received event: member-join

      debian03はdebian01とdebian02が追加されたというイベントを受け取ります。またdebian03が追加されたというイベントはdebian02だけでなく、debian01にも伝播して通知され、3台のノードが同じクラスタに参加している状態になります。

      root@debian01:~# serf members
      debian01  192.168.33.101:7946  alive 
      debian02  192.168.33.102:7946  alive 
      debian03  192.168.33.103:7946  alive

      debian01のログ

      2015/12/02 10:55:33 [INFO] serf: EventMemberJoin: debian03 192.168.0.103
      2015/12/02 10:55:34 [INFO] agent: Received event: member-join

      ノードのクラスタからの離脱

      ノードをクラスタから離脱させるためにはそのノードのagentを停止させればOKです。正常終了した場合と、異常終了した場合とではこのときに他のサーバに通知されるイベントが異なります。

      正常終了

      正常終了するには以下方法があります。
      • serf leaveコマンドを実行
      • SIGINTあるいはCtrl-Cでserf agentプロセスを終了させる。
      正常終了させた場合は終了したという通知がそのagentから発せられクラスタの各ノードに伝播します。ここではdebian03で(フォアグランドで実行していた)agentをCtrl-Cで終了させてみます。

      debian03でCtrl-C

      ^C==> Caught signal: interrupt
      ==> Gracefully shutting down agent...
          2015/12/02 10:58:49 [INFO] agent: requesting graceful leave from Serf
          2015/12/02 10:58:49 [INFO] serf: EventMemberLeave: debian03 192.168.0.103
          2015/12/02 10:58:49 [INFO] agent: requesting serf shutdown
          2015/12/02 10:58:49 [INFO] agent: shutdown complete

       debian01,debian02にはそれぞれmember-leaveのイベントが通知されているのが分かります。
      debian01
      2015/12/02 11:04:51 [INFO] serf: EventMemberLeave: debian03 192.168.0.103
      2015/12/02 11:04:52 [INFO] agent: Received event: member-leave

      debian02
      2015/12/02 10:58:49 [INFO] serf: EventMemberLeave: debian03 192.168.0.103
      2015/12/02 10:58:50 [INFO] agent: Received event: member-leave

      この場合、メンバーの確認すると 正常終了したサーバはleftと表示されます。

      root@debian01:~# serf members
      debian01  192.168.33.101:7946  alive 
      debian02  192.168.33.102:7946  alive 
      debian03  192.168.33.103:7946  left

       

      異常終了

      SIGTERMでプロセスを停止した場合やサーバの電源OFFなど、強制的にagentが停止された場合は他のノードがそのことを感知してイベントとして通知されます。debian03のagentをSIGTERM(9)で停止してみます。

      root@debian03:~# ps -ef | grep -i serf
      root      3607  3592  0 19:48 pts/0    00:00:00 serf agent -node=debian03 -bind=192.168.33.103 -join=192.168.0.102
      root@debian03:~# kill -9 3607
      この場合は他のメンバーがdebian03がアクセス不可になっていることを検出する形になります。
      debian01

      2015/12/02 11:19:28 [INFO] memberlist: Suspect debian03 has failed, no acks received
      2015/12/02 11:19:30 [INFO] memberlist: Suspect debian03 has failed, no acks received
      2015/12/02 11:19:31 [INFO] memberlist: Suspect debian03 has failed, no acks received
      2015/12/02 11:19:33 [INFO] memberlist: Marking debian03 as failed, suspect timeout reached
      2015/12/02 11:19:33 [INFO] serf: EventMemberFailed: debian03 192.168.0.103
      2015/12/02 11:19:34 [INFO] agent: Received event: member-failed
      2015/12/02 11:19:42 [INFO] serf: attempting reconnect to debian03 192.168.0.103:7946
      2015/12/02 11:21:12 [INFO] serf: attempting reconnect to debian03 192.168.0.103:7946
      ...
      ...

      debian02
      2015/12/02 11:12:24 [INFO] memberlist: Suspect debian03 has failed, no acks received
      2015/12/02 11:12:27 [INFO] memberlist: Suspect debian03 has failed, no acks received
      2015/12/02 11:12:28 [INFO] memberlist: Suspect debian03 has failed, no acks received
      2015/12/02 11:12:29 [INFO] serf: EventMemberFailed: debian03 192.168.0.103
      2015/12/02 11:12:30 [INFO] agent: Received event: member-failed
      2015/12/02 11:14:05 [INFO] serf: attempting reconnect to debian03 192.168.0.103:7946
      .....


      debian01,debian02にはmember-failedのイベントが通知されているのが分かります。またイベントを受け取ったあとも一定間隔でdebian03へのアクセスを試み続けます。

      この場合にクラスタのメンバをチェックするとfailedと表示されます。

      root@debian01:~# serf members
      debian02  192.168.0.102:7946  alive  
      debian03  192.168.0.103:7946  failed 
      debian01  192.168.0.101:7946  alive


      まとめ

      serfの起動と停止をためしてみました。今回はサーバの死活だけでしたがserfを利用するとサーバの状態を把握したり、イベントを受けて任意のスクリプトを実行したりすることができます。クラスタの管理自体も非集中型でシンプルなものになっておりそれぞれのサーバにserfのバイナリをコピーするだけで構成することが可能です。

    2015年11月17日火曜日

    コンテナ上でのipvsおよびiptablesの利用

    従来の仮想マシンと比較して軽量な仮想環境としてコンテナが注目されている。コンテナによる仮想環境での特徴としてコンテナで同じカーネルを共有している点があげられる。

    また、コンテナで実際のサービスを提供する場合、アプリケーションがほかにカーネルの提供する機能も必要となる。そこで今回はコンテナ環境下でのカーネルの機能の利用について実際に試してみた。

    コンテナ上でのカーネルの機能の利用

    • コンテナではapacheやmysqlといったプロセスはそれぞれの名前空間で実行されている
    • カーネルはすべてのコンテナでホストのカーネルを利用している。
    • ipvsやiptablesはカーネル側の機能
    コンテナではホストのカーネルを共有しそれぞれのコンテナごとに独立した名前空間を作成して、その上でユーザプロセスを実行することによって仮想環境を実現している。
    しかし、実際のサービスなどにおいてはhttpサーバやデータベースなどのユーザプロセスだけではなくカーネルの機能も合わせて利用される。例えば、ロードバランスやパケットのフィルタリングを行う場合などはipvsやiptablesといったカーネルの機能が必要になる。
    コンテナ上でカーネルの機能を利用する場合、そのカーネルについてはすべてのコンテナで共有されている点において通常とは事情が異なる。
    今回はipvsおよびiptablesについて実際にコンテナ上での利用を試してみた。

    システム構成

    コンテナ構成図
    検証としてWebサーバへのアクセスをLVSを利用してロードバランスするシステムをコンテナを利用して構成することを考える(上図)
    ホストマシン2台からなる構成で、それぞれのホストにipvsを利用してWebコンテナへのロードバランスを実行するLVSコンテナ、 iptablesとapacheを使ってLVSからロードバランスされてきたアクセスを受けるWebコンテナを作成する。
    以下の説明ではそれぞれのホスト及びコンテナの構成方法についての詳細は割愛して、それぞれのコンテナ上でのipvsおよびiptablesの動作および設定についてのみ述べる

    LVSコンテナ

    LVSコンテナは2つのネットワークインターフェースeth0、eth1を持ちそれぞれが仮想ブリッジを介して仮想イーサネット(veth)によってホストマシン(物理マシン)のインターフェースに接続されている。eth0側が外部ネットワーク、eth1側がWebコンテナ側のネットワークになっている。

    ipvsの設定

    ipvsの設定はコンテナ上にipvsadmによって可能である。ただし、ipvsadmを利用してコンテナ上でipvsの設定を実行する前にホスト側でip_vsモジュールをmodprobeにより読み込んでおく必要がある。

    lvshost# modprobe ip_vs

    またコンテナ上でipvsadmで設定を行うためにはコンテナがroot権限で実行されている必要がある。いわゆる一般ユーザ権限による非特権コンテナではipvsの設定はできないようである。
    実際にipvs設定を行うと次のようになる。今回はロードバランスするアドレスは10.10.0.100としてeth0のエイリアスとして設定してある。またLVSのパケットの転送方式はダイレクトルーティング(-gオプション)を選択している。

    lvs1 # ipvsadm -A -t 10.10.0.100:80 -s lc
    lvs1 # ipvsadm -a -t 10.10.0.100:80 -r 192.168.0.50 -g
    lvs1 # ipvsadm -a -t 10.10.0.100:80 -r 192.168.0.51 -g
    lvs1 # ipvsadm -Ln
    IP Virtual Server version 1.2.1 (size=4096)
    Prot LocalAddress:Port Scheduler Flags
      -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
    TCP  10.10.0.100:80 lc
      -> 192.168.10.50:80             Route   1      0          0        
      -> 192.168.10.51:80             Route   1      0          0

    lvs1 # ip addr add 10.10.0.100 label eth0:100 dev eth0
    lvs1 # echo 1 > /proc/sys/net/ipv4/ip_forward

     このようにipvsの設定方法は普通のLinux Boxの場合とほとんど変わらない。

    Webコンテナ 

    Webコンテナのネットワークインターフェースは仮想ブリッジを介して仮想イーサネット(veth)によりホストマシ ンのインターフェースに接続されている。ホストマシンのネットワークインターフェースは先ほどのLVSホストのeth1側のネットワークに接続するように 構成されている。
    Webコンテナはアドレスが異なる(192.168.0.50と192.168.0.51)2つのものを作成して、それそれにLVSコンテナからロードバランスされたパケットが転送されてくるようにする。
     またここではWebコンテナは検証のためいわゆる非特権コンテナで実行されている。つまりWebコンテナ上のrootはホスト上からみた場合一般ユーザになっている。

    Webコンテナの設定

    コンテナ上でiptablesコマンドによりiptablesの設定は可能である。ただし、ipvsの場合と同様に設定を実行する前にホスト側でip_tablesモジュールをmodprobeにより読み込んでおく必要がある。

    webhost# modprobe ip_tables

    LVSでダイレクトルーティング方式での転送を選択しているので、ロードバランスされてWebコンテナに振り分けられてくるパケットの宛先はサービスアドレス(10.10.0.100)のまになっている。この宛先のパケットをWebコンテナで受け取るためにiptablesのREDIRECTターゲットを利用する。

    root@web1# iptables -t nat -A PREROUTING -p tcp --dport 80 -d 10.10.0.100 -j REDIRECT

    ipvsの場合と異なり、iptablesの設定はいわゆる非特権コンテナ上でも可能なようである。テスト時にWebコンテナの区別がつくようにindex.htmlにコンテナ名を記述しておく。

    root@web1# echo "HELLO,This is web1" > /var/www/html/index.htm

    同様にもう一つのWebコンテナ(web2)をipアドレスを変えて(192.168.0.51)作成する。本来であれば同じホストにコンテナ複数作成してロードバランスしたとしても負荷分散や冗長化という観点からはあまり意味がないが検証用にそのように構成した。

    2つのWebコンテナを構成後、LVSコンテナのeth0側からサービスアドレスにアクセスするとロードバランスされていることが確認された。

    $ curl http://10.10.0.100
    HELLO,This is web1
    $ curl http://10.10.0.100
    HELLO,This is web2

    コンテナ上のiptables/ipvsの設定について

    作成した2つのWebコンテナでiptablesの設定を表示すると確認できるが、それぞれのコンテナで設定が独立していることがわかる。ipvsの設定も同様にコンテナごとに独立したものになる。

    コンテナ上のiptablesのLOGターゲットについて

    コンテナ上の場合、iptablesのLOGターゲットでのログの取得はできないようである。
    これは一般的なカーネル空間のログの問題のようで、今のところカーネル空間のログをそれぞれのコンテナに関連するログごとに分けて記録するような仕組みがない。つまりコンテナからカーネル空間のログを記録する動作をした場合、すべてのコンテナおよびホストで共通になっている場所に記録される(あまり自信がありませんが)。その関係上、コンテナ上ではiptablesを利用してLOGターゲットでカーネル空間にログを出力することは、今のところ意図的にできないようになっているようである。
    コンテナ上でiptablesのログを記録するには、ユーザ空間にログを出力するULOGやNFLOGターゲットを利用する必要がある。

    その他

    検証はしていないが、コンテナ上にインストールして実行するipvsadmやiptablesのコマンドはホストのカーネルバージョンとの互換性がある必要があるとおもわれる。

    まとめ

    • iptableやipvsはコンテナでも同様に利用できる。
      • ただしホスト側での事前のモジュールの読み込みが必要である。
      • またipvsはroot権限でコンテナを実行していないと設定できない。
    • iptablesでログを取得したい場合はULOGかNFLOGを使うこと。 
    これらの点に注意すれば、普通のLinux Boxと同様な方法で、コンテナを利用したロードバランサやファイアフォールをipvsやiptablesを使って構築することが可能である。ただしコンテナの場合、オーバーヘッドがなどの観点から、スループットなどについて別途検証が必要あると思われる。

    参考文献
    • コンテナ上でiptablesのログターゲットが利用できない背景(カーネル空間のログの問題)はここを参考にさせていただきました。
    • 非特権コンテナについてはこちらを参考にさせていただきました(日本語約ページ)



    2015年11月13日金曜日

    最近話題のLinuxコンテナを試してみました。

    Linuxコンテナ

    従来の仮想マシンと比較して軽量な仮想環境としてコンテナが注目されています。仮想マシンのようにまるごと物理マシンをエミュレーションした環境を用意するのでなく、ネットワークやプロセスIDなどのみが独立した環境(コンテナ)を用意してその中でプロセスを実行するタイプのOSレベル仮想環境です。今回はそのコンテナについてもっともベースとなる部分を試してみました。

    LXCのインストール

    最近はコンテナの利用する環境を整えるのにも各ディストリビューションに対応したパッケージをインストールするだけで大丈夫です。 コンテナを利用するための実装にはいくつのかの種類がありますが今回はLXCを利用します。他にはDockerなどが有名です。ただLinuxコンテナの核となる部分はカーネル側の機能なので、実装が違っていてもベースとなる考え方は一緒です。 
     今回はDebian/Jessieを利用しますが、LXCはほとんどのディストリビューションでパッケージ化されていると思います。またカーネルが必要な機能を有効にしてコンパイルされていないとダメなのですが、ディストリビューションの標準カーネルであればまず問題ないはずです。
     DebianのJessieではapt-getでインストールするだけです。
    # apt-get install lxc

    コンテナの作成 


     ではコンテナを作りましょう。一連の作業はrootで実行する必要があります。コンテナの作成においては、コンテナ用のファイルシステムを作成することがメインです。あとはコンテナを実行するためのLXCの設定ファイルも必要です。

    コンテナ用のファイルシステム

    コンテナ内のプロセスからはこのファイルシステムがルートファイルシステム(/)として見え、基本的にここにしかアクセスできません。コンテナ内のプロセスはこのルートファイルシステム上で実行されます。

     コンテナを作成するには先ほどインストールしたLXCのユーティリティに含まれるlxc-createコマンドを利用するのが簡単です。

    lxc-create -n <コンテナ名> -t <テンプレート名> -- <テンプレートオプション>
    例:test1という名前で64bit Debian/Jessieのルートファイルシステムを作成する場合 


    # lxc-create -n test1 -t debian -- --arch amd64 --release=jessie

     lxc-create コマンドは-tオプションで指定したテンプレートに従ってルートファイルシステムを作成します。テンプレートは/usr/share/lxc/templates/lxc-<テンプレート名>という名前で実体が用意されています。必ずしもホストのLinuxと同じディストリビューションのファイルシステムでなくてはいけないということはありませんがアーキテクチャは同じでないといけません。ホストが64bitであるならばルートファイルシステムも64bit用のものを用意します。
     debianテンプレートの場合debootstrapコマンドを利用してネットワーク越しにrootfsを取得してきます

    # lxc-create -n test1 -t debian -- --arch amd64 --release=jessie

     LXCの標準的な構成であればコンテナの保存場所は/var/lib/lxcになります。その場所の<コンテナ名>ディレクトリ以下にコンテナの一連のファイルが作成されます。

    # cd /var/lib/lxc
    # tree -L 1 test1
    test1
    |-- config
    `-- rootfs
    rootfs以下が作成されたルートファイルシステムです。configがLXCの設定ファイルになります。rootfs以下にはdebootstrapで作成された(最小インストールの)Debianのルートファイルシステムが作成されています。

    # ls test1/rootfs/
    bin   dev  home  lib64    mnt  proc  run     selinux  sys  usr
    boot  etc  lib     media    opt  root  sbin  srv      tmp  var

     作成されたコンテナの一覧はlxc-lsコマンドで確認できます。

    # lxc-ls -f
    NAME   STATE    IPV4  IPV6  AUTOSTART 
    -------------------------------------
    test1  STOPPED  -     -     NO        

    まだ、コンテナを作成しただけで実行してはいないのでSTOPPEDになっています。

    コンテナの実行

    早速コンテナを実行してみましょう。コンテナを実行するとはコンテナ内でなんからのプロセスを実行することです。実行するものはなんでも良いのですが、コンテナ内ではコンテナのルートファイルシステムにしかアクセスできませんので、その上にプロセス本体のファイルが存在していることが必要です。本体の実行にライブラリが必要なのであればそれも同様にルートファイルシステム上に存在している必要ががあります。

     デフォルトで作成される設定ファイルは内容がまだスカスカで実用には十分ではありませんがとりあえずコンテナ内でプロセスを実行することはできるはずです。

     ここではコンテナ内でシェル(bash)を実行してみます。コンテナ内でのプログラムの実行するにはlxc-startコマンドを利用します。

    # lxc-start -n test1 -- /bin/bash
    bash: cannot set terminal process group (-1): Inappropriate ioctl for device
    bash: no job control in this shell

     警告が出てしまいますが、シェルをコンテナ内で実行できました。このシェル上で色々確認してみるとこのシェルのプロセスがコンテナ内に閉じ込められていることがわかります。
     ファイルシステムは/var/lib/lxc/test1/rootfs以下の部分にしかアクセスすることができません。そこがコンテナ内ではルートファイルシステム(/)として見えていることが分かります。
    root@test1:/# pwd
    /
    root@test1:/# ls
    bin   dev  home  lib64  mnt  proc  run   selinux  sys  usr
    boot  etc  lib   media  opt  root  sbin  srv      tmp  var
    root@test1:/# cat /etc/hostname
    test1

     psでプロセスを確認してみると自身のシェル(とpsコマンド)しか見えないのがわかります。他に大量に動作しているであろうホスト上のプロセスは確認できません。しかもシェルのPIDがなんと1番になっているのがわかります。

    root@test1:/# ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 05:12 ?        00:00:00 /bin/bash
    root        11     1  0 05:40 ?        00:00:00 ps -ef

     ipコマンドでネットワークを確認してみるとネットワーク(eth0)がないことがわかります。ディストリビューションによってはLXCインストール時にコンテナ用のネットワークが用意され有効になっているかもしれません。いずれにしてもホスト上のものとは別のネットワークが見えるはずです。

    root@test1:/# ip addr
    1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

     シェルから抜けて終了させてみます。するとコンテナも終了します。lxc-lsコマンドで確認してみてもstoppedになっていると思います。コンテナでは基本的にコンテナ内でPID1番に見えるプロセスが終了するとコンテナ自身も終了します。

     再びコンテナ内でbashを実行して、ホスト上の別端末からlxc-lsコマンドで確認してみます。今度はステータスがRUNNINGになっていると思います。
    # lxc-start -n test1 /bin/bash
    bash: cannot set terminal process group (-1): Inappropriate ioctl for device
    bash: no job control in this shell
    root@test1:/#

    別端末から
    # lxc-ls -f
    NAME   STATE    IPV4  IPV6  AUTOSTART 
    -------------------------------------
    test1  RUNNING  -     -     NO


     コンテナの外からコンテナ内で実行しているプロセスがどのようにみえているか確認してみます。bashだとどれがコンテナ内のbashなのか確認しにくいのでコンテナ内のbashから適当なプロセスをさらに実行して目印にしておきます。

    root@test1:/# tail -f /dev/null

     ホスト上でpsコマンド実行するとコンテナ内のプロセスは普通に確認できることがわかります。プロセスIDも他のプロセスと同様のものがちゃんと振られています。コンテナ実行時のlxc-startが親プロセスとしてコンテナ内のbashを実行し、それがコンテナ内ではPID1番(外側では12146番)として扱われています。

    # ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    ...
    root     12142 12081  0 05:47 pts/1    00:00:00 lxc-start -n test1 /bin/bash
    root     12146 12142  0 05:47 pts/1    00:00:00 /bin/bash
    root     12156 12146  0 05:49 pts/1    00:00:00 tail -f /dev/null

    まとめ

    Linuxコンテナとは基本的にはこのようにプロセスを他のプロセスやファイルシステム、ネットワークなどから隔離して実行する技術です。隔離する事によりプロセスの予期せぬ動作からシステムや他のプロセスを保護することができます。

    2014年9月13日土曜日

    GUIDパーティションとMBRブート


    • GUIDパーティションのディスクからGRUBで通常(MBR)起動する際のメモ。
    • なんとなくやってできていたので気にしていませんでしたが、UEFIでのブートを試した際に今まで勘違いしていたことが分かったのでその点のまとめ

    GUIDパーティションディスクからの起動

      GUIDパーティションテーブル(GPT)の規格はUEFI(EFI)の規格とセットであるが、GUIDパーティションテーブルのディスクからの起動にはEFIブートが必須というわけではない。
      Windowsの場合は無理な場合が多いが、LinuxではGPTのディスクからも従来通りの方法(MBRを利用した方法)での起動は可能。

    GUIDパーティションテーブル

      GUIDパーティションテーブルの先頭のセクタ(LBA0)は従来のMBRと同じものになっている。これは互換性や安全性のため。GPTに対応していない機器にディスクを接続してしまったときに、ディスクと認識されなかったり、さらには初期化のされていないディスクと勘違いされていきなり初期化されたりしないようにするため。
      従来のMBRパーティションでは、MBR内にパーティション情報を格納してたが、GPTではここにはパーティションを情報を格納しない。MBRにつづく第2セクタ(LBA1)にあるパーティションテーブルヘッダおよび第3セクタからはじまるパーティションエントリに情報を格納する。
      MBRパーテョンでは基本パーテョションが4つまでであったり、大きなサイズ(2TB以上)のディスクを扱えなかったりしたのは、MBR内のパーティション情報を格納する領域の大きさ的にそれが限界であったため。
      GPTではこのパーテョション情報を格納する領域は十分に広い。パーティションは128個まで、ディスクサイズは8ZiB(ゼビバイト)まで扱える(もちろん、情報が格納できるというだけで実際に使えるかどうかはOSなどの対応による)

    MBRブート

      MBRブートでのブートローダはMBR内に格納する。MBRのサイズは全体で512B(1セクタ)であるが上記のパーティションテーブル情報分があるのでブートローダに利用できる領域は446Bしか用意されていない。
      このサイズでは多機能なブートローダは格納できない。そこでGRUBなどの多機能なブートローダでは、MBR内には多機能なブートローダの本体を読み出すだけのブートローダをインストールして、本体は別の(もっと大きなサイズのプログラムを置ける)場所から読み込むようになっている。

    MBRの第1パーテョション

      MBRパーティションでは第1パーティションは第1シリンダ(63セクタ)以降から始まるというルールがあった。このルールのためMBRの第2セクタから62セクタまでの61セクタ(約31KB)は必ず空きになっていた(MBR Gap)
      GRUBではこの領域にブートローダの本体を置き、MBR上のブートローダがこれをロードして本体が実行されるようになっている。
      この領域に本体を置いておく利点は、OSの管理外の領域であるために、一度書き込んだデータのディスク上の物理的な位置(セクタ位置)は変化しない。本体のインストール時にそのセクタ位置を記録しておけば、それをもとにMBRのブートローダが本体の位置を確定することができる。
      OSの管理するファイルシステム上に書き込んだ場合、OS側のファイルシステムの調整によって本体のファイルそのものは消去されなくてもディスク上の物理的な位置は変化する可能性がある。そうなった場合、最初に記録したセクタ位置から本体のファイルが移動して読み込めなくなる(MBR上のブートローダはファイルシステムを理解できないので本体の移動に追従できない)

      パーティションツールによってはMBRパーティションであっても、第2セクタからすぐに第1パーティションを切ることが可能であるが、そのようなことをするとGRUBなどのブートローダはうまくインストールできない可能性がある。

    GUIDパーティションテーブルとMBRブート

      GRUB2はGPTでのMBRブートに対応している。ただし、GPTでは先頭のMBRの次の第2セクタからパーティション情報の領域がすぐに始まってしまう(MBR Gapがない)のでMBRの場合と同じ場所にGRUBの本体をインストールすることができない。そこでGPTでGRUB2のMBRブートを利用する場合、GRUBの本体をインストールするための専用の領域を確保する必要がある。

    bios_grub領域

      linuxのpartd(GNU Parted)の場合パーティションにbios_grubオプションを設定することによってGRUBの本体をインストールする領域にすることが可能。その他のGPT対応のパーティションニングツールではパーティションタイプをEF02に設定する。
      GRUB(GRUB2)はGPTディスクへのインストール時にこの領域をみつけるとそこに本体を自動的にインストールする。GRUB2の説明を読むとこの領域は最低31KB必要とかかれているがこれはMBR Gapが最低31KBだったことに対応している。

      GRUB2はこのbios_grub領域がない場合でも、/bootディレクトリ以下に本体(ファイル)をインストールして起動するようにすることができる。この場合、そのファイルのディスク上の物理的なセクタ位置をインストール時に記録して、その情報をもとにMBRのブートローダが本体の位置を特定して読み込む。ただし、何からの原因で/bootのファイルシステムが調整されてファイルの物理的なセクタ位置が変更されてしまうと起動できなくなる。bios_grub領域を利用することを推奨。

    • MBRのブートローダは他の場所にあるブートローダを読み込むだけ。
    • OSはその読み込まれたブートローダによって起動される。

    EFI専用パーティション

     GPTディスクでのブートで関連してくるパーティションにEFI専用パーティション(EFIシステムパーティション)というものがある。
     これはGPTディスクでEFIブートする場合にEFI形式のブートローダを格納しておくためFAT32でフォーマットされた領域であって、前述のbios_grub領域とは全くの別物である。bios_grub領域はフォーマットをする必要はない。
     GPTディスクであってもMBRブートのみする場合にはこの領域は必要ない。

    MBRブートとEFIブート

      従来のBIOSのMBRブートでは起動時に初めに読み込むブートローダの位置は第1セクタで決め打ちされていて、BIOSではどの機器(ディスクやCDROM,USBメモリ)の第1セクタのブートローダを読み込むかくらいしか選択できなかった。
      EFI BIOSでは、EFI BIOSがFAT32でフォーマットされたEFI専用パーティションを"理解"できるので、パーティション内に格納されているEFI形式のブートローダのファイルの位置を確定して直接読み込むことができる。
      つまりEFI BIOSでEFIブートする場合、BIOSでの設定では起動する機器ではなく起動に利用するブートローダファイルを選択することができる。
      ファイルは通常のファイルシステムにおけるファイルパスで指定される。
      デフォルトのブートローダのファイルパスも決められているのでEFIブート時に"機器"しか選択しなかった場合はその機器上にあるEFI専用パーティションのデフォルトのファイルパスにあるブートローダが選択される。

    実際の手順

     ディスクにpartedでパーティションを作成する。そんなには必要ないがbios_grub領域は100MB(2048セクタ)確保している。

    # parted /dev/sda
    (parted) u s 
    (parted) mklabel gpt
    (parted) mkpart primary 2048 4095
    (parted) set 1 bios_grub on
    (parted) mkpart primary 4096  234441614
    (parted) p                                              

    Sector size (logical/physical): 512B/4096B
    Partition Table: gpt

    Number  Start  End         Size        File system  Name     Flags
     1      2048s  4095s       2048s                    primary  bios_grub
     2      4096s  234441614s  234437519s               primary

      linuxをインストールする。今回はdebootstrapで上記で作成した領域2にインストールした。OSをインストールする領域は2番目(/dev/sda2)になる点に注意。bios_grub領域は確保しておくだけでフォーマットなどをする必要はない。

    # mkfs.ext4 /dev/sda2 # mount /dev/sda2 /mnt/disk 
    # debootstrap --arch amd64 jessie /mnt/disk http://ftp.jp.debian.org/debian
    # mount -o bind /dev /mnt/disk/dev
    # LANG=C chroot /mnt/disk /bin/bash
    (以下chroot下で)
    # proc proc /proc
    # apt-get update
    # apt-get install grub-pc
    (apt-getでのgrubのデバイスへのインストールは実行しないでおく)

    bios_grub領域の中身をチェック。なにも入っていない。
    # dd if=/dev/sda1 of=./bios_grub.img bs=512 count=64
    # od bios_grub.img
    0000000 000000 000000 000000 000000 000000 000000 000000 000000
    *
    0100000

    GRUB2を/dev/sdaにインストールすると、bios_grub領域に書き込まれていることが分かる。
    # grub-install /dev/sda
    # dd if=/dev/sda1 of=./bios_grub_after.img bs=512 count=64
    # od bios_grub_after.img | head
    0000000 164122 000450 004164 137126 100463 046350 057001 172277
    0000020 063201 026613 076603 000010 102017 000351 076200 000377
    0000040 043164 105546 063035 046613 063004 140061 077660 042471
    ...
    ...

    カーネルをインストールして起動できるか確認する。ログインするためのパスワードを設定しておくこと。

    # apt-get install linux-image-amd64
    # update-grub
    # passwd

    (chroot環境から抜けて再起動)

     MBRブートなので起動する機器(起動するディスク)をBIOSで選択すると、その第一セクタにあるブートローダが読み込まれ、それがbios_grub領域にあるGRUB本体をロードしてGRUBがファイルシステム上にあるカーネルを起動する。

    まとめ

    •  Linuxの場合、GUIDパーティションディスクでもMBRブートは可能。
    •  GRUB2の場合bios_grub領域を作成しておく必要がある。
    •  EFI専用パーティションはGPTであってもMBRブートする場合には必要はない。











      

    2014年5月9日金曜日

    Dockerを使ってみました。

    Dockerを使って見ました。

    ちょっとまえにもさわったことがあるのですが、最近流行りのDockerをつかってみました。Dockerとはコンテナタイプの仮想環境に独自の機能を組み合わせて使いやすく便利にしたもののようです。

     コンテナタイプ

    kvmといったいわゆる仮想マシンのようにまるごと物理マシンをエミュレーションした環境を用意するのではく、ネットワークやプロセスIDなどのみが独立した環境(コンテナ)を用意してその中でプロセスを実行するタイプの仮想環境です。仮想マシンと比較すると圧倒的に軽い(ホストに対する負荷が低い)のが特徴みたいです。

    最近またバージョンがあがってDockerの最新版は0.10のようです。

    Dorkerに必要なもの

    カーネル

    Linuxでコンテナ機能を利用するためにはカーネルが以下の機能をサポートしている必要があります。
    • Namespace(名前空間)
    • Cgroups

    Namespace

    ネットワークやプロセスID、ユーザIDなどについてそれらが独立した環境を用意するカーネルの機能です。

    Cgroups

    プロセスにが利用できるリソース(CPUの数やメモリの量)を制限するカーネルの機能です。

     それぞれかなり前のバージョンから一部はサポートされていましたが、Dockerを使うためには必要なすべて機能がそろった3.8以降のカーネルが必要です。
    •   もちろん、カーネルコンフィグで機能が有効になっている必要があります。

    以下の2つはちょっと前までは必要だったのですが、最新バージョンでは必要でなくなったものです。

    Aufsファイルシステム

    Dokerはコンテナのイメージ(ディレクトリツリー)を格納するのにaufsを利用していましたが、aufsは標準カーネルにマージされていないので、利用するためにはパッチをカーネルにあてる必要がありました。そのためDockerはディストリビューションの標準カーネルでは動かないケースがあり、パッケージとしても提供されていないディストリビューションもあったようです。
    • ubuntuなどはaufsパッチの当たっているカーネルを採用していて、パッケージとしてもDockerが提供されていました。
    最近(といってもちょっと前ですが)aufsが必須ではなくなり、ディストリビューションに関係なく動作するようになりました。
    • ただaufsのパッチをカーネルに当てて、aufsとの組み合わせでdockerを利用している人はまだ多いみたいです。

    lxc(linux container)

    Dockerの以前のバージョンではコンテナを作成したりするのに、lxcと呼ばれる(Dockerとは別の)ツールを利用していました。最新のDockerではこの部分がDocker側に実装されてたのでこれも必須ではなくなりました。
    • lxcとの組み合わせで最新版のDockerを動作させることは可能です。

    インストール

    最近のディストリビューションではパッケージとしてDockerが提供されているところも多いようです。その場合は当然上記のカーネルに必要な機能などはすでに満たされているでしょうから、apt-getやyumといったパッケージ管理ツールでインストールすればOKです。

     今回は上記の必要なカーネルの機能やファイルシステムは揃っているとしてパッケージを使わずに直接Dockerをインストールしてみます。

    バイナリインストール

    詳しい説明は以下にかかれています。
      http://docs.docker.io/installation/binaries/
    •  まず、DockerのページからDockerのバイナリをダウンロードしてきます。
      • Docker本体として必要なものはこのバイナリだけです。
      • Dockerに必要な機能(デーモンやコマンドラインインターフェースなど)はすべてこのコマンド+オプションの組み合わせで実行します。
    •  そして、ダウンロードしてきたバイナリをパスの通っている場所に置けば完了!
    # cp docker /usr/local/bin/
     staticなバイナリとなっているので実行するライブラリなども必要ないようです(なのでちょっとサイズは大きい)

     カーネルの条件させ満たしていれば、パッケージなどがなくてもDockerのインストールは簡単です。

    その他インストールするもの

    上記のページによると以下のものもインストールしないといけないようです。
    • iptables
    • git
    • psコマンド
    • xzユーティリティ
    これらは既にインストールされているか、なくてもほとんどのディストリビューションでパッケージとして提供されているとおもいます。

    使ってみる

     さっそくDockerをつかってみます。

    Dockerディレクトリの準備

     /var/lib/dockerがデフォルトでDockerが利用する(イメージやその他を保存する)ディレクトリです。
    • Dockerデーモンの起動時のオプションで別の場所も指定することもできます。中身を特に準備する必要は有りません。ディレクトリだけつくっておきます。
    • 作らなくても初回デーモン実行時に作成されるようです。
      # mkdir /var/lib/docker
     ディストリビューションのパッケージでインストールした場合はインストールの過程ですでに作られていると思います。

    cgroupfsのマウント

    Dockerを利用するためにdockerデーモンを起動する必要がありますが、そのまえにcgroupfsのマウントが必要です。
    •  以前のちょっとさわったときのDockerのバージョンでは以下のように簡単にマウントするだけでOKだった(と思った)のですが、最新のものでは別の方法でマウントする必要があるようです。
      # mount -t cgroup cgroup /sys/fs/cgroup (これではダメ)
    • マウントするためのスクリプトがDockerのページからダウンロードできるのでこれを利用します。
      • https://raw.githubusercontent.com/tianon/cgroupfs-mount/master/cgroupfs-mount
    # wget  https://raw.githubusercontent.com/tianon/cgroupfs-mount/master/cgroupfs-mount
    # sh ./cgroup-mount
    cgroupfsのマウント場所はどこでもいいのですが、/sys/fs/cgroup以下にするのが一般的なようです(スクリプトもそうなっています)

     ディストリビューションのDockerパッケージでインストールした場合、/etc/init.d/の下などに起動用のスクリプトが用意されると思います。それをいつもの方法で起動すれば、適切にcgroupfsのマウントなどを行った後、Dockerデーモンを起動してくれるとおもいます。

    Dockerデーモンの起動

    cgroupfsのマウントが終わったらdockerデーモン起動です。dockerデーモン起動はdockerコマンドを利用します。
    • しつこいようですが、Dockerはこのdockerコマンドしか使用しません。
    # docker -d &
      • -d オプションでデーモンモードで起動します。この際にいくつかのオプション(今回は省略)を指定できます。-d オプションをつけてもバックグラウンドにはならないので、そうしたい場合"&"が必要です。

    コンテナの中身の準備

    これでDockerを実行する準備は整いました。まずコンテナの中身(イメージ)を準備しましょう。コンテナの中身として必要なものは簡単に言えばコンテナ内で実行したいアプリケーションとそれを実行するためのルートファイルシステムです。
    •   アプリケーションを実行するためには、ライブラリや他のコマンドが必要なのでルートファイルシステムをまとめて用意する感じなのだと思います。またapt-getなどのコマンドもあればコンテナ上でそれ実行してアプリケーションをインストールすることもできるでしょう。
    コンテナの中身は1から自分で作成することももちろんできますが、Dockerのオフィシャルレポジトリに様々なコンテナのイメージが登録されているのでそれを利用することもできます(むしろそれを積極的に利用するのが流儀のようです)
    • レポジトリとからベースとなるイメージを取得してきて、それに自分に必要なアプリケーションをインストール(デプロイ)するのがよくあるやり方のようです。
    • アプリケーションまですでにインストールされている(あとは実行するだけの)コンテナのイメージも多数登録されています。
     とりあえず、今回はubuntuのベースイメージを取得してみましょう。
    •  コマンドは"docker pull イメージ名"で取得できます。
    # docker pull ubuntu
    Pulling repository ubuntu
    3db9c44f4520: Download complete
    99ec81b80c55: Download complete
    5e019ab7bf6d: Download complete
    a7cf8ae4e998: Download complete
    316b678ddf48: Download complete
    74fe38d11401: Download complete
    511136ea3c5a: Download complete
    02dae1c13f51: Download complete
    e7206bfc66aa: Download complete
    cb12405ee8fa: Download complete
    f10ebce2c0e1: Download complete
    ef519c9ee91a: Download complete
    e2aa6665d371: Download complete
    5e66087f3ffe: Download complete
    6cfa4d1f33fb: Download complete
    f0ee64c4df74: Download complete
    82cdea7ab5b5: Download complete
    2209cbf9dcd3: Download complete
    5dbd9cb5a02f: Download complete
    07302703becc: Download complete
    cf8dc907452c: Download complete
    4d26dd3ebc1c: Download complete
    d4010efcfd86: Download complete
    • 今回はタグで細かく指定していないので、ubuntuという名前がつくものは全部取得されています。
    • docker pullを実行した際に下記のようなエラーが出る場合、ca-certificatesパッケージをインストールすれば大丈夫のようです。
      Get https://index.docker.io/v1/repositories/ubuntu/images: x509: failed to load system roots and no roots provided
    # apt-get install ca-certificates
      • インストールしたあとはDockerデーモンの再起動が必要です。 

     取得したイメージはdocker imagesで確認できます。
    • イメージはレポジトリ名とタグで表現されていて、イメージIDが同一であれば同じ実体を示しています。
      • 下記の場合、タグがlatest,trusty,14.04のものは同じ実体です。
    # docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    ubuntu              saucy               5e019ab7bf6d        12 days ago         180 MB
    ubuntu              13.10               5e019ab7bf6d        12 days ago         180 MB
    ubuntu              12.04               74fe38d11401        12 days ago         209.4 MB
    ubuntu              precise             74fe38d11401        12 days ago         209.4 MB
    ubuntu              quantal             a7cf8ae4e998        12 days ago         171.2 MB
    ubuntu              12.10               a7cf8ae4e998        12 days ago         171.2 MB
    ubuntu              latest              99ec81b80c55        12 days ago         266 MB
    ubuntu              trusty              99ec81b80c55        12 days ago         266 MB
    ubuntu              14.04               99ec81b80c55        12 days ago         266 MB
    ubuntu              13.04               316b678ddf48        12 days ago         169.4 MB
    ubuntu              raring              316b678ddf48        12 days ago         169.4 MB
    ubuntu              10.04               3db9c44f4520        2 weeks ago         183 MB
    ubuntu              lucid               3db9c44f4520        2 weeks ago         183 MB

    コンテナの実行

    コンテナの実行というよりは、コンテナ上でコマンドを実行するという意味です。
    • コンテナの存在意義のためには実行するコマンド(アプリケーション)が必要です。
    • 今回はとりあえず、コンテナ上でシェル(/bin/bash)を実行してみます。
    • コンテナの実行にはdocker runコマンドを利用します。
    # docker run [オプション] <イメージ名>[:タグ名] [コマンド] [コマンドの引数] ...
    • イメージのタグを省略した場合、"latest"が指定されたものと解釈されます。
    • 下記の例ではubuntu:latest、すなわちubuntu version 14.04のイメージ上でシェルが実行されます。
      • -t オプションは(仮想)端末を開くオプション、-iは標準入力をキープするオプションです。シェルを実行して、入力を受け付けるために指定しています。
    # docker run -t -i ubuntu /bin/bash
    root@9be1af7efb11:/# cat /etc/lsb-release
    DISTRIB_ID=Ubuntu
    DISTRIB_RELEASE=14.04
    DISTRIB_CODENAME=trusty
    DISTRIB_DESCRIPTION="Ubuntu 14.04 LTS"
    •  ubuntu 14.04の環境上でシェル(bash)が実行されているのがわかります。
    実行しているコンテナはdocker psコマンドで確認できます。
    •  別の端末から
    # docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    9be1af7efb11        ubuntu:latest       /bin/bash           5 seconds ago       Up 4 seconds                            stoic_blackwell  

    コマンドの設定

    今回は省略しますが、コンテナのイメージにはそのコンテナ上で実行するコマンド(アプリケーション)をあらかじめ設定することができます。
    •   特にコマンドを指定しないでコンテナを"実行"した場合にはそのコマンドが実行されます。
    • 先ほど、取得してきたコンテナはベースのイメージなので実行するコマンドは設定されていません。
    • コマンドを指定しないで実行するとエラーになります。

    コンテナの停止

    dockerのコンテナは実行時に指定したコマンドが終了するとコンテナも終了します。
    •  コマンドが終了するとコンテナの存在意義もなくなります。
    上記のbashを実行した例では、bashから抜けると同時にコンテナも終了します。

    root@9be1af7efb11:/# exit (上記で実行したシェルを抜ける)
    # docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES




    まとめ

    最新版になってパッケージが提供されていなくても、Dockerをインストールして実行するのはかなり簡単になったようです。カーネルのバージョンと機能の問題はありますが、最近のディストリビューションの最新のカーネルであればあまり問題はないとおもいます。