本ページにはプロモーションが含まれます
Linux

【Docker】はじめてのDocker

この記事は約16分で読めます。

はじめに

案件はオンプレかAWSだったりの現場作業なので、触る機会があまりありませんでした。
個人開発でWordPressのローカル環境が必要になって、VMの既存イメージから立ち上げ手間かかるし、いろいろライブラリやアプリケーション入れたくなっちゃうし、サクッとできる方法ないかなーと、ふと今までご縁のなかったDockerを使うことにしました。

とりあえずこれだけ覚えておけば現場でもOK

インストールは割愛。
Dockerインストールマニュフェスト
docker-compose.yml
でインストールしたいコンテナの情報を記載
以下WordPress立ち上げてみる。自分なりに何か調べてコメント記載した。

# Docker Composeのバージョン指定
version: '3'

# サービス(コンテナ)の定義
services:
  
  # MySQLデータベースコンテナ
  db:
    # 使用するDockerイメージ(MySQL 8.0)
    image: mysql:8.0
    
    # ボリューム(データ永続化)
    volumes:
      # 名前付きボリューム db_data を /var/lib/mysql にマウント
      # MySQLのデータベースファイルが保存される場所
      # downしてもデータは残る
      - db_data:/var/lib/mysql
    
    # コンテナが停止したら自動で再起動
    restart: always
    
    # 環境変数(MySQLの設定)
    environment:
      # MySQLのrootパスワード
      MYSQL_ROOT_PASSWORD: rootpassword
      # 作成するデータベース名
      MYSQL_DATABASE: wordpress
      # WordPressが使うMySQLユーザー名
      MYSQL_USER: wordpress
      # WordPressが使うMySQLパスワード
      MYSQL_PASSWORD: wordpress

  # WordPressコンテナ
  wordpress:
    # dbコンテナが起動してから起動する
    depends_on:
      - db
    
    # 使用するDockerイメージ(WordPress最新版)
    image: wordpress:latest
    
    # ポートマッピング(ホスト:コンテナ)
    ports:
      # ホストの8080番ポートをコンテナの80番ポートに転送
      # http://192.168.184.129:8080 でアクセス可能
      - "8080:80"
    
    # コンテナが停止したら自動で再起動
    restart: always
    
    # 環境変数(WordPress→MySQL接続設定)
    environment:
      # MySQLサーバーのアドレス(サービス名:ポート)
      WORDPRESS_DB_HOST: db:3306
      # MySQLユーザー名(上のMYSQL_USERと同じ)
      WORDPRESS_DB_USER: wordpress
      # MySQLパスワード(上のMYSQL_PASSWORDと同じ)
      WORDPRESS_DB_PASSWORD: wordpress
      # 使用するデータベース名(上のMYSQL_DATABASEと同じ)
      WORDPRESS_DB_NAME: wordpress
    
    # ボリューム(ファイル共有)
    volumes:
      # ホストの ./wp-content を コンテナの /var/www/html/wp-content にマウント
      # ホスト: /home/kohaku/wordpress/wp-content/
      # コンテナ: /var/www/html/wp-content/
      # テーマ、プラグイン、アップロードファイルがここに保存される
      # 双方向リアルタイム同期、downしても残る
      - ./wp-content:/var/www/html/wp-content

# 名前付きボリュームの定義
volumes:
  # MySQLデータ用の永続ボリューム
  # Docker管理下の領域に保存される
  # down しても残る、down -v で削除される
  db_data:

2・
基本操作(最重要)

起動(バックグラウンド)

docker-compose up -d

停止

docker-compose stop

停止+コンテナ削除(データは消えない)

docker-compose down

完全削除(コンテナ+ネットワーク+イメージキャッシュ)

※永続化していないデータは消えるので注意

docker-compose down --rmi all --volumes --remove-orphans

コンテナ状態確認

実行中コンテナ一覧

docker ps

全コンテナ一覧(停止中含む)

docker ps -a

WordPressコンテナで作業したいとき

シェルに入る(bash)

docker-compose exec wordpress bash

WP-CLI を使う例

docker-compose exec wordpress wp plugin list
docker-compose exec wordpress ls -la

docker-compose exeはリモートコマンド実行みたいな認識でよい


MySQLコンテナに入る

MySQL の中に入る(root)

docker-compose exec db mysql -u root -p

データベース一覧

show databases;

ログを見る

WordPressのログ(Apache)

docker-compose logs -f wordpress

MySQL のログ

docker-compose logs -f db

コンテナを再構築したいとき

イメージを再ビルド

docker-compose build

再起動(停止 → 起動)

docker-compose restart

不要な Docker 資源をまとめて掃除

使っていないコンテナ・ネットワーク・イメージを削除

docker system prune

さらに volume も削除(危険)

docker system prune -a --volumes

バックアップ関連

ホスト側の WordPress ディレクトリを zip化

(例)

zip -r wp-backup.zip ./wp

MySQL ダンプ

docker-compose exec db mysqldump -u root -p wordpress > backup.sql

「よくある誤解 → 正しい理解」まとめ

❌ 誤解①:コンテナ内で変更すれば全部残る

✔ 正しい理解

「マウントした場所だけ残り、その他はコンテナ削除(down)で全部消える」。


❌ 誤解②:コンテナは“サーバー本体”だと思っていた

✔ 正しい理解

コンテナは“使い捨ての実行箱”。本体はイメージ(テンプレ)。


❌ 誤解③:docker-compose down は“停止するだけ”だと思っていた

✔ 正しい理解

down は停止+破壊(コンテナの OS 層を消す)まで行う。


❌ 誤解④:コンテナは仮想マシン(VM)と同じ

✔ 正しい理解

コンテナは OS を丸ごと仮想化しない。プロセスを隔離して動かしているだけ。軽量で高速。

安定版

version: "3.9"

services:
  asset-build:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - .:/app
    command: sh /app/build-assets.sh

  db:
    image: mysql:8.0
    container_name: wp-db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    volumes:
      - db_data:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d
    ports:
      - "3300:3306"
    networks:
      - wordpress-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpassword"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    command: 
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --max_allowed_packet=256M
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  wordpress:
    depends_on:
      db:
        condition: service_healthy
          #image: wordpress:latest
    build: .
    container_name: wp-main
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_TABLE_PREFIX: wp_
      WORDPRESS_DEBUG: true
      WORDPRESS_CONFIG_EXTRA: |
        define('WP_MEMORY_LIMIT', '256M');
        define('WP_MAX_MEMORY_LIMIT', '512M');
        define('DISABLE_WP_CRON', false);
        define('AUTOMATIC_UPDATER_DISABLED', false);
    volumes:
      - ./wp:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    ports:
      - "8080:80"
    networks:
      - wordpress-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/wp-admin/install.php"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
          
  wpcli:
    image: wordpress:cli
    container_name: wp-cli
    depends_on:
      - wordpress
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
      MYSQL_SSL_MODE: DISABLED
    volumes:
      - ./wp:/var/www/html
    working_dir: /var/www/html
    networks:
      - wordpress-network

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    container_name: wp-pma
    restart: always
    depends_on:
      db:
        condition: service_healthy
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
      PMA_USER: root
      PMA_PASSWORD: rootpassword
      UPLOAD_LIMIT: 256M
      MEMORY_LIMIT: 512M
      MAX_EXECUTION_TIME: 600
    ports:
      - "8081:80"
    networks:
      - wordpress-network
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  # バックアップサービス(オプション)
  backup:
    image: fradelg/mysql-cron-backup:latest
    container_name: wp-backup
    restart: always
    depends_on:
      db:
        condition: service_healthy
    environment:
      MYSQL_HOST: db
      MYSQL_USER: root
      MYSQL_PASS: rootpassword
      MYSQL_DATABASE: wordpress
      CRON_TIME: "0 2 * * *"
      MAX_BACKUPS: 7
      INIT_BACKUP: 0
      GZIP_LEVEL: 9
    volumes:
      - ./backups:/backup
    networks:
      - wordpress-network

volumes:
  db_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: ./mysql-data

networks:
  wordpress-network:
    driver: bridge

phpunitが使えるよう #image: wordpress:latest コメントアウト。
wordpress:latest は本番実行用イメージで、phpunit や composer が入っていない。テスト用途(WordPress公式テスト構成)を想定していない。そのため phpunit は phar 直実行か、テスト用に拡張したイメージが必要。

phpunitテストするときは以下のようにやるとテスト可能。xxxxxxxxは作っているプラグイン名。

 docker compose run --rm wpcli sh -c '
 curl -Ls https://phar.phpunit.de/phpunit-9.phar -o /tmp/phpunit &&
 cd wp-content/plugins/xxxxxxxx &&
 php /tmp/phpunit --do-not-cache-result -c phpunit.xml.dist
 '

wp-conten/plugins/xxxxxxxx/phpunit.xml.dist

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true" verbose="true">
  <testsuites>
    <testsuite name="Plugin Tests">
      <directory>tests</directory>
    </testsuite>
  </testsuites>
</phpunit>

wp-conten/plugins/xxxxxxxx/tests/bootstrap.php

<?php
// --------------------------------------
// テスト時のみ rate limit 無効化  <- 作っているプラグインのオプション設定とかここに記載
// --------------------------------------
define( 'AIGB_DISABLE_RATE_LIMIT', true );

// --------------------------------------
// WordPress 本体を読み込む
// (Docker内の実体)
// --------------------------------------
require_once dirname( __DIR__, 4 ) . '/wp-load.php';

// プラグイン本体を明示ロード
require_once dirname( __DIR__ ) . '/xxxxxxxx.php';

XXXXXXXX_Controller_Test.php

<?php
/**
 * XXXXXXXX Controller Permission Tests
 *
 * @package XXXXXXXX 
 */

use PHPUnit\Framework\TestCase;

class XXXXXXXX_Controller_Test extends TestCase {

    protected function setUp(): void {
        parent::setUp();
        // 初期化書いていく

    }

    protected function tearDown(): void {
        // 終了処理書いていく
        parent::tearDown();
    }

    // 以降テストケース書いていく

ファイル名、クラス名ともに末尾 _TEST でした。TestCaseを継承。別の方法で、定義テストしようとしたら動かなかったんよね。

build-assets.shは、js&cssのminify実行用

#!/bin/sh
set -e

echo "== install tools =="
npm install -g postcss postcss-cli cssnano terser

CSS_DIR="wp/wp-content/plugins/xxxxxxxx/public/css"
JS_DIR="wp/wp-content/plugins/xxxxxxxx/public/js"

echo "== CSS minify =="
for f in "$CSS_DIR"/*.css; do
  case "$f" in
    *.min.css) continue ;;
  esac
  out="$(echo "$f" | sed 's/\.css$/.min.css/')"
  echo "  $f -> $out"
  postcss "$f" -o "$out" -u cssnano
done

echo "== JS minify =="
for f in "$JS_DIR"/*.js; do
  case "$f" in
    *.min.js) continue ;;
  esac
  out="$(echo "$f" | sed 's/\.js$/.min.js/')"
  echo "  $f -> $out"
  terser "$f" --compress --mangle --output "$out"
done

echo "== done =="

んー、設定面倒くさいけど、構築爆速だから良いね。
mysqladminは使ってないけどポート変えて動くのかとか検証してみた。
テストもできるようになったので、安心安心安心!

おわりに

マウントしたアンマウントしたりは上書きの原因になるの注意。
今度はイメージ拡張とかトライしてみようと思います。


直近でAWSもそうだけど、サーバーレスの動きも定着しつつ、データ運用、アプリケーション対応に重要の比重が増し
もうサーバー設定ごにょごにょする時代は終りをむかえてきたのだよ。
その内、サーバの概念もアプリケーション概念もAIにより急速にわかっていくだろう。今そうなってるから。

ゲーム制作全然進んでないよ。