Shell脚本如何配置容器日志采集:从基础到实战的完整指南
目录导读
容器日志采集的核心挑战
在容器化环境中,Docker或Kubernetes默认的日志驱动(如json-file)会将所有输出写入宿主机特定目录,当容器数量激增时,手动管理日志变得困难,常见痛点包括:

- 日志文件无限增长:默认无轮转策略,磁盘容易爆满。
- 格式混乱:不同容器输出的json、text、syslog格式需要统一解析。
- 权限问题:采集脚本可能无法访问特定容器的日志路径。
Shell脚本的优势在于轻量、无需额外守护进程,且能直接调用Docker API或docker logs命令实现灵活采集。
Shell脚本在日志采集中的角色
Shell脚本适合作为“胶水层”,连接容器运行时与日志后端(如Elasticsearch、Splunk或本地文件),典型工作流:
容器日志 → Shell脚本采集 → 格式化 → 写入文件或发送到远程服务器
适用场景:
- 小型开发环境或边缘节点无需复杂日志平台。
- 作为监控系统启动前的临时采集方案。
- 对日志格式有特殊转换需求(如JSON转CSV)。
环境准备与依赖安装
在编写脚本前,确保宿主机具备以下条件:
# 检查Docker是否运行
docker ps &>/dev/null || { echo "Docker未运行"; exit 1; }
# 安装必要的工具(以Ubuntu为例)
sudo apt-get install -y jq curl # jq用于解析JSON,curl用于发送HTTP请求
关键点:
docker logs命令默认只显示最近日志,需添加--since或--follow实现持续采集。- 使用
docker inspect获取容器日志路径(默认位于/var/lib/docker/containers/<container_id>/<container_id>-json.log)。
基础日志采集脚本编写
以下脚本演示如何定期采集所有运行中容器的日志,并按容器名保存为独立文件:
#!/bin/bash
# 采集所有容器日志到 /var/log/container_logs/
OUTPUT_DIR="/var/log/container_logs"
mkdir -p "$OUTPUT_DIR"
# 获取所有容器ID(忽略退出状态)
docker ps -q | while read -r CID; do
CONTAINER_NAME=$(docker inspect --format '{{.Name}}' "$CID" | sed 's/\///')
# 使用jq提取日志内容(去除时间戳前缀,保持原始输出)
docker logs "$CID" 2>/dev/null | jq -R '{"ts": now, "msg": .}' > "$OUTPUT_DIR/${CONTAINER_NAME}.json"
# 每30秒轮询一次
sleep 30 &
done
# 等待所有后台进程
wait
说明:
- 脚本通过
docker logs抓取标准输出和错误(2>/dev/null隐藏Docker元数据)。 - 使用
jq将每行日志转换为JSON格式,并添加时间戳(now是jq内置函数)。 - 每个容器独立文件,避免混写。
高级配置:动态采集与过滤器
实际生产环境中,往往需要按需采集特定容器或过滤敏感信息,以下脚本实现:
- 基于标签(
log_collect=true)筛选容器。 - 过滤掉包含“DEBUG”且长度小于10的日志条目(节省空间)。
#!/bin/bash
# 高级日志采集:支持标签过滤与内容过滤
FILTER_LABEL="log_collect=true"
OUTPUT_DIR="/var/log/container_logs/filtered"
REGEX_IGNORE='^(DEBUG|TRACE).{0,10}$' # 忽略以DEBUG/TRACE开头且短的日志
docker ps --filter "label=$FILTER_LABEL" -q | while read -r CID; do
docker logs --follow "$CID" 2>/dev/null | while read -r line; do
# 过滤条件
if [[ "$line" =~ $REGEX_IGNORE ]]; then
continue
fi
# 添加元数据
echo "{\"container\": \"$(docker inspect --format '{{.Name}}' "$CID" | sed 's/\///')\", \"log\": $(jq -Rsa . <<<"$line")}" >> "$OUTPUT_DIR/merged.log"
done
done
重点:
--follow实现实时流式采集,适合监控场景。- 使用
jq -Rsa .对特殊字符(如引号、换行)进行转义,防止JSON破坏。
日志轮转与存储优化
容器日志可能单日产生数GB,轮转策略必不可少,Shell脚本可实现logrotate风格的简单轮转:
#!/bin/bash
# 日志轮转:按文件大小分割(每100MB创建新文件)
MAX_SIZE=104857600 # 100MB
LOG_DIR="/var/log/container_logs"
rotate_file() {
local base="$1"
local count=${2:-1}
if [ -f "${base}.${count}" ]; then
rotate_file "$base" $((count+1))
fi
mv "${base}.json" "${base}.${count}.json" 2>/dev/null
}
cd "$LOG_DIR" || exit 1
for f in *.json; do
if [ $(stat -c%s "$f") -gt $MAX_SIZE ]; then
rotate_file "$(basename "$f" .json)"
fi
done
优化建议:
- 结合
cron每天凌晨执行轮转脚本。 - 压缩旧日志:
gzip -9 *.1.json。 - 设置保留天数:
find . -name '*.json.gz' -mtime +30 -delete。
常见问题与问答
Q1:Shell脚本采集日志与Filebeat/Logstash有什么区别?
- 优势:Shell无需额外内存,适合资源受限的边缘设备;可快速定制,无需学习新工具。
- 劣势:缺乏弹性、重试机制,大规模集群不稳定,生产环境建议使用Filebeat或Fluentd。
Q2:采集的日志中出现了乱码或特殊字符,如何解决?
- 确保使用
jq -Rsa .对输入进行JSON序列化。 - 检查容器内应用编码:
docker exec <container> locale,若为POSIX需设置export LC_ALL=C.UTF-8。
Q3:脚本采集时磁盘I/O过高,如何限制?
- 增加
read -t 1等待时间,降低轮询频率。 - 使用
ionice -c 3降低脚本进程的I/O优先级。
Q4:如何将日志发送到远程服务器(如Elasticsearch)?
- 在脚本中集成
curl:curl -X POST "http://your-es-host:9200/logs/_doc" -H "Content-Type: application/json" -d "$(tail -1 output.log)"。 - 注意批量提交减少请求次数,可使用
数组+curl --data-binary。
Q5:Kubernetes环境下如何采集Pod日志?
- Kubernetes默认使用journald,Shell脚本可通过
kubectl logs代替docker logs。 - 或者直接读取Pod挂载的日志文件(如
/var/log/pods/),需注意权限。