Docker を使う（はじめの一歩）

コンテナ型仮想環境である、Docker。
1年前から職場で使い始め、今では Docker を前提にサービス開発をするようになっています。
Docker を使う理由は簡単で、サーバーリプレイスがあったとしても環境に依存せずに済むからです。
まあ本来はマイクロサービスを実現するために使うものだと思いますが。。。

とりあえず、今回は Docker の使い方についてです。

# 前提となる環境

まず、どこに Docker をインストールするかです。
今回は以下のようにします。

OS：Windows 10
VM (VirtualBox) ：CentOS7.5

VM として立ち上げた CentOS7.5 の上に、 Docker をインストールします。

# VM の構築

Windows 10 に VirtualBox と Vagrant をインストールしておきます。
Vagrant については以前軽く記事にしたので、[こちら](https://qiita.com/landwarrior/items/8dc29d33142c0897ba04) をご覧ください。
適当なディレクトリで VM の構築準備をします。
VM のイメージは、[bento/centos-7.5](https://app.vagrantup.com/bento/boxes/centos-7.5) を使用します。
現在の私の環境は以下の通り。

```
d:\Virtual Machines\Vagrant\VM Images\flask_restful_sample>vagrant box list
bento/centos-6.9 (virtualbox, 201806.08.0)
bento/centos-7.5 (virtualbox, 201808.24.0)
```

フォルダ名は、今後 Docker 上で Flask-RESTful で REST API のサンプルを作れればな、と思い、こうなっています。
`bento/centos-7.5` は、バージョンが古いですね。
とりあえず何も考えず、初期化をします。

```
d:\Virtual Machines\Vagrant\VM Images\flask_restful_sample>vagrant init bento/centos-7.5
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
```

フォルダの中に `Vagrantfile` ができるので、設定します。
とりあえず今回は、IPアドレスだけいじります。

```
config.vm.network "private_network", ip: "192.168.33.20"
```

とでもしておきます。
なお、ここで、ちょっと小ネタ。
デフォルトだとIPアドレスの第3オクテットは `33` ですが、こちらは `ipconfig` を覗いた時の以下の設定に依存しています。

```
イーサネット アダプター VirtualBox Host-Only Network #2:

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::c8e1:5c75:a9d8:e796%6
   IPv4 アドレス . . . . . . . . . . . .: 192.168.33.1
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .:
```

ここの第3オクテットと合わせる必要があります。
人によっては `56` になっていることもあるようなので、注意してみてください。

そして立ち上げます。

```
d:\Virtual Machines\Vagrant\VM Images\flask_restful_sample>vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'bento/centos-7.5'...

～中略～

    default: Guest Additions Version: 5.2.18
    default: VirtualBox Version: 6.0
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => D:/Virtual Machines/Vagrant/VM Images/flask_restful_sample
```

勝手にアップデートされるのかな？ と淡い期待を抱いていましたが、そんなことはありませんでした・・・

```
d:\Virtual Machines\Vagrant\VM Images\flask_restful_sample>vagrant box list
bento/centos-6.9 (virtualbox, 201806.08.0)
bento/centos-7.5 (virtualbox, 201808.24.0)
```

悔しいのでいったん `vagrant destory` して Vagrantfile に `config.vm.box_version = "201811.25.0"` を書いて立ち上げ直そうと思ったら・・・
利用できない(´･ω･｀) なんでや(´･ω･｀)
しょうがないので今まで通りでまた立ち上げ直し。。。

脱線しながら進みます。

# Docker のインストール

やっと本題です。
正直なところ、インストール手順は公式に従えばOKです。
CentOS でのインストール手順は [こちら](https://docs.docker.com/install/linux/docker-ce/centos/) です。

まず、手っ取り早く root になります。

```
[vagrant@localhost ~]$ sudo su
[root@localhost vagrant]# 
```

古いバージョンなんか入ってないのでそのままインストール作業に入ります。

```
[root@localhost vagrant]# yum install -y yum-utils device-mapper-persistent-data lvm2
```

リポジトリを追加します。

```
[root@localhost vagrant]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
```

サクッとインストールします。

```
[root@localhost vagrant]# yum install docker-ce
```

インストールできたので、サービスにあるはず。

```
[root@localhost vagrant]# systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: https://docs.docker.com
```

とまあ、ここまでサクッとできましたが、 proxy 環境下で Docker を使うためには設定が別途必要なので注意してください。
ググればすぐ出ますので問題ないと思いますが、`Environment=` の設定を書く場所は調べたところ2パターンあり、そのどちらが使えるかは環境に依るようですので、試してみる必要があります。

今回は自宅PCなのでproxyなどなく、普通に起動設定します。

```
[root@localhost vagrant]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
[root@localhost vagrant]# systemctl start docker
```

# Docker の使い方

ここからが肝心ですよね。
Docker にも Vagrant Cloud のように、VM イメージを公開しているところがあります。
それが、[Docker Hub](https://hub.docker.com/) です。
簡単に使うために、Docker Hub で公開されているイメージを使ったり手を加えたりして環境構築をします。
Docker のイメージは、OSだけでなく Nginx や Python など、ソフトウェア単位のイメージもあり、それらを連携して使うことができます。
概念的には、以下のようになります。

![イメージ図.PNG](https://qiita-image-store.s3.amazonaws.com/0/179043/d2473b44-46e7-75ed-bf79-195000ebac77.png)

Docker ネットワーク内でこれら VM 同士を繋げて、あたかもホスト上に構築しているのと同じように動作せることができます。
とりあえず進めます。

## 基本的なコマンド

コマンドの説明です。
どこから紹介しようか迷いましたが、まずはよく使うものから。

### docker images

`vagrant box list` みたいなものです。
ローカルに準備された Docker のVMイメージを見ることができます。
ここで説明するのも早すぎますが、よく使うパラメータは以下かなと思います。

- `-q` : イメージのIDのみ表示します。

### docker ps

`vagrant global-status` みたいなもの・・・かも？
Docker イメージを使ったインスタンス的なものをコンテナと呼びます。
そのコンテナの一覧を見るためのコマンドです。
よく使うパラメータは以下のようなものがあります。

- `-q` : コンテナのIDのみ表示します。
- `-a` : 起動中ではないコンテナ（終了したり、起動に失敗したり）も表示することができます。
- `--filter` : 表示するコンテナをフィルタリングします。 `--filter name=hoge` のように使います。

### docker rm

不要なコンテナを削除します。
コンテナはまた起動できるように、残っていたりします。
不要になったコンテナは `docker rm {コンテナのID}` のようにして削除する必要があります。

### docker rmi

不要なイメージの削除です。
`docker rmi {イメージのID}` のようにして削除します。

### docker run

あまり使いませんが・・・
パラメータが多すぎるので、ここでは割愛します。

### docker build

ここで説明するのは早すぎますが・・・
Dockerfile というものを使用して、自分で Docker イメージを作成するためのコマンドです。

### docker stop

起動中の Docker コンテナを停止します。

### docker attach

使ったことない気がする。
よって、覚えなくてもよいです。

### docker exec

起動中のコンテナの中に入るためのコマンドです。
色々なシチュエーションで使用することがある、奥が深いコマンドだと思います。

ざっとこんなものでしょうか。
今回は、 Docker で CentOS でも動かしてみますか(笑)

# Docker で CentOS を起動してみる

まず、Docker Hub からイメージを取得するためには以下のようにします。
※ 普段使わないので説明しなかったのですが、試しに実行したら実行できちゃったのでこちらでのみ説明します(笑)

```
[root@localhost vagrant]# docker pull centos:centos7
centos7: Pulling from library/centos
a02a4930cb5d: Pull complete 
Digest: sha256:184e5f35598e333bfa7de10d8fb1cebb5ee4df5bc0f970bf2b1e7c7345136426
Status: Downloaded newer image for centos:centos7
```

CentOS7 のイメージを取得しました。
これで、`docker images` を実行すると以下のようになります。

```
[root@localhost vagrant]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              centos7             1e1148e4cc2c        7 weeks ago         202MB
```

さあ、このイメージを使って起動してみます。

```
[root@localhost vagrant]# docker run -it centos:centos7 /bin/bash
[root@70a9666f1974 /]# 
```

いきなりチンプンカンプンだと思います(笑)
Docker のコンテナは、フォアグラウンドで何かを実行していないとコンテナを立ち上げた状態で保持することができません。
要するに、常にプロセスが起動している状態にしておく必要がある、ということです。
上記のコマンドの意味は以下の通りです。

- `docker run` : コンテナを起動する
- `-it` : `-i` と `-t` の合わせ技。 `-i` は、コンテナの標準入力を開きます。`-t` は、tty（端末デバイス）を使う、と、私の持っている参考書には載っています。意味も分からずつけています('A`)
- `centos:centos7` : 最初の `centos` は、docker イメージのリポジトリ名です。その次の `centos7` は、タグの名前です。
- `/bin/bash` : コンテナ起動時、フォアグラウンドで実行するコマンドを指定しています。ここでは、bash を起動しています。

コンテナを起動した後の表示が

```
[root@70a9666f1974 /]#
```

となっていますね。
これは、コンテナ内に入っていることを示しています。
試しにコマンドを実行してみます。

```
[root@70a9666f1974 /]# hostname
70a9666f1974
[root@70a9666f1974 /]# ip addr show
bash: ip: command not found
[root@70a9666f1974 /]# ifconfig
bash: ifconfig: command not found
[root@70a9666f1974 /]# cat /etc/redhat-release 
CentOS Linux release 7.6.1810 (Core)
```

全然コマンドがない・・・
しかし、CentOS7.6 が動いているのだけは分かりましたね。
ここで `exit` と打ってみましょう。

```
[root@70a9666f1974 /]# exit
exit
[root@localhost vagrant]# 
```

コンテナから抜けました。
試しにコンテナの状態を見てみます。

```
[root@localhost vagrant]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@localhost vagrant]# 
```

あれれ、見つかりません。
それもそのはず、`docker run` で起動した `/bin/bash` のプロセスを `exit` で終了したので、コンテナは終了されました。
`-a` オプションをつければ確認可能です。

```
[root@localhost vagrant]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                          PORTS               NAMES
70a9666f1974        centos:centos7      "/bin/bash"         12 minutes ago      Exited (0) About a minute ago                       naughty_gates
```

では次に、このコンテナを起動しっぱなしにしましょう。

```
[root@localhost vagrant]# docker start 70a9666f1974
```

このコマンドは、先ほど終了してしまったコンテナを起動し直します。
`-i` オプションをつければまた標準入力に入れるようですが、今回はバックグラウンド実行させたいためオプションはつけていません。
状態を見ると、以下のようになっています。

```
[root@localhost vagrant]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
70a9666f1974        centos:centos7      "/bin/bash"         16 minutes ago      Up About a minute                       naughty_gates
```

先ほどのコンテナが復活しました。
なお、初めからバックグラウンド実行するためには以下のように起動します。

```
[root@localhost vagrant]# docker run -di centos:centos7 /bin/bash
458d8d9871b053ee3d46d72736a7cdd3846108b258c89acc80099a1c2a10f2ca
[root@localhost vagrant]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
458d8d9871b0        centos:centos7      "/bin/bash"         2 seconds ago       Up 1 second                             elated_banzai
70a9666f1974        centos:centos7      "/bin/bash"         19 minutes ago      Up 4 minutes                            naughty_gates
```

新たに `-d` オプションをつけることによって、標準入力に入ることなくコンテナを起動できました。
ちなみにこの起動時には `-t` オプションをつけなかったのですが、正直これの意味が分かってないのでつけてもつけなくてもちゃんと動きます。
`-i` オプションをつけなかったら起動して即終了していたので必要っぽいです。
おそらく、 `/bin/bash` の場合は標準入力を与えてあげないと起動し続けることはできないのかと・・・

では次に、標準入力に入った後でコンテナを終了せずに抜ける方法です。
また、コンテナを立ち上げます。

```
[root@localhost vagrant]# docker run -it centos:centos7 /bin/bash
[root@e250cdae49fe /]# 
```

起動してコンテナに入っています。
この状態で、キーボードの Ctrl + p, Ctrl + q と順番に入力します。
そうするとコンテナから抜けることができます。
抜けた後、状態を確認するとコンテナは起動したままとなっています。

```
[root@localhost vagrant]# docker run -it centos:centos7 /bin/bash
[root@e250cdae49fe /]# [root@localhost vagrant]# 
[root@localhost vagrant]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
e250cdae49fe        centos:centos7      "/bin/bash"         About a minute ago   Up About a minute                       epic_allen
458d8d9871b0        centos:centos7      "/bin/bash"         4 minutes ago        Up 4 minutes                            elated_banzai
70a9666f1974        centos:centos7      "/bin/bash"         23 minutes ago       Up 8 minutes                            naughty_gates
```

たくさんコンテナを作成してしまいました。
一括で終了させるためには、以下のようにすると楽です。

```
[root@localhost vagrant]# docker stop `docker ps -q`
e250cdae49fe
458d8d9871b0
70a9666f1974
[root@localhost vagrant]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
e250cdae49fe        centos:centos7      "/bin/bash"         2 minutes ago       Exited (137) 3 seconds ago                       epic_allen
458d8d9871b0        centos:centos7      "/bin/bash"         6 minutes ago       Exited (137) 3 seconds ago                       elated_banzai
70a9666f1974        centos:centos7      "/bin/bash"         25 minutes ago      Exited (137) 3 seconds ago                       naughty_gates
```

`docker stop` の引数として `docker ps -q` の戻り値を渡してあげます。
全部終了しても問題ない時にだけ使いましょう。

コンテナの削除も以下のようにして一発です。

```
[root@localhost vagrant]# docker rm `docker ps -aq`
e250cdae49fe
458d8d9871b0
70a9666f1974
[root@localhost vagrant]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@localhost vagrant]# 
```

こちらのコマンドでは、起動中のコンテナは削除されないため安心して実行することができます。

# 終わり

まだ一歩目を踏み出しただけですが、ここまで書くのに疲れました・・・
次回は `docker build` の説明ができたらなあと思います。

docker build の記事ではないですが、続編を書きました↓
[Docker を使う（python のイメージで色々確認してみる）](https://qiita.com/landwarrior/items/fd918da9ebae20486b81)