読者です 読者をやめる 読者になる 読者になる

SideCI Blog

自動コードレビューサービスSideCIを提供している株式会社アクトキャットのコーポレートブログです。



Dockerfileの書き方と気をつけたほうが良いこと

Dockerはみなさんご存知でしょうか?LXC (LinuX Containers)と言われるものです。何が出来る技術か?というと、仮想環境(VM)を作る事が出来る技術です。このDockerfileについて書こうと思うのですが、最初にdockerについてざっくりだけ紹介。

VirtualBoxとかVMWareとかとそれほど変わらないですね。Vagrantとかも流行ってますが、VagrantはVirtualBoxと合わせて使う、それらをより便利にするツールです。VagrantfileってのがVagrantにはあって、

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # ...
end

なんかこう、こんな感じに、仮想環境に事前設定して、

$ vagrant init
$ vagrant up
$ vagrant ssh

とかで仮想環境を立ち上げて、vagrant haltで停止させたりとか出来ます。 ただ、もちろん、既存のVirtualMachine技術の上で動いているので、パソコンを1台エミュレーションしてるようなもので、起動はまぁそこそこ時間が掛かるし、エミュレーションしている分、余分にCPUなども消費します。

LXC技術はVMのそれらの欠点を欠点を解決した新しい目の技術です。2013年ごろから登場。DockerというOSSがLXCの中では最も注目されています。

dockerのイメージ図

くじらさんのロゴが可愛いです。くじらさんの上にコンテナがいっぱい載ってますね。

docker公式サイトのイメージ紹介図

イメージ図

LXCはDockerをインストール、動作させているコンピュータ上のKernelの上で動きます。PC本体のエミュレーションは行わない。なので、起動も一瞬。終了も一瞬。

  • Base Image
    • Image(commit_id: 1)
      • Image2(commit_id: 2)
      • Image3(commit_id: 2)
        • Image4(commit_id: 3)

↑こんなかんじに、まるでGitのレポジトリのように、マシンの状態をcommitしていく、バージョン管理していくことも出来ます。このImageは電源が切れた状態のイメージではないです。

で、起動する時は、 $ docker run IMAGE_ID 実行コマンド といった感じのコマンドでImageからContainerを作ることが出来ます。このContainerがVMでいうVM(マシン)です。

で、Dockerfileってのが、

# Memcached
#
# VERSION       1.0

# use the ubuntu base image provided by dotCloud
FROM ubuntu

# make sure the package repository is up to date
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update

# install memcached
RUN apt-get install -y memcached

だいたいこんな感じでして、 FROMでベースとなるイメージを設定して、そのイメージをコンテナとして起動した後に実行すべきコマンド群、設定群などを記述するファイルです。FROMの部分は自分で作成したイメージなんかも指定出来ます。 その他、docker indexでたくさんのイメージが公開されています。 ただ、イメージはあんまり説明がなかったり、何をしてあるイメージなのか分からなかったりで、あまり役には立たないかも。。。中に何が入ってるか分からないイメージなんて使いたくないですよねw。 半分ぐらいはこのイメージを作るためにこのDockerfileを使いましたー、みたいなのが書いてあったりします。

残りの半分のイメージはどうやって作ったんだろう?ってのは、だいたいは、

  1. ubuntuをベースにコンテナを起動 sudo docker run -i -t ubuntu /bin/bashで起動してbashを起動
  2. apt-getとかでちょこちょこアップデートする
  3. 適宜commit

みたいな感じで、Dockerfileではなく、コンテナ内で各種コマンドを実行して、保存して、docker indexにpushして登録しているのかなと。

全部Dockerfileに書けばスマートなイメージが作れてきれいだよね! とか私も最初は思ってたんですが、これは良くなかったなー、時間の無駄だったなーと。。。

Dockerfileをimageにする時は sudo docker build -t IMAGE_NAME . な感じで、Dockerfileでベースに指定したイメージに、Dockerfileで書いたコマンドをバンバン実行していって、イメージが出来上がります。ただ、このDockerfileの中で実行するコマンドってrubyのインストール(つまりrubyのビルドとか!)なんですよね。めっちゃコマンド1個1個が重たいんです。

そして、どこかのタイミングでコマンドがエラーはくと、イメージの生成が止まっちゃうんです。まぁ、当たり前ですが。一応、そのエラーはいた時点以降の行を弄って、再度実行したりすると、前回までの処理の部分はスキップしてくれたりします。

たとえそれでも、ruby言語自体のインストールとかまぁめちゃめちゃCPUタイム使う処理が入ってるのは間違いないわけで。Dockerfileをbuildしたあと、イメージからコンテナ起動して、中身確認したらいまいち上手くソフトがインストールできてなかったり(依存関係とかで)すると、もう一回Dockerfileを最初からbuildし直し(rubyまたインストール!?)になっちゃいます。

というわけで、rbenvでいっぱいのバージョンのrubyを入れた環境をつくろうと思ったら、丸一日潰してしまいました。。。

Dockerfileを作るためのコツは、

  1. Dockerfileを最初から完璧にしようと思わない
  2. ubuntuなどの公式ベースイメージでコンテナ起動して/bin/bashで普通にコマンドラインでちょこちょこ入れて設定していく
  3. 設定していく手順は一応メモっておく
  4. メモっておいた手順をDockerfileに書いてみる
  5. buildしてみる
  6. build成功したらせっかくだからdocker indexにpushしてあげよう、Dockerfileの説明付きで

という気がしてます。これからDocker触るぞ!と思う人はぜひどうぞ!w