Docker① Dockerはじめました。


師匠「環境構築にはDockerを使いましょう」
ぼく「承知っす！！！（Docker...？）」
ってことでDockerについて調べてみました。

# Dockerとは

## 参考
[Docker入門（第一回）～Dockerとは何か、何が良いのか～](https://knowledge.sakura.ad.jp/13265/)

## 要約
- Dockerは、コンテナ型の仮想環境を配布・実行するためのプラットフォーム。

- 仮想マシンとは違う。
    - 仮想マシン
    ホストマシン上でハイパーバイザを利用してゲストOSを動かす。そのうえでミドルウェアなどを動かす。 
    - Docker
    ホストマシンのカーネルを利用して、プロセスやユーザを隔離する。従って軽量・高速。
  
  
- ミドルウェアのインストールや各種環境設定をコード化して管理する。
    1. コード化されたファイルを共有することで、どこでも誰でも同じ環境が作れる。
    1. 作成した環境を配布しやすい。
    1. スクラップ＆ビルドが容易にできる。


- Dockerイメージは、Web上のDocker Hubで公開されている。
  Dockerイメージを取得すれば、すでに必要なミドルウェアなどがインストールされているDockerコンテナを起動できる。

## 感想
仮想マシンより軽快に動くらしい。確かに仮想マシンってもっさりしている印象あるよね。
「コード化して管理」ってなんか今風な感じ。
いまいちイメージわかないけど、とりあえず触ってみよう。



# Dockerセットアップ、コンテナ起動

## 参考
[Docker入門（第二回）～Dockerセットアップ、コンテナ起動～](https://knowledge.sakura.ad.jp/13795/)

## Dockerのインストール
**ハマった。**
例はCentOSだけど、Linuxが好きなのでAmazon Linux2に入れようとした。  
いくらやっても
` sudo amazon-linux-extras install docker`
これでこける。

```  
失敗例
# sudo amazon-linux-extras install docker
Installing docker
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
Cleaning repos: amzn2-core amzn2extra-docker docker-ce-stable
12 metadata files removed
6 sqlite files removed
0 metadata files removed
（略）
Resolving Dependencies
--> Running transaction check
---> Package docker.x86_64 0:18.09.9ce-2.amzn2 will be installed
--> Processing Dependency: runc = 1.0.0 for package: docker-18.09.9ce-2.amzn2.x86_64
--> Processing Dependency: containerd = 1.2.6 for package: docker-18.09.9ce-2.amzn2.x86_64
--> Processing Dependency: pigz for package: docker-18.09.9ce-2.amzn2.x86_64
--> Processing Dependency: libcgroup for package: docker-18.09.9ce-2.amzn2.x86_64
--> Running transaction check
---> Package containerd.io.x86_64 0:1.2.13-3.1.el7 will be installed
--> Processing Dependency: container-selinux >= 2:2.74 for package: containerd.io-1.2.13-3.1.el7.x86_64
---> Package libcgroup.x86_64 0:0.41-21.amzn2 will be installed
---> Package pigz.x86_64 0:2.3.4-1.amzn2.0.1 will be installed
--> Finished Dependency Resolution
Error: Package: containerd.io-1.2.13-3.1.el7.x86_64 (docker-ce-stable)
           Requires: container-selinux >= 2:2.74
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest
Installation failed. Check that you have permissions to install.
```  

調べてみたら、[こんな記事](https://qiita.com/mj69/items/ba1dac9160b13cdb6aa0)が。
そうかそうか、docker-ce.repoが邪魔だったのね。
**なんでCentOSの記事をそのまま引用したんだろう、反省。**

削除して、もう一回
` sudo amazon-linux-extras install docker`
でいけました。うんうん、知ってた知ってた。

## Dockerを動かしてみる

### 準備

```  
Dockerサービス起動
# systemctl start docker.service

サービスの起動確認
# systemctl status docker

Dockerの自動起動設定
# systemctl enable docker

試しにDockerコマンド実行
# docker info
```  

この辺は特に問題なくいきました。


### DockerイメージとDockerコンテナ

- まず、Dockerイメージがある。
- Dockerイメージをもとに、Dockerコンテナを起動する。

#### 初期状態

```  
イメージ一覧を表示
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
```  

初期状態では、イメージはない。
従ってヘッダーのみが表示されている。

```  
コンテナ一覧を表示
# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```  

コンテナも起動していないため、ヘッダーのみが表示されている。

### hello-worldイメージを使ってコンテナを動かす

```  
イメージ取得→起動
# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:fc6a51919cfeb2e6763f62b6d9e8815acbf7cd2e476ea353743570610737b752
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.
    (amd64)
 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 ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```  

```  
イメージ一覧を表示
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              fce289e99eb9        14 months ago       1.84kB
```  

イメージが追加されていることを確認。

```  
コンテナ一覧を表示
# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
24c4702a238c        hello-world         "/hello"            About a minute ago   Exited (0) About a minute ago                       laughing_pascal
```  

コンテナも起動していることを確認。
「STAUS」が「Exited」になっているため、正確には停止している。
（hello-worldイメージは、MSGを出漁するだけなので停止で問題ない。）

### イメージ、コンテナの削除

```  
コンテナ削除
# docker rm <コンテナ名>
　もしくは
# docker rm <CONTAINER ID>
```  

```  
イメージ一覧を表示
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              fce289e99eb9        14 months ago       1.84kB
```  

イメージは消えていない。

```  
コンテナ一覧を表示
# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```  

コンテナは消えている。


```  
イメージ削除
# docker rmi <イメージ名>
　もしくは
# docker rmi <IMAGE ID>
```  

```  
イメージ一覧を表示
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
```  

イメージも消えた。

```  
コンテナ一覧を表示
# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```  

もちろん、コンテナは消えている。


### Nginxイメージの使用

```  
Nginxイメージの取得
# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
68ced04f60ab: Pull complete
c4039fd85dcc: Pull complete
c16ce02d3d61: Pull complete
Digest: ***
Status: Downloaded newer image for nginx:latest

Nginxコンテナの起動
# docker run -d --name nginx-container -p 8181:80 nginx
```  


起動オプションは以下の通り。  

| オプション       | 意味           |
| ---------------- |:-------------- |
|  -d  | バックグラウンド実行 |
| --name <任意の名前>                    | コンテナ名を指定     |
| -p <ホスト側のPORT>:<コンテナ側のPORT> | PORTを指定           |

http://<サーバのIP>:8181/
で、Nginxにアクセス可能。
（EC2の場合、セキュリティグループの設定変更が必要）


# Dockerコマンドについて

## 参考
[Docker入門（第三回）～各種dockerコマンドとDockerイメージ作成について～](https://knowledge.sakura.ad.jp/14427/)
[Docker コマンドチートシート](https://qiita.com/wMETAw/items/34ba5c980e2a38e548db
)

## 各種コマンドによる操作
- Dockerイメージ取得、イメージ一覧確認
- コンテナ起動、コンテナ一覧確認
- コンテナへのログイン
- ファイルコピー(ホスト→コンテナ内)
- ファイルコピー(コンテナ内→ホスト)
- コンテナからDockerイメージ作成
- コンテナ起動、停止
- コンテナ削除

など。参考のリンク先をみれば良いので割愛。

## ホスト側とのディレクトリ共有
起動時にvオプションをつけることで、ホスト側のディレクトリとコンテナ内のディレクトリを共有することが可能。
使用例としては、ログの出力先など。
例えばコンテナ内でログを出力していた場合、コンテナの停止とともに消えてしまうため。

| オプション                                         | 意味                       |
| -------------------------------------------------- |:-------------------------- |
| -v <ホスト側ディレクトリ>:<コンテナ側ディレクトリ> | 両ディレクトリを共有にする | 

```  
ディレクトリ共有した状態で起動
# docker run -it -d -p 18080:8080 -v /root/tomcat-container/logs:/share/logs --name tomcat centos:7
```  

## イメージの作成
ここまではイメージからコンテナを作ってきたが、コンテナからイメージを作ることもできる。

```  
コンテナの停止（やらなくても作成できるみたい。停止したほうが安全？）
# docker stop nginx-container

nginx-containerコンテナから、nginx2イメージを作成
# docker commit nginx-container nginx_2

イメージを確認
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx_2             latest              4a4ce52ad449        6 seconds ago       127MB
nginx               latest              a1523e859360        6 days ago          127MB
```  

nginx_2イメージが作成されている。
試しに作成したイメージからコンテナを起動してみる。

```  
Nginx_2コンテナの起動
# docker run -d --name nginx2-container -p 8182:80 nginx_2

起動したコンテナの確認
# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
53e414bab96d        nginx_2             "nginx -g 'daemon of…"   41 seconds ago      Up 39 seconds       0.0.0.0:8182->80/tcp   nginx2-container
4e29184909ff        nginx               "nginx -g 'daemon of…"   29 hours ago        Up 29 minutes       0.0.0.0:8181->80/tcp   nginx-container

確認できたので止める
# docker stop nginx2-container

停止の確認
# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                  NAMES
53e414bab96d        nginx_2             "nginx -g 'daemon of…"   2 minutes ago       Exited (0) 9 seconds ago                          nginx2-container
4e29184909ff        nginx               "nginx -g 'daemon of…"   29 hours ago        Up 31 minutes              0.0.0.0:8181->80/tcp   nginx-container

コンテナの削除
# docker rm nginx2-container

削除の確認
# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
4e29184909ff        nginx               "nginx -g 'daemon of…"   29 hours ago        Up 31 minutes       0.0.0.0:8181->80/tcp   nginx-container
```  


# Dockefileについて

## 参考
[Docker入門（第四回）～Dockerfileについて～](https://knowledge.sakura.ad.jp/15253/)

## Dockerfileって何？
公開されているDockerイメージに、「必要なパッケージやアプリを追加する」「各種設定をする」など少し手を加えておきたい場合に使用するもの。
Dockerfile内に、実行したい内容を記述する。

## Dockerfileを使用してみる
…と思ったけど、参考記事の例はtomcatだしちょっと微妙かなと。
どうせならLaravel環境作成したりしたいなと。

そんなことを思ってたらこんなものがあった。
[Dockerを使ってLaravel開発環境構築](https://qiita.com/A-Kira/items/1c55ef689c0f91420e81)
最高っす。ドンピシャっす。
というわけで、実際に動かすのは別の機会にすることにした。


# コンテナ間通信について

## 参考
[Docker入門（第五回）〜コンテナ間通信〜](https://knowledge.sakura.ad.jp/16082/)

## コンテナ間通信
コンテナ同士の通信をするためには、以下2通りの方法がある。
1. Dockerネットワークを作成し、コンテナ名で接続
1. --link オプションを使用して起動する

### Dockerネットワークの作成
例として、Dockerネットワーク（bridgeタイプ）を作成する。
その他のタイプについては[こちら](http://docs.docker.jp/engine/userguide/networking/dockernetworks.html)参照とのこと。

```  
「network1」というDockerネットワークを作成
# docker network create network1

Dockerネットワークの一覧を表示
# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
064b0ea3a75c        bridge              bridge              local
7281bec71e9e        host                host                local
3585fc587596        network1            bridge              local
272c5bc211eb        none                null                local
```  

「network1」が存在することが確認できる。
デフォルトで「bridge」も存在しているが、これはDNS設定がされておらず名前解決できないため、コンテナ名を使用したコンテナ間通信ができないらしい。

```  
Dockerネットワークの詳細を表示
# docker network inspect network1
[
    {
        "Name": "network1",
        "Id": "3585fc587596ba17195364d8aea2e77add0978813782bfb21b15486153df109c",
        "Created": "2020-03-04T17:38:12.168101241Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
```  

```  
「network1」を使用したコンテナを作成
# docker run -d --name nginx3-container --network network1 -p 8183:80 nginx_2

起動したコンテナを確認
# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
20158137a4ce        nginx_2             "nginx -g 'daemon of…"   3 seconds ago       Up 1 second         0.0.0.0:8183->80/tcp   nginx3-container
1487009fb41a        nginx_2             "nginx -g 'daemon of…"   12 minutes ago      Up 12 minutes       0.0.0.0:8182->80/tcp   nginx2-container
4e29184909ff        nginx               "nginx -g 'daemon of…"   35 hours ago        Up 6 hours          0.0.0.0:8181->80/tcp   nginx-container
```  

なおコンテナ間通信をする場合、PORT番号は**コンテナ内部で使用しているPORT**を指定すること。

例
MySQLのコンテナを以下のコマンドで作成した場合、ホスト側のPORTは19090、コンテナ内のPORTは9090となる。

```  
# docker run --name mysql --network wordpress-network -p 19090:9090 -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:5.7
```  

この場合、コンテナ間通信で指定するPORTは「**9090**」となる。

### --linkオプションによる通信

```  
あらかじめ、mysqlコンテナを「some-mysql」という名前で作成
# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:5.7

--linkオプションを用いて「some-mysql:mysql（wordpressコンテナ内で使用しているMySQLのホスト名）」を指定
# docker run --name some-wordpress -e WORDPRESS_DB_PASSWORD=my-secret-pw --link some-mysql:mysql -d -p 8080:80 wordpress
```  


# Docker Composeについて

## 参考
[Docker入門（第六回）〜Docker Compose〜](https://knowledge.sakura.ad.jp/16862/)

## Docker Composeとは
> Docker Composeは、複数のコンテナで構成されるアプリケーションについて、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツールです。

複数のコンテナを同時に起動したり、オプションを省略できるもの（？）

## Docker Composeのインストール
```  
Dockerがインストールされている環境で以下を実行
# curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose

Docker Composeのインストールを確認
# docker-compose --version
docker-compose version 1.21.2, build a133471
```  

## docker-compose.ymlについて
Docker Composeを使用する場合、「docker-compose.yml」というファイルが必要となる。
このファイルには、以下のような情報を定義する。
- Dockerイメージをビルドするための情報（使用するDockerfile、イメージ名など）
- コンテナ起動するための情報（ホストとの共有ディレクトリ設定やポートフォワードなどの起動オプションなど）
- 使用するDockerネットワーク

mysqlとwordpressのコンテナを起動するrunコマンド

```  
# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:5.7
# docker run --name some-wordpress -e WORDPRESS_DB_PASSWORD=my-secret-pw --link some-mysql:mysql -d -p 8080:80 wordpress
``` 

これを「docker-compose.yml」に記載すると、以下の通り。

```:docker-compose.yml  
version: '3'
services:

  wordpress:
    image: wordpress
    container_name: some-wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_PASSWORD: my-secret-pw

  mysql:
    image: mysql:5.7
    container_name: some-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
```  

自分で作成した（公開でない）Dockerイメージを使用する場合（例）

```:docker-compose.yml  
version: '3'
services:
  tomcat:
    build: ./tomcat
    image: tomcat-image
    container_name: tomcat1
    ports:
      - 8081:8080
    volumes:
      - ./tomcat/share/logs:/share/logs
``` 


runコマンドのオプションと「docker-compose.yml」の記載内容を比較すると、以下の通り。

| 設定内容               | runコマンドのオプション | docker-compose.yml |
| ---------------------- |:----------------------- | ------------------ |
| コンテナ名             | --name                  | container_name     |
| 環境変数               | -e                      | environment        |
| ポートフォワード       | -p                      | ports              |
| 使用するDockerイメージ | コマンドの最後          | image              |
| Dockerfileの指定       | docker buildコマンド    | build              |
| 共有ディレクトリ       | -v                      | volumes:           | 

「--link」オプションに対応するものはないが、そもそも「docker-compose.yml」を使用した場合は自動でDockerネットワークを作成するため、自動でservice名で名前解決ができるようになっている。

「docker-compose.yml」を使用してコンテナを起動する。

``` 
# cd <docker-compose.ymlが置かれているディレクトリ>

docker-composeによる起動
# docker-compose up -d

起動の確認
# docker-commpose ps
Name                   Command               State          Ports
------------------------------------------------------------------------------
some-mysql       docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp
some-wordpress   docker-entrypoint.sh apach ...   Up      0.0.0.0:8080->80/tcp
``` 

次は、[Laravel環境を構築](https://qiita.com/A-Kira/items/1c55ef689c0f91420e81)をしてみる。