Docker向けのコンテナをゼロから作ってみよう

Docker向けのコンテナを公開するDocker Hubでは多数のコンテナが公開されており、これらをベースにして独自のコンテナを作成できる。しかし、新規に独自のコンテナを作成したい場合もあるだろう。今回は、新規にコンテナを作成するのに必要な作業手順を紹介する。

Dockerにおける一般的なコンテナ作成手順

Dockerではよく使われるOS環境を含むコンテナがあらかじめ用意されており、それをベースに独自のコンテナを作成できる。たとえば、『これから始める「DockerでかんたんLAMP環境 for CentOS」』記事では、既存のCentOSコンテナをベースに、独自の環境を構築する手順を紹介している。

しかし、公開されているコンテナではなく、ゼロからコンテナを構築したいという場合もある。たとえばDocker Hubで提供されている公式のCent OSのコンテナでは、ファイル容量削減のためか一部のファイルが削除された状態になっており、それらのファイルを参照する処理を実行するとエラーが発生することがある。また、公式には公開されていないようなOS環境のコンテナが必要というケースも考えられる。

こういった場合に向け、Dockerでは「docker import」というコマンドが用意されている。このコマンドは、指定したtarファイルから「Base Image」を作成する処理を行うものだ。

DockerにおけるコンテナとBase Image

「Base Image」という用語は聞き慣れないかもしれないが、これはその名の通りコンテナのベースとなるイメージファイルを指す言葉だ。Dockerのドキュメントの「Image」ページに掲載されている図が分かりやすいが、DockerではUnion File Systemという機構を使い、複数のディスクイメージを組み合わせてコンテナが使用するルートディレクトリを用意している。

(注:一般的にはディスクイメージというとファイルシステムをその構造や制御情報までも含めてファイル化したものを指すが、DockerのImageは単にディレクトリおよびファイルだけを含むもので、厳密にはディスクイメージとは異なるものである)

図1 Dockerのドキュメントで説明されているコンテナのイメージ

 Dockerのコンテナに格納されているファイルやディレクトリはベースとなる「Base Image」と、コンテナに対する変更が差分の形で記録されている「Image」と呼ばれるものに記録されている。Base Imageにはコンテナのルートディレクトリとなる完全なディレクトリツリーが含まれており、これに差分が含まれるImageを順次適用したものが、コンテナの実際のルートディレクトリとなる。

Dockerではコンテナを終了するたびにコンテナに加えた変更が破棄され、もし加えた変更を恒久的に反映させたい場合はコミット処理を実行する必要があるが、このコミットというのは新しいImageを作成することに相当する。こういった構造を取ることで、Dockerではコンテナが変更されたあとも任意のコミット時の状態にすぐに復帰させたり、また過去の任意のコミットの状態から別の状態に分岐させたり、といったことが簡単に行えるようになっている。

「docker import」コマンドでBase Imageを作成する

前述のように、DockerでBase Imageを作成するには、「docker import」コマンドを使用する。詳しくはこのコマンドのmanページを見てほしいが、docker importコマンドはtarでアーカイブ化されたディレクトリツリーをBase Imageに変換するツールだ。たとえば、「container_root」というディレクトリを用意し、これに対し次のように実行すると、このディレクトリをルートディレクトリとするBase Imageとコンテナが作成される。

# mkdir container_root
  
  <ここでcontainer_rootディレクトリに必要なファイルやディレクトリをコピーする>
  
# cd container_root
# tar -c . | docker import - <イメージ名>:<タグ名>

なお、このときタグ名は省略可能だ。

とはいえ、この説明だけでは実際にどういった手順で作業を行えば良いか分かりにくいだろう。そこで、続いては具体的にさまざまなディストリビューション向けのイメージを作成する実例を紹介しよう。

DebianのBase Imageを作成する

まずはDebianやUbuntuといった、Debian系ディストリビューションのBase Image作成方法について説明しよう。Debian系ディストリビューションでは、OSの稼動に最低限必要なファイルなどを指定したディレクトリにインストールする「debootstrap」というコマンドが用意されており、これを使ってOS環境を構築できる。まずはこのdebootstrapをインストールする。

# apt-get install debootstrap

debootstrapでは、引数としてインストールするディストリビューションのリリース名とインストール先ディレクトリ、そしてパッケージのダウンロード先を指定する。

たとえばDebian 7.0のBase Imageを作成したい場合、リリース名は「wheezy」となる。また、パッケージのダウンロード先はミラーサイトなどのURLを指定する。たとえばさくらインターネットが提供しているDebianのミラーサイトを使用する場合、そのURLは「http://debian-mirror.sakura.ne.jp/debian/」となる。この場合、実際に実行するコマンドは次のようになる。

# mkdir debian-wheezy  ←インストールするディレクトリを作成する
# debootstrap wheezy ./debian-wheezy http://debian-mirror.sakura.ne.jp/debian/
W: Cannot check Release signature; keyring file not available /usr/share/keyrings/debian-archive-keyring.gpg
I: Retrieving Release
I: Retrieving Packages
 g[:]
 g[:]
I: Configuring tasksel-data...
I: Base system installed successfully.

debootstrapコマンドを実行すると必要となるパッケージがダウンロードされ、続いて指定したディレクトリにインストールされる。

正しく環境が構築できているかは、chrootコマンドを使ってそのディレクトリをルートディレクトリとしたシェルを実行することで確認できる。

# chroot debian-wheezy /bin/bash
# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  selinux  srv  sys  tmp  usr  var
# cat /etc/debian_version
7.6
# exit

最後に、Debian環境をインストールしたディレクトリに移動してdocker importコマンドを実行することで、Base Imageとそれを使用するコンテナが作成される。

# cd debian-wheezy
# tar -c . | docker import - debian-wheezy
67221db56e0b3f031e999968eaf1a3220501b72e09413f46aa3634e6efab3438

UbuntuのBase Imageを作成する

UbuntuはDebianから派生したディストリビューションであるため、ディストリビューションのリリース名とダウンロード先URLが異なるだけで、Debianとほぼ同じ流れでBase Imageを作成できる。たとえばUbuntu 14.04(Trusty Tahr)のBase Imageを作成したい場合、次のようにdebootstrapを実行すれば良い。

# mkdir ubuntu-trusty
# debootstrap trusty ./ubuntu-trusty http://ftp.jaist.ac.jp/pub/Linux/ubuntu/
# cd ubuntu-trusty
# tar -c . | docker import - ubuntu-trusty

Red Hat Linux系のBase Imageを作成する

Red Hat Enterprise Linux(RHEL)やその互換ディストリビューションであるCentOS、FedoraといったRed Hat Linux系のディストリビューションの場合、debootstrapのような最低限のOS環境に必要なファイルを簡単にインストールするツールは用意されていない。ただし、代わりにyumコマンドを使って似たような作業を行うことは可能だ。具体的には、インストール先のディレクトリを指定するための「--installroot」オプションを使ってインストール先ディレクトリを指定し、また指定したグループに含まれるパッケージをまとめてインストールする「groupinstall」コマンドを使用して「Core」グループのパッケージをインストールすることで、debootstrapと似たような作業を実行できる。

Red Hat Linux系のディストリビューションであればyumコマンドはほぼ標準で含まれているが、Debianなどのディストリビューションの場合は別途インストールしておく必要がある。apt-getコマンドでインストールが可能なので、インストールしておこう。

# apt-get install yum

また、yumコマンドを使ってパッケージのダウンロード元を指定するための設定ファイルも必要だ。Red Hat Linux系ディストリビューションを作業するホストとし、ホストと同一の環境を持つコンテナを作成する場合はホスト側の設定ファイルを流用できるが、ホストとは異なる環境のコンテナを作成したい場合や、Debian系ディストリビューション上で作業する場合などは、作成する環境を明示的に指定するためにも設定ファイルが必要となる。

たとえばx86_64版のCentOS 6.5の場合、設定ファイルは以下のようになる。また、CentOSの別のバージョンのBase Imageを作りたい場合はバージョン番号を表す「6.5」の部分を適宜ほかのものに差し替えることで対応できる。

[main]
cachedir=/var/cache/yum/x86_64/6.5
keepcache=0
debuglevel=2
logfile=/var/log/yum.log
exactarch=1
obsoletes=1
gpgcheck=0
plugins=1

[base]
name=CentOS-6 - Base
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS/6.5/os/x86_64/

[updates]
name=CentOS-6 - Updates
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS/6.5/updates/x86_64/

これを、適当な名前(今回はmy.yum.confとした)で作業用のディレクトリに保存しておく。続いて、「--installroot」オプションでインストール先ディレクトリを指定してyumコマンドを実行する。たとえば/var/tmp/centos6というディレクトリにインストールを行う場合、次のように実行する。

# mkdir /var/tmp/centos6
# yum -c my.yum.conf --installroot=/var/tmp/centos6 -y groupinstall Core

なお、--installrootオプションではフルパスでインストール先ディレクトリを指定する必要がある。このオプションを間違えると、実行中の環境に指定したOS環境が上書きされてしまうので注意したい。

コマンドを実行すると、必要なパッケージがダウンロードされて指定したディレクトリ以下にインストールされる。また、yumコマンドではデバイスファイルの作成は行わないので、それらについては以下のように手動で行う必要がある。

# cd /var/tmp/centos6/dev
# mknod -m 666 null c 1 3
# mknod -m 666 zero c 1 5
# mknod -m 666 random c 1 8
# mknod -m 666 urandom c 1 9
# mkdir -m 755 pts
# mkdir -m 1777 shm
# mknod -m 666 tty c 5 0
# mknod -m 666 tty0 c 4 0
# mknod -m 666 tty1 c 4 1
# mknod -m 666 tty2 c 4 2
# mknod -m 666 tty3 c 4 3
# mknod -m 666 tty4 c 4 4
# mknod -m 600 console c 5 1
# mknod -m 666 full c 1 7
# mknod -m 600 initctl p
# mknod -m 666 ptmx c 5 2

以上で、./centos6ディレクトリ以下にOS環境に必要となるファイルが揃ったことになる。確認のため、chrootコマンドでこのディレクトリをルートディレクトリに変更してシェルを起動してみて、正しく動作するか確認してみよう。

$ chroot /var/tmp/centos6 /bin/bash

最後に、ルートディレクトリとするディレクトリに移動してdocker importコマンドを実行してBase Imageを作成する。

# cd /var/tmp/centos6
# tar -c . | docker import - centos6

作成したコンテナをDocker Hubにアップロードする

以上の手順で作成したコンテナは、Docker Hubからダウンロードしたコンテナと同様に起動したり、またコミットを行ってカスタマイズしたり、Docker Hubにアップロードすることが可能だ。Docker Hubに作成したコンテナをアップロードする手順は前回記事(Dockerコンテナをクラウドサービス上で共有できる「Docker Hub」を使ってみる)で解説しているので、そちらを参照して欲しい。

さらに、Dockerfileを使ってこれらコンテナをベースとする独自コンテナを自動作成させることもできる。ただしDockerfileを利用する場合、Docker Hubに作成したコンテナをアップロードしておく必要がある。

Docker付属のシェルスクリプトを使う

ここまでdocker importコマンドを使ってBase Imageを作成する方法を説明してきたが、DockerのソースコードにはCentOSやDebian、UbuntuなどのBase Imageを作成するためのスクリプトが含まれている。Docker Hubで公開されているCentOSなどの公式イメージはこれらのスクリプトを使って作成されているようだ。

これらは配布されているソースコードのcontribディレクトリ以下に「mkimage-*.sh」という名称で格納されているので、興味のある方は確認してみてほしい。

【関連記事】
>>Dockerコンテナをクラウドサービス上で共有できる「Docker Hub」を使ってみる
>>これから始める「DockerでかんたんLAMP環境 for CentOS」
>>LXCを使った権限分離とテンプレートのカスタマイズ
>>15分で分かるLXC(Linux Containers)の仕組みと基本的な使い方

おしらせ