Podman実践的トラブルシューティングガイド
本ガイドは、Podman運用で実際に遭遇する問題とその解決方法を、具体的な手順とともに解説します。
トラブルシューティングの基本アプローチ
診断フローチャート
graph TD
A[問題発生] --> B{コンテナ起動?}
B -->|No| C[起動エラー診断]
B -->|Yes| D{ネットワーク接続?}
C --> E[権限問題]
C --> F[イメージ問題]
C --> G[リソース問題]
D -->|No| H[ネットワーク診断]
D -->|Yes| I{パフォーマンス正常?}
I -->|No| J[パフォーマンス診断]
I -->|Yes| K[アプリケーション問題]
基本診断コマンド
#!/bin/bash
# podman-diagnose.sh - Podman環境の基本診断
echo "=== Podman環境診断 ==="
echo ""
# 1. バージョン情報
echo "[INFO] バージョン情報:"
podman version
# 2. システム情報
echo -e "\n[INFO] システム情報:"
podman info --format json | jq '{
host: {
os: .host.os,
kernel: .host.kernel,
arch: .host.arch,
rootless: .host.rootless,
cgroupVersion: .host.cgroupVersion
},
store: {
driver: .store.graphDriverName,
root: .store.graphRoot,
runRoot: .store.runRoot
}
}'
# 3. 実行中のコンテナ
echo -e "\n[INFO] 実行中のコンテナ:"
podman ps --format "table \{\{.Names\}\}\t\{\{.Status\}\}\t\{\{.State\}\}"
# 4. システムリソース
echo -e "\n[INFO] システムリソース:"
podman system df
# 5. ネットワーク
echo -e "\n[INFO] ネットワーク:"
podman network ls
# 6. 最近のイベント
echo -e "\n[INFO] 最近のイベント (エラーのみ):"
podman events --since 1h --filter event=died --format json | jq '.'
起動エラーの解決
1. Permission Denied エラー
症状
$ podman run alpine ls
Error: OCI runtime error: permission denied
診断と解決
#!/bin/bash
# fix-permission-denied.sh
echo "権限問題の診断開始..."
# 1. SELinuxステータス確認
if command -v getenforce &> /dev/null; then
selinux_status=$(getenforce)
echo "SELinux: $selinux_status"
if [ "$selinux_status" = "Enforcing" ]; then
echo "[WARN] SELinux が原因の可能性がある"
# SELinuxコンテキストの修正
echo "SELinuxコンテキストを修正中..."
restorecon -R ~/.local/share/containers
# 一時的な回避策(テスト用)
echo "テスト実行(SELinux無効):"
podman run --security-opt label=disable alpine ls
fi
fi
# 2. ユーザー名前空間確認
echo -e "\nユーザー名前空間の確認:"
if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then
userns_enabled=$(cat /proc/sys/kernel/unprivileged_userns_clone)
if [ "$userns_enabled" = "0" ]; then
echo "[NG] ユーザー名前空間が無効"
echo "修正方法:"
echo "sudo sysctl -w kernel.unprivileged_userns_clone=1"
echo "永続化: echo 'kernel.unprivileged_userns_clone=1' | sudo tee /etc/sysctl.d/userns.conf"
else
echo "[OK] ユーザー名前空間は有効"
fi
fi
# 3. subuid/subgid確認
echo -e "\nUID/GIDマッピング:"
if ! grep -q "^$USER:" /etc/subuid; then
echo "[NG] subuid エントリがない"
echo "修正方法:"
echo "sudo usermod --add-subuids 100000-165535 $USER"
else
echo "[OK] subuid: $(grep "^$USER:" /etc/subuid)"
fi
if ! grep -q "^$USER:" /etc/subgid; then
echo "[NG] subgid エントリがない"
echo "修正方法:"
echo "sudo usermod --add-subgids 100000-165535 $USER"
else
echo "[OK] subgid: $(grep "^$USER:" /etc/subgid)"
fi
# 4. ストレージ権限
echo -e "\nストレージディレクトリ権限:"
storage_dirs=(
"$HOME/.local/share/containers"
"$HOME/.config/containers"
"$XDG_RUNTIME_DIR/containers"
)
for dir in "${storage_dirs[@]}"; do
if [ -d "$dir" ]; then
perms=$(stat -c "%a %U:%G" "$dir")
echo "$dir: $perms"
else
echo "$dir: 存在しません(作成されます)"
fi
done
2. イメージプルエラー
症状
$ podman pull myregistry.com/myimage:latest
Error: initializing source docker://myregistry.com/myimage:latest: pinging container registry myregistry.com: Get "https://myregistry.com/v2/": x509: certificate signed by unknown authority
診断と解決
#!/bin/bash
# fix-registry-errors.sh
registry_url=$1
if [ -z "$registry_url" ]; then
echo "使用方法: $0 <registry_url>"
exit 1
fi
echo "レジストリ接続問題の診断: $registry_url"
# 1. DNS解決確認
echo -e "\n1. DNS解決テスト:"
if host $registry_url > /dev/null 2>&1; then
echo "[OK] DNS 解決成功"
host $registry_url | head -3
else
echo "[NG] DNS 解決失敗"
echo "対処法: /etc/resolv.confを確認してください"
fi
# 2. ネットワーク接続性
echo -e "\n2. ネットワーク接続性:"
if curl -k -s -o /dev/null -w "%{http_code}" https://$registry_url/v2/ | grep -q "401\|200"; then
echo "[OK] レジストリに到達可能"
else
echo "[NG] レジストリに到達不可"
echo "ファイアウォールやプロキシ設定を確認してください"
fi
# 3. TLS証明書確認
echo -e "\n3. TLS証明書の確認:"
echo | openssl s_client -servername $registry_url -connect $registry_url:443 2>/dev/null | \
openssl x509 -noout -text | grep -E "Subject:|Issuer:|Not After"
# 4. 自己署名証明書の場合の対処
echo -e "\n4. 自己署名証明書の設定方法:"
cat << EOF
# 方法1: 証明書を信頼済みに追加
sudo mkdir -p /etc/containers/certs.d/$registry_url
sudo cp registry-ca.crt /etc/containers/certs.d/$registry_url/ca.crt
# 方法2: insecureレジストリとして設定
echo '[[registry]]
location = "$registry_url"
insecure = true' | sudo tee -a /etc/containers/registries.conf
# 方法3: 一時的にスキップ
podman pull --tls-verify=false $registry_url/image:tag
EOF
# 5. 認証設定
echo -e "\n5. レジストリ認証:"
if podman login --get-login $registry_url > /dev/null 2>&1; then
echo "[OK] 認証情報が保存されている"
else
echo "[WARN] 認証が必要な場合:"
echo "podman login $registry_url"
fi
3. ストレージ容量不足
症状
$ podman pull large-image:latest
Error: writing blob: write /var/tmp/storage123/layer.tar: no space left on device
診断と解決
#!/bin/bash
# fix-storage-issues.sh
echo "ストレージ問題の診断..."
# 1. ディスク使用状況
echo "ディスク使用状況:"
df -h | grep -E "Filesystem|podman|containers|overlay|/var|/home|/$"
# 2. Podmanストレージ使用状況
echo -e "\nPodmanストレージ分析:"
podman system df -v
# 3. 大きなイメージ/コンテナの特定
echo -e "\n大きなイメージ (上位10):"
podman images --format "table \{\{.Repository\}\}:\{\{.Tag\}\}\t\{\{.Size\}\}" | sort -k2 -hr | head -10
echo -e "\n大きなコンテナ (上位10):"
podman ps -a --format "table \{\{.Names\}\}\t\{\{.Size\}\}" | sort -k2 -hr | head -10
# 4. クリーンアップ提案
echo -e "\nクリーンアップオプション:"
# 未使用イメージ
unused_images=$(podman images -f dangling=true -q | wc -l)
if [ $unused_images -gt 0 ]; then
echo "- 未使用イメージ: $unused_images 個"
echo " 削除: podman image prune"
fi
# 停止中のコンテナ
stopped_containers=$(podman ps -a -f status=exited -q | wc -l)
if [ $stopped_containers -gt 0 ]; then
echo "- 停止中のコンテナ: $stopped_containers 個"
echo " 削除: podman container prune"
fi
# 未使用ボリューム
unused_volumes=$(podman volume ls -f dangling=true -q | wc -l)
if [ $unused_volumes -gt 0 ]; then
echo "- 未使用ボリューム: $unused_volumes 個"
echo " 削除: podman volume prune"
fi
# 5. 自動クリーンアップスクリプト
echo -e "\n自動クリーンアップスクリプト:"
cat << 'EOF'
#!/usr/bin/env bash
# auto-cleanup.sh - 定期実行用(注意: 影響範囲を理解した上で利用する)
set -euo pipefail
# 誤実行防止: 明示的に有効化しない限り動かない
if [ "${PODMAN_AUTO_CLEANUP:-NO}" != "YES" ]; then
echo "[ABORT] Set PODMAN_AUTO_CLEANUP=YES to enable cleanup." >&2
exit 1
fi
# 30日以上古いイメージを削除
# 影響が大きい(未使用イメージも削除されるため注意)
podman image prune --all --filter "until=720h" --force
# 終了したコンテナを削除
podman container prune --force
# 未使用ネットワーク等を削除(影響が大きい)
podman system prune --force
# ボリューム削除はデータ消失につながるため、明示的に opt-in(必要時のみ)
if [ "${PRUNE_VOLUMES:-NO}" = "YES" ]; then
podman volume prune --force
podman system prune --volumes --force
fi
EOF
ネットワーク問題の解決
1. コンテナ間通信不可
症状
# Container A から Container B に接続できない
$ podman exec container-a ping container-b
ping: container-b: Name or service not known
診断と解決
#!/bin/bash
# fix-network-connectivity.sh
echo "コンテナネットワーク診断..."
# 1. ネットワーク構成の確認
echo "ネットワーク一覧:"
podman network ls
echo -e "\nデフォルトネットワークの詳細:"
podman network inspect podman | jq '.[0] | {
name: .name,
driver: .driver,
subnet: .subnets[0].subnet,
gateway: .subnets[0].gateway
}'
# 2. コンテナのネットワーク設定確認
container_name=${1:-"container-a"}
echo -e "\nコンテナ '$container_name' のネットワーク設定:"
podman inspect $container_name | jq '.[0].NetworkSettings | {
Networks: .Networks,
IPAddress: .IPAddress,
Gateway: .Gateway
}'
# 3. 同一ネットワーク上での通信設定
echo -e "\n解決方法:"
cat << 'EOF'
# 方法1: 同じカスタムネットワークを使用
podman network create myapp-net
podman run -d --name app1 --network myapp-net alpine sleep 3600
podman run -d --name app2 --network myapp-net alpine sleep 3600
# 方法2: 既存コンテナをネットワークに接続
podman network connect myapp-net existing-container
# 方法3: Pod内で実行(localhost通信可能)
podman pod create --name myapp-pod
podman run -d --pod myapp-pod --name web nginx
podman run -d --pod myapp-pod --name app python:alpine
EOF
# 4. ネットワークデバッグツール
echo -e "\nデバッグコマンド:"
cat << 'EOF'
# DNS解決テスト
podman exec container-a nslookup container-b
# ネットワーク経路確認
podman exec container-a traceroute container-b
# ポート開放確認
podman exec container-a nc -zv container-b 80
EOF
2. ホストからコンテナへのアクセス不可
症状
$ curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused
診断と解決
#!/bin/bash
# fix-port-mapping.sh
port=${1:-8080}
echo "ポートマッピング診断 (ポート: $port)"
# 1. ポートマッピング確認
echo -e "\nポートマッピング状態:"
podman port --all | grep -E ":$port|Port"
# 2. リッスンポート確認
echo -e "\nシステムのリッスンポート:"
ss -tlnp | grep ":$port" || echo "ポート $port でリッスンしているプロセスなし"
# 3. ファイアウォール確認
echo -e "\nファイアウォール状態:"
if command -v firewall-cmd &> /dev/null; then
sudo firewall-cmd --list-ports
# ポート開放が必要な場合
echo -e "\nポート開放コマンド:"
echo "sudo firewall-cmd --add-port=$port/tcp --permanent"
echo "sudo firewall-cmd --reload"
fi
# 4. iptables確認(rootlessの場合)
echo -e "\niptablesルール (rootless):"
if [ -f /proc/sys/net/ipv4/ip_forward ]; then
ip_forward=$(cat /proc/sys/net/ipv4/ip_forward)
echo "IP転送: $ip_forward"
if [ "$ip_forward" = "0" ]; then
echo "[WARN] IP転送が無効"
echo "有効化: echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward"
fi
fi
# 5. 正しいポートマッピング例
echo -e "\n正しいポートマッピング方法:"
cat << EOF
# 基本的なマッピング
podman run -d -p 8080:80 nginx
# 特定インターフェースにバインド
podman run -d -p 127.0.0.1:8080:80 nginx
# 全インターフェースにバインド
podman run -d -p 0.0.0.0:8080:80 nginx
# 複数ポート
podman run -d -p 8080:80 -p 8443:443 nginx
EOF
パフォーマンス問題の解決
1. コンテナの起動が遅い
診断と解決
#!/bin/bash
# analyze-slow-startup.sh
container=${1:-"test-container"}
image=${2:-"alpine"}
echo "起動パフォーマンス分析..."
# 1. 起動時間の計測
echo -e "\n起動時間計測:"
time_output=$(time -p podman run --rm --name $container $image echo "Started" 2>&1)
echo "$time_output"
# 2. 起動プロセスの詳細分析
echo -e "\n詳細なタイミング分析:"
podman --log-level=debug run --rm $image true 2>&1 | grep -E "time=|duration=" | tail -20
# 3. ストレージドライバーの確認
echo -e "\nストレージドライバー:"
storage_driver=$(podman info --format '\{\{.Store.GraphDriverName\}\}')
echo "現在のドライバー: $storage_driver"
if [ "$storage_driver" != "overlay" ]; then
echo "[WARN] 最適でないストレージドライバーを使用中"
echo "推奨: overlay (native) または overlay (fuse-overlayfs)"
fi
# 4. イメージレイヤーの分析
echo -e "\nイメージレイヤー分析:"
podman history --format "table \{\{.ID\}\}\t\{\{.Size\}\}\t\{\{.CreatedBy\}\}" $image | head -10
# 5. 最適化の提案
echo -e "\nパフォーマンス改善方法:"
cat << EOF
1. イメージの最適化
- マルチステージビルドの使用
- レイヤー数の削減
- ベースイメージの軽量化
2. ストレージの最適化
- overlayドライバーの使用
- SSDの使用
- 定期的なガベージコレクション
3. システムチューニング
- vm.max_map_count の増加
- ulimitの調整
- cgroup v2の使用
EOF
2. メモリ使用量が多い
診断と解決
#!/bin/bash
# analyze-memory-usage.sh
echo "メモリ使用状況の分析..."
# 1. 全体的なメモリ使用状況
echo "システムメモリ状態:"
free -h
# 2. コンテナごとのメモリ使用量
echo -e "\nコンテナメモリ使用量:"
podman stats --no-stream --format "table \{\{.Name\}\}\t\{\{.MemUsage\}\}\t\{\{.MemPerc\}\}\t\{\{.PIDs\}\}"
# 3. 詳細なメモリ分析
echo -e "\nメモリ詳細分析:"
for container in $(podman ps --format "\{\{.Names\}\}"); do
echo -e "\n--- $container ---"
# cgroupメモリ情報
podman exec $container cat /sys/fs/cgroup/memory.current 2>/dev/null | \
numfmt --to=iec --suffix=B --format="%.2f" | \
xargs echo "現在の使用量:"
podman exec $container cat /sys/fs/cgroup/memory.max 2>/dev/null | \
numfmt --to=iec --suffix=B --format="%.2f" 2>/dev/null | \
xargs echo "制限値:"
# プロセス情報
echo "上位プロセス:"
podman exec $container ps aux --sort=-%mem | head -5
done
# 4. メモリリークの検出
echo -e "\nメモリリーク検出:"
cat << 'EOF'
#!/bin/bash
# memory-leak-detector.sh
container=$1
duration=${2:-60}
echo "メモリ使用量を${duration}秒間監視..."
for i in $(seq 1 $duration); do
mem=$(podman stats --no-stream --format "\{\{.MemUsage\}\}" $container | cut -d'/' -f1)
echo "$(date +%H:%M:%S) - $mem"
sleep 1
done | tee memory-trend.log
# 増加傾向の分析
echo "メモリ増加率の計算..."
python3 - memory-trend.log <<'PY'
import re
import sys
from typing import Optional
path = sys.argv[1]
unit_to_bytes = {
"B": 1,
"KB": 1000,
"MB": 1000**2,
"GB": 1000**3,
"TB": 1000**4,
"KiB": 1024,
"MiB": 1024**2,
"GiB": 1024**3,
"TiB": 1024**4,
}
def to_bytes(value: str) -> Optional[float]:
value = value.strip()
match = re.match(r"^([0-9.]+)\s*([A-Za-z]+)$", value)
if not match:
return None
number = float(match.group(1))
unit = match.group(2)
multiplier = unit_to_bytes.get(unit)
if multiplier is None:
return None
return number * multiplier
values = []
with open(path, encoding="utf-8") as file:
for line in file:
line = line.strip()
parts = line.split(" - ", 1)
if len(parts) != 2:
continue
bytes_value = to_bytes(parts[1])
if bytes_value is None:
continue
values.append(bytes_value)
if len(values) < 2:
print("メモリ使用量ログが不足しています。")
sys.exit(1)
delta = values[-1] - values[0]
rate = delta / (len(values) - 1)
print(f"開始: {values[0]:.0f} B")
print(f"終了: {values[-1]:.0f} B")
print(f"差分: {delta:.0f} B")
print(f"平均増加(1サンプルあたり): {rate:.0f} B")
threshold = 10 * 1024 # 1サンプルあたり10KiB以上の増加をリーク疑いとみなす
if rate > threshold:
print(f"判定: 増加傾向あり(リーク疑い、平均 {rate/1024:.1f} KiB/サンプル)")
elif delta > 0:
print(f"判定: 微増傾向(要観察、平均 {rate:.0f} B/サンプル)")
else:
print("判定: 増加傾向なし(安定)")
PY
EOF
# 5. 最適化提案
echo -e "\nメモリ最適化方法:"
echo "1. メモリ制限の設定: podman run -m 512m"
echo "2. スワップ制限: podman run -m 512m --memory-swap 512m"
echo "3. OOMキラーの調整: podman run --oom-kill-disable=false"
高度なトラブルシューティング
1. systemd統合の問題
症状
$ systemctl --user start podman-myapp
Failed to start podman-myapp.service: Unit not found.
診断と解決
#!/bin/bash
# fix-systemd-integration.sh
container_name=${1:-"myapp"}
echo "systemd統合の診断..."
# 1. systemdユニットファイルの生成
echo -e "\nsystemdユニットファイル生成:"
podman generate systemd --new --name $container_name > $container_name.service
echo "生成されたユニットファイル:"
cat $container_name.service
# 2. ユニットファイルの配置
echo -e "\nユニットファイルの配置:"
mkdir -p ~/.config/systemd/user
cp $container_name.service ~/.config/systemd/user/
systemctl --user daemon-reload
# 3. 自動起動の設定
echo -e "\nサービスの管理:"
cat << EOF
# サービスの有効化と起動
systemctl --user enable $container_name.service
systemctl --user start $container_name.service
# 状態確認
systemctl --user status $container_name.service
# ログ確認
journalctl --user -u $container_name.service
EOF
# 4. トラブルシューティングチェックリスト
echo -e "\nチェックリスト:"
echo "- [ ] loginctl show-user でLinger=yes確認"
echo "- [ ] XDG_RUNTIME_DIR が設定されている"
echo "- [ ] systemd --user が実行されている"
2. cgroup v2関連の問題
診断と解決
#!/bin/bash
# fix-cgroup-issues.sh
echo "cgroup設定の診断..."
# 1. cgroupバージョン確認
echo "cgroupバージョン:"
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
echo "cgroup v2 が有効"
echo "利用可能なコントローラー:"
cat /sys/fs/cgroup/cgroup.controllers
else
echo "cgroup v1 が有効"
echo "[WARN] cgroup v2 への移行を推奨"
fi
# 2. 委譲設定の確認
echo -e "\nsystemd委譲設定:"
systemctl show --user -p Delegate
systemctl show --user -p DelegateControllers
# 3. ユーザースライスの設定
echo -e "\nユーザースライス設定:"
cat << 'EOF'
# /etc/systemd/system/user@.service.d/delegate.conf
[Service]
Delegate=cpu cpuset io memory pids
EOF
# 4. リソース制限の適用確認
echo -e "\nリソース制限テスト:"
podman run --rm --memory 100m --cpus 0.5 alpine sh -c '
echo "メモリ制限: $(cat /sys/fs/cgroup/memory.max | numfmt --to=iec)"
echo "CPU制限: $(cat /sys/fs/cgroup/cpu.max)"
'
トラブルシューティングチェックリスト
起動時の問題
- SELinuxの状態確認 (
getenforce) - ユーザー名前空間の有効化
- subuid/subgidの設定
- ストレージディレクトリの権限
ネットワークの問題
- ファイアウォールルール
- ポートの競合確認
- IP転送の有効化
- DNSの設定
パフォーマンスの問題
- ストレージドライバーの最適化
- cgroupの設定
- システムリソースの確認
- カーネルパラメータの調整
運用の問題
- ログローテーション設定
- 監視エージェントの動作
- バックアップスクリプトの実行
- 自動クリーンアップの設定
緊急時の対処法
#!/usr/bin/env bash
# emergency-recovery.sh
set -euo pipefail
echo "緊急リカバリ手順..."
echo
echo "[WARNING] このスクリプトは Podman のデータを削除します。"
echo " - podman system reset(イメージ/コンテナ/ネットワーク等の削除)"
echo " - ~/.config/containers と ~/.local/share/containers の退避(必要なら削除)"
echo
echo "原則としてテスト環境でのみ使用し、本番環境では実行しないでください。"
echo "実行する場合は、事前に影響範囲を確認し、必要なデータをバックアップしてください。"
echo
if [ "${PODMAN_EMERGENCY_RECOVERY:-}" != "YES" ]; then
echo "[ABORT] 実行を中止しました。"
echo "実行する場合は、環境変数 PODMAN_EMERGENCY_RECOVERY=YES を設定して再実行してください。"
exit 1
fi
read -r -p "本当に実行しますか?(実行する場合は 'yes' と入力): " answer
if [ "$answer" != "yes" ]; then
echo "[ABORT] 中止しました。"
exit 1
fi
backup_dir="$HOME/podman-emergency-backup-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
# 1. 全コンテナの停止(存在しない場合もあるため失敗しても継続)
echo "1. 全コンテナ停止"
podman stop --all --time 0 || true
# 2. 設定とデータの退避(reset 前にコピーして保全)
echo "2. 設定とデータの退避: $backup_dir"
if [ -d "$HOME/.config/containers" ]; then
cp -a "$HOME/.config/containers" "$backup_dir/containers.config"
fi
if [ -d "$HOME/.local/share/containers" ]; then
cp -a "$HOME/.local/share/containers" "$backup_dir/containers.data"
fi
# 3. ネットワークのリセット(注意: 未使用ネットワークが削除される)
echo "3. ネットワークリセット"
podman network prune
# 4. ストレージのリセット(注意: データ消失)
echo "4. ストレージクリーンアップ(データ消失)"
podman system reset
# 5. 再起動(必要に応じて)
echo "5. 必要に応じてシステム再起動を検討してください"
まとめ
トラブルシューティングは、問題の正確な診断から始まります。本ガイドで紹介したツールとテクニックを活用し、システマティックなアプローチで問題解決にあたってください。
重要なのは、問題が発生する前の予防的な対策です。定期的な監視、適切なリソース管理、そして継続的なメンテナンスにより、多くの問題を未然に防ぐことができます。