HAProxy多节点配置同步脚本
HAProxy配置文件拆分
vi /usr/lib/systemd/system/haproxy.service
添加SUBCONFIG
配置目录
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "SUBCONFIG=/etc/haproxy/conf.d" "PIDFILE=/run/haproxy.pid" "EXTRAOPTS=-S /run/haproxy-master.sock"
ExecStartPre=/usr/sbin/haproxy -Ws -f $CONFIG -f $SUBCONFIG -c -q $EXTRAOPTS
ExecStart=/usr/sbin/haproxy -Ws -f $CONFIG -f $SUBCONFIG -p $PIDFILE $EXTRAOPTS
ExecReload=/usr/sbin/haproxy -Ws -f $CONFIG -f $SUBCONFIG -c -q $EXTRAOPTS
/etc/haproxy
目录结构
ubuntu@k8s-lb1:/etc/haproxy$ tree
.
├── conf.d
│ ├── k8s-ingress.blacklist
│ ├── k8s-ingress.cfg
│ ├── kafka.cfg
│ ├── kafka.whitelist
│ ├── ks-console.cfg
│ ├── kube-apiserver.cfg
│ └── sync-cfg.sh
├── errors
│ ├── 400.http
│ ├── 403.http
│ ├── 408.http
│ ├── 500.http
│ ├── 502.http
│ ├── 503.http
│ └── 504.http
└── haproxy.cfg
2 directories, 15 files
同步脚本sync-cfg.sh
#!/bin/bash
#
# HAProxy 配置文件同步脚本
# 功能:将本地 HAProxy 配置同步到远程节点并重载服务
# 作者:Sulan Huang
# 版本:1.0
#
# 配置参数
REMOTE_HOST="k8s-lb2" # 远程节点IP地址
REMOTE_USER="ubuntu" # 远程节点用户名(Ubuntu用户)
HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
HAPROXY_CONF_DIR="/etc/haproxy/conf.d"
BACKUP_DIR="/etc/haproxy/backup"
LOGFILE="$HOME/haproxy-sync.log"
SSH_KEY="$HOME/.ssh/id_rsa" # SSH私钥路径(使用当前用户的HOME目录)
# Ubuntu 环境特定配置
CURRENT_USER=$(whoami)
NEED_SUDO=true
SYSTEMCTL_CMD="sudo systemctl"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数 - 写入文件(不带颜色)
log_to_file() {
local level=$1
shift
local message="$@"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 确保日志目录存在
local log_dir=$(dirname "$LOGFILE")
if [[ ! -d "$log_dir" ]]; then
mkdir -p "$log_dir" 2>/dev/null || true
fi
# 写入日志文件(去除颜色代码)
echo "${timestamp} [$level] $message" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOGFILE" 2>/dev/null || true
}
# 颜色日志函数
log_info() {
local message="$1"
echo -e "${BLUE}[INFO]${NC} $message"
log_to_file "INFO" "$message"
}
log_success() {
local message="$1"
echo -e "${GREEN}[SUCCESS]${NC} $message"
log_to_file "SUCCESS" "$message"
}
log_warning() {
local message="$1"
echo -e "${YELLOW}[WARNING]${NC} $message"
log_to_file "WARNING" "$message"
}
log_error() {
local message="$1"
echo -e "${RED}[ERROR]${NC} $message"
log_to_file "ERROR" "$message"
}
# 通用日志函数(保持向后兼容)
log() {
local level=$1
shift
local message="$@"
log_to_file "$level" "$message"
}
# 显示使用帮助
show_help() {
cat << EOF
HAProxy 配置同步脚本
用法: $0 [选项]
选项:
-h, --host 远程主机IP地址 (默认: $REMOTE_HOST)
-u, --user 远程主机用户名 (默认: $REMOTE_USER)
-k, --key SSH私钥路径 (默认: $SSH_KEY)
-t, --test 仅测试配置文件语法,不执行同步
-f, --force 强制同步,跳过确认提示
--help 显示此帮助信息
示例:
$0 # 使用默认配置同步
$0 -h 192.168.1.101 -u ubuntu # 指定远程主机和用户
$0 -t # 仅测试配置文件
$0 -f # 强制同步,不询问确认
注意事项:
- 确保本地和远程主机都安装了 HAProxy
- 确保当前用户在两个节点上都有 sudo 权限
- 确保已配置 SSH 密钥认证:ssh-copy-id ubuntu@remote_host
- 建议先使用 -t 参数测试配置文件语法
EOF
}
# 解析命令行参数
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--host)
REMOTE_HOST="$2"
shift 2
;;
-u|--user)
REMOTE_USER="$2"
shift 2
;;
-k|--key)
SSH_KEY="$2"
shift 2
;;
-t|--test)
TEST_ONLY=true
shift
;;
-f|--force)
FORCE_SYNC=true
shift
;;
--help)
show_help
exit 0
;;
*)
log_error "未知参数: $1"
show_help
exit 1
;;
esac
done
}
# 检查依赖
check_dependencies() {
log_info "检查依赖项..."
# 检查必要的命令
local commands=("haproxy" "rsync" "ssh" "scp" "sudo")
for cmd in "${commands[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
log_error "缺少必要命令: $cmd"
exit 1
fi
done
# 检查当前用户
if [[ "$CURRENT_USER" == "root" ]]; then
NEED_SUDO=false
SYSTEMCTL_CMD="systemctl"
log_info "当前用户为 root,无需 sudo"
else
log_info "当前用户为 $CURRENT_USER,将使用 sudo 执行特权操作"
# 检查sudo权限
if ! sudo -n true 2>/dev/null; then
log_warning "检测到需要 sudo 密码,请确保有 sudo 权限"
# 测试sudo权限
if ! sudo -v; then
log_error "无法获取 sudo 权限"
exit 1
fi
fi
fi
# 检查配置文件是否存在(使用sudo读取)
if [[ "$NEED_SUDO" == true ]]; then
if ! sudo test -f "$HAPROXY_CONFIG"; then
log_error "HAProxy 主配置文件不存在: $HAPROXY_CONFIG"
exit 1
fi
if ! sudo test -d "$HAPROXY_CONF_DIR"; then
log_warning "HAProxy 配置目录不存在: $HAPROXY_CONF_DIR"
log_info "将尝试创建配置目录"
sudo mkdir -p "$HAPROXY_CONF_DIR"
fi
else
if [[ ! -f "$HAPROXY_CONFIG" ]]; then
log_error "HAProxy 主配置文件不存在: $HAPROXY_CONFIG"
exit 1
fi
if [[ ! -d "$HAPROXY_CONF_DIR" ]]; then
log_warning "HAProxy 配置目录不存在: $HAPROXY_CONF_DIR"
log_info "将尝试创建配置目录"
mkdir -p "$HAPROXY_CONF_DIR"
fi
fi
# 检查SSH密钥
if [[ ! -f "$SSH_KEY" ]]; then
log_error "SSH私钥文件不存在: $SSH_KEY"
log_info "提示:请运行 'ssh-keygen -t rsa -b 4096' 生成SSH密钥"
exit 1
fi
# 检查日志目录权限
local log_dir=$(dirname "$LOGFILE")
if [[ "$NEED_SUDO" == true ]] && [[ ! -w "$log_dir" ]]; then
log_warning "无法写入日志目录 $log_dir,尝试使用用户目录"
LOGFILE="$HOME/haproxy-sync.log"
log_info "日志文件已更改为: $LOGFILE"
fi
log_success "依赖检查通过"
}
# 测试配置文件语法
test_config() {
log_info "检查 HAProxy 配置文件语法..."
# 创建临时配置文件进行测试
local temp_config="/tmp/haproxy_test_$.cfg"
# 合并主配置文件和conf.d目录下的配置文件(使用sudo读取)
{
if [[ "$NEED_SUDO" == true ]]; then
sudo cat "$HAPROXY_CONFIG"
if sudo test -d "$HAPROXY_CONF_DIR" && [[ $(sudo ls -A "$HAPROXY_CONF_DIR"/*.cfg 2>/dev/null | wc -l) -gt 0 ]]; then
for conf_file in $(sudo ls "$HAPROXY_CONF_DIR"/*.cfg 2>/dev/null); do
if sudo test -f "$conf_file"; then
echo ""
echo "# Include: $conf_file"
sudo cat "$conf_file"
fi
done
fi
else
cat "$HAPROXY_CONFIG"
if [[ -d "$HAPROXY_CONF_DIR" ]] && [[ $(ls -A "$HAPROXY_CONF_DIR"/*.cfg 2>/dev/null | wc -l) -gt 0 ]]; then
for conf_file in "$HAPROXY_CONF_DIR"/*.cfg; do
if [[ -f "$conf_file" ]]; then
echo ""
echo "# Include: $conf_file"
cat "$conf_file"
fi
done
fi
fi
} > "$temp_config"
# 测试配置语法
if haproxy -c -f "$temp_config" 2>/dev/null; then
log_success "配置文件语法检查通过"
rm -f "$temp_config"
return 0
else
log_error "配置文件语法检查失败"
log_error "详细错误信息:"
haproxy -c -f "$temp_config" 2>&1 | while read line; do
log_error " $line"
done
rm -f "$temp_config"
return 1
fi
}
# 测试远程连接
test_remote_connection() {
log_info "测试远程连接 $REMOTE_USER@$REMOTE_HOST..."
if ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=yes "$REMOTE_USER@$REMOTE_HOST" "echo 'Connection test successful'" &>/dev/null; then
log_success "远程连接测试成功"
return 0
else
log_error "无法连接到远程主机 $REMOTE_USER@$REMOTE_HOST"
log_error "请检查网络连接、SSH密钥或主机信息"
return 1
fi
}
# 创建远程备份
create_remote_backup() {
log_info "在远程主机创建配置备份..."
local backup_timestamp=$(date '+%Y%m%d_%H%M%S')
local remote_backup_dir="/etc/haproxy/backup/$backup_timestamp"
ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "
sudo mkdir -p '$remote_backup_dir'
if sudo test -f '$HAPROXY_CONFIG'; then
sudo cp '$HAPROXY_CONFIG' '$remote_backup_dir/'
fi
if sudo test -d '$HAPROXY_CONF_DIR'; then
sudo cp -r '$HAPROXY_CONF_DIR' '$remote_backup_dir/'
fi
echo 'Backup created at: $remote_backup_dir'
" 2>/dev/null
if [[ $? -eq 0 ]]; then
log_success "远程备份创建成功: $remote_backup_dir"
return 0
else
log_error "远程备份创建失败"
return 1
fi
}
# 同步配置文件
sync_config() {
log_info "同步配置文件到远程主机..."
# 创建临时目录用于rsync
local temp_dir="/tmp/haproxy_sync_$"
mkdir -p "$temp_dir/etc/haproxy"
# 复制本地文件到临时目录(处理权限问题)
if [[ "$NEED_SUDO" == true ]]; then
sudo cp "$HAPROXY_CONFIG" "$temp_dir/etc/haproxy/"
sudo chown -R "$CURRENT_USER:$CURRENT_USER" "$temp_dir"
if sudo test -d "$HAPROXY_CONF_DIR" && [[ $(sudo ls -A "$HAPROXY_CONF_DIR" 2>/dev/null | wc -l) -gt 0 ]]; then
sudo cp -r "$HAPROXY_CONF_DIR" "$temp_dir/etc/haproxy/"
sudo chown -R "$CURRENT_USER:$CURRENT_USER" "$temp_dir"
fi
else
cp "$HAPROXY_CONFIG" "$temp_dir/etc/haproxy/"
if [[ -d "$HAPROXY_CONF_DIR" ]] && [[ $(ls -A "$HAPROXY_CONF_DIR" 2>/dev/null | wc -l) -gt 0 ]]; then
cp -r "$HAPROXY_CONF_DIR" "$temp_dir/etc/haproxy/"
fi
fi
# 同步主配置文件
if rsync -avz --delete -e "ssh -i $SSH_KEY" "$temp_dir/etc/haproxy/haproxy.cfg" "$REMOTE_USER@$REMOTE_HOST:/tmp/haproxy.cfg.new" 2>/dev/null; then
# 在远程主机上移动文件到正确位置
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo mv /tmp/haproxy.cfg.new '$HAPROXY_CONFIG'" 2>/dev/null; then
log_success "主配置文件同步成功"
else
log_error "主配置文件移动到目标位置失败"
rm -rf "$temp_dir"
return 1
fi
else
log_error "主配置文件同步失败"
rm -rf "$temp_dir"
return 1
fi
# 同步conf.d目录
if [[ -d "$temp_dir/etc/haproxy/conf.d" ]]; then
# 确保远程目录存在
ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo mkdir -p '$HAPROXY_CONF_DIR'" 2>/dev/null
if rsync -avz --delete -e "ssh -i $SSH_KEY" "$temp_dir/etc/haproxy/conf.d/" "$REMOTE_USER@$REMOTE_HOST:/tmp/haproxy_conf_d/" 2>/dev/null; then
# 在远程主机上移动文件到正确位置
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "
sudo rm -rf '$HAPROXY_CONF_DIR'/*
sudo mv /tmp/haproxy_conf_d/* '$HAPROXY_CONF_DIR/' 2>/dev/null || true
sudo rmdir /tmp/haproxy_conf_d 2>/dev/null || true
" 2>/dev/null; then
log_success "配置目录同步成功"
else
log_error "配置目录移动到目标位置失败"
rm -rf "$temp_dir"
return 1
fi
else
log_error "配置目录同步失败"
rm -rf "$temp_dir"
return 1
fi
else
log_warning "配置目录为空或不存在,跳过同步"
fi
# 清理临时目录
rm -rf "$temp_dir"
return 0
}
# 测试远程配置
test_remote_config() {
log_info "测试远程主机配置文件语法..."
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "haproxy -c -f '$HAPROXY_CONFIG'" 2>/dev/null; then
log_success "远程配置文件语法检查通过"
return 0
else
log_error "远程配置文件语法检查失败"
ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "haproxy -c -f '$HAPROXY_CONFIG'" 2>&1 | while read line; do
log_error " $line"
done
return 1
fi
}
# 重载远程HAProxy服务
reload_remote_haproxy() {
log_info "重载远程 HAProxy 服务..."
# 检查HAProxy服务状态
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo systemctl is-active haproxy" &>/dev/null; then
# 使用systemctl reload
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo systemctl reload haproxy" 2>/dev/null; then
log_success "HAProxy 服务重载成功"
# 验证服务状态
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo systemctl is-active haproxy" &>/dev/null; then
log_success "HAProxy 服务运行正常"
return 0
else
log_error "HAProxy 服务重载后状态异常"
return 1
fi
else
log_error "HAProxy 服务重载失败,尝试重启服务"
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo systemctl restart haproxy" 2>/dev/null; then
log_success "HAProxy 服务重启成功"
return 0
else
log_error "HAProxy 服务重启失败"
# 显示详细错误信息
ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo systemctl status haproxy --no-pager -l" 2>&1 | while read line; do
log_error " $line"
done
return 1
fi
fi
else
log_warning "远程 HAProxy 服务未运行,尝试启动服务"
if ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" "sudo systemctl start haproxy" 2>/dev/null; then
log_success "HAProxy 服务启动成功"
return 0
else
log_error "HAProxy 服务启动失败"
return 1
fi
fi
}
# 重载本地HAProxy服务
reload_local_haproxy() {
log_info "重载本地 HAProxy 服务..."
# 检查HAProxy服务状态
if sudo systemctl is-active haproxy &>/dev/null; then
# 使用systemctl reload
if sudo systemctl reload haproxy 2>/dev/null; then
log_success "HAProxy 服务重载成功"
# 验证服务状态
if sudo systemctl is-active haproxy &>/dev/null; then
log_success "HAProxy 服务运行正常"
return 0
else
log_error "HAProxy 服务重载后状态异常"
return 1
fi
else
log_error "HAProxy 服务重载失败,尝试重启服务"
if sudo systemctl restart haproxy 2>/dev/null; then
log_success "HAProxy 服务重启成功"
return 0
else
log_error "HAProxy 服务重启失败"
# 显示详细错误信息
sudo systemctl status haproxy --no-pager -l 2>&1 | while read line; do
log_error " $line"
done
return 1
fi
fi
else
log_warning "本地 HAProxy 服务未运行,尝试启动服务"
if sudo systemctl start haproxy 2>/dev/null; then
log_success "HAProxy 服务启动成功"
return 0
else
log_error "HAProxy 服务启动失败"
return 1
fi
fi
}
# 显示同步摘要
show_summary() {
log_info "同步摘要:"
echo " 源主机: $(hostname)"
echo " 目标主机: $REMOTE_HOST"
echo " 目标用户: $REMOTE_USER"
echo " 主配置文件: $HAPROXY_CONFIG"
echo " 配置目录: $HAPROXY_CONF_DIR"
echo " 日志文件: $LOGFILE"
}
# 主函数
main() {
echo "=========================================="
echo " HAProxy 配置同步脚本"
echo "=========================================="
# 解析命令行参数
parse_args "$@"
# 检查依赖
check_dependencies
# 测试本地配置
if ! test_config; then
log_error "本地配置文件语法错误,停止同步"
exit 1
fi
# 如果只是测试模式,直接退出
if [[ "$TEST_ONLY" == true ]]; then
log_success "测试模式:本地配置文件语法正确"
exit 0
fi
# 显示同步摘要
show_summary
# 确认同步操作
if [[ "$FORCE_SYNC" != true ]]; then
echo ""
read -p "确认执行同步操作? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "同步操作已取消"
exit 0
fi
fi
# 测试远程连接
if ! test_remote_connection; then
exit 1
fi
# 创建远程备份
if ! create_remote_backup; then
log_error "无法创建远程备份,停止同步"
exit 1
fi
# 同步配置文件
if ! sync_config; then
log_error "配置文件同步失败"
exit 1
fi
# 测试远程配置
if ! test_remote_config; then
log_error "远程配置文件语法错误,请检查"
exit 1
fi
# 重载远程HAProxy服务
if ! reload_remote_haproxy; then
log_error "远程 HAProxy 服务重载失败"
exit 1
fi
# 重载本地HAProxy服务
if ! reload_local_haproxy; then
log_error "本地 HAProxy 服务重载失败"
exit 1
fi
log_success "=========================================="
log_success "HAProxy 配置同步完成!"
log_success "=========================================="
}
# 捕获退出信号,进行清理
trap 'log_info "脚本执行中断"; exit 1' INT TERM
# 执行主函数
main "$@"