麒麟v10系统部署 Redis 7.4.7(支持单机/主从/哨兵模式)
📋 概述
本安装脚本用于在 Kylin Linux Advanced Server V10 (ARM64) 系统上部署 Redis 7.4.7,支持三种部署模式:
– 单机模式 (Standalone):独立运行的 Redis 实例
– 主从模式 (Replication):一主一从的高可用架构
– 哨兵模式 (Sentinel):主从架构 + 自动故障转移
脚本放到了末尾,自行验证。
🖥️ 环境信息
- 操作系统: Kylin Linux Advanced Server V10 (Halberd)
- 架构: aarch64 (ARM64)
- 内核版本: 4.19.90-89.23.v2401.ky10.aarch64
- Redis 版本: 7.4.7
- Jemalloc 版本: 5.3.0(源码编译)
🎯 部署模式选择
| 特性 | 单机模式 | 主从模式 | 哨兵模式 |
|---|---|---|---|
| 适用场景 | 开发、测试 | 生产环境 | 生产环境(推荐) |
| 服务器数量 | 1 台 | 2 台(1 主 1 从) | 2 台 Redis(三台也行自行修改SLAVE_IP) + 3 台哨兵 |
| 数据冗余 | ❌ 无 | ✅ 主从同步 | ✅ 主从同步 |
| 读写分离 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
| 自动故障转移 | ❌ 不支持 | ❌ 需手动切换 | ✅ 自动切换 |
| 配置复杂度 | 简单 | 中等 | 较复杂 |
| IP 要求 | 无限制 | 需配置主从 IP | 需配置主从 + 哨兵 IP |
📍 节点信息示例
主从模式
- 主节点: 10.202.17.12
- 从节点: 10.202.17.13
哨兵模式
- 主节点: 10.202.17.12
- 从节点: 10.202.17.13,10.202.17.14(在SLAVE_IP这里切换ip就行)
- 哨兵节点: 10.202.17.12, 10.202.17.13, 10.202.17.14(可与 Redis 同机部署)
✨ 功能特性
✅ 多种部署模式
– 单机模式:适用于开发、测试或小规模生产环境
– 主从模式:适用于需要高可用的生产环境
– 哨兵模式:适用于需要自动故障转移的生产环境
✅ 智能检测
– 自动检测系统环境和架构
– 根据配置自动识别部署模式
– 主从/哨兵模式下自动检测节点角色(主/从/哨兵)
– 检查运行用户是否存在
– 避免重复添加系统配置
✅ 源码编译
– jemalloc 5.3.0 源码编译安装
– Redis 使用 jemalloc 内存分配器
ARM64 架构
✅ 灵活配置
– 支持单机、主从、哨兵三种部署模式
– 支持完全自定义安装路径
– 通过 config.sh 集中管理配置
– 主从/哨兵模式下自动识别节点角色
– 哨兵可与 Redis 同机部署或独立部署
✅ 系统优化
– 内核参数优化
– 禁用透明大页
– 资源限制配置
– Systemd 服务管理
✅ 安全加固
– 密码认证
– 用户权限隔离
– 安全的服务停止方式(TERM 信号)
✅ 持久化
– RDB 快照
– AOF 日志
– 自动保存策略
📁 文件清单
redis_install/
├── redis-7.4.7.tar.gz # Redis 源码包(必需)
├── jemalloc-5.3.0.tar.bz2 # jemalloc 源码包(必需)
├── install_redis.sh # 主安装脚本(必需)
├── config.sh # 配置文件(推荐)
├── config.sh.example # 配置文件示例(参考)
└── README.md # 说明文档
redis版本下载地址:https://download.redis.io/releases/
jemalloc-5.3.0下载地址:https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2
🚀 快速开始
1. 前置要求
必须满足的条件:
– ✅ Root 权限
– ✅ 运行用户已存在(默认:gzapps)
– ✅ 所有源码包已上传
– ✅ 主从模式下:当前主机 IP 必须为主节点或从节点 IP
创建运行用户(如果不存在):
useradd -r -s /sbin/nologin gzapps
2. 修改配置
方法一:使用示例配置文件(推荐)
# 复制示例配置文件
cp config.sh.example config.sh
# 编辑配置
vim config.sh
方法二:直接编辑现有配置
vim config.sh
重要配置项:
2.1 选择部署模式
# ============================================================================
# 部署模式配置
# ============================================================================
# 可选值: standalone (单机) / replication (主从) / sentinel (哨兵)
export DEPLOY_MODE="standalone"
2.2 基础配置(所有模式都需要)
# 安装目录(根据实际情况修改)
export INSTALL_DIR="/AppHome/redis" # Redis 安装目录
export CONFIG_DIR="/AppHome/redis/conf" # 配置文件目录
export DATA_DIR="/AppHome/redis/data" # 数据目录
export LOG_DIR="/AppHome/redis/logs" # 日志目录
# 运行用户(必须已存在)
export REDIS_USER="gzapps"
# Redis 密码(必须修改)
export REDIS_PASSWORD="1q2w3e4rAa!@#"
# 性能配置
export REDIS_MAXMEMORY="4gb"
2.3 主从模式额外配置(仅在 DEPLOY_MODE=replication 或 sentinel 时需要)
# 主从节点 IP
export MASTER_IP="10.202.17.12"
export SLAVE_IP="10.202.17.13"
#export SLAVE_IP="10.202.17.14"
2.4 哨兵模式额外配置(仅在 DEPLOY_MODE=sentinel 时需要)
# 哨兵配置
export SENTINEL_PORT=26379 # 哨兵端口
export SENTINEL_QUORUM=2 # 投票数(建议:哨兵总数/2+1)
export SENTINEL_IPS="10.202.17.12 10.202.17.13 10.202.17.14" # 哨兵节点 IP(空格分隔)
export MASTER_NAME="mymaster" # 主节点名称
哨兵部署说明:
– 哨兵可以与 Redis 主从节点同机部署(如示例中的 10.202.17.12 和 10.202.17.13)
– 也可以部署在独立节点上(如示例中的 10.202.17.14)
– 建议至少部署 3 个哨兵节点以保证高可用
– SENTINEL_QUORUM 建议设置为哨兵总数的一半加一
3. 执行安装
3.1 单机模式安装
cd redis_install
chmod +x *.sh
# 确保 config.sh 中 DEPLOY_MODE="standalone"
./install_redis.sh
3.3 哨兵模式安装
在主节点 (10.202.17.12) 上:
cd redis_install
chmod +x *.sh
# 确保 config.sh 中 DEPLOY_MODE="sentinel"
# 确保当前 IP 在 SENTINEL_IPS 列表中(如果要部署哨兵)
./install_redis.sh
在从节点 (10.202.17.13) 上:
cd redis_install
chmod +x *.sh
# 使用相同的 config.sh 配置
./install_redis.sh
在哨兵节点 (10.202.17.14) 上(如果是独立哨兵节点):
cd redis_install
chmod +x *.sh
# 使用相同的 config.sh 配置
# 注意:独立哨兵节点的 IP 不在 MASTER_IP 和 SLAVE_IP 中
# 但必须在 SENTINEL_IPS 列表中
./install_redis.sh
说明:
– 哨兵可以与 Redis 主从节点同机部署
– 脚本会自动判断当前节点是否需要部署哨兵服务
– 建议至少部署 3 个哨兵节点
4. 验证安装
4.1 单机模式验证
# 检查服务状态
systemctl status redis
# 测试连接
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# ping
# 查看 Redis 信息
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# info server
4.2 主从模式验证
# 检查服务状态
systemctl status redis
# 测试连接
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# ping
# 查看主从状态
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# info replication
4.3 哨兵模式验证
# 检查 Redis 服务状态
systemctl status redis
# 测试 Redis 连接
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# ping
# 查看主从状态
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# info replication
# 检查哨兵服务状态(在哨兵节点上)
systemctl status redis-sentinel
# 查看哨兵监控的主节点信息
/AppHome/redis/bin/redis-cli -p 26379 sentinel masters
# 查看哨兵监控的从节点信息
/AppHome/redis/bin/redis-cli -p 26379 sentinel slaves mymaster
# 查看所有哨兵节点
/AppHome/redis/bin/redis-cli -p 26379 sentinel sentinels mymaster
📂 安装后的目录结构
/AppHome/redis/
├── bin/ # 可执行文件
│ ├── redis-server # Redis 服务器
│ ├── redis-cli # Redis 客户端
│ ├── redis-benchmark # 性能测试工具
│ ├── redis-check-rdb # RDB 检查工具
│ └── redis-check-aof # AOF 检查工具
├── conf/
│ └── redis.conf # Redis 配置文件
├── data/
│ ├── dump.rdb # RDB 持久化文件
aof # AOF 持久化文件
│ └── redis_6379.pid # PID 文件
└── logs/
└── redis.log # Redis 日志文件
/usr/local/lib/
└── libjemalloc.so.* # jemalloc 库文件
/etc/systemd/system/
└── redis.service # Systemd 服务文件
⚙️ 配置说明
Redis 核心配置
| 配置项 | 值 | 说明 |
|---|---|---|
| bind | 0.0.0.0 | 监听所有网卡 |
| port | 6379 | 监听端口 |
| daemonize | yes | 后台运行 |
| requirepass | 自定义 | Redis 密码 |
| maxmemory | 4gb | 最大内存限制 |
| maxmemory-policy | allkeys-lru | LRU 淘汰策略 |
| hz | 100 | 高频率定时任务 |
| appendonly | yes | 启用 AOF |
| save | 900 1 / 300 10 / 60 10000 | RDB 保存策略 |
系统优化参数
脚本会自动配置(避免重复添加):
# 内核参数
vm.overcommit_memory = 1 # 允许内存过量分配
net.core.somaxconn = 65535 # 增加连接队列
# 禁用透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 资源限制
gzapps soft nofile 65535
gzapps hard nofile 65535
gzapps soft nproc 65535
gzapps hard nproc 65535
Systemd 服务配置
[Service]
Type=forking # 后台进程模式
User=gzapps # 运行用户
PIDFile=/AppHome/redis/data/redis_6379.pid
ExecStart=/AppHome/redis/bin/redis-server /AppHome/redis/conf/redis.conf
ExecStop=/bin/kill -s TERM $MAINPID # 优雅停止
TimeoutStartSec=30s # 启动超时
Restart=always # 自动重启
LimitNOFILE=65535 # 文件描述符限制
🔧 服务管理
基本操作
# 启动服务
systemctl start redis
# 停止服务
systemctl stop redis
# 重启服务
systemctl restart redis
# 查看状态
systemctl status redis
# 查看日志
journalctl -u redis -f
# 开机自启
systemctl enable redis
# 禁用自启
systemctl disable redis
Redis 命令
# 连接 Redis
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@#
# 查看信息
redis-cli -a 1q2w3e4rAa!@# info
redis-cli -a 1q2w3e4rAa!@# info replication
redis-cli -a 1q2w3e4rAa!@# info memory
# 查看配置
redis-cli -a 1q2w3e4rAa!@# config get "*"
# 查看慢查询
redis-cli -a 1q2w3e4rAa!@# slowlog get 10
# 查看客户端连接
redis-cli -a 1q2w3e4rAa!@# client list
🔍 主从复制验证(仅主从模式)
在主节点验证
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# info replication
期望输出:
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.202.17.13,port=6379,state=online,offset=1632,lag=0
master_failover_state:no-failover
master_replid:f6f7770caf8dc4d40f47a30824ec181caa3c6676
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1632
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:1631
在从节点验证
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# info replication
期望输出:
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.202.17.12
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_read_repl_offset:1660
slave_repl_offset:1660
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:f6f7770caf8dc4d40f47a30824ec181caa3c6676
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1660
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:804
repl_backlog_histlen:857
测试数据同步
主节点写入:
redis-cli -a 1q2w3e4rAa!@# set test_key "hello from master"
从节点读取:
redis-cli -portRedis@2026 get test_key
# 输出: "hello from master"
🐛 故障排查
1. 服务启动超时
症状:systemctl start redis 超时
排查步骤:
# 1. 查看 systemd 日志
journalctl -u redis -n 50
# 2. 查看 Redis 日志
tail -100 /AppHome/redis/logs/redis.log
# 3. 检查 PID 文件
ls -la /AppHome/redis/data/redis_6379.pid
# 4. 手动启动测试
/AppHome/redis/bin/redis-server /AppHome/redis/conf/redis.conf
2. 主从同步失败
症状:从节点 master_link_status:down
排查步骤:
# 1. 检查网络连通性
ping 10.202.17.12
telnet 10.202.17.12 6379
# 2. 检查防火墙
firewall-cmd --list-all
# 3. 检查主节点日志
tail -f /AppHome/redis/logs/redis.log
# 4. 检查密码配置
grep "requirepass\|masterauth" /AppHome/redis/conf/redis.conf
解决方案:
# 开放防火墙端口
fird-port=6379/tcp
firewall-cmd --reload
redis-server /AppHome/redis/conf/redis.conf --test-memory 1
```
3. **检查资源**
```bash
top
free -h
df -h
```
4. **检查网络**
```bash
netstat -tlnp | grep 6379
ss -tlnp | grep 6379
```
---
## 🔄 哨兵模式故障转移测试(仅哨兵模式)
### 测试自动故障转移
**1. 模拟主节点故障**
在主节点上停止 Redis 服务:
```bash
systemctl stop redis
2. 观察哨兵日志
在任意哨兵节点上查看日志:
tail -f /AppHome/redis/logs/sentinel.log
你会看到类似以下日志:
+sdown master mymaster 10.202.17.12 6379
+odown master mymaster 10.202.17.12 6379 #quorum 2/2
+failover-triggered master mymaster 10.202.17.12 6379
+failover-state-select-slave master mymaster 10.202.17.12 6379
+selected-slave slave 10.202.17.13:6379 10.202.17.13 6379 @ mymaster 10.202.17.12 6379
+failover-state-send-slaveof-noone slave 10.202.17.13:6379 10.202.17.13 6379 @ mymaster 10.202.17.12 6379
+failover-end master mymaster 10.202.17.12 6379
+switch-master mymaster 10.202.17.12 6379 10.202.17.13 6379
3. 验证新主节点
查看哨兵监控的主节点信息:
/AppHome/redis/bin/redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
应该返回新的主节点 IP(原从节点):
1) "10.202.17.13"
2) "6379"
4. 恢复原主节点
启动原主节点的 Redis 服务:
systemctl start redis
原主节点会自动变成从节点,同步新主节点的数据。
验证故障转移结果
# 在新主节点上查看角色
/AppHome/redis/bin/redis-cli -h 10.202.17.13 -a 1q2w3e4rAa!@# info replication | grep role
# 输出: role:master
# 在原主节点上查看角色
/AppHome/redis/bin/redis-cli -h 10.202.17.12 -a 1q2w3e4rAa!@# info replication | grep role
# 输出: role:slave
💾 备份与恢复
自动备份脚本
创建 /usr/local/bin/redis_backup.sh:
#!/bin/bash
BACKUP_DIR="/backup/redis/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
# 触发 RDB 快照
/AppHome/redis/bin/redis-cli -a 1q2w3e4rAa!@# bgsave
# 等待快照完成
sleep 10
# 备份文件
cp /AppHome/redis/data/dump.rdb $BACKUP_DIR/
cp /AppHome/redis/data/appendonly.aof $BACKUP_DIR/
cp /AppHome/redis/conf/redis.conf $BACKUP_DIR/
# 保留最近 7 天
find /backup/redis/ -type d -mtime +7 -exec rm -rf {} \;
添加到 crontab
# 每天凌晨 2 点备份
0 2 * * * /usr/local/bin/redis_backup.sh
📊 性能测试
基础性能测试
/AppHome/redis/bin/redis-benchmark \
-h 127.0.0.1 \
-p 6379 \
-a 1q2w3e4rAa!@# \
-c 50 \
-n 10000
内存不足
症状:Redis 拒绝写入
排查步骤:
# 查看内存使用
redis-cli -a 1q2w3e4rAa!@# info memory | grep used_memory_human
redis-cli -a 1q2w3e4rAa!@# info memory | grep maxmemory_human
解决方案:
# 临时调整(重启后失效)
redis-cli -a 1q2w3e4rAa!@# config set maxmemory 8gb
脚本如下
config.sh:
#!/bin/bash
################################################################################
# Redis 安装配置文件
#
# 使用方法:
# 1. 修改下面的配置参数
# 2. 将此文件和 install_redis.sh 放在同一目录
# 3. 运行 install_redis.sh 时会自动加载此配置
################################################################################
# ============================================================================
# 部署模式配置
# ============================================================================
# 可选值:
# - standalone : 单机模式
# - replication : 主从模式
# - sentinel : 主从+哨兵模式
export DEPLOY_MODE="standalone"
# ============================================================================
# Redis 版本配置
# ============================================================================
export REDIS_VERSION="7.4.7"
# ============================================================================
# 安装目录配置
# ============================================================================
export INSTALL_DIR="/AppHome/redis" # Redis 安装目录
export CONFIG_DIR="/AppHome/redis/conf" # 配置文件目录(redis.conf 存放位置)
export DATA_DIR="/AppHome/redis/data" # 数据存储目录
export LOG_DIR="/AppHome/redis/logs" # 日志目录
# ============================================================================
# Redis 服务配置
# ============================================================================
export REDIS_PORT=6379 # Redis 监听端口
export REDIS_USER="gzapps" # Redis 运行用户
export REDIS_PASSWORD="YourStrongPassword123" # Redis 密码
# ============================================================================
# 主从节点 IP 配置(仅在 DEPLOY_MODE=replication 或 sentinel 时生效)
# ============================================================================
export MASTER_IP="10.202.17.12" # 主节点 IP
export SLAVE_IP="10.202.17.13" # 从节点 IP
# ============================================================================
# 哨兵配置(仅在 DEPLOY_MODE=sentinel 时生效)
# ============================================================================
export SENTINEL_PORT=26379 # 哨兵监听端口
export SENTINEL_QUORUM=2 # 哨兵判定主节点下线所需的投票数
export SENTINEL_IPS="10.202.17.12 10.202.17.13 10.202.17.14" # 哨兵节点 IP 列表(空格分隔)
export MASTER_NAME="mymaster" # 主节点名称(哨兵监控用)
# ============================================================================
# 性能配置
# ============================================================================
export REDIS_MAXMEMORY="4gb" # 最大内存限制(根据服务器实际内存调整)
# ============================================================================
# 持久化配置
# ============================================================================
export ENABLE_AOF="yes" # 是否启用 AOF 持久化
install_redis.sh
#!/bin/bash
################################################################################
# Redis 7.4.7 安装脚本(支持单机/主从/哨兵模式)
# 适用系统: Kylin Linux Advanced Server V10 (ARM64)
# 作者: glj
# 日期: 2025-02-10
#
# 部署模式:
# - standalone: 单机模式
# - replication: 主从模式
# - sentinel: 主从+哨兵模式
################################################################################
set -e # 遇到错误立即退出
# ============================================================================
# 加载外部配置文件(如果存在)
# ============================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "$SCRIPT_DIR/config.sh" ]]; then
source "$SCRIPT_DIR/config.sh"
echo "[INFO] 已加载外部配置文件: config.sh"
fi
# ============================================================================
# 全局变量配置(可被 config.sh 覆盖)
# ============================================================================
DEPLOY_MODE="${DEPLOY_MODE:-standalone}" # 部署模式:standalone、replication 或 sentinel
REDIS_VERSION="${REDIS_VERSION:-7.4.7}"
REDIS_TAR="redis-${REDIS_VERSION}.tar.gz"
REDIS_DIR="redis-${REDIS_VERSION}"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/redis}"
CONFIG_DIR="${CONFIG_DIR:-/etc/redis}" # 配置文件目录(可自定义)
DATA_DIR="${DATA_DIR:-/data/redis}"
LOG_DIR="${LOG_DIR:-/var/log/redis}"
REDIS_PORT="${REDIS_PORT:-6379}"
REDIS_USER="${REDIS_USER:-redis}"
REDIS_PASSWORD="${REDIS_PASSWORD:-YourStrongPassword123}" # 请修改为实际密码
MAXMEMORY="${REDIS_MAXMEMORY:-2gb}"
# 主从配置(仅在 replication 或 sentinel 模式下使用)
MASTER_IP="${MASTER_IP:-10.202.17.12}"
SLAVE_IP="${SLAVE_IP:-10.202.17.13}"
# 哨兵配置(仅在 sentinel 模式下使用)
SENTINEL_PORT="${SENTINEL_PORT:-26379}"
SENTINEL_QUORUM="${SENTINEL_QUORUM:-2}"
SENTINEL_IPS="${SENTINEL_IPS:-10.202.17.12 10.202.17.13 10.202.17.14}"
MASTER_NAME="${MASTER_NAME:-mymaster}"
# 节点角色和当前 IP(由 check_node_role 函数设置)
CURRENT_IP=""
NODE_ROLE=""
IS_SENTINEL_NODE=false # 当前节点是否为哨兵节点
# 配置文件完整路径
REDIS_CONF="${CONFIG_DIR}/redis.conf"
SENTINEL_CONF="${CONFIG_DIR}/sentinel.conf"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# ============================================================================
# 工具函数
# ============================================================================
# 日志输出函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
# 检查命令是否存在
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# 检查是否为 root 用户
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本必须以 root 用户运行"
exit 1
fi
}
# 检查当前主机角色(根据部署模式)
check_node_role() {
log_info "检查部署模式: $DEPLOY_MODE"
# 验证部署模式配置
if [[ "$DEPLOY_MODE" != "standalone" && "$DEPLOY_MODE" != "replication" && "$DEPLOY_MODE" != "sentinel" ]]; then
log_error "无效的部署模式: $DEPLOY_MODE"
log_error "有效值: standalone (单机) / replication (主从) / sentinel (哨兵)"
exit 1
fi
# 获取当前主机 IP
CURRENT_IP=$(hostname -I | awk '{print $1}')
log_info "当前主机 IP: $CURRENT_IP"
# 单机模式:直接设置为 standalone
if [[ "$DEPLOY_MODE" == "standalone" ]]; then
NODE_ROLE="standalone"
log_info "部署模式: 单机模式 (Standalone)"
return 0
fi
# 主从模式或哨兵模式:检查当前主机角色
if [[ "$DEPLOY_MODE" == "replication" || "$DEPLOY_MODE" == "sentinel" ]]; then
log_info "部署模式: ${DEPLOY_MODE} 模式"
log_info "检查当前主机角色..."
if [[ "$CURRENT_IP" == "$MASTER_IP" ]]; then
NODE_ROLE="master"
log_info "当前节点为主节点 (Master)"
elif [[ "$CURRENT_IP" == "$SLAVE_IP" ]]; then
NODE_ROLE="slave"
log_info "当前节点为从节点 (Slave)"
else
log_error "当前 IP ($CURRENT_IP) 不在配置的主从节点中"
log_error "配置的主节点: $MASTER_IP"
log_error "配置的从节点: $SLAVE_IP"
log_error "请检查 config.sh 中的 MASTER_IP 和 SLAVE_IP 配置"
exit 1
fi
fi
# 哨兵模式:额外检查是否为哨兵节点
if [[ "$DEPLOY_MODE" == "sentinel" ]]; then
log_info "检查是否为哨兵节点..."
# 将哨兵 IP 列表转换为数组
local sentinel_ips_array=($SENTINEL_IPS)
# 检查当前 IP 是否在哨兵列表中
for sentinel_ip in "${sentinel_ips_array[@]}"; do
if [[ "$CURRENT_IP" == "$sentinel_ip" ]]; then
IS_SENTINEL_NODE=true
log_info "当前节点同时为哨兵节点 (Sentinel)"
break
fi
done
if [[ "$IS_SENTINEL_NODE" == false ]]; then
log_warn "当前节点不在哨兵列表中,将只部署 Redis 服务"
fi
fi
}
# 检查系统信息
check_system() {
log_info "检查系统信息..."
# 检查操作系统
if [[ -f /etc/os-release ]]; then
. /etc/os-release
log_info "操作系统: $PRETTY_NAME"
fi
# 检查架构
ARCH=$(uname -m)
log_info "系统架构: $ARCH"
# 检查内核版本
KERNEL=$(uname -r)
log_info "内核版本: $KERNEL"
}
# ============================================================================
# 依赖检查与安装
# ============================================================================
# 检查并安装编译依赖
install_dependencies() {
log_info "检查并安装编译依赖..."
local packages=(
"gcc"
"make"
"tcl"
"wget"
"tar"
"bzip2"
)
local to_install=()
# 检查哪些包需要安装
for pkg in "${packages[@]}"; do
if ! rpm -q "$pkg" &>/dev/null; then
to_install+=("$pkg")
fi
done
# 如果有需要安装的包
if [[ ${#to_install[@]} -gt 0 ]]; then
log_info "需要安装的包: ${to_install[*]}"
log_info "正在安装依赖包(请稍候...)"
yum install -y "${to_install[@]}" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
log_info "依赖包安装完成"
else
log_warn "部分依赖包安装失败,继续执行..."
fi
else
log_info "所有依赖包已安装"
fi
}
# 编译安装 jemalloc
install_jemalloc() {
log_info "开始编译安装 jemalloc..."
local JEMALLOC_VERSION="5.3.0"
local JEMALLOC_TAR="jemalloc-${JEMALLOC_VERSION}.tar.bz2"
local JEMALLOC_DIR="jemalloc-${JEMALLOC_VERSION}"
local BUILD_LOG="/tmp/jemalloc_build.log"
# 检查是否已安装
if ldconfig -p | grep -q libjemalloc; then
log_info "jemalloc 已安装,跳过编译"
return 0
fi
# 检查源码包是否存在
if [[ ! -f "$JEMALLOC_TAR" ]]; then
log_error "jemalloc 源码包 $JEMALLOC_TAR 不存在"
log_error "请将 jemalloc-5.3.0.tar.bz2 放到当前目录"
return 1
fi
log_info "使用本地 jemalloc 源码包: $JEMALLOC_TAR"
# 解压
log_info "解压 jemalloc..."
tar -xjf "$JEMALLOC_TAR" 2>&1 | tee "$BUILD_LOG" > /dev/null
cd "$JEMALLOC_DIR"
# 生成配置文件
log_info "生成配置文件..."
./autogen.sh >> "$BUILD_LOG" 2>&1 || {
log_warn "autogen.sh 失败,尝试直接 configure"
}
# 配置
log_info "配置 jemalloc..."
./configure --prefix=/usr/local >> "$BUILD_LOG" 2>&1
if [[ $? -ne 0 ]]; then
log_error "配置失败,查看日志: $BUILD_LOG"
cd ..
return 1
fi
# 编译
log_info "编译 jemalloc(使用 $(nproc) 核心,请稍候...)"
make -j$(nproc) >> "$BUILD_LOG" 2>&1
if [[ $? -ne 0 ]]; then
log_error "编译失败,查看日志: $BUILD_LOG"
cd ..
return 1
fi
# 安装
log_info "安装 jemalloc..."
make install >> "$BUILD_LOG" 2>&1
if [[ $? -ne 0 ]]; then
log_error "安装失败,查看日志: $BUILD_LOG"
cd ..
return 1
fi
# 配置库路径
log_info "配置库路径..."
echo "/usr/local/lib" > /etc/ld.so.conf.d/jemalloc.conf
ldconfig
# 返回上级目录
cd ..
# 验证安装
if ldconfig -p | grep -q libjemalloc; then
log_info "jemalloc 安装成功"
# 清理临时文件
rm -rf "$JEMALLOC_DIR"
rm -f "$BUILD_LOG"
return 0
else
log_error "jemalloc 安装失败,查看日志: $BUILD_LOG"
return 1
fi
}
# ============================================================================
# Redis 编译安装
# ============================================================================
# 编译 Redis
compile_redis() {
log_info "开始编译 Redis ${REDIS_VERSION}..."
local BUILD_LOG="/tmp/redis_build.log"
# 检查源码包是否存在
if [[ ! -f "$REDIS_TAR" ]]; then
log_error "Redis 源码包 $REDIS_TAR 不存在"
exit 1
fi
# 解压源码
log_info "解压 Redis 源码..."
tar -zxf "$REDIS_TAR" 2>&1 | tee "$BUILD_LOG" > /dev/null
cd "$REDIS_DIR"
# 使用 jemalloc 编译(Redis 官方推荐方式)
log_info "使用 jemalloc 编译 Redis(使用 $(nproc) 核心,请稍候...)"
make MALLOC=jemalloc -j$(nproc) >> "$BUILD_LOG" 2>&1
if [[ $? -ne 0 ]]; then
log_error "编译失败,查看日志: $BUILD_LOG"
cd ..
exit 1
fi
# 安装
log_info "安装 Redis 到 $INSTALL_DIR..."
make PREFIX="$INSTALL_DIR" install >> "$BUILD_LOG" 2>&1
if [[ $? -ne 0 ]]; then
log_error "安装失败,查看日志: $BUILD_LOG"
cd ..
exit 1
fi
cd ..
log_info "Redis 编译安装完成"
rm -f "$BUILD_LOG"
}
# ============================================================================
# 系统配置
# ============================================================================
# 检查或创建 Redis 用户
check_redis_user() {
log_info "检查 Redis 用户: $REDIS_USER"
if ! id "$REDIS_USER" &>/dev/null; then
log_error "用户 $REDIS_USER 不存在"
log_error "请先创建用户,或修改 config.sh 中的 REDIS_USER 配置"
log_error "创建用户命令: useradd $REDIS_USER"
exit 1
else
log_info "用户 $REDIS_USER 已存在"
fi
}
# 创建目录结构
create_directories() {
log_info "创建目录结构..."
# 创建所有必要的目录
mkdir -p "$INSTALL_DIR"
mkdir -p "$DATA_DIR"
mkdir -p "$LOG_DIR"
mkdir -p "$CONFIG_DIR"
# 统一设置所有目录的所有者为 Redis 用户
log_info "设置目录权限..."
chown -R "$REDIS_USER:$REDIS_USER" "$INSTALL_DIR"
chown -R "$REDIS_USER:$REDIS_USER" "$DATA_DIR"
chown -R "$REDIS_USER:$REDIS_USER" "$LOG_DIR"
chown -R "$REDIS_USER:$REDIS_USER" "$CONFIG_DIR"
log_info "目录创建完成"
}
# 配置系统参数
configure_system() {
log_info "配置系统参数..."
# 配置内核参数(避免重复添加)
if ! grep -q "vm.overcommit_memory" /etc/sysctl.conf; then
log_info "添加内核参数到 /etc/sysctl.conf..."
cat >> /etc/sysctl.conf <<EOF
# Redis 优化参数
vm.overcommit_memory = 1
net.core.somaxconn = 65535
EOF
sysctl -p
else
log_info "内核参数已存在,跳过添加"
fi
# 禁用透明大页
if [[ -f /sys/kernel/mm/transparent_hugepage/enabled ]]; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
log_info "已禁用透明大页"
fi
# 配置 limits(避免重复添加)
if ! grep -q "^$REDIS_USER.*nofile" /etc/security/limits.conf; then
log_info "添加资源限制到 /etc/security/limits.conf..."
cat >> /etc/security/limits.conf <<EOF
# Redis 资源限制
$REDIS_USER soft nofile 65535
$REDIS_USER hard nofile 65535
$REDIS_USER soft nproc 65535
$REDIS_USER hard nproc 65535
EOF
else
log_info "资源限制已存在,跳过添加"
fi
log_info "系统参数配置完成"
}
# ============================================================================
# Redis 配置
# ============================================================================
# 生成 Redis 配置文件
generate_redis_config() {
log_info "生成 Redis 配置文件..."
# 使用之前检查的节点角色
local role="$NODE_ROLE"
local mode_desc=""
# 根据角色设置描述
case "$role" in
standalone)
mode_desc="单机模式 (Standalone)"
;;
master)
if [[ "$DEPLOY_MODE" == "sentinel" ]]; then
mode_desc="哨兵模式 - 主节点 (Sentinel Master)"
else
mode_desc="主从模式 - 主节点 (Master)"
fi
;;
slave)
if [[ "$DEPLOY_MODE" == "sentinel" ]]; then
mode_desc="哨兵模式 - 从节点 (Sentinel Slave)"
else
mode_desc="主从模式 - 从节点 (Slave)"
fi
;;
esac
log_info "生成配置: ${mode_desc}"
# 生成配置文件
cat > "$REDIS_CONF" <<EOF
# Redis ${REDIS_VERSION} 配置文件
# 部署模式: ${mode_desc}
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================================================
# 网络配置
# ============================================================================
bind 0.0.0.0
protected-mode yes
port ${REDIS_PORT}
tcp-backlog 511
timeout 0
tcp-keepalive 300
# ============================================================================
# 通用配置
# ============================================================================
daemonize yes
pidfile ${DATA_DIR}/redis_${REDIS_PORT}.pid
loglevel notice
logfile ${LOG_DIR}/redis.log
databases 16
hz 100
# ============================================================================
# 持久化配置
# ============================================================================
# RDB 配置
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ${DATA_DIR}
# AOF 配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
# ============================================================================
# 安全配置
# ============================================================================
requirepass ${REDIS_PASSWORD}
# ============================================================================
# 内存管理
# ============================================================================
maxmemory ${MAXMEMORY}
maxmemory-policy allkeys-lru
maxmemory-samples 5
# ============================================================================
# 慢查询日志
# ============================================================================
slowlog-log-slower-than 10000
slowlog-max-len 128
# ============================================================================
# 客户端配置
# ============================================================================
maxclients 10000
EOF
# 如果是从节点,添加主从复制配置
if [[ "$role" == "slave" ]]; then
log_info "添加主从复制配置..."
cat >> "$REDIS_CONF" <<EOF
# ============================================================================
# 主从复制配置
# ============================================================================
replicaof ${MASTER_IP} ${REDIS_PORT}
masterauth ${REDIS_PASSWORD}
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
EOF
fi
# 设置配置文件权限
chown "$REDIS_USER:$REDIS_USER" "$REDIS_CONF"
chmod 640 "$REDIS_CONF"
log_info "Redis 配置文件生成完成: $REDIS_CONF"
}
# 生成哨兵配置文件
generate_sentinel_config() {
log_info "生成 Sentinel 配置文件..."
# 生成哨兵配置
cat > "$SENTINEL_CONF" <<EOF
# Redis Sentinel ${REDIS_VERSION} 配置文件
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================================================
# 网络配置
# ============================================================================
bind 0.0.0.0
port ${SENTINEL_PORT}
daemonize yes
pidfile ${DATA_DIR}/redis-sentinel.pid
logfile ${LOG_DIR}/sentinel.log
# ============================================================================
# 监控配置
# ============================================================================
# 监控主节点:sentinel monitor <master-name> <ip> <port> <quorum>
# quorum: 判定主节点下线所需的哨兵投票数
sentinel monitor ${MASTER_NAME} ${MASTER_IP} ${REDIS_PORT} ${SENTINEL_QUORUM}
# 主节点密码
sentinel auth-pass ${MASTER_NAME} ${REDIS_PASSWORD}
# ============================================================================
# 故障转移配置
# ============================================================================
# 主节点无响应超时时间(毫秒)
sentinel down-after-milliseconds ${MASTER_NAME} 5000
# 故障转移超时时间(毫秒)
sentinel failover-timeout ${MASTER_NAME} 15000
# 同时进行同步的从节点数量
sentinel parallel-syncs ${MASTER_NAME} 1
EOF
# 设置配置文件权限
chown "$REDIS_USER:$REDIS_USER" "$SENTINEL_CONF"
chmod 640 "$SENTINEL_CONF"
log_info "Sentinel 配置文件生成完成: $SENTINEL_CONF"
}
# ============================================================================
# Systemd 服务配置
# ============================================================================
# 创建 systemd 服务文件
create_systemd_service() {
log_info "创建 systemd 服务..."
# 创建 Redis 服务
cat > /etc/systemd/system/redis.service <<EOF
[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
Type=forking
User=${REDIS_USER}
Group=${REDIS_USER}
PIDFile=${DATA_DIR}/redis_${REDIS_PORT}.pid
ExecStart=${INSTALL_DIR}/bin/redis-server ${REDIS_CONF}
ExecStop=/bin/kill -s TERM \$MAINPID
TimeoutStartSec=30s
TimeoutStopSec=30s
Restart=always
RestartSec=5s
LimitNOFILE=65535
# 安全加固
PrivateTmp=yes
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOF
log_info "Redis 服务创建完成"
# 如果是哨兵模式且当前节点是哨兵节点,创建哨兵服务
if [[ "$DEPLOY_MODE" == "sentinel" && "$IS_SENTINEL_NODE" == true ]]; then
log_info "创建 Sentinel 服务..."
cat > /etc/systemd/system/redis-sentinel.service <<EOF
[Unit]
Description=Redis Sentinel
After=network.target redis.service
Requires=redis.service
[Service]
Type=forking
User=${REDIS_USER}
Group=${REDIS_USER}
PIDFile=${DATA_DIR}/redis-sentinel.pid
ExecStart=${INSTALL_DIR}/bin/redis-sentinel ${SENTINEL_CONF}
ExecStop=/bin/kill -s TERM \$MAINPID
TimeoutStartSec=30s
TimeoutStopSec=30s
Restart=always
RestartSec=5s
LimitNOFILE=65535
# 安全加固
PrivateTmp=yes
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOF
log_info "Sentinel 服务创建完成"
fi
# 重载 systemd
systemctl daemon-reload
log_info "Systemd 服务配置完成"
}
# ============================================================================
# 启动与验证
# ============================================================================
# 启动 Redis 服务
start_redis() {
log_info "启动 Redis 服务..."
systemctl enable redis
systemctl start redis
# 等待服务启动
sleep 3
# 检查服务状态
if systemctl is-active --quiet redis; then
log_info "Redis 服务启动成功"
else
log_error "Redis 服务启动失败"
systemctl status redis
exit 1
fi
# 如果是哨兵模式且当前节点是哨兵节点,启动哨兵服务
if [[ "$DEPLOY_MODE" == "sentinel" && "$IS_SENTINEL_NODE" == true ]]; then
log_info "启动 Sentinel 服务..."
systemctl enable redis-sentinel
systemctl start redis-sentinel
# 等待服务启动
sleep 3
# 检查服务状态
if systemctl is-active --quiet redis-sentinel; then
log_info "Sentinel 服务启动成功"
else
log_error "Sentinel 服务启动失败"
systemctl status redis-sentinel
exit 1
fi
fi
}
# 验证 Redis 安装
verify_redis() {
log_info "验证 Redis 安装..."
# 检查 Redis 版本
local version=$("$INSTALL_DIR/bin/redis-server" --version | awk '{print $3}' | cut -d'=' -f2)
log_info "Redis 版本: $version"
# 检查 jemalloc(通过 Redis INFO 命令)
local allocator=$("$INSTALL_DIR/bin/redis-cli" -a "$REDIS_PASSWORD" --no-auth-warning \
INFO memory 2>/dev/null | grep -E 'mem_allocator' | awk -F: '{print $2}' | tr -d '\r')
if [[ "$allocator" =~ jemalloc ]]; then
log_info "Redis 正在使用 jemalloc 内存分配器: $allocator"
else
log_warn "Redis 未使用 jemalloc,当前内存分配器: ${allocator:-未知}"
fi
# 测试连接(隐藏密码警告)
log_info "测试 Redis 连接..."
if "$INSTALL_DIR/bin/redis-cli" -a "$REDIS_PASSWORD" --no-auth-warning ping 2>/dev/null | grep -q PONG; then
log_info "Redis 连接测试成功"
else
log_error "Redis 连接测试失败"
exit 1
fi
# 根据部署模式检查状态
if [[ "$DEPLOY_MODE" == "replication" ]]; then
log_info "检查主从复制状态..."
"$INSTALL_DIR/bin/redis-cli" -a "$REDIS_PASSWORD" --no-auth-warning info replication 2>/dev/null
elif [[ "$DEPLOY_MODE" == "sentinel" ]]; then
log_info "检查主从复制状态..."
"$INSTALL_DIR/bin/redis-cli" -a "$REDIS_PASSWORD" --no-auth-warning info replication 2>/dev/null
# 如果当前节点是哨兵节点,检查哨兵状态
if [[ "$IS_SENTINEL_NODE" == true ]]; then
log_info "检查 Sentinel 状态..."
"$INSTALL_DIR/bin/redis-cli" -p "$SENTINEL_PORT" --no-auth-warning sentinel masters 2>/dev/null
fi
else
log_info "单机模式部署,跳过主从状态检查"
fi
}
# ============================================================================
# 清理函数
# ============================================================================
cleanup() {
log_info "清理临时文件..."
# 清理 Redis 临时目录
if [[ -d "$REDIS_DIR" ]]; then
rm -rf "$REDIS_DIR"
log_info "已清理 Redis 临时文件"
fi
# 清理 jemalloc 临时目录
if [[ -d "jemalloc-5.3.0" ]]; then
rm -rf "jemalloc-5.3.0"
log_info "已清理 jemalloc 临时文件"
fi
}
# ============================================================================
# 主函数
# ============================================================================
main() {
log_info "=========================================="
log_info "Redis ${REDIS_VERSION} 安装脚本"
log_info "=========================================="
# 前置检查
check_root
check_node_role # 检查部署模式和节点角色
check_redis_user # 检查 Redis 用户是否存在
check_system
# 安装依赖
install_dependencies
# 编译安装 jemalloc
install_jemalloc
# 编译安装 Redis
compile_redis
# 系统配置
create_directories
configure_system
# Redis 配置
generate_redis_config
# 哨兵模式:如果当前节点是哨兵节点,生成哨兵配置
if [[ "$DEPLOY_MODE" == "sentinel" && "$IS_SENTINEL_NODE" == true ]]; then
generate_sentinel_config
fi
create_systemd_service
# 启动服务
start_redis
# 验证安装
verify_redis
# 清理
# cleanup
# 输出安装信息
log_info "=========================================="
log_info "Redis 安装完成!"
log_info "=========================================="
log_info "部署模式: $DEPLOY_MODE"
log_info "节点角色: $NODE_ROLE"
log_info "安装目录: $INSTALL_DIR"
log_info "配置文件: $REDIS_CONF"
log_info "数据目录: $DATA_DIR"
log_info "日志目录: $LOG_DIR"
log_info "服务管理: systemctl {start|stop|restart|status} redis"
log_info "连接命令: $INSTALL_DIR/bin/redis-cli -a $REDIS_PASSWORD"
# 主从模式额外提示
if [[ "$DEPLOY_MODE" == "replication" ]]; then
log_info "----------------------------------------"
log_info "主从复制信息:"
log_info " 主节点: $MASTER_IP"
log_info " 从节点: $SLAVE_IP"
log_info " 查看复制状态: $INSTALL_DIR/bin/redis-cli -a $REDIS_PASSWORD info replication"
fi
# 哨兵模式额外提示
if [[ "$DEPLOY_MODE" == "sentinel" ]]; then
log_info "----------------------------------------"
log_info "哨兵模式信息:"
log_info " 主节点: $MASTER_IP"
log_info " 从节点: $SLAVE_IP"
log_info " 哨兵节点: $SENTINEL_IPS"
log_info " 哨兵端口: $SENTINEL_PORT"
log_info " 主节点名称: $MASTER_NAME"
log_info " 查看复制状态: $INSTALL_DIR/bin/redis-cli -a $REDIS_PASSWORD info replication"
if [[ "$IS_SENTINEL_NODE" == true ]]; then
log_info " 哨兵配置: $SENTINEL_CONF"
log_info " 哨兵服务: systemctl {start|stop|restart|status} redis-sentinel"
log_info " 查看哨兵状态: $INSTALL_DIR/bin/redis-cli -p $SENTINEL_PORT sentinel masters"
fi
fi
log_info "=========================================="
}
# 执行主函数
main "$@"