はじめに
レガシーシステムの Docker 移行で最も重要なのは再現性と移植性です。GlassFish 4.1 / Java 8 / MySQL 5.7 から Payara 6.11 / Java 21 / MySQL 8.0 への移行において、開発環境、AWS EC2、Windows Server の3つの環境で同一の Docker 構成を実現した方法を書いていきます。
Docker 構成の全体像
Apache、Payara、MySQL の3層構成を Docker Compose で実現しました。
┌─────────────┐
│ Apache │ :80
└──────┬──────┘
│
┌──────▼──────┐
│ Payara │ :8080, :4848
└──────┬──────┘
│
┌──────▼──────┐
│ MySQL │ :3306
└─────────────┘
Payara Dockerfile の設計
基本構成
FROM payara/server-full:6.2023.12-jdk11
# 環境変数の設定
ENV PAYARA_DIR=/opt/payara6
ENV DOMAIN_NAME=domain1
# domain.xml と設定ファイルのコピー
COPY domain.xml ${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/config/
COPY myapp-api.war ${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/autodeploy/
# ログディレクトリの永続化
VOLUME ["${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/logs"]
# ポート公開
EXPOSE 8080 4848
# Payara 起動
CMD ["asadmin", "start-domain", "--verbose", "domain1"]
設計のポイント
1. domain.xml の直接配置
既存の domain.xml をそのまま使用することで、設定の移行コストを最小化しました。
既存設定を最大限活用でき、移行時の変更を最小限に抑えられます。ただし、環境固有の設定(データベース接続情報など)は環境変数で外部化する必要があります。
2. WAR の自動デプロイ
autodeploy ディレクトリに WAR を配置することで、起動時に自動デプロイされます。
COPY myapp-api.war ${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/autodeploy/
asadmin deploy コマンドを使う方法もありますが、自動デプロイの方がシンプルです。
3. ログの永続化
Volume を使ってログを永続化します。
VOLUME ["${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/logs"]
Docker Compose で実際のパスをマウント:
volumes:
- /opt/myapp/logs:/opt/payara6/glassfish/domains/domain1/logs
4. --verbose オプション
起動ログを標準出力に出力することで、docker logs でログを確認できます。
CMD ["asadmin", "start-domain", "--verbose", "domain1"]
docker logs でリアルタイムにログ確認でき、トラブルシューティングが容易になります。ログ集約ツールとの連携も簡単です。
Docker Compose の設計
開発環境用の構成
version: '3.8'
services:
apache:
image: httpd:2.4
container_name: myapp-apache
ports:
- "80:80"
volumes:
- ./apache/httpd.conf:/usr/local/apache2/conf/httpd.conf
- ./apache/htdocs:/usr/local/apache2/htdocs
depends_on:
- payara
networks:
- myapp-network
payara:
build:
context: ./payara
dockerfile: Dockerfile
container_name: myapp-payara
ports:
- "8080:8080"
- "4848:4848"
volumes:
- /opt/myapp/logs:/opt/payara6/glassfish/domains/domain1/logs
- /opt/myapp/data:/opt/myapp/data
environment:
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
depends_on:
- mysql
networks:
- myapp-network
mysql:
image: mysql:8.0
container_name: myapp-mysql
ports:
- "3306:3306"
volumes:
- /opt/myapp/mysql/data:/var/lib/mysql
- /opt/myapp/mysql/init:/docker-entrypoint-initdb.d
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
networks:
- myapp-network
networks:
myapp-network:
driver: bridge
設計のポイント
1. depends_on による起動順序の制御
depends_on:
- mysql
これにより、MySQL → Payara → Apache の順で起動します。ただし、depends_on は起動順序を制御するだけで、サービスの準備完了を待つわけではありません。
2. ヘルスチェックの追加
Payara の起動に時間がかかるため、ヘルスチェックを追加します。
payara:
healthcheck:
test: ["CMD", "asadmin", "list-applications"]
interval: 10s
timeout: 5s
retries: 10
サービスが準備完了してから次のサービスを起動でき、docker-compose ps でヘルスステータスを確認できます。
3. 環境変数による設定の外部化
.env ファイルで環境変数を管理します。
# .env
DB_NAME=myapp
DB_USER=myapp_user
DB_PASSWORD=change_this_password
MYSQL_ROOT_PASSWORD=change_this_root_password
同じ Docker Compose ファイルを異なる環境で使用でき、機密情報をリポジトリに含めずに済みます。環境ごとの差分管理も容易です。
本番環境用の違い
本番環境では、セキュリティとパフォーマンスを考慮した設定が必要です。
payara:
ports:
- "8080:8080"
# 4848 は公開しない(管理コンソール)
environment:
- JAVA_OPTS=-Xms2g -Xmx4g
restart: always
管理コンソールのポート(4848)を公開せず、JVM のメモリ設定を追加しています。restart: always で自動再起動も有効化しました。
フォルダ構成の標準化
すべての環境で /opt/myapp 配下に統一しました。
/opt/myapp/
├── logs/ # Payara のログ
├── data/ # アプリケーションデータ
├── mysql/
│ ├── data/ # MySQL データディレクトリ
│ ├── init/ # 初期化 SQL
│ └── backup/ # バックアップファイル
└── scripts/ # 運用スクリプト
├── backup.sh
├── restore.sh
└── restart.sh
標準化のメリット
-
運用スクリプトの共通化
- パスがハードコードされていても問題なし
- 環境ごとのスクリプト修正が不要
-
権限管理の簡素化
-
/opt/myapp配下のみ権限設定 - バックアップ範囲が明確
-
-
トラブルシューティングの効率化
- ログの場所が統一
- 環境差異による混乱を回避
運用スクリプトの整備
DB バックアップスクリプト
#!/bin/bash
# backup.sh - MySQL のバックアップを取得
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/myapp/mysql/backup"
DB_NAME="${DB_NAME:-myapp}"
CONTAINER_NAME="myapp-mysql"
# バックアップディレクトリの作成
mkdir -p "${BACKUP_DIR}"
# mysqldump の実行
docker exec "${CONTAINER_NAME}" \
mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" "${DB_NAME}" \
> "${BACKUP_DIR}/backup_${DATE}.sql"
# 古いバックアップを削除(30日以上前)
find "${BACKUP_DIR}" -name "backup_*.sql" -mtime +30 -delete
echo "Backup completed: backup_${DATE}.sql"
docker exec でコンテナ内のコマンドを実行し、タイムスタンプ付きでバックアップを保存しています。古いバックアップは自動削除されます。
Payara 再起動スクリプト
#!/bin/bash
# restart.sh - Payara コンテナの再起動
CONTAINER_NAME="myapp-payara"
echo "Stopping Payara container..."
docker stop "${CONTAINER_NAME}"
echo "Starting Payara container..."
docker start "${CONTAINER_NAME}"
echo "Waiting for Payara to start..."
sleep 10
# ヘルスチェック
docker exec "${CONTAINER_NAME}" \
asadmin list-applications
echo "Payara restart completed"
コンテナの停止と起動を分離し、起動待ち時間を設定しています。ヘルスチェックで正常性も確認します。
SQL スクリプト実行
#!/bin/bash
# exec-sql.sh - SQL スクリプトの実行
SQL_FILE="$1"
CONTAINER_NAME="myapp-mysql"
DB_NAME="${DB_NAME:-myapp}"
if [ -z "${SQL_FILE}" ]; then
echo "Usage: $0 <sql_file>"
exit 1
fi
docker exec -i "${CONTAINER_NAME}" \
mysql -u root -p"${MYSQL_ROOT_PASSWORD}" "${DB_NAME}" \
< "${SQL_FILE}"
echo "SQL script executed: ${SQL_FILE}"
-i オプションで標準入力を渡し、引数チェックでエラーを防止しています。
CloudFormation による IaC 化
AWS 環境のインフラ構成を CloudFormation でコード化しました。
EC2 インスタンスの定義
AWSTemplateFormatVersion: '2010-09-09'
Description: 'MyApp Application Infrastructure'
Parameters:
InstanceType:
Type: String
Default: t3.medium
Description: EC2 instance type
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: EC2 key pair name
Resources:
MyAppEC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: ami-xxxxxxxxx # Amazon Linux 2023
KeyName: !Ref KeyName
SecurityGroups:
- !Ref MyAppSecurityGroup
Tags:
- Key: Name
Value: myapp-server
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
# Docker インストールは初期化スクリプトで実施
セキュリティグループの定義
MyAppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for MyApp application
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/8 # 社内ネットワークのみ
HTTP/HTTPS は全公開し、SSH は社内ネットワークのみに制限しています。MySQL のポート(3306)は公開していません。
IaC のメリット
-
再現性の確保
- 同じ構成を何度でも再構築可能
- 手動設定ミスを排除
-
バージョン管理
- Git で変更履歴を管理
- レビューとロールバックが可能
-
ドキュメントとしての役割
- インフラ構成が自己文書化
- 新規メンバーの理解が容易
初期セットアップスクリプト
Amazon Linux 2023 での環境構築を自動化しました。
#!/bin/bash
# setup.sh - Docker 環境の初期セットアップ
set -e
echo "=== Docker のインストール ==="
sudo yum install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ec2-user
echo "=== 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
echo "=== ディレクトリの作成 ==="
sudo mkdir -p /opt/myapp/{logs,data,mysql/{data,init,backup},scripts}
echo "=== MySQL データディレクトリの権限設定 ==="
sudo chown -R 999:999 /opt/myapp/mysql/data
echo "=== .env ファイルの作成 ==="
cat <<EOF | sudo tee /opt/myapp/.env
DB_NAME=myapp
DB_USER=myapp_user
DB_PASSWORD=change_this_password
MYSQL_ROOT_PASSWORD=change_this_root_password
EOF
echo "=== Git リポジトリのクローン ==="
cd /opt/myapp
git clone https://github.com/example/myapp-infra.git infra
echo "=== Docker Compose の起動 ==="
cd /opt/myapp/infra
docker-compose up -d
echo "Setup completed successfully!"
set -e でエラー時に停止し、各ステップをログ出力しています。MySQL の UID/GID(999)に権限設定も行います。
Windows Server 環境の Hyper-V 構築
Windows Server 上で Docker を動かすため、Hyper-V 上に Rocky Linux を構築しました。
Hyper-V の有効化
# PowerShell で実行
Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart
Rocky Linux のインストール手順
-
ISO のダウンロード
- Rocky Linux 9 の ISO をダウンロード
-
仮想マシンの作成
- 世代: 第2世代
- メモリ: 8GB
- ネットワーク: 外部仮想スイッチ
- ディスク: 100GB
-
セキュアブートの無効化(重要)
仮想マシン設定 → セキュリティ → セキュアブートを無効化Rocky Linux は第2世代の仮想マシンでセキュアブートをサポートしていないため、無効化が必須です。
-
Rocky Linux のインストール
- 言語選択: 日本語 または English
- ネットワークとホスト名: Ethernet (eth0) が接続済みであることを確認
-
インストール先のパーティション設定(推奨):
- デフォルトの自動パーティション設定では
/homeが大きすぎるため、サーバー用途では手動設定を推奨 -
/boot/efi: 600MiB -
/boot: 1GiB -
swap: 4GiB(メモリサイズに応じて調整) -
/: 52GiB~(物理ストレージに応じて調整、目安 50-60GB) -
/home: 2GiB(サーバー用途のため最小限に設定) - デバイスタイプ: LVM を選択
- ファイルシステム: xfs(デフォルト)
- デフォルトの自動パーティション設定では
- ユーザーの作成: 一般ユーザーを作成し、管理者権限を付与
- root パスワードを設定
静的 IP の設定
固定IPがDHCPと衝突していないかのチェック手順(推奨)
- ルーター/ゲートウェイの設定画面で DHCP 範囲を確認(例: 10.20.1.100-10.20.1.200 ならその範囲外を使う)
- 使いたいIPに ping して応答の有無を確認(例:
ping 10.20.1.50) - ping の直後に ARP テーブルで MAC 有無を確認(例:
arp -aで該当IPにMACが出れば使用中) - ping 応答なし + ARP に出ない + DHCP 範囲外 なら衝突リスクは低いが、情シスの予約一覧があればそちらを優先
# nmcli で静的 IP を設定
sudo nmcli connection modify eth0 ipv4.addresses 192.168.1.100/24
sudo nmcli connection modify eth0 ipv4.gateway 192.168.1.1
sudo nmcli connection modify eth0 ipv4.dns "8.8.8.8 8.8.4.4"
sudo nmcli connection modify eth0 ipv4.method manual
sudo nmcli connection up eth0
nmcli コマンドで設定変更し、設定後は nmcli connection up で適用します。
静的IP設定後の注意点:
- 固定IP設定後に SSH が固まることがあるため、Hyper-V のコンソールで
ip aを確認するのが推奨 - WinSCP などクライアントの接続先も固定IPに更新が必要
- Docker イメージビルド時に
apt-getが名前解決できない場合は、DNS 設定が原因の可能性あり -
docker compose build/upは固定IPを当てる前(DHCPのまま)に実行するか、到達可能な社内DNSを設定してから再実行
Docker のインストール
# Rocky Linux での Docker インストール
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl start docker
sudo systemctl enable docker
Rocky Linux は RHEL 互換なので、CentOS のリポジトリを使用します。
クライアントからの名前解決設定
Windows hosts ファイルでの設定(非Hyper-V環境用)
Windows の hosts ファイル(C:\Windows\System32\drivers\etc\hosts)を編集すると、その PC からのみ固定 URL でアクセス可能になります。
重要な制約:
- hosts ファイルはローカル端末のみで有効
- 別の端末からは同じホスト名でアクセス不可
- 複数端末からアクセスする場合は、社内 DNS サーバーに A レコードを登録するか、IP アドレス直接でアクセス
- DNS キャッシュが残る場合は
ipconfig /flushdnsやブラウザ再起動、それでも反映しない場合は OS 再起動で確認
クライアント設定不要で名前解決する方法(推奨)
クライアント側で hosts ファイルや特別な設定を一切行わずに、URL でアクセスさせる唯一の方法は DNS サーバーに A レコードを登録 することです。
例: 社内 DNS サーバーに以下のレコードを登録
karte-dev.local → 10.20.1.60
mold-dev.local → 10.20.1.50
この方法のメリット:
- クライアント側は DHCP で IP を取得するだけ(設定不要)
- クライアント側の hosts ファイル変更不要
- 全てのクライアントから
http://karte-dev.localなどの URL で即座にアクセス可能 - 管理者が DNS サーバー側で一元管理できる
※ DNS サーバーへのアクセス権限がない場合は、ネットワーク管理者に依頼してください。
リポジトリの分離戦略
開発環境と本番環境でリポジトリを分離しました。
開発環境: アプリケーションリポジトリ内
myapp-app/
├── src/
├── container/
│ ├── docker-compose.yml
│ ├── .env.example
│ ├── apache/
│ ├── payara/
│ └── README.md
└── README.md
理由:
- 開発環境も同じ Docker 構成で統一
- アプリケーションコードと環境定義を一元管理
- ローカルでの検証が容易
本番環境: 専用インフラリポジトリ
myapp-infra/
├── docker-compose.yml
├── .env.example
├── cloudformation/
│ └── infrastructure.yaml
├── scripts/
│ ├── setup.sh
│ ├── backup.sh
│ └── restore.sh
├── apache/
├── payara/
└── README.md
理由:
- 機密情報(パスワード、証明書など)の管理を分離
- インフラチームと開発チームの責任分界を明確化
- 本番環境の変更履歴を独立して管理
苦労した点と解決策
1. MySQL 8.0 の認証方式変更
問題:
MySQL 8.0 ではデフォルトの認証プラグインが caching_sha2_password に変更され、既存アプリケーションが接続できない。
解決策:
CREATE USER 'myapp_user'@'%'
IDENTIFIED WITH mysql_native_password BY 'password';
または、my.cnf で設定:
[mysqld]
default-authentication-plugin=mysql_native_password
2. Payara の起動時間
問題:
Payara コンテナの起動に時間がかかり、docker-compose up で他のサービスより遅れる。
解決策:
ヘルスチェックを実装:
payara:
healthcheck:
test: ["CMD", "asadmin", "list-applications"]
interval: 10s
timeout: 5s
retries: 10
3. Windows Server での Docker 構築
問題:
Windows Server に Docker を直接インストールする方法もあったが、Linux コンテナを動かすには複雑な設定が必要。
解決策:
Hyper-V 上に Rocky Linux を構築し、Linux 環境で Docker を動かす方針に変更。結果的に AWS 環境と同じ構成を維持できた。
4. domain.xml の複雑さ
問題:
GlassFish / Payara の domain.xml は巨大で複雑。環境ごとに手動で編集するのは現実的でない。
解決策:
- 環境固有の設定は環境変数で外部化
-
domain.xmlは最小限の変更で共通化 - データベース接続情報など動的な設定は起動時にスクリプトで注入
# 起動時に設定を注入
asadmin set resources.jdbc-connection-pool.mysql-pool.property.URL=jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
まとめ
マルチ環境対応 Docker 基盤の構築について、実践的な内容を紹介しました。
重要なポイント:
- Docker Compose で3層構成を統合
- フォルダ構成を標準化して運用スクリプトを共通化
- CloudFormation でインフラをコード化
- Windows Server でも Hyper-V 上で同じ Docker 構成を実現
実現できたこと:
- 開発、AWS、オンプレミスで同一構成
- 環境構築時間を 95% 削減
- 属人化を排除し、誰でも環境構築可能