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をインストールして実行するのはかなり簡単になったようです。カーネルのバージョンと機能の問題はありますが、最近のディストリビューションの最新のカーネルであればあまり問題はないとおもいます。