1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マルチ環境対応Docker基盤の構築実践

Posted at

はじめに

レガシーシステムの 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

標準化のメリット

  1. 運用スクリプトの共通化

    • パスがハードコードされていても問題なし
    • 環境ごとのスクリプト修正が不要
  2. 権限管理の簡素化

    • /opt/myapp 配下のみ権限設定
    • バックアップ範囲が明確
  3. トラブルシューティングの効率化

    • ログの場所が統一
    • 環境差異による混乱を回避

運用スクリプトの整備

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 のメリット

  1. 再現性の確保

    • 同じ構成を何度でも再構築可能
    • 手動設定ミスを排除
  2. バージョン管理

    • Git で変更履歴を管理
    • レビューとロールバックが可能
  3. ドキュメントとしての役割

    • インフラ構成が自己文書化
    • 新規メンバーの理解が容易

初期セットアップスクリプト

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 のインストール手順

  1. ISO のダウンロード

    • Rocky Linux 9 の ISO をダウンロード
  2. 仮想マシンの作成

    • 世代: 第2世代
    • メモリ: 8GB
    • ネットワーク: 外部仮想スイッチ
    • ディスク: 100GB
  3. セキュアブートの無効化(重要)

    仮想マシン設定 → セキュリティ → セキュアブートを無効化
    

    Rocky Linux は第2世代の仮想マシンでセキュアブートをサポートしていないため、無効化が必須です。

  4. 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% 削減
  • 属人化を排除し、誰でも環境構築可能
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?