Dockerのお勉強!

ハロー、ききです!
自作アプリを作ろうと、実行環境を調べているときに、Dockerというのを最近知りました。
知識としてはうっすら知っているのですが、Dockerとはどう言ったものなのか、仮想環境との違いなどを学びましたので備忘録として投稿しました!

目次

Dockerって?

Dockerはオープンプラットフォームで、アプリとその実行環境をまとめて、
どこでも同じように動かしやすくする仕組みです。

利点として、

・端末の違いで動かない
・開発環境を作るのが面倒
・DBやミドルウェアのバージョンを合わせるのが大変
・環境構築手順が長くなる

このようなことが解決できます。

アプリケーションとインフラを分けて扱いやすくし、開発からテスト、本番反映までの流れを短くするために使われます。

コンテナ

コンテナは、実際にアプリが動いている実行環境です。
イメージから作られる実行可能なインスタンスで、作成、起動、停止、移動、削除ができ、ネットワークやストレージを接続することもできます。

コンテナはイメージから作られるため、
同じイメージを使えば、同じ構成のコンテナを複製することも可能です。
そのため、開発環境や、テスト環境、本番環境などを同じ構成で簡単に作れます。

イメージ

普段は「◯◯みたいなイメージです。」や画像の意味などでイメージと言うため、わかりづらいですが、
Dockerのイメージというのはそれとは別の意味を持つ単語になります。

Docker公式では、イメージは、コンテナを作るための読み取り専用のテンプレートと説明されています。
コンテナ実行に必要なファイル、バイナリ、ライブラリ、設定がイメージに含まれます。
Dockerfileに書いた手順から作成でき、各命令はイメージのレイヤーとして積み重なります。
一度作ったレイヤーはキャッシュとして再利用されるため、変更がない部分は毎回作り直さずに済みます。
そのため、Dockerfileの書き方によっては、ビルドが速くなったり遅くなったりします。

Dockerfile

Dockerfileはイメージを作るための命令を書いたテキストファイルです。

Dockerfileには、以下のような内容を書きます。

・土台にするイメージ
・コンテナに入れるファイル
・コンテナにインストールするライブラリ
・コンテナ起動時に実行するコマンド

Dockerfileに書いただけでは、イメージはできません。
docker buildを実行すると、DockerがDockerfileの命令を上から順に読み取り、イメージを作成します。
そして、そのイメージをdocker runで起動すると、コンテナが作成されて、アプリが動きます。

Compose

Docker Composeは、複数のコンテナを1つのYAMLファイルでまとめて定義・起動するための仕組みです。

Docker Engine

Docker Engineは、コンテナ化されたアプリケーションを構築・実行するための中核です。


docker rundocker buildを実行すると、Docker CLIがDocker daemon(デーモン)に命令を送ります。
Docker demonはその命令に従って、イメージを作成したり、コンテナを起動・停止・削除したりします。

つまり、Docker Engineは、コンテナやイメージを管理している本体部分です。

Dockerfile例

例として、PythonアプリとPostgreSQLを使う構成で考えてみます。
DockerではアプリとDBのように役割が異なるものは、基本的に別々のコンテナとして扱います
そのため、PythonアプリとPostgreSQLをDockerで動かす場合は、別々のコンテナに分けます。
当然ですが、実行するにはDockerが必要ですのでインストールを忘れずに。。。

ファイル構成例

sample-docker-app/
├── Dockerfile
├── compose.yml
├── requirements.txt
└── main.py

Dockerfile

Dockerfile
FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . . 
CMD ["python", "main.py"]

このDockerfileはPythonアプリのイメージを作成しています。
PostgreSQLのイメージはここには記載していません。
PythonアプリとPostgreSQLを一緒に動かす場合は、compose.yml に app と db を別々のサービスとして定義します。

FROM python:3.12

FROMで最初に何をベースにしてイメージを作るかを指定します。
今回はPython公式のpython:3.12を使用しています。

WORKDIR /app

WORKDIRでコンテナ内の作業ディレクトリを指定します。
今回はコンテナ内の/appを指定しています。
以降のCOPYRUNCMDは基本的にこの/appのディレクトリを基準に実行されます。

COPY requirements.txt .

COPYでファイルをコピーします。
COPY コピーしたいファイル コピー先のフォルダ このような書き方で書きます。
今回、コピーしたいファイルは、Dockerfileと同じ階層のためフルパスではなくファイル名だけ指定し、
コピー先の.は、/appを指します。

requirements.txtにはPythonアプリで使うライブラリを書いてまとめています。
先にこのファイルだけコピーしている理由として、
main.py本体のみを変更した場合に、
ライブラリインストール部分のキャッシュを再利用し、高速化する狙いがあります。

RUN pip install --no-cache-dir -r requirements.txt

RUNでイメージ作成時に、指定した処理を実行します。
重要な点として、コンテナ起動時ではなく、イメージ作成時に実行されます。
今回はpiprequirements.txtファイルに指定しているライブラリをインストールします。
--no-cache-dirは、pipのキャッシュを残さないための指定です。
イメージサイズを余計に大きくしないために使用しています。

COPY . .

現在のフォルダにあるアプリ本体のファイルを、イメージ内の/appにコピーします。
左の . は、自分のPC側の現在フォルダです。
右の . は、コンテナ内の現在の作業ディレクトリ、つまり /app です。

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

CMDはコンテナを起動したときに実行するコマンドです。
今回、コンテナ起動時にpython main.pyを実行します。
重要なのはコンテナ起動時に実行されると言う点です。

compose.yml

compose.yml
services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    environment:
      DB_HOST: db
      DB_PORT: 5432
      DB_NAME: sample_db
      DB_USER: sample_user
      DB_PASSWORD: sample_password

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: sample_db
      POSTGRES_USER: sample_user
      POSTGRES_PASSWORD: sample_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U sample_uesr -d sample_db"]
      interval: 5s
      timeout: 5s
      retries: 5
  
  volumes:
    postgres_data:

Dockerfileは、基本的に1つのイメージを作るための手順を書きます。
しかし、実際のアプリではPythonアプリだけでなく、PostgreSQLのようなDBも一緒に必要になることがあります。
このように複数のコンテナをまとめて定義・起動したいときに使うのが Docker Compose です。

services:

起動したいコンテナのまとまりを書きます。
今回は下記のように設定しています。

app → Pythonアプリ用コンテナ
db → PostgreSQL用コンテナ

app:

appはPythonアプリ用のサービスです。

build: .

現在のフォルダにあるDockerfileを使って、アプリ用のイメージを作成すると言う意味です。
つまり、さっき書いたDockerfileはここで使われます。

depends_on:

サービスの起動順を指定します。
この例では、appdbに依存しているので、
Composeは依存関係に基づいて起動・停止順を制御します。
ただし、単にdbと書いただけでは、PostgreSQLが接続可能になるまで待ってくれるわけではありません。
そのため、healthcheckを設定し、接続可能になってから次の起動に移るように設定します。
db:
condition: service_healthy

これはdbサービスがhealthyになってからappを起動すると言う指定です。
dbに指定されたhealthcheckから判定します。

environment:...

これは、Pythonアプリコンテナに渡す環境変数です。
アプリ側のソースでは、この値を使ってDBへ接続します。
重要なのはDB_HOST: dbです。
appコンテナからPostgreSQLへ接続するときはlocalhostではなくdbを使います。

db:

dbはPostgreSQL用のサービスです。
ただし、本番環境のDBをコンテナ化するのは、考える必要があります。
永続化、バックアップ、復旧、性能、監視、運用責任をきちんと設計する必要があります。
その設計ができないなら、マネージドDBや通常のDBサーバーを使う方が安全です。

image: postgres:16

PostgreSQL16のイメージを使ってDBコンテナを作ると言う意味です。
アプリ側ではDockerfileからイメージを作成しましたが、
PostgreSQLはDocker Hubに用意されている postgres のDocker公式イメージを使います。
そのため、DB用のDockerfileは自分で書かず、compose.yml で image: postgres:16 を指定します。

environment:...

これは、PostgreSQLコンテナに渡す設定です。
今回は作成するDB名、ユーザー名、ユーザーのパスワードを設定しています。

healthcheck:

test: ["CMD-SHELL", "pg_isready -U sample_user -d sample_db"]
これはPosgreSQLに対して、接続可能な状態か確認しています。
interval: 5sは5秒ごとに確認します。
timeout: 5sは5秒以内に応答がなければ失敗扱いにします。
retries: 5は5回失敗したらunhealthyと判断します。

volumes:

これは、PostgreSQLのデータを保存するための設定です。
DBコンテナを消してもデータを残したいので、PostgreSQLのデータ保存場所をpostgres_dataと言うVolumeに接続しています。
volumeはコンテナのライフサイクルを超えてデータを永続化する仕組みです。
docker volume lsのコマンドで参照可能です。

requirements.txt

requirements.txt
psycopg2-binary==2.9.10

PythonからPostgreSQLへ接続するためのライブラリです。

main.py

main.py
import os
import psycopg2

def main():
  conn = psycopg2.connect(
    host=os.environ["DB_HOST"],
    port=os.environ["DB_PORT"],
    dbname=os.environ["DB_NAME"],
    user=os.environ["DB_USER"],
    password=os.environ["DB_PASSWORD"],
  )

  cur = conn.cursor()
  cur.execute("SELECT version();")
  version = cur.fetchone()

  print("PostgreSQLに接続できました")
  print(version[0])

  cur.close()
  conn.close()

if __name__ == "__main__":
  main()

DBに接続して、接続したDBのバージョンをSELECTで取得し、コマンド出力するような処理を記載しています。

conn

環境変数からDB接続情報を取得しています。

cur

DB接続情報からcursorを取得しています。
cursorはデータ操作(CRUD)を行うときに使用します。

コンテナ起動の流れとして

docker compose up --buildのコマンドを実行すると、compose.ymlを元に下記のような流れでコンテナが作成されます。

STEP
コマンドを実行

docker compose up --buildをコマンドで実行することで下記の実行が行われる

STEP
DockerfileからPythonアプリ用イメージを作る

app は build: . を見る

STEP
PostgreSQLイメージからDBコンテナを作る

db は image: postgres:16 を見る

STEP
appコンテナとdbコンテナが同じCompose環境内で起動する

STEP
appコンテナは DB_HOST=db でPostgreSQLへ接続する

最後に

今回、Dockerの基本的なことを学びました。

実際、自作アプリを複数作るようになったりすると、
アプリごとにコンテナ作ってみるのもアリなのかなと思ったりしました。

次は、仮想環境との違いなんかも調べてみようかなって思ってます。

それでは、最後まで見てくれてありがとです!

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

ハロー、ききです!
ある時はプログラマー/システムエンジニアの会社員として働き、
またある時は猫になってブログをにゃんにゃん書いてます。
ITの知識でお役立てそうなことや、私自身がにゃやんだこと、
私がいいなと思ったものについて発信しております。

いつかこんな猫飼いたい…

目次