方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

【Docker】DockerFile・docker-compose.yml・Dockerコマンド

この記事は約17分で読めます。

Dockerとは:コンテナ型仮想化技術

  • コンテナ型仮想化技術:Dockerは、従来の仮想マシンよりも軽量で効率的なコンテナを用いて仮想化(パソコンの中に仮想パソコンを起動する)を実現
  • Dockerのコンセプトは、アプリケーションを開発、テスト、デプロイする過程をより効率的かつ一貫性のあるものにすること。これにより、環境の違いによって発生する問題(「うちのマシンでは動くけど、他のマシンでは動かない」というような問題)を大幅に減少させることができる。

Dockerイメージ

  • アプリケーションの設計図:Dockerイメージは、アプリケーションとその実行に必要なすべてのファイル、ライブラリ、依存関係、環境設定を含む静的なファイル
  • レイヤー化された構造:イメージは複数のレイヤーから成り立っており、これによりイメージの再利用、共有、更新が容易

Dockerコンテナ

  • 実行可能なインスタンス:Dockerコンテナは、Dockerイメージから生成された実行時の環境。コンテナはイメージを基にして起動し、アプリケーションを実行するための隔離された環境を提供する。
  • 軽量かつ柔軟性:コンテナは軽量で、必要に応じて簡単に起動、停止、移動、削除が可能。

Tag:Imageのversion

  • tag名を何も指定しないと 自動的に「latest」タグが使用される
  • image名とタグ名を : で区切って、下記のように表記される。
Zsh
nginx:latest
nginx:1.14-perl

Dockerの概念に関して詳しくは下記参照

【図解】Dockerの全体像を理解する -前編- - Qiita
この記事は何かイメージやコンテナなどの基本からdocker-compose、docker-machine, docker swarmなどのDocker周りの様々な概念の全体像を整理して、Dock…

Dockerfile

アプリケーションをDocker化して起動したい時に作成する。

Dockerイメージを作成するためのテキストファイル(レシピ)。

pythonのflaskアプリケーションを例に記載

Flask アプリケーションの外部化:host=’0.0.0.0’

docker化するということは、dockerコンテナという独自の環境に対して、外部からアクセスできるようにしてアプリケーションを検証するということなので、アプリケーションを外部に開放しないといけない。実行ファイルにhost='0.0.0.0' を指定する。

host='0.0.0.0'を指定しない場合、アプリケーションは外部からの接続を受け付けず、127.0.0.1(localhost)からのみアクセス可能

Python
# app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, Docker!'

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

Dockerfileの作成

Dockerfileは、Dockerイメージの設計図。DockerfileがないとDockerを起動できない。

YAML
# 基本イメージとしてPythonを使用
FROM python:3.8-slim

# 作業ディレクトリを設定
WORKDIR /app

# 必要なファイルをコンテナにコピー
COPY . /app

# 必要なパッケージをインストール
RUN pip install -r requirements.txt

# アプリケーションの実行コマンド
# コンテナが起動する時に、app.pyを実行することを意味する。
CMD ["python", "app.py"]

WORKDIR

  • WORKDIRディレクティブは、コンテナ内での作業ディレクトリ(カレントディレクトリ)を設定する。すべての後続の命令は、このディレクトリを基準に実行される。

COPY

  • COPYコマンドは、ホストマシンからファイルやディレクトリをコンテナ内にコピーする
  • <source>はホストマシン上のファイルまたはディレクトリのパスであり、<destination>はコンテナ内のパス。<destination>WORKDIRで指定したディレクトリを基準とする。
YAML
COPY <source> <destination>

例えば、下記のflaskアプリケーションがあったとして、実行ファイル(ここではroutes.py)がapp/配下にある場合は、app/ディレクトリ内の全てのファイルとサブディレクトリを、コンテナ内にコピーする。

YAML
COPY app/ /app/

COPYの相対パスと絶対パス記述に関して

Dockerfile内でパスを指定する際に、「/」を先頭につけるかどうかで、相対パスと絶対パスが区別される。

copy先のディレクトリの手前に「/」をつけなければ相対パス、「/」をつければ絶対パスで指定することを表す。

app/WORKDIR に設定されているディレクトリに対する相対パス。WORKDIR は設定しない場合は、コンテナのルートディレクトリになる。

/app/ :コンテナのルートディレクトリからの絶対パス。

ややこしくなってきたが、ローカル環境にあるflaskアプリケーションの実行ファイルがapp/配下にあって、それをdockerコンテナのapp/に再現したい場合は

1)WORKDIRを設定しない場合

コンテナのルートディレクトリがデフォルトの作業ディレクトリになる。

WORKDIR が設定されていない場合、COPY app/ /app/COPY app/ app/ は実質的に同じ操作を意味する。どちらの命令も、ホストの app/ ディレクトリをコンテナの /app/ ディレクトリにコピーする。

Dockerは必要なディレクトリを自動的に作成するので、app/ ディレクトリが存在しない場合でもエラーは発生しない。相対パスでCOPYを指定した時に、workdirを設定していないと、そもそもapp/ディレクトリがないので、copy先のディレクトリが作成されてないよというエラーが出そうな気がするが、そこはよしなに対応してくれる。

YAML
COPY app/ /app/ #コンテナのルートディレクトリからの絶対パスで指定
COPY app/ app/ #相対パス。WORKDIRを設定していないのでルートディレクトリからのパスになる

2)WORKDIRを設定する場合

workdirを/appと記載した場合

絶対パスで指定する場合は、workdirを設定しない場合と同じで、もちろん変更はない。

一方で相対パスで指定する場合は、カレントディレクトリが/appなので、「.」と設定する。

YAML
WORKDIR /app
COPY app/ /app/ #コンテナのルートディレクトリからの絶対パスで指定
COPY app/ . #相対パス。/appに作成したいので、カレントディレクトリ「.」

ちなみにWORKDIR/test に設定した場合、COPY コマンドでファイルをコピーするときにも WORKDIR の値を考慮してコピー先のパスを指定する必要がある。まず、ないと思うが。

YAML
WORKDIR /test
COPY app/ /test/

となる。仮にこのdockerfileで実行した場合、ホストの下記のファイル構成は

YAML
app/
  app.py
  static/
    style.css

Dockerコンテナ内では

YAML
/test/
  app.py
  static/
    style.css

となる。

requirements.txt

先ほどのcopyの続きだが、下記のようなflaskアプリケーションのフォルダ構成の場合、app/配下をdockerコンテナのapp/配下にコピーした場合、requirements.txtはapp/配下にないので、当然コピーされない。

なので、別途コピーする必要がある。

ローカルのフォルダ構成と同じにする場合には、下記で絶対パスのルートディレクトリ「/」を記載

YAML
COPY requirements.txt /
YAML
# 使用するPythonのベースイメージ
FROM python:3.10-slim

# 作業ディレクトリを設定
WORKDIR /app

# 依存関係をインストール
COPY requirements.txt /
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのコードをコピー
COPY app/ /app/

# コンテナ内で実行するコマンド
CMD ["python3", "routes.py"]
  • -r フラグ: “requirements file”(要件ファイル)を意味し、pip に対して与えられたファイルからパッケージ名のリストを読み込み、それらをインストールするよう指示
  • --no-cache-dir オプション:requirements.txtにリストされたパッケージをキャッシュなしでインストールする。dirはdirectoryの略。

pip を使ってPythonのライブラリをインストールする時、通常はダウンロードしたライブラリをキャッシュとして保存する。Dockerのイメージを作るときには、キャッシュがイメージの中にも保存されてしまい、イメージのサイズが大きくなってしまう。

--no-cache-dir オプションを使ってキャッシュを無効にすると、ライブラリはインストールされますが、キャッシュとして保存されないないため、イメージのサイズが小さくなる。

ただし、メリットばかりではなく、キャッシュを無効にすると、pipは毎回必要なパッケージをインターネットからダウンロードしなければならず、それがDockerコンテナビルド時間の増加につながる。

Flaskとその他必要なライブラリを記載したrequirements.txtを作成する。

YAML
flask==1.1.2
requests>=2.24.0
numpy

-uオプション:バッファリング無効

バッファリング

  • バッファリングは、データを一時的に格納するプロセス
  • Pythonでは、デフォルトで標準出力(print文などによる出力)は「バッファリング」される。つまり、実際に画面に表示されるまで一定量のデータが溜まるか、または出力バッファがフラッシュされる(空にされる)まで、出力が保留されます。

リアルタイムでpythonのログを出力するには、このバッファリングを無効にする必要があり、そのためには、Python スクリプトを -u オプションをつけて実行する必要がある。

YAML
CMD ["python", "-u", "script.py"]

-u オプションの -u は、特に略称ではなく、単なるオプションのフラグ。Pythonの公式ドキュメントでは、-u は単に “unbuffered binary stdout and stderr”(バッファリングされないバイナリ形式のstdoutおよびstderr)の意味で使用されている。

Docker イメージのビルド

Dockerfileを使用してDockerイメージをビルド。

現在のディレクトリ(.)にあるDockerfileを使用して、flask-appという名前のイメージを作成する場合

  • -t オプションは「tag」の略で、ビルドされるイメージに名前(タグ)を付けるために使用される
  • flask-appは、イメージに付ける名前。この名前は任意で、後でイメージを識別しやすくするために使う。例えば、アプリケーションの名前やバージョンなど、意味のある名前を付けるのが一般的
  • ドット . は、Dockerfileがあるディレクトリ。この場合、現在の作業ディレクトリ(コマンドを実行しているディレクトリ)。Dockerfile が別のディレクトリにある場合、そのディレクトリのパスを指定する必要あり。
Zsh
docker build -t flask-app .

Docker コンテナの実行

ビルドされたイメージからコンテナを起動する。

  • 8080番ポートをローカルマシンの5000番ポートにマッピングして、flask-appイメージからコンテナを起動する。これにより、http://localhost:8080 でFlaskアプリケーションにアクセスできるようになる。
Zsh
docker run -p 8080:5000 flask-app

環境変数を指定するとき

これで、Dockerコンテナをビルドして実行して、サイトにアクセスできるようになる。

ただし、これだと、毎回portマッピングや環境変数をdocker runのオプションコマンドで指定しないといけないので手間。というわけで、docker-compose.ymlという概念が登場する。

docker-compose.ymlは絶対ないと、Dockerを起動できないというわけではないが、docker操作を効率化するためのもの

docker-compose.yml

YAML
version: '3'
services:
  web:
    build: .
		image: flask-app
    ports:
      - "8080:5000"
    environment:
      - FLASK_ENV=development
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    volumes:
      - ./app:/app
  • version: '3': これは docker-compose.yml ファイルのバージョンを指定。バージョン3はDocker Composeの特定のバージョンでサポートされる機能セットを持っている。
  • services: このセクションでは、管理するサービス(コンテナ)を定義する。サービスは、実行するコンテナのこと。
  • web: 定義されたサービスの名前。この例では “web” という名前のサービスになっているが、これはFlaskアプリケーションを実行するコンテナになる
  • build: .: これはDocker Composeに、カレントディレクトリ(.)を使用してコンテナイメージをビルドするよう指示。docker build -t flask-app . コマンドの最後につけている部分を指す。
  • ports: port mapping。ホストマシンの8080ポートをコンテナの5000ポートにマッピングすることを意味
  • environment: 環境変数をコンテナに設定。この例では、FLASK_ENVdevelopment に設定しているので、Flaskが開発モードで実行されることを意味する。また、OPENAI_API_KEY は環境変数から読み取られ、コンテナ内の環境変数として設定されます。
  • volumes: ホストマシンとコンテナ間でファイルシステムのマウントを設定。この例では、ホストマシンのカレントディレクトリ内の app ディレクトリをコンテナ内の /app にマウントしている。ホストマシン上で app ディレクトリ内のPython関数(またはその他のファイル)を編集すると、それらの変更がすぐにコンテナ内のファイルにも反映される。コンテナが動いている間にファイルを変更した場合、その変更はコンテナ内で実行されているアプリケーションにも直ちに影響を与える。

複数のDockerコンテナ間で通信したい場合は、external:trueを設定する。

Dockerによる自動イメージ名生成の仕組み

image キーに関して

  • docker-compose.yml ファイルに image キーを記載しなくても docker-compose up を実行することは可能。この場合、Docker Composeは自動的にイメージ名を生成し、ビルドされたイメージに割り当てる。
  • image キーが指定されていない場合、Docker Composeはデフォルトの命名規則に従ってイメージ名を生成する。
YAML
<ディレクトリ名>-<サービス名>

<ディレクトリ名>docker-compose.yml ファイルが存在するディレクトリの名前

<サービス名>docker-compose.yml ファイル内の services セクションで定義されているサービスの名前。加えて、Docker Composeはイメージに自動的にタグ latest を付ける。

たとえば、docker-compose.yml ファイルが myproject ディレクトリにある場合には、Docker Composeは myproject-web という名前のイメージを自動的に生成し、タグ latest を付ける。これにより、ビルドされたイメージは myproject-web:latest として識別される。特定のイメージ名を指定する必要がある場合は、image キーを使って名前を明示的に設定する。

Dockerコマンド

docker-compose.ymlファイルを作成すると、docker-composeコマンドを使ってDockerを操作(ビルド・実行・停止・削除)できるようになる。

ビルド(Build)

YAML
docker-compose build

docker-compose.ymlに定義されたサービスのイメージをビルドする。これにより、指定されたDockerfileに基づいて各サービスのDockerイメージが作成される。

実行(Up / Run)

docker-compose.yml ファイルに定義されたすべてのサービス(コンテナ)を起動する場合

YAML
docker-compose up

指定されたサービス(コンテナ)を個別に起動し、任意のコマンドを実行する場合

SERVICE:実行するサービス名

YAML
docker-compose run [options] SERVICE [COMMAND] [ARGS...]

実行時に強制ビルド:up —build

docker-compose up コマンドは通常、ビルドされたイメージがない場合には新規ビルドを実行する。既にビルドされたイメージがある場合、再ビルドせずにコンテナを起動する。ビルドを強制的に行いたい場合は、--build オプションを使用する。

YAML
docker-compose up --build

ログ(logs)

ログのリアルタイム表示:-f(followの略)

特定のサービスのログを見たい場合は、サービス名を指定する。例:docker-compose logs -f web

停止(Down)

YAML
docker-compose down

docker-compose upで起動したサービスを停止し、コンテナ、ネットワークやボリュームなどのリソースも削除する。イメージは削除されない。イメージを削除したい場合は、docker-compose down コマンドに --rmi オプション(remove imageの略)を追加する。

イメージ削除:—rmi(remove image)

YAML
docker-compose down --rmi all #全てのイメージ削除
docker rmi [イメージ名またはイメージID] #特定のイメージ削除

個別のサービス停止(Stop)

全てのサービスを停止するときはdownだが、個別のサービスを停止したいときはstop。

stopコマンドは指定したサービスのコンテナの実行を停止させるが、コンテナを削除はしない。

YAML
docker-compose stop service_name  # service_name:停止したいサービス名

コンテナ内でのコマンド実行(Exec:execute)

Zsh
docker-compose exec -it service_name bash

Execはexecute(実行する)の略

-it オプション

  • インタラクティブな操作や端末(TTY)のアタッチメントを可能にするために使用される
  • -i--interactiveの略。-t:-ttyの略。仮想端末(TTY)
  • 既に実行中のコンテナに対して、インタラクティブなシェルを起動するには、docker exec コマンドに -it オプションを使用する

docker exec ではコンテナ名またはIDを使用し、docker-compose exec ではサービス名を使用する

Zsh
docker exec -it my_container bash #コンテナ名 or コンテナID
docker-compose exec my_service bash #サービス名

bash

  • コンテナ内で実行するコマンドを指定している
  • すべてのコンテナに bash がインストールされているわけではありません。特に軽量なイメージでは、bash の代わりに sh(シェル)や他のコマンドラインインターフェースが使われることがある

コンテナ一覧表示:ps(process statusの略)

Zsh
docker ps

現在実行中のコンテナの一覧を表示する。これにより、システム上でどのDockerコンテナが稼働しているかを簡単に確認できる。

以下のような情報を含む表を出力する:

  • CONTAINER ID:コンテナの一意のID。
  • IMAGE:コンテナを作成するのに使用されたイメージ。
  • COMMAND:コンテナ起動時に実行されたコマンド。
  • CREATED:コンテナが作成されてからの経過時間。
  • STATUS:コンテナの状態(実行中、停止中、一時停止中など)。
  • PORTS:コンテナが開放しているポートと、それに対応するホストマシンのポート。
  • NAMES:コンテナの名前。

コメント

タイトルとURLをコピーしました