Dockerfileの書き方を深掘りしつつまとめてみた


## はじめに

この記事は、現在Dockerについて学習している自分が、Dockerfileの書き方について、
ファイル内に記述されているコマンドの意味などをアウトプットを兼ねて解説した内容になります。

## 過去に投稿したDocker関連の記事はこちら

#### 第1回Dockerハンズオン初心者・中級者編で学んだことをまとめてみた
https://qiita.com/yuta-ushijima/items/fdcaea4220e773a5ad2d

#### docker-compose.ymlの書き方について解説してみた
https://qiita.com/yuta-ushijima/items/d3d98177e1b28f736f04

## Dockerfileってなに？

Dockerを使ってアプリケーションを動かす際に、DockerImage(以下Image)の元となるファイルのこと。食べ物でいえば、原材料のようなものと思うとわかりやすいかもしれません。

Dockerfile内には、Imageを生成する際に実行させたいコマンドを、命令を実行するための環境変数の引数にして記述します。

## Dockerfileサンプルコード

```dockerfile

# 使用するrubyのバージョン
# rubyの2.4.1というImageをベースにDockerfileを作成する
FROM ruby:2.4.1

# このDockerfileのメタデータを記述
# MAINTAINERはdeprecateになった
LABEL maintainer="Yuta_Ushijima <contact@yuta-u.com>"|

# 文字コードの設定
# 日本語を受け付けるように設定(rails consoleで日本語を使うなど) => LANG=C.UTF-8
# 設定したロケールの値をC.UTF-8で上書きする => LC_ALL=C.UTF-8
# 文字コードのカテゴリをutf-8に設定 => LC_CTYPE="utf-8"
ENV LANG=C.UTF-8 \
    LC_ALL=C.UTF-8 \
    LC_CTYPE="utf-8"

# 使用するエディタ
ENV EDITOR vim 

# インストールするライブラリなどをshell形式で記述。\(バックスラッシュ)を使って、一つのRUNで複数のコマンドを実行できる。
RUN apt-get update --fix-missing \
  && apt-get -y upgrade \
  && apt-get install -qq -y \
    apt-utils \
    build-essential \
    ca-certificates \
    curl \
    git \
    glances \
    graphviz \
    htop \
    libcurl3 \
    libpq-dev \
    man \
    mysql-client \
    nano \
    nodejs \
    patch \
    software-properties-common \
    sudo \
    unzip \
    vim \
    wget \
    xvfb \
    zlib1g \
    zlib1g-dev \
  && apt-get autoremove -y \ => 不要なパッケージの自動削除。-yで確認ダイアログの非表示。
  && apt-get clean \ => パッケージキャッシュの削除
  && fc-cache -f -v => 利用可能なフォントのキャッシュを更新。

# bundle installをスムーズに実行させるため、予め重めのgemをここでinstallしていおく
RUN gem update --system
RUN gem install -q --no-rdoc --no-ri pkg-config --version 1.3.1
RUN gem install -q --no-rdoc --no-ri rails --version 5.2.1
RUN gem install -q --no-rdoc --no-ri nokogiri --version 1.8.4
RUN gem install -q --no-rdoc --no-ri mysql2 --version 0.5.2

# GemfiletとGemfile.lockをルートディレクトリにコピー
COPY Gemfile Gemfile.lock ./
# Gemfile.lockが通常のファイルであれば、bundle installを実行
RUN if test -f Gemfile.lock; then \
    bundle install --binstubs --jobs=4 --retry=3; \
  fi
# コピー元のGemfileとGemfile.lockを削除
RUN rm Gemfile Gemfile.lock

# ディレクトリの作成
RUN mkdir -p /memo-app/backend

# 実行するディレクトリの指定
WORKDIR /memo-app/backend

# 現在のディレクトリ(ここでは/memo-app/backend)の新規ファイルやディレクトリをルートディレクトリに追加
ADD . .

    # RAILS_SERVE_STATIC_FILES=trueにすることで、rails serverを起動時にpublic/assetsを読み込む
ENV RAILS_SERVE_STATIC_FILES=true \
    PORT=3000 \
    # コンテナにxtermというターミナルエミュレータを割り当てる
    TERM=xterm

ENV PORT=3000 \
    # RACK_ENV=development \
    # RAILS_ENV=development \
    RAILS_SERVE_STATIC_FILES=true \
    TERM=xterm

EXPOSE 3000
EXPOSE 3310
```

## Dockerfileで記述する環境変数について

環境変数の種類自体は、そこまで数が多くないので、それぞれを表にしてまとめてみます。

|項目|意味|リファレンスリンク
|:--:|:--:|:--:|
|FROM | ビルドする際にどのImageをベースにするか | http://docs.docker.jp/v1.10/engine/reference/builder.html
|LABEL|Dockerfileのメタデータを記述 | https://docs.docker.com/engine/reference/builder/#label
|ENV|環境変数のkeyとvalueを設定 | https://docs.docker.com/engine/reference/builder/#env
|RUN | 引数に設定したコマンドをビルド時に実行。引数の書き方がshell形式とexec形式がある。 | https://docs.docker.com/engine/reference/builder/#run
|COPY | プロジェクトソースにあるファイルやディレクトリを指定先にコピーする | http://docs.docker.jp/v1.10/engine/reference/builder.html#copy
|WORKDIR| RUNやCMDなどの命令を実行するディレクトリを指定 | http://docs.docker.jp/v1.10/engine/reference/builder.html#workdir
|ADD | 新しく追加されたファイルやディレクトリを指定先に追加 | http://docs.docker.jp/v1.10/engine/reference/builder.html#add
|EXPOSE | コンテナ実行時に指定したポートで接続用意ができていることをDockerに伝える | https://docs.docker.com/engine/reference/builder/#expose


## RUNにおけるapt-getコマンドについて

[Dockerfileのベストプラクティス](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html)の要点をまとめると、

* `apt-get`の`update`と`install`は同一のRUNで実行すること

```dockerfile
# -yで確認ダイアログを出さずにinstallを実行する
apt-get update && apt-get install -y \
```

* RUNの最終行で`apt-get clean`してキャッシュを削除すること
(apt-get clean allじゃなくてもapt-get cleanだけでOK。apt-get cleanのあとにどんな引数を書いても無視されるため)

```dockerfile
RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    lxc=1.0* \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && apt-get clean \
```

* 更新しやすくするために、installするライブラリはABCの降順で記述すること



## 参考サイト

#### Cロケールについて
https://docs.oracle.com/cd/E26924_01/html/E27144/glmbx.html

https://wiki.archlinux.jp/index.php/%E3%83%AD%E3%82%B1%E3%83%BC%E3%83%AB

#### Dockerfileのベストプラクティス
http://docs.docker.jp/engine/articles/dockerfile_best-practice.html

#### apt-getコマンドについて
https://qiita.com/white_aspara25/items/723ae4ebf0bfefe2115c

#### apt-get clean allについて
https://askubuntu.com/questions/144222/how-do-apt-get-clean-and-apt-get-clean-all-differ

#### fc-cache -fvについて
https://qiita.com/nju33/items/b80d92a4257edeb4b9a1

#### シェルスクリプトのif文について
https://shellscript.sunone.me/if_and_test.html

#### xtermについて
http://docs.docker.jp/v1.12/engine/reference/run.html#id67