【Docker】docker-composeを利用してVM上に独自ドメインでWebサイトを公開する

こんにちは

もう年の瀬ですね

年末年始で時間があるので、Dockerをもう一度勉強したいと思っています

なので、今回はあえてVM上にdocker-composeを利用して、独自ドメインでWebサイトを公開する流れを書いてみます

(つい先日までサーバー上にyumなどでApacheなどのwebサーバーを別途インストールしないと、DockerでNginxイメージを利用していても、Webサイトを外部公開できないと思っていたのは内緒

 

■環境

プラットフォーム:Vagrant

VM:AlmaLinux9

Web構成:Nginx + PHP-FPM + MySQL

ドメイン:www.example.com

 

■手順

※前提としてVagrantでAlmaLinux9のサーバーを1台構築しておいてください

 

1. git のインストール

※使わないですが念のためインストールしています

dnf install git

 

2. dockerのインストール

・レポジトリの追加

sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

 

・docker-ce のインストール

sudo dnf -y install docker-ce docker-ce-cli containerd.io

 

・バージョンの確認

docker --version

→ Docker version 29.1.3 のように出力されればOK

 

・docker の起動

sudo systemctl start docker

 

・自動化設定

sudo systemctl enable docker

 

・起動確認

systemctl status docker

→ active (running) になっていればOK

 

3. docker-composeのインストール

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

 

・実行権限の付与

sudo chmod +x /usr/local/bin/docker-compose

 

・バージョン確認

/usr/local/bin/docker-compose --version

→ Docker Compose version v5.0.1 のように出力されればOK

 

4. docker-composeの作成

全体のディレクトリ構成は下記です

※かなり長いので読み飛ばしていただいて問題ございません

.
├── README.md
├── app
│   ├── Dockerfile
│   ├── conf
│   │   ├── disable_display_error.ini
│   │   └── zz-docker.conf
│   └── src
│       ├── common
│       │   └── db_connect.php
│       ├── css
│       │   └── user_list.css
│       ├── get_all_users.php
│       └── index.php
├── db
│   ├── Dockerfile
│   ├── conf
│   │   └── my.cnf
│   ├── data
│   └── sql
│       └── init.sql
├── docker-compose.yml
└── web
    ├── Dockerfile
    └── conf
        └── www.example.com.conf

 

docker-compose.yml

version: "3.9"
services:
  web01:
    build:
      context: ./
      dockerfile: web/Dockerfile
    container_name: web01-container
    volumes:
      - ./app/src:/var/www/vhosts/www.example.com/public_html
    depends_on:
      - app01  
    ports:
      - "80:80"

  app01:
    build:
      context: ./
      dockerfile: app/Dockerfile
    container_name: app01-container
    volumes:
      - ./app/src:/var/www/vhosts/www.example.com/public_html
    restart: always  
    ports:
      - "9000:9000"
    
  db01:
    build:
      context: ./
      dockerfile: db/Dockerfile
    container_name: db01-container
    env_file: .env/db_env_file
    restart: always
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/conf/my.cnf:/etc/my.cnf
    ports:
      - "3307:3306"

 

./env/db_env_file

MYSQL_ROOT_PASSWORD=Testtest991!

→ MySQLコンテナのrootユーザー用パスワードを記載しています

 

app/Dockerfile

FROM php:8.0.30-fpm

#php.ini用confファイルコピー
COPY ./app/conf/disable_display_error.ini /usr/local/etc/php/conf.d/

#php-fpm用confファイルコピー
COPY ./app/conf/zz-docker.conf /usr/local/etc/php-fpm.d/

RUN groupmod -g 1000 www-data
RUN usermod -u 1000 www-data

#ドキュメントルート作成
RUN apt-get update -y && \
    apt-get install -y vim less && \
    docker-php-ext-install pdo_mysql && \
    mkdir -p /var/www/vhosts/www.example.com/public_html

COPY ./app/src /var/www/vhosts/www.example.com/public_html

# ユーザー切り替え
USER www-data

#作業ディレクトリ設定
WORKDIR /var/www/vhosts/www.example.com/public_html

#ポート開放
EXPOSE 9000

→ php-fpm用のDockerfileとなり、ドキュメントルート(/var/www/vhosts/www.example.com/public_html)の作成などを実行しています

 

app/conf/disable_display_error.ini

display_errors = Off
log_errors = true

→ phpのエラー内容がブラウザ上に出力されないようにしています

 

app/conf/zz-docker.conf

[global]
daemonize = no

[www]
listen = 9000
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

→ php-fpmのチューニング用confファイルです

 

app/src/common/db_connect.php

<?php
const DSN = 'mysql:host=db01;dbname=test_db';
const USERNAME = 'root';
const PASSWORD = 'Testtest991!';

try {
       function db_connect(){
        $dbh = new PDO(DSN,USERNAME, PASSWORD, [
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, 
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
        ]);
        return $dbh;
    }
} catch (PDOException $e) {
    echo 'ERROR: Could not connect'.$e->getMessage();
}

→ MySQLコンテナへ接続するためのコードです

 

app/src/css/user_list.css

body {
  margin: 0;
  font-family: 'Segoe UI', sans-serif;
  background-color: #f5f5f5;
  display: flex;
  justify-content: center;
  padding: 40px 0;
}

/* コンテナ */
.container {
  background: #fff;
  padding: 30px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.05);
  width: 90%;
  max-width: 800px;
}

/* 見出し */
h2 {
  margin: 0 0 20px;
  font-size: 24px;
  color: #333;
  text-align: center;
}

/* テーブル */
table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 20px;
}
thead th {
  background-color: #f0f0f0;
  padding: 10px;
  font-weight: 600;
  text-align: left;
  border-bottom: 2px solid #ddd;
}
tbody td {
  padding: 10px;
  border-bottom: 1px solid #eee;
}
tbody tr:hover {
  background-color: #fafafa;
}

→ 最低限のcssですが、なくても問題ないです

 

app/src/get_all_users.php

<?php

//DBへ接続
require_once "./common/db_connect.php";
$dbh = db_connect();

//すべてのユーザーを取得
$sql = ("SELECT * FROM users ORDER BY id DESC");
$stmt = $dbh->prepare($sql);
$stmt->execute();
$users = $stmt->fetchAll();

?>

→ MySQLコンテナへ接続し、登録済みのユーザー情報を取得しています

 

app/src/index.php

<?php session_start(); 

//ユーザー一覧を取得
require_once './get_all_users.php';

?>

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>ユーザー一覧ページ</title>
  <link rel="stylesheet" href="./css/user_list.css">
</head>
<body>
  <div class="container">
    <h2>ユーザー一覧</h2>
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>ユーザー名</th>
        </tr>
      </thead>
      <tbody>
        <?php foreach ($users as $user): ?>
        <tr>
          <td><?= htmlspecialchars($user['id']) ?></td>
          <td><?= htmlspecialchars($user['name']) ?></td>
        </tr>
        <?php endforeach; ?>
      </tbody>
    </table>
  </div>
</body>
</html>

→ 独自ドメインでアクセスすると表示される画面であり、dbから取得したユーザーIDと名前が表示されるようにしています

 

db/Dockerfile

FROM mysql:8.0
COPY db/sql /docker-entrypoint-initdb.d
COPY db/conf/my.cnf /etc/my.cnf
EXPOSE 3306

→ 3306番ポートでMySQLを公開するようにしており、後述する init.sql ファイルをコンテナ内にコピーすることで、コンテナ起動時に自動的にDBの作成やデータの投入を実行しています

 

db/conf/my.cnf

# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove the leading "# " to disable binary logging
# Binary logging captures changes between backups and is enabled by
# default. It's default setting is log_bin=binlog
# disable_log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M
#
# Remove leading # to revert to previous value for default_authentication_plugin,
# this will increase compatibility with older clients. For background, see:
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_default_authentication_plugin
default-authentication-plugin=mysql_native_password

datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
#sql_mode=''
sql_mode='NO_ENGINE_SUBSTITUTION'
log-error=/var/log/mysqld_error.log

general_log
general_log_file=/var/log/mysqld_general.log

→ MySQL用のconfファイルです

 

db/data

→ データ永続化のためにMySQLコンテナのDBディレクトリ配下をローカルにbindマウントしています

 

db/sql/init.sql

CREATE DATABASE test_db;
CREATE TABLE test_db.users (id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, name VARCHAR(50) NOT NULL);
INSERT INTO test_db.users (id, name) VALUES (1, 'test01');
INSERT INTO test_db.users (id, name) VALUES (2, 'test02');
INSERT INTO test_db.users (id, name) VALUES (3, 'test03');
INSERT INTO test_db.users (id, name) VALUES (4, 'test04');

→DBとテーブルの作成、および初期データを投入しています

 

web/Dockerfile

FROM nginx:1.25.0

#confファイルのコピー
COPY ./web/conf/www.example.com.conf /etc/nginx/conf.d

#ドキュメントルート作成
RUN mkdir -p /var/www/vhosts/www.example.com/public_html

EXPOSE 80

→ nginxコンテナを作成しています

 

web/conf/www.example.com.conf

server {
    listen 80;
    server_name  www.example.com;
    root         /var/www/vhosts/www.example.com/public_html;
    index        index.php;
    access_log   /var/log/nginx/www.example.com_access.log main;
    error_log    /var/log/nginx/www.example.com_error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
        index index.php;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass app01:9000;
    }

}

→ www.example.com で独自ドメインを設定しており、.php 拡張子のファイルはphp-fpm用コンテナ(app01)へプロキシするようにしています

 

5. docker-compose の起動

・Dockerfileのビルド

docker-compose build --no-cache

→ 初回ビルドなので –no-cache は不要ですが、念のためつけています

 

・起動

docker-compose up -d

→ VMのスペックによってはかなり時間がかかるかもしれません

 

・確認

docker container ps -a

→ nginx_php-fpm_mysql_docker のプレフィックスがついたコンテナが3種類起動していればOKです

 

6. ブラウザから確認

・ローカルのhostsファイルで名前解決

[VMのIPアドレス] www.example.com

 

・ブラウザからアクセス

http://www.example.com/

→下記のように独ドメインでアクセスできること + DB内のユーザー情報が画面上の表示されていればOK

 

■まとめ

いかがでしたでしょうか?

最近では基本的にはECSなどのマネージドサービスを利用した公開が多いかと思いますが、まだまだVM上に直接公開していることもあると思うので、その際にこの記事が何かの助けになれば幸いです

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA