♟️

INCEPTION : Docker イメージを仮想化し、新しい個人用仮想マシンを構築メモ

2024/08/21に公開


Inceptionというコンテナ仮想化の課題に取り組みました。
このプロジェクトの目的は、Docker を使用してシステム管理に関する知識を広げることです。複数の Docker イメージを仮想化し、新しい個人用仮想マシンに作成します。

Dockerを使ってWordPress、データベース、サーバーの構築ができるようになると、ドメインを購入しなくてもローカル環境や内部ネットワークでウェブサイトやサービスを実装・運用することが可能です。また、プロジェクトの迅速な立ち上げ、仮想サーバーのテスト、学習目的での利用など、さまざまなシナリオで有用です。ただし、公開サイトとして運用する場合には、ドメインの購入や適切なホスティング環境が必要になることもあります。

まず、ホストOSにVirtual Machineをダウンロードし、その中でDocker環境を構築します。Dockerはそれぞれのコンテナを独立して起動する必要があり、Docker Composeを使用します。Dockerで立ち上げたコンテナの中でbashを起動させ、そこからさらにデータベースにログインして操作したり サーバー内部でログを確かめたりするものとなります。

映画'Inception'の中で、主人公が 他人の夢の中に入り、その中でまた夢を見て その夢の中の夢でアイデアを植え付ける、といった階層構造が 今回の仮想環境の構造と 類推させられていることを念頭におくと非常に興味深い実装になりました。現状どの位置にいるのか、どこにリクエストを送って、どんなレスポンスが返ってくるのか逐一把握する必要があります。スクリプトやconfファイルの1行、1文字ずつ全てを理解していなければ思うように動作できないので、なかなか手強いプロジェクトとなりました。

実装にあたり、残された箇条書きメモを公開します。

DockerとDocker Composeの仕組み

Dockerは、アプリケーションをコンテナと呼ばれる軽量の仮想環境で実行するためのプラットフォームです。コンテナは、アプリケーションとその依存関係を含む一つのパッケージとして動作し、どこでも同じ環境で実行できます。これにより、開発環境と本番環境の違いによる問題を軽減できます。

  • イメージ: コンテナの静的な定義を提供します。Dockerfileを用いて作成されます。
  • コンテナ: イメージから作成された動的な実行単位です。必要なリソースを共有しつつ、他のコンテナとは隔離されています。

Docker Composeは、複数のコンテナを定義し、協調して動作させるためのツールです。docker-compose.ymlという設定ファイルを使用して、サービス、ネットワーク、ボリュームを定義します。これにより、複雑なアプリケーションもシンプルに管理できます。

Docker Composeを使用するDockerイメージと使用しないDockerイメージの違い

  • 使用する場合: Docker Composeは、複数のコンテナを一括して管理できます。例えば、Nginx、MariaDB、WordPressの各コンテナを同時に起動し、それらをネットワークで連携させることが容易です。開発、テスト、デプロイの一貫性を保つために、サービス全体の構成を一つのファイルで管理できるのが利点です。

  • 使用しない場合: 単一のコンテナを個別に起動するのは簡単ですが、依存関係が複雑なアプリケーションには向いていません。手動でコンテナ間のネットワークを設定し、個別に起動、停止、再起動する必要があり、管理が煩雑になります。

VMと比較したDockerの利点

  1. 軽量性:

    • VM: 各VMは独自のOSを持ち、リソースを大量に消費します。
    • Docker: ホストOSのカーネルを共有するため、軽量で高速に起動します。
  2. 効率性:

    • VM: リソースのオーバーヘッドが大きく、パフォーマンスが低下することがあります。
    • Docker: リソースのオーバーヘッドが少なく、高いパフォーマンスを維持できます。
  3. 移植性:

    • VM: ホストOSに依存する部分が多く、移行が困難な場合があります。
    • Docker: コンテナは同一の環境を保つため、どのホストでも同じように動作します。
  4. スケーラビリティ:

    • VM: スケールアップやスケールアウトがリソースの制約で困難になることがあります。
    • Docker: 短時間でコンテナを増減できるため、スケーラブルな運用が可能です。

Docker Networkについて

Docker Networkは、コンテナ同士や外部の世界とコンテナを接続する仕組みです。コンテナ間の通信を制御します。ネットワークを利用することで、同じホスト内や異なるホスト間でコンテナ同士を接続できます。主なネットワークタイプには、Bridgeネットワーク(デフォルトで使用されるローカルホスト内のネットワーク)や、Overlayネットワーク(複数ホスト間のコンテナを接続)があり、柔軟なネットワーク構築が可能です。

https://o2mamiblog.com/docker-beginner-2/

TLS v1.2/v1.3 証明書について

TLS (Transport Layer Security) は、ネットワーク通信を保護するプロトコルです。インターネット上の通信を暗号化して安全性を確保する技術です。TLS v1.2は広く使われており、TLS v1.3はセキュリティとパフォーマンスが向上しています。これらのバージョンは、サーバーとクライアント間のデータ交換を第三者が解読できないように保護します。
TLS v1.2: 広く普及しており、各種暗号スイートをサポートしますが、設定によっては脆弱性のリスクがあります。
TLS v1.3: より安全で効率的な暗号化を提供し、セッション再開やハンドシェイクの高速化など、セキュリティとパフォーマンスが向上しています。
これらの証明書は、HTTPS通信やVPN接続、メールセキュリティなど、インターネット上での安全なデータ通信に使用されます。TLS証明書を適切に導入することで、通信内容の盗聴や改ざんを防ぎ、信頼性の高いセキュアな接続を実現します。

イメージのビルド

docker-compose build mariadb

コンテナの起動

docker-compose up -d mariadb

ログの確認

docker-compose logs mariadb

不要なDockerオブジェクトの削除

docker system prune -a

wordpressコンテナ内でbash起動

docker exec -it wordpress-container bash

コンテナを起動する - container run

オプション 意味 用途
-i
--interactive
コンテナの標準入力に接続する コンテナを対話操作する
-t
--tty
擬似ターミナルを割り当てる コンテナを対話操作する
-d
--detach
バックグラウンドで実行する ターミナルが固まるのを避ける
--rm 停止済コンテナを自動で削除する 起動時に停止済コンテナと一意な情報が衝突するのを避ける
--name コンテナに名前をつける コンテナを指定しやすくする
--platform イメージのアーキテクチャを明示する M1 Mac で必要な場合がある

確認表示コマンド

  • docker ps
    docker ps は Docker コンテナのリストを表示するコマンドです。オプションの -a を付けると、すべてのコンテナ(稼働中、停止中、終了済み)を表示します。

    • docker ps : 稼働中のコンテナのみ表示
    • docker ps -a : すべてのコンテナ(稼働中、停止中、終了済み)を表示
  • docker container ls
    docker container lsdocker ps のエイリアスであり、同じ機能を持ちます。デフォルトでは、稼働中のコンテナのみを表示します。

    • docker container ls : 稼働中のコンテナのみ表示
    • docker container ls -a : すべてのコンテナ(稼働中、停止中、終了済み)を表示
  • docker volume ls

  • docker network ls

  • docker image ls

  • docker volume inspect <>

docker-compose.ymlファイルでポートを設定する方法には、主に2つの形式があります。具体的には、"ホストポート:コンテナポート"形式と単一のポート番号の形式です。それぞれの違いと目的について説明します。

.ymlファイル内でのポート

1. "ホストポート:コンテナポート"形式

この形式では、ホストマシンのポート番号を指定し、それをコンテナの特定のポート番号にマッピングします。例えば:

ports:
  - "443:443"

この設定の意味は以下の通りです:

  • ホストマシンのポート443にアクセスすると、コンテナ内のポート443にリクエストが転送されます。
  • 具体的には、ホストマシンのポート443 (通常はHTTPSトラフィックに使用) をコンテナ内のポート443にマッピングしています。

2. 単一のポート番号形式

一方で、単一のポート番号形式では、コンテナ内でリスンしているポート番号を指定します。例えば:

ports:
  - 3306

この設定の意味は以下の通りです:

  • ホストマシンの任意のポート(空いているポート)が自動的に割り当てられ、それがコンテナのポート3306にマッピングされます。
  • 具体的には、コンテナ内のポート3306 (通常はMariaDBやMySQLなどのデータベースサーバが使用) をホストマシンの空いているポートにマッピングします。この場合、ホストポートはDockerが自動的に選択します。

MariaDB

  • mariadb用のユーザー、パスワード、データベース名、ホスト名を設定する
  • wordpressからアクセスできるように繋げる
    MariaDBのインストール時に使用されるパッケージについて、「mariadb」「mariadb-client」「mariadb-server」の違いを説明します。

パッケージの違い

mariadb

  • 内容: MariaDBのサーバーとクライアントの両方を含むメタパッケージ。
  • 用途: 通常、単純に「mariadb」をインストールすると、MariaDBサーバーおよびクライアントが一緒にインストールされます。これは一般的なデータベースサーバーとして使用される設定です。

mariadb-client

  • 内容: MariaDBのクライアントプログラムのみを含むパッケージ。
  • 用途: リモートのMariaDBサーバーに接続して操作するために使用されます。データベースサーバーとしての機能は含まれていません。クライアントマシンにインストールして、データベースの管理やクエリ実行を行う場合に使用されます。

mariadb-server

  • 内容: MariaDBのサーバープログラムのみを含むパッケージ。
  • 用途: データベースサーバーとしての機能を提供します。実際のデータベース管理システムをホストするために使用されます。クライアントツールは含まれていません。

どちらを使用するべきか

  • 単一のMariaDBコンテナでサーバーとクライアントの両方を必要とする場合: mariadbパッケージをインストールします。このパッケージは、MariaDBサーバーとクライアントの両方をインストールします。
  • データベースサーバーをセットアップする場合: mariadb-serverパッケージをインストールします。このパッケージは、データベースサーバーとしての機能を提供し、サーバーのみがインストールされます。通常、データベースサーバーをホストするコンテナで使用します。
  • クライアントツールのみが必要な場合: mariadb-clientパッケージをインストールします。このパッケージは、MariaDBのクライアントツールを提供します。リモートサーバーに接続して操作するために使用されます。

MariaDBを独立したコンテナで起動する場合、mariadb-serverを使用するのが一般的です。以下は、MariaDBの初期化および設定を行うDockerfileの例です。

MariaDBのcnfファイル設定

MariaDBの50-server.cnfファイルでbind-addressの設定を変更することで、MariaDBが外部からの接続を受け付けるようにすることができます。デフォルトでは、MariaDBはローカルホスト(127.0.0.1)のみでリッスンしています。この設定を変更することで、MariaDBを他のコンテナや外部ホストからアクセス可能にします。

設定変更の理由

bind-address0.0.0.0に変更すると、MariaDBが全てのネットワークインターフェースで接続を受け付けるようになります。これにより、同じDockerネットワーク内の他のコンテナや外部ホストからMariaDBにアクセスできるようになります。

変更方法

50-server.cnfファイルを以下のように編集します:

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address            = 0.0.0.0

なぜ0.0.0.0に変更するのか?

0.0.0.0は、MariaDBが全てのネットワークインターフェースでリッスンすることを意味します。これにより、以下のような利点があります:

  1. 同じDockerネットワーク内の他のコンテナからのアクセス:例えば、WordPressコンテナがMariaDBコンテナに接続する場合、127.0.0.1では接続できないため、0.0.0.0にすることで接続が可能になります。
  2. 外部ホストからのアクセス:開発やテスト環境で外部ホストからデータベースに接続する必要がある場合、0.0.0.0に設定することでこれを実現できます。

注意点

bind-address0.0.0.0に設定すると、セキュリティの観点からリスクが増加する可能性があります。特に、データベースがインターネットに直接公開されている場合は注意が必要です。適切なファイアウォール設定や、必要に応じてIPアドレス制限を行うことを検討してください。

変更手順の例

  1. MariaDBコンテナに入る

    docker exec -it mariadb-container bash
    
  2. 設定ファイルを編集する

    vi /etc/mysql/mariadb.conf.d/50-server.cnf
    

    bind-address0.0.0.0に変更します。

  3. MariaDBサービスを再起動する

    service mysql restart
    

これで、MariaDBが外部からの接続を受け付けるようになります。

entrypoint.shの設定例

このスクリプトは、環境変数のチェック、MariaDBデータベースの初期化と起動、ユーザーとデータベースの作成、そしてMariaDBサーバーの再起動を行います。あくまで一例です。以下に詳しく説明します。

#!/bin/bash

# 環境変数のチェック
if [ -z "$MYSQL_ROOT_PASSWORD" -o -z "$WORDPRESS_DB_NAME" -o -z "$WORDPRESS_DB_USER" -o -z "$WORDPRESS_DB_PASSWORD" ]; then
  echo "Error: Missing required environment variables."
  exit 1
fi

# データベースディレクトリの初期化
if [ ! -d "/var/lib/mysql/mysql" ]; then
  echo "Initializing MariaDB data directory..."
  mysql_install_db --user=mysql --datadir=/var/lib/mysql
fi

# MariaDBサーバーの起動
echo "Starting MariaDB server..."
mysqld --user=mysql &

# MariaDBサーバーの起動待ち
until mysqladmin ping -h"localhost" --silent; do
  echo "Waiting for MariaDB server to start..."
  sleep 1
done

# ルートパスワードの設定
if [ -n "$MYSQL_ROOT_PASSWORD" ]; then
  echo "Setting root password..."
  mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$MYSQL_ROOT_PASSWORD';"
fi

# データベースとユーザーの作成
echo "Creating database and user..."
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS \`$WORDPRESS_DB_NAME\`;"
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "GRANT ALL ON \`$WORDPRESS_DB_NAME\`.* TO '$WORDPRESS_DB_USER'@'%' IDENTIFIED BY '$WORDPRESS_DB_PASSWORD';"

# WordPress用のデータベースを作成
echo "Creating WordPress database..."
mysql -u root -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS \`wordpress\`;"

# MariaDBサーバーの停止
echo "Stopping MariaDB server..."
mysqladmin -u root -p"$MYSQL_ROOT_PASSWORD" shutdown

# MariaDBサーバーをフォアグラウンドで起動
echo "Starting MariaDB server..."
exec mysqld --user=mysql

各セクションの詳細説明

  1. 環境変数のチェック:

    • -z オプションは、変数が空であるかどうかをチェックします。
    • -o オプションは、論理 OR を意味します。いずれかの環境変数が空であればエラーを出力し、スクリプトを終了します。
  2. データベースディレクトリの初期化:

    • /var/lib/mysql/mysql ディレクトリが存在しない場合、新しいデータベースディレクトリを初期化します。
  3. MariaDBサーバーの起動:

    • MariaDBサーバーをバックグラウンドで起動します。
  4. MariaDBサーバーの起動待ち:

    • サーバーが完全に起動するまで待機します。
  5. ルートパスワードの設定:

    • MYSQL_ROOT_PASSWORD が設定されている場合、ルートパスワードを設定します。
  6. データベースとユーザーの作成:

    • 指定されたデータベースとユーザーを作成します。
  7. WordPress用のデータベースを作成:

    • WordPress用のデータベースを作成します。
  8. MariaDBサーバーの停止:

    • サーバーを停止します。
  9. MariaDBサーバーをフォアグラウンドで起動:

    • フォアグラウンドでサーバーを再起動します。

これでスクリプトの動作が理解できると思います。必要に応じて環境変数や設定を調整してください。

Wordpress

  • php8.2, CLI, 付随パッケージインストール
  • wordpress用の管理者&一般ユーザー、パスワード、メールを環境変数作成
  • confファイル編集
  • TLS証明書発行
  • 独自ドメインを使用する

https://www.slideshare.net/slideshow/dockerword-press/44488232

NGINX

  • sslの設定をする
  • http, httpsの設定をする
  • ポート等
docker compose up -d
docker compose exec nginx /bin/bash

nginx で PHP を動かすためにconfファイルを編集
https://qiita.com/Yuhkih/items/2b26f3761578637d0005

nginx サービスが自己署名証明書を使用することで HTTPS で hrinka.42.fr ドメイン名をリッスンするように設定され、NGINX、MariaDB、および WordPress サービスが適切に連携するようになる

nginx.confの解説

構成概要

このnginx.confファイルは、Nginxウェブサーバの設定を行います。以下のような機能を実現しています:

  1. HTTPからHTTPSへのリダイレクト
  2. HTTPSでのサーバ設定
  3. SSL/TLS設定
  4. ウェブサイトのルートディレクトリ設定
  5. PHPファイルの処理方法設定

詳細解説

最初のサーバブロック

server {
	listen 80;
	listen [::]:80;
	server_name xoxo;
	return 301 https://$host$request_uri;
}
  • listen 80;

    • NginxがIPv4アドレスのポート80(HTTPの標準ポート)でリクエストを待ち受けるように設定します。
  • listen [::]:80;

    • NginxがIPv6アドレスのポート80でリクエストを待ち受けるように設定します。
  • server_name xoxo;

    • サーバの名前をxoxoに設定します。この名前はリクエストのHostヘッダーと一致する必要があります。
  • return 301 https://hostrequest_uri;

    • すべてのHTTPリクエストをHTTPSにリダイレクトします。ステータスコード301は「永久に移動した」を意味します。

二つ目のサーバブロック

server {
	listen 443 ssl;
	listen [::]:443 ssl;
	
	server_name xoxo;

	ssl_protocols TLSv1.2 TLSv1.3;

	root /var/www/html;
	index index.php index.html index.htm;

	location / {
		autoindex on;
		try_files $uri $uri/ =404;
	}

	location ~ \.php$ {
    		fastcgi_split_path_info ^(.+\.php)(/.+)$;
    		fastcgi_pass wordpress:9000;
    		fastcgi_index index.php;
    		include fastcgi_params;
    		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    		fastcgi_param PATH_INFO $fastcgi_path_info;
    	}
}
  • listen 443 ssl;

    • NginxがIPv4アドレスのポート443(HTTPSの標準ポート)でSSLを使用してリクエストを待ち受けるように設定します。
  • listen [::]:443 ssl;

    • NginxがIPv6アドレスのポート443でSSLを使用してリクエストを待ち受けるように設定します。
  • server_name xoxo;

    • サーバの名前をxoxoに設定します。
  • ssl_protocols TLSv1.2 TLSv1.3;

    • 使用するTLSプロトコルのバージョンを指定します。ここではTLSv1.2とTLSv1.3が有効になっています。
  • root /var/www/html;

    • ウェブサイトのルートディレクトリを指定します。このディレクトリにウェブページのファイルが格納されます。
  • index index.php index.html index.htm;

    • デフォルトで読み込まれるインデックスファイルのリストを指定します。リクエストされたディレクトリ内にこれらのファイルがあれば、それが自動的に提供されます。

location / ブロック

location / {
	autoindex on;
	try_files $uri $uri/ =404;
}
  • autoindex on;

    • ディレクトリの内容を自動的にリスト表示します。
  • try_files $uri $uri/ =404;

    • リクエストされたURIが存在するファイルまたはディレクトリであるかを確認します。存在しない場合は404エラーページを返します。

location ~ .php$ ブロック

location ~ \.php$ {
	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	fastcgi_pass wordpress:9000;
	fastcgi_index index.php;
	include fastcgi_params;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	fastcgi_param PATH_INFO $fastcgi_path_info;
}
  • location ~ .php$;

    • 拡張子が.phpで終わるリクエストを処理します。
  • fastcgi_split_path_info ^(.+.php)(/.+)$;

    • リクエストURIからスクリプトファイルとパス情報を分割します。
  • fastcgi_pass wordpress:9000;

    • PHPリクエストを処理するためにFastCGIサーバ(ここではWordPressコンテナのポート9000)に渡します。
  • fastcgi_index index.php;

    • デフォルトのインデックスファイルとしてindex.phpを指定します。
  • include fastcgi_params;

    • FastCGIの標準パラメータをインクルードします。
  • fastcgi_param SCRIPT_FILENAME document_rootfastcgi_script_name;

    • FastCGIに渡すスクリプトのファイル名を設定します。
  • fastcgi_param PATH_INFO $fastcgi_path_info;

    • FastCGIに渡すパス情報を設定します。

以上がnginx.confファイルの各部分の解説です。この設定により、NginxはHTTPリクエストをHTTPSにリダイレクトし、HTTPSでのアクセスをサポートし、PHPファイルの処理も行います。

おまけ

Dockerでcontainerを起動すると、勝手に名前がつきます。(指定しなければ)
付けられる名前は例えば、

happy_goldstine
angry_almeida
のように、 形容詞 + 人名 です。

形容詞は全部で57個、人名は全部で73個

ちなみに、 偉大な科学者とHackerの名前 です。

その組み合わせは 57 x 73 = 4,161 個です。

ところが、出てくる組み合わせは 4,160個で 一つだけ、出ない組み合わせがあります。

その理由は以下のコードにあります。

    if name == "boring_wozniak" /* Steve Wozniak is not boring */ {
        goto begin
    }

それは、 boarning_wazniak。

なぜなら、Steve Wozniakはつまらなくないから。

(……だそうです。)

参考資料

https://ja.wordpress.org/support/article/how-to-install-wordpress/
https://mariadb.org/documentation/#getting-started
https://nginx.org/en/docs/

Dockerについてなるべくわかりやすく説明する
https://qiita.com/rawHam/items/80ba13d2d2d56dba411e

ttyとは?
https://zenn.dev/hohner/articles/43a0da20181d34

mariadbコンテナの構築
https://www.infra-linux.com/menu-docker3/mariadb-container-build/

nginxコンテナの構築
https://www.infra-linux.com/menu-docker3/building-nginx-container/

php-fpmとは
https://qiita.com/kotarella1110/items/634f6fafeb33ae0f51dc

https://circleci.com/ja/blog/what-is-yaml-a-beginner-s-guide/

Inception概要
https://medium.com/@afatir.ahmedfatir/unveiling-42-the-network-inception-a-dive-into-docker-and-docker-compose-cfda98d9f4ac

Discussion