「docker import」コマンドでDockerコンテナをゼロから作る

図1 Dockerコンテナの構造

 Docker向けのコンテナを公開するDocker Hubでは多数のコンテナが公開されており、これらをベースにして独自のコンテナを作成できる。しかし、新規に独自のコンテナを作成したい場合もあるだろう。今回は、Dockerコンテナにおけるファイル/ディレクトリ情報の格納方法について解説するとともに、新規にコンテナを作成するのに必要な作業手順を紹介する。

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

 Dockerではよく使われるOS環境を含むコンテナがあらかじめ用意されており、それをベースに独自のコンテナを作成できる。しかし、公開されているコンテナではなく、ゼロからコンテナを構築したいという場合もある。

 たとえばコンテナを公開できるリポジトリサービス「Docker Hub」で提供されている公式のCent OSのコンテナでは、ファイル容量削減のためか一部のファイルが削除された状態になっており、それらのファイルを参照する処理を実行するとエラーが発生することがある。

 また、公式には公開されていないようなOS環境のコンテナが必要というケースも考えられる。そこで今回は、Dockerで独自のコンテナを一から作成する手順を紹介する。

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

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

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

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

 Dockerコンテナでは、ファイルやディレクトリなどに対する変更はBase Imageに対する差分という形で記録される。Base Imageにはコンテナのルートディレクトリとなる完全なディレクトリツリーが含まれており、これにその差分を順次適用したものが、コンテナの実際のルートディレクトリになる仕組みだ(図1)。

図1 Dockerコンテナの構造
図1 Dockerコンテナの構造

 Dockerではコンテナを終了するたびにコンテナに加えた変更が破棄され、もし加えた変更を恒久的に反映させたい場合はコミット処理を実行する必要がある。コミットを実行するとその差分が保存され、新たなID(Image ID)が割り振られる。こういった構造によって、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

 ちなみに、docker importコマンドと似たコマンドとして「docker load」コマンドがある。このコマンドは「docker save」コマンドで作成した、タグやバージョン情報までも含んだコンテナイメージファイルを読み込むものだ。docker saveコマンドでコンテナをファイルに書き出した場合、Base Imageとコミットによって記録されたファイルシステムの差分がアーカイブ化されたものが出力される。

 なお、Dockerではイメージをファイルに書き出す機能として「docker export」コマンドも用意されている。こちらはdocker importコマンドの挙動からも推測できるとおり、コンテナのファイルシステムだけをそのままtarコマンドでアーカイブ化したものを出力する。そのため、こちらは新たにBase Imageを作成する目的で使われる。

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を作成するためのスクリプトが含まれている。ソースコードリポジトリのcontrib以下にあるmk-image-*.shがそれだ。

 たとえば、CentOSのイメージは「https://github.com/docker/docker/blob/master/contrib/mkimage-yum.sh」というシェルスクリプトで作成できる。Docker Hubで公開されているCentOSの公式イメージはこれらのスクリプトを使って作成されているようだ。独自のイメージを作成する際には、これらも参考にすると良いだろう。