LoginSignup
1
3

More than 3 years have passed since last update.

Dockerfileを書くにあたって気をつけなきゃと思ったこと

Last updated at Posted at 2021-04-18

想定読者

  • Dockerは使ったことあるけど、Dockerfileを真面目に書いたことはない方
  • 気にすべきことを1つでも知っておきたいと思っている方

気をつけなきゃと思ったこと

1. コマンドの微妙な違いを理解しておく

1.1 RUN cd <dir>WORKDIR <dir>

 どちらも作業ディレクトリを指定するという意味では同じですが、微妙な違いを認識しておらず少しハマりました。
例えば、centosのベースイメージにnginxをソースからビルドしてインストールする場合、深く考えずに

Dockerfile(抜粋)
RUN cd /usr/local/src
RUN curl -O https://nginx.org/download/nginx-1.18.0.tar.gz
RUN tar xvzf nginx-1.18.0.tar.gz
RUN cd nginx-1.18.0
RUN ./configure
RUN make
RUN make install

のようにかいて docker build しようとすると、

/bin/sh: ./configure: No such file or directory

となります。
はじめは、curlでファイル取れてない?tarで解凍失敗した?などなど考え検証するのですが、実際には4行目で /nginx-1.18.0 に移動できていないことが原因だったりします。

RUN cd nginx-1.18.0とだけ書いてしまうと、/nginx-1.18.0を一瞬覗いただけで実際には移動しておらず、その後のコマンドはカレントディレクトリで実行されてしまうため、./configureなんてないよ、と言われているという次第です。なので一行目のcdも機能しておらず、結果的に全てのコマンドが/で実行されている形になっています。

こんなときに、WORKDIRを用いて、

Dockerfile(抜粋)
WORKDIR /usr/local/src
RUN curl -O https://nginx.org/download/nginx-1.18.0.tar.gz
RUN tar xvzf nginx-1.18.0.tar.gz
WORKDIR /usr/local/src/nginx-1.18.0
RUN ./configure
RUN make
RUN make install

のようにに書きかえてあげるとちゃんとディレクトリを渡り歩いてうまく動いてくれます1
ちなみに、RUN cdを使って4行目以降を

RUN cd /usr/local/src/nginx-1.18.0 && ./configure && make && make install

のように書くこともできます。
というか、イメージ容量削減の観点では後者の方が望ましいようです。(説明は3.2にて)

1.2 ARGENV

変数を定義するという意味では同じですが、ARGは頻繁には使われないイメージです。違いは以下。

  • ENV → コンテナ内に定義される環境変数
  • ARG → Dockerfile内でのみ使用できる変数

私は ARG は主に、

Dockerfile(抜粋)
ARG NGINX_VERSION

RUN curl -O https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
docker build --build-arg NGINX_VERSION=1.18.0 --tag=sample_image:sample_tag

のようにビルド時に --build-arg で渡したバージョンをDockerfile内で参照する形で使っています。

1.3 VOLUMEdocker run -v

これは、Dockerfileのコマンドというより、Dockerのマウントの種類の違いに関するものなので少し逸れますが、若干ハマったのでメモ的に記載します。マウントの種類とその違いについては各自調査いただければと思います。

 Dockerfile内に VOLUME <path> とかくと、docker run -v <path>:<path> と書いたのと同等なのかと思っていましたが、前者は マウントの種類のうち「(Docker)ボリューム」によるマウントで、後者は「バインドマウント」によるマウントであって、挙動が異なるということを書いておきます。(詳細は長くなりそうなので別記事で書くかもしれません。)

2. 認証情報等をイメージに残さないようにする

社内環境でDockerを使うととにかくプロキシの壁にはまります。

  • docker build しようとしたら、初っ端からエラー => ビルド時にプロキシ認証情報を渡す
  • yum install / wget / curlしようとしたら名前解決エラー => プロキシ認証情報を設定ファイルに書き込んでから実行2 or 実行時に引数で渡す3

などなど。
ビルドするときだけでこれだけプロキシ認証情報を入れ込むタイミングがあるので、気をつけないとイメージの中のどこかに残ったままになってしまう恐れがあります。考えられるbetterな対応としては、

ビルド時にプロキシ認証情報を渡すとき
docker build --build-arg HTTP_PROXY=${HTTP_PROXY} --build-arg HTTPS_PROXY=${HTTPS_PROXY} --tag=sample_image:sample_tag

のように、ホストに事前に設定した環境変数HTTP_PROXY, HTTPS_PROXYを--build-argで渡す4

プロキシ認証情報を設定ファイルに書き込んだ場合

Dockerfileのなかで、書き込んだプロキシ情報をsedなどで一通り削除しておく4

3. イメージの容量がなるべく軽くなるようにする

3.1 なるべくオフィシャルイメージを使う

オフィシャルイメージは熟練者が書いたDockerfileで作られており無駄がないので、特別な理由がない限りはオフィシャルイメージを使うことをおすすめします。

以下は、nginx(latest)のイメージを例にイメージを比較した表です。

容量 ベースイメージ nginxインストール方法
Official Image 126 MB debian:buster-slim 不要
自作1 328 MB centos 8 yum install nginx
自作2 487 MB centos 8 ソースからビルド

「現実にはcentosあたりをベースに自作するケースが多いのでは」という仮説から自作のものはcentos 8をベースにしているので熟練者の削減術の成果がわかるものではありません。あくまで参考数値とご認識ください。centosはalpineやdebianと比べるとだいぶ容量をとるようですね。

3.2 RUNコマンドは極力まとめる

依存ライブラリがたくさんあるときに、

Dockerfile(抜粋)
RUN yum install -y gcc
RUN yum install -y pcre pcre-devel
RUN yum install -y zlib zlib-devel
RUN yum install -y openssl openssl-devel
RUN yum install -y make

のように一つ一つRUN する形で書くと、その都度イメージのレイヤが追加されてイメージの容量の増加に繋がってしまうようです。こういうときは、

Dockerfile(抜粋)
RUN yum install -y gcc \
    && yum install -y pcre pcre-devel \
    && yum install -y zlib zlib-devel \
    && yum install -y openssl openssl-devel \
    && yum install -y make

のように一つの RUN でまとめてみましょう。

3.3 マルチステージビルドを活用する

Vue.js アプリケーションを Docker 化する にあるように、1つのDockerfileの中で

  • アプリのビルドイメージ作成ステージ
  • ビルド成果物デプロイ用イメージ作成ステージ

のように複数のステージを定義し連携することで、アプリのビルド時のみ必要であったファイル等を最終的なイメージに含めなくて済み、イメージの軽量化につながるというものです。

そのほかにも、イメージの軽量化につながる工夫が公式ページ(以下)等にたくさん書かれているので、Dockerfileの熟練者を目指す方は熟読と実践をしてみていただければと思います。

終わりに

Dockerはなかなか慣れるまで時間がかかります。次はdocker-composeについてかけたらと思います。


  1. 本題から逸れるので省きましたが、実際にはこの前にここに記載のある依存ライブラリ(とgccとmake)のインストール処理が必要です。 

  2. yum → /etc/yum.conf, wget → ~/.wgetrc, curl → ~/.curlrc 

  3. wget → -e http_proxy={プロキシ認証情報}, curl → -x {プロキシ認証情報} (yumの場合は --setopt=proxy={プロキシ認証情報}でできるとの情報もあるが動作未確認) 

  4. これで完全にプロキシ認証情報が残らないと言い切れるほど検証ができておりませんゆえ、その点ご容赦ください。 

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3