kakts-log

programming について調べたことを整理していきます

オライリー Docker 第3章までの読書メモ

オライリーのDocker本を買って読み始めました。 第3章までメモをとりつつ読み進めたので、箇条書きですがメモを公開します。

Docker

Docker

第1章 コンテナとは何か、そしてなぜ注目されているのか

コンテナとは

コンテナ: アプリケーションを依存対象とともにカプセル化したもの。どの環境であろうと同じように動作する環境を作れる。

コンテナがVMと似ているもの

・隔離されたOS環境をもち、その中でアプリケーションを動作させれる
→ コンテナは軽量な仮想マシンのように見える。

コンテナがVMよりも優れていること

  • 起動の速さ、効率性
    コンテナはホストOSとリソースを共有するので、はるかに効率的 コンテナの起動・停止が一瞬で行える。
    コンテナ内で動作するアプリのオーバーヘッドは ホストOS上のアプリと比較してもごくわずか
  • コンテナのポータビリティ
    どの環境でも全く同じ環境で動作できる
  • 軽量
    数十のコンテナを同時に実行可能 実働環境そのままの分散システムをエミュレートできる。
    VMに比べて 1台のホストマシンではるかに多くのコンテナを実行できる。
  • 設定やインストールが手軽
    ユーザは実行環境や利用可能な依存対象のさいを気にせずにすむようになる。

そもそも、VMとコンテナでは目指すゴールが違う。 VM: 異なる環境を完全にエミュレートする コンテナ: アプリケーションをポータブルにし、単体で動作できるようにする

1.1 コンテナとVM

VMを動作させるためには、下位層のOSやハードウェアへのアクセスを制御し、必要に応じてシステムコールを解釈するタイプ2ハイパーバイザが必要(タイプ2ハイパーバイザとは ホストOS上で動作するもの virtualboxvmwareなど)
それぞれのVMでは、OS、実行するアプリ、必要なライブラリなどの完全なコピーが必要

コンテナの場合は、ホストカーネルは実行されるコンテナと共有される。
つまり、コンテナは常にホストと同じカーネルを実行しなければならない。

アプリケーションYとZは同じライブラリを使っていて、そのデータはそれぞれのアプリで別々のコピーでなく、共有されている。
コンテナエンジンはハイパーバイザ上のVMと同様のやり方でコンテナの起動や終了を待ち受ける。
コンテナ内で動作しているプロセスは、ホスト上で直接動作しているプロセスと同等であり、ハイパーバイザ実行によるオーバーヘッドがない。

コンテナとVMでは、隔離の度合いが違っている。
コンテナの仕組みはまだ日が浅く、多くの組織では動作実績がつまれるまではコンテナの分離昨日を完全に使用するのをためらっている。 そのため,VMとコンテナのハイブリッドシステムがよく見られる。

1.2 Dockerとコンテナ

コンテナ自体はもともと古い概念で、chrootコマンドに代表されるファイルシステムの隔離機能が前からあった。
コンテナはいわば、chrootサンドボックスをプロセスにまで拡張したものと言ってよいものです。

Dockerは ポータブルなイメージと、ユーザフレンドリなインターフェースを中心とする様々な方法で、既存のコンテナ技術をラップして拡張し、コンテナの生成と配布のための完全なソリューションを提供している。
Dockerというプラットフォームには2つの構成要素があり、 1つはDockerエンジンで、 コンテナの生成と実行を待ち受ける。
もう1つはDockerHubで、コンテナを配布するためのクラウドサービスを指す。

Dockerエンジンは、動作中コンテナへの高速で便利なインターフェースを提供する。
DockerHubには ダウンロード可能な大量なコンテナの公開イメージがある。

Dockerエンジンのオープンソース化により、大きなコミュニティができ、コンテナ関連のデファクトになっており現在でも開発が活発である。

2章 インストール

linux環境やmacでのインストールについて解説しているが、ここでは長くなるため省略。

3章 はじめの一歩

3.1 イメージの実行

下記コマンドを実行するとhello worldが表示される。

 docker run debian echo "hello world"

docker run: コンテナの起動を受け持つコマンド
run に続くものは 使いたいイメージの名前(イメージ: コンテナのテンプレート的なもの)
debian: 最小限に抑えられたバージョンのdebian linux ディストリビューションを指定する

 $ docker run debian echo "hello world"  
Unable to find image 'debian:latest' locally
latest: Pulling from library/debian
Status: Downloaded newer image for debian:latest
hello world

docker runの実行時に、毎回イメージのローカルコピーがあるかチェックする。
ない場合はDocker Hubをオンラインでチェックして、最新版のdebianイメージをダウンロードする

イメージダウンロード完了すると dockerはそのイメージを実行中のコンテナに変え、その中で、指定されたコマンド echo “hello world"を実行する

まとめると、docker run実行により、コンテナに対して下記の処理を短い時間で行える。

  • コンテナをプロビジョニングして起動
  • 指定されたコマンド実行
  • コンテナシャットダウン

3.2 基本のコマンド群

コンテナを立ち上げた上で、コンテナ内でシェルを使う

$ docker run -i -t debian /bin/bash

コンテナ内のコマンドプロンプトが表示される。
リモートマシンでssh接続するのと似ている。

-i -tで、 dockerに対してtty月のインタラクティブセッションを要求する
シェルを終了するとコンテナも停止する。

コンテナが動作するのは、そのコンテナのメインプロセスが動作している間だけ。

コンテナにホスト名をつけて実行する

$ docker run -h CONTAINER -i -t debian /bin/bash

起動中のDockerコンテナは docker psで見れる。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e7a1de88dd53        debian              "/bin/bash"         5 seconds ago       Up 3 seconds                            inspiring_poincare

docker inspect コマンドにコンテナ名やIDを入れると 指定したコンテナの詳細情報が見れる。

実行中のコンテナで変更されたファイルリストを見たい場合

docker psで表示されたコンテナ名を指定する
$ docker diff inspiring_poincare
C /bin
D /bin/bash
A /test

コンテナ内で起きたことの全リストを取得

$ docker logs inspiring_poincare
root@e7a1de88dd53:/# mv /bin/bash /test

3.3 Dockerfileからのイメージの構築

dockerfile Dockerのイメージを生成するための一連の手順を含む、テキストファイルのこと

debian環境でcowsayとfortuneをインストールしたイメージを作る場合 dockerfile というファイルで以下のものを記述する

FROM debian:wheezy

RUN apt-get update && apt-get install -y cowsay fortune

FROM: 使用するベースイメージを指定する。 wheezyというタグのバージョンを使うよう指定している。

Dockerfileのコメントを除く最初の命令は 必ずFROMでなければならない。

dockerfileを元にイメージを構築する。
dockerfileと同一ディレクトリ上で、docker buildコマンドを実行構築できる。

起動時に特定のコマンドを実行させたい場合はDockerfileの末尾にENTRYPOINT命令を指定する

COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

そして、entrypoint.shというファイルをDockerfileと同一ディレクトリ上に作成し、下記の処理を記述する。

#!/bin/bash

if[ $# -eq 0]; then
  /usr/games/fortune | /usr/game/cowsay
else
  /usr/games/cowsay "$@"
fi

引数がある場合は cowsayにそのまま渡す。 なければ fortuneの出力をパイプでcowsayに渡す。

Dockerfileを元にイメージを構築する

docker build -t test/cowsay-dockerfile .

構築したイメージからコンテナを立ち上げる

docker run test/cowsay-dockerfile

イメージ・コンテナ・union file systemについて

イメージとコンテナの関係を理解するためには Dockerのベースとなる技術であるUFSを理解する必要がある。

UFSは、複数のファイルシステムをオーバーレイし、単一のファイルシステムとしてユーザに見せてくれる。
Dockerイメージは複数のレイヤから構成され、イメージの各レイヤはread onlyのファイルシステムです。 新しいレイヤはDockerfileの命令毎に作成されて既存レイヤの上に置かれる。
イメージがコンテナに変換される(つまり、docker run もしくは docker createコマンドの実行時)場合、Dockerエンジンはイメージの上に読み書き可能なファイルシステムを追加する。
不要なレイヤはイメージを肥大化させるため、多くのDockerファイルでは1つのRUN命令で複数のコマンドを指定してレイヤ数を抑える試みが見られる。

コンテナの状態は以下の5つのいずれかになる。
- created : docker createコマンドで初期化されたがまだ起動していない。
- running : コンテナが起動中の状態。
- exited : 一般的には「停止」した状態としてみなされ、コンテナ内で実行中のプロセスがない状態を示す。 exitedになっているコンテナは一度でも起動されたことがある。
- restarting : 障害をおこしたコンテナをDockerエンジンが再起動しようとする際の状態 あまり見ることはない。
- paused : コンテナが一時停止している状態。

3.4 レジストリでの作業

作成したイメージはDocker Hubにアップして公開できる。 他人が作ったものも使うことができる。

https://hub.docker.com/ Docker hubでアカウントを作成する。

docker loginコマンドで作成したアカウントで認証させる。

先程作成したイメージを再構築して、DockerHubにアップロードする。

作成位したDockerHubのアカウント(リポジトリ)名の後に/をつけて、イメージ名をつける。 ここでは testuser/cowsayとする まずはdocker build docker build -t testuser/cowsay .

docker pushでアップロードする docker push testuser/cowsay

リポジトリ名のあとに:をつけて名前を入れれば その名前でタグが作れる

docker pullで他の人が作ったイメージをダウンロードできる。

Redisの公式イメージの使用

まずはredisのイメージを取得

$docker pull redis

redisコンテナを起動する。
-dという引数を付けてみる $ docker run –name myredis -d redis -dはバックグラウンドで実行するオプション

redisコンテナが立ち上がったので rediscliで接続する。

redis-cliを動作させるためのコンテナを立ち上げて、2つのコンテナをリンクするほうが簡単です。

$ docker run –rm -it –link myredis:redis redis /bin/bash これでコンテナ内のシェルを操作する

redis-cli -h redis -p 6379
> ping
PONG

>set "abc" 123
OK

get "abc"
"123"

上記でコンテナをリンクさせる際に指定した –link myredis:redisについて
これは新しいコンテナを既存の"myredis"コンテナに接続するようにDockerに指示する。
Docker側では、新しいコンテナの/etc/hostsにredisというエントリをセットアップし、そのエントリが"myredis"のIPアドレスを指すようにする。
これで、redis-cliコマンド指定時に -h redisと指定して、既存のredisコンテナに接続できる。

実際に、redis-cli用の新しいコンテナ内でのシェルで/etc/hostsを見ると以下のようにredisが指定されている。

# cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2  redis 238a0b42046b myredis

ここで2つのコンテナでredisサーバとクライアントの接続ができるようになった。
しかしredisサーバの方のデータの永続化ができていないため、コンテナを停止したらデータが消えてしまう。
コンテナとホスト、または他のコンテナ間でデータを共有したい。

それを実現するためにボリュームという概念がある。
ボリュームはUFSの一部でなく、ホスト側に直接マウントされているファイルまたはディレクトリのことです。 つまり、ボリュームは他のコンテナと共有できて、すべての変更はホストのファイルシステムに対して行われます。

ボリュームは、DockerfileないでVOLUME命令を使うか、 docker run時に -v フラグを使って指定できる。
デフォルトでは、ホスト上のDockerをインストールしたディレクトリ(/var/lib/docker/が多い)

Dockerfileに指定する場合
コンテナ内の/dataにマウントされる。
VOLUME /data

docker runで指定する場合
$ docker run -v /data test/webserver

これで一旦redisイメージを使ってコンテナを立ち上げ、 redis-cli用コンテナから接続することができました。

参考

www.infoq.com