はじめてのDockerfile

## はじめに

[前回の記事](https://qiita.com/suzu12/items/315fba934de11af826bd)では
Dockerレジストリにあるイメージを pull してそのイメージを使用してコンテナを立てました。
このようにレジストリで提供されている Docker イメージを使用してコンテナをたてることもできますが、
自分で手を加えて自分の使いたい形でコンテナをたてることもできます。

1. [ゼロから始めるDocker入門](https://qiita.com/suzu12/items/315fba934de11af826bd)
2. [はじめてのDockerfile](https://qiita.com/suzu12/items/c4bc47c0df6ec9b9290b)           ←今回はここ
3. [はじめてのDocker compose](https://qiita.com/suzu12/items/4ec2cc8c1a7c23aa112b)


## 対象読者

- `Dockerfile` を作成して `image` の作成を試したい方」
- `Docker instruction` にどのようなものがあるか知りたい方


## 目次

- [はじめに](#はじめに)
- [対象読者](#対象読者)
- [目次](#目次)
- [Dockerfile](#dockerfile)
    - [Sample](#sample)
    - [dokcer build .](#dokcer-build-)
    - [dokcer build -f [PATH/Dockerfile] .](#dokcer-build--f-pathdockerfile-)
    - [docker run](#docker-run)
- [Docker instruction](#docker-instruction)
- [CMD と ENTRYPOINT の違い](#cmd-と-entrypoint-の違い)
- [ADD と COPY の違い](#add-と-copy-の違い)
- [後片付け](#後片付け)
- [おわりに](#おわりに)
- [参考文献](#参考文献-2)

## Dockerfile

`Dockerfile` とは **Dockerイメージ** の設計図となるもので
テキストファイル形式で作成してそのファイルからイメージを作成することができます。

### Sample

イメージを作成するために実際に　`Dockerfile`　を作成していきます。
適当なディレクトリに移動してそのなかで　`Dockerfile`　を作成します。
ディレクトリ内は `Dockerfile` のみにします。
（後に出てくるビルドコンテキストというものでカレントディレクトリを指定することが多いため。）
（**※ `.dockerignore`　を作成しファイルパスを記述すれば大丈夫なようです。**）

```sh:ターミナル
touch Dockerfile

# あとで使用したい copy.txt を作成し編集しておく。
touch copy.txt
```

```sh:copy.txt
# ここではわかりやすいように以下のような文字列を入れておく。
[COPY]でコピーしたファイルです。
```

```dockerfile:Dockerfile
# ubuntu image の最新をベースに
FROM ubuntu:latest

# /var/www/html で命令を実行していく(パスの指定は練習なので適当なもので大丈夫です。)
# コンテナ内に入ったときのパスにもなる
WORKDIR /var/www/html

# echo の内容を sample.txt に格納
RUN echo 'sampleです！' > sample.txt

# 前のステップで作成したテキストファイル
#     ホスト   コンテナ
COPY copy.txt copy.txt

# ubuntu の デフォルトコマンド はもとから bash だけど明示的に記述
CMD ["/bin/bash"]
```

### dokcer build .

`Dockerfile` の記述が終わったら
`Dockerfile` があるディレクトリ内で次のコマンドを叩きます。

```terminal:ターミナル
docker build .
```

すると `Dockerfile` で記述した命令がステップ毎に実行されていって
image　が作成されます。

作成した `image` を確認します。

```sh:ターミナル
# 確認方法①
## イメージ一覧の一番上に作成したイメージがあると思います。
docker images

# 確認方法②
## docker build .　したときの最後のあたりにshaからはじまる英数字の羅列の : のあと
## 少々長いですが、docker images　で出るものと同じです。
```

`build` は構築、
`.` はビルドコンテキストと呼ばれるものです。(`.` はカレントディレクトリを示しています。)


https://wa3.i-3-i.info/word1537.html


`docker daemon` にこのビルドコンテキスト内を渡しています。
要するにイメージを作成・構築するために `Dockerfile` を書きましたが
この `Dockerfile` を `docker daemon` に渡している、ということです。
(ややこしくなってしまいますが、渡すのは `Dockerfile` だけではなくビルドコンテキスト内の全てのファイルになることに注意してください。)


### dokcer build -f [PATH/Dockerfile] .

`Dockerfile` をカレントディレクトリに置かなくても `build` することができます。
それは　`-f` フラグを使用して `Dockerfile` のあるパスを指定するだけです。

https://docs.docker.jp/engine/reference/commandline/build.html#dockerfile-f

### docker run

作成したイメージからコンテナを立ち上げてみます。
(**※ 補足ですが、デフォルトコマンドを決めたので bash がなくとも docker run コマンドをしたときに自動的にシェルに入ることはできます。**)

```
docker run -it [image Id] bash
```

入ったら `/var/www/html` にいることがわかります。

また `ls` コマンドで確認したときに `COPY` 命令でコピーした `copy.txt` と `RUN` 命令で作成した `sample.txt` 作成されていることがわかります。

`cat` など使用して自分が編集した内容と一緒なのか確認もできます。

## Docker instruction

instructionとは命令のことです。
`Dockerfile` に命令を記述し
イメージを構築していきます。

| instruction | 意味 |　使用例| 
|:-:|:-:|:-:|
| FROM  | 基本的には Dockerfile の書き始めに使用する。用途はイメージの取得で構築するイメージのベースを指定する。  |FROM <イメージ名>|
| RUN  | イメージレイヤの階層化。Linuxコマンドを実行。  |RUN <コマンド>|
| CMD  | コンテナ実行時のデフォルトコマンドを指定。  |CMD ["/bin/bash"]|
| ENV  | 環境変数を示す  |ENV <キー>=<値>|
| ADD  | 追加元ファイル・ディレクトリを追加先に追加。追加元がローカルにあり圧縮ファイルの場合展開し追加される。  |ADD <追加元(src)> <追加先(container)>|
| COPY  | コピー元ファイル・ディレクトリをコピー先に追加  | COPY <コピー元> <コピー先>|
| ENTRYPOINT  | `docker run` 時に実行される。  |ENTRYPOINT ["実行ファイル", "パラメータ1", "パラメータ2"]|
| VOLUME  | ホストのファイルやフォルダのマウントポイント  |VOLUME ["/data"]|
| WORKDIRE | RUNやADDなどDocker instructionが実行されるディレクトリの変更  |WORKDIR /path/to/workdir|


https://docs.docker.jp/engine/reference/builder.html#builder-format


### CMD と ENTRYPOINT の違い

この2つは命令はどちらものコンテナ起動時になんのコマンドを実行するのかを決めるコマンド。


- `ENTRYPOINT` は `docker run` 時にデフォルトコマンドの上書きができない。

- 上書きできるのは `CMD` で書いた部分のみ

**(※ `ENTORYPOINT` でコマンドを指定した場合は `CMD ["パラメータ1", "パラメータ2"]` と指定することができます。)**


#### 参考文献

https://docs.docker.jp/engine/reference/builder.html#cmd-entrypoint

https://qiita.com/hihihiroro/items/d7ceaadc9340a4dbeb8f

https://qiita.com/uehaj/items/e6dd013e28593c26372d


### ADD と COPY の違い

どちらもファイルやフォルダのコピーができる。

違いとしては下記がある。

- `ADD` はコピー時に圧縮ファイルを解凍する
- `COPY` はしない

#### 参考文献

https://qiita.com/YumaInaura/items/1647e509f83462a37494

https://qiita.com/momotaro98/items/bf34eef176cc2bdb6892

## 後片付け

使用していないイメージコンテナを削除していきます。

```
docker image prune
```

```
docker system prune
```

## おわりに

`Dockerfile` の理解をすることによって自分で `Dockerfile` を書くことができますし、
プロジェクトで使用している内容を理解することもできるようになります。

とはいえ、馴染みのない `instruction` がでてきたり、応用的な使用をしていたりするとその都度調べることにはなります。ただ、基礎的なところを抑えておけば全く読めないということはなくなりますし、メンタル的にも良いかなとも思っています。

## 参考文献

- [Dockerfile リファレンス](https://docs.docker.jp/engine/reference/builder.html)
- [カレントディレクトリとは](https://wa3.i-3-i.info/word1537.html)
- [[docker] CMD とENTRYPOINT の違いを試してみた](https://qiita.com/hihihiroro/items/d7ceaadc9340a4dbeb8f)
- [DockerfileのCMDとENTRYPOINTを改めて解説する](https://qiita.com/uehaj/items/e6dd013e28593c26372d)
- [Dockerfile の ADD と COPY の違いを結論から書く](https://qiita.com/YumaInaura/items/1647e509f83462a37494)
- [DockerfileにてなぜADDよりCOPYが望ましいのか](https://qiita.com/momotaro98/items/bf34eef176cc2bdb6892)
- [ゼロから始めるDocker入門](https://qiita.com/suzu12/items/315fba934de11af826bd)