今更ながらdockerメモ

目次

サマリ

dockerをさわってみたのでメモ。

参考文献

ていねいなユーザマニュアルが標準装備なのはたいへんありがたいですね。

docs.docker.com

dockerのインストール

Ubuntuだとaptで入れられるのでらくちんですね。 なお、以下のバージョンしか公式にサポートしていない模様。 さらに、カーネルは3.10以上じゃないとダメ。

  • dockerがサポートしているUbuntuのバージョン
  • dockerがサポートしているカーネルのバージョン
    • 3.10 以降

以下、手順。僕の環境はUbuntu 14.04。 ログアウトは、端末を開き直すだけじゃダメ。

sudo apt-get install linux-image-extra-$(uname -r) は、 aufsを使うためだけっぽいのでいらないと思う。

# aptがhttpsを使えるように
$ sudo aptitude update
$ sudo aptitude install apt-transport-https ca-certificates

# aptのGPG keyを追加
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

# /etc/apt/sources.list.d/docker.listをまっさらにして作りなおす
$ echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" > /etc/apt/sources.list.d/docker.list

# aptのパッケージをアップデート
$ sudo aptitude update

# lxc-dockerをすでに入れていたらpurgeしておこう
$ sudo aptitude purge lxc-docker

# aptが正しくリポジトリを認識ているか確認しよう
$ sudo apt-cache policy docker-engine

# インストール!
$ sudo apt-get install docker-engine

# Dockerグループに自分をいれておくと、sudoしなくてもdockerコマンドをたたけるようになるので
# いれておく
$ sudo gpasswd -a $USER Docker

# 一旦ログアウトしよう
$ logout

dockerでhello world

docker run hello-worldして、以下のメッセージが出てきたら成功。 最初にDocker Hubからhello-worldイメージを取ってきて、 そこから起動するみたい。

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
03f4658f8b78: Pull complete 
a3ed95caeb02: Pull complete 
Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
Status: Downloaded newer image for hello-world:latest

Hello from Docker.
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/userguide/

Docker Hubからdocker imageを取得して動かす

GitHubみたいに、docker imageを置いておくことができる Docker Hubというものがある。 公式リポジトリがいくつかあるようなので、そこから試しにubuntuを取得してみる。 docker pull <リポジトリ>:<タグ>でdocker imageを取得できる。 <タグ>を省略すると、最新版を取得する。

例えば、以下の例では、ubuntuリポジトリのタグ"14.04"を取得する。

$ docker pull ubuntu:14.04

取得したイメージはdocker imagesで見られる。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              14.04               07c86167cdc4        2 days ago          188 MB
hello-world         latest              690ed74de00f        4 months ago        960 B

取得したものは、docker runで動かすことができる。 取得しなくとも、docker runを打つと、 勝手にDocker Hubに探しに行って取得してくれるので便利。 プログラムを動かしたままコンテナから脱出するにはCtrl-p Ctrl-q。 logoutしてしまうと、コンテナが停止してしまう。

# 単に動かす
$ docker run ubuntu echo "hello world"
### ubuntuイメージがない場合はDocker Hubから勝手に取ってきてくれる
hello world

# backgroundで動かす
$ docker run -d ubuntu echo "hello world"
### ubuntuイメージがない場合はDocker Hubから勝手に取ってきてくれる
hello world

# -itをつけると、対話型のプログラムも動かせる。
$ docker run -it ubuntu /bin/bash
root@44d395e6298d:/#

# コンテナに名前をつける
$ docker run -it --name bash_test ubuntu /bin/bash 

コンテナのリストを取得するのは、docker ps docker ps -aで、止まっているコンテナも見ることができる。

NAMES(コンテナの名前。以後コンテナ名とする)はDocker Engineが勝手につけてくれる(もちろん自分でつけてもよい)。 動かしたり止めたりするときに、コンテナIDを指定してもよいし、コンテナ名を指定してもよい。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS                      PORTS               NAMES
44d395e6298d        ubuntu:14.04        "/bin/bash"            13 seconds ago      Exited (0) 3 seconds ago                        sharp_panini
b4a331a7280d        ubuntu:14.04        "echo 'hello world'"   26 seconds ago      Exited (0) 25 seconds ago                       mad_hugle
a627000187a6        hello-world         "/hello"               8 minutes ago       Exited (0) 8 minutes ago                        nostalgic_bhabha

お作法としては、コンテナは使い終わったら破棄。 停止しているコンテナの破棄は、docker rmでできる。

# sharp_paniniを破棄
$ docker rm sharp_panini
sharp_panini 

# いちいち指定するのが面倒なので全部破棄
$ docker rm $(docker ps -a -q)

# --rmをつけてrunすると、終了後にdocker rm してくれる
$ docker run --rm -it ubuntu /bin/bash

オレオレdocker imageを作る

直感的なやり方

いちばん直感的なのは、コンテナ起動して、必要なプログラムをインストールした後、コミットするやり方。 例えば、sysstatをインストール済みのコンテナを作るとすると、こんな感じになる。

$ docker run -it ubuntu:14.04 /bin/bash
root@cd9379115537:/# sudo aptitude install sysstat
root@cd9379115537:/# exit
# -m はコミットログ。 -a は変更作成者。
# nbisco/sysstatというイメージを、testタグをつけて作成。
$ docker commit -m "Added sysstat" -a "nbisco" \
cd9379115537 nbisco/sysstat:test

Dockerfileを使うやり方

Dockerfileを使うと、いちいちコンテナ起動しなくても docker imageを作ることができる。

何はともあれ、まずはDockerfileを作る。

$ mkdir test 
$ cd test
$ touch Dockerfile

Dockerfileを作ったら、以下を書き込む。 何のことはない、単にやってほしいことを書くだけだ。

# コメント行
FROM ubuntu:14.04 # どのイメージをベースにするか
MAINTAINER nbisco <XXXX@XXXX.XXX>  # イメージのメンテナはだれか
RUN aptitude update && aptitude install -y sysstat # コンテナ起動後に実行するコマンド

Dockerfileを作ったら、以下のコマンドでイメージ作成。

# 最後の引数はDockerfileへのパスを指定すること。dotなので見落としがち。
$ docker build -t nbisco/sysstat:dev . 

作ったimageは同じようにdocker run nbisco/sysstat:devで起動できる。 らくちん。

Dockerのネットワーク機能

コンテナにおけるネットワークは、(1)何とも繋がないnone、(2)ホストオンリーのhost、 (3)他コンテナやホストと繋げられるbridgeの3種類に大別できる。 デフォルトはbridge。

ネットワークは、以下のコマンドで確認できる。

$ docker network ls
NETWORK ID          NAME                DRIVER
18a2866682b8        none                null                
c288470c46f6        host                host                
7b369448dccb        bridge              bridge  

ネットワークの状況は、docker network inspect <network名>で調べられる。 ※特に何も繋がっていない状況なので、"Containers"が空になっている。

$docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "157fb4893818ff6e8fa7512b142b9e97df007f042d61f5bbc7bbdfbe9c9da2cb",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        }
    }
]

動かしているコンテナのネットワークを切り離すこともできる。

$ docker run -d --name networktest training/webapp python app.py
$ docker network disconnect bridge networktest

専用のbridgeネットワークを作って、コンテナ同士をつなげたりもできる。

# コンテナスタート時にネットワークを指定する場合
$ docker network create -d bridge my-bridge-network 
$ docker run -d --net=my-bridge-network --name network_test1 ubuntu
$ docker run -it --net=my-bridge-network --name network_test2 ubuntu /bin/bash
root@047745a86ac1:/# ping network_test1
PING db (172.18.0.3) 56(84) bytes of data.
64 bytes from db.my-bridge-network (172.18.0.3): icmp_seq=1 ttl=64 time=0.101 ms
64 bytes from db.my-bridge-network (172.18.0.3): icmp_seq=2 ttl=64 time=0.063 ms

# 一旦止めれば、ネットワークに接続させることもできる
$ docker run -it --name network_test3 ubuntu /bin/bash
★IPが172.18.0.0と別セグメントになっているので、届かない。
root@51057043d3ff:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:25 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4814 (4.8 KB)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

$ docker stop network_test3
$ docker network connect my-bridge-network network_test3
$ docker start network_test3
$ docker exec -it network_test3 /bin/bash
root@51057043d3ff:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:26 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4825 (4.8 KB)  TX bytes:648 (648.0 B)

# ★もう1個できて、network_test1/2とつながるようになった
eth1      Link encap:Ethernet  HWaddr 02:42:ac:12:00:04  
          inet addr:172.18.0.4  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe12:4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:26 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4825 (4.8 KB)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Dockerにおけるデータ管理

コンテナ起動時に-vオプションをつけると、コンテナイメージに含まれない ボリュームを作ることができる。例えばこんな感じ。

$ docker run -d -P -v /testweb --name web training/webapp python app.py
$ docker exec -it web /bin/bash
root@fc0beb95b345:/opt/webapp# ls /
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  testweb  tmp  usr  var
root@fc0beb95b345:/opt/webapp# 

ボリュームの場所は、docker inspectで調べられる。 ボリュームと言っても、単なるディレクトリに見えるので、作ったデータを取り出すのは簡単。

$ docker inspect web | less
()
        "Mounts": [
            {
                "Name": "08375e69b62c71792953d2dc83ac735c1c5cb8de63ac655557098f9446b95936",
                "Source": "/var/lib/docker/volumes/08375e69b62c71792953d2dc83ac735c1c5cb8de63ac655557098f9446b95936/_data",
                "Destination": "/testweb",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

()

$ docker exec -it web /bin/bash
docker exec -it web /bin/bash
root@fc0beb95b345:/opt/webapp# touch /testweb/test.txt
$ ls /var/lib/docker/volumes/08375e69b62c71792953d2dc83ac735c1c5cb8de63ac655557098f9446b95936/_data
test.txt

既存のホストディレクトリをマウントすることもできる。 lsするとサブディレクトリ以下まで普通に見えてしまう。

$ mkdir /home/nbisco/test_docker
$ touch /home/nbisco/test_docker/test.txt
$ mkdir /home/nbisco/test_docker/test_docker_sub
$ docker run -d -P -v /home/nbisco/test_docker:/test_docker --name web training/webapp python app.py
$ docker exec -it web /bin/bash
root@fce1df482e8d:/opt/webapp# ls /test_docker
test_docker_sub test.txt 

ディレクトリだけでなく、ファイルもマウントできちゃう。 わざわざdotfileをcloneしたりしなくて済むね。

$ docker run --rm -it -v ~/.bash_history:/root/.bash_history ubuntu /bin/bash

もちろん、他のコンテナとディレクトリを共有することもできる。 Docker Userguideには、/dbdataを共有する例が載ってる。 コンテナ間でディレクトリを共有すると、バックアップも手軽にできちゃう。

$ docker create -v /dbdata --name dbstore training/postgres /bin/true
$ docker run -d --volumes-from dbstore --name db1 training/postgres
$ docker run -d --volumes-from dbstore --name db2 training/postgres

# バックアップの例
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

# バックアップから復元したデータでコンテナを動かす
$ docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

以上、ざっくり基本動作のメモでした。