























在运行基于BSC(币安智能链)的节点集群时,遇到了一个诡异的问题:归档节点(archive node)的Docker容器显示为运行状态,但实际上内部的geth进程已经停止工作。
bash
$ sudo docker ps
CONTAINER ID IMAGE STATUS NAMES
6f96594ef9a4 myorg/chain:0.0.4 Up 2 days bsc-archive-1
容器状态显示 Up 2 days,看起来一切正常。
bash
$ sudo docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT
6f96594ef9a4 bsc-archive-1 0.00% 0B / 0B
关键异常点:
这明显不正常!归档节点正常运行时应该有持续的资源消耗。
bash
$ sudo docker exec -it 6f96594ef9a4 geth attach /root/.ethereum/geth.ipc --exec "eth.blockNumber"
cannot exec in a stopped state
$ sudo docker exec -it 6f96594ef9a4 ps aux
cannot exec in a stopped state
虽然 docker ps 显示容器运行中,但无法在容器内执行任何命令。
最后一条日志停留在昨天:
log
INFO [01-08|22:31:37.346] Re-queue blocks number=1,681,468
hash=0x2a9ad1f2fb1235dd3a84a06f42f85ef853049acbde9e1062e90b50a8a167e63e
日志停在了区块重组(re-queue blocks)操作,之后再无输出。
这是一种特殊的容器故障模式:
┌─────────────────────────────────────┐
│ Docker Container (Running) │
│ ┌───────────────────────────────┐ │
│ │ tini (PID 1) - 存活 ✓ │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ geth 进程 - 已挂 ✗ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
关键点:
从日志分析,可能的原因包括:
Re-queue blocks number=1,681,468
这个操作可能触发了:
配置中设置了:
yaml
--cache 8000 # 8GB缓存
--gcmode archive # 归档模式,不清理历史状态
--txlookuplimit 0 # 无限制事务索引
归档模式 + 大缓存 + 全量索引,内存需求极高,可能触发OOM。
从统计数据看,validator1的磁盘读取达到了345GB,archive节点可能在密集I/O时遇到了问题。
restart: always 却不重启?这是Docker的设计机制:
yaml
restart: always # 仅在容器退出时重启
restart策略只关注容器退出:
Docker无法感知容器内部进程的健康状态!
修改 docker-compose.yml,添加健康检查配置:
yaml
archive:
image: myorg/chain:0.0.4
user: "1000:1000"
command:
- --datadir
- /data
- --gcmode
- archive
# ... 其他参数 ...
restart: unless-stopped
# 核心配置:健康检查
healthcheck:
test: ["CMD-SHELL", "geth attach --exec 'eth.blockNumber' /data/geth.ipc || exit 1"]
interval: 60s # 每60秒检查一次
timeout: 30s # 30秒超时
retries: 3 # 连续失败3次才标记为unhealthy
start_period: 300s # 启动后5分钟才开始检查
volumes:
- ./data/archive-data:/data
- ./logs/archive:/data/logs
network_mode: "host"
stop_grace_period: 120s
工作原理:
unhealthy查看健康状态:
bash
$ sudo docker ps
CONTAINER ID STATUS NAMES
6f96594ef9a4 Up 2 hours (healthy) bsc-archive-1
仅有healthcheck还不够,因为Docker不会自动重启 unhealthy 的容器。需要添加一个监控容器:
yaml
services:
archive:
# ... 上面的配置 ...
healthcheck:
test: ["CMD-SHELL", "geth attach --exec 'eth.blockNumber' /data/geth.ipc || exit 1"]
interval: 60s
timeout: 30s
retries: 3
start_period: 300s
validator1:
# ... 其他验证节点 ...
# 自动治愈容器
autoheal:
image: willfarrell/autoheal:latest
container_name: autoheal
restart: always
environment:
- AUTOHEAL_CONTAINER_LABEL=all # 监控所有容器
- AUTOHEAL_INTERVAL=10 # 每10秒检查一次
- AUTOHEAL_START_PERIOD=300 # 启动后5分钟才开始监控
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 访问Docker API
工作流程:
┌─────────────────────────────────────────────────┐
│ 1. Archive容器geth进程卡死 │
│ ↓ │
│ 2. HealthCheck连续3次失败 (3分钟) │
│ ↓ │
│ 3. 容器状态变为 unhealthy │
│ ↓ │
│ 4. Autoheal检测到unhealthy状态 │
│ ↓ │
│ 5. 自动执行 docker restart bsc-archive-1 │
│ ↓ │
│ 6. 容器重启,geth进程恢复正常 │
└─────────────────────────────────────────────────┘
如果不想依赖第三方镜像,可以自己写监控脚本。
创建 ./scripts/container-monitor.sh:
bash
#!/bin/bash
# 配置
CONTAINER_NAME="bsc-archive-1"
CHECK_INTERVAL=60
LOG_FILE="/data/chain/logs/monitor.log"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 检查容器健康状态
check_container_health() {
local health=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null)
echo "$health"
}
# 检查geth进程是否存在
check_geth_process() {
docker exec "$CONTAINER_NAME" pgrep -x "geth" > /dev/null 2>&1
return $?
}
# 检查geth是否响应
check_geth_responsive() {
timeout 30 docker exec "$CONTAINER_NAME" \
geth attach --exec 'eth.blockNumber' /data/geth.ipc > /dev/null 2>&1
return $?
}
# 重启容器
restart_container() {
log "🔄 重启容器: $CONTAINER_NAME"
docker restart "$CONTAINER_NAME"
# 等待容器启动
sleep 30
# 验证重启是否成功
if docker ps | grep -q "$CONTAINER_NAME"; then
log "✅ 容器重启成功"
else
log "❌ 容器重启失败,请人工介入!"
# 可以在这里添加告警通知
fi
}
# 主监控循环
main() {
log "🚀 监控服务启动,监控容器: $CONTAINER_NAME"
while true; do
# 检查容器是否存在
if ! docker ps | grep -q "$CONTAINER_NAME"; then
log "⚠️ 容器不存在或已停止"
sleep "$CHECK_INTERVAL"
continue
fi
# 方法1:检查健康状态(如果配置了healthcheck)
health=$(check_container_health)
if [ "$health" == "unhealthy" ]; then
log "❌ 容器健康检查失败 (状态: unhealthy)"
restart_container
sleep 120 # 重启后等待2分钟再继续监控
continue
fi
# 方法2:检查geth进程
if ! check_geth_process; then
log "❌ geth进程不存在"
restart_container
sleep 120
continue
fi
# 方法3:检查geth响应性
if ! check_geth_responsive; then
log "❌ geth进程无响应"
restart_container
sleep 120
continue
fi
# 一切正常
log "✓ 容器运行正常 (health: $health)"
sleep "$CHECK_INTERVAL"
done
}
# 捕获退出信号
trap 'log "📛 监控服务停止"; exit 0' SIGTERM SIGINT
# 启动监控
main
使用systemd管理监控脚本:
创建 /etc/systemd/system/chain-monitor.service:
ini
[Unit]
Description=BSC Container Health Monitor
After=docker.service
Requires=docker.service
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/data/chain
ExecStart=/bin/bash /data/chain/scripts/container-monitor.sh
Restart=always
RestartSec=10
# 日志配置
StandardOutput=append:/data/chain/logs/monitor-service.log
StandardError=append:/data/chain/logs/monitor-service.log
[Install]
WantedBy=multi-user.target
启用服务:
bash
# 1. 创建脚本
sudo mkdir -p /data/chain/scripts
sudo vim /data/chain/scripts/container-monitor.sh
# 粘贴上面的脚本内容
# 2. 设置权限
sudo chmod +x /data/chain/scripts/container-monitor.sh
sudo chown ubuntu:ubuntu /data/chain/scripts/container-monitor.sh
# 3. 创建systemd服务
sudo vim /etc/systemd/system/chain-monitor.service
# 粘贴服务配置
# 4. 重载并启动
sudo systemctl daemon-reload
sudo systemctl enable chain-monitor
sudo systemctl start chain-monitor
# 5. 查看状态
sudo systemctl status chain-monitor
# 6. 查看日志
sudo journalctl -u chain-monitor -f
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HealthCheck | 简单,Docker原生支持 | 只标记状态,不会自动重启 | 配合其他方案使用 |
| Autoheal容器 | 配置简单,开箱即用 | 依赖第三方镜像,需要Docker socket权限 | 测试/开发环境 |
| 自定义脚本 | 完全可控,可自定义逻辑,无第三方依赖 | 需要额外维护脚本 | 生产环境(推荐) |
yaml
version: '3.8'
services:
archive:
image: myorg/chain:0.0.4
container_name: bsc-archive-1
user: "1000:1000"
command:
- --datadir
- /data
- --http
- --http.addr
- "0.0.0.0"
- --http.port
- "8555"
- --http.api
- "eth,net,web3,debug,admin,txpool"
- --syncmode
- full
- --gcmode
- archive
- --cache
- "4000" # 降低缓存,避免OOM (原来是8000)
- --txlookuplimit
- "0"
- --ipcpath
- /data/geth.ipc
- --log.file=/data/logs/node.log
- --log.rotate
- --log.maxsize=100
- --log.maxbackups=10
- --verbosity=3 # 降低日志级别 (原来是5)
volumes:
- ./data/archive-data:/data
- ./logs/archive:/data/logs
- ./config:/chain/config
network_mode: "host"
restart: unless-stopped
stop_grace_period: 120s
# 健康检查配置
healthcheck:
test: ["CMD-SHELL", "geth attach --exec 'eth.blockNumber' /data/geth.ipc || exit 1"]
interval: 60s
timeout: 30s
retries: 3
start_period: 300s
# 资源限制(可选)
deploy:
resources:
limits:
memory: 10G
reservations:
memory: 6G
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"
# 验证节点配置类似...
validator1:
# ... 配置 ...
# 自动治愈容器
autoheal:
image: willfarrell/autoheal:latest
container_name: autoheal
restart: always
environment:
- AUTOHEAL_CONTAINER_LABEL=all
- AUTOHEAL_INTERVAL=10
- AUTOHEAL_START_PERIOD=300
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
bash
# 停止现有容器
sudo docker-compose down
# 启动新配置
sudo docker-compose up -d
# 查看容器状态
sudo docker ps
bash
# 方法1:docker ps 会显示健康状态
$ sudo docker ps
CONTAINER ID STATUS NAMES
6f96594ef9a4 Up 5 minutes (healthy) osc-archive-1
# 方法2:查看详细健康检查信息
$ sudo docker inspect osc-archive-1 | grep -A 20 "Health"
bash
# 手动杀掉geth进程,测试自动恢复
sudo docker exec -it bsc-archive-1 pkill geth
# 观察容器状态变化
watch -n 5 'sudo docker ps | grep archive'
# 大约3-5分钟后,应该看到容器自动重启
bash
# 实时查看autoheal的工作日志
sudo docker logs -f autoheal
# 应该能看到类似输出:
# [2026-01-09 15:30:45] Container bsc-archive-1 is unhealthy - Restarting
# [2026-01-09 15:30:50] Container bsc-archive-1 restarted successfully
原配置可能导致内存不足:
yaml
--cache 8000 # 8GB → 改为 4000 (4GB)
--verbosity 5 # debug → 改为 3 (info)
防止单个容器耗尽系统资源:
yaml
deploy:
resources:
limits:
memory: 10G
cpus: '4'
bash
# 添加到crontab
0 2 * * * find /data/osc/logs -name "*.log" -mtime +7 -delete
在监控脚本中添加告警通知:
bash
# 发送邮件告警
send_alert() {
echo "Archive node restarted at $(date)" | \
mail -s "OSC Node Alert" admin@example.com
}
# 发送Telegram通知
send_telegram() {
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" \
-d text="⚠️ OSC Archive容器已自动重启"
}
Docker容器的"僵尸状态"是由于:
restart: always 只在容器退出时生效,无法处理内部进程故障实现完整的自动恢复机制需要三个组件:
HealthCheck → 检测问题
↓
Autoheal/Script → 执行重启
↓
restart: always → 确保重启
生产环境推荐配置:
healthcheck - 及时发现问题测试/开发环境:
除了自动重启,还应该:
通过这套完整的监控和自动恢复方案,可以大大提高BSC节点的稳定性和可用性。
相关资源:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。