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 "📌 バージョン情報:"
podman version

# 2. システム情報
echo -e "\n📌 システム情報:"
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📌 実行中のコンテナ:"
podman ps --format "table \{\{.Names\}\}\t\{\{.Status\}\}\t\{\{.State\}\}"

# 4. システムリソース
echo -e "\n📌 システムリソース:"
podman system df

# 5. ネットワーク
echo -e "\n📌 ネットワーク:"
podman network ls

# 6. 最近のイベント
echo -e "\n📌 最近のイベント (エラーのみ):"
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 "⚠️  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 "❌ ユーザー名前空間が無効です"
        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 "✅ ユーザー名前空間は有効です"
    fi
fi

# 3. subuid/subgid確認
echo -e "\nUID/GIDマッピング:"
if ! grep -q "^$USER:" /etc/subuid; then
    echo "❌ subuidエントリがありません"
    echo "修正方法:"
    echo "sudo usermod --add-subuids 100000-165535 $USER"
else
    echo "✅ subuid: $(grep "^$USER:" /etc/subuid)"
fi

if ! grep -q "^$USER:" /etc/subgid; then
    echo "❌ subgidエントリがありません"
    echo "修正方法:"
    echo "sudo usermod --add-subgids 100000-165535 $USER"
else
    echo "✅ 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 "✅ DNS解決成功"
    host $registry_url | head -3
else
    echo "❌ 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 "✅ レジストリに到達可能"
else
    echo "❌ レジストリに到達不可"
    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 "✅ 認証情報が保存されています"
else
    echo "⚠️  認証が必要な場合:"
    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 "\n📊 Podmanストレージ分析:"
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'
#!/bin/bash
# auto-cleanup.sh - 定期実行用

# 30日以上古いイメージを削除
podman image prune -a --filter "until=720h" -f

# 終了したコンテナを削除
podman container prune -f

# 未使用ボリュームを削除
podman volume prune -f

# ビルドキャッシュをクリア
podman system prune --volumes -f
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 "\n🔗 iptablesルール (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 "⚠️  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 "⚠️  最適でないストレージドライバーを使用中"
    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 "\n📝 systemdユニットファイル生成:"
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 "⚠️  cgroup v2への移行を推奨"
fi

# 2. 委譲設定の確認
echo -e "\n🔧 systemd委譲設定:"
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の設定
  • システムリソースの確認
  • カーネルパラメータの調整

運用の問題

  • ログローテーション設定
  • 監視エージェントの動作
  • バックアップスクリプトの実行
  • 自動クリーンアップの設定

🆘 緊急時の対処法

#!/bin/bash
# emergency-recovery.sh

echo "緊急リカバリ手順..."

# 1. 全コンテナの強制停止
echo "1. 全コンテナ停止"
podman stop -a -t 0

# 2. ネットワークのリセット
echo "2. ネットワークリセット"
podman network prune -f

# 3. ストレージのリセット(注意:データ消失)
echo "3. ストレージクリーンアップ"
podman system reset --force

# 4. 設定の再初期化
echo "4. 設定リセット"
rm -rf ~/.config/containers
rm -rf ~/.local/share/containers

# 5. 再起動
echo "5. システム再起動を推奨"

まとめ

トラブルシューティングは、問題の正確な診断から始まります。本ガイドで紹介したツールとテクニックを活用し、システマティックなアプローチで問題解決にあたってください。

重要なのは、問題が発生する前の予防的な対策です。定期的な監視、適切なリソース管理、そして継続的なメンテナンスにより、多くの問題を未然に防ぐことができます。