MySQL 9.x バックアップ|Docker × AlmaLinux/Ubuntuでの認証問題と解決策

MySQL 9.x バックアップ完全ガイド – Docker環境での認証問題と解決策(AlmaLinux/Ubuntu対応)

WordPressのバックアップ自動化は運用の要です。しかし、MySQL 9.1から9.4へのアップデートにより、従来のバックアップ手法が突然機能しなくなるという深刻な問題が発生しています。本記事では、RHEL系(AlmaLinux)とDebian系(Ubuntu)の両環境で検証した結果を基に、この問題の根本原因と実践的な解決策を詳しく解説します。

環境構成と前提条件

本記事では、以下の2つの環境で検証を行いました:

AlmaLinux環境(RHEL系)

OS: AlmaLinux 9.x(RHEL 9互換)
アーキテクチャ: ARM64/v8
Docker: 24.x以降
Docker Compose: v2.20以降
WordPress: 6.x(最新版)
MySQL: 9.1〜9.4

Ubuntu環境(Debian系)

OS: Ubuntu 22.04/24.04 LTS
アーキテクチャ: ARM64/v8およびx86_64
Docker: 24.x以降
Docker Compose: v2.20以降
WordPress: 6.x(最新版)
MySQL: 9.1〜9.4

プロジェクト構成

wordpress-docker/
├── docker-compose.yml     # コンテナ構成定義
├── Dockerfile             # WordPressカスタマイズ
├── entrypoint.sh         # 初期化スクリプト
├── setup.sh              # 環境セットアップ
├── .env                  # 環境変数
├── php.ini               # PHP設定
├── backup/               # バックアップ保存先
└── scripts/              # 自動化スクリプト

MySQL 9.xで何が変わったのか

バージョン別の主要変更点

MySQL 9.0(2024年7月リリース)

  • mysql_native_password認証プラグインの完全削除
  • VECTOR型のサポート追加(AI/ML用途)
  • 認証セキュリティの大幅強化

MySQL 9.1(2024年10月リリース)

  • CREATE VIEW IF NOT EXISTS構文の追加
  • 認証メカニズムの更なる強化
  • ARM64最適化の改善

MySQL 9.2〜9.3(2024年後半〜2025年前半)

  • パフォーマンス改善
  • セキュリティ機能の拡張
  • Docker環境での動作改善

MySQL 9.4(2025年7月リリース)

  • GCC 11以降が必須に
  • CMake 4のサポート
  • RHEL7/CentOS7のARM版サポート終了

認証プラグインの変遷

-- MySQL 5.7以前(デフォルト)
mysql_native_password

-- MySQL 8.0〜8.3
mysql_native_password(非推奨だが利用可能)
caching_sha2_password(デフォルト)

-- MySQL 8.4
mysql_native_password(デフォルト無効、オプションで有効化可能)
caching_sha2_password(デフォルト)

-- MySQL 9.0以降
mysql_native_password(完全削除)
caching_sha2_password(唯一の選択肢)

問題の発生と症状

初期症状:サイレントな失敗

ある日のシステムアップデート後、以下の問題が発生しました:

  1. バックアップの無言の失敗
    • cronジョブは正常に実行されているように見える
    • しかしバックアップファイルが生成されない
    • エラーログにも記録されない
  2. ヘルスチェックエラー template parsing error: template: :1:8: executing "" at <.State.Health.Status>: map has no entry for key "Health"
  3. 認証エラー mysqldump: Got error: 1045: Access denied for user 'root'@'localhost' (using password: YES) when trying to connect

問題の深刻度

この問題の影響は甚大です:

  • ✗ 日次バックアップが完全に停止
  • ✗ 災害復旧計画(DRP)が機能不全
  • ✗ データ損失リスクの増大
  • ✗ コンプライアンス要件の違反可能性

OS別の違いと注意点

検証の結果、OSによって以下の違いが確認されました:

AlmaLinux(RHEL系)での特徴

ユーザーとグループ管理

# AlmaLinuxでのMySQLユーザー確認
$ docker exec wordpress-db id mysql
uid=999(mysql) gid=999(mysql) groups=999(mysql)

# ホストでの権限設定
$ sudo chown 999:999 ./db_data

SELinuxの影響

# SELinuxコンテキストの確認
$ ls -Z ./db_data
system_u:object_r:container_file_t:s0 ./db_data

# 必要に応じてコンテキストを設定
$ sudo chcon -Rt svirt_sandbox_file_t ./db_data

ファイアウォール設定

# firewalldでポート開放
$ sudo firewall-cmd --permanent --add-port=3306/tcp
$ sudo firewall-cmd --reload

Ubuntu(Debian系)での特徴

ユーザーとグループ管理

# UbuntuでのMySQLユーザー確認
$ docker exec wordpress-db id mysql
uid=999(mysql) gid=999(mysql) groups=999(mysql)

# www-dataユーザーの確認(WordPressコンテナ)
$ docker exec wordpress id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)

AppArmorの影響

# AppArmorステータス確認
$ sudo aa-status | grep docker

# 必要に応じてプロファイルを調整
$ sudo aa-complain /usr/bin/docker

UFW(Uncomplicated Firewall)設定

# ufwでポート開放
$ sudo ufw allow from any to any port 3306 proto tcp
$ sudo ufw reload

Dockerの挙動の違い

興味深いことに、Dockerコンテナ内の挙動はOSに関わらずほぼ同一です:

# docker-compose.yml(両OS共通)
version: '3.9'

services:
  db:
    image: mysql:9.4
    platform: linux/arm64/v8  # ARMの場合
    # platform: linux/amd64    # x86_64の場合
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
    volumes:
      - db_data:/var/lib/mysql

主な違いはホストOSのファイル権限管理にあります。

根本的な解決策

解決策1:認証バイパスによる緊急バックアップ

この方法は、MySQL 9.xの厳格な認証を一時的に回避します:

ステップ1:準備

# 一時ディレクトリの作成
mkdir -p temp_mysql_data temp_run_mysqld

# データベースファイルのコピー(権限保持)
sudo cp -rp db_data/* temp_mysql_data/

# 権限の確認と設定
sudo chown -R 999:999 temp_mysql_data/

ステップ2:バックアップ実行

#!/bin/bash

# MySQL 9.x対応バックアップスクリプト
docker run --rm \
  --platform linux/$(uname -m) \
  -v $(pwd)/temp_mysql_data:/var/lib/mysql:ro \
  -v $(pwd)/temp_run_mysqld:/var/run/mysqld \
  -v $(pwd)/backup:/backup \
  mysql:9.4 \
  bash -c '
    # MySQLを認証スキップモードで起動
    docker-entrypoint.sh mysqld \
      --skip-grant-tables \
      --skip-networking \
      --datadir=/var/lib/mysql &
    
    # 起動待機(最大60秒)
    echo "MySQLの起動を待機中..."
    for i in {1..60}; do
      if mysqladmin ping --silent 2>/dev/null; then
        echo "MySQL起動確認"
        break
      fi
      sleep 1
    done
    
    # バックアップ実行
    echo "バックアップ開始: $(date)"
    mysqldump \
      --no-tablespaces \
      --column-statistics=0 \
      --skip-add-drop-table \
      --single-transaction \
      --quick \
      --lock-tables=false \
      --all-databases > /backup/full_backup_$(date +%Y%m%d_%H%M%S).sql
    
    if [ $? -eq 0 ]; then
      echo "バックアップ成功"
    else
      echo "バックアップ失敗"
      exit 1
    fi
    
    # クリーンアップ
    mysqladmin shutdown 2>/dev/null
    sleep 5
  '

ステップ3:後処理

# 一時ディレクトリの削除
sudo rm -rf temp_mysql_data temp_run_mysqld

# バックアップファイルの確認
ls -lh backup/*.sql

解決策2:専用バックアップコンテナの構築

より洗練された永続的な解決策です:

# docker-compose.yml
version: '3.9'

services:
  db:
    image: mysql:9.4
    container_name: wordpress-db
    platform: linux/arm64/v8
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      # MySQL 9.x固有の設定
      MYSQL_ROOT_HOST: '%'
    volumes:
      - db_data:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "--protocol=TCP"]
      interval: 30s
      timeout: 15s
      retries: 5
      start_period: 90s  # MySQL 9.xは起動が遅い
    networks:
      - wp-network

  wordpress:
    depends_on:
      db:
        condition: service_healthy
    image: wordpress:latest
    container_name: wordpress
    platform: linux/arm64/v8
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
    volumes:
      - wordpress_data:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    networks:
      - wp-network

  backup:
    image: mysql:9.4
    container_name: wordpress-backup
    platform: linux/arm64/v8
    depends_on:
      db:
        condition: service_healthy
    environment:
      MYSQL_HOST: db
      MYSQL_PORT: 3306
      MYSQL_USER: root
      MYSQL_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      TZ: Asia/Tokyo
      BACKUP_SCHEDULE: "0 2 * * *"  # 毎日午前2時
      BACKUP_RETENTION_DAYS: 30     # 30日間保持
    volumes:
      - ./backup:/backup
      - ./scripts:/scripts:ro
    entrypoint: ["/scripts/backup-entrypoint.sh"]
    networks:
      - wp-network

volumes:
  db_data:
    driver: local
  wordpress_data:
    driver: local

networks:
  wp-network:
    driver: bridge

バックアップエントリーポイントスクリプト

#!/bin/bash
# scripts/backup-entrypoint.sh

set -e

# カラー出力の定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 環境変数の検証
if [ -z "$MYSQL_HOST" ] || [ -z "$MYSQL_PASSWORD" ]; then
    log_error "必須の環境変数が設定されていません"
    exit 1
fi

# cronのインストール
apt-get update -qq && apt-get install -y -qq cron > /dev/null 2>&1

# バックアップスクリプトの作成
cat > /backup.sh << 'SCRIPT'
#!/bin/bash

source /etc/environment

# ログ関数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# MySQLの接続確認
wait_for_mysql() {
    local max_attempts=30
    local attempt=0
    
    while [ $attempt -lt $max_attempts ]; do
        if mysqladmin ping -h "$MYSQL_HOST" --port="$MYSQL_PORT" \
           -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" --silent 2>/dev/null; then
            return 0
        fi
        attempt=$((attempt + 1))
        sleep 2
    done
    return 1
}

# メインバックアップ処理
perform_backup() {
    local backup_file="/backup/${MYSQL_DATABASE}_$(date +%Y%m%d_%H%M%S).sql"
    local temp_file="${backup_file}.tmp"
    
    log "バックアップ開始: $MYSQL_DATABASE"
    
    if ! wait_for_mysql; then
        log "ERROR: MySQLへの接続に失敗しました"
        return 1
    fi
    
    # MySQL 9.x対応のバックアップオプション
    if MYSQL_PWD="$MYSQL_PASSWORD" mysqldump \
        -h "$MYSQL_HOST" \
        --port="$MYSQL_PORT" \
        -u "$MYSQL_USER" \
        --no-tablespaces \
        --column-statistics=0 \
        --single-transaction \
        --quick \
        --lock-tables=false \
        --routines \
        --triggers \
        --events \
        "$MYSQL_DATABASE" > "$temp_file" 2>/dev/null; then
        
        # バックアップの検証
        if [ -s "$temp_file" ]; then
            mv "$temp_file" "$backup_file"
            
            # 圧縮
            gzip "$backup_file"
            log "バックアップ成功: ${backup_file}.gz ($(du -h ${backup_file}.gz | cut -f1))"
            
            # 古いバックアップの削除
            find /backup -name "*.sql.gz" -mtime +${BACKUP_RETENTION_DAYS} -delete
            log "古いバックアップを削除しました(${BACKUP_RETENTION_DAYS}日以上)"
            
            return 0
        else
            log "ERROR: バックアップファイルが空です"
            rm -f "$temp_file"
            return 1
        fi
    else
        log "ERROR: mysqldumpが失敗しました"
        rm -f "$temp_file"
        return 1
    fi
}

# 実行
perform_backup
exit $?
SCRIPT

chmod +x /backup.sh

# 環境変数をcronで利用可能にする
printenv | grep -E '^MYSQL_|^BACKUP_|^TZ=' > /etc/environment

# cronジョブの設定
echo "$BACKUP_SCHEDULE /backup.sh >> /var/log/backup.log 2>&1" | crontab -

# 初回バックアップの実行
log_info "初回バックアップを実行中..."
/backup.sh

# cronデーモンの起動とログ監視
log_info "バックアップスケジューラーを起動しました"
log_info "スケジュール: $BACKUP_SCHEDULE"
log_info "保持期間: ${BACKUP_RETENTION_DAYS}日"

cron && tail -f /var/log/backup.log

自動バックアップシステムの構築

監視スクリプト

#!/bin/bash
# scripts/monitor-backup.sh

BACKUP_DIR="./backup"
ALERT_EMAIL="admin@example.com"
MAX_AGE_HOURS=26
MIN_SIZE_MB=1

# Slack通知関数(オプション)
send_slack_alert() {
    local message=$1
    local webhook_url="YOUR_SLACK_WEBHOOK_URL"
    
    if [ ! -z "$webhook_url" ]; then
        curl -X POST -H 'Content-type: application/json' \
            --data "{\"text\":\"⚠️ バックアップ警告: $message\"}" \
            "$webhook_url" 2>/dev/null
    fi
}

# メール通知関数
send_email_alert() {
    local message=$1
    echo "$message" | mail -s "MySQL Backup Alert" "$ALERT_EMAIL"
}

# 最新のバックアップを確認
check_latest_backup() {
    local latest_backup=$(ls -t $BACKUP_DIR/*.sql.gz 2>/dev/null | head -1)
    
    if [ -z "$latest_backup" ]; then
        echo "❌ エラー: バックアップファイルが見つかりません"
        send_slack_alert "バックアップファイルが見つかりません"
        return 1
    fi
    
    # バックアップの経過時間を確認
    local backup_age_seconds=$(($(date +%s) - $(stat -c %Y "$latest_backup" 2>/dev/null || stat -f %m "$latest_backup")))
    local backup_age_hours=$((backup_age_seconds / 3600))
    
    if [ $backup_age_hours -gt $MAX_AGE_HOURS ]; then
        echo "⚠️  警告: 最新のバックアップは${backup_age_hours}時間前です(閾値: ${MAX_AGE_HOURS}時間)"
        send_slack_alert "バックアップが${backup_age_hours}時間前です"
    fi
    
    # バックアップサイズを確認
    local backup_size_bytes=$(stat -c %s "$latest_backup" 2>/dev/null || stat -f %z "$latest_backup")
    local backup_size_mb=$((backup_size_bytes / 1024 / 1024))
    
    if [ $backup_size_mb -lt $MIN_SIZE_MB ]; then
        echo "⚠️  警告: バックアップサイズが小さすぎます(${backup_size_mb}MB < ${MIN_SIZE_MB}MB)"
        send_slack_alert "バックアップサイズが異常に小さいです(${backup_size_mb}MB)"
    fi
    
    echo "✅ バックアップ確認完了:"
    echo "   ファイル: $(basename $latest_backup)"
    echo "   経過時間: ${backup_age_hours}時間"
    echo "   サイズ: ${backup_size_mb}MB"
    
    return 0
}

# ヘルスチェック
check_mysql_health() {
    if docker exec wordpress-db mysqladmin ping -h localhost --protocol=TCP >/dev/null 2>&1; then
        echo "✅ MySQL: 正常動作中"
    else
        echo "❌ MySQL: 応答なし"
        send_slack_alert "MySQLが応答していません"
        return 1
    fi
}

# メイン処理
main() {
    echo "=== バックアップ監視レポート $(date '+%Y-%m-%d %H:%M:%S') ==="
    
    check_mysql_health
    check_latest_backup
    
    echo "=== 監視完了 ==="
}

main

復元手順

通常の復元

#!/bin/bash
# scripts/restore-backup.sh

BACKUP_FILE=$1

if [ -z "$BACKUP_FILE" ]; then
    echo "使用方法: $0 <バックアップファイル>"
    echo "例: $0 backup/wordpress_20250825_020000.sql.gz"
    exit 1
fi

if [ ! -f "$BACKUP_FILE" ]; then
    echo "エラー: ファイルが見つかりません: $BACKUP_FILE"
    exit 1
fi

echo "復元を開始します: $BACKUP_FILE"

# WordPressを停止
echo "WordPressコンテナを停止中..."
docker compose stop wordpress

# バックアップファイルを展開
if [[ "$BACKUP_FILE" == *.gz ]]; then
    echo "バックアップファイルを展開中..."
    gunzip -c "$BACKUP_FILE" > /tmp/restore.sql
    RESTORE_FILE="/tmp/restore.sql"
else
    RESTORE_FILE="$BACKUP_FILE"
fi

# 復元実行
echo "データベースを復元中..."
docker exec -i wordpress-db mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DATABASE} < "$RESTORE_FILE"

if [ $? -eq 0 ]; then
    echo "✅ 復元成功"
    
    # 復元の確認
    echo "復元されたテーブルを確認中..."
    docker exec wordpress-db mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "
        USE ${MYSQL_DATABASE};
        SHOW TABLES;
        SELECT COUNT(*) as '投稿数' FROM wp_posts;
        SELECT COUNT(*) as 'ユーザー数' FROM wp_users;
    "
    
    # WordPressを再起動
    echo "WordPressを再起動中..."
    docker compose start wordpress
    
    echo "✅ 復元完了!"
else
    echo "❌ 復元失敗"
    exit 1
fi

# 一時ファイルの削除
[ -f "/tmp/restore.sql" ] && rm /tmp/restore.sql

緊急復元(認証問題がある場合)

#!/bin/bash
# scripts/emergency-restore.sh

BACKUP_FILE=$1

# 全コンテナを停止
docker compose down

# 現在のデータをバックアップ
echo "現在のデータをバックアップ中..."
sudo mv db_data db_data_backup_$(date +%Y%m%d_%H%M%S)

# 新しいデータディレクトリを作成
mkdir -p db_data

# 一時的なMySQLコンテナを起動(認証スキップモード)
docker run -d \
  --name mysql-emergency \
  --platform linux/arm64/v8 \
  -v $(pwd)/db_data:/var/lib/mysql \
  -v $(pwd)/$BACKUP_FILE:/restore.sql:ro \
  -e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
  mysql:9.4 \
  --skip-grant-tables --skip-networking

sleep 10

# データベースの初期化と復元
docker exec mysql-emergency bash -c "
    # データベース作成
    mysql -e 'CREATE DATABASE IF NOT EXISTS ${MYSQL_DATABASE};'
    
    # 復元
    mysql ${MYSQL_DATABASE} < /restore.sql
    
    # 権限の再設定
    mysql -e \"
        FLUSH PRIVILEGES;
        ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '${MYSQL_ROOT_PASSWORD}';
        ALTER USER 'root'@'%' IDENTIFIED WITH caching_sha2_password BY '${MYSQL_ROOT_PASSWORD}';
        CREATE USER IF NOT EXISTS '${MYSQL_USER}'@'%' IDENTIFIED WITH caching_sha2_password BY '${MYSQL_PASSWORD}';
        GRANT ALL PRIVILEGES ON ${MYSQL_DATABASE}.* TO '${MYSQL_USER}'@'%';
        FLUSH PRIVILEGES;
    \"
"

# 緊急コンテナを停止・削除
docker stop mysql-emergency
docker rm mysql-emergency

# 通常のサービスを起動
docker compose up -d

echo "✅ 緊急復元完了"

トラブルシューティング実践

問題1:.htaccessファイルの権限問題

症状

  • トップページは表示される
  • 個別記事が404エラー

AlmaLinuxでの解決

# .htaccessファイルの作成
cat > wordpress_data/.htaccess << 'EOF'
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
EOF

# 権限設定(AlmaLinux)
sudo chown 33:33 wordpress_data/.htaccess
sudo chmod 644 wordpress_data/.htaccess

# SELinuxコンテキスト設定
sudo chcon -t httpd_sys_content_t wordpress_data/.htaccess

Ubuntuでの解決

# 権限設定(Ubuntu)
sudo chown www-data:www-data wordpress_data/.htaccess
# または数値指定
sudo chown 33:33 wordpress_data/.htaccess
sudo chmod 644 wordpress_data/.htaccess

問題2:データベース移行時の文字コード問題

# バックアップ時に文字コードを明示
docker exec wordpress-db mysqldump \
  --default-character-set=utf8mb4 \
  --no-tablespaces \
  -u root -p${MYSQL_ROOT_PASSWORD} \
  ${MYSQL_DATABASE} > backup.sql

# 復元時も文字コードを明示
docker exec -i wordpress-db mysql \
  --default-character-set=utf8mb4 \
  -u root -p${MYSQL_ROOT_PASSWORD} \
  ${MYSQL_DATABASE} < backup.sql

問題3:パーマリンク設定の不具合

-- データベースで直接修正
UPDATE wp_options 
SET option_value = 'https://your-domain.com' 
WHERE option_name IN ('siteurl', 'home');

-- パーマリンク構造の確認
SELECT option_value 
FROM wp_options 
WHERE option_name = 'permalink_structure';

問題4:コンテナ間の接続問題

# ネットワークの確認
docker network ls
docker network inspect wordpress-docker_wp-network

# コンテナ間の疎通確認
docker exec wordpress ping -c 3 db
docker exec wordpress nslookup db

# MySQLポートの確認
docker exec wordpress nc -zv db 3306

パフォーマンス最適化

MySQL 9.x向けの最適化設定

# mysql-init/custom.cnf
[mysqld]
# MySQL 9.x推奨設定
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1

# クエリキャッシュ(MySQL 8.0以降は削除)
# query_cache_size = 0
# query_cache_type = 0

# 接続関連
max_connections = 200
max_connect_errors = 1000000
wait_timeout = 28800
interactive_timeout = 28800

# スロークエリログ
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

# バイナリログ(レプリケーション用)
log_bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M

# 文字コード
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

Docker Composeでのリソース制限

services:
  db:
    image: mysql:9.4
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '1.0'
          memory: 1G

セキュリティ強化

1. 環境変数の安全な管理

# .env.example
MYSQL_ROOT_PASSWORD=ChangeThisStrongPassword123!
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=AnotherStrongPassword456!

# 本番環境では.envを暗号化
ansible-vault encrypt .env

2. ネットワークの分離

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 外部アクセス不可

services:
  db:
    networks:
      - backend
  
  wordpress:
    networks:
      - frontend
      - backend

3. 読み取り専用マウント

volumes:
  - ./scripts:/scripts:ro
  - ./mysql-init:/docker-entrypoint-initdb.d:ro

完全自動化スクリプト

マスターセットアップスクリプト

#!/bin/bash
# setup-complete.sh

set -e

# カラー定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# ロゴ表示
show_banner() {
    echo -e "${BLUE}"
    cat << "EOF"
╔══════════════════════════════════════════╗
║   WordPress + MySQL 9.x Setup Script     ║
║          Fully Automated Edition         ║
╚══════════════════════════════════════════╝
EOF
    echo -e "${NC}"
}

# OS検出
detect_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS=$ID
        VER=$VERSION_ID
    else
        echo -e "${RED}OSの検出に失敗しました${NC}"
        exit 1
    fi
    
    echo -e "${GREEN}検出されたOS: $OS $VER${NC}"
}

# 依存関係のインストール
install_dependencies() {
    echo -e "${YELLOW}依存関係をインストール中...${NC}"
    
    case $OS in
        ubuntu|debian)
            sudo apt-get update
            sudo apt-get install -y docker.io docker-compose jq curl wget
            ;;
        almalinux|rocky|centos|rhel)
            sudo dnf install -y docker docker-compose jq curl wget
            ;;
        *)
            echo -e "${RED}サポートされていないOS: $OS${NC}"
            exit 1
            ;;
    esac
    
    # Dockerサービスの起動
    sudo systemctl start docker
    sudo systemctl enable docker
    
    # ユーザーをdockerグループに追加
    sudo usermod -aG docker $USER
    
    echo -e "${GREEN}依存関係のインストール完了${NC}"
}

# プロジェクトディレクトリの作成
create_project_structure() {
    echo -e "${YELLOW}プロジェクト構造を作成中...${NC}"
    
    mkdir -p {backup,scripts,mysql-init}
    
    # .envファイルの生成
    if [ ! -f .env ]; then
        cat > .env << EOF
# MySQL設定
MYSQL_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)

# WordPress設定
WORDPRESS_DB_HOST=db:3306
WORDPRESS_DB_NAME=wordpress
WORDPRESS_DB_USER=wpuser
WORDPRESS_TABLE_PREFIX=wp_

# バックアップ設定
BACKUP_SCHEDULE="0 2 * * *"
BACKUP_RETENTION_DAYS=30
EOF
        echo -e "${GREEN}.envファイルを生成しました${NC}"
    fi
    
    # 権限設定
    chmod 600 .env
}

# Docker Composeファイルの生成
generate_docker_compose() {
    echo -e "${YELLOW}docker-compose.ymlを生成中...${NC}"
    
    # アーキテクチャの検出
    ARCH=$(uname -m)
    if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
        PLATFORM="linux/arm64/v8"
    else
        PLATFORM="linux/amd64"
    fi
    
    cat > docker-compose.yml << EOF
version: '3.9'

services:
  db:
    image: mysql:9.4
    container_name: wordpress-db
    platform: ${PLATFORM}
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: \${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: \${MYSQL_DATABASE}
      MYSQL_USER: \${MYSQL_USER}
      MYSQL_PASSWORD: \${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "--protocol=TCP"]
      interval: 30s
      timeout: 15s
      retries: 5
      start_period: 90s
    networks:
      - wp-network

  wordpress:
    depends_on:
      db:
        condition: service_healthy
    image: wordpress:latest
    container_name: wordpress
    platform: ${PLATFORM}
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: \${WORDPRESS_DB_HOST}
      WORDPRESS_DB_USER: \${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: \${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: \${WORDPRESS_DB_NAME}
      WORDPRESS_TABLE_PREFIX: \${WORDPRESS_TABLE_PREFIX}
    volumes:
      - wordpress_data:/var/www/html
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    networks:
      - wp-network

  backup:
    image: mysql:9.4
    container_name: wordpress-backup
    platform: ${PLATFORM}
    depends_on:
      db:
        condition: service_healthy
    environment:
      MYSQL_HOST: db
      MYSQL_USER: root
      MYSQL_PASSWORD: \${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: \${MYSQL_DATABASE}
      BACKUP_SCHEDULE: \${BACKUP_SCHEDULE}
      BACKUP_RETENTION_DAYS: \${BACKUP_RETENTION_DAYS}
    volumes:
      - ./backup:/backup
      - ./scripts:/scripts:ro
    command: ["/scripts/backup-entrypoint.sh"]
    networks:
      - wp-network

volumes:
  db_data:
  wordpress_data:

networks:
  wp-network:
    driver: bridge
EOF
    
    echo -e "${GREEN}docker-compose.yml生成完了${NC}"
}

# スクリプトの生成
generate_scripts() {
    echo -e "${YELLOW}スクリプトを生成中...${NC}"
    
    # バックアップエントリーポイント
    cat > scripts/backup-entrypoint.sh << 'EOF'
#!/bin/bash
apt-get update && apt-get install -y cron
printenv | grep -E '^MYSQL_|^BACKUP_' > /etc/environment

cat > /backup.sh << 'SCRIPT'
#!/bin/bash
source /etc/environment
MYSQL_PWD="$MYSQL_PASSWORD" mysqldump \
    -h "$MYSQL_HOST" -u "$MYSQL_USER" \
    --no-tablespaces --column-statistics=0 \
    --single-transaction --quick \
    "$MYSQL_DATABASE" | gzip > "/backup/${MYSQL_DATABASE}_$(date +%Y%m%d_%H%M%S).sql.gz"
find /backup -name "*.sql.gz" -mtime +${BACKUP_RETENTION_DAYS} -delete
SCRIPT

chmod +x /backup.sh
echo "$BACKUP_SCHEDULE /backup.sh >> /var/log/backup.log 2>&1" | crontab -
/backup.sh
cron && tail -f /var/log/backup.log
EOF
    
    chmod +x scripts/backup-entrypoint.sh
    
    echo -e "${GREEN}スクリプト生成完了${NC}"
}

# サービスの起動
start_services() {
    echo -e "${YELLOW}サービスを起動中...${NC}"
    
    docker compose up -d
    
    # 起動確認
    echo -e "${YELLOW}サービスの起動を確認中...${NC}"
    sleep 10
    
    if docker compose ps | grep -q "Up"; then
        echo -e "${GREEN}✅ すべてのサービスが正常に起動しました${NC}"
    else
        echo -e "${RED}❌ サービスの起動に失敗しました${NC}"
        docker compose logs
        exit 1
    fi
}

# 設定情報の表示
show_info() {
    source .env
    
    echo -e "${BLUE}"
    echo "╔══════════════════════════════════════════╗"
    echo "║         セットアップ完了!                ║"
    echo "╚══════════════════════════════════════════╝"
    echo -e "${NC}"
    
    echo -e "${GREEN}アクセス情報:${NC}"
    echo "  WordPress URL: http://localhost:8080"
    echo ""
    echo -e "${GREEN}データベース情報:${NC}"
    echo "  ホスト: localhost:3306"
    echo "  データベース名: $MYSQL_DATABASE"
    echo "  ユーザー: $MYSQL_USER"
    echo "  パスワード: .envファイルを参照"
    echo ""
    echo -e "${GREEN}管理コマンド:${NC}"
    echo "  起動: docker compose up -d"
    echo "  停止: docker compose down"
    echo "  ログ: docker compose logs -f"
    echo "  バックアップ: docker exec wordpress-backup /backup.sh"
    echo ""
    echo -e "${YELLOW}※ 初回アクセス時にWordPressの初期設定を行ってください${NC}"
}

# メイン処理
main() {
    show_banner
    detect_os
    install_dependencies
    create_project_structure
    generate_docker_compose
    generate_scripts
    start_services
    show_info
}

# 実行
main

GitHub リポジトリ

完全なコードとドキュメントは以下のリポジトリで公開しています:

リポジトリ: https://github.com/superdoccimo/wpbk

リポジトリの内容

  • ✅ AlmaLinux/Ubuntu両対応のセットアップスクリプト
  • ✅ MySQL 9.1〜9.4対応のバックアップソリューション
  • ✅ 自動復元スクリプト
  • ✅ ヘルスチェック・監視ツール
  • ✅ トラブルシューティングガイド

将来への備え

MySQL 10.xへの準備

MySQL 10.xでは以下の変更が予想されます:

  1. AI/ML機能の本格統合
    • VECTOR型の拡張
    • 機械学習モデルの直接実行
  2. さらなるセキュリティ強化
    • 量子耐性暗号化
    • ゼロトラストアーキテクチャ
  3. クラウドネイティブ機能
    • Kubernetes統合の改善
    • サーバーレス対応

推奨される準備

# 将来を見据えた設定
services:
  db:
    image: mysql:${MYSQL_VERSION:-9.4}  # バージョン変数化
    environment:
      # 将来の機能フラグ
      MYSQL_ENABLE_VECTOR: "true"
      MYSQL_ENABLE_ML: "false"  # 将来有効化

まとめ

MySQL 9.xへのアップデートによるバックアップ問題は、以下の要因によるものでした:

  1. 認証メカニズムの根本的変更
    • mysql_native_passwordの完全削除
    • caching_sha2_passwordへの強制移行
  2. セキュリティ強化
    • より厳格な権限管理
    • デフォルトでの制限的な設定
  3. プラットフォーム依存性
    • OSによる権限管理の違い
    • SELinux/AppArmorの影響

解決のポイント

  • 認証バイパスによる緊急バックアップ
  • 専用バックアップコンテナの構築
  • OS別の権限管理
  • 自動化と監視の実装

これらの対策により、MySQL 9.xでも安定したバックアップ運用が可能になります。

参考リンク


最終更新: 2025年8月25日
動作確認環境: AlmaLinux 9, Ubuntu 22.04/24.04, MySQL 9.1-9.4, Docker 24.x

タイトルとURLをコピーしました