5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

②1日10分で理解するコンテナ技術入門 - Dockerfileの書き方 -

Last updated at Posted at 2024-06-11

はじめに

前回の記事では、nginxのコンテナイメージを使用してdocker container runなどのコマンドの使用例を説明しました。ここで使用したnginxのイメージは、公式に配布されているイメージでしたが、Dockerfileを用いると自分好みにカスタマイズしたコンテナイメージを作成できるようになります。コンテナを使っていくうえで、Dockerfileはほぼ必ず使うものになるので、この記事でDockerfileの使い方とイメージをつかんでいただければと思います。

Dockerfileとはどんなものか

Dockerfileは、dockerコンテナのイメージを作成するための設定ファイルです。アプリケーションに必要なパッケージをインストールしたり、必要なファイルをコピーしたりと、コンテナを作成して動作させるために必要な操作を記述します。

例えば、nginxを使って自分で作ったhtmlを公開したい場合、下記のイメージのように、自分で作ったhtmlファイルをnginxのベースイメージに加える必要があります。
image.png

そのため、Dockerfileには以下のようなことを記述します。
image.png

nginxのイメージをベースに、自分で作ったhtmlファイルを表示するようなイメージを作成する例をお見せしたいと思います。nginxでは、デフォルトの状態では以下のようなページを表示するウェブサーバが起動します。
image.png

起動した際に表示するページを、デフォルトのページから自分が作成したページに置き換えるためのDockerfileを作成してみます。
以下のようなページを表示することを目標にします。
image.png

用意する必要があるのは、

  1. カスタムイメージのためのDockerfile
  2. 表示するhtmlファイル
    の2点です。下記の様なディレクトリ構成でこれらのファイルを配置します。
.
├── Dockerfile
└── html
    └── index.html

Dockerfileは、概要で説明したような手順と同様で、以下のように記述します。

# ベースイメージとして公式のnginxイメージを使用
FROM nginx:latest

# カスタムのindex.htmlをnginxのデフォルトのドキュメントルートにコピー
COPY html/index.html /usr/share/nginx/html/index.html

# デフォルトのnginxコマンドを実行
CMD ["nginx", "-g", "daemon off;"]

htmlファイルは、サンプルとして以下のようなものを使用します(このファイルの内容は本記事の本筋とは関係ありません)。

<!DOCTYPE html>
<html>

<head>
  <title>Custom Nginx Page</title>
  <style>
    body {
      width: 50em;
      margin: 0 auto;
      font-family: Arial, sans-serif;
      background-color: #f4f4f4;
    }

    header {
      background-color: #333;
      color: white;
      padding: 10px 0;
      text-align: center;
    }

    nav ul {
      list-style: none;
      padding: 0;
    }

    nav ul li {
      display: inline;
      margin: 0 10px;
    }

    nav ul li a {
      color: white;
      text-decoration: none;
    }

    .content {
      margin: 20px 0;
    }

    footer {
      text-align: center;
      padding: 10px 0;
      background-color: #333;
      color: white;
    }
  </style>
</head>

<body>
  <header>
    <h1>Welcome to My Custom Nginx Page!</h1>
    <nav>
      <ul>
        <li><a href="#">Home</a></li>
        <li><a href="#">About</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </nav>
  </header>
  <div class="content">
    <p>If you see this page, the nginx web server is successfully installed and working with a custom index.html file.
    </p>
  </div>
  <footer>
    <p><em>Thank you for using nginx.</em></p>
  </footer>
</body>

</html>

Dockerfileのあるディレクトリに移動し、以下のコマンドでコンテナイメージをビルドします。

docker build -t custom-nginx .

-tオプションを指定すると、作成するコンテナイメージの名前を指定することができます。この場合はcustom-nginxという名前を指定しています。
docker image lsコマンドで作成したコンテナイメージを確認します。

$ docker image ls
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY               TAG         IMAGE ID      CREATED         SIZE
localhost/custom-nginx   latest      1c4d9598f126  37 seconds ago  192 MB
docker.io/library/nginx  latest      4f67c83422ec  11 days ago     192 MB

localhost/custom-nginxが、いま作成したカスタムのnginxイメージで、docker.io/library/nginxが、カスタムのnginxイメージを作成するうえでベースとして使用したコンテナイメージになります。
ではこのコンテナイメージから、docker container runコマンドでコンテナを起動します。

$ docker container run -d -p 10000:80 localhost/custom-nginx:latest
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
4586930a4c616e5afcc4f978b243bcf6aa06f8dd7bb1d0b14d1542d08e1e765d

docker container lsコマンドで、コンテナの起動を確認します。

$ docker container ls
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID  IMAGE                          COMMAND               CREATED         STATUS         PORTS                  NAMES
4586930a4c61  localhost/custom-nginx:latest  nginx -g daemon o...  41 seconds ago  Up 41 seconds  0.0.0.0:10000->80/tcp  nostalgic_kepler

無事に起動しているので、ブラウザでhttp://localhost:10000にアクセスすると、カスタムのページが表示されます。

Dockerfileでよく使うコマンド

よく使うコマンドは以下のようなものになります。

コマンド 説明 使用例
FROM ベースとなるイメージを指定します。 FROM ubuntu:20.04
RUN イメージのビルド時に実行するコマンドを指定します。 RUN apt-get update && apt-get install -y nginx
CMD コンテナ起動時に実行するデフォルトのコマンドを指定します。 CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT コンテナ起動時に実行するメインのコマンドを指定します。 ENTRYPOINT ["docker-entrypoint.sh"]
COPY ホストシステムからコンテナにファイルまたはディレクトリをコピーします。 COPY myapp/ /usr/src/myapp/
ADD COPY と似ていますが、URLからのダウンロードやアーカイブの自動解凍もサポートします。 ADD myapp.tar.gz /usr/src/myapp/
ENV 環境変数を設定します。ビルド時と実行時に使用されます。 ENV APP_ENV=production
EXPOSE コンテナがリッスンするネットワークポートを指定します。メタデータの一種で、実際に何か操作を行うわけではなく、コンテナがリッスンするポートを分かりやすくしています。 EXPOSE 80
WORKDIR 作業ディレクトリを設定します。この命令以降の、RUNコマンド等のコマンドが実行されるディレクトリを指定します。 WORKDIR /usr/src/myapp
USER コンテナ内でコマンドを実行するユーザーを指定します。 USER appuser
VOLUME ホストとコンテナ間でマウントされるボリュームを指定します。 VOLUME /var/lib/mysql
ARG ビルド時に渡される引数を指定します。 ARG build_env=production
LABEL メタデータをイメージに追加します。バージョン情報やメンテナンス情報を含めるのに便利です。 LABEL version="1.0" description="A simple example"
ONBUILD このイメージが他のイメージのベースとして使用されたときに実行するコマンドを指定します。 ONBUILD ADD . /app/src

Dockerfileの使用例

上記で紹介したコマンドを使って、もう少し発展的なDockerfileを作成してみます。次に作成するイメージは、ubuntu:20.04をベースに、nginxをインストールし、カスタムhtmlファイルを表示します。また、環境変数の設定、コンテナイメージにラベルを追加、コンテナが使用するポートの明示などを行います。

FROM ubuntu:20.04

# メタデータの追加
LABEL maintainer="yourname@example.com" version="1.0"

# 環境変数の設定
ENV APP_ENV=production

# 作業ディレクトリの設定
WORKDIR /usr/src/app

# パッケージのインストール
RUN apt-get update && apt-get install -y nginx

# アプリケーションコードのコピー
COPY html/ /usr/share/nginx/html/

# ポートの公開
EXPOSE 80

# デフォルトのコマンド
CMD ["nginx", "-g", "daemon off;"]

docker image build -t custom-nginx-from-ubuntu .コマンドを実行して、上記のDockerfileからコンテナイメージをビルドします。

LABELでイメージをフィルタリング

LABELコマンドでラベル付しているので、フィルタリングすることができます。ここでは、docker image lsコマンド実行時にフィルタリングする方法を紹介します。
まず、いまあるイメージをdocker image lsコマンドで確認します。

$ docker image ls
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY                          TAG         IMAGE ID      CREATED        SIZE
localhost/custom-nginx-from-ubuntu  latest      49badc0cc0e3  2 minutes ago  188 MB
docker.io/library/ubuntu            20.04       5f5250218d28  6 days ago     75.2 MB

localhost/custom-nginx-from-ubuntuと、docker.io/library/ubuntuの二つのイメージがあることが分かります。ラベルでフィルターして、localhost/custom-nginx-from-ubuntuのみを表示してみます。
フィルタリングする場合、--filterオプションを使います。今回作成したカスタムイメージには、maintainerというラベルをキーとして付与したので、このキーを持つイメージを絞り込みます。docker image ls --filter "label=maintainer"コマンドを使います。

$ docker image ls --filter "label=maintainer"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY                          TAG         IMAGE ID      CREATED        SIZE
localhost/custom-nginx-from-ubuntu  latest      49badc0cc0e3  2 minutes ago  188 MB

このように、該当のラベルを持つイメージだけが表示されます。
また、ラベルはKey-Valueの形で指定しているので、Key-Valueの形でフィルタリングすることもできます。LABELを同じKey、違うValueで作成したDockerfileから、custom-nginx-from-ubuntu2というイメージをビルドしました。ラベルがmaintainerのキーを持つものでイメージを絞り込むと、以下のように二つのイメージが絞り込まれます。

$ docker image ls --filter "label=maintainer"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY                           TAG         IMAGE ID      CREATED        SIZE
localhost/custom-nginx-from-ubuntu2  latest      3fd56fc54470  2 minutes ago  188 MB
localhost/custom-nginx-from-ubuntu   latest      49badc0cc0e3  9 minutes ago  188 MB

ここから、Valueの値をもとに絞り込んでみます。maintainer=yourname@example.comのものを絞り込むと、

$ docker image ls --filter "label=maintainer=yourname@example.com"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY                          TAG         IMAGE ID      CREATED         SIZE
localhost/custom-nginx-from-ubuntu  latest      49badc0cc0e3  11 minutes ago  188 MB

このように最初に作成したほうのカスタムイメージが表示されます。また、maintainer=yourname2@example.comで絞り込むと、

$ docker image ls --filter "label=maintainer=yourname2@example.com"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY                           TAG         IMAGE ID      CREATED        SIZE
localhost/custom-nginx-from-ubuntu2  latest      3fd56fc54470  4 minutes ago  188 MB

このように後に作成したイメージが表示されます。

環境変数を確認

稼働しているコンテナに接続して、環境変数が正しく設定されているか確認してみます。
まずはコンテナをdocker runコマンドで実行します。

$ docker run -d -p 10000:80 custon-nginx-from-ubuntu 
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
a90bcbc31d3567e658a20ac2f84060cfb4fe0dcdefbe8d4d5eb72ff576a44a1e

コンテナに入る(コンテナで新しいシェルセッションを開始する)には、docker execコマンドを-itオプションを合わせて実行します。今回起動したコンテナのIDは、docker runコマンドの時の出力からa90~であることが分かったので、aをコンテナIDとして指定しています。また、シェルを実行したいため、実行するコマンドには/bin/bashを指定しています。

$ docker exec -it a /bin/bash
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
root@a90bcbc31d35:/usr/src/app# 

それでは、Dockerfile内で指定した環境変数を確認してみます。

root@a90bcbc31d35:/usr/src/app# echo $APP_ENV
production

このように、正しく設定されていることが分かります。

ENTRYPOINTとCMDの違い

ぱっと見よく似たENTRYPOINTCMDですが、当然異なる使い道で使われます。よくあるのは、

  • ENTRYPOINTでデフォルトで実行するコマンドを指定する
  • CMDでデフォルト引数を指定する

という使われ方があります。例えば、

FROM ubuntu:20.04

ENTRYPOINT ["echo"]
CMD ["hello"]

のようなDockerfileをビルドすると、デフォルトでは以下の様に実行されます。

$ docker build -t entry-cmd .
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
STEP 1/3: FROM ubuntu:20.04
STEP 2/3: ENTRYPOINT ["echo"]
--> 52812f7b33e5
STEP 3/3: CMD ["hello"]
COMMIT entry-cmd
--> 021f720c1602
Successfully tagged localhost/entry-cmd:latest
021f720c1602a360bcedb797e22350746198086982311c678e4065e021ffd6fd

$ docker run entry-cmd
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
hello # CMDで指定したデフォルト引数のhelloが出力される

docker runのときの引数として異なる値を渡すと、以下のようになります。

$ docker run entry-cmd world
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
world

ENTRYPOINTで指定されたコマンドは、docker run時に--entrypointオプションをつけて上書きしない限り必ず実行されます。CMDで指定されたものは、docker run時に引数として与えることでそのまま上書きされます。そのため、このような使いわけを行います。
ちなみに、下記ようにENTRYPOINTCMDを入れ替えて実行することはできません(ENTRYPOINTで指定されたものがコマンドとして認識されるため)。

FROM ubuntu:20.04

CMD ["echo"]
ENTRYPOINT ["hello"]
$ docker run entry-cmd
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
Error: runc: runc create failed: unable to start container process: exec: "hello": executable file not found in $PATH: OCI runtime attempted to invoke a command that was not found

まとめ

以上、Dockerfileの基本的な使い方でした。次回はシンプルなウェブアプリを実装し、Dockerで実行する場合とそうでない場合を比較したいと思います。
次の記事

5
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?