清理 Docker 容器日志脚本
一个实用的 bash 脚本,用于查看 Docker 容器日志大小排行,并支持按容器名或短 ID 清理日志文件。

在服务器上运行 Docker 容器一段时间后,容器的日志文件(通常是以 json 格式存储在 /var/lib/docker/containers/ 目录下)可能会变得非常庞大,占用大量磁盘空间,甚至导致系统 I/O 阻塞和性能下降。

这里分享一个实用的 bash 脚本,它不仅可以列出各个容器日志的大小排行,还支持通过指定容器名称或容器的短 ID 来安全地清理日志,而且不需要重启容器

脚本功能

  1. 查看日志排行:统计各个容器的日志占用大小,从大到小排序输出。
  2. 清理指定容器日志:安全清空特定容器日志,解决占用问题的同时不中断容器服务。
  3. 清理所有日志:一键清理所有容器日志(执行前有二次确认机制)。

脚本代码 (docker_logs.sh)

创建并保存以下代码为 docker_logs.sh,赋予执行权限后即可使用:

#!/bin/bash

# ====================================================
# Docker 容器日志管理脚本
# 用法:
#   1. 查看日志占用排行: ./docker_logs.sh
#   2. 清理指定容器日志: ./docker_logs.sh clean <容器名或ID>
#   3. 清理所有容器日志: ./docker_logs.sh clean all
# ====================================================

COMMAND=$1
TARGET=$2

# ----------------- 功能 1:列出日志排行 -----------------
list_logs() {
    echo -e "\n================================ Docker 容器日志大小占用排行 ================================"
    printf "%-12s %-35s %-40s %-20s\n" "日志大小" "容器名称" "容器ID" "镜像名称"
    echo "-----------------------------------------------------------------------------------------------------------"

    sudo docker ps -a --format '{{.ID}}' | while read -r id; do
        name=$(sudo docker inspect --format '{{.Name}}' "$id" | sed 's/^\///')
        image=$(sudo docker inspect --format '{{.Config.Image}}' "$id")
        logpath=$(sudo docker inspect --format '{{.LogPath}}' "$id")
        
        if [ -f "$logpath" ]; then
            size=$(sudo du -h "$logpath" | cut -f1)
            size_kb=$(sudo du -k "$logpath" | cut -f1)
            
            # id 只取前 12 位短 ID
            short_id=${id:0:12}
            
            echo "${size_kb}|${size}|${name}|${short_id}|${image}"
        fi
    done | sort -nr | awk -F'|' '{printf "%-10s %-35s %-40s %-20s\n", $2, $3, $4, $5}'

    echo "==========================================================================================================="
    echo "提示: 可以使用 './docker_logs.sh clean <容器名或ID>' 来清理特定容器的日志。"
}

# ----------------- 功能 2:清理容器日志 -----------------
clean_logs() {
    if [ -z "$TARGET" ]; then
        echo "错误: 请提供要清理的容器名称、ID,或者使用 'all' 清理所有容器。"
        echo "用法: ./docker_logs.sh clean <容器名或ID>"
        echo "示例: ./docker_logs.sh clean apisix"
        exit 1
    fi

    # 确定要处理的容器列表
    if [ "$TARGET" = "all" ]; then
        echo "警告: 准备清理所有容器的日志!"
        read -p "确定要继续吗?(y/N): " confirm
        if [[ "$confirm" != [yY] && "$confirm" != [yY][eE][sS] ]]; then
            echo "已取消。"
            exit 0
        fi
        # 获取所有容器 ID
        containers=$(sudo docker ps -a -q)
    else
        # 验证目标容器是否存在 (可以是名字或 ID)
        container_id=$(sudo docker ps -a -q -f "name=^/${TARGET}$" -f "id=${TARGET}")
        
        if [ -z "$container_id" ]; then
            # 如果按精确匹配没找到,尝试模糊匹配 (比如输入了短ID)
            container_id=$(sudo docker ps -a -q -f "id=${TARGET}")
            if [ -z "$container_id" ]; then
                # 最后尝试模糊匹配名字
                container_id=$(sudo docker ps -a -q -f "name=${TARGET}")
            fi
        fi

        if [ -z "$container_id" ]; then
            echo "错误: 找不到容器 '${TARGET}'。请使用 './docker_logs.sh' 查看可用的容器列表。"
            exit 1
        fi
        
        containers="$container_id"
        
        # 如果模糊匹配找到了多个,提示并退出以防止误删
        count=$(echo "$containers" | wc -l)
        if [ "$count" -gt 1 ]; then
            echo "错误: '${TARGET}' 匹配到了多个容器。请使用更精确的容器名称或 ID。"
            sudo docker ps -a --format "table {{.ID}}\t{{.Names}}" | grep "$TARGET"
            exit 1
        fi
    fi

    # 执行清理逻辑
    for id in $containers; do
        name=$(sudo docker inspect --format '{{.Name}}' "$id" | sed 's/^\///')
        logpath=$(sudo docker inspect --format '{{.LogPath}}' "$id")
        
        if [ -f "$logpath" ]; then
            old_size=$(sudo du -h "$logpath" | cut -f1)
            # 使用 truncate 安全清空日志,不影响运行中的容器
            sudo truncate -s 0 "$logpath"
            echo "✅ 已清理容器 [${name}] 的日志 (释放了 ${old_size} 空间)。"
        else
            echo "⚠️  容器 [${name}] 的日志文件不存在或没有权限访问。"
        fi
    done
}

# ----------------- 主入口逻辑 -----------------
case "$COMMAND" in
    clean)
        clean_logs
        ;;
    help|-h|--help)
        echo "用法:"
        echo "  $0                  - 列出所有容器日志大小排行"
        echo "  $0 clean <name|id>  - 清理指定容器名或 ID 的日志"
        echo "  $0 clean all        - 清理所有容器的日志"
        ;;
    "")
        list_logs
        ;;
    *)
        echo "未知命令: $COMMAND"
        echo "请使用 '$0 help' 查看帮助。"
        exit 1
        ;;
esac

使用方法

首先,给脚本添加执行权限:

chmod +x docker_logs.sh

1. 查看日志占用排行(默认行为)

直接运行脚本,会输出按日志大小排序的列表:

./docker_logs.sh

2. 清理指定容器的日志

支持通过容器名称精确匹配或模糊匹配(如 nginx, apisix):

./docker_logs.sh clean apisix

也支持通过 容器短 ID 清理(如 a1b2c3):

./docker_logs.sh clean a1b2c3

注意: 使用 truncate 命令清理日志是非常安全的,因为它会在不关闭文件描述符的情况下将文件大小清零。这意味着不需要停止或重启容器,容器仍然能向其中写入新日志,不会引起程序异常或卡死。

3. 一键清理所有容器日志

如果想一键释放所有空间:

./docker_logs.sh clean all

脚本具有防呆设计,在执行 all 命令时会提示 (y/N) 确认,避免误删!

长效解决建议

虽然这个脚本可以非常方便地处理手头的问题,但为了防止日后再次发生“日志撑爆硬盘”的情况,强烈建议在运行容器或 docker-compose.yml 中设置日志轮转配置。

docker-compose.yml 中增加以下 logging 配置段:

services:
  your_service:
    image: your_image
    # 限制单个文件最大50M,最多保留3个旧文件
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "3"

配置后重新启动容器(docker-compose up -d),Docker 将会自动帮您管理日志轮转。


最后修改于 2026-03-02