ゆるーいDocker入門

# はじめに

Dockerを何となくで使っている勢なので、自分なりに少しまとめてみようかなと思いました。
対象読者はある程度Docker知っている人向けです。
タイトルにはゆるーいと書いてますが、少し長いです。。。

# 前提条件

* Dockerのインストール方法は記載しません
* ターミナルがある程度使えることが前提の内容となります

# そもそもDockerって何？

Dockerは仮想化技術の一種という理解です。[VMware Workstation Player](https://www.vmware.com/jp/products/workstation-player.html)や[Oracle VM VirtualBox](https://www.virtualbox.org/)のようなイメージです。1台のPC上に複数のOSを立ち上げて管理できる、という感じでしょうか。
正確にはVMWareはハイパーバイザ型仮想化、Dockerはコンテナ型仮想化という違いはありますが、イメージ的には同じ感じです。

Docker使うと1台のPC上にDebianの環境を作ることも、Ubuntuの環境を作ることもできるし、簡単に削除もできる。例えば、Linuxの環境が欲しいけど、PCに丸ごとインストールするのはなー、という場合に便利です。

# Dockerのメリットは？

世の中でここまで広く普及しているのでたくさんメリットはありますが、簡単にまとめてみます。

### 同じ環境を簡単に共有できる

例えば、Aさんの環境では動くけど、Bさんの環境では動かない、ということはよくあると思います。
Dockerの場合、Dockerfileというファイルを共有するだけで同じ環境を共有することができます。
たった一つのファイルを共有するだけで同じ環境共有できるってすごくないですか？

### CI/CDと相性が良い

CI/CDの説明は省略しますが、CIの中でテストを実行する場合、その環境と同じものをローカルでも用意したい、と思うかと思います。
そうでないと事前に動作確認ができないので。特にCIの環境はLinuxベースのものが多いので、ローカルでの開発でテストを回す場合、CIと同じ環境を用意してテストを実行する、ということができると非常に開発体験が良いです。

### Docker Hubが充実している

[Docker Hub](https://hub.docker.com/)にいろんなイメージがあるのですが、おそらく大半の人が欲しいイメージがそろっていると思います。
なので、基本的には自分でDockerfileをゴリゴリと記述することなく、自分がやりたいことが実現できることが多いです。
例えば、Tensorflow使いたいなという場合も公式のイメージありますし、Pythonもそれぞれのバージョンのイメージがあります。


# とにかく使ってみよう

とりあえず使ってみるのが早い、ということで使ってみましょう。
今回は先日RCが発表されたPython 3.10のイメージを動かしてみます。

### イメージのダウンロード

まずは利用するイメージをダウンロードします。
イメージのダウンロードは`docker pull`というコマンドを使います。

```bash
$ docker pull python:3.10.0rc1-buster
```

ダウンロードしているイメージの一覧は`docker images`コマンドで確認します。

```bash
$ docker pull python:3.10.0rc1-buster
```

pythonのイメージがダウンロードされていることを確認して下さい。

![Snag_13eedb5c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/b70c701b-e921-b067-922f-5cc11dddba3b.png)

#### 補足： イメージに関して

イメージは以下のような構成になっています。
`<イメージ名>:<tag>`

今回の場合はpythonがイメージ名、3.10.0rc1-busterがタグ名です。
ユーザ作成のイメージの場合はイメージ名の前にユーザ名が入ったりますが、考え方は同じです。
タグ名を変えることでPython 3.9のイメージをダウンロードすることも可能です。

### イメージからコンテナを作成する

次に、イメージからコンテナを作成します。
今回はコンテナの詳細な説明は省略しますが、仮想環境上で動くOSみたいなイメージです。
(厳密にはコンテナ型仮想化の場合カーネルはホストOSのものを利用しますので、コンテナ内にカーネルは存在しません)

```bash
$ docker create --name python_3_10 -it python:3.10.0rc1-buster
```

コンテナの作成には`docker create`コマンドを利用します。
文法は以下です。

```
docker create --name <コンテナ名、任意> -it <イメージ名>
```

作成したコンテナを表示します。

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

以下の画像のように1レコード表示されていればOKです。
先ほど作成したコンテナが作られていますね。
(CONTTAINER IDは画像と異なるかもしれません。)

![Snag_14104909.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/88aa011b-59b3-9da7-9e34-b22b15a3952a.png)


`docker ps`の表示内容について簡単に捕捉します。

* CONTAINER ID
    * コンテナごとの固有の識別子。コンテナを指定する際に使用する。例えば削除するときなど。
* IMAGE
    * コンテナがどのイメージから作成されたのか
* COMMAND
    * コンテナが実行された際に実行されるコマンド
* CREATED
    * いつ作成されたのか
* STATUS
    * コンテナの状態。作成後、実行中、停止中などがある
* PORTS
    * コンテナとホスト間でポートを共有している場合はその情報を表示
* NAMES
    * コンテナの名前、コンテナを実行する際などに利用する


### 作成したコンテナを実行する

では、作成したコンテナを実行してみましょう。

```bash
$ docker start -i python_3_10
```

以下の画像のようにPython 3.10のプロンプトが表示されていますね。

![Snag_141197b1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/710a706c-2f77-e5dd-1681-b6a7a3876fc2.png)

`docker ps`を実行した際にCOMMANDには「python3」と記述されていました。
つまり、コンテナを起動するとデフォルトではpython3コマンドが実行される、ということになります。

### コンテナの内部に入ってみる

次に、このコンテナ内部に入るために新しいコンテナを作成します。
(pythonのプロンプトはCtrl+Dで閉じることができます)

```bash
$ docker create --name python_3_10_bash -it python:3.10.0rc1-buster /bin/bash
```

実際にコンテナが2つ作成されていることを確認します。

![Snag_142c0ac0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/245a3a5c-b38f-bea6-b6b1-8936062e1bfd.png)


ここで重要なのは新しく作成したコンテナのCOMMANDが「/bin/bash」になっていることです。
つまり、新しく作成したコンテナは起動するとデフォルトで「/bin/bash」が実行される、ということになります。

では、実行してみましょう。

```bash
$ docker start -i python_3_10_bash
```

すると、以下の画像のようにプロンプトが表示され、コマンドを実行することができます。

![Snag_141e2e0b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/57efa03d-c045-c0af-e03c-17497a81270c.png)

python3コマンドを実行すると、最初に作成したコンテナと同じ表示がされることがわかります。

![Snag_141f6032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/f7b0c02c-11ff-9735-f3bd-bd898a882457.png)

なお、コンテナから抜ける場合は、「Ctrl+p Ctrl+q」を利用します。
exitコマンド等を利用するとコンテナ自体の起動が終了するので注意してください。
「Ctrl+p Ctrl+q」で終了した場合は、以下の画像のようにコンテナの状態がUPとなっており、起動中であることがわかります。

![Snag_1421f11e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/a51f5356-7882-cd90-a119-9929af759a04.png)

起動中のコンテナに再度接続する場合は以下のコマンドを実行してください。
(`docker attach`コマンドでも可能、ただし挙動が一部異なる)

```bash
$ docker exec -it python_3_10_bash /bin/bash
```

`docker exec`の場合、新しいプロセルが立ち上がるので、exitしてもコンテナ自体は停止しませんが、attachの場合は、直接コンテナのターミナルに接続するため、exitするとコンテナごと停止します。
(下図参照)

![Snag_1427870c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/a15dcad5-b95e-c5ed-7331-149522e1a122.png)

### 中間地点でのまとめ

これまでの内容でDockerイメージのダウンロード、コンテナの作成、コンテナ実行までを体験しました。
コマンドをたたくだけで、Python 3.10の環境を簡単に構築することができたと思います。
今回はすでに作成されているイメージをダウンロードするところから始めました。
次に皆さんが思うのは、そもそもイメージってどうやって作るの？ということだと思います。
では、次はイメージの作り方をみていきたいと思います。

# Docker イメージを作ってみよう

### Dockerfileを作成する

以下の内容でファイルを作成します

```dockerfile:Dockerfile
FROM debian:buster

RUN apt update &&\
    apt install -y tree

CMD ["/bin/bash"]
```

ファイルの内容を簡単に解説します。
「FROM debian:buster」の部分で、debian:busterイメージをベースに作る、という定義です。
Dockerfileは必ずベースイメージの定義が必要です。

「RUN」の部分では、コンテナ内で実行するコマンドを記述します。
今回はtreeコマンドのインストールを実行しています。

「CMD」の部分で、コンテナが起動した際にデフォルトで実行するコマンドを記述します。
今回はシェルを実行するようにします。

### Dockerイメージを作成する(ビルド)

作成したDockerfileが存在するディレクトリ内で以下のコマンドを実行します。

```bash
$ docker build -t custom-image:1.0 -f Dockerfile .
```

文法は以下です。

```
docker build -t <イメージ名:ラベル> -f <Dockerfileの名前> <Dockerfileが存在するディレクトリ>
```

実際にイメージが作成されているかどうかを見てみましょう。

```bash
$ docker images
```

以下の画像のように確かにイメージが作成されていますね。

![Snag_1442b9b2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/2d4932c3-d82f-cd44-b040-570f299bae4d.png)

### 作成したイメージからコンテナを作成、起動する

先ほどまでは`docker create`でコンテナ作成、`docker start`でコンテナ起動、としていました。
ただ、`docker run`コマンドを利用することで、`docker create`と`docker start`両方を一度にすることが可能です。

```bash
$ docker run -it --name custom-container custom-image:1.0
```

Dockerfile内でtreeコマンドをインストールしていましたので、実際に利用できるかも確認します。
以下の画像のようにインストールしたtreeコマンドがきちんと利用できていますね。

![Snag_144b3fa4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/e7430abf-bff7-b288-749d-6d21bd4416e6.png)

また、新しくコンテナが作成されていることも確認できます。
(下図参照)

![Snag_144d2af9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/dda864ad-5422-e0e1-e997-3a10ca91ff99.png)


### 中間地点でのまとめ

Dockerfileの作成からコンテナの作成までを体験しました。
一番最初にPython 3.10のイメージを利用した際の理解がより深まったのではないでしょうか。
Python 3.10のイメージも今回作成したDockerfileと同じように作成して、Python 3.10が動くようにしている、ということになります。
Docker Hubに公開されている様々なイメージも今回の方法と同じようにして作られています。
つまり、カスタマイズしたDockerfileを共有すれば、それだけで同じ環境を共有できる、という意味が理解できたのではないでしょうか。

# おまけ：Docker Composeについて

Dockerと同じくらいDocker Composeについても聞くのではないでしょうか。
Docker Composeも非常に便利なツールなので、簡単に説明します。

Dockerを単体で利用している場合、以下のような使いにくさを感じる時があります。

* コンテナ起動時のオプションがたくさんあって、毎回入力するのめんどくさい
* あるシステムで複数のコンテナを立ち上げる必要があるのだけど、それぞれのコンテナをいちいち立ち上げるのがめんどくさい

安心してください。その悩み、Docker Composeを使えば解決できますよ！
Docker Composeで個人的に便利だと思う部分は以下です。

* コンテナの起動オプションをファイルで管理できる
* 複数のコンテナを一括で起動できる
* 複数のコンテナを一つのファイルで管理できる

ユースケースとしては、ポートの設定や、ボリュームのマウント設定などが利用頻度多いですね。
最近はDocker単体で利用することはほとんどなくて、Docker Composeの利用がほとんどになってきています。

## docker-compose.ymlを作ってみよう

Dockerは対応するファイル名がDockerfileでしたが、Docker Composeはdocker-compose.ymlファイルになります。
docker-compose.ymlファイルを作成することが第一歩になります。

以下2つのファイルを作成してください。

* Dockerfile
* docker-compose.yml

```:Dockerfile
FROM debian:buster

RUN apt update &&\
    apt install -y tree jq

CMD ["/bin/bash"]
```

```:docker-compose.yml
version: "3"
services:
  py_3_7_4:
    image: python:3.7.4-buster
    stdin_open: true
    tty: true
    volumes:
      - type: bind
        source: "."
        target: "/home"
  tree_app:
    stdin_open: true
    tty: true
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - type: bind
        source: "."
        target: "/home"
  nginx:
    image: nginx
    stdin_open: true
    tty: true
    ports:
      - "8080:80"
```

## コンテナを起動してみよう

まずはDocker Composeでコンテナを作成してみましょう。
以下のコマンドを実行してください。
※Docker Composeで最初に覚えておく必要があるコマンドはupのみでOKです。

```bash
$ docker-compose up -d py_3_7_4
```

![Snag_5bca9b8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/ef9d158c-c1d8-a24e-70e8-800564536a22.png)

これでPython 3.7の環境のコンテナが作成できました。
では、YAMLファイルの中身を見ていきましょう。


```
version: "3"
services:
  py_3_7_4:
    image: python:3.7.4-buster
    stdin_open: true
    tty: true
    volumes:
      - type: bind
        source: "."
        target: "/home"
```


![Snag_5cd5c71.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/5e99a1fa-5724-300b-15fc-a08d037274fd.png)

py_3_7_4がサービス名です。Socker Composeでコンテナを起動するときに利用します。
imageの部分がベースイメージ名です。
stdin_openとttyは両方有効にすることで、コンテナ内での操作が可能になります。
volumesの部分でホストとコンテナ内のボリュームのマウント設定を実施します。
※volumeの設定が無いとコンテナ内部からホストのファイルを参照できません。

ちなみに、上記をDockerのみで実現しようとした場合は、以下のコマンドを実行する必要があります。

```bash
$ docker run -it --detach --name py_3_7_4 -v ${PWD}:/home python:3.7.4-buster
```

Docker Composeを使うと、事前に設定をファイルとして定義しておき、簡単なコマンドを実行するだけで設定済みのコンテナを作成できるので便利です。


## 自作コンテナを起動してみよう

```bash
$ docker-compose up -d tree_app
```

![Snag_5daa08f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/8f6c7309-20be-2107-b749-9c3d9366c12a.png)

docker-compose.ymlの内容はほとんど同じですが、buildの部分が異なります。
buildの部分にはDockerfileの場所を指定します。
これで、自分で作成したDockerfileからコンテナを起動することができます。

![Snag_5db168a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192873/4d0a9595-747a-8168-1fc7-42137514846a.png)

## 複数のコンテナを一度に起動してみよう

以下のコマンドを実行します。

```
$ docker-compose up -d
```

するとdocker-compose.ymlに定義されているコンテナすべてを一度に起動することができます。

# 参考リンク集

* [Dockerコンテナが簡単に作れる！【Dockerfile 】サンプル ](https://canmakewakuwaku.com/docker_dockerfile/)
    * Dockerfileの作成方法の参考に
* [チームにdockerを布教することになったので、布教の教材を作ってみた。](https://zenn.dev/a1008u/books/6bf96a769bedb2be53ae)
    * 内容が非常にわかりやすく丁寧に解説されているのでお勧めです
* [最低限これさえ知ってればいいDockerのコマンドまとめ](https://zenn.dev/jpn_asane/articles/feb6ca2b3cc715)


# 最後に

Dockerは最近のエンジニアはにとっては必須のスキルの一つになっています。
実際に使ってみると、本当にいろいろな場面で便利に使うことができます。
この記事で少しでも身近に感じて頂ければなと思います。