轻量级 CI/CD 实战(三):Kafka消费者Docker容器化部署

===================================

目录

背景与目标

在日志分析系统中,Nginx 日志通过 Filebeat 发送到 Kafka 集群,需部署一个长期运行的消费者程序,将日志解析后写入 MySQL 并缓存到 Redis。

为提升部署效率、避免环境依赖冲突,采用 Docker 容器化方案,实现:

  • 服务开机自启(--restart=always
  • 网络直连宿主机(--network host
  • 代码更新后快速重建

项目结构

项目存放于 /opt/log_consumer/,目录结构如下:
/opt/log_consumer/
├── consumer.py # 主程序:Kafka消费 + MySQL写入 + Redis缓存
├── requirements.txt # Python依赖列表
└── Dockerfile # Docker镜像构建定义

关键配置文件

Dockerfile

使用官方 python:3.9-slim 镜像,轻量且兼容性好:
# 使用轻量 Python 镜像
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY requirements.txt .

# 安装 Python 依赖(禁用缓存确保干净构建)
RUN pip install --no-cache-dir -r requirements.txt

# 复制主程序
COPY consumer.py .

# 启动命令
CMD ["python", "consumer.py"]

requirements.txt

指定项目所需Python依赖及固定版本,避免版本兼容问题:
kafka-python==2.0.2
PyMySQL==1.1.0
redis==5.0.1

consumer.py 注意事项

Kafka 消费者初始化时,必须使用集群主机名,不可用 localhost 或 127.0.0.1,否则容器内无法访问宿主机Kafka集群:
consumer = KafkaConsumer(
‘nginx-logs’,
bootstrap_servers=[“kafka1:9092”, “kafka2:9092”, “kafka3:9092”], # ← 关键!
auto_offset_reset=’latest’,
enable_auto_commit=True,
group_id=’log-consumer-group’
)

同时开发时需确保:

  1. 所有依赖库已正确导入
  2. 每行参数末尾加英文逗号 ,(避免 SyntaxError 语法错误)
  3. 异常处理完善(防止进程意外退出导致容器重启)

部署流程

清理旧容器

容器名称冲突是部署最常见错误,部署前务必先停止并删除旧容器,即使容器已停止,名称仍会被占用:
docker stop log-consumer 2>/dev/null && docker rm log-consumer 2>/dev/null

语法预检

提前校验Python代码语法,避免因语法错误导致容器启动后无限重启,无输出即表示语法正确:
cd /opt/log_consumer
python3 -m py_compile consumer.py

构建镜像

基于当前目录的Dockerfile构建自定义镜像,镜像命名为log-consumer
docker build -t log-consumer .

构建成功后,可通过 docker images 命令查看本地镜像列表,确认log-consumer镜像已生成。

启动容器

使用host网络模式让容器共享宿主机网络,实现直连Kafka集群,同时开启开机自启和后台运行:
docker run -d
–network host
–name log-consumer
–restart=always
log-consumer

参数说明

  • -d:后台运行容器
  • --network host:容器共享宿主机网络,可直接访问kafka1:9092等地址
  • --name log-consumer:指定容器唯一名称
  • --restart=always:容器异常退出/宿主机重启后自动重启容器

验证运行状态

通过查看容器实时日志,验证消费者程序是否正常启动和运行:
docker logs -f log-consumer

启动成功标志(程序输出类似内容):

启动Kafka日志消费者
OK MySQL连接成功
OK Redis连接成功
OK Kafka消费者创建成功
TARGET 开始监听日志...

容器运行状态验证

也可通过前台运行方式直接验证(运行后按Ctrl+C停止):
docker run -it –network host –rm log-consumer

常见问题排查

容器不断重启

现象

执行docker ps查看容器状态,显示log-consumer容器状态为Restarting

原因

程序启动后立即退出,核心原因包括代码语法错误、数据库/Redis/Kafka连接失败、依赖缺失等。

解决

# 先停止异常重启的容器
docker stop log-consumer
# 前台运行容器,直接查看控制台报错信息(关键排查步骤)
docker run -it --network host --rm log-consumer
# 根据前台输出的具体报错信息修复代码/环境问题后,重新构建启动

Kafka 连接失败(NoBrokersAvailable)

现象

容器日志中报错kafka.errors.NoBrokersAvailable,无法连接Kafka集群。

原因

  1. bootstrap_servers 配置为localhost:9092/127.0.0.1:9092,容器内无法解析
  2. Kafka 服务未监听外网/宿主机IP,仅监听localhost
  3. 宿主机防火墙/安全组阻断9092端口
  4. 主机名(kafka1/kafka2/kafka3)未做DNS解析或/etc/hosts映射

解决

# 1. 修正consumer.py中bootstrap_servers为集群主机名
# 2. 在宿主机测试Kafka端口连通性
telnet kafka1 9092
# 3. 确保宿主机/etc/hosts已配置Kafka主机名与IP的映射
# 4. 开放防火墙9092端口(如需要)
firewall-cmd --add-port=9092/tcp --permanent
firewall-cmd --reload

Python 语法错误

现象

容器日志报错SyntaxError: invalid syntax,但报错行代码看似无语法问题。

原因

  1. 上一行代码参数末尾缺少英文逗号(最常见原因)
  2. 代码中混入中文标点(如中文逗号、括号)
  3. 代码文件含BOM头或Windows格式的CRLF换行符
  4. 多行参数缩进不一致

典型错误示例(kafka3:9092末尾缺少逗号,导致代码行合并):
consumer = KafkaConsumer(
bootstrap_servers = [“kafka1:9092”, “kafka2:9092”, “kafka3:9092”]
‘nginx-logs’,
auto_offset_reset=’latest’,
enable_auto_commit=True,
group_id=’log-consumer-group’,
session_timeout_ms=30000,
heartbeat_interval_ms=10000
)

解决

# 1. 检查代码中的隐藏字符和格式问题
cat -A consumer.py
# 2. 再次用官方工具预检语法
python3 -m py_compile consumer.py
# 3. 手动重写可疑代码行,确保使用英文标点、缩进一致
# 4. 将文件转换为Linux换行格式(LF)
sed -i 's/\r$//' consumer.py

一键重启脚本

编写Shell脚本实现停止旧容器-删除旧容器-构建新镜像-启动新容器的一键化操作,简化代码更新后的部署流程:
#!/bin/bash
cd /opt/log_consumer

# 停旧容器,忽略容器不存在的错误
docker stop log-consumer 2>/dev/null
# 删除旧容器,忽略容器不存在的错误
docker rm log-consumer 2>/dev/null

# 构建新镜像
docker build -t log-consumer .

# 启动新容器
docker run -d --network host --name log-consumer --restart=always log-consumer

# 部署成功提示
echo "✅ 消费者容器已一键重启,查看实时日志:docker logs -f log-consumer"

脚本使用方式

# 给脚本添加执行权限
chmod +x restart_consumer.sh
# 执行一键重启
./restart_consumer.sh

总结

通过Docker容器化改造Kafka消费者程序,为日志监控系统带来了四大核心价值:

  1. 环境隔离:Python依赖库仅存在于容器内,不污染宿主机环境,避免多项目依赖冲突
  2. 快速部署:仅需6条核心命令即可完成从0到1的部署,新人可快速上手
  3. 高可用保障:通过--restart=always实现容器异常自动重启,提升服务稳定性
  4. 部署标准化:任何人拿到项目代码和Dockerfile,均可在任意安装Docker的机器上一键运行

运行环境:CentOS 7 + Docker 24.0 + Kafka 3.3