ubuntu
Ubuntu サーバーでの Docker 複数プロジェクト運用ベストプラクティス
基本方針(FHSに沿った配置)
- コード(Git管理)とランタイムデータ(永続化)を分離
- 設定(/etc)・データ(/var/lib, /srv)・ログ(/var/log)を意識した配置
- Secrets はGitに含めない(.env, docker secrets, 外部Secret管理)
- kebab-case 命名で統一(services/volumes/networks/フォルダ)
どこにクローンするか(推奨)
- /srv/
: サービス(スタック)単位のホーム。HTTP/アプリ配信向けの標準的な場所 - /opt/
: 自前配布のソフト群。自己完結型のツール/バイナリ置き場としても適切 - /home/ubuntu/apps/
: 個人運用/検証用途なら可(本番は /srv 推奨)
関連ディレクトリの役割:
- /etc/: OSやデーモンの設定(例: /etc/docker/daemon.json, systemd units)
- /var/lib/: ランタイムデータ(例: /var/lib/docker, DBデータの実体など)
- /var/log/: ログ保管(アプリはDockerのログドライバでローテーション)
- /mnt/data または /data: 追加ディスクがある場合のマウント先(大容量データ向け)
推奨レイアウト例(複数スタック)
/srv/
gateway/ # 例: Traefik などのエッジ/ゲートウェイ
compose.yml
traefik.yml
acme/
acme.json # 0600 権限で永続化
config/
secrets/ # Git管理外
README.md
private-db-stack/ # DB専用スタック(非公開)
compose.yml
.env # パスワード等はSecrets/外部ボリューム推奨
backup/
scripts/
cron.d/
data/ # どうしてもバインドマウントが必要な場合のみ
api-server/
compose.yml
.env
config/
scripts/
web-app/
compose.yml
.env
config/
public/
# 共通: 外部Dockerネットワーク(例)
# proxy-net を作成して gateway と公開したいアプリだけ参加させる
各スタック配下の推奨ファイル:
- compose.yml: v2/v3 Compose。
restart: unless-stoppedとhealthcheckを設定 - .env: 変数(Gitに含めない)。環境ごとに分ける(.env, .env.prod)
- config/: コンテナへマウントする設定(nginx.conf 等)
- secrets/: 認証情報。権限600/700。可能なら Docker secrets を使用
- data/: バインドマウントが必要な場合のみ。原則は「名前付きボリューム」
- scripts/: デプロイ/バックアップ/メンテのスクリプト
Compose とネットワーク/ボリューム
- 外部ネットワークを明示して共有
networks:
proxy-net:
external: true
- ボリュームは「名前付き」を基本にし、実体はDockerに管理させる
volumes:
app-data:
db-data:
起動・自動化(2つの選択肢)
- Compose の restart ポリシーに任せる(簡易)
restart: unless-stoppedを各サービスに設定- サーバー再起動で自動復旧
- systemd でComposeスタックを管理(明示制御)
- /etc/systemd/system/gateway.service(例)
[Unit]
Description=gateway stack (docker compose)
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
WorkingDirectory=/srv/gateway
RemainAfterExit=yes
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
- 有効化:
systemctl enable --now gateway
Docker デーモンのログローテーション
- /etc/docker/daemon.json(例)
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
- 変更後は
systemctl restart docker(実環境では計画的に)
権限とユーザー
- 運用ユーザー(例: ubuntu/deploy)を
dockerグループに追加(sudo不要運用は要検討) - データディレクトリはコンテナのUID/GIDに合わせてchown(例: postgres 999:999)
- 機密ファイルは 600/700 権限
セキュリティ基本
- SSH: キー認証、PasswordAuthentication=no、fail2ban
- ファイアウォール: ufw allow 22,80,443; 最小限の公開
- docker.sock 直マウントは避け、必要なら docker-socket-proxy
- Traefikダッシュボードは本番公開しない/認証保護
バックアップ戦略
- 名前付きボリュームのスナップショット
# 例: ボリュームをtgz化(メンテナンス時間に実施)
docker run --rm -v db-data:/v -v $(pwd):/backup alpine sh -c "tar czf /backup/db-data-$(date +%F).tgz -C /v ."
- DBは論理バックアップ(pg_dump, mysqldump)と物理バックアップを併用
- バックアップ対象: /srv/**/compose.yml, config, secrets, acme.json
運用Tips
- 監視:
docker ps,docker compose ps,docker logs -f,docker system df - クリーンアップ:
docker system pruneは慎重に(本番では -a は基本使わない) - 更新:
docker compose pull && docker compose up -d(サービス単位で段階的に) - リソース:
healthcheckとdeploy.resources(Compose v3)で制御
システムアップデートと再起動(総合手順)
本番/準本番のUbuntuに対し、再起動が必要なアップデートへ安全に対応するための簡易手順です。
1) 事前チェック
- 空き容量/イメージ使用量
df -h docker system df - APT 整合性(ロック/破損の確認と修復)
sudo dpkg --configure -a sudo apt -f install - 最低限のバックアップ(必要に応じて)
- /srv/**/compose.yml, config/, secrets/, acme.json
- DB は論理バックアップ(pg_dump, mysqldump)
2) アップデート適用と再起動要否の確認
sudo apt update
sudo apt upgrade -y # 重要更新を含む場合は sudo apt full-upgrade -y
# 再起動要否
test -f /var/run/reboot-required && cat /var/run/reboot-required || echo "reboot not required"
3) 再起動前の健全性確認
systemctl status docker --no-pager
docker ps --format '{{.Names}} {{.Status}}'
docker compose ls
sudo ss -lntp | grep -E ':80|:443'
- Traefik を使う場合: acme.json など永続化の確認(例: /srv/gateway/acme/acme.json は 0600)
4) 再起動の実行
sudo reboot
5) 再起動後の確認
uptime -p && uname -r
systemctl --failed
systemctl status docker --no-pager
docker ps --format '{{.Names}} {{.Status}}'
# 代表スタック
docker compose -f /srv/gateway/compose.yml ps 2>/dev/null || true
sudo ss -lntp | grep -E ':80|:443'
curl -I http://127.0.0.1 2>/dev/null | head -n 1 || true
- 自動復旧方針別
- restart: unless-stopped のみ → 起動していないサービスは個別に
docker compose up -d - systemd 管理 →
systemctl status <unit>/sudo systemctl restart <unit>
- restart: unless-stopped のみ → 起動していないサービスは個別に
6) よくあるトラブルと対処
- Docker が起動しない/遅い
journalctl -u docker -b --no-pager | tail -n 200 df -h sudo systemctl restart docker - Compose 外部ネットワーク未作成(例: proxy-net)
docker network ls | grep proxy-net || docker network create proxy-net - Traefik が 80/443 を掴んでいない
docker logs traefik --tail=200 - コンテナが Unhealthy/未起動
docker ps --format '{{.Names}} {{.Status}}' docker logs <service> --tail=200 docker compose -f /srv/<stack>/compose.yml up -d --remove-orphans
7) 自動アップデート(unattended-upgrades)方針
- 現状確認
cat /etc/apt/apt.conf.d/20auto-upgrades 2>/dev/null || true grep -E 'Unattended-Upgrade::Automatic-Reboot' /etc/apt/apt.conf.d/50unattended-upgrades 2>/dev/null || true - 自動再起動を避けたい場合(例)
sudo sed -i 's|^//\?\s*Unattended-Upgrade::Automatic-Reboot .*|Unattended-Upgrade::Automatic-Reboot "false";|' /etc/apt/apt.conf.d/50unattended-upgrades - 許容する場合は時間指定(例)
sudo sed -i 's|^//\?\s*Unattended-Upgrade::Automatic-Reboot-Time .*|Unattended-Upgrade::Automatic-Reboot-Time "02:00";|' /etc/apt/apt.conf.d/50unattended-upgrades
まとめ
- 本番は /srv を起点にスタック単位でクローン・運用
- 名前付きボリューム+外部ネットワークで疎結合に
- 設定/データ/ログ/Secrets を分離し、権限とバックアップを徹底