Harbor(外置db+redis) + K8s 高可用集群实施手册(独立LB + Calico + kubeasz版)二(harbor内置db确认版本二进制安装高可用pgsql15.15集群)
Harbor(外置db+redis) + K8s 高可用集群实施手册(独立LB + Calico + kubeasz版)一-CSDN博客
个人更新难免有疏漏,有建议或者疏漏也请留言,共同进步。
3.部署harbor(10.0.0.104/10.0.0.105)
3.1介绍参考:
https://blog.csdn.net/m0_66705547/article/details/143044790?spm=1011.2415.3001.5331
3.2下载harbor
wget https://github.com/goharbor/harbor/releases/download/v2.14.2/harbor-offline-installer-v2.14.2.tgz
#国内加速地址
wget https://githubfast.com/goharbor/harbor/releases/download/v2.14.2/harbor-offline-installer-v2.14.2.tgz
3.3安装 docker docker compose (10.0.0.104/105)
3.3.1更新阿里云镜像源
#!/bin/bash
# 配置 Ubuntu 24.04 阿里云镜像源脚本
# 说明: 本脚本将配置 Ubuntu 24.04 (noble) 使用阿里云镜像源
# 检查是否为 root 用户
if [ "$(id -u)" -ne 0 ]; then
echo "错误: 该脚本需要 root 权限才能执行。请使用 sudo 运行此脚本。"
exit 1
fi
# 检查是否为 Ubuntu 24.04
source /etc/os-release
if [ "$VERSION_ID" != "24.04" ] || [ "$ID" != "ubuntu" ]; then
echo "警告: 检测到系统可能不是 Ubuntu 24.04 (noble)"
echo "当前系统: $PRETTY_NAME"
read -p "是否继续配置阿里云镜像源? (y/n): " confirm
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
echo "已取消配置。"
exit 1
fi
fi
# 备份原有的 sources.list
echo "正在备份原有的 sources.list 文件到/etc/apt/sources.list.d/ubuntu.sources.bak..."
cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak
if [ $? -ne 0 ]; then
echo "错误: 备份 sources.list 文件失败。"
exit 1
fi
echo "备份完成。"
# 写入阿里云镜像源
echo "正在配置阿里云镜像源..."
sudo sed -i "s@http://.*archive.ubuntu.com@https://mirrors.aliyun.com/@g" /etc/apt/sources.list.d/ubuntu.sources
sudo sed -i "s@http://.*security.ubuntu.com@https://mirrors.aliyun.com/@g" /etc/apt/sources.list.d/ubuntu.sources
if [ $? -ne 0 ]; then
echo "错误: 写入镜像源配置失败。"
echo "正在恢复备份..."
cp /etc/apt/sources.list.d/ubuntu.sources.bak /etc/apt/sources.list.d/ubuntu.sources
exit 1
fi
echo "阿里云镜像源配置完成。"
# 更新 apt 缓存
echo "正在更新软件包列表..."
apt update
if [ $? -ne 0 ]; then
echo "警告: apt update 执行失败,可能是网络问题。"
echo "建议检查网络连接后手动执行 'sudo apt update'"
fi
echo ""
echo "=============================================="
echo "阿里云镜像源配置成功完成!"
echo "原始 sources.list 已备份为 /etc/apt/sources.list.d/ubuntu.sources.bak"
echo "如需恢复原配置,请执行: sudo cp /etc/apt/sources.list.d/ubuntu.sources.bak /etc/apt/sources.list.d/ubuntu.sources"
echo "=============================================="
3.2.2 安装 docker docker compose (10.0.0.104/105)
#!/bin/bash
# =============================================================================
# Docker 和 Docker Compose 安装脚本 (APT + 二进制)
#
# 此脚本使用 APT 包管理器从阿里云镜像源安装 Docker CE、containerd 和相关插件,
# 并从二进制文件安装独立的 Docker Compose。
# 支持 Ubuntu/Debian 系统。
# 脚本执行 root 权限检查,设置阿里云镜像源,添加 Docker GPG 密钥,
# 安装 Docker 组件,安装 Docker Compose,将当前用户添加到 docker 组,
# 并包含全面的错误处理。
#
# 使用方法:
# 1. chmod +x install-docker-apt.sh
# 2. sudo ./install-docker-apt.sh
#
# =============================================================================
set -e # 如果命令退出状态非零,则立即退出
# Colors for output / 输出颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color / 无颜色
# Print colored output / 打印带颜色的输出
print_info() {
echo -e "${BLUE}[信息]${NC} $1"
}
print_success() {
echo -e "${GREEN}[成功]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[警告]${NC} $1"
}
print_error() {
echo -e "${RED}[错误]${NC} $1"
}
# Check if running as root / 检查是否以 root 权限运行
check_root_privileges() {
print_info "检查 root 权限..."
if [[ $EUID -eq 0 ]]; then
print_success "以 root 身份运行"
else
print_error "此脚本必须以 root 或 sudo 运行"
exit 1
fi
}
# Check if running on Ubuntu/Debian / 检查是否运行在 Ubuntu/Debian 上
check_os() {
print_info "检查操作系统..."
if [[ -f /etc/debian_version ]] || grep -qi "ubuntu" /etc/os-release; then
print_success "检测到 Debian/Ubuntu 系统"
else
print_error "此脚本仅支持 Ubuntu/Debian 系统。"
exit 1
fi
}
# Install necessary system tools / 安装必要的系统工具
install_prerequisites() {
print_info "安装必要的系统工具 (ca-certificates, curl, gnupg, apt-transport-https)..."
apt-get update
apt-get install -y ca-certificates curl gnupg apt-transport-https
print_success "系统工具安装完成"
}
# Add Docker's official GPG key / 添加 Docker 的官方 GPG 密钥
add_docker_gpg_key() {
print_info "添加 Docker 的 GPG 公钥..."
# Create directory for keys / 创建密钥目录
install -m 0755 -d /etc/apt/keyrings
# Download and install GPG key from Aliyun mirror / 从阿里云镜像下载并安装 GPG 密钥
if ! curl -fsSL "https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg; then
print_error "下载或安装 Docker GPG 密钥失败"
exit 1
fi
# Set correct permissions / 设置正确权限
chmod a+r /etc/apt/keyrings/docker.gpg
print_success "Docker GPG 密钥已添加"
}
# Add Docker repository / 添加 Docker 仓库
add_docker_repository() {
print_info "添加 Docker 软件源..."
# Determine architecture / 确定架构
ARCH=$(dpkg --print-architecture)
# Determine OS codename / 确定 OS 代号
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS_CODENAME="$VERSION_CODENAME"
else
print_error "无法确定 OS 代号 (/etc/os-release 不存在)"
exit 1
fi
# Write repository info / 写入仓库信息
REPO_URL="deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu ${OS_CODENAME} stable"
if ! echo "$REPO_URL" | tee /etc/apt/sources.list.d/docker.list > /dev/null; then
print_error "写入 Docker 仓库信息失败"
exit 1
fi
print_success "Docker 仓库已添加"
}
# Update package index / 更新包索引
update_package_index() {
print_info "更新 APT 包索引..."
apt-get update
print_success "APT 包索引已更新"
}
# Install Docker packages / 安装 Docker 包
install_docker_packages() {
print_info "安装 Docker CE, containerd.io, 和相关插件..."
# Install the packages / 安装包
if ! apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin; then
print_error "Docker 包安装失败"
exit 1
fi
print_success "Docker CE, containerd.io, 和 docker-buildx-plugin 已安装"
}
# Install Docker Compose binary / 安装 Docker Compose 二进制文件
install_docker_compose() {
print_info "正在安装 Docker Compose v5.0.2 (二进制文件)..."
# Download Docker Compose binary / 下载 Docker Compose 二进制文件
COMPOSE_URL="https://github.com/docker/compose/releases/download/v5.0.2/docker-compose-linux-x86_64"
COMPOSE_BIN="/usr/local/bin/docker-compose"
if ! curl -L -o "$COMPOSE_BIN" "$COMPOSE_URL"; then
print_error "下载 Docker Compose 失败"
exit 1
fi
# Set executable permissions / 设置可执行权限
chmod +x "$COMPOSE_BIN"
print_success "Docker Compose (二进制文件) 安装成功"
}
# Start and enable Docker service / 启动并启用 Docker 服务
setup_docker_service() {
print_info "启动并启用 Docker 服务..."
# Enable and start Docker service / 启用并启动 Docker 服务
systemctl enable docker
systemctl start docker
print_success "Docker 服务已启动并启用"
}
# Add current user to docker group / 将当前用户添加到 docker 组
add_user_to_docker_group() {
print_info "将当前用户添加到 docker 组..."
# Get the current user (before sudo) / 获取当前用户(sudo 之前)
ORIGINAL_USER=$(logname 2>/dev/null || whoami | awk '{print $1}')
# Create docker group if it doesn't exist / 如果不存在则创建 docker 组
if ! getent group docker > /dev/null 2>&1; then
print_info "创建 docker 组..."
groupadd docker
fi
# Add user to docker group / 将用户添加到 docker 组
usermod -aG docker "$ORIGINAL_USER"
print_success "已将 $ORIGINAL_USER 添加到 docker 组"
print_warning "您需要注销并重新登录才能使组更改生效。"
print_info "或者,您可以运行: newgrp docker"
}
# Verify installations / 验证安装
verify_installations() {
print_info "验证安装..."
# Check Docker version / 检查 Docker 版本
print_info "检查 Docker 版本..."
if command -v docker >/dev/null 2>&1; then
DOCKER_VERSION=$(docker --version 2>/dev/null || echo "未知")
print_success "Docker 版本: $DOCKER_VERSION"
else
print_error "Docker 安装验证失败"
exit 1
fi
# Check Docker Compose version (binary) / 检查 Docker Compose 版本(二进制文件)
print_info "检查 Docker Compose (二进制文件) 版本..."
if command -v docker-compose >/dev/null 2>&1; then
COMPOSE_VERSION=$(docker-compose --version 2>/dev/null || echo "未知")
print_success "Docker Compose (二进制文件) 版本: $COMPOSE_VERSION"
else
print_warning "Docker Compose (二进制文件) 安装验证失败或未安装 (这可能不是错误,取决于您的需求)"
# Optionally, you could make this an error if docker-compose is strictly required
# print_error "Docker Compose (二进制文件) 安装验证失败"
# exit 1
fi
# Check Docker Compose plugin version / 检查 Docker Compose 插件版本
print_info "检查 Docker Compose (插件) 版本..."
if docker compose version >/dev/null 2>&1; then
PLUGIN_COMPOSE_VERSION=$(docker compose version 2>/dev/null || echo "未知")
print_success "Docker Compose (插件) 版本: $PLUGIN_COMPOSE_VERSION"
else
print_warning "Docker Compose (插件) 安装验证失败或未安装 (这可能不是错误,取决于您的需求)"
fi
# Test Docker daemon / 测试 Docker 守护进程
print_info "测试 Docker 守护进程..."
if timeout 10 docker info >/dev/null 2>&1; then
print_success "Docker 守护进程运行正常"
else
print_error "Docker 守护进程测试失败"
exit 1
fi
print_success "所有验证都通过了!"
}
# Main function / 主函数
main() {
print_info "开始 Docker (APT) 和 Docker Compose (二进制) 安装..."
print_info "日期: $(date)"
check_root_privileges # 检查 root 权限
check_os # 检查操作系统
install_prerequisites # 安装必要工具
add_docker_gpg_key # 添加 GPG 密钥
add_docker_repository # 添加仓库
update_package_index # 更新包索引
install_docker_packages # 安装 Docker 包
install_docker_compose # 安装 Docker Compose 二进制文件
setup_docker_service # 设置并启动 Docker 服务
add_user_to_docker_group # 将用户添加到 docker 组
verify_installations # 验证安装结果
print_info ""
print_success "安装成功完成!"
print_info ""
print_info "下一步操作:"
print_info "1. 注销并重新登录(或运行: newgrp docker)以便无需 sudo 即可使用 Docker"
print_info "2. 测试 Docker: docker run hello-world"
print_info "3. 测试 Docker Compose (插件): docker compose version"
print_info "4. 测试 Docker Compose (二进制): docker-compose --version"
print_info ""
print_info "Docker 更多信息: https://docs.docker.com/"
print_info "Docker Compose 更多信息: https://docs.docker.com/compose/"
}
# Run main function / 运行主函数
main "$@"
3.3核实内置pgsql,redis版本
尽量兼容自带数据库大版本
#解压
tar xzvf harbor-offline-installer-v2.14.2.tgz
cd harbor/
#加载镜像
docker load -i harbor.v2.14.2.tar.gz
#查看pgsql版本
docker images
docker run --rm goharbor/harbor-db:v2.14.2 postgres --version
#注意下面这行
2026-02-10 06:00:03.757 UTC [1] LOG: starting PostgreSQL 15.15 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 12.2.0, 64-bit
#查看redis版本
docker run --rm goharbor/redis-photon:v2.14.2 redis-server --version
Redis server v=7.2.11 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=7d2a79c22df711d4
#核实系统自带的版本
root@DESKTOP-B1I6R18:~# apt list -a postgresql
Listing... Done
postgresql/noble-updates 16+257build1.1 all
postgresql/noble 16+257build1 all
root@DESKTOP-B1I6R18:~# apt list -a redis
Listing... Done
redis/noble-updates,noble-security 5:7.0.15-1ubuntu0.24.04.2 all
redis/noble 5:7.0.15-1build2 all
#pg为16.11 redis为7.0.15 由于版本相差不大理论上也不会有bug。稳妥起见直接二进制安装对应版本
3.4编译部署pgsql集群(1主一从)
3.4.1基于流复制部署pgsql 集群

3.4.2流复制类似于MySQL的的同步机制,支持以下两种同步机制
异步流复制
同步流复制
在实际生产环境中,建议需要根据环境的实际情况来选择同步或异步模式,同步模式需要等待备库的写盘才能返回成功,如此一来,在主备库复制正常的情况下,能够保证备库的数据不会丢失,但是带来的一个负面问题,一旦备库宕机,主库就会挂起而无法进行正常操作,即在同步模式中,备库对主库的有很大的影响,而异步模式就不会。因此,在生产环境中,大多会选择异步复制模式,而非同步模式.
3.4.3 流复制特点
延迟极低,不怕大事务
支持断点续传
支持多副本
配置简单
备库与主库物理完全一致,并支持只读
3.4.4 PostgreSQL 15.15 高可用集群完整部署
1. 系统基础准备与优化
cat > set_pgos.sh <<eof
#!/bin/bash
# =============================================
# 系统优化配置脚本(专为 PostgreSQL 优化)
# 注意:必须以 root 用户运行!
# =============================================
# 颜色定义
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
# 检查是否为 root 用户
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}错误:此脚本必须以 root 权限运行!${NC}" >&2
exit 1
fi
# 设置 PostgreSQL 用户名(请根据实际情况修改)
PG_USER="postgres"
# 检查 PG_USER 是否存在
if ! id "$PG_USER" &>/dev/null; then
echo -e "${YELLOW}警告:用户 '$PG_USER' 不存在,仍将继续配置 limits.conf...${NC}"
fi
echo -e "${YELLOW}执行系统优化配置...${NC}"
# 1. 内核参数优化(追加到 /etc/sysctl.conf)
cat >> /etc/sysctl.conf << EOF
# === PostgreSQL 推荐内核参数 (added by $(basename $0) on $(date)) ===
kernel.shmmax = 17179869184 # 16GB shared memory max size
kernel.shmall = 4194304 # shared memory total pages (page size * this = bytes)
kernel.shmmni = 4096 # max number of shared memory segments
kernel.sem = 250 256000 32 1024 # semaphores: SEMMSL SEMMNS SEMOPM SEMMNI
net.core.rmem_max = 16777216 # max receive buffer size
net.core.wmem_max = 16777216 # max send buffer size
net.ipv4.tcp_wmem = 4096 65536 16777216 # min/default/max send buffer
net.ipv4.tcp_rmem = 4096 87380 16777216 # min/default/max recv buffer
vm.swappiness = 1 # 减少交换分区使用(1 表示几乎不用 swap)
vm.dirty_ratio = 15 # 脏页占内存百分比上限(同步写入触发点)
vm.dirty_background_ratio = 5 # 后台异步写入脏页的阈值
EOF
# 应用新的 sysctl 参数
echo -e "${YELLOW}正在应用新的内核参数...${NC}"
if sysctl -p; then
echo -e "${GREEN}✓ 内核参数已成功加载。${NC}"
else
echo -e "${RED}✗ 应用 sysctl 参数失败,请手动检查 /etc/sysctl.conf。${NC}"
exit 1
fi
# 2. 文件描述符与进程数限制优化
cat >> /etc/security/limits.conf << EOF
# === PostgreSQL 用户资源限制 (added by $(basename $0) on $(date)) ===
${PG_USER} soft nofile 65536
${PG_USER} hard nofile 65536
${PG_USER} soft nproc 32768
${PG_USER} hard nproc 32768
EOF
echo -e "${GREEN}✓ 系统优化配置已完成!${NC}"
echo -e "${YELLOW}注意:${NC}新 limits 配置将在 ${PG_USER} 下次登录时生效。"
echo "建议重启系统或重新登录 PostgreSQL 用户以确保 ulimit 生效。"
eof
1.2 I/O 调度器优化
cat set_NUMA.sh
#!/bin/bash
set -e
echo "=== 系统优化:关闭 NUMA + 智能设置 I/O 调度器 ==="
# 1. 检测当前是否已关闭 NUMA
if grep -q "numa=off" /proc/cmdline; then
echo "[✓] NUMA 已关闭"
else
echo "[!] NUMA 未关闭,将修改 GRUB 配置(需重启生效)"
# 备份原配置
cp /etc/default/grub /etc/default/grub.bak.$(date +%Y%m%d)
# 添加 numa=off 参数(避免重复添加)
if grep -q "GRUB_CMDLINE_LINUX_DEFAULT" /etc/default/grub; then
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"/GRUB_CMDLINE_LINUX_DEFAULT="\1 numa=off"/' /etc/default/grub
else
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet splash numa=off"' >> /etc/default/grub
fi
# 更新 GRUB
if command -v update-grub &>/dev/null; then
update-grub
elif command -v grub2-mkconfig &>/dev/null; then
grub2-mkconfig -o /boot/grub2/grub.cfg
fi
echo "[✓] GRUB 已更新,请重启系统使 NUMA 关闭生效"
fi
# 2. 为所有磁盘设置 I/O 调度器(临时生效)
echo -e "\n=== 设置 I/O 调度器 ==="
for disk in /sys/block/sd* /sys/block/nvme*; do
[[ -d "$disk" ]] || continue
dev=$(basename "$disk")
# 跳过分区(只处理磁盘设备)
[[ "$dev" =~ [0-9]$ ]] && continue
# 检测是否为旋转磁盘(1=机械盘,0=SSD)
if [[ -f "$disk/queue/rotational" ]]; then
rotational=$(cat "$disk/queue/rotational")
else
rotational=0 # NVMe 默认视为 SSD
fi
# 选择调度器(Ubuntu 24.04 内核 6.x 使用 mq-deadline/none)
if [[ "$rotational" -eq 1 ]]; then
scheduler="mq-deadline"
else
scheduler="none" # 替代 noop
fi
# 检查调度器是否可用
if [[ -f "$disk/queue/scheduler" ]]; then
available=$(cat "$disk/queue/scheduler")
if [[ "$available" == *"$scheduler"* ]]; then
echo "$scheduler" > "$disk/queue/scheduler" 2>/dev/null && \
echo "[✓] $dev ($([[ $rotational -eq 1 ]] && echo 'HDD' || echo 'SSD')) → $scheduler" || \
echo "[✗] $dev: 无法设置调度器(权限不足?)"
else
echo "[!] $dev: $scheduler 不可用,可用选项: $available"
fi
fi
done
# 3. 永久生效:创建 udev 规则
echo -e "\n=== 配置永久生效规则 ==="
cat > /etc/udev/rules.d/60-io-scheduler.rules <<'EOF'
# SSD/NVMe 使用 none 调度器
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
# 机械盘使用 mq-deadline
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"
EOF
udevadm control --reload-rules && udevadm trigger
echo "[✓] udev 规则已部署,新插拔设备将自动应用调度器"
# 4. 验证当前状态
echo -e "\n=== 当前调度器状态 ==="
for disk in /sys/block/sd* /sys/block/nvme*; do
[[ -d "$disk" && ! "$(basename $disk)" =~ [0-9]$ ]] || continue
dev=$(basename "$disk")
sched=$(cat "$disk/queue/scheduler" 2>/dev/null | sed -n 's/.*\[\(.*\)\].*/\1/p')
rot=$(cat "$disk/queue/rotational" 2>/dev/null)
type=$([[ "$rot" == "1" ]] && echo "HDD" || echo "SSD")
echo "$dev: $type → [$sched]"
done
echo -e "\n=== 操作完成 ==="
echo "• NUMA: 已配置(重启后生效)"
echo "• I/O 调度器: 已临时设置 + udev 永久规则部署"
echo "• 请执行 'sudo reboot' 使 NUMA 设置完全生效"
1.2.1使用dns私服
见 2.2.2.1小节
Harbor(外置db+redis) + K8s 高可用集群实施手册(独立LB + Calico + kubeasz版)一(初始化+ dns私服+minio集群)-CSDN博客
2. PostgreSQL 二进制安装
2.1 编译安装 PostgreSQL(所有数据库节点)
vim install_pg15.sh
#!/bin/bash
set -euo pipefail
POSTGRESQL_VERSION="15.15"
INSTALL_DIR="/apps/pgsql"
DATA_DIR="/pgsql/data"
PGHOME="$INSTALL_DIR"
SOCKET_DIR="/pgsql/run"
PARENT_DIR=$(dirname "$DATA_DIR")
DB_USER="postgres"
color() {
local msg="$1"
local status="$2"
local RES_COL=60
printf "%-${RES_COL}s" "$msg"
if [[ "$status" == "0" || "$status" == "success" ]]; then
echo -e "[\033[1;32m OK \033[0m]"
elif [[ "$status" == "1" || "$status" == "failure" ]]; then
echo -e "[\033[1;31mFAILED\033[0m]"
else
echo -e "[\033[1;33mWARNING\033[0m]"
fi
}
if [[ $EUID -ne 0 ]]; then
color "请以 root 用户运行此脚本!" 1
exit 1
fi
if [[ -f /etc/os-release ]]; then
source /etc/os-release
else
color "无法识别操作系统" 1
exit 1
fi
CPUs=$(nproc 2>/dev/null || grep -c '^processor' /proc/cpuinfo || echo 2)
[[ -z "$CPUs" ]] && CPUs=2
install_deps() {
color "正在安装编译依赖..." 2
if [[ "$ID" == "centos" || "$ID" == "rocky" || "$ID" == "rhel" ]]; then
yum install -y gcc make readline-devel zlib-devel tar wget
elif [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
apt update
apt install -y gcc make libreadline-dev zlib1g-dev tar wget libssl-dev
else
color "不支持的操作系统: $ID" 1
exit 1
fi
}
install_postgresql() {
local tarball="postgresql-${POSTGRESQL_VERSION}.tar.gz"
local url="https://mirrors.aliyun.com/postgresql/source/v${POSTGRESQL_VERSION}/${tarball}"
mkdir -p "${INSTALL_DIR%/*}" "${DATA_DIR%/*}"
if [[ ! -f "$tarball" ]]; then
color "正在从阿里云下载 PostgreSQL ${POSTGRESQL_VERSION}..." 2
wget -O "$tarball" "$url" || { color "下载失败!" 1; exit 1; }
fi
tar -xf "$tarball"
cd "postgresql-${POSTGRESQL_VERSION}"
color "配置编译选项..." 2
./configure --prefix="${INSTALL_DIR}" --with-openssl
color "编译 PostgreSQL(使用 $CPUs 核心)..." 2
make -j "$CPUs" world
color "安装 PostgreSQL..." 2
make install-world
if ! id "$DB_USER" &>/dev/null; then
useradd -m -s /bin/bash -d "/home/$DB_USER" "$DB_USER"
fi
mkdir -p "$DATA_DIR"
chown -R "$DB_USER:$DB_USER" "$PARENT_DIR"
cat > /etc/profile.d/pgsql.sh <<EOF
export PGHOME=${INSTALL_DIR}
export PATH=\${PGHOME}/bin:\$PATH
export PGDATA=${DATA_DIR}
export PGHOST=${SOCKET_DIR} # ← 关键:让 psql 默认用这个 socket
export PGUSER=postgres
export MANPATH="${PGHOME}/share/man${MANPATH:+:${MANPATH}}"
alias pgstart='pg_ctl -D \$PGDATA start'
alias pgstop='pg_ctl -D \$PGDATA stop'
alias pgrestart='pg_ctl -D \$PGDATA restart'
alias pgstatus='pg_ctl -D \$PGDATA status'
EOF
chmod +x /etc/profile.d/pgsql.sh
sleep 1
if [[ -z "$(ls -A "$DATA_DIR" 2>/dev/null)" ]]; then
color "初始化数据库集群..." 2
su - "$DB_USER" -c "${INSTALL_DIR}/bin/initdb -D '${DATA_DIR}' --locale=en_US.UTF-8 -E UTF8"
else
color "数据目录非空,跳过 initdb。" 2
fi
}
start_service() {
color "配置 systemd 服务..." 2
cat > /lib/systemd/system/postgresql.service <<EOF
[Unit]
Description=PostgreSQL database server
After=network.target
[Service]
Type=forking
User=${DB_USER}
Group=${DB_USER}
ExecStart=${INSTALL_DIR}/bin/pg_ctl -D ${DATA_DIR} -l ${DATA_DIR}/logfile start
ExecStop=${INSTALL_DIR}/bin/pg_ctl -D ${DATA_DIR} stop
ExecReload=${INSTALL_DIR}/bin/pg_ctl -D ${DATA_DIR} reload
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now postgresql.service
if systemctl is-active --quiet postgresql.service; then
color "PostgreSQL ${POSTGRESQL_VERSION} 安装并启动成功!" 0
echo "--------------------------------------------------"
echo "安装目录: $INSTALL_DIR"
echo "数据目录: $DATA_DIR"
echo "服务命令: systemctl start|stop|restart postgresql"
echo "切换用户: su - postgres"
echo "查看状态: pgstatus (需重新登录或 source /etc/profile)"
echo "--------------------------------------------------"
else
color "PostgreSQL 启动失败,请检查日志!" 1
exit 1
fi
}
main() {
install_deps
install_postgresql
start_service
}
main "$@"
source /etc/profile.d/pgsql.sh
3. 主节点配置(10.0.0.102)
#!/bin/bash
# configure_primary.sh - PostgreSQL 15.15 主库配置脚本
set -euo pipefail
DATA_DIR="/pgsql/data"
INSTALL_DIR="/apps/pgsql"
DB_USER="postgres"
PRIMARY_IP=$(ip -4 route get 8.8.8.8 2>/dev/null | awk '{print $7; exit}' || hostname -I 2>/dev/null | cut -d' ' -f1)
STANDBY_IP="10.0.0.103" #按实际使用更改
STANDBY_NAME="standby1"
# 生成密码并保存到变量和文件
REPL_PASSWORD=$(openssl rand -base64 12)
color() {
local msg="$1"; local status="$2"
printf "%-60s" "$msg"
if [[ "$status" == "0" ]]; then
echo -e "[\033[1;32m OK \033[0m]"
else
echo -e "[\033[1;31mFAILED\033[0m]"
fi
}
if [[ $EUID -ne 0 ]]; then
color "请以 root 用户运行!" 1
exit 1
fi
# 检查数据库是否已初始化
color "检查数据库是否已初始化..." 0
if [ ! -d "${DATA_DIR}/base" ]; then
color "❌ 数据库未初始化!" 1
echo "请先运行安装脚本安装PostgreSQL"
exit 1
fi
# 确保必要目录存在
color "确保必要目录存在..." 0
mkdir -p "${DATA_DIR}/pg_wal_archive" "${DATA_DIR}/pg_log" /pgsql/run
chown -R "$DB_USER:$DB_USER" "${DATA_DIR}/pg_wal_archive" "${DATA_DIR}/pg_log" /pgsql/run
# 应用主库 postgresql.conf
color "应用主库 postgresql.conf..." 0
cat > "${DATA_DIR}/postgresql.conf" <<EOF
# 主库核心配置
listen_addresses = '*'
port = 5432
max_connections = 200
shared_buffers = 4GB
# 流复制配置
wal_level = replica
max_wal_senders = 5
max_replication_slots = 5
hot_standby = off # 主库不需要此参数,但保留以防万一
# 归档配置
archive_mode = on
archive_command = 'test ! -f ${DATA_DIR}/pg_wal_archive/%f && cp %p ${DATA_DIR}/pg_wal_archive/%f'
# 日志配置
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_timezone = 'Asia/Shanghai'
EOF
chown "$DB_USER:$DB_USER" "${DATA_DIR}/postgresql.conf"
# 配置 pg_hba.conf
color "配置 pg_hba.conf..." 0
cat > "${DATA_DIR}/pg_hba.conf" <<EOF
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
host replication repl ${STANDBY_IP}/32 scram-sha-256
host all repl ${STANDBY_IP}/32 scram-sha-256
EOF
chown "$DB_USER:$DB_USER" "${DATA_DIR}/pg_hba.conf"
# 重启服务使配置生效
color "重启 PostgreSQL 服务使配置生效..." 0
systemctl restart postgresql || {
color "❌ 重启服务失败!" 1
echo "查看详细错误:"
journalctl -u postgresql -n 50 --no-pager
exit 1
}
# 等待服务就绪
color "等待 PostgreSQL 完全重启..." 0
for i in {1..20}; do
if su - "$DB_USER" -c "${INSTALL_DIR}/bin/pg_isready -h localhost -p 5432" >/dev/null 2>&1; then
break
fi
sleep 2
done
if ! su - "$DB_USER" -c "${INSTALL_DIR}/bin/pg_isready -h localhost -p 5432"; then
color "❌ PostgreSQL 启动失败,配置可能有误" 1
echo "请检查日志:"
journalctl -u postgresql -n 50 --no-pager | grep -A 20 "ERROR\|FATAL"
exit 1
fi
# 创建复制用户
color "创建复制用户 repl..." 0
su - "$DB_USER" -c "${INSTALL_DIR}/bin/psql -c \"DO \\$\\$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_user WHERE usename = 'repl') THEN
CREATE USER repl WITH REPLICATION LOGIN ENCRYPTED PASSWORD '${REPL_PASSWORD}';
ELSE
ALTER USER repl WITH REPLICATION ENCRYPTED PASSWORD '${REPL_PASSWORD}';
END IF;
END
\\$\\$;\" postgres"
# ========================
# 7. 完成提示 - 修复:现在正确显示密码
# ========================
color "✅ 主库配置完成!" 0
echo "--------------------------------------------------"
echo "主库 IP: ${PRIMARY_IP}"
echo "复制用户: repl"
echo "复制密码: ${REPL_PASSWORD}"
echo "从库名称: ${STANDBY_NAME}"
echo "WAL归档目录: ${DATA_DIR}/pg_wal_archive"
echo "--------------------------------------------------"
echo "请将上述信息保存好,备库配置时需要使用复制密码!"
echo "--------------------------------------------------"
4. 从节点配置(10.0.0.103)
#!/bin/bash
# configure_standby_optimized.sh - PostgreSQL 15.15 备库配置脚本
set -euo pipefail
DATA_DIR="/pgsql/data"
TEMP_BACKUP_DIR="/tmp/pg_backup_temp_$(date +%s)"
ARCHIVE_DIR="${DATA_DIR}/pg_wal_archive"
INSTALL_DIR="/apps/pgsql"
DB_USER="postgres"
PRIMARY_IP="10.0.0.24"
STANDBY_IP=$(hostname -I | awk '{print $1}')
REPL_USER="repl"
REPL_PASSWORD=""
color() {
local msg="$1"; local status="$2"
printf "%-60s" "$msg"
if [[ "$status" == "0" ]]; then
echo -e "[\033[1;32m OK \033[0m]"
else
echo -e "[\033[1;31mFAILED\033[0m]"
fi
}
if [[ $EUID -ne 0 ]]; then
color "❌ 请以 root 用户运行!" 1
exit 1
fi
# 获取复制密码
echo "请输入主节点复制用户的密码(从主节点 configure_primary.sh 输出中获取):"
read -s REPL_PASSWORD
echo
if [[ -z "$REPL_PASSWORD" ]]; then
color "❌ 复制密码不能为空!" 1
exit 1
fi
# 测试主库连接
color "测试主库连接..." 0
if ! timeout 10 bash -c "echo > /dev/tcp/${PRIMARY_IP}/5432" 2>/dev/null; then
color "❌ 无法连接到主库 ${PRIMARY_IP}:5432!" 1
exit 1
fi
# 验证主库复制用户连接
color "验证复制用户连接..." 0
if su - "$DB_USER" -c "PGPASSWORD='${REPL_PASSWORD}' /apps/pgsql/bin/pg_isready -h '${PRIMARY_IP}' -p 5432 -U '${REPL_USER}'" >/dev/null 2>&1; then
color "✅ 主库连接验证成功!" 0
else
color "❌ 复制用户连接验证失败!" 1
echo "请确认主库已正确配置复制用户和pg_hba.conf"
exit 1
fi
# 停止 PostgreSQL 服务
color "停止 PostgreSQL 服务..." 0
systemctl stop postgresql 2>/dev/null || true
# 清空现有数据目录 - 完全删除并重建
color "清空现有数据目录..." 0
rm -rf "${DATA_DIR}" 2>/dev/null || true
mkdir -p "$DATA_DIR"
chown "$DB_USER:$DB_USER" "$DATA_DIR"
# 创建临时备份目录
mkdir -p "$TEMP_BACKUP_DIR"
chown "$DB_USER:$DB_USER" "$TEMP_BACKUP_DIR"
# 使用临时目录进行备份,然后移动到最终位置
color "使用 pg_basebackup 初始化备库数据..." 0
if ! su - "$DB_USER" -c "
PGPASSWORD='${REPL_PASSWORD}' ${INSTALL_DIR}/bin/pg_basebackup \
-D '${TEMP_BACKUP_DIR}' \
-Fp \
-P \
-R \
-W \
-X stream \
-h '${PRIMARY_IP}' \
-p 5432 \
-U '${REPL_USER}'
"; then
color "❌ pg_basebackup 执行失败!" 1
rm -rf "$TEMP_BACKUP_DIR" 2>/dev/null || true
exit 1
fi
# 将临时目录内容移动到最终位置
color "移动数据到最终目录..." 0
mv "${TEMP_BACKUP_DIR}"/* "${DATA_DIR}/"
rmdir "$TEMP_BACKUP_DIR"
# 确保 standby.signal 存在
if [ ! -f "${DATA_DIR}/standby.signal" ]; then
touch "${DATA_DIR}/standby.signal"
chown "$DB_USER:$DB_USER" "${DATA_DIR}/standby.signal"
fi
# 配置备库 postgresql.conf
color "配置备库 postgresql.conf..." 0
cat > "${DATA_DIR}/postgresql.conf" <<EOF
# 备库核心配置
listen_addresses = '*'
port = 5432
max_connections = 200
shared_buffers = 4GB
effective_cache_size = 12GB
maintenance_work_mem = 1GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 26214kB
min_wal_size = 1GB
max_wal_size = 4GB
# 热备配置
hot_standby = on
max_standby_streaming_delay = 30s
wal_receiver_status_interval = 10s
hot_standby_feedback = on
max_standby_archive_delay = 30s
# 日志配置
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_timezone = 'Asia/Shanghai'
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
# Unix socket
unix_socket_directories = '/pgsql/run'
# 其他配置
dynamic_shared_memory_type = posix
bgwriter_delay = 200ms
bgwriter_lru_maxpages = 100
bgwriter_lru_multiplier = 2.0
EOF
chown "$DB_USER:$DB_USER" "${DATA_DIR}/postgresql.conf"
# 确保日志目录存在
mkdir -p "${DATA_DIR}/pg_log"
chown "$DB_USER:$DB_USER" "${DATA_DIR}/pg_log"
# 确保归档目录存在
mkdir -p "$ARCHIVE_DIR"
chown "$DB_USER:$DB_USER" "$ARCHIVE_DIR"
# 设置正确的权限
chown -R "$DB_USER:$DB_USER" "$DATA_DIR"
# 设置数据目录权限 - 关键修复点
chmod 700 "$DATA_DIR"
find "$DATA_DIR" -type d -exec chmod 700 {} \;
find "$DATA_DIR" -type f -exec chmod 600 {} \;
# 启动备库服务
color "启动 PostgreSQL 备库服务..." 0
systemctl start postgresql
# 等待服务启动
color "等待服务启动完成..." 0
for i in {1..30}; do
if systemctl is-active --quiet postgresql; then
break
fi
sleep 2
done
if systemctl is-active --quiet postgresql; then
color "✅ 备库启动成功!" 0
# 验证流复制状态
color "验证流复制状态..." 0
if su - "$DB_USER" -c "PGHOST=/pgsql/run ${INSTALL_DIR}/bin/psql -c \"SELECT pg_is_in_recovery();\" postgres" 2>/dev/null | grep -q "t"; then
color "✅ 备库已成功进入恢复模式!" 0
echo "流复制正在运行..."
else
color "⚠️ 备库可能未正确配置为只读模式" 1
fi
# 显示备库状态
echo ""
echo "📊 备库状态信息:"
su - "$DB_USER" -c "PGHOST=/pgsql/run ${INSTALL_DIR}/bin/psql -c \"SELECT now() as current_time, pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(), pg_is_in_recovery();\" postgres"
else
color "❌ 备库启动失败!" 1
echo ""
echo "🔍 详细错误信息:"
journalctl -u postgresql.service --no-pager -n 20
echo ""
echo "📋 数据目录权限检查:"
ls -la "$DATA_DIR" | head -5
echo ""
echo "📋 PostgreSQL 错误日志:"
tail -20 "$DATA_DIR/pg_log/"* 2>/dev/null || echo "未找到日志文件"
exit 1
fi
# 显示连接信息
color "✅ 备库配置完成!" 0
echo "=================================================="
echo "备库 IP: ${STANDBY_IP}"
echo "主库 IP: ${PRIMARY_IP}"
echo "复制用户: ${REPL_USER}"
echo "数据目录: ${DATA_DIR}"
echo "WAL归档目录: ${ARCHIVE_DIR}"
echo "安装目录: ${INSTALL_DIR}"
echo "=================================================="
echo ""
echo "🔧 验证命令(本地连接使用):"
echo "# 确认处于恢复模式(需要重新登录或 source /etc/profile)"
echo "psql -U postgres -c \"SELECT pg_is_in_recovery();\""
echo ""
echo "# 或者显式指定socket路径"
echo "PGHOST=/pgsql/run psql -U postgres -c \"SELECT pg_is_in_recovery();\""
echo ""
echo "🔧 验证命令(主库上执行):"
echo "# 查看复制状态"
echo "psql -U postgres -c \"SELECT pid, application_name, client_addr, state, sync_state FROM pg_stat_replication;\""
echo ""
echo "# 查看WAL接收器状态(在备库执行)"
echo "PGHOST=/pgsql/run psql -U postgres -c \"SELECT pid, status, received_lsn, last_msg_receipt_time FROM pg_stat_wal_receiver;\""
echo ""
echo "# 查看复制延迟(在备库执行)"
echo "PGHOST=/pgsql/run psql -U postgres -c \"SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp())) AS lag_seconds;\""
echo "=================================================="
5.主库创建表测试
-- 1. 创建一个名为 "zz" 的测试表
CREATE TABLE zz (
id SERIAL PRIMARY KEY,
description TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- 2. 插入几条测试数据
INSERT INTO zz (description) VALUES
('这是来自主库的第一条测试数据'),
('流复制同步测试 - 第二条'),
('验证成功!');
-- 3. 立刻查询一下,确认数据已经在主库了
SELECT * FROM zz;
psql
psql (15.15)
Type "help" for help.
postgres=# CREATE TABLE replication_test (
id SERIAL PRIMARY KEY,
info TEXT,
test_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE
postgres=# INSERT INTO replication_test (info) VALUES ('This is a test from Master'), ('Second row inserted');
INSERT 0 2
postgres=# SELECT * FROM replication_test;
id | info | test_time
----+----------------------------+----------------------------
1 | This is a test from Master | 2026-02-12 07:54:36.665403
2 | Second row inserted | 2026-02-12 07:54:36.665403
(2 rows)
postgres=# CREATE TABLE zz (
id SERIAL PRIMARY KEY,
description TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE
postgres=# INSERT INTO zz (description) VALUES
('这是来自主库的第一条测试数据'),
('流复制同步测试 - 第二条'),
('验证成功!');
INSERT 0 3
postgres=# SELECT * FROM zz;
id | description | created_at
----+------------------------------+----------------------------
1 | 这是来自主库的第一条测试数据 | 2026-02-12 08:17:14.878815
2 | 流复制同步测试 - 第二条 | 2026-02-12 08:17:14.878815
3 | 验证成功! | 2026-02-12 08:17:14.878815
(3 rows)
6.从库测试
su - postgres
postgres@master-03:~$ psql
psql (15.15)
Type "help" for help.
postgres=# SELECT * FROM replication_test;
id | info | test_time
----+----------------------------+----------------------------
1 | This is a test from Master | 2026-02-12 07:54:36.665403
2 | Second row inserted | 2026-02-12 07:54:36.665403
(2 rows)
postgres=# SELECT * FROM zz;
id | description | created_at
----+------------------------------+----------------------------
1 | 这是来自主库的第一条测试数据 | 2026-02-12 08:17:14.878815
2 | 流复制同步测试 - 第二条 | 2026-02-12 08:17:14.878815
3 | 验证成功! | 2026-02-12 08:17:14.878815
(3 rows)
5. HAProxy + Keepalived 配置
部署说明
1. 节点分布
HAProxy1 节点 (IP: 10.0.0.101)
运行: install_ha_proxy_keepalived.sh
角色: Keepalived MASTER
优先级: 100
HAProxy2 节点 (IP: 10.0.0.102)
运行: install_ha_proxy_keepalived.sh
角色: Keepalived BACKUP
优先级: 90
Monitor 节点 (任意节点,如应用服务器)
运行: monitor_cluster.sh, failover_handler.sh
用于集群监控和故障转移
应用服务器
使用: connection_examples.sh 作为连接参考
1.系统优化+安装配置 HAProxy + Keepalived
#!/bin/bash
# install_ha_proxy_keepalived.sh - 安装配置 HAProxy + Keepalived 高可用 (含系统优化)
set -euo pipefail
# 参数配置
VIP="10.0.0.100"
NODE1_IP="10.0.0.101" # HAProxy1 IP (MASTER)
NODE2_IP="10.0.0.102" # HAProxy2 IP (BACKUP)
INTERFACE="ens3" # 网络接口名,根据实际情况调整
PG_PRIMARY="10.0.0.24" # PostgreSQL 主库
PG_STANDBY="10.0.0.182" # PostgreSQL 备库
PG_PORT="5432"
color() {
local msg="$1"; local status="$2"
printf "%-60s" "$msg"
if [[ "$status" == "0" ]]; then
echo -e "[\033[1;32m OK \033[0m]"
else
echo -e "[\033[1;31mFAILED\033[0m]"
fi
}
if [[ $EUID -ne 0 ]]; then
color "❌ 请以 root 用户运行!" 1
exit 1
fi
# 检查网络接口
if ! ip addr show "$INTERFACE" &>/dev/null; then
color "❌ 网络接口 $INTERFACE 不存在!" 1
echo "请修改 INTERFACE 变量为正确的网络接口名"
exit 1
fi
# 1. 安装 HAProxy 和 Keepalived
color "安装 HAProxy 和 Keepalived..." 0
apt update
apt install -y haproxy keepalived
# 2. 系统参数优化
color "开始系统参数优化..." 0
# 内核参数优化
color "配置内核参数..." 0
cat >> /etc/sysctl.conf <<EOF
# HAProxy + Keepalived 优化参数
# 增加最大连接数
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 5000
# 优化TCP参数
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
# 网络缓冲区优化
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 65536 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# VRRP 相关优化
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
# 系统文件描述符限制
fs.file-max = 1048576
# 增加可用端口范围
net.ipv4.ip_local_port_range = 1024 65535
# 增加TIME_WAIT状态连接的回收
net.ipv4.tcp_max_tw_buckets = 5000
# TCP快速回收
net.ipv4.tcp_fastopen = 3
# 增加路由缓存大小
net.ipv4.route.flush = 1
# 网络监控优化
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 1800
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
EOF
# 应用内核参数
sysctl -p
# 文件描述符优化
color "配置文件描述符限制..." 0
cat >> /etc/security/limits.conf <<EOF
# HAProxy + Keepalived 文件描述符优化
haproxy soft nofile 102400
haproxy hard nofile 102400
keepalived soft nofile 102400
keepalived hard nofile 102400
root soft nofile 102400
root hard nofile 102400
EOF
# HAProxy 服务优化
color "配置 HAProxy 服务参数..." 0
mkdir -p /etc/systemd/system/haproxy.service.d
cat > /etc/systemd/system/haproxy.service.d/override.conf <<EOF
[Service]
LimitNOFILE=102400
LimitNPROC=32768
Restart=always
RestartSec=5
OOMScoreAdjust=-1000
EOF
# Keepalived 服务优化
color "配置 Keepalived 服务参数..." 0
mkdir -p /etc/systemd/system/keepalived.service.d
cat > /etc/systemd/system/keepalived.service.d/override.conf <<EOF
[Service]
LimitNOFILE=102400
LimitNPROC=32768
Restart=always
RestartSec=5
OOMScoreAdjust=-1000
EOF
# 重新加载 systemd 配置
systemctl daemon-reload
# 3. 配置 HAProxy
color "配置 HAProxy..." 0
cat > /etc/haproxy/haproxy.cfg <<EOF
global
log stdout local0
log stdout local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
nbthread 4
cpu-map auto:1/1-4 0-3
defaults
log global
mode tcp
option tcplog
option dontlognull
retries 3
timeout connect 5000
timeout client 50000
timeout server 50000
# PostgreSQL 读写连接 (主库优先)
listen postgresql_rw
bind *:5432
mode tcp
balance roundrobin
option pgsql-check user=postgres
server primary $PG_PRIMARY:$PG_PORT check port=$PG_PORT inter=2000 rise=2 fall=3 maxconn 5000
server standby $PG_STANDBY:$PG_PORT check port=$PG_PORT inter=2000 rise=2 fall=3 backup maxconn 5000
# PostgreSQL 只读连接 (备库优先)
listen postgresql_ro
bind *:5433
mode tcp
balance roundrobin
option pgsql-check user=postgres
server standby $PG_STANDBY:$PG_PORT check port=$PG_PORT inter=2000 rise=2 fall=3 maxconn 5000
server primary $PG_PRIMARY:$PG_PORT check port=$PG_PORT inter=2000 rise=2 fall=3 backup maxconn 5000
# 状态检查页面
listen stats
bind *:9696
mode http
stats enable
stats uri /stats
stats refresh 30s
stats admin if LOCALHOST
EOF
# 4. 配置 Keepalived
color "配置 Keepalived..." 0
# 获取当前节点角色
if ip addr show | grep -q "$NODE1_IP"; then
ROLE="MASTER"
PRIORITY=100
NODE_ID="1"
elif ip addr show | grep -q "$NODE2_IP"; then
ROLE="BACKUP"
PRIORITY=90
NODE_ID="2"
else
color "❌ 当前IP不匹配预设的节点IP!" 1
echo "当前IP: $(hostname -I)"
echo "期望IP1: $NODE1_IP (MASTER), IP2: $NODE2_IP (BACKUP)"
exit 1
fi
cat > /etc/keepalived/keepalived.conf <<EOF
vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 2
weight 2
fall 3
rise 2
}
vrrp_instance VI_POSTGRES_$NODE_ID {
state $ROLE
interface $INTERFACE
virtual_router_id 51
priority $PRIORITY
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
$VIP/24
}
track_script {
chk_haproxy
}
notify_master "/etc/keepalived/master.sh"
notify_backup "/etc/keepalived/backup.sh"
notify_fault "/etc/keepalived/fault.sh"
preempt_delay 10
}
EOF
# 创建通知脚本
mkdir -p /etc/keepalived/
cat > /etc/keepalived/master.sh <<'EOF'
#!/bin/bash
NODE=$(hostname)
echo "$(date): Node $NODE became MASTER" >> /var/log/keepalived.log
systemctl start haproxy
echo "$(date): Started HAProxy on MASTER node" >> /var/log/keepalived.log
EOF
cat > /etc/keepalived/backup.sh <<'EOF'
#!/bin/bash
NODE=$(hostname)
echo "$(date): Node $NODE became BACKUP" >> /var/log/keepalived.log
systemctl stop haproxy
echo "$(date): Stopped HAProxy on BACKUP node" >> /var/log/keepalived.log
EOF
cat > /etc/keepalived/fault.sh <<'EOF'
#!/bin/bash
NODE=$(hostname)
echo "$(date): Node $NODE entered FAULT state" >> /var/log/keepalived.log
systemctl stop haproxy
echo "$(date): Stopped HAProxy due to fault" >> /var/log/keepalived.log
EOF
chmod +x /etc/keepalived/*.sh
# 配置 HAProxy 允许绑定特权端口
color "配置 HAProxy 权限..." 0
sed -i 's/^ENABLED=.*/ENABLED=1/' /etc/default/haproxy
# 5. 启动服务
color "启动 HAProxy 和 Keepalived..." 0
systemctl enable haproxy keepalived
systemctl restart haproxy keepalived
# 等待服务启动
sleep 3
if systemctl is-active --quiet haproxy && systemctl is-active --quiet keepalived; then
color "✅ HAProxy + Keepalived 启动成功!" 0
echo ""
echo "📊 高可用配置信息:"
echo "VIP: $VIP"
echo "本节点: $(hostname -I | awk '{print $1}') ($ROLE)"
echo "VIP当前位置: $(ip addr show | grep "$VIP" | head -1 | awk '{print $NF}' || echo "NOT FOUND")"
echo "网络接口: $INTERFACE"
echo ""
echo "🔌 连接信息:"
echo "读写连接: psql -h $VIP -p 5432" # 自动连接主库
echo "只读连接: psql -h $VIP -p 5433" # 自动连接备库
echo "监控页面: http://$VIP:9696/stats"
echo ""
echo "🔄 服务管理:"
echo "systemctl status haproxy keepalived"
echo "systemctl restart haproxy keepalived"
echo ""
echo "🔍 验证命令:"
echo "ip addr show | grep $VIP # 检查VIP位置"
echo "curl http://$VIP:9696/stats # 检查监控页面"
echo "haproxy -v # 检查HAProxy版本"
echo ""
echo "🔧 系统优化已完成:"
echo "• 内核参数已调整以支持高并发连接"
echo "• 文件描述符限制已设置为 102400"
echo "• TCP 参数已优化以提高网络性能"
echo "• HAProxy 和 Keepalived 服务已配置资源限制"
echo "• 网络连接跟踪参数已优化"
else
color "❌ 服务启动失败!" 1
echo "查看日志:"
journalctl -u haproxy -n 20
journalctl -u keepalived -n 20
exit 1
fi
3. 集群监控脚本 (monitor_cluster.sh)
#!/bin/bash
# monitor_cluster.sh - PostgreSQL 集群监控脚本
set -euo pipefail
PRIMARY_HOST="10.0.0.24"
STANDBY_HOST="10.0.0.182"
VIP="10.0.0.100"
PG_PORT="5432"
LOG_FILE="/var/log/postgresql_monitor.log"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
check_cluster_health() {
local primary_healthy=false
local standby_healthy=false
local current_primary=""
# 检查主库连接
if pg_isready -h "$PRIMARY_HOST" -p "$PG_PORT" -U "postgres" 2>/dev/null; then
# 检查是否是主库
if sudo -u postgres psql -h "$PRIMARY_HOST" -p "$PG_PORT" -U "postgres" -tAc "SELECT pg_is_in_recovery();" 2>/dev/null | grep -q "f"; then
primary_healthy=true
current_primary="$PRIMARY_HOST"
fi
fi
# 检查备库连接
if pg_isready -h "$STANDBY_HOST" -p "$PG_PORT" -U "postgres" 2>/dev/null; then
# 检查是否是备库
if sudo -u postgres psql -h "$STANDBY_HOST" -p "$PG_PORT" -U "postgres" -tAc "SELECT pg_is_in_recovery();" 2>/dev/null | grep -q "t"; then
standby_healthy=true
if [ "$primary_healthy" = false ]; then
current_primary="$STANDBY_HOST"
fi
fi
fi
# 记录状态
log_message "Cluster Health Check:"
log_message " Primary ($PRIMARY_HOST): $(if [ "$primary_healthy" = true ]; then echo "HEALTHY"; else echo "UNHEALTHY"; fi)"
log_message " Standby ($STANDBY_HOST): $(if [ "$standby_healthy" = true ]; then echo "HEALTHY"; else echo "UNHEALTHY"; fi)"
log_message " Current Primary: $current_primary"
log_message " VIP Location: $(ip addr show | grep "$VIP" | head -1 | awk '{print $NF}' || echo "NOT_FOUND")"
# 检查VIP和主库是否在同一节点
local vip_node=$(ip addr show | grep "$VIP" | head -1 | awk '{print $NF}')
if [ -n "$current_primary" ] && [ "$vip_node" != "$current_primary" ]; then
log_message "⚠️ WARNING: VIP and current primary are on different nodes!"
fi
# 返回状态
if [ "$primary_healthy" = true ] && [ "$standby_healthy" = true ]; then
echo "HEALTHY"
return 0
elif [ "$primary_healthy" = false ] && [ "$standby_healthy" = true ]; then
echo "NEEDS_FAILOVER"
return 1
else
echo "CRITICAL"
return 2
fi
}
# 执行健康检查
cluster_status=$(check_cluster_health)
echo "Cluster Status: $cluster_status"
# 根据状态采取行动
case "$cluster_status" in
"NEEDS_FAILOVER")
echo "❌ 主库故障,需要故障转移!"
log_message "Automatic failover triggered due to primary failure"
# 这里可以调用故障转移脚本
# /path/to/failover_handler.sh failover
;;
"CRITICAL")
echo "❌ 集群严重故障!"
log_message "CRITICAL: Both primary and standby are unavailable"
;;
*)
echo "✅ 集群状态正常"
;;
esac
4. 故障转移脚本 (failover_handler.sh)
#!/bin/bash
# failover_handler.sh - PostgreSQL 故障转移处理脚本
set -euo pipefail
PRIMARY_HOST="10.0.0.24"
STANDBY_HOST="10.0.0.182"
VIP="10.0.0.100"
SSH_KEY="/root/.ssh/id_rsa"
PG_USER="postgres"
PG_PORT="5432"
color() {
local msg="$1"; local status="$2"
printf "%-60s" "$msg"
if [[ "$status" == "0" ]]; then
echo -e "[\033[1;32m OK \033[0m]"
else
echo -e "[\033[1;31mFAILED\033[0m]"
fi
}
promote_standby() {
local standby_host="$1"
color "提升备库为新主库: $standby_host" 0
# 停止备库服务
ssh "$standby_host" "systemctl stop postgresql"
# 移除 standby.signal 文件以允许提升
ssh "$standby_host" "rm -f /pgsql/data/standby.signal"
# 启动为新主库
ssh "$standby_host" "systemctl start postgresql"
# 等待服务启动
sleep 10
# 验证是否已提升为主库
if ssh "$standby_host" "sudo -u postgres psql -tAc 'SELECT pg_is_in_recovery();'" | grep -q "f"; then
color "✅ 备库已成功提升为主库!" 0
return 0
else
color "❌ 备库提升失败!" 1
return 1
fi
}
demote_old_primary() {
local old_primary="$1"
color "降级旧主库: $old_primary" 0
# 停止旧主库服务
ssh "$old_primary" "systemctl stop postgresql"
# 等待一段时间确保不再接受连接
sleep 5
}
update_haproxy_config() {
local new_primary="$1"
color "更新 HAProxy 配置..." 0
# 更新HAProxy配置,将新主库设为主要服务器
sed -i "s/server primary .*/server primary $new_primary:$PG_PORT check port=$PG_PORT inter=2000 rise=2 fall=3 maxconn 5000/" /etc/haproxy/haproxy.cfg
sed -i "s/server standby .*/server standby $new_primary:$PG_PORT check port=$PG_PORT inter=2000 rise=2 fall=3 backup maxconn 5000/" /etc/haproxy/haproxy.cfg
# 重新加载HAProxy配置
systemctl reload haproxy
}
perform_failover() {
color "开始故障转移程序..." 0
# 检查主库状态
if pg_isready -h "$PRIMARY_HOST" -p "$PG_PORT" -U "$PG_USER" 2>/dev/null; then
color "⚠️ 主库似乎仍在运行,跳过故障转移" 2
return 0
fi
color "❌ 检测到主库故障,开始故障转移..." 0
# 检查备库是否健康
if ! pg_isready -h "$STANDBY_HOST" -p "$PG_PORT" -U "$PG_USER" 2>/dev/null; then
color "❌ 备库也不可用,无法进行故障转移!" 1
exit 1
fi
# 提升备库为主库
if promote_standby "$STANDBY_HOST"; then
# 降级旧主库
demote_old_primary "$PRIMARY_HOST"
# 更新HAProxy配置
update_haproxy_config "$STANDBY_HOST"
color "🎉 故障转移完成!" 0
color "新的主库: $STANDBY_HOST" 0
color "VIP $VIP 现在指向新主库" 0
# 发送告警邮件(可选)
echo "PostgreSQL 故障转移完成: $PRIMARY_HOST -> $STANDBY_HOST" | mail -s "PostgreSQL Failover Alert" admin@example.com 2>/dev/null || true
else
color "❌ 故障转移失败!" 1
exit 1
fi
}
case "$1" in
"check")
# 检查集群状态
if pg_isready -h "$PRIMARY_HOST" -p "$PG_PORT" -U "$PG_USER" 2>/dev/null; then
echo "Primary is healthy: $PRIMARY_HOST"
if pg_isready -h "$STANDBY_HOST" -p "$PG_PORT" -U "$PG_USER" 2>/dev/null; then
echo "Standby is healthy: $STANDBY_HOST"
echo "Status: HEALTHY"
else
echo "Standby is unhealthy: $STANDBY_HOST"
echo "Status: DEGRADED"
fi
else
echo "Primary is unhealthy: $PRIMARY_HOST"
echo "Status: NEEDS_FAILOVER"
fi
;;
"failover")
perform_failover
;;
*)
echo "Usage: $0 {check|failover}"
exit 1
;;
esac
部署步骤总结:
在 HAProxy1 节点 (10.0.0.101):
# 安装 HAProxy + Keepalived
chmod +x install_ha_proxy_keepalived.sh
./install_ha_proxy_keepalived.sh
在 HAProxy2 节点 (10.0.0.102):
# 安装 HAProxy + Keepalived
chmod +x install_ha_proxy_keepalived.sh
./install_ha_proxy_keepalived.sh
在监控节点(可选,通常在应用服务器):
# 监控脚本
chmod +x monitor_cluster.sh failover_handler.sh
在所有节点上设置定时任务:
# 添加到 crontab
echo "*/5 * * * * /path/to/monitor_cluster.sh >> /var/log/monitor.log 2>&1" >> /etc/crontab
更多推荐

所有评论(0)