CircleCI中until循环执行docker compose logs grep异常返回255退出码
问题分析与解决方案
问题核心
你的Kafka就绪检测脚本在镜像构建耗时超过1分钟时失效,grep持续返回255错误码,但单独执行相同命令却能找到目标日志行。这是因为Kafka启动日志已被Docker日志滚动机制归档,默认的docker compose logs无法读取到历史滚动日志,同时命令中混用Docker Compose v1和v2版本也可能加剧这个问题。
具体原因
- 日志滚动导致启动日志丢失:Docker默认使用
json-file日志驱动,当日志文件达到默认大小(通常10MB)或运行时间较久时,会自动滚动归档旧日志。当镜像构建耗时≥1分钟,Kafka的启动日志已经被归档到旧日志文件中,docker compose logs默认只读取当前活跃的日志文件,因此找不到目标行。 - Docker Compose版本混用:你在启动服务时用了v1的
docker-compose up,而读取日志时用了v2的docker compose logs,两者在日志存储的兼容性上存在细微差异,可能导致部分历史日志无法被检索。 - 管道中断引发的异常:
grep -q找到匹配后会立即退出,导致上游的docker compose logs进程收到SIGPIPE信号中断输出,后续循环中可能出现日志读取异常(虽然单独执行能成功,但循环中的连续执行可能触发这个问题)。
解决方案
方案1:强制读取所有历史日志
修改就绪检测脚本,添加--tail all参数让docker compose logs输出所有日志(包括已滚动的归档日志):
until docker compose logs --tail all kafka | grep -q "started (kafka.server.KafkaServer)"; do sleep 1 done echo "Kafka is ready"
方案2:统一Docker Compose版本
将所有命令切换为v2的docker compose,避免版本混用带来的兼容性问题:
- run: name: 'Start background services' working_directory: services background: true command: docker compose up
方案3:改用更可靠的就绪检测方式
依赖日志的检测方式容易受日志滚动影响,建议使用Kafka自带的工具或端口检测来验证就绪状态:
方式A:使用Kafka主题列表命令检测(推荐)
until docker compose exec -T kafka kafka-topics.sh --list --bootstrap-server localhost:9092 > /dev/null 2>&1; do sleep 1 done echo "Kafka is ready"
(添加-T参数避免终端交互问题,适配CI环境)
方式B:使用端口检测(简易版)
until nc -z localhost 9092; do sleep 1 done echo "Kafka is ready"
注意:端口通了仅代表Kafka进程在运行,不代表服务完全就绪,生产或严格测试环境建议用方式A。
方案4:调整Docker日志滚动策略
在services/docker-compose.yml中修改Kafka的日志配置,增大日志文件大小或禁用滚动,确保启动日志不会被快速归档:
services: kafka: # 其他配置... logging: driver: "json-file" options: max-size: "100m" # 增大单日志文件大小 max-file: "5" # 保留更多归档文件
内容的提问来源于stack exchange,提问作者MasterOdin
相关产品推荐
相关产品推荐

