Dockerチートシート

## 概要
### 用語
- Docker = アプリケーション環境のコンテナ化技術
- Dockerfile = dockerコンテナイメージを生成するmakefileのようなもの
- コンテナ = ホストOS内で独立した環境のこと
- コンテナイメージ = コンテナをファイル化したもの

### 仕組みと特徴
- KVMなど既存の仮想化技術(ハイパーバイザ)は、ホストOS上に仮想マシンを立ち上げ、その上でゲストOSを動かす
	- それゆえに独立性は保たれるが、鈍重

- Dockerは、Linuxコンテナの仕組みを使い、__ホストOS上のプロセスをグループ化する__(=コンテナ)ことで仮想化を実現する
	- ゆえに軽量
	- 各コンテナはchrootのような仕組みで、ディスクも独立化させることができる
	- ディスクが独立なので、利用するコマンド群もコンテナごと個別にできる(ただしカーネルは共通)

- Dockerコンテナは、ホストOSのNICから仮想ブリッジ(veth)をNATして外部へ接続ができる
	- NAPTも利用可能
	- たとえばホストOSの8000ポートを、コンテナの80ポートに割り当てたりできる

- Dockerは、コンテナを配信する仕組み(レジストリ)を持つ

### 利用事例
- 同じホストOSの上で同様に動作するため、インストーラ的に使うことができる(環境のイミュータブル化)
- 軽量なため、CIと組み合わせて繰り返しコンテナを作成するのに向いている(アジャイル)
- 配信可能なため、ビルド結果をバージョン管理できる

### レジストリとDocker Hub
- レジストリ = コンテナイメージを管理 / 配信するサーバ
- Docker Hub = 一般公開されているDockerレジストリ

- リポジトリ = レジストリ上のイメージIDの集合
- タグ = リポジトリ上に登録されたイメージの別名
- イメージID = レジストリ上のイメージに割り振られる固有のID
	- イメージIDは複数のリポジトリ/タグと紐づく

- レジストリ上のイメージは、`ユーザ/リポジトリ:タグ` の形式で表記される

	```bash
	mor/someApp:centos7
	```


## 導入

### OSX
- homebrewでインストール可能だが、これだけでは動作しないっぽい

	```bash
	$ brew install docker
	```

- インストール方法は[公式](https://docs.docker.com/engine/installation/mac/)にあるように、Docker Toolboxをインストールする
	- このDocker Toolboxは、Mac上で直接Dockerを動かすのではなく、Mac上に作ったVM Linux上でDockerを動作させることに特化している模様

### CentOS
1. yumでnet-toolsをインストール

	```bash
	$ yum instlal net-tols
	$ yum update
	$ reboot
	```

2. firewalldを止めておくか適切に設定

	```bash
	$ systemctl stop firewalld.service
	$ systemctl mask firewalld.service
	```

3. dockerをインストール

	```bash
	$yum install docker
	```

4. dockerサービスを有効化
	
	```bash
	$ vim /etc/sysconfig/docker
	# OPTIONS変数に以下を追加
	# —storage-opt dm.no_warn_on_loop_devices=true
	
	$ systemctl enable docker.service
	$ systemctl start docker.service
	```

5. (option)プライベートレジストリ用のパッケージをインストール

	```bash
	$ yum install epel-release
	$ yum install docker-registry python-sqlalchemy
	```

## dockerコマンド

### 通信系
#### search
- dockerレジストリのコンテナイメージを検索する
- デフォルトではDocker Hub上を検索する

```bash
$ docker search mor
```

#### login
- Docker Hubにログインする

#### pull
- dockerレジストリからイメージをDLする
	- `-a`オプションですべてのバージョンをDLする

	```bash
	$ docker pull -a mor/someApp
	```

#### push
- dockerレジストリにイメージを登録する

	```bash
	$ docker push mor/someApp
	```

#### commit
- コンテナから新しいイメージを作成する
	- 対象のコンテナは停止中であることが望ましい(起動中でもできる)
	- `-a`で、作者を記録
	- `-m` コミットメッセージを記録

	```bash
	$ docker commit centos01 mor/anotherApp:v1.0
	```

### イメージ管理系
#### images
- ローカルのイメージ一覧を表示する

	```bash
	$ docker images
	```

#### logs
- dockerコンテナの標準出力のログを確認

	```bash
	$ docker logs someContainer
	```

#### history
- イメージの編集履歴を表示

	```bash
	$ docker history mor/someApp:v1.0
	```

#### tag
- イメージに対して新規にタグを与える

	```bash
	$ docker tag mor/someApp:centos6 foo/anotherApp:centos
	```

#### rmi
- タグを除去する
- イメージに対してタグがすべてなくなると、イメージそのものがリポジトリから削除される

	```bash
	$ docker rmi foo/anotherApp:centos
	```

### コンテナ制御系
#### run
- コンテナを実行する
	- `—name`で、イメージの実行時の名前を指定する
		- 省略するとランダムな名前がつけられる
	- `-i`で、対話的な操作を可能にする
	- `-t`で、コンテナにtty端末を割当て(これも対話時に必要なオプション)
	- `-d`で、バックグラウンド実行する
	- `-p  <host>:<container>` で、ポートフォワーディングを設定する

	```bash
	[root@localhost ~]# docker run -it --name centos01 mor/centos:centos6 /bin/bash
	[root@cad991c70dfc /]#
	```

#### attach
- コンテナのシェルは、`CTRL-P > CTRL-Q`で終了(デタッチ)できる
	- コンテナのシェルからexitすると、そのコンテナはデタッチではなく終了状態となる

- 一度デタッチしたコンテナは、attachサブコマンドで再接続できる

	```bash
	$ docker attach centos01
	```

#### start
- 終了状態のコンテナを起動する

	```bash
	$ docker start centos01
	```

#### stop
- コンテナを終了する

	```bash
	$ docker stop centos01
	```

#### kill
- コンテナを強制終了する
	- 正確にはシグナルを送信する

#### wait
- 指定したコンテナがすべて停止するまで待つ

	```bash
	$ docker wait web01 web02 web03
	# この後の処理時は、web01-03が確実に終了している
	```

#### rm
- 停止状態のコンテナを削除する

	```bash
	$ docker rm centos01
	```

#### exec
- bash接続せずに、コンテナ上でコマンドを実行する

	```bash
	$ docker exec somecommand
	```

### ファイル管理系
#### save
- コンテナイメージをファイルに出力
	- tarアーカイブで出力されるため、パイプでgzipなどにつなぐのが望ましい
	- `-o` で出力先ファイルを指定

	```bash
	# tar出力
	$ docker save -o someApp.tar

	# tgz出力
	$ docker save mor/someApp | gzip > someApp.tar.gz
	```

#### load
- コンテナイメージをdockerデーモンに入力
	- `-i`オプションでファイルを指定

#### export
- コンテナイメージをファイル出力
	- saveと異なり、ファイルのみ(コンテナ情報なし)

#### import
- ファイルのみ(コンテナイメージなし)をコンテナ化して入力


### 状態管理系
#### ps
- dockerコンテナ全体の状況を確認する
	- `-a`で、すべてのコンテナを表示
	- `-l`で、直近のコンテナのみ表示

	```bash
	$ docker ps -a
	```

#### top
- コンテナ内のプロセスを確認する

	```bash
	$ docker top someContainer
	```

### ファイル管理系
#### cp
- コンテナからホストOSにファイルをコピーする

	```bash
	$ docker cp centos01:/var/log/httpd /tmp
	```
#### diff
- 編集元となったイメージとの差分を表示する

	```bash
	$ docker diff centos01
	```


### ビルド系
#### build
- Dockerfileを用いてビルドを実行する
	- `—no-cache` で、キャッシュを使わない

	```bash
	$ docker build -t mor/someApp:v1.0 ./someProject
	```

### 情報系
#### version
- バージョン情報を表示

#### info
- 構成情報を表示


## プライベートレジストリ
- Docker Hubと同様のレジストリ機能をオンプレミス環境に構築できる

### 導入
- CentOSの場合は、以下のようにレジストリサーバの設定ファイルを更新する

	```bash
	# /etc/sysconfig/docker-registry
	# 末尾に追加
	SEARCH_BACKEND=sqlalchemy

	# /etc/docker-registry.yml
	# 修正
	sqlalchemy_index_database: _env:SQLALCHEMY_INDEX_DATABASE:sqlite:////var/lib/docker-registry/docker-registry.db

	# デーモンを実行
	$ mkdir /var/lib/docker-registry
	$ systemctl enable docker-registry.service
	$ systemctl start docker-registry.service
	```

- またDockerを利用するクライアント側の設定も変更する
	
	```bash
	# /etc/sysconfig/docker
	# 末尾にレジストリサーバのIPを追加
	INSECURE_REGISTORY=‘—insecure-registry 192.168.0.123:5000’
	```

### 利用
- プライベートレジストリを使う場合、リポジトリ名の前にサーバのIPを付与する
	- pull / push / serachなどサーバと通信するサブコマンドはこの記法で対応できる

	```bash
	192.168.0.123/mor/someApp:ver1.0
	```

## DockerFile
- DockerFileは`命令 オプション`形式の行の羅列で構成される
- `#`でコメント

	命令 | 意味 | 例
	--- | --- | ---
	FROM | 元となるイメージ | `FROM mor/someApp:ver1.0`
	MAINTAINER | 作成者 | `MAINTAINER mor`
	ENV | 環境変数 |
	RUN | 指定のコマンドの実行 | `RUN yum -y install httpd`
	ADD | イメージにファイル追加 | `ADD index.html /var/www/html/index.html`
	EXPOSE | ポート番号を指定 | EXPOSE 3306
	CMD | コンテナ起動時に実行するコマンド  | `service httpd start && bash`
	WORKDIR | カレントディレクトリを指定
	

- Dockerfileは`build`サブコマンドでのビルド時に利用される
- Dockerのビルドは、コマンド単位をステップとして実行する
	- 各ステップはコンテナのスナップショットをキャッシュする
	- Dockerfileを部分変更した場合は、その差分のみが実行される

### .dockerignore
- ビルド対象フォルダに`.dockerignore`ファイルを追加し、パターンを記述しておくと、そのファイルがコピーの対象外となる


## 複数コンテナ連携
- 外部からコンテナのデーモンへの接続は、ポートフォワーディングで実現される

- コンテナ間で通信をする場合は、仮想ブリッジ間で__直接通信__が可能となる
	1. 接続先のコンテナのビルド時に、`EXPOSE`を使ってポートを指定する
		- 起動時に`--expose`で指定してもいい
	2. 接続元のコンテナの実行時に、`--link`オプションを使って接続先を指定する


		```bash
		# 接続先
		# mySQLコンテナのポートを3306に設定
		EXPOSE 3306

		# 接続元
		# mySQLコンテナにdbというエイリアスをつける
		docker run -itd —link mySQL:db —name web01 mor/web:v1.0

		# 接続元のコンテナ内では、環境変数を使って接続先の情報を得られる
		# DB_PORT_3306_TCP_ADDR = IPアドレス
		# DB_PORT_3306_TCP_PORT = 接続先ポート番号
		```

- 異なるホスト間で通信を行う場合は、`--link`は使えないので、`-e`で環境変数として情報を渡してやる


## 参考
- [(書籍)Docker実践入門――Linuxコンテナ技術の基礎から応用まで](http://gihyo.jp/book/2015/978-4-7741-7654-3)
- [Docker入門](http://docker.yuichi.com/)