monolithic kernel

Docker入門時にハマったところ (boot2dockerとDocker Registryの話)

Dockerを使い始めたので、動かしてみる中でハマったポイントについてまとめてみました。 紹介するのはboot2dockerとDocker Registryについてです。

なお、本記事ではOS X 10.9でboot2docker 1.1.0を利用して作業することを想定しています。

boot2docker

boot2dockerは、Dockerコンテナを動作させることを目的に作られた軽量なLinuxディストリビューションです。 VirtualBoxとの連携により、Linux以外の環境でも簡単にDockerを利用できます。 ここでは、DockerコンテナにとってのホストであるVirtualBox内のboot2docker環境をVM、VirtualBoxにとってのホストを単にホストと表記します。 boot2dockerをVirtualBoxで動作させる場合、DockerはVM内で動作しますが、ホストからVM内のDockerデーモンにコマンドを送ることで、ホストのdockerコマンドでDockerを制御できます。

しかし、あくまでもDockerが動作しているのはVM内であるため、DockerコンテナにとってのホストはVMです。 VirtualBoxの設定を行わない限り、ユーザが操作している外側のホストとDockerコンテナの環境は分離されているため、VMの存在を意識していないとハマります。 具体的には、ホストとコンテナでファイルを共有したい場合やSSHポートフォワーディングを行いたい場合に問題になりました。

ホストOSとコンテナでのファイル共有

docker runの-vオプションでディレクトリをマウントしてファイルを共有する場合、共有はDockerコンテナにとってのホストであるVMとDockerコンテナ間で行われます。 そのため、オプションの引数ではホストではなくVMでのパスを指定する必要があります。 単なる永続化ではなく、ホストとコンテナでファイルを受け渡しすることを目的とするのであれば、VMとコンテナの共有に加えて、VMとホストでのファイルの受け渡しも必要です。

といっても、いちいちファイルを受け渡しするのは面倒なので、VirtualBoxの共有フォルダ機能を利用するという手もあります。 ホストとVMで共有フォルダを設定した上で、その場所を-vオプションで指定してコンテナと共有することで、擬似的にホストとコンテナ間でのファイル共有ができるというわけです。

ただし、公式に配布されているboot2dockerのディスクイメージにはVirtualBox Guest Additionsが含まれていません。 そのままでは共有フォルダを利用できないため、Guest Additionsを含むディスクイメージを用意する必要があります。

どこかに転がっているイメージを探してきてもいいのですが、以下のページの手順に従うことで、Guest Additionsを含むディスクイメージを簡単に作れます。 なお、手順ではシェルスクリプトがVirtualBoxのバージョンを検出してDockerfileを生成するようになっていますが、手で$VBOX_VERSIONを置換してしまえば、VirtualBoxがインストールされていない、あるいは実際にディスクイメージを使う環境とは異なる環境で実行することも可能です。

共有フォルダの設定やマウントの手順は普通にVirtualBoxを使う場合と同様です。 以下の例では、VM上のホストと全く同じパスにホストのディレクトリをマウントすることで、-vオプションを指定する際にホストのパスを指定する感覚で使えるようにしています。

VBoxManage sharedfolder add boot2docker-vm -name shared -hostpath $HOME/work
boot2docker ssh "sudo sh -c 'modprobe vboxsf && mkdir -p $HOME/work && mount -t vboxsf -o uid=1000,gid=1000,dmode=755,fmode=644 shared $HOME/work'"

また、この例ではマウント時のUID、GIDおよびパーミッションを指定しています。 コンテナ内でrootとして共有フォルダを参照するのであればこれらの指定は不要ですが、そうでない場合には、コンテナ内でファイルを操作するユーザがアクセスできるようにUID、GIDおよびパーミッションを適切に設定する必要があります。

あまりないとは思いますが、共有フォルダにはchmodやchownは効かないので、コンテナでこういった操作を行っている場合には正しく動作しない原因になります。 対象方法はたぶんありませんので、諦めましょう。

SSHポートフォワーディング

ネットワーク周りもVMの存在を意識しなければならないポイントのひとつです。 コンテナやDockerデーモンにSSHポートフォワーディングを行って外部と通信させたい場合、ホストでsshを起動するだけでは通信できません。 VirtualBoxのポートフォワーディングの設定を行ってVMのポートをホストにフォワーディングするか、ホストではなくVMでsshを起動してポートフォワーディングする必要があります。

# VMに入る
boot2docker ssh
# VM内でポートフォワーディング
ssh -N -L 5000:localhost:5000 remote

Docker Registry

作成したDockerイメージを別のマシンとやりとりする場合、Docker Registryを利用します。 イメージを作成した環境でdocker pushし、利用したい環境でdocker pullするといった具合です。 公式のRegistryとしてDocker Hub Registryが存在するのですが、プライベートリポジトリの作成は有料で、結構いいお値段です。 私はどうしてもお金を払いたくなかったので、面倒ですが自分でDocker Registryを構築しました。

Docker RegistryのDockerイメージは公開されているため、docker pullしてdocker runするだけで利用できます。

docker pull registry
docker run -p 5000:5000 registry

永続化のための細かい設定については、GitHubを参照してください。 ただ、デプロイのための一時的な置き場として使うのであれば、永続化は必ずしも必要ではないかもしれません。

認証

閉じていないネットワーク上にDocker Registryを構築するのであれば、認証を行う必要があります。 Docker Registry自体は認証機能を持たないので、外部からDocker Registryへの直接のアクセスを禁止した上で、何らかの認証機構を挟むことになります。 DockerクライアントはHTTPのBasic認証に対応しているため、Basic認証を利用するのが一般的だろうと思われます。 これは、Docker Registryの手前にApacheやnginxをリバースプロキシとして配置し、そこでBasic認証を行うことで実現可能です。

ただし、DockerクライアントはSSLなしでのBasic認証を受け入れないようになっているため、サーバ側ではSSLを有効にしなければなりません。 読者の皆さんはSSL証明書をお持ちだと思うので、特に問題はないかもしれませんが、残念ながら私は持っていないため、自己署名証明書を使うしかありません。 Dockerクライアントは証明書の正当性をきちんとチェックする実装になっているため、サーバ側で自己署名証明書を適当に設定しただけでは通らず、クライアント側でサーバの自己署名証明書を受け入れるようにする設定も必要です。 私はこのあたりで面倒になって断念し、SSHポートフォワーディングを使う方法に切り替えました。

SSHポートフォワーディングを行う場合、クライアントとサーバの間でSSHを利用しているのであれば、新たに何かを設定する必要はありません。 お手軽なので、自分ひとりで作業する場合にはこちらで十分でしょう。 ただし、上で述べたようにVMとホストの違いを意識する必要がある点には注意してください。

おわりに

以上、Dockerを使ってみた中でハマったポイントについて紹介しました。 次回は、Dockerを使い始めた狙いや使ってみた感想などを紹介できればと思います。