2026年8月7日

2026年8月7日

シェルスクリプトでDockerを操作する方法(起動待ち・DBバックアップ・更新・掃除の自動化)

シェルスクリプトで Docker を操作するときは、docker / docker compose コマンドをそのまま並べるだけでなく、「コンテナが本当に使える状態になるまで待つ」「cron では TTY なしで実行する」「失敗したら止まる」という3点を押さえると、安定して動く自動化になります。この記事では、WordPress と MySQL を Docker Compose で動かしている環境を例に、起動待ち、データベースのバックアップ、イメージの更新、不要なデータの掃除をスクリプト化する方法と、つまずきやすい点を説明します。

前提と準備

Compose v2 の docker compose を使う

現在の Docker では、Compose は docker compose(スペース区切り、v2 のプラグイン)として提供されています。以前の docker-compose(ハイフン区切り、v1)はサポートが終了しているため、新しく書くスクリプトは docker compose を前提にしてください。どちらが使えるかは次のコマンドで確認できます。

docker compose version
docker version --format '{{.Server.Version}}'

オプションや出力形式はバージョンによって追加・変更されることがあります(2026年10月現在)。スクリプトを書いたサーバーと動かすサーバーのバージョンをそろえておくと、思わぬ違いを避けられます。

スクリプトの共通の書き出し

#!/usr/bin/env bash
set -euo pipefail

cd /srv/wordpress            # compose.yaml がある場所
export COMPOSE_PROJECT_NAME=wordpress

set -euo pipefail で、コマンドの失敗、未定義の変数、パイプラインの途中の失敗を検知してスクリプトを止めます。Compose はカレントディレクトリの compose.yaml(または docker-compose.yml)とディレクトリ名からプロジェクトを判断するため、cron から実行するときは冒頭で cd しておくのが安全です。-f でファイルを、-p や COMPOSE_PROJECT_NAME でプロジェクト名を明示する方法もあります。

docker グループと権限の扱い

docker コマンドを sudo なしで使うために、ユーザーを docker グループに入れる運用はよくあります。ただし、Docker デーモンを操作できる権限は、実質的にホストの root 権限と同じです。自動化用のスクリプトを動かすユーザーは用途を限定し、Docker のソケット(/var/run/docker.sock)を Web アプリのコンテナにマウントするような構成は避けてください。

コンテナが使える状態になるまで待つ

docker compose up -d はコンテナを起動した時点で戻ってきますが、そのときデータベースが接続を受け付けているとは限りません。直後にバックアップやマイグレーションを実行すると、接続エラーで失敗することがあります。

Compose v2 では docker compose up -d --wait で、サービスが起動して healthcheck が healthy になるまで待てます。古い環境で --wait が使えない場合や、待ち時間を細かく制御したい場合は、docker inspect で状態を確認する関数を用意します。

wait_healthy() {
  local service="$1" timeout="${2:-120}" waited=0 id status
  id="$(docker compose ps -q "$service")"
  [[ -n "$id" ]] || { echo "$service が起動していません" >&2; return 1; }

  while (( waited < timeout )); do
    status="$(docker inspect --format \
      '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$id")"
    case "$status" in
      healthy|running) return 0 ;;
      unhealthy|exited|dead) echo "$service: $status" >&2; return 1 ;;
    esac
    sleep 2
    waited=$(( waited + 2 ))
  done
  echo "$service: ${timeout}秒待っても準備ができませんでした" >&2
  return 1
}

healthcheck が定義されているコンテナは starting → healthy と変わるので、healthy になるまで待ちます。healthcheck がないコンテナは running になった時点で戻りますが、これは「プロセスが動いている」だけで「接続を受け付けている」ことは意味しません。データベースのように準備に時間がかかるサービスには、compose.yaml で healthcheck を定義しておくのがおすすめです。

データベースをバックアップする

コンテナ内の MySQL / MariaDB から、ホストにダンプを取り出す例です。作業前に、ダンプの保存先に十分な空き容量があることを確認してください。

backup_db() {
  local dest="/var/backups/wp/db-$(date +%Y%m%d-%H%M%S).sql.gz"
  mkdir -p "$(dirname "$dest")"

  docker compose exec -T db sh -c \
    'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --default-character-set=utf8mb4 "$MYSQL_DATABASE"' \
    | gzip > "$dest.tmp"

  mv "$dest.tmp" "$dest"
  echo "保存しました: $dest"
}

この例のポイントは次のとおりです。

  • **-T で TTY を割り当てない**: docker compose exec は既定で TTY を割り当てます。cron などの端末がない環境やパイプにつなぐ場合は -T が必要で、付けないとエラーになったり、出力に制御文字が混ざってダンプが壊れたりします。docker exec の場合は -t を付けないようにします。
  • パスワードをホスト側のコマンドに書かない: シングルクォートで囲んだ sh -c の中で、コンテナの環境変数 $MYSQL_ROOT_PASSWORD を展開しています。ホストの ps やシェルの履歴にパスワードが残りません。可能であれば、root ではなくバックアップ専用のユーザーを作って使ってください。
  • **一時ファイルに書いてから mv**: 途中で失敗したときに、壊れたファイルが正規のバックアップとして残らないようにします。set -o pipefail によって、mysqldump が失敗した場合もスクリプトは止まります。

MariaDB の新しいイメージでは、mysqldump の代わりに mariadb-dump を使う構成になっていることがあります。コマンドが見つからないというエラーが出たら、使っているイメージのドキュメントで確認してください。取ったバックアップは、別の環境で実際に復元できるかを定期的に試しておくと安心です。

WP-CLI をコンテナから実行する

WordPress の操作は、公式の wordpress:cli イメージを使うと、ホストに PHP を入れずに WP-CLI を実行できます。compose.yaml に WordPress 本体と同じボリュームとネットワークを使う wpcli サービスを定義しておき、必要なときだけ起動します。

docker compose run --rm -T wpcli wp plugin list --update=available
docker compose run --rm -T wpcli wp core verify-checksums

--rm を付けると、実行後にコンテナが自動で削除されます。WordPress 本体のイメージと CLI のイメージで Web サーバーのユーザー ID が異なる組み合わせがあり、ファイルを書き込む操作で権限エラーになることがあります。その場合は、--user で本体側のファイル所有者と同じ ID を指定して実行してください。プラグインの更新などの変更を伴う操作は、先にバックアップを取り、ステージング環境で試してから本番に適用するのが基本です。

イメージを更新して再起動する

イメージを最新にしてコンテナを作り直す流れは、次のようになります。

update_stack() {
  backup_db
  docker compose pull
  docker compose up -d --remove-orphans
  wait_healthy db 180
  wait_healthy wordpress 120
  docker compose ps
}

更新の前にバックアップを取り、更新後に healthcheck が通ることを確認してから処理を終えます。latest タグのイメージを使っていると、意図しないメジャーバージョンアップが入ることがあります。本番環境では mysql:8.4 のようにバージョンを固定したタグを使い、更新するときにタグを書き換える運用のほうが、何が変わったかを把握しやすくなります。

不要なイメージやデータを掃除する

更新を繰り返すと、使われなくなった古いイメージがディスクを圧迫します。

docker system df
docker image prune -f --filter "until=168h"
docker builder prune -f --filter "until=168h"

docker system df で、イメージ・コンテナ・ボリューム・ビルドキャッシュがそれぞれどれだけ容量を使っているかを確認できます。--filter "until=168h" は、作成から7日以上たったものに限る指定です。docker image prune は既定ではタグのない(dangling)イメージだけを削除し、-a を付けるとどのコンテナにも使われていないイメージ全体が対象になります。

docker system prune --volumes や docker volume prune は、どのコンテナにも使われていないボリュームを削除します。たとえば docker compose down でコンテナを削除した直後に実行すると、データベースのデータが入ったボリュームも「未使用」として消える可能性があります。対象になる範囲は Docker のバージョンやオプションによっても異なるため、ボリュームの削除は自動化のスクリプトには入れず、内容を確認してから手作業で行ってください。

cron で定期実行する

スクリプトを cron に登録する例です。

30 3 * * * /usr/local/bin/wp-docker-backup.sh >> /var/log/wp-docker-backup.log 2>&1

cron の環境では PATH が短いため、docker コマンドが見つからないことがあります。スクリプトの冒頭で PATH を設定するか、command -v docker で確認してください。同じスクリプトが前回の実行と重なって動くと、バックアップや更新が衝突します。flock を使って多重起動を防いでおくと安全です。

失敗に気づけるよう、終了コードが 0 以外のときにメールやチャットへ通知する仕組みも用意しておきましょう。なお、docker run 自体が失敗したときは、コンテナの終了コードではなく 125(デーモン側のエラー)などが返るため、ログと合わせて原因を確認します。

まとめ

  • 新しいスクリプトは Compose v2 の docker compose を前提にし、冒頭で set -euo pipefail と cd を書く
  • 起動直後に処理する場合は up -d --wait か healthcheck の確認で「使える状態」まで待つ
  • cron やパイプでは docker compose exec -T を使い、パスワードはコンテナの環境変数で展開する
  • 更新の前にはバックアップを取り、タグを固定して変化を管理する
  • 掃除は image prune などの範囲を限ったコマンドにとどめ、ボリュームの削除は自動化しない
  • docker グループやソケットへのアクセスは root 相当の権限だと理解して扱う

お気軽にご相談ください

お見積りへ お問い合わせへ
▲