dotCloud製Linux Container, Docker触ってみた

http://www.docker.io

とりあえず、VM一個立てて、rackupしようと思った。

digitaloceanのvpsで試していたんだけど、なんかおかしいと思ったらkernelのバグの影響だったようで、3.5.0-21以上を使ったほうが良さそう。 https://github.com/dotcloud/docker/issues/25

ちなみにdigitaloceanのvpsでは

$ uname -r
3.5.0-17-generic

なので、Vagrantで適当なUbuntuのVM立てた。

def v10(config)
  config.vm.box = "ubuntu-quantal"
  config.vm.box_url = "http://cloud-images.ubuntu.com/quantal/current/quantal-server-cloudimg-vagrant-amd64-disk1.box"
end

Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
  v10(config)
end

Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
  v10(config)
end

Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
  config.vm.provider :virtualbox do |vb|
    config.vm.box = "ubuntu-quantal"
    config.vm.box_url = "http://cloud-images.ubuntu.com/quantal/current/quantal-server-cloudimg-vagrant-amd64-disk1.box"
  end
end
$ uname -r
3.5.0-26-generic

Install

$ sudo apt-get install lxc wget bsdtar curl
$ sudo apt-get install linux-image-extra-`uname -r`
$ wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz
$ tar -xf docker-master.tgz

基本(READMEに書いてる)

僕は当初Containerとimageを混同していて、$ sudo ./docker pull IMAGE_NAME でVMのようなものを作っているのかと思ってたが、そうではなくimageはコンテナのひな形、インスタンスに対するクラスのようなもの。また、runで新しくコンテナを作ってコマンドを実行しても、コマンドが終了すればコンテナは消える。

# イメージのダウンロード
$ sudo ./docker pull IMAGE_NAME
# コンテナ上でコマンドを実行
$ sudo ./docker run -i -t IMAGE_NAME COMMAND
# shell立ち上げることで、普通にそのコンテナにログインして作業みたいなこともできる
$ sudo ./docker run -i -t IMAGE_NAME /bin/bash

# Container の IDに対して操作出来る
$ (sudo ./docker -d || echo "Docker daemon already running") &
$ CONTAINER=$(docker run -d base /bin/sh -c "while true; do echo Hello world; sleep 1; done")
$ echo $CONTAINER
2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1
$ docker logs $CONTAINER
2013/03/29 07:33:54 docker logs 2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
$ docker logs $JOB
2013/03/29 07:33:56 docker logs 2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
$ docker kill $JOB
2013/03/29 07:34:23 docker kill 2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1

# runはコンテナを新しく起動してコマンドを実行するもので、
# 永続的なコンテナを作るものではないので、コマンドの実行が終わると
# コンテナは消える。
# コマンドの実行によりコンテナに与えた変更はcommitすることにより
# 新たなimageが作成できる。
$ sudo ./docker run -i -t base bash
root@ea19df583e44:/# adduser toqoz
Adding user `toqoz' ...
Adding new group `toqoz' (1000) ...
Adding new user `toqoz' (1000) with group `toqoz' ...
Creating home directory `/home/toqoz' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for toqoz
Enter the new value, or press ENTER for the default
        Full Name :
        Room Number :
        Work Phone :
        Home Phone :
        Other []:
        Is the information correct? [Y/n]
root@ea19df583e44:/# su toqoz
bash: no job control in this shell
$ exit
exit
root@ea19df583e44:/# exit
exit
$ sudo ./docker run -i -t base bash
2013/03/29 08:08:37 docker run -i -t base bash
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@b612c0b3d72d:/# su toqoz
Unknown id: toqoz

自分用のbase imageを作成

$ sudo ./docker pull base
$ CONTAINER=$(docker run -d base apt-get install -y curl)
$ docker commit -m "Installed curl" $CONTAINER $USER/sample
$ docker push $USER/sample
$ docker images
2013/03/29 08:16:58 docker images
REPOSITORY          TAG                 ID                                                                 CREATED             PARENT
vagrant/sample        latest              c287f15311bfbb0c2e664f5e29561d5f8e03567b7a9f640140106213bbc6fa9d   45 seconds ago      base:latest
base                latest              b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545
base                ubuntu-12.10        b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545
base                ubuntu-quantal      b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545
base                ubuntu-quantl       b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545

# ps でコンテナを探してattachすることもできる
$ sudo ./docker ps
2013/03/29 09:12:28 docker ps
ID                                                                 IMAGE         COMMAND                CREATED          STATUS          COMMENT
307304dc7bd498f7ee3b8e76e3bbc492b8b1a7c698fb7e100c3b4d331e108d99   base:latest   apt-get install -y r   5 minutes ago    Up 5 minutes
61b385d5cd8ce70d3e6df7a256777aacdebf3cebb483285f50f006d6f0c17e00   base:latest   echo hello             21 minutes ago   Up 21 minutes
7ddd667b4edf3c603b89027a2f2f731b64c6b34487e2a03ab96daa4af3504cbe   base:latest   echo hoge              24 minutes ago   Up 24 minutes
$ sudo ./docker attach 307304dc7bd498f7ee3b8e76e3bbc492b8b1a7c698fb7e100c3b4d331e108d99
$ sudo ./docker commit -m 'dist-updated' $CONTAINER $USER/base
# sudo ./docker run some_image apt-get update && apt-get install foo が
# (sudo ./docker run some_image apt-get update) && apt-get install foo みたいな感じに解釈されてしまうので、とりあえず sh -c '' で実行してる
$ CONTAINER=$(sudo ./docker run -d base sh -c '\
  export DEBIAN_FRONTEND=noninteractive && \
  echo "deb http://archive.ubuntu.com/ubuntu quantal-updates main universe multiverse" >> /etc/apt/sources.list && \
  apt-get -y update && \
  apt-get -y upgrade && \
  apt-get -y dist-upgrade \
  apt-get install -y build-essential openssl libreadline6 libreadline6-dev curl git-core' \
)

$ sudo ./docker commit -m 'Installed build-essential openssl libreadline6 libreadline6-dev curl git-core' $CONTAINER $USER/base

自分用のbase imageをベースにruby用のimageを作る

$ CONTAINER=$(sudo ./docker run -d $USER/base sh -c '\
  export DEBIAN_FRONTEND=noninteractive && \
  apt-get install -y libssl-dev zlib1g-dev libyaml-dev && \
  cd /tmp && \
  git clone git://github.com/sstephenson/ruby-build.git && \
  cd ruby-build && PREFIX=/opt/ruby-build ./install.sh && \
  /opt/ruby-build/bin/ruby-build --with-open-ssl-dir=usr 2.0.0-p0 /opt/ruby2.0.0'
)
$ sudo ./docker commit -m 'Installed Ruby' $CONTAINER $USER/rubybase
$ sudo ./docker run $USER/rubybase env PATH=/opt/ruby2.0.0/bin:$PATH ruby -v
2013/04/01 01:19:08 docker run vagrant/rubybase env PATH=/opt/ruby2.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games ruby -v
ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]

ruby用のimageをbaseにrack app用のimageを作る

$ CONTAINER=$(sudo ./docker run -d $USER/rubybase env PATH=/opt/ruby2.0.0/bin:$PATH gem install rack --no-ri --no-rdoc
$ sudo ./docker commit -m 'Installed rack' $CONTAINER $USER/rackbase

rack appを起動するしてアクセスしてみる

$ CONTAINER=$(sudo ./docker run -d -p 9292 $USER/rackbase \
  env PATH=/opt/ruby2.0.0/bin:$PATH \
  sh -c 'rackup -b "run proc { |env| [200, { %{Content-Type} => %{text/html} }, %{Hello\n}.chars] }"' \
)

$ sudo ./docker logs $CONTAINER
2013/04/01 05:42:48 docker logs baa621dd4def
[2013-04-01 05:42:38] INFO  WEBrick 1.3.1
[2013-04-01 05:42:38] INFO  ruby 2.0.0 (2013-02-24) [x86_64-linux]
[2013-04-01 05:42:38] INFO  WEBrick::HTTPServer#start: pid=5 port=9292
$ PORT=$(sudo ./docker port $CONTAINER 9292)
$ HOST=$(ifconfig | grep 'inet ' | head -n1 | sed -e 's/^.*inet addr://' -e 's/ .*//')
$ curl $HOST:$PORT
Hello

実際にprivate paasみたいな感じで使うには

コンテナの存在をチェックして、再起動する仕組みと、ルーターを作れば、とりあえず簡素なの作れる気がした。

環境構築の自動化について

imageをpushすることも出来るので、chefなどを使った自動化というより、dockerのエコシステムの中でうまく自動化していくのがいいんじゃないかなと思ったりした。ただ現状、そのimageがどのように作られたかとかを知る術を僕は知りません。