主库 (192.168.0.121)

从库 (192.168.0.116)

镜像

osixia/keepalived                                            2.0.20          593459a9e605   6 years ago    72.9MB
registry.cn-hangzhou.aliyuncs.com/qingcloudtech/postgresql   latest          01da659724f5   2 years ago    327MB

部署 Keepalived

在主库 (192.168.0.121) 执行:

# 1. 停止并删除旧容器
docker stop keepalived-master 2>/dev/null
docker rm keepalived-master 2>/dev/null

# 2. 确认配置文件存在
cat > /data/keepalived/keepalived.conf <<'EOF'
global_defs {
   router_id PG_MASTER_121
   enable_script_security
}

vrrp_script chk_postgresql {
    script "/scripts/check_pg.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_postgresql
    }
    virtual_ipaddress {
        192.168.0.200/24 dev eth0 label eth0:vip
    }
}
EOF

# 3. 确认检查脚本存在
cat > /data/keepalived/scripts/check_pg.sh <<'EOF'
#!/bin/bash
timeout 2 bash -c 'echo > /dev/tcp/127.0.0.1/5432' 2>/dev/null
exit $?
EOF
chmod +x /data/keepalived/scripts/check_pg.sh

# 4. ⚠️ 关键修复:挂载到正确的路径 /usr/local/etc/keepalived/
docker run -d --name keepalived-master \
  --restart always \
  --network host \
  --cap-add=NET_ADMIN \
  --privileged \
  -v /data/keepalived/keepalived.conf:/usr/local/etc/keepalived/keepalived.conf \
  -v /data/keepalived/scripts:/scripts \
  osixia/keepalived:2.0.20

# 5. 等待 10 秒
sleep 10

# 6. 检查 VIP 是否绑定
ip addr show eth0 | grep 192.168.0.200

在从库 (192.168.0.116) 执行:

# 1. 停止并删除旧容器
docker stop keepalived-slave 2>/dev/null
docker rm keepalived-slave 2>/dev/null

# 2. 创建配置文件 (注意 state=BACKUP, priority=90)
mkdir -p /data/keepalived/scripts
cat > /data/keepalived/keepalived.conf <<'EOF'
global_defs {
   router_id PG_SLAVE_116
   enable_script_security
}

vrrp_script chk_postgresql {
    script "/scripts/check_pg.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_postgresql
    }
    virtual_ipaddress {
        192.168.0.200/24 dev eth0 label eth0:vip
    }
}
EOF

# 3. 创建检查脚本
cat > /data/keepalived/scripts/check_pg.sh <<'EOF'
#!/bin/bash
timeout 2 bash -c 'echo > /dev/tcp/127.0.0.1/5432' 2>/dev/null
exit $?
EOF
chmod +x /data/keepalived/scripts/check_pg.sh

# 4. ⚠️ 关键修复:挂载到正确的路径
docker run -d --name keepalived-slave \
  --restart always \
  --network host \
  --cap-add=NET_ADMIN \
  --privileged \
  -v /data/keepalived/keepalived.conf:/usr/local/etc/keepalived/keepalived.conf \
  -v /data/keepalived/scripts:/scripts \
  osixia/keepalived:2.0.20

# 5. 等待 10 秒
sleep 10

# 6. 检查 VIP (从库此时不应该有 VIP)
ip addr show eth0 | grep 192.168.0.200

检查主库 VIP

# 在主库执行
ip addr show eth0

查看 Keepalived 日志确认配置加载正确

# 在主库执行
docker logs keepalived-master | grep -E "(Virtual IP|192.168.0.200|MASTER)"

测试数据库连接

PGPASSWORD=Admin@123 psql -h 192.168.0.200 -U my_app_user -d my_app_db -c "SELECT pg_is_in_recovery();"

主库已经完成,现在需要在 从库 (192.168.0.116) 上部署 Keepalived。

# 1. 创建目录
mkdir -p /data/keepalived/scripts

# 2. 创建 Keepalived 配置文件 (注意:state=BACKUP, priority=90)
cat > /data/keepalived/keepalived.conf <<'EOF'
global_defs {
   router_id PG_SLAVE_116
   enable_script_security
}

vrrp_script chk_postgresql {
    script "/scripts/check_pg.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_postgresql
    }
    virtual_ipaddress {
        192.168.0.200/24 dev eth0 label eth0:vip
    }
}
EOF

# 3. 创建健康检查脚本
cat > /data/keepalived/scripts/check_pg.sh <<'EOF'
#!/bin/bash
timeout 2 bash -c 'echo > /dev/tcp/127.0.0.1/5432' 2>/dev/null
exit $?
EOF
chmod +x /data/keepalived/scripts/check_pg.sh

# 4. 启动 Keepalived 容器 (注意挂载路径是 /usr/local/etc/keepalived/)
docker run -d --name keepalived-slave \
  --restart always \
  --network host \
  --cap-add=NET_ADMIN \
  --privileged \
  -v /data/keepalived/keepalived.conf:/usr/local/etc/keepalived/keepalived.conf \
  -v /data/keepalived/scripts:/scripts \
  osixia/keepalived:2.0.20

# 5. 等待 10 秒
sleep 10

# 6. 检查从库此时不应该有 VIP
ip addr show eth0 | grep 192.168.0.200

执行后,从库应该看不到 192.168.0.200(因为主库优先级更高)。

修复!

在主库 (192.168.0.121) 执行

# 1. 停止并删除现有 Keepalived 容器
docker stop keepalived-master && docker rm keepalived-master

# 2. 修改配置文件(移除 enable_script_security)
cat > /data/keepalived/keepalived.conf <<'EOF'
global_defs {
   router_id PG_MASTER_121
}

vrrp_script chk_postgresql {
    script "/scripts/check_pg.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_postgresql
    }
    virtual_ipaddress {
        192.168.0.200/24 dev eth0 label eth0:vip
    }
}
EOF

# 3. 重新启动 Keepalived
docker run -d --name keepalived-master \
  --restart always \
  --network host \
  --cap-add=NET_ADMIN \
  --privileged \
  -v /data/keepalived/keepalived.conf:/usr/local/etc/keepalived/keepalived.conf \
  -v /data/keepalived/scripts:/scripts \
  osixia/keepalived:2.0.20

# 4. 等待 10 秒
sleep 10

# 5. 验证 Keepalived 状态
echo "=== 主库 Keepalived 日志 ==="
docker logs keepalived-master 2>&1 | tail -20

echo "=== 主库 VIP 状态 ==="
ip addr show eth0 | grep 192.168.0.200

在从库 (192.168.0.116) 执行

# 1. 停止并删除现有 Keepalived 容器
docker stop keepalived-slave && docker rm keepalived-slave

# 2. 修改配置文件(移除 enable_script_security)
cat > /data/keepalived/keepalived.conf <<'EOF'
global_defs {
   router_id PG_SLAVE_116
}

vrrp_script chk_postgresql {
    script "/scripts/check_pg.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_postgresql
    }
    virtual_ipaddress {
        192.168.0.200/24 dev eth0 label eth0:vip
    }
}
EOF

# 3. 重新启动 Keepalived
docker run -d --name keepalived-slave \
  --restart always \
  --network host \
  --cap-add=NET_ADMIN \
  --privileged \
  -v /data/keepalived/keepalived.conf:/usr/local/etc/keepalived/keepalived.conf \
  -v /data/keepalived/scripts:/scripts \
  osixia/keepalived:2.0.20

# 4. 等待 10 秒
sleep 10

# 5. 验证 Keepalived 状态
echo "=== 从库 Keepalived 日志 ==="
docker logs keepalived-slave 2>&1 | tail -20

echo "=== 从库 VIP 状态 ==="
ip addr show eth0 | grep 192.168.0.200 || echo "✓ 正常:从库当前没有 VIP"

修复后重新测试故障切换
等主库和从库都修复完成后,在 主库 执行:

# 1. 先启动 PostgreSQL(如果还在停止状态)
docker start postgresql-master

# 2. 等待 10 秒让主库恢复 MASTER 状态
sleep 10

# 3. 确认 VIP 在主库
echo "=== 确认 VIP 在主库 ==="
ip addr show eth0 | grep 192.168.0.200

# 4. 再次模拟故障
echo "=== 再次模拟故障:停止 PostgreSQL ==="
docker stop postgresql-master

# 5. 等待 15 秒让 Keepalived 检测并切换
sleep 15

# 6. 检查主库状态(应该降级)
echo "=== 主库 Keepalived 日志(应该看到 FAULT 或 BACKUP)==="
docker logs keepalived-master 2>&1 | tail -20

echo "=== 主库 VIP 状态(应该没有 VIP)==="
ip addr show eth0 | grep 192.168.0.200 || echo "✓ 主库已释放 VIP"

然后 登录从库 检查:

# 从库应该有 VIP 了
echo "=== 从库 VIP 状态(应该有 VIP)==="
ip addr show eth0 | grep 192.168.0.200

echo "=== 从库 Keepalived 日志(应该看到 MASTER)==="
docker logs keepalived-slave 2>&1 | tail -20

检查从库是否接管 VIP
请 登录从库 (192.168.0.116) 执行:

# 1. 检查从库是否有 VIP(应该有了!)
echo "=== 从库 VIP 状态 ==="
ip addr show eth0 | grep 192.168.0.200

# 2. 查看从库 Keepalived 日志
echo "=== 从库 Keepalived 日志 ==="
docker logs keepalived-slave 2>&1 | tail -20

# 3. 测试从库 PostgreSQL 是否可连接
echo "=== 测试从库 PostgreSQL 连接 ==="
timeout 2 bash -c 'echo > /dev/tcp/192.168.0.116/5432' && echo "✓ 从库 PostgreSQL 可连接" || echo "✗ 从库 PostgreSQL 不可连接"

然后恢复主库并验证 VIP 漂回
在 主库 (192.168.0.121) 执行:

# 1. 恢复主库 PostgreSQL
echo "=== 恢复主库 PostgreSQL ==="
docker start postgresql-master

# 2. 等待 15 秒让主库优先级恢复并抢占 VIP
echo "=== 等待 15 秒让 VIP 漂回主库... ==="
sleep 15

# 3. 验证 VIP 回到主库
echo "=== 主库 VIP 状态(应该有 VIP)==="
ip addr show eth0 | grep 192.168.0.200

# 4. 查看主库 Keepalived 日志
echo "=== 主库 Keepalived 日志 ==="
docker logs keepalived-master 2>&1 | tail -20

# 5. 验证数据库可写
echo "=== 验证数据库可写 ==="
docker exec -it postgresql-master bash -c "PGPASSWORD=Admin@123 psql -h 127.0.0.1 -U my_app_user -d my_app_db -c 'SELECT pg_is_in_recovery();'"

现在测试 VIP 漂回(恢复主库)
请在 主库 (192.168.0.121) 执行:

# 1. 恢复主库 PostgreSQL
echo "=== 恢复主库 PostgreSQL ==="
docker start postgresql-master

# 2. 等待 15 秒让主库优先级恢复并抢占 VIP
echo "=== 等待 15 秒让 VIP 漂回主库... ==="
sleep 15

# 3. 验证 VIP 回到主库
echo "=== 主库 VIP 状态(应该有 VIP)==="
ip addr show eth0 | grep 192.168.0.200

# 4. 查看主库 Keepalived 日志
echo "=== 主库 Keepalived 日志 ==="
docker logs keepalived-master 2>&1 | tail -20

然后在 从库 (192.168.0.116) 执行:

# 1. 验证从库 VIP 已释放
echo "=== 从库 VIP 状态(应该没有 VIP)==="
ip addr show eth0 | grep 192.168.0.200 || echo "✓ 从库已释放 VIP"

# 2. 查看从库 Keepalived 日志
echo "=== 从库 Keepalived 日志 ==="
docker logs keepalived-slave 2>&1 | tail -15

监控告警

# 建议添加监控脚本,检测 Keepalived 状态变化
cat > /usr/local/bin/check_ha.sh <<'EOF'
#!/bin/bash
VIP="192.168.0.200"
if ip addr show eth0 | grep -q $VIP; then
    echo "本机持有 VIP"
else
    echo "警告:本机未持有 VIP"
fi
EOF
chmod +x /usr/local/bin/check_ha.sh
bash /usr/local/bin/check_ha.sh

更多推荐