bitA Tech Blog
🐳

Dockerを使うことになった際に役立ちそうな知識

に公開

はじめに

本記事は社内勉強会でDockerの使い方をテーマに取り扱った内容を書き起こしたものです。

参画しているプロジェクトでDockerを使用しているため使い方を知りたい、もしくはDockerを使用することになったがイマイチ使い方、学び方がわからない、といった方の助けになれば幸いです。

この記事で取り扱う内容

Dockerの機能や仕組みについて簡単に解説します。
また複数のコンテナイメージを用いてWebアプリケーションを実現する方法を説明します。

Dockerとは何か

公式の日本語訳では下記のように記載されています。

アプリケーションの構築、共有、実行を加速
Dcokerを使うと、開発者は面倒な環境構成や管理を行わずに、どこでもアプリケーションを構築、共有、実行、検証できるようになります。

引用: https://www.docker.com/ja-jp/

本来は「アプリケーションの構築、共有、実行を提供する製品群の総称」という意味になります。
一方、開発現場ではコンテナ型の仮想化技術によって上記が実現されていることから、「Docker」という単語がコンテナ型の仮想化技術そのものを指して使われることも多いと感じます。
したがって「Docker」のという単語は文脈に応じて細かな意味が異なることに注意していただくと良さそうです。

Dockerの仕組み

Dockerの仕組みイメージ

上図はDockerがアプリケーションを構築、共有、実行する仕組みを簡単に表現したものです。

Container

Linuxカーネルの機能を利用し、ホスト上でも隔離された状態でアプリケーションを実行する環境です。

コンテナ内の出来事はホストや他のコンテナには影響を与えません。

Container Image

コンテナを構築するためのベースとなる情報です。

コンテナイメージはDocker Hub等のイメージレジストリから取得できます。
また Dockerfile を使用しコンテナイメージの作成も可能です。

現在は Open Container Initiative (OCI)という仕様で共通化されているため、OCI仕様のコンテナイメージは下記のようなDocker以外のコンテナランタイムでも動作します。

Image Registry

Docker Hub等のコンテナイメージを保存・管理・共有するための中央集約型の場所です。

Dockerfile

コンテナイメージを構築するためテキストファイルです。

参考: Dockerfile reference

Virtual Network

コンテナが他のコンテナや外部サービスとの通信するために接続するネットワークです。

ネットワークの作成、削除、接続はコマンドで操作でき、複数のコンテナを同じネットワークに接続させることで、IPアドレスを使用せずにコンテナ名で名前解決ができるようなります。

Filesystem

マウントイメージ
引用: https://docs.docker.jp/storage/index.html

コンテナは隔離された環境であるため、コンテナを削除するとコンテナ内で生成されたデータも一緒に削除されます。
データベースなどデータの永続化が必要なアプリケーションをコンテナとして動作させる際はホスト上のファイルシステムを対象のコンテナにマウントすることでデータの永続化を実現します。

マウントには下記3種があります。

ボリューム(volume)

Dockerによって管理されているボリュームをコンテナにマウントする。

バインド マウント(bind mount)

ホスト上の任意のファイルやディレクトリをコンテナにマウントする。

tmpfs マウント

メモリ領域をコンテナにマウントする。
データはメモリに書き込まれるためコンテナを削除した際はホスト、コンテナのどちらにもデータは残らない。

コンテナを動かしてみよう

Dockerをインストールした端末を使用しコンテナの動作を確認します。
ここでは皆さんのお家にもあるRaspberry Piで動作を確認しました。

デモの概要

デモアプリケーションのイメージ

上図のようにnginx、MySQL、Webアプリケーションのコンテナを使用し簡単なWebアプリケーションをDockerを使用して構築します。

nginx、MySQLのコンテナはDockerHubで公開されているコンテナイメージを使用します。
またWebアプリケーションコンテナはPythonのコンテナイメージをベースに新たにコンテナイメージを作成します。

確認環境(Raspberry Pi 5)

Software Version
OS Debian GNU/Linux 12 (bookworm) aarch64
Docker version 28.2.2, build e6534b4
Docker Compose version v2.36.2
MySQL MySQL Ver 15.1 Distrib 10.11.11-MariaDB, for debian-linux-gnu (aarch64) using EditLine wrapper

使用したコンテナイメージ

本記事ではDocker Hubから取得したコンテナイメージを使用しています。
Docker Hubの制限[1] が気になる場合などは Amazon ECR Public Galleryからでも同等のイメージを取得し使用できます。

Image Tag
mysql 8.4.5
python 3.13.5-slim-bookworm
nginx 1.28.0-bookworm

ディレクトリ構成

動作を確認した際のディレクトリ構成です。
基本的に下記ディレクトリ構成のルートで作業をします。

.
├── app
│   ├── Dockerfile
│   ├── requirements.txt
│   └── src
│       ├── __init__.py
│       ├── db.py
│       ├── main.py
│       ├── models.py
│       └── templates
│           └── index.html
├── compose.yaml
├── db
│   └── initdb.d
│       └── 001_create_table.sql
└── nginx
    └── default.conf

デモプロジェクト

下記にデモ用のプロジェクトを作成しましたのでご活用ください。

https://github.com/komeloper/study-docker

コンテナイメージを取得してみる

Docker Hub等のイメージレジストリからコンテナをPullします。
docker pull <イメージ名>:<タグ> とすることで指定したタグのイメージを取得できます。
タグ名を省略した場合は latest のイメージが取得されます。

# Registry から Container Image を Pull する
# タグ名指定なし(latest)
$ docker pull mysql

# タグ名を指定
$ docker pull mysql:8.4.5

# ローカル内のイメージを確認する
$ docker images
REPOSITORY                            TAG                    IMAGE ID       CREATED        SIZE
mysql                                 8.4.5                  bf4c7f45ae54   2 months ago   779MB
mysql                                 latest                 cd7958658f92   2 months ago   877MB

コンテナイメージを作ってみる

デモプロジェクトのDockerfileを使用し新たにコンテナイメージを作成します。
参考までにデモプロジェクトのDockerfileで実施している内容を各命令ごとにコメントを記載します。

また詳細な命令の内容やベストプラクティスについてはDockerfile referenceを参照してください。

# build時の引数と初期値を設定(Dockerfile内で利用可)
ARG IMAGE_TAG=3.13.5-slim-bookworm

# ベースとなるPythonイメージを指定(タグ名はARGで設定された値)
FROM python:${IMAGE_TAG}

# 環境変数と初期値を設定(コンテナ起動時に上書き可)
ENV DB_HOST=db \
    DB_PORT=3306

# 以降の命令の作業ディレクトリを指定
WORKDIR /src

# ホスト上の`requirements.txt`を`/src`ディレクトリにコピー
COPY requirements.txt .

# 新しいレイヤー上でコマンドを実行
# ここでは`app`ユーザーの追加、`requirements.txt`をもとにPythonパッケージをインストール、`/src`ディレクトリの所有者を`app`ユーザーへ変更
RUN useradd --home-dir /src --uid 1000 app \
    && pip install --no-cache-dir -r requirements.txt \
    && chown -R app:app /src

# 以降の命令の実行者を`app`ユーザーへ変更
USER app

# ホスト上の`src`ディレクトリを`app/`ディレクトリとしてコピー
COPY ./src/ ./app

# tcp/8000で接続を受け付ける(実際に公開はしない)
EXPOSE 8000

# コンテナ起動時のデフォルトコマンドを指定
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
# デモプロジェクトを使用している場合は `app` ディレクトリへ移動します
$ cd app

# build-argとtagを指定しコンテナイメージをbuild
$ docker build --build-arg IMAGE_TAG=3.13.5-slim-bookworm --tag demo-app:0.1.0 .

# コンテナイメージの確認
$ docker images
REPOSITORY          TAG                    IMAGE ID       CREATED        SIZE
demo-app            0.1.0                  1a860224795a   5 hours ago    210MB
python              3.13.5-slim-bookworm   e07b767bb2b3   2 weeks ago    146MB
nginx               1.28.0-bookworm        e55a872cbf1b   2 months ago   198MB
mysql               8.4.5                  bf4c7f45ae54   2 months ago   779MB

コンテナを起動してみる

docker run コマンドでコンテナのもととなるイメージを指定しコンテナを起動します。

docker run コマンドは docker pulldocker createdocker start コマンドの動作を含みます。

  • -d: デタッチドモード、コンテナをバックグラウンドで実行
  • --name: コンテナに名前を設定
  • -e: 起動するコンテナ内の環境変数を設定

コンテナ内で利用する環境変数はコンテナイメージによって異なります。
詳しくはコンテナイメージの提供元の情報を確認してください。

参考: mysql

# コンテナを作って起動(イメージがなければPull)
# docker pull & docker create & docker start
$ docker run -d \
    --name db001 \
    -e MYSQL_ROOT_PASSWORD=password \
    -e MYSQL_DATABASE=database \
    mysql:8.4.5

コンテナを操作してみる

続いてコンテナを起動してみるで起動したコンテナを操作します。

# 起動中のコンテナで任意のコマンドを実行する
$ docker exec db001 touch container.txt

# 起動中のコンテナに /bin/bash を指定しで接続する
$ docker exec -it db001 /bin/bash

# コンテナから抜ける(コンテナ内で実行)
$ exit

# 起動中のコンテナ内のファイルをカレントディレクトリにコピーする
$ docker cp db001:/container.txt .

# コンテナの状況を確認(停止中のコンテナも表示する場合は `docker ps -a` )
$ docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS          PORTS                 NAMES
9c1176f18c10   mysql:8.4.5   "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   3306/tcp, 33060/tcp   db001

# コンテナで出力されたログを確認する
$ docker logs db001

# コンテナを停止する
$ docker stop db001

# コンテナを削除する
$ docker rm db001

コンテナをサービスとして利用してみる

コンテナをホスト上で稼働するサービスとして公開するため、コンテナを起動してみるで紹介した docker run コマンドを下記のように変更し、コンテナを起動します。

追記した下記の -p フラグはホスト側の tcp/33066 ポートにコンテナ側の tcp/3306 ポートを割り当てる意味となります。
なお、Dockerのバージョンが27以下の場合は意図せずホストの外部にもポートを公開する場合[2]もあるため取り扱いにはご注意ください。

 # tcp/33066ポートを使用してコンテナを起動する
 $ docker run -d \
-    --name db001 \
+    --name db002 \
     -e MYSQL_ROOT_PASSWORD=password \
     -e MYSQL_DATABASE=database \
+    -p 33066:3306 \
     mysql:8.4.5

ホストからコンテナとして起動したMySQLが利用できるかの確認
(MySQLクライアントよりMySQLコンテナに割り当てたポートへ接続できるか確認)

# ホストからMySQLクライアントで接続してみる
# Welcome to the MariaDB monitor... が出力されれば接続OK
$ mysql -P 33066 -u root -p database
Enter password:  # MYSQL_ROOT_PASSWORDに設定した値を入力

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 19
Server version: 8.4.5 MySQL Community Server - GPL

コンテナのデータを永続化してみる

ボリュームを作成し、コンテナにマウントすることでコンテナ内のデータを永続化します。

MySQLコンテナでは初回実行時に /docker-entrypoint-initdb.d 内の .sql ファイルなどを実行する機能があります。

ここでは db_data01 ボリュームを作成し、データベースのデータが格納される /var/lib/mysql にマウントします。
またコンテナ作成時にデモ用のテーブルを作成するため、DDLを記載したsqlファイルを格納したディレクトリを /docker-entrypoint-initdb.d にbindマウントします。

--mount フラグの記載は -v フラグを使用しすると下記のように省略も可能です。

  • -v db_data01:/var/lib/mysql
  • -v ./initdb.d:/docker-entrypoint.initdb.d:ro
db/initdb.d/001_create_table.sql
-- デモ用テーブルを作成
CREATE TABLE `database`.demo_table (
  id BIGINT auto_increment NOT NULL COMMENT 'Primary Key',
  created_at DATETIME DEFAULT NULL COMMENT '作成日時',
  updated_at DATETIME DEFAULT NULL COMMENT '更新日時',
  value varchar(100) NULL COMMENT '値',
  CONSTRAINT demo_table_pk PRIMARY KEY (id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'demo_table';
# db_data01ボリュームを作成する
$ docker volume create db_data01

# 作成したボリュームを確認
$ docker volume ls
DRIVER    VOLUME NAME
local     db_data01

# 作成したとホスト側のディレクトリをマウントし、コンテナを起動
$ docker run -d \
    --name db003 \
    -e MYSQL_ROOT_PASSWORD=password \
    -e MYSQL_DATABASE=database \
    -p 33067:3306 \
    --mount type=volume,source=db_data01,target=/var/lib/mysql \
    --mount type=bind,source=./db/initdb.d,target=/docker-entrypoint-initdb.d,readonly \
    mysql:8.4.5

# MySQLに接続し、データを挿入する
$ mysql -P 33067 -u root -p database
# ...省略...

MySQL [database]> insert into demo_table (created_at, updated_at, value) values (current_timestamp(), current_timestamp(), 'test data01');
Query OK, 1 row affected (0.009 sec)

MySQL [database]> select * from demo_table;
+----+---------------------+---------------------+-------------+
| id | created_at          | updated_at          | value       |
+----+---------------------+---------------------+-------------+
|  1 | 2025-06-22 14:37:01 | 2025-06-22 14:37:01 | test data01 |
+----+---------------------+---------------------+-------------+

MySQL [database]>quit
Bye

# db003 コンテナを停止&削除
$ docker stop db003 && docker rm db003

続いてデータの永続化ができているか確認します。
新たに db_data01 ボリュームをマウントしたMySQLコンテナを起動し、
db003 コンテナで登録したデータが取得できれば成功です。

# `db_data01` ボリュームをマウントした `db004` コンテナを起動
$ docker run -d \
    --name db004 \
    -e MYSQL_ROOT_PASSWORD=password \
    -e MYSQL_DATABASE=database \
    -p 33068:3306 \
    --mount type=volume,source=db_data01,target=/var/lib/mysql \
    mysql:8.4.5

$ mysql -P 33068 -u root -p database
# ...省略...

# `db003` コンテナで登録したデータが取得できるか確認
MySQL [database]> select * from demo_table;
+----+---------------------+---------------------+-------------+
| id | created_at          | updated_at          | value       |
+----+---------------------+---------------------+-------------+
|  1 | 2025-06-22 14:37:01 | 2025-06-22 14:37:01 | test data01 |
+----+---------------------+---------------------+-------------+

複数のコンテナを連携してみる

デモの概要の図のような構成するためコンテナ同士を連携します。

docker network create コマンドで demo-nw01 ネットワークを作成し、コンテナのデータを永続化してみるで作成した db004 コンテナを接続します。
(db004 コンテナは停止させずに demo-nw01 ネットワークへ参加させます)

# Networkを作成
$ docker network create --driver bridge demo-nw01

# 作成したネットワークを確認
$ docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
b7ee6c7b42bd   bridge      bridge    local
2b0cea38706e   demo-nw01   bridge    local
ef0245f590b6   host        host      local
6aef57c9dc4d   none        null      local

# 実行中のコンテナを作成したネットワークに追加
$ docker network connect demo-nw01 db004

続いてコンテナイメージを作ってみるで作成したコンテナイメージとnginxのコンテナイメージをベースに demo-nw01 ネットワークへ参加させたコンテナを起動します。

# コンテナ起動時に作成したネットワークに参加
# demo-webコンテナ
$ docker run -d \
    --name app \
    -e DB_HOST=db004 \
    -e DB_USER=root \
    -e DB_PASSWORD=password \
    -e DB_SCHEMA=database \
    --net demo-nw01 \
    demo-app:0.1.0

# Nginxコンテナ
$ docker run -d \
    --name nx01 \
    -p 80:80 \
    -v ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
    --net demo-nw01 \
    nginx:1.28.0-bookworm

最後にブラウザより http://localhost/ へアクセスし下記が表示されれば成功です。

デモアプリ

Docker Composeを使用する

Docker Composeは、複数のコンテナからなるアプリケーションを定義し、実行するためのツールです。

これまでは様々なコマンドを使用し複数のコンテナの起動、操作を実現しましたが非常に複雑で手間のかかる作業でした。
Docker Composeでは yaml ファイルに複数のコンテナの設定を記載することで、 docker compose コマンドを通じ全てのコンテナ起動・管理ができるようになります。

他に下記のような便利な機能もあります。

  • yaml ファイル内で .env ファイルに記載した変数が利用できる
  • コンテナが同一のネットワークの場合は yaml に記載したサービス名で名前解決できる

また yaml ファイルのファイル名はデフォルトで下記に対応しています。
プロジェクトによってファイル名が異なっても正常に動作するためご安心ください。

  • compose.yml
  • compose.yaml
  • docker-compose.yml
  • docker-compose.yaml

実際にDocker Composeを使用して複数のコンテナを連携してみると同じ構成を実現します。
一部、ポートやコンテナ名が干渉してしまうため、複数のコンテナを連携してみるで使用したコンテナが起動中の場合は一度、停止&削除してから実施してください。

Docker Composeは docker compose <command> の形式でコマンド入力することによりコンテナを操作できます。

Docker Composeの詳細な使用方法については公式ドキュメントなどを参照してください。

参考までにデモプロジェクトのcompose.yamlにコメントを記載します。
(.env.sample ファイルは .env にリネームしてご利用ください)

compose.yaml
# 起動するサービス(コンテナ)の定義
services:
  # `db` というサービス名でMySQLコンテナを定義
  db:
    # コンテナイメージを指定
    image: 'mysql:8.4.5'
    # コンテナの環境変数を設定(設定値は`.env`に記載の内容を設定)
    environment:
      MYSQL_ROOT_PASSWORD: '${DEMO_DB_PASSWORD}'
      MYSQL_DATABASE: '${DEMO_DB_DATABASE}'
    # tcp/3306で接続を受け付ける
    expose:
      - '3306'
    # マウント設定
    volumes:
      # ボリュームをマウント
      - type: 'volume'
        source: 'db_data02'
        target: '/var/lib/mysql'
      # バインド マウント
      - type: 'bind'
        source: './db/initdb.d'
        target: '/docker-entrypoint-initdb.d'
        read_only: true
    # 参加するネットワークを設定
    networks:
      - 'demo-nw02'

  # `app` というサービス名でWebアプリケーションコンテナを定義
  app:
    # コンテナイメージの指定(コンテナイメージの指定時は`--tag` に相当)
    image: 'demo-app:0.1.0'
    # コンテナイメージのbuild時の設定
    build:
      context: './app'
      args:
        IMAGE_TAG: '3.13.5-slim-bookworm'
    environment:
      DB_USER: '${DEMO_DB_USER}'
      DB_PASSWORD: '${DEMO_DB_PASSWORD}'
      DB_SCHEMA: '${DEMO_DB_DATABASE}'
    expose:
      - '8000'
    networks:
      - 'demo-nw02'
    # `db` サービスの起動待ってからサービスを起動する
    depends_on:
      - 'db'

  # `nginx` というサービス名でNginxコンテナを定義
  nginx:
    image: nginx:1.28.0-bookworm
    # ホスト側のtcp/80ポートに Nginxコンテナのtcp/80ポートを割り当てる
    ports:
      - "80:80"
    volumes:
      - './nginx/default.conf:/etc/nginx/conf.d/default.conf:ro'
    networks:
      - 'demo-nw02'
    depends_on:
      - 'app'

# volumeの定義(`docker volume create db_data02` に相当)
volumes:
  db_data02:
    name: 'db_data02'

# networkの定義(`docker network create --driver bridge demo-nw02` に相当)
networks:
  demo-nw02:
    driver: 'bridge'
# コンテナをbuildし、バックグラウンドでコンテナを起動する(buildが不要な場合は `--build` を削除)
$ docker compose up -d --build

# コンテナの状態を確認する
$ docker compose stats

# サービス名を指定してログを確認する
$ docker compose logs app

# コンテナを停止&削除する
$ docker compose down

docker compose up を実行後、複数のコンテナを連携してみると同様にブラウザから http://localhost/ へアクセスした際に画面が表示されれば成功です。

Dockerを業務で使用する際に気をつけたいこと

最後に業務でDockerを使用する際の注意点です。

一部のツールの利用に有料ライセンスが必要

Docker DesktopやDocker Hubを制限なく業務で利用するには有料ライセンスが必要になります。[3]
参画したプロジェクトでライセンスが付与されていない場合は下記のような代替手段をご検討ください。

Docker Desktopの代替

Docker Hubの代替

不要なポートは使用しない

コンテナをサービスとして利用してみるでも紹介した通り、Dockerを利用する環境によっては意図せずホスト側のポートを解放している場合[2:1]があります。

起動したコンテナなどに脆弱性がある場合、意図せずホスト側のポートも開けてしまうと悪用されてしまう可能性があります。
不必要にポート開けないよう設定の見直しや使用してないコンテナは停止するなど運用方法をご検討ください。

Docker Engine v28: デフォルトでコンテナネットワークを強化する

Container imageに秘密情報を書き込まない

コンテナイメージを作成する際、データベースの接続情報など秘密情報を書き込まないように注意が必要です。

万が一、コンテナイメージに秘密情報を書き込んだままDocker Hubなどで公開してしまうと秘密情報も一緒に公開されてしまいます。

.env などで秘密情報を管理していてもDockerfileの COPY 命令で他のソースコードに紛れてイメージに含まれてしまう可能性もあります。
.dockerignore などを活用しコンテナイメージに必要以上の情報が混入しないようご検討ください。

コンテナに必要以上の権限を与えない

Dockerでは下記のような特徴もあります。

  • Dockerデーモンはデフォルト設定ではrootユーザーとして実行される
  • --privileged フラグを付与し起動したコンテナはホスト上のプロセスとほぼ同じ権限を持つ

以上のことからコンテナの起動方法次第でホスト側のファイルを操作するなど、意図せずホストに影響を与える場合もあるため注意が必要です。

rootユーザーの権限が不要であればDockerをRootless modeで実行する、コンテナ起動時に必要な権限のみ付与するなどの対応で万が一、コンテナがホストに与える悪影響を抑止できます。

CPUアーキテクチャの違いでイメージが使えないこともある

コンテナイメージはCPUアーキテクチャの種類(x86, arm64など)によって同じアプリケーションでも使用するコンテナイメージが異なります。
MySQL、Pythonなど複数のCPUアーキテクチャに合わせたコンテナイメージが公開されてるため通常利用には影響はありませんが、
コンテナイメージを作成し配布する場合などは注意が必要です。

CPUアーキテクチャが異なるコンテナイメージの作成が必要な場合はDocker Buildxを利用することでマルチアーキテクチャイメージを簡単にビルドできます。

参考文献

脚注
  1. Docker Hub usage and limitsによる制限 ↩︎

  2. Docker Engine v28: デフォルトでコンテナネットワークを強化する ↩︎ ↩︎

  3. Dockerプランに関するよくある質問 ↩︎

bitA Tech Blog
bitA Tech Blog

Discussion