ブログ

ryuzeeによるブログ記事。不定期更新
アジャイル開発に取り組むチーム向けのコーチングや、技術顧問、認定スクラムマスター研修などの各種トレーニングを提供しています。ぜひお気軽にご相談ください(初回相談無料)

5分で分かるDockerのキホン

全国100万人のImmutable Infrastructure職人のみなさんこんにちは。 もう誰も彼もがDockerなので、あんまりブログに書こうという気にもならなかったのですが、知り合いからリクエストを貰ったので、5分くらいで分かるようにかいつまんで概略を説明します。

Dockerとは

  • 詳しくは本家サイト見ればだいたい分かる。
  • 仮想化技術
  • コンテナ単位でパッケージング
    • VirtualBoxとかと違って高速、オーバーヘッドが少ない。chrootに近い。LXCには依存しなくなっている
    • コンテナごとにIDが振られる
    • コンテナは差分保存なのでロールバックも簡単
  • 一回作ればどこでも動く。Javaっぽい
  • Dockerfileでコンテナを作成する
    • Dockerfileの1行ごとにコンテナIDがフラれる
  • 動作環境
    • Linux Kernel 3.8以降 64bit OS
    • Macの場合はVirtualBoxの中で動かす形になる→ boot2docker
    • Windowsの場合もVirtualBoxを使って仮想マシンの中にDocker環境を作ることになる。
  • 何がうれしいの?
    • 高速に起動する。したがってCI用に使ったり、Chefのクックブックの実験環境に使ったり、開発環境に使ったり色々便利。もう本番環境で使っている例も多数
    • コンテナ内に閉じ込めることでポータビリティがあがる。アプリケーションのデプロイ戦略が楽な方に大きく変わりうる
    • 差分で管理されるので配布と再利用が簡単
    • いろんな環境で動く
    • Dockerfileを使ってコードでインフラを管理できる

Ubuntu 12.04 LTS 64bitで動かす

まずKernelを3.8に変更する

sudo apt-get update
sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring
sudo reboot

aptレポジトリを追加してインストール

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
sudo sh -c "echo deb https://get.docker.io/ubuntu docker main /etc/apt/sources.list.d/docker.list"
sudo apt-get update
sudo apt-get install lxc-docker

とりあえずDockerを使ってUbuntuのコンテナを起動する

sudo docker run -i -t ubuntu /bin/bash

初回なのでたくさんイメージをダウンロードするのでちょっと時間がかかるはず。終わると以下のようにログインできている

Unable to find image 'ubuntu' locally
Pulling repository ubuntu
316b678ddf48: Download complete 
a7cf8ae4e998: Download complete 
3db9c44f4520: Download complete 
()
cb12405ee8fa: Download complete 
4d26dd3ebc1c: Download complete 
d4010efcfd86: Download complete 
root@6485cdbce682:/# 

この状態だと、インタラクティブシェルで、exitするとプロセスが終了する。

どのコンテナが起動しているかを確認する

sudo docker ps

として確認すればOK。(ps -a だと停止済のコンテナも含む)

コンテナ自体を削除する場合

以下のようにする(末尾のパラメータはコンテナID)

sudo docker rm 04b26990125f

複数のコンテナをまとめて停止したり削除する

以下のように引数にコマンド実行の結果を渡せばOK。

sudo docker stop `sudo docker ps -aq`
sudo docker rm `sudo docker ps -aq`

独自のイメージを作る

適当に変更を加えたあとにログアウトし、

sudo docker commit コンテナID 適当な名前

のようにする。作ったものを確認するには

sudo docker images 名前

として一覧に出ることを確認すればOK

たとえば

sudo docker commit eb0dd1195df2 ryuzee/apache2

これができれば

sudo docker run -i -t ryuzee/apache2 /bin/bash

にように今度はこれを使うことができる。なお、イメージの名称は、ユーザー名/中身 という形式にするのが推奨

イメージの一覧を取得する

sudo docker images

とすることでイメージの一覧が取得できる。

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              13.10               5e019ab7bf6d        4 weeks ago         180 MB
ubuntu              saucy               5e019ab7bf6d        4 weeks ago         180 MB
ubuntu              12.04               74fe38d11401        4 weeks ago         209.6 MB
()
ubuntu              10.04               3db9c44f4520        4 weeks ago         183 MB
ubuntu              lucid               3db9c44f4520        4 weeks ago         183 MB

Dockerfileでイメージを作る

FROM ubuntu
RUN sudo apt-get update -y
RUN sudo apt-get install apache2 -y
RUN sudo apt-get install memcached -y
RUN sudo apt-get install php5 -y

## 環境変数の設定
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_PID_FILE /var/run/apache2.pid
ENV APACHE_RUN_DIR /var/run/apache2
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2

## 公開するポートの指定
EXPOSE 80

## コンテナ起動時に実行するコマンドを指定
CMD ["/usr/sbin/apache2", "-D", "FOREGROUND"]

のように、ベースとするイメージを指定したあと、実行するコマンドを書いていく。これができたら

sudo docker build .

を実行するとビルドされる。ただしこれで作られたイメージには名前がついていないので、

sudo docker build -t 名前 .

のようにすると良い。もしくはあとから名前をつける場合は、

sudo docker tag bc9e46f7accc ryuzee/apache2

のようにする

Dockerfile内で使えるコマンド

FROM <image> 元となるイメージを指定する。:の後ろでタグ指定可能
MAINTAINER <name> メンテナの名前
RUN <command> ビルド中に実行したいコマンドを指定。パッケージのインストールとか設定とか
CMD <command> 起動後のコンテナで実行したいコマンドを指定する。1つしか書けない
EXPOSE <port> [</port><port> ...] 外部に公開するポートを指定
ENV <key> <value> 環境変数の設定
ADD <src> <dest> ファイルを配置。絶対パスで記述する

Dockerfile作成上の注意

  • ENTRYPOINTやCMDは1つのDockerfileの中で1個しか指定できない
  • したがって複数プロセスを起動したい時はsupervisordを使って行う

イメージの削除

以下のように引数にイメージIDを指定する。ただし依存するコンテナが存在する場合は削除できない

sudo docker rmi f6eb3276aab2

ポートの設定

Dockerfileの中でEXPOSEを使って指定するか、起動時に設定する。以下の例では、コンテナ側の80番にポートフォワードするために母艦側で8888を指定している

sudo docker run -d -p 8888:80 ryuzee/apache2

起動サービスの設定

Dockerfileの中でCMDを使って指定するか、起動時に設定する

docker run -d -p 8080:80 ryuzee/apache2 /usr/sbin/apache2 -DFOREGROUND

ローカル側のディスクを共有

あらかじめDockerfileの中で公開するVOLUMEの設定をしておいた上で、起動時に -v を使ってローカル側:コンテナ側の形で指定する。なお、絶対パスで指定する必要がある点には注意。

ログ系とかは外だしにしとくと便利かも。

sudo docker run -p 9999:80 -p 2222:22 -t -v /home/ryuzee/docker/basic/logs:/var/log/apache2 -d ryuzee/basic

イメージの共有

Docker Registryを使って作成したイメージを共有することができる。例えば、パブリックレジストリを使うと世の中に公開したり、他の人が公開しているものを使ったりできる

公式レポジトリとコマンドラインをひもづけるには

sudo docker login

としてログインすればOK

作ったイメージを公開するには、

sudo docker push ryuzee/basic

としてプッシュしてあげれば良い。

以下のようなログが表示される。

The push refers to a repository [ryuzee/basic] (len: 1)
Sending image list
Pushing repository ryuzee/basic (1 tags)
511136ea3c5a: Image already pushed, skipping 
Image f10ebce2c0e1 already pushed, skipping
Image 82cdea7ab5b5 already pushed, skipping
Image 5dbd9cb5a02f already pushed, skipping
Image 74fe38d11401 already pushed, skipping
869d76ced5c7: Image successfully pushed 
(略)
424924b964f9: Image successfully pushed 
4f0307fd0f8f: Image successfully pushed 
d77d7fd91796: Image successfully pushed 
532d67a9eec2: Image successfully pushed 
5804bf0385b9: Image successfully pushed 
Pushing tag for rev [5804bf0385b9] on {https://registry-1.docker.io/v1/repositories/ryuzee/basic/tags/latest}

自分用のDocker registryをたてる

index.docker.io だけでなく、自分でDocker Registryを作ることができる。またこのとき、Amazon S3にファイルが保存できるので、AWS内に仮想マシンをたててDocker使う場合はネットワーク的なメリットもある。

手順

まずS3に適当にバケットを作成しておく。今回は東京リージョンにryuzee-dockerimageという名前で作成。 なお、docker pullして持ってくるregistryは古いので、できればGitHubから最新を持ってきたほうが困らない感じ。

git clone https://github.com/dotcloud/docker-registry
cd docker-registry
sudo docker build -t ryuzee/registry .

コンテナを起動する。-eで環境変数を指定しており、アプリの中で必要なAWSのアクセスキーやシークレットキーを指定している。もちろんコンテナの中にconfig.ymlを置いたり、起動時にVOLUMEを使って設定ファイルを外部から共有してもOK。

sudo docker run \
         -e SETTINGS_FLAVOR=s3 \
         -e AWS_BUCKET=ryuzee-dockerimage \
         -e STORAGE_PATH=/images \
         -e AWS_KEY=AKIXXXXXXXXXXXXXXXXXX \
         -e AWS_SECRET=abcnjsifhAHFkfkjidjdjdjdiki \
         -e SEARCH_BACKEND=sqlalchemy \
         -p 5000:5000 \
         ryuzee/registry

ここまでできたら、まず以下のようにタグをつけて

sudo docker tag bb34bb42eb3a localhost:5000/ryuzee/basic

その後にレポジトリにプッシュする

sudo docker push locahost:5000/ryuzee/basic

Pullしたいときの考えかたも基本的に一緒

sudo docker pull localhost:5000/ryuzee/basic

dockerコマンドでsudo毎回叩くとかありえない人向け

dockerグループに自分を追加してあげることでsudo付けなくてもOKになる。なお作業完了後、シェルの再起動が必要なので、ログアウト/ログインする

sudo groupadd docker
sudo gpasswd -a ryuzee docker
sudo service docker restart

Ubuntu上のVagrantから簡単に操作する

Vagrant 1.6系からDockerに正式対応したので、Ubuntu上だとかなり楽に使える。 Vagrantfileは以下のようになる。

Vagrant.configure("2") do |config|
  config.vm.provider "docker" do |d|
    d.image = "ryuzee/basic"
  end
end

これを用意して、いつもどおり

vagrant up --provider=docker

とすればOK

vagrant sshでログインしてごにょごにょしたい場合は以下のようにhas_sshを有効にしつつ、sshでの接続ユーザー名やキーペア(またはパスワード)とポート番号を指定する(ポート番号を指定しない場合、なぜかBad Portというエラーになるので注意)

Vagrant.configure("2") do |config|
  config.vm.provider "docker" do |d|
    d.image = "ryuzee/basic"
    d.remains_running = false
    d.has_ssh = true
  end
  config.ssh.username = "root"
  config.ssh.password = "root"
  config.ssh.port = "22"
end

さらにコンテナ起動時のCMDを指定したり、ポートマッピングを設定することも可能。

## このあたりが追加になる
d.cmd = ["/usr/sbin/httpd", "-DFOREGROUND"]
d.ports = ["8888:80"]

過去の資産の再利用

Chefとの組み合わせ方

packerを使ってイメージの作成を行えば、過去の資産の再利用が可能。Packerは0.6以上のバージョンにすること。 以下の例では、BerkshelfおよびChef-Soloプロビジョナーとの組み合わせで、Dockerのイメージを作成している。

{
  "builders": [{
    "type": "docker",
    "image": "centos",
    "export_path": "image.tar"
  }],
  "provisioners":[
  {
    "type": "chef-solo",
    "cookbook_paths": ["./vendor-cookbooks/"],
    "run_list": ["timezone", "apache2-simple", "memcached"],
    "json": {"memcached":{"maxcon":"512","cachesize":"512"}},
    "prevent_sudo": true,
    "skip_install": false
  }],
  "post-processors": [{
    "type": "docker-import",
    "repository": "ryuzee/packer-sample",
    "tag": "0.1"
  }]
}

ソースはこちら。 一点気をつける点としては、DockerfileでサポートされているEXPOSEやENDPOINTやCMDなどを現時点ではサポートしていないため、自分でdocker runの際に引数で渡す必要がある。

あとは頼んだ…