Dockerについて調べるきっかけ
Go言語でのアプリケーション作成をしている際に、DBの設定をDockerで行う、という手順がありましたが、詳しい解説はなかったので戸惑ってしまいました。
(Go言語の解説なので仕方がない)
Dockerという自分にとって、新しく出てきた概念についてまとめを行い、
作成したdocker-composeについても解説をし、理解を深めていきたいと思います。
Dockerとはなにか
Dockerとはコンテナを作成、管理するためのオープンソースソフトウェアです。
コンテナというのは、アプリケーションを実行するための実行環境(OS,ライブラリやフレームワーク,設定や依存ツール)をパッケージ化した技術です。
この共通のコンテナを遣えば自宅PCだろうが職場PC、クラウドでも同じ挙動が担保される、ということになります。
環境の作りこみは通常、「PCやサーバに直接インストールして」行いますが、これだと人によってバラつきが出ます。
(このバラつきのせいで、Aさんの環境では動作したのにBさんの環境では動作しない!というようなことが起こる)
それをコンテナによって防ぐ、ということになります。
Dockerは上述のコンテナを、作ったり実際に動かすためのツールです。
以下のロゴが象徴するように、dockerというクジラ君がフロントエンド、バックエンド、データベース用のライブラリや設定をそれぞれコンテナ入れ、積み上げて運んでくれる、というイメージです。
Dockerでコンテナを作成してみる
コンテナを作成するためには、DockerFileを作成する必要があります。
このファイルがコンテナの設計図となっていきます。
実際にアプリケーションを作成しようとすると、上述の通りフロントエンド用、バックエンド用、データベース用、その他用・・・というように複数台のコンテナを作成し、起動させる必要があります。
ただ、dockerファイルは一つでよく、その中で定義していくようなイメージです。
docker-compose.yml
├─ frontend (React/TypeScript)
├─ backend (Go)
├─ db (MySQL)
└─ adminer (DB GUI, 開発用)
今回はDB(MySQL設定)のためのDockerFileを作成しました
version: "3.9"
services:
mysql:
image: mysql:8.0
container_name: mysql
ports:
- "3306:3306"
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: no
MYSQL_DATABASE: <DB_NAME>
MYSQL_USER: <DB_USER>
MYSQL_PASSWORD: <DB_PASS>
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "mysql"]
interval: 3s
timeout: 5s
retries: 5
start_period: 5s
restart: always
volumes:
- ./external-apps/db/:/docker-entrypoint-initdb.d
#ローカルでは ./external-apps/db/ 配下にSQLファイルを配置している
networks:
- api-network
mysql-cli:
image: mysql:8.0
command: mysql -hmysql -uapp -ppassword api_database
depends_on:
mysql:
condition: service_healthy
networks:
- api-network
networks:
api-network:
driver: bridge
ブロックごとに説明します。
version
version はDocker Composeファイルのバージョン指定です。Composeファイルの書式や利用できる機能はこのバージョンに応じて決まります。例えばこのファイルでは version: "3.9" と記載されており、Compose仕様バージョン3.9に沿った設定を書いていることを示します
service
services はコンテナ化する各サービス(アプリケーションコンテナ)を定義するセクションです。
この中にサービス名ごとのブロックをネストさせ、その中に具体的な設定を記述します。
たとえばこのファイルでは、mysql と mysql-cli という2つのサービスが定義されています。それぞれのサービスブロック内に、使用するイメージや環境変数などの設定項目が含まれています。
image
image はコンテナの元になるDockerイメージを指定する項目です。
ここで記述したイメージを元にコンテナが起動します。例えば image: mysql:8.0 と書かれていれば、Docker Hub上の「mysql」イメージのバージョン8.0を使用するという意味です
container_name
container_name はコンテナに任意の名前を付けるための設定です。
たとえばファイルでは container_name: mysql としており、このコンテナに「mysql」という名前を明示的に付けています。加えて、この名前(あるいはサービス名)は同じネットワーク上の他のコンテナからホスト名として利用できるため、mysql-cli コンテナ側でデータベースのホストを「mysql」と指定すれば接続できるようになります
enviroment
environment はコンテナ内で設定する環境変数を列挙する項目です。
環境変数とは、コンテナ内のアプリケーションの設定値を外部から渡すためのものです。例えばMySQL公式イメージでは、MYSQL_DATABASE や MYSQL_USER など特定の変数名を定義すると、初期データベース名やユーザ名・パスワードを自動設定してくれます。
ファイルでは以下のような環境変数が設定されています。
・MYSQL_ALLOW_EMPTY_PASSWORD: no – 空のパスワード(パスワード未設定)を許可しない設定
・MYSQL_DATABASE: – 初期作成するデータベース名
・MYSQL_USER: – 初期作成されるデータベースユーザー名
・MYSQL_PASSWORD: ${MYSQL_PASSWORD} – 上記ユーザーのパスワード
ports
ports はホストとコンテナのポート番号を対応付け(ポートマッピング)する設定です。
書式は "ホスト側ポート:コンテナ側ポート" で、ホストマシンのポートとコンテナ内部のポートを紐づけます。例えばファイルでは - "3306:3306" と指定されており、ホストの3306番ポートに来た通信をコンテナ内の3306番ポート(MySQLのデフォルトポート)に転送することを意味します。
これにより、ホストマシン(開発PCなど)から見るとローカルの3306番ポートでコンテナ内のMySQLに接続できるようになります。
volumes
volumes はホスト側のファイルシステムとコンテナ内のディレクトリを共有(マウント)する設定です。
書式は "ホスト側のパス:コンテナ内のパス" となっており、指定したフォルダをコンテナ内に接続します。
例えばファイルの記述では以下のボリュームマウントが行われています。
ホスト側のパス コンテナ内のパス
./external-apps/db/ /docker-entrypoint-initdb.d
ホスト側の ./external-apps/db/ ディレクトリ(Composeファイルからの相対パス)に置いたファイルが、コンテナ内の /docker-entrypoint-initdb.d にマウントされます。MySQL公式イメージでは、このディレクトリに置かれたSQLスクリプトを初回起動時に自動実行してデータベースの初期化を行う仕組みがあります。そのため、この設定によりホスト側に用意した初期化用SQLファイルなどをコンテナ起動時に読み込ませることができます。また、volumes を使ってデータディレクトリをホストにマウントすれば、コンテナを削除してもデータをホスト側に保存(永続化)しておくことが可能です。
healthcheck
healthcheck はコンテナ内のサービスが正常に動作しているかを確認するための設定です。ここで指定したコマンドをコンテナ内で定期的に実行し、その成否によってコンテナの**ヘルス(健康状態)**を判定します。
ファイルのMySQLサービスでは、mysqladmin ping -h mysql というコマンドでデータベースサーバーへの接続応答を確認するようになっています(test にコマンドをリスト形式で指定)。加えて、interval: 3s(チェック間隔3秒)、timeout: 5s(タイムアウト5秒)、retries: 5(最大5回リトライ)、start_period: 5s(開始から5秒間は猶予期間)といったパラメータも設定されています。これらにより、コンテナ起動直後は5秒待ってからヘルスチェックを開始し、以降3秒ごとに上記コマンドでサービス応答を確認します。5秒以内に応答がなければ1回失敗とみなし、これを最大5回繰り返しても成功しない場合にコンテナの状態を“不健康(unhealthy)”と判断します。正常に応答があればコンテナ状態が“healthy”となります。
このように healthcheck を設定しておくと、他のサービスから「このコンテナは準備完了したか?」を判断しやすくなります。
restart
restart はコンテナの再起動ルールを指定する項目です。restart: always と設定すると、そのコンテナが停止した場合に常に自動再起動するようになります。
例えばデータベースのコンテナが何らかの理由で停止してしまっても、自動的に再起動されるためサービス継続性を高められます(手動で明示的に停止した場合を除きます)
他にも no(自動再起動しない)、on-failure(エラー終了時のみ再起動)、unless-stopped(停止指示しない限り再起動)といったオプションがありますが、開発用途では always が指定されることが一般的です。
depends_on
depends_on はサービス間の起動順序の依存関係を指定する設定です。
ファイルでは mysql-cli サービスが mysql サービスに依存するよう指定されており、depends_on: の下で mysql: を列挙しています。その下にさらに condition: service_healthy という条件を付けることで、「依存先の mysql サービスのヘルスチェックが通った後にこのサービスを起動する」という動作になります。
つまりMySQLデータベースコンテナが完全に立ち上がり利用可能(healthy)になってから、mysql-cli コンテナを実行するといった制御が可能です。
command
command はコンテナ起動時にデフォルトで実行されるコマンドを上書きするための設定です。
ファイルでは mysql-cli サービスにおいて、command: mysql -hmysql -uapp -ppassword api_database と記述されています。これはMySQLクライアントを使ってmysqlホスト(先のcontainer_name: mysqlで名付けたDBコンテナ)に接続し、appユーザー(パスワードpassword)でデータベースapi_databaseにログインするコマンドを実行せよ、という指示です。
の設定により、Composeで mysql-cli コンテナを起動すると自動的にMySQLクライアントが実行され、先に起動したデータベースコンテナに接続します。
networks
networks はコンテナ同士の通信ネットワークを設定する項目です。Docker Composeではデフォルトでプロジェクト用の共通ネットワーク(例:プロジェクト名_default)が作られ、同じComposeファイル内のサービスは自動的にそのネットワーク上で互いに通信できるようになります。
ファイルでは api-network というカスタムネットワークを定義し(ファイル末尾の networks: セクションで driver: bridge として作成)、各サービスでそれに参加するよう networks: - api-network と記述しています。
これにより2つのサービスはいずれも api-network 上に接続され、互いを名前で認識し通信することができます(前述のとおりmysql-cliからmysqlコンテナへサービス名で接続可能)。
Dockerコマンドで動作確認
ここまで記述したファイルによって以下が可能になっているはずです。
・MySQLデータベースのコンテナをワンコマンドで立ち上げられる
・初期状態で api_database というデータベースと app ユーザが自動的に作成される
・初期化用SQLファイルを external-apps/db/ に置くと、起動時に自動実行されてDBが初期化される
・ホストPCから localhost:3306 経由でコンテナ内のMySQLに接続できる
・コンテナの状態(MySQLがちゃんと起動したかどうか)をヘルスチェックで監視できる
・MySQLが準備完了になってから mysql-cli コンテナを起動し、自動でデータベースに接続できる
・ネットワークを分けて管理しているため、複数サービスを同時に安全に利用できる
実際に試してみます。
まずは、以下のコマンドでコンテナを作成、起動します。
また、設定に書いたネットワークやボリュームも自動で用意してもらいます。
docker-compose up -d
次に以下コマンドを実行し、MySQLに接続します。
docker-compose run mysql-cli
このとき、dockerfile側のmysql-cli配下で以下のような設定を書いていました。
command: mysql -hmysql -uapp -ppassword api_database
これにより、docker-compose run mysql-cli を実行したときに、環境変数で設定しておいたユーザー名・パスワード・DB名を使って、MySQLにログインすることができます。
ターミナルで以下のように表示されているため、MySQLにログインできていることが確認できます。
mysql>
また、今回はdockerファイルのvolumesセクションで以下のように設定しているため、./external-apps/db/に配置しておいたinit.db(テーブルを2つ定義している)も実行されます
volumes:
- ./external-apps/db/:/docker-entrypoint-initdb.d
そこで、以下コマンドを実行し、定義したテーブルが作成されているか確認します。
mysql>show databases;
すると実行結果が表示され、想定通りSQLファイルも実行されていることがわかります。
+--------------------+
| Database |
+--------------------+
| api_database |
| information_schema |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
ちなみにinit.dbは以下のようなファイルです。
CREATE DATABASE IF NOT EXISTS api_database;
USE <DB_NAME>;
CREATE TABLE albums (
id INT PRIMARY KEY AUTO_INCREMENT,
release_date DATE,
category_id INT,
title VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE categories (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
まとめ
Docker は「同じ環境を誰でも簡単に再現できる仕組み」です。
DockerFileを共有して開発を効率化させることができます。
今回の例では、MySQL の環境構築をワンコマンドで行い、初期化SQLも自動実行できる仕組みを学びました。
次回以降は、バックエンドやフロントエンド用のDockerFileも作成して理解を深めたいです!