Triển Khai Nhanh Redis Cluster Từ Single Redis

10 Likes Comment

Khi bạn phát triển hệ thống thì ban đầu ai cũng bắt đầu từ một Single Database ở đây là Redis để tiết kiệm chi phí nếu dự án ổn thì ta mở rộng quy mô sau, tuy nhiên khi mở rộng quy mô ta gặp vấn đề về việc triển khai mở rộng làm sao cho máy chủ, trên mạng bạn có thể tìm hiểu và thấy nhiều bài hướng dẫn về triển khai mô hình Redis Cluster tuy nhiên trong bài này thì mình chia sẻ kết quả của một script giúp bạn chạy phát ăn luôn không cần làm gì gõ cái là có luôn hệ thống cluster và slave từ một máy chủ Single Database

I: Mô hình triển khai

Trong bài này thì mình làm 4 máy chủ theo hình thức cluster mỗi cluster thì có một máy chủ master và một máy chủ slave


Lưu ý:

  • Máy chủ Master 1 là máy chủ đã có Redis đang chạy
  • Các máy chủ còn lại là chưa cài đặt gì
  • Tools có thể chạy ở một máy chủ trung gian hay máy cá nhân không cần phải truy cập vào bất cứu server nào

Thông số các cấu hình

Master 1Master 2Slave 1Slave 2
IP10.130.0.710.130.0.510.130.0.610.130.0.3
SSH Pass Root9V23dYJk5ZqDdNGU9V23dYJk5ZqDdNGU9V23dYJk5ZqDdNGU9V23dYJk5ZqDdNGU
Redis PassfoobaredN/AN/AN/A

Các thông tin trong bảng thì bạn thay vào script

II: File Bash chạy triển khai toàn bộ

#!/bin/bash
#---------------------------------------------------------------------
# Script triển khai 2 cụm Redis (Master-Slave) trên 4 máy chủ và kiểm tra
# trạng thái replication của cả master và slave.
#
# --- Cụm 1 ---
# Master: Server1
# Slave:  Server2
#
# --- Cụm 2 ---
# Master: Server3
# Slave:  Server4
#
# Thông tin các máy chủ:
#
# Server1 (Master cụm 1):
#   IP: 10.10.10.1
#   SSH: user: root, pass: 555555555555
#
# Server2 (Slave cụm 1):
#   IP: 10.10.10.2
#   SSH: user: root, pass: 987654321
#
# Server3 (Master cụm 2):
#   IP: 10.10.10.3
#   SSH: user: root, pass: 987654321
#
# Server4 (Slave cụm 2):
#   IP: 10.10.10.4
#   SSH: user: root, pass: 987654321
#
# Quy trình:
#   1. Phát hiện phiên bản Redis trên Server1.
#   2. Kiểm tra/cài đặt Redis trên tất cả các máy chủ theo phiên bản đó.
#   3. Cấu hình vai trò cho từng máy chủ:
#         - Master: không có dòng "slaveof"
#         - Slave: thêm dòng "slaveof <MASTER_IP> 6379"
#      Kèm theo cấu hình bảo mật (requirepass, masterauth) và bind 0.0.0.0.
#   4. Restart dịch vụ Redis trên từng máy chủ.
#   5. Kiểm tra trạng thái replication:
#         - Trên các máy chủ slave: hiển thị "master_link_status"
#         - Trên các máy chủ master: in đầy đủ thông tin replication (role, số lượng slave,...)
#
# Yêu cầu:
#   - sshpass phải được cài đặt trên máy chủ chạy script này.
#
# CẢNH BÁO: Script sẽ thay đổi file cấu hình và cài đặt phần mềm trên các máy chủ.
#---------------------------------------------------------------------

set -e

check_sshpass() {
  if ! command -v sshpass &>/dev/null; then
    echo ">> sshpass chưa được cài đặt. Đang tiến hành cài đặt..."
    if command -v apt-get &>/dev/null; then
      apt-get update && apt-get install -y sshpass
    elif command -v yum &>/dev/null; then
      yum install -y sshpass
    else
      echo ">> Không tìm thấy package manager (apt-get hoặc yum). Vui lòng cài đặt sshpass theo cách thủ công."
      exit 1
    fi
  else
    echo ">> sshpass đã được cài đặt."
  fi
}

# Gọi hàm kiểm tra sshpass trước khi thực hiện các tác vụ khác
check_sshpass



###############################
# Thông số cấu hình chung
###############################
REDIS_PASSWORD="foobared"
REDIS_PORT="6379"
# File cấu hình mặc định sau khi cài Redis từ source
REDIS_CONF="/etc/redis/redis.conf"

###############################
# Thông tin các máy chủ
###############################
# --- Cụm 1 ---
MASTER1_IP="10.130.0.7"
MASTER1_SSH_USER="root"
MASTER1_SSH_PASS="9V23dYJk5ZqDdNGU"

SLAVE1_IP="10.130.0.5"
SLAVE1_SSH_USER="root"
SLAVE1_SSH_PASS="9V23dYJk5ZqDdNGU"

# --- Cụm 2 ---
MASTER2_IP="10.130.0.6"
MASTER2_SSH_USER="root"
MASTER2_SSH_PASS="9V23dYJk5ZqDdNGU"

SLAVE2_IP="10.130.0.3"
SLAVE2_SSH_USER="root"
SLAVE2_SSH_PASS="9V23dYJk5ZqDdNGU"

#########################################
# Hàm: detect_redis_version
# Mục đích: Lấy phiên bản Redis đang chạy trên một máy chủ.
#########################################
detect_redis_version() {
  local server_ip="$1"
  local ssh_user="$2"
  local ssh_pass="$3"
  local version_line
  version_line=$(sshpass -p "$ssh_pass" ssh -o StrictHostKeyChecking=no ${ssh_user}@${server_ip} "redis-server --version" 2>/dev/null || echo "")
  if [ -z "$version_line" ]; then
      echo "none"
  else
      # Ví dụ: "Redis server v=6.0.9 ..."
      local version
      version=$(echo "$version_line" | awk '{for(i=1;i<=NF;i++) { if ($i ~ /^v=/) { split($i, a, "="); print a[2]; break } } }')
      echo "$version"
  fi
}

#########################################
# Hàm: build_and_install_redis
# Mục đích: Build và cài đặt Redis từ source với phiên bản chỉ định.
#########################################
build_and_install_redis() {
  local server_ip="$1"
  local ssh_user="$2"
  local ssh_pass="$3"
  local target_version="$4"
  echo ">> [${server_ip}] Build và cài đặt Redis version ${target_version} từ source..."
  sshpass -p "$ssh_pass" ssh -o StrictHostKeyChecking=no ${ssh_user}@${server_ip} bash <<EOF
apt-get update && apt-get install -y build-essential tcl wget
cd /tmp
wget http://download.redis.io/releases/redis-${target_version}.tar.gz
tar xzf redis-${target_version}.tar.gz
cd redis-${target_version}
make
make install
# Tạo thư mục cho cấu hình nếu chưa có và copy file cấu hình mẫu
[ ! -d /etc/redis ] && mkdir -p /etc/redis
cp redis.conf /etc/redis/redis.conf
# Tạo file unit systemd để quản lý dịch vụ Redis
cat <<EOT > /etc/systemd/system/redis.service
[Unit]
Description=Redis In-Memory Data Store
After=network.target

[Service]
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always

[Install]
WantedBy=multi-user.target
EOT
systemctl daemon-reload
systemctl enable redis.service
systemctl start redis.service
EOF
}

#########################################
# Hàm: check_and_install_redis
# Mục đích: Kiểm tra sự tồn tại của Redis trên server; nếu chưa có thì cài đặt
#            từ source với phiên bản target_version. Nếu đã cài nhưng phiên bản
#            không khớp, sẽ đưa ra cảnh báo.
#########################################
check_and_install_redis() {
   local server_ip="$1"
   local ssh_user="$2"
   local ssh_pass="$3"
   local target_version="$4"
   echo ">> [${server_ip}] Kiểm tra sự tồn tại của Redis..."
   local version_line
   version_line=$(sshpass -p "$ssh_pass" ssh -o StrictHostKeyChecking=no ${ssh_user}@${server_ip} "redis-server --version" 2>/dev/null || echo "")
   if [ -z "$version_line" ]; then
       echo ">> [${server_ip}] Chưa cài Redis. Tiến hành cài đặt Redis version ${target_version} từ source..."
       build_and_install_redis "$server_ip" "$ssh_user" "$ssh_pass" "$target_version"
   else
       local installed_version
       installed_version=$(echo "$version_line" | awk '{for(i=1;i<=NF;i++) { if ($i ~ /^v=/) { split($i, a, "="); print a[2]; break } } }')
       if [ "$installed_version" != "$target_version" ]; then
           echo ">> [${server_ip}] Cảnh báo: Phiên bản Redis hiện tại ($installed_version) KHÔNG khớp với phiên bản mong muốn ($target_version)."
           # Có thể bổ sung logic cập nhật/hạ cấp nếu cần.
       else
           echo ">> [${server_ip}] Redis version $installed_version đã được cài đặt."
       fi
   fi
}

#########################################
# Hàm: configure_redis_role
# Mục đích: Cấu hình file cấu hình Redis cho vai trò master hoặc slave.
#   - Nếu role là "master": đảm bảo không có dòng "slaveof".
#   - Nếu role là "slave": thêm dòng "slaveof <MASTER_IP> <REDIS_PORT>".
# Đồng thời, thêm các thông số bảo mật (requirepass, masterauth) và bind 0.0.0.0.
#########################################
configure_redis_role() {
  local server_ip="$1"
  local ssh_user="$2"
  local ssh_pass="$3"
  local role="$4"         # "master" hoặc "slave"
  local master_ip="$5"    # Chỉ dùng khi role là "slave"
  echo ">> [${server_ip}] Cấu hình Redis với vai trò ${role}..."
  sshpass -p "$ssh_pass" ssh -o StrictHostKeyChecking=no ${ssh_user}@${server_ip} bash <<EOF
# Xác định file cấu hình Redis
if [ -f /etc/redis/redis.conf ]; then
  CONF_FILE="/etc/redis/redis.conf"
elif [ -f /etc/redis.conf ]; then
  CONF_FILE="/etc/redis.conf"
else
  echo "Không tìm thấy file cấu hình Redis trên ${server_ip}!"
  exit 1
fi

# Sao lưu file cấu hình hiện tại
cp \$CONF_FILE \${CONF_FILE}.bak

# Loại bỏ các dòng liên quan đến slaveof, requirepass và masterauth cũ (nếu có)
sed -i '/^slaveof/d' \$CONF_FILE
sed -i '/^requirepass/d' \$CONF_FILE
sed -i '/^masterauth/d' \$CONF_FILE

# Cấu hình bảo mật
echo "requirepass ${REDIS_PASSWORD}" >> \$CONF_FILE
echo "masterauth ${REDIS_PASSWORD}" >> \$CONF_FILE

# Nếu vai trò là slave thì thêm dòng "slaveof"
if [ "$role" = "slave" ]; then
  echo "slaveof ${master_ip} ${REDIS_PORT}" >> \$CONF_FILE
fi

# Đảm bảo Redis lắng nghe trên mọi interface
sed -i '/^bind/d' \$CONF_FILE
echo "bind 0.0.0.0" >> \$CONF_FILE

# Restart dịch vụ Redis
if command -v systemctl >/dev/null 2>&1; then
  if systemctl status redis.service >/dev/null 2>&1; then
    systemctl restart redis.service
  elif systemctl status redis-server >/dev/null 2>&1; then
    systemctl restart redis-server
  else
    echo "Không tìm thấy dịch vụ Redis qua systemctl. Vui lòng restart Redis thủ công."
  fi
else
  if service redis status >/dev/null 2>&1; then
    service redis restart
  elif service redis-server status >/dev/null 2>&1; then
    service redis-server restart
  else
    echo "Không tìm thấy dịch vụ Redis qua lệnh service. Vui lòng restart Redis thủ công."
  fi
fi
EOF
}

#########################################
# Hàm: check_replication_status
# Mục đích: Kiểm tra trạng thái replication trên một máy chủ (thường dùng cho slave).
# Lấy thông tin từ "info replication" và lọc các dòng liên quan.
#########################################
check_replication_status() {
  local server_ip="$1"
  local ssh_user="$2"
  local ssh_pass="$3"
  echo ">> [${server_ip}] Kiểm tra trạng thái replication (Slave)..."
  sshpass -p "$ssh_pass" ssh -o StrictHostKeyChecking=no ${ssh_user}@${server_ip} bash <<EOF
echo "----- Replication Status trên ${server_ip} (Slave) -----"
redis-cli -a ${REDIS_PASSWORD} info replication | grep -E "role:|master_link_status:"
echo "---------------------------------------------------------"
EOF
}

#########################################
# Hàm: check_master_status
# Mục đích: Kiểm tra trạng thái replication trên máy chủ master.
# In đầy đủ thông tin từ "info replication" để xem số lượng slave kết nối,…
#########################################
check_master_status() {
  local server_ip="$1"
  local ssh_user="$2"
  local ssh_pass="$3"
  echo ">> [${server_ip}] Kiểm tra trạng thái replication (Master)..."
  sshpass -p "$ssh_pass" ssh -o StrictHostKeyChecking=no ${ssh_user}@${server_ip} bash <<EOF
echo "----- Replication Status trên ${server_ip} (Master) -----"
redis-cli -a ${REDIS_PASSWORD} info replication
echo "---------------------------------------------------------"
EOF
}

#########################################
# Quy trình chính
#########################################
echo "======================================="
echo "Bắt đầu triển khai 2 cụm Redis (Master-Slave)"
echo "======================================="

# 1. Phát hiện phiên bản Redis trên Server1 (Master của cụm 1)
target_version=$(detect_redis_version "${MASTER1_IP}" "${MASTER1_SSH_USER}" "${MASTER1_SSH_PASS}")
if [ "$target_version" = "none" ]; then
  echo "Error: Không phát hiện được phiên bản Redis trên ${MASTER1_IP}. Vui lòng kiểm tra lại."
  exit 1
fi
echo ">> Phiên bản Redis trên ${MASTER1_IP}: $target_version"

# 2. Kiểm tra / cài đặt Redis trên tất cả các máy chủ theo phiên bản target
for server in "${MASTER1_IP}:${MASTER1_SSH_USER}:${MASTER1_SSH_PASS}" \
              "${SLAVE1_IP}:${SLAVE1_SSH_USER}:${SLAVE1_SSH_PASS}" \
              "${MASTER2_IP}:${MASTER2_SSH_USER}:${MASTER2_SSH_PASS}" \
              "${SLAVE2_IP}:${SLAVE2_SSH_USER}:${SLAVE2_SSH_PASS}"; do
  IFS=":" read -r ip user pass <<< "$server"
  check_and_install_redis "$ip" "$user" "$pass" "$target_version"
done

# 3. Cấu hình vai trò cho từng máy chủ
# --- Cụm 1 ---
echo ">> Cấu hình cụm 1:"
configure_redis_role "${MASTER1_IP}" "${MASTER1_SSH_USER}" "${MASTER1_SSH_PASS}" "master"
configure_redis_role "${SLAVE1_IP}" "${SLAVE1_SSH_USER}" "${SLAVE1_SSH_PASS}" "slave" "${MASTER1_IP}"

# --- Cụm 2 ---
echo ">> Cấu hình cụm 2:"
configure_redis_role "${MASTER2_IP}" "${MASTER2_SSH_USER}" "${MASTER2_SSH_PASS}" "master"
configure_redis_role "${SLAVE2_IP}" "${SLAVE2_SSH_USER}" "${SLAVE2_SSH_PASS}" "slave" "${MASTER2_IP}"

# Chờ vài giây để dịch vụ khởi động lại
sleep 5

# 4. Kiểm tra trạng thái replication:
echo "======================================="
echo "Kiểm tra trạng thái replication trên các Slave"
echo "======================================="
check_replication_status "${SLAVE1_IP}" "${SLAVE1_SSH_USER}" "${SLAVE1_SSH_PASS}"
check_replication_status "${SLAVE2_IP}" "${SLAVE2_SSH_USER}" "${SLAVE2_SSH_PASS}"

echo "======================================="
echo "Kiểm tra trạng thái replication trên các Master"
echo "======================================="
check_master_status "${MASTER1_IP}" "${MASTER1_SSH_USER}" "${MASTER1_SSH_PASS}"
check_master_status "${MASTER2_IP}" "${MASTER2_SSH_USER}" "${MASTER2_SSH_PASS}"

echo "======================================="
echo "Triển khai 2 cụm Redis (Master-Slave) và kiểm tra trạng thái hoàn tất."

Lưu ý:

  • Trong mã nguồn này hệ thống sẽ kiểm tra và cài đặt các thành phần cần thiết để chạy
  • Tools chạy sẽ phát hiên version của redis đang dùng và cài đặt trên các máy chủ khác qua build cùng phiên bản đảm bảo đồng bộ

Sau khi chạy chúng ta có kết quả

III: Kiểm tra đồng bộ

Để kiểm tra mình dùng một đoạn code như sau để kiểm tra

#!/bin/bash
# ------------------------------------------------------------
# Script mẫu để chèn dữ liệu vào Redis nhằm kiểm tra sự đồng bộ
# của các node (cluster) trong hệ thống.
#
# Yêu cầu:
#   - Đã cài đặt redis-cli (kiểm tra bằng lệnh: redis-cli --version)
#
# Cách sử dụng:
#   Nếu redis của bạn có password, hãy xuất biến môi trường REDIS_PASSWORD:
#       export REDIS_PASSWORD=your_redis_password
#
#   Sau đó chạy script:
#       ./insert_sample_data.sh host1:port1 host2:port2 ...
#
# Ví dụ:
#   export REDIS_PASSWORD=123456
#   ./insert_sample_data.sh 192.168.1.100:6379 192.168.1.101:6379 192.168.1.102:6379
#
# Script sẽ:
#   1. Chèn một key mẫu vào node đầu tiên.
#   2. Chờ một vài giây để dữ liệu được đồng bộ (nếu hệ thống có cơ chế replication).
#   3. Kiểm tra xem key đó có tồn tại và có giá trị đúng trên các node khác không.
# ------------------------------------------------------------

# Kiểm tra số lượng tham số
if [ "$#" -lt 1 ]; then
    echo "Usage: $0 host:port [host:port ...]"
    echo "Nếu redis của bạn có password, hãy xuất biến môi trường REDIS_PASSWORD trước khi chạy script."
    exit 1
fi

# Nếu có password được thiết lập qua biến môi trường, thêm tùy chọn -a cho redis-cli
AUTH_PARAM=""
if [ ! -z "$REDIS_PASSWORD" ]; then
    AUTH_PARAM="-a ${REDIS_PASSWORD}"
    echo "Sử dụng password từ biến môi trường REDIS_PASSWORD."
fi

# Cấu hình dữ liệu mẫu
KEY_PREFIX="sample"
SAMPLE_DATA="HelloRedisCluster"
TIMESTAMP=$(date +%s)
KEY="${KEY_PREFIX}:${TIMESTAMP}"

echo "Chèn dữ liệu mẫu: key = ${KEY}, value = ${SAMPLE_DATA}"

# Lấy node đầu tiên (sẽ dùng để chèn dữ liệu mẫu)
FIRST_NODE=$1
FIRST_HOST=$(echo $FIRST_NODE | cut -d':' -f1)
FIRST_PORT=$(echo $FIRST_NODE | cut -d':' -f2)

echo "Chèn dữ liệu vào node đầu tiên: ${FIRST_HOST}:${FIRST_PORT}"
redis-cli -h "${FIRST_HOST}" -p "${FIRST_PORT}" ${AUTH_PARAM} SET "${KEY}" "${SAMPLE_DATA}"
if [ $? -ne 0 ]; then
    echo "Lỗi: Không thể chèn dữ liệu vào ${FIRST_HOST}:${FIRST_PORT}"
    exit 1
fi

# Đợi một chút để dữ liệu có thể được đồng bộ (nếu hệ thống có cơ chế replication)
SLEEP_TIME=2
echo "Đợi ${SLEEP_TIME} giây để dữ liệu được đồng bộ..."
sleep "${SLEEP_TIME}"

# Kiểm tra dữ liệu trên tất cả các node đã chỉ định
echo "Bắt đầu kiểm tra dữ liệu trên các node..."
for NODE in "$@"
do
    HOST=$(echo "${NODE}" | cut -d':' -f1)
    PORT=$(echo "${NODE}" | cut -d':' -f2)
    echo "Kiểm tra key ${KEY} trên node ${HOST}:${PORT}..."
    VALUE=$(redis-cli -h "${HOST}" -p "${PORT}" ${AUTH_PARAM} GET "${KEY}")
    
    if [ "${VALUE}" == "${SAMPLE_DATA}" ]; then
        echo "Thành công: Node ${HOST}:${PORT} có dữ liệu đúng."
    else
        echo "Lỗi: Node ${HOST}:${PORT} không có dữ liệu hoặc dữ liệu không khớp."
    fi
done

echo "Hoàn thành kiểm tra dữ liệu mẫu."

Để chạy được chúng ta lưu code lại thành file insert_sample_data.sh và chạy lệnh sau

export REDIS_PASSWORD=foobared && ./insert_sample_data.sh 10.130.0.7:6379 10.130.0.5:6379 10.130.0.6:6379 10.130.0.3:6379

Kết quả ta có như sau

Lưu ý: bạn sẽ thấy một cụm cluser nào đó không có dữ liệu lý do là redis nó chia dữ liệu theo cụm vì thế bạn sẽ chỉ thấy một cụm có dữ liệu mô hình sharing của redis

Khi sử dụng Redis Cluster, toàn bộ dữ liệu được chia ra thành các hash slot và mỗi master (với các slave tương ứng) chỉ chịu trách nhiệm một phần các slot đó. Vì vậy, nếu bạn chèn dữ liệu vào một master của cụm nào đó thì chỉ có master và slave của cụm đó có dữ liệu, còn các node khác thì không

Từ đó code của bạn phải hỗ trợ cluster redis và đọc theo hashing của dữ liệu để biết sẽ ghi vào máy chủ nào

0 0 votes
Article Rating

You might like

Avatar

About the Author: Vô Ưu

Xin chào! Tôi là Vouu, người sáng lập blog này. Tôi là một người yêu thích. Tôi có một công việc ban ngày với tư cách là một chuyên viên bơm vá săm xe các loại, và trang web này là một trong những hoạt động yêu thích của tôi, đặc biệt là trong những lúc rảnh rỗi. Hy vọng bạn thích sử dụng trang web này, và nó sẽ mang lại cho bạn nhiều điều bổ ích. Phản hồi từ người đọc sẽ truyền cảm hứng cho tôi để tôi có thể viết được nhiều hơn nữa !. Bạn chỉ cần để lại nhận xét bên dưới nếu bạn thấy bài viết này hữu ích. Chúc bạn ngày mới tốt lành!
Subscribe
Notify of
guest
0 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x