22
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

改めてDockerを理解する⑦[Dockerfileを細かく見ていこう]

Last updated at Posted at 2025-01-06

はじめに

こんにちは!ITスクールRareTECHにてCS(Customer Support)を担当している池村です。今回の記事はDockerfileについてです。DockerfileはDockerの基本コマンドとかなり密接に関わっていますので、最初からここに手をつけるのはお勧めしません。

①〜⑥まで読んでいない方はまずそちらを読んでからをおすすめします。
①はこちら

前回の記事はこちら

Dockerfileとは

まずDockerfileとはって何?というお話しですが、今までのややこしい環境構築等を一つのファイルにまとめて、イメージの作成をしちゃおうというものです。
例えば、前回の記事でWebサーバー(Flask)を立ち上げて環境構築するまでを一つのコマンドで実行していたわけですが、長ったらしくて面倒です。

長いコマンド
docker run --network my_network --name flask_container -v "$(pwd)/flask_app:/app" -w /app -p 5000:5000 python:3.9-slim sh -c "ls && pip install -r requirements.txt && python app.py"

これをファイルに書き込んでおいて、イメージ化し、実行するだけで終わらせることができます。

短縮できない記述もあります。

DockerHubにあるイメージ類も、基本的にはこのDockerfileで作られていることが多いです。

5e4522d1-0529-45c3-81eb-d62d1cc0c532_720.png

Dockerfileの基本的な要素

サンプルDockerfile

書き方
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt requirements.txt
COPY app.py app.py

RUN pip install -r requirements.txt

EXPOSE 5000

CMD ["python", "app.py"]

このDockerfileはサンプルであり、いい書き方ではないですが、初心者が順序よく理解するためにはちょうどいいものとして作成しています。

1. FROM

まずFROMですが、これはどのイメージをベースにするコンテナか決めるための項目です。searchpullで検索や取得をしていたイメージたちですね。もちろんタグ付けもできるので、今回はPythonの軽量なコンテナイメージである3.9-slimを選択しています。

Python以外で色々コンテナを作るとしたら、以下のような例があります。

FROMはイメージを選択
# Ubuntuの最新版を使いたい場合
FROM ubuntu:latest

# MySQLのコンテナを使いたい場合
FROM mysql:latest

# Nginxを使いたい場合
FROM nginx:latest

# ReactとかExpressを使いたい場合
FROM node:16-alpine

# Goを使いたい場合
FROM golang:1.20-alpine

# Javaを使いたい場合
FROM openjdk:17-jdk-slim

と、様々あります。作りたいコンテナによって、ここは変わってきますね。

一つのDockerfileに複数のイメージを書いているように見えますが、本来FROMで指定するのは一つですのでご注意ください。例として一つのファイルに複数書いているだけです。

2. RUN

次はRUNです。Dockerfileはイメージを作成するためのものです。このRUNは、イメージの作成中(build中)に実行されるものです。今回のサンプルのファイルの中ではpip install -r requirements.txtを実行していますね。

全てではありませんが、先ほどFROMで見てきたイメージに合わせたRUNを考えると以下が考えられます。

Ubuntu
Ubuntuの場合
FROM ubuntu:latest
RUN apt-get update && apt-get install -y vim git curl python3

Ubuntuをどう使いたいかにもよりますが、まずはパッケージ管理システムのアップデートが最初に来ることが多いです。その状態で色々なツールをインストールしています。

Nginx
Nginxの場合
FROM nginx:latest
RUN apt-get update && apt-get install -y openssl certbot

今回入れているのはSSL証明書の作成用opensslと無料かつ自動でSSL証明書を発行できるcertbotというツールです。apt-getを使っているのは、NginxのベースイメージがDebianやUbuntuを使用しているためです。

Node.js
Node.jsでReactのプロジェクトを作成したい場合
FROM node:16-alpine
RUN apk add --no-cache git && npm install -g create-react-app

ベースがAlpineLinuxなので、パッケージ管理システムはapkを使います。
--no-cacheをつけると、キャッシュを残しません。

本来、パッケージ管理システムはインストールした履歴のようなものを残していて、再インストール時に高速で取得できるようになっているのですが、正直一回入れてしまえば再インストールすることはほぼないので、だったら余計なキャッシュは残さず、その分イメージを軽量にします。

あとはReactのプロジェクトを作成しています。

Golang
GoでWebフレームワーク『Gin』を使用する場合
FROM golang:1.20-alpine
RUN apk add --no-cache git && go install github.com/gin-gonic/gin@latest

こちらもAlpineなのでapkでgitも入れておいて、Ginというフレームワークをインストールしておきます。

色々書きましたが、RUNはイメージを作成する時に実行してくれるコマンドを記述する場所です。それを覚えておいてください。

3. COPY(ADDも一応)とWORKDIR

COPYはホストのローカルにあるディレクトリ構成をコンテナ内にコピーする項目です。
これはボリュームとも関連してきます。特定のディレクトリにバインドマウントしたい場合、ここに書いていきます。

初学者にとって、ここはイメージがつきにくい部分だと思っています。じっくり理解していきたいです。

基本の部分
COPY <ソース> <ターゲット>

基本は上記👆です。

例えば
# ローカルのapp.pyをコンテナ内の/appの下にコピー
COPY app.py /app

ここで注意点です。
コンテナの中に/appなんてディレクトリあるの?という疑問ですね。
結論:ないです。
なので作る必要があります。

ここでWORKDIRが必要になってきます。

書き方
FROM python:3.9-slim

WORKDIR /app

WORKDIRを指定することで、コンテナ内での作業ディレクトリが決まります。これを設定してあげることで、COPYやRUNがその指定した作業ディレクトリで行われるようになります。

ここですごいのは、勝手に/appのディレクトリを作ってくれることです。
なのでWORKDIRCOPYRUNよりも前に書かれることが多いです。
順番的にこの辺りで説明した方が良さそうなのでここにしました。(迷走)

ということは

ここまでのまとめ
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt requirements.txt
COPY app.py app.py

RUN pip install -r requirements.txt
  1. イメージはPythonの軽量イメージ
  2. 作業ディレクトリは/app(コンテナ内のルートディレクトリにあるappというディレクトリの中)
  3. その中にrequirements.txtと、app.pyを同じ名前でコピー
  4. その後のそのrequirements.txtを使って必要なライブラリをインストール

requirements.txtとapp.pyは一緒にCOPYもできます。

その場合
COPY requirements.txt app.py .

としてください。これによって、同じ名前で.(/app)にコピーされます。

ADDについて

COPYを拡張した記法としてADDがあります。
これはCOPYと違って、圧縮ファイルなどを解凍してくれます。基本的な使い方はCOPYと同様です。

ADDの使い方
ADD <ソース> <ターゲット>

COPYと同じく、ローカルのファイルやディレクトリをコンテナ内の指定の場所にコピーします。

実際に使ってみる
# /app/に解凍されたファイルが展開
ADD my_archive.tar.gz /app/

# URLから直接持ってくることも可能
ADD https://example.com/file.txt /app/
特徴 COPY ADD
ローカルファイルのコピー ✅ 可能 ✅ 可能
圧縮ファイルの解凍 ❌ 不可能 ✅ 自動で解凍
リモートリソースの取得 ❌ 不可能 ✅ 可能
推奨される使用ケース シンプルなファイルやディレクトリのコピー 圧縮ファイルの解凍やリモートリソース取得

個人的にはCOPYがしっかり使えればOKだと思っています。

4. EXPOSE

EXPOSEはポート番号をどうするか決める部分です。今回のサンプルの場合、EXPOSE 5000としているので、5000番ポートを開けておくよ〜といった形ですね。

EXPOSEの例
# Flaskのデフォルトポート番号
EXPOSE 5000

# Djangoのデフォルトポート番号
EXPOSE 8000

# SSHなら
EXPOSE 22

# ファイルサーバーとかなら(Samba)
EXPOSE 445

ポートフォワーディングをしているわけではないので、起動時に外部との通信を行う際は、ホストポート:コンテナポートを指定して起動する必要があります。

5. CMD

次にCMDの書き方です。これはRUNと違って、コンテナの起動時に実行されるコマンドを書く部分です。大体はDockerfileの最後に記述されることが多い印象です。
これも例を見つつじっくり確認していきましょう。

Ubuntu
Ubuntuの場合
CMD ["bash"]

Ubuntuの場合、これを最後に書くことで、Bashが立ち上がります。なので、今まで打ってきたコマンドの一部を省略できます。

runを使う時一部を省略できる
docker run -it my_container

/bin/bashを省略することができました。ただ、-itは必須ですのでご注意ください。

Flask
Flaskの場合
CMD ["python", "app.py"]

これを最後に書いてあげると、app.pyに記述しているWebサーバーがコンテナ起動と同時に立ち上がります。

ReactやExpress
npmやnode系
CMD ["npm", "start"]
CMD ["node", "app.js"]

これを最後に書いてあげると、JS関係のアプリケーションを起動可能です。

ENV

ENVは環境変数です。.envファイルのように、大事な情報等を管理するために使われます。ただ、こういったファイルに入れるパスワードやトークンの情報は危険な情報です。使い方に注意です。

ENVの使い方
ENV APP_PORT=5000
CMD ["python", "app.py", "--port", "$APP_PORT"]

上記のような使い方は特に問題はないです。コンテナ内でしか使われない環境変数ですが、ここで機密情報を入れた状態のイメージを作成して、DockerHubに入れちゃったりしたら大変なことです。

結論、機密情報を入れないようにしましょう。

その他

LABEL:初学者は知らなくて良い。イメージの情報を入れられます。誰が作ったとかそういうの。

VOLUME:ボリュームを設定。Composeを使えるようになったら、正直使わない。

USER:コンテナ内でのユーザー管理用。デフォルトではrootユーザーになる。これを変えたい場合に使うが、まあ使わないですね。

ENTRYPOINTCMDと似ているが、docker runの時に上書きできるかできないかの違い。

CMDの場合
docker run my_app            # 実行: python app.py
docker run my_app ls         # 実行: ls
ENTRYPOINTの場合
docker run my_app            # 実行: python app.py
docker run my_app --version  # 実行: python app.py --version

長くなってしまいましたが、ここまでが基本的なDockerfileの要素です。

作ったDockerfileからイメージを作って起動しよう

ではここからは作ったDockerfileを実際にイメージ化して、コンテナとして立ち上げていきましょう。ここでは新しくbuildというコマンドが出てきます。

環境準備

まずは適当なディレクトリにDockerfileを作成しましょう。(ホーム以外)

環境準備用
mkdir test_flask && cd test_flask && touch Dockerfile app.py requirements.txt
こうなっていたらOK
.
├── Dockerfile
├── app.py
└── requirements.txt

次にDockerfileの中身を編集していきます。エディタはなんでも良いです。

ファイルに記述
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt requirements.txt
COPY app.py app.py

RUN pip install -r requirements.txt

EXPOSE 5000

CMD ["python", "app.py"]

記述が終わったら次にapp.pyの中身を書いていきましょう。

app.pyの中身
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, Qiita!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

できたら、次はrequirements.txtです。

requirements.txtの中身
Flask

これで準備は完了です。

イメージの作成

ではイメージを作成していきます。

書き方
docker build -t test_flask_app .

必ずDockerfileのあるところで実行してください。

-tはタグ付けです。イメージに名前をつける作業と思ってください。
今回はtest_flask_appという名前をつけました。

以下のようになれば問題なくイメージはできています。
image.png

できたイメージを確認するときはdocker imagesを叩いてみてください。

では次に、作ったイメージからコンテナを動かしていきます。

runしてみよう
docker run -d -p 5000:5000 test_flask_app

Macの方は5001:5000の方が良いかも。

では動いているか確認といきましょう。

curlコマンドで行う場合
curlでの確かめ方
curl http://localhost:5000

成功するとこんなレスポンスが帰ってくるはずです。(私は5001番ポートを使用)
image.png

ブラウザの場合

以下のリンクをクリックしてみてください。

成功すると、ブラウザの画面に文字が出ているはずです。
image.png

コンテナが動いているかどうかは、docker psで確認してみてください。Upなら動いているし、Exitedなら動いていないです。

おわりに

今回はDockerfileについて解説しました。
これがあるだけで今までの苦労がかなり短縮された気がしています。
Dockerfileにもたくさんの書き方がありますが、そこはいったん置いておいて、次はいよいよDockerComposeの話をしていきたいと思います。

これができたらアプリ開発の環境構築は怖くないです!頑張りましょう💪

余談

前のDockerネットワークの記事がやたら伸びているのを見て震えています。

22
23
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
22
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?