Bài 03
High Availability: Keepalived VRRP + MySQL Replication
Bài 03 — High Availability: Keepalived VRRP + MySQL Replication
Đến bài này, hệ thống của bạn đã có:
- Bài 01: 1 site WordPress trên 1 server.
- Bài 02: 1 nginx proxy load balance đến 2 backend.
Vẫn còn 2 điểm chết (single point of failure):
- Proxy chỉ có 1 con — proxy die thì cả site sập. DNS chỉ trỏ 1 IP, không biết chuyển đi đâu.
- Database chỉ có 1 con — DB die thì backend cũng vô dụng. Có 10 backend cũng không cứu được.
Bài này là phần cuối — biến hệ thống thành thực sự HA: proxy có cả Master + Backup tự động chuyển khi 1 con die, DB có replica đứng sẵn nhận traffic khi DB chính die.
Đây là phần khó nhất của chặng này. Đọc xong là bạn dựng được kiến trúc mà mọi web service production thực sự đang chạy.
Mô hình HA tổng thể
Sau bài này, hệ thống sẽ trông như sau:
┌──────────────┐
│ Client │
└──────┬───────┘
│ https://test.com
│ DNS trỏ về VIP
▼
┌──────────────────┐
│ VIP 192.168.32.100│ ← IP ảo
└─────────┬─────────┘
│ (Keepalived VRRP)
┌──────────────────┴──────────────────┐
│ │
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ Proxy MASTER │ ◄── VRRP ────► │ Proxy BACKUP │
│ 192.168.32.135 │ (multicast) │ 192.168.32.136 │
│ - nginx │ │ - nginx │
│ - keepalived │ │ - keepalived │
└─────────┬──────────┘ └─────────┬──────────┘
│ │
└──────────────┬───────────────────────┘
│ load balance
┌─────────────┴────────────┐
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ Backend 1 │ │ Backend 2 │
│ 192.168.32.133 │ │ 192.168.32.134 │
│ - nginx │ │ - nginx │
│ - php-fpm │ │ - php-fpm │
│ - WordPress │ │ - WordPress │
└─────────┬──────────┘ └─────────┬──────────┘
│ │
└──────────────┬───────────┘
│ MySQL connections
┌─────────────┴────────────┐
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ DB MASTER │ ──►│ DB SLAVE │
│ 192.168.32.137 │binlog 192.168.32.138 │
│ - MariaDB │ │ - MariaDB │
│ (write + read) │ │ (read only) │
└────────────────────┘ └────────────────────┘
(replication async)3 lớp HA:
- Lớp VIP (Keepalived VRRP) — 1 IP ảo, 2 proxy luân phiên giữ.
- Lớp Backend (nginx load balance) — đã có ở Bài 02.
- Lớp Database (MySQL Replication) — Master ghi, Slave đọc + standby.
Bài này dạy từ trên xuống. Phần A: Keepalived VRRP. Phần B: MySQL Replication. Phần C: gắn cả 3 lớp.
Phần A — Keepalived VRRP
Tổng quan về Keepalived
Keepalived là phần mềm dùng để cung cấp tính sẵn sàng cao (High Availability — HA) và cân bằng tải (Load Balancing) trong các hệ thống máy chủ. Triển khai trên Linux để đảm bảo các dịch vụ quan trọng luôn hoạt động ngay cả khi xảy ra lỗi.
Keepalived ban đầu được thiết kế để bảo vệ các dịch vụ LVS (Linux Virtual Server) bằng cách sử dụng giao thức VRRP (Virtual Router Redundancy Protocol). Tuy nhiên, nó đã phát triển để hỗ trợ nhiều tính năng:
- Failover: đảm bảo dịch vụ luôn sẵn sàng bằng cách chuyển đổi giữa Master và Backup.
- Load Balancing: phân phối lưu lượng truy cập đến nhiều backend.
- Health Check: theo dõi tình trạng của dịch vụ backend và loại bỏ node không hoạt động.
3 thành phần chính của Keepalived
- VRRP (Virtual Router Redundancy Protocol): quản lý IP ảo (Virtual IP — VIP) giữa các máy chủ để thực hiện failover.
- Health Checkers: kiểm tra tình trạng các dịch vụ backend.
- Configuration Scripts: thực thi các hành động tùy chỉnh dựa trên trạng thái hệ thống.
Ở bài này tập trung dùng Keepalived VRRP để làm HA, kết hợp với nginx proxy đã làm Load Balancing ở Bài 02.
VRRP là gì
VRRP (Virtual Router Redundancy Protocol) là giao thức được sử dụng để đảm bảo tính khả dụng và dự phòng cho các router hoặc gateway trong mạng.
Keepalived sử dụng VRRP để cung cấp tính năng dự phòng gateway cho các máy chủ trong mạng, đảm bảo rằng khi một router (hoặc máy chủ) gặp sự cố, một router khác sẽ thay thế và tiếp tục cung cấp dịch vụ mà không có gián đoạn.
Các thành phần chính trong VRRP
Virtual Router (Router ảo): đại diện cho một địa chỉ IP ảo (VIP). Các máy chủ trong mạng sẽ sử dụng IP ảo này làm gateway. VRRP đảm bảo IP ảo luôn có một router "chủ" để xử lý lưu lượng. Nếu router chính gặp sự cố, một router "phụ" sẽ thay thế.
Master Router: router đảm nhận vai trò cổng mặc định (gateway) cho các máy chủ trong mạng. Đây là router chính giữ VIP.
Backup Router: router phụ, luôn giám sát tình trạng của router chính. Khi router chính gặp sự cố, router phụ sẽ trở thành router chủ và tiếp quản VIP.
Virtual IP Address (VIP): địa chỉ IP ảo mà các thiết bị mạng sử dụng làm gateway. Địa chỉ này không gắn với bất kỳ máy chủ hoặc router cụ thể nào mà được chia sẻ giữa các router trong nhóm VRRP.
Priority: mỗi router trong nhóm VRRP có một mức độ ưu tiên. Router với priority cao hơn sẽ trở thành Master. Nếu hai router có cùng priority, một trong hai sẽ được chọn dựa vào địa chỉ MAC.
Cách hoạt động của VRRP
Truyền thông giữa các router
Các router gửi các gói tin VRRP định kỳ để thông báo về tình trạng của chúng. Gói tin chứa: trạng thái (Master/Backup), priority, thông tin VIP.
Trạng thái bình thường:
┌──────────────────┐ ┌──────────────────┐
│ MASTER │ │ BACKUP │
│ 192.168.32.135 │ │ 192.168.32.136 │
│ priority=101 │ │ priority=90 │
│ │ │ │
│ ┌────────────┐ │ VRRP │ │
│ │ Có VIP │ │ packet │ │
│ │ 32.100 │ │ ────────►│ │
│ └────────────┘ │ mỗi 1s │ (lắng nghe) │
│ │ │ │
└──────────────────┘ └──────────────────┘
▲
│ traffic vào VIP
│
┌────┴────┐
│ Client │
└─────────┘Cơ chế truyền gói VRRP
Mặc định: IP multicast (Layer 3) — gói tin VRRP được gửi qua multicast IP
224.0.0.18. Hiệu quả khi nhiều router cùng nhóm, không cần biết IP từng con.Unicast: VRRP cũng hỗ trợ gửi trực tiếp đến từng router. Cần cấu hình IP cụ thể. Tốn ít băng thông hơn và bảo mật hơn (không broadcast ra mạng).
Failover trigger: Nếu BACKUP không nhận được gói VRRP từ MASTER trong thời gian bằng 3 lần khoảng quảng bá (
advert_int), nó sẽ coi MASTER đã gặp sự cố và khởi động quy trình failover.
Chuyển giao quyền điều khiển (Failover)
1. MASTER chết (server down, network die):
┌──────────────────┐ ┌──────────────────┐
│ MASTER (X) │ │ BACKUP │
│ 192.168.32.135 │ │ 192.168.32.136 │
│ ☠️ DEAD │ │ priority=90 │
│ │ no VRRP │ │
│ │ ─ ─ ─ ─ ─│ (chờ 3s) │
│ │ │ │
└──────────────────┘ └──────────────────┘
│
▼
2. Sau 3 × advert_int (= 3s default):
┌──────────────────┐ ┌──────────────────┐
│ MASTER (X) │ │ ✨ NEW MASTER │
│ 192.168.32.135 │ │ 192.168.32.136 │
│ ☠️ DEAD │ │ ┌───────────┐ │
│ │ │ │ Có VIP │ │
│ │ │ │ 32.100 │ │
│ │ │ └───────────┘ │
└──────────────────┘ └──────────────────┘
▲
│ traffic redirect
┌────┴────┐
│ Client │
└─────────┘Preempt — quay về master khi sống lại
Mặc định: máy có priority cao hơn sẽ giành quyền quản lý VIP. Khi MASTER bị lỗi và hoạt động trở lại, VIP sẽ được đưa trả về máy có priority cao hơn.
3. MASTER cũ sống lại:
┌──────────────────┐ ┌──────────────────┐
│ MASTER (revive) │ │ BACKUP again │
│ 192.168.32.135 │ │ 192.168.32.136 │
│ priority=101 │ │ priority=90 │
│ ┌───────────┐ │ VRRP │ │
│ │ VIP 32.100│ │ ────────► │
│ │ TRỞ VỀ │ │ priority │ │
│ └───────────┘ │ cao hơn │ │
└──────────────────┘ └──────────────────┘⚠️ Để tắt tính năng preempt (giữ VIP ở backup, không trả về master) — dùng nopreempt trong cấu hình. Hữu ích khi không muốn "flap" 2 lần (1 lần fail over, 1 lần fail back).
Cài đặt và cấu hình Keepalived VRRP
Yêu cầu
- 2 server nginx proxy (đã thông mạng), đã cài đặt và cấu hình proxy load balancing (kết quả của Bài 02).
- IP: master
192.168.32.135, backup192.168.32.136. - VIP dự kiến:
192.168.32.100.
Cài đặt
Trên cả 2 server (master và backup):
sudo dnf install -y keepalived nginx
sudo systemctl enable --now keepalivedCấu hình Keepalived trên MASTER
File cấu hình mặc định: /etc/keepalived/keepalived.conf
sudo vim /etc/keepalived/keepalived.confNội dung:
vrrp_instance VI_1 {
state MASTER # Router hiện tại là Master
interface ens33 # Giao diện mạng sử dụng
virtual_router_id 51 # ID nhóm VRRP, phải giống nhau giữa các router cùng nhóm
priority 101 # Mức độ ưu tiên (cao hơn = ưu tiên làm Master)
advert_int 1 # Thời gian giữa các lần quảng bá VRRP (giây)
authentication {
auth_type PASS # Loại xác thực (PASS = mật khẩu)
auth_pass 1111 # Mật khẩu xác thực
}
virtual_ipaddress {
192.168.32.100 # Địa chỉ IP ảo chia sẻ giữa các router
}
}Giải thích từng dòng:
state MASTER— thiết lập router này là Master. Nếu Master ngừng hoạt động, một router khác có thể trở thành Master.interface ens33— chỉ định giao diện mạng mà VRRP sẽ chạy. Phải đúng tên interface (kiểm tra bằngip a).virtual_router_id 51— ID nhóm VRRP. Các router cùng nhóm phải có ID này giống nhau. Khác nhóm phải khác ID.priority 101— Master nên có priority cao hơn Backup.advert_int 1— tần suất gửi gói VRRP để thông báo trạng thái (mỗi 1 giây).authentication— phương thức xác thực, ngăn router lạ xâm nhập nhóm.virtual_ipaddress— VIP mà nhóm router chia sẻ.
Cấu hình Keepalived trên BACKUP
sudo vim /etc/keepalived/keepalived.confvrrp_instance VI_1 {
state BACKUP # Router hiện tại là Backup
interface ens33
virtual_router_id 51 # Cùng ID với MASTER
priority 90 # Priority THẤP HƠN MASTER (101 vs 90)
advert_int 1
authentication {
auth_type PASS
auth_pass 1111 # Cùng password với MASTER
}
virtual_ipaddress {
192.168.32.100 # Cùng VIP với MASTER
}
}Chỉ khác MASTER ở 2 dòng:
state BACKUPpriority 90(thấp hơn 101 của Master)
Restart và test
Trên cả 2 server:
sudo systemctl restart keepalived
sudo systemctl status keepalivedKiểm tra VIP trên MASTER (192.168.32.135):
ip a | grep 192.168.32.100
# inet 192.168.32.100/32 scope global ens33VIP đã gắn vào interface. Trên BACKUP, không thấy VIP — vì BACKUP đang chờ.
Test failover
Trên MASTER, dừng keepalived hoặc shutdown server:
# Trên master
sudo systemctl stop keepalivedTrên BACKUP (192.168.32.136), kiểm tra:
ip a | grep 192.168.32.100
# inet 192.168.32.100/32 scope global ens33 ← VIP đã chuyển sang!Failover diễn ra trong 3 giây (3 × advert_int = 3 × 1s).
Khởi động lại MASTER:
sudo systemctl start keepalivedVIP tự quay về MASTER (do priority cao hơn). Đây là behavior preempt mặc định.
Cấu hình unicast (thay multicast)
Mặc định keepalived chạy multicast 224.0.0.18. Nếu mạng không cho phép multicast (cloud VPC, một số switch) — chuyển sang unicast:
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.32.100
}
unicast_peer {
192.168.32.136 # Địa chỉ IP của router BACKUP
}
}Trên BACKUP, set unicast_peer trỏ về MASTER (192.168.32.135).
Phân biệt nhiều nhóm Keepalived
Nếu trong cùng một mạng có nhiều nhóm keepalived khác nhau (vd: 1 nhóm cho web proxy, 1 nhóm cho DB), phân biệt bằng:
virtual_router_id— các router cùng nhóm dùng chung ID, khác nhóm khác ID.VIP— mỗi nhóm có VIP riêng.authentication— mỗi nhóm dùng password khác nhau để đảm bảo chỉ router cùng nhóm giao tiếp với nhau.- Sử dụng card mạng khác nhau — physical isolation.
HA cho multi-service trong một server
Trong bối cảnh hiện tại, một server thường chạy nhiều service độc lập. Khi cấu hình HA cho server nhưng:
- Service A chết, Service B vẫn hoạt động → VIP bị chuyển đi → các dịch vụ khác (B, C, D) bị ảnh hưởng oan.
Giải pháp: cấu hình keepalived cho TỪNG dịch vụ, mỗi dịch vụ quản lý một VIP riêng. Nếu dịch vụ đó chết → kích hoạt failover cho VIP đó, các dịch vụ khác vẫn hoạt động bình thường.
Mô hình multi-VIP
┌─────────────────────────────────────┐
│ Server: 192.168.32.135 (MASTER) │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ Service A │ │ Service B │ │
│ │ VIP: .100 │ │ VIP: .101 │ │
│ │ VRID: 50 │ │ VRID: 51 │ │
│ └─────┬──────┘ └─────┬──────┘ │
│ │ │ │
│ ┌─────┴──────────────────┴──────┐ │
│ │ keepalived (cả 2 instance) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
│ │
│ VRRP │ VRRP
▼ ▼
┌─────────────────────────────────────┐
│ Server: 192.168.32.136 (BACKUP) │
└─────────────────────────────────────┘
Service A chết → chỉ VIP .100 chuyển → Service B (VIP .101) vẫn ở masterCấu hình multi-instance trong file config
# VRRP Instance for Service A
vrrp_instance VI_A {
state MASTER
interface ens33
virtual_router_id 50 # VRID duy nhất cho service A
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass serviceA_pass # Password riêng
}
virtual_ipaddress {
192.168.32.100 # VIP cho service A
}
track_script {
check_serviceA # Theo dõi trạng thái service A
}
}
# VRRP Instance for Service B
vrrp_instance VI_B {
state MASTER
interface ens33
virtual_router_id 51 # VRID KHÁC cho service B
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass serviceB_pass
}
virtual_ipaddress {
192.168.32.101 # VIP cho service B
}
track_script {
check_serviceB
}
}
vrrp_script check_serviceA {
script "/etc/keepalived/check_serviceA.sh" # Đường dẫn tới script
interval 2 # Kiểm tra mỗi 2 giây
weight 2 # Giảm độ ưu tiên đi 2 nếu lỗi
}
vrrp_script check_serviceB {
script "/etc/keepalived/check_serviceB.sh"
interval 2
weight 2
}Tạo script kiểm tra dịch vụ
Tạo script kiểm tra trạng thái dịch vụ:
sudo vim /etc/keepalived/check_serviceA.sh#!/bin/bash
if systemctl is-active --quiet serviceA; then
exit 0 # Trả về 0 nếu service đang chạy
else
exit 1 # Trả về 1 nếu service gặp sự cố
fiTương tự cho check_serviceB.sh. Cấp quyền execute:
sudo chmod +x /etc/keepalived/check_serviceA.sh
sudo chmod +x /etc/keepalived/check_serviceB.shHiểu cơ chế weight
Khi script trả về 1 (service chết):
Trước khi service chết:
MASTER: priority=100, weight không trừ → effective = 100
BACKUP: priority=90 → effective = 90
→ MASTER thắng (100 > 90)
Service chết, script return 1:
MASTER: priority=100, weight -2 → effective = 98
BACKUP: priority=90 → effective = 90
→ MASTER vẫn thắng (98 > 90) ← KHÔNG failover!⚠️ weight phải đủ lớn để priority sau khi trừ < priority backup. Ví dụ trên weight 2 chưa đủ. Nên đặt:
vrrp_script check_serviceA {
script "/etc/keepalived/check_serviceA.sh"
interval 2
weight -20 # ⚠️ NEGATIVE để trừ vào priority master khi script fail
}Lúc này:
Service chết:
MASTER: 100 - 20 = 80
BACKUP: 90
→ BACKUP thắng → VIP chuyển sang BACKUP ✓(Một số tài liệu dùng weight 2 với nghĩa "tăng priority backup khi script trên backup OK". Cách dùng phụ thuộc context — đọc kỹ doc keepalived.)
Notification email
Có thể cấu hình gửi email khi xảy ra failover:
global_defs {
notification_email {
admin@example.com
}
notification_email_from keepalived@example.com
smtp_server 192.168.1.1
smtp_connect_timeout 30
router_id SERVER_1 # Đặt tên định danh cho server này
}Các điểm quan trọng về track_script
Mỗi
vrrp_instancequản lý một VIP. Router quảng bá các VIP mình đang quản lý. Gói VRRP vớivirtual_router_idđược gửi đi theo từng service.Nếu có sự cố trên service nào → Keepalived vẫn tự động chuyển trạng thái mà KHÔNG cần track_script (chỉ dựa vào VRRP heartbeat).
Track_script để tách biệt về mặt dịch vụ. Nếu chỉ muốn HA cho service A (không cho service B) thì bắt buộc phải có track_script để theo dõi tình trạng service A.
Track_script khi nào dùng:
- Muốn Keepalived không chỉ dựa vào trạng thái VRRP mà còn kiểm tra trạng thái thực của dịch vụ trước khi thay đổi trạng thái VIP.
- Khi có yêu cầu phục hồi dịch vụ (service failover) khi dịch vụ gặp sự cố mà không chỉ dựa vào trạng thái mạng.
- Đây là cơ chế health check mà Keepalived cung cấp.
Trên BACKUP: cấu hình tương tự, chỉ cần thay đổi
priority. Có thể KHÔNG cần track_script trên backup.
Vấn đề kinh điển: network die nhưng service vẫn sống
Trường hợp không kích hoạt được failover khi sử dụng track_script:
Scenario nguy hiểm:
┌─────────────────────┐ ┌─────────────────────┐
│ MASTER │ │ BACKUP │
│ - nginx UP ✓ │ ⚡ │ - nginx UP ✓ │
│ - keepalived UP │ network │ - keepalived UP │
│ - track_script OK │ DIE! │ - track_script OK │
│ │ ────❌──│ │
└─────────────────────┘ └─────────────────────┘
▲ ▲
│ │
│ Client không tới được │ Client không tới được
│ (nếu route qua network die) │Khi sử dụng track_script mà network giữa các server gặp vấn đề nhưng health check service vẫn hoạt động:
- Track_script trên MASTER vẫn return 0 (service nginx OK)
- Failover không được kích hoạt → user vẫn không truy cập được
Lý do: khi dùng track_script, failover dựa trên trạng thái dịch vụ (weight bị giảm xuống và bị đẩy xuống làm backup). KHÔNG dựa vào network. Còn nếu không dùng track_script thì failover phụ thuộc vào network (VRRP heartbeat fail).
2 cách khắc phục:
Cấu hình track_script CHỈ trên MASTER, không cấu hình trên BACKUP — backup không tự coi mình "không cần lên master".
Thêm script check network trên master để ping tới backup:
#!/bin/bash
# /etc/keepalived/check_network.sh
ping -c 2 -W 1 192.168.32.136 > /dev/null
exit $?Trong config, thêm vrrp_script cho check_network với weight đủ lớn để force giảm priority khi network die.
Xử lý lỗi cấu hình
Keepalived ghi log vào file log hệ thống (thường là /var/log/messages hoặc /var/log/syslog) nếu gặp lỗi trong file cấu hình. Khi keepalived không start được:
sudo journalctl -u keepalived -n 50
sudo tail -f /var/log/messages | grep -i keepalivedPhần B — MySQL Replication
Phần A đã giải quyết HA cho proxy. Còn DB? Bài 01-02 chỉ có 1 DB → die là sập tầng DB → backend vô dụng. Phần này dạy MySQL Replication để có DB backup sẵn sàng.
MySQL Replication là gì
MySQL Replication là quá trình sao chép dữ liệu từ một database server (gọi là Master hoặc Source) sang một hoặc nhiều database server khác (gọi là Slave hoặc Replica) một cách tự động và liên tục.
┌─────────────────────┐ ┌─────────────────────┐
│ MASTER (Source) │ ─── replicate ──► │ SLAVE (Replica) │
│ 192.168.32.137 │ │ 192.168.32.138 │
│ │ │ │
│ - Chấp nhận write │ │ - Read-only │
│ - Chấp nhận read │ │ - Standby cho HA │
│ - Ghi binlog │ │ - Đọc binlog từ │
│ │ │ master và apply │
└─────────────────────┘ └─────────────────────┘
│ ▲
│ binary log (binlog) │
└──────────────────────────────────────────┘
copy + replayLợi ích của replication
- Read scaling — read query đẩy sang slave, master chỉ xử lý write.
- Backup — backup từ slave, không impact master.
- Geographic distribution — slave ở data center khác cho latency thấp.
- High Availability — master die → promote slave thành master mới.
- Phân tích / report — chạy query nặng trên slave, không ảnh hưởng production.
Async vs Semi-sync vs Sync
3 mode replication, đánh đổi giữa performance và data safety:
ASYNC (mặc định):
Master commit ──► trả OK cho client ──► gửi binlog cho slave (sau)
Risk: master die trước khi gửi → slave thiếu data ⚠️
Lợi: nhanh, không ảnh hưởng latency master
SEMI-SYNC:
Master commit ──► gửi binlog cho slave ──► slave ack ──► trả OK cho client
Risk: slave die → master treo chờ ack
Lợi: đảm bảo ít nhất 1 slave có data trước khi confirm
SYNC (Galera Cluster, không phải replication thuần):
Master prepare ──► tất cả node ack ──► tất cả commit cùng lúc
Risk: chậm hơn nhiều, network requirement cao
Lợi: zero data loss, multi-master writeBài này dùng async (mặc định MySQL/MariaDB) — đơn giản, đủ cho HA cơ bản.
Cách hoạt động — Binary Log và 3 Thread
Binary Log (binlog) là gì
Binary log (binlog) là file log ghi lại mọi thay đổi dữ liệu (INSERT, UPDATE, DELETE, DDL) trên master. Đây là "nguồn sự thật" mà slave dùng để replay.
Master ghi vào binlog mỗi statement/transaction:
/var/lib/mysql/mysql-bin.000001
/var/lib/mysql/mysql-bin.000002
...
Mỗi event có:
- Position (offset trong file)
- Timestamp
- Statement / row change3 dạng binlog format:
- Statement-Based Replication (SBR) — ghi SQL statement gốc. Nhỏ gọn nhưng có vấn đề với non-deterministic (
NOW(),UUID()). - Row-Based Replication (RBR) — ghi row thay đổi cụ thể. An toàn nhất. Default modern.
- Mixed — auto chọn giữa SBR và RBR.
3 thread của replication
┌──────────────────────────┐ ┌──────────────────────────┐
│ MASTER │ │ SLAVE │
│ │ │ │
│ Client INSERT/UPDATE │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────────┐ │ │ │
│ │ MySQL engine │ │ │ │
│ └──────┬───────┘ │ │ │
│ │ write │ │ │
│ ▼ │ │ │
│ ┌──────────────┐ │ TCP │ ┌──────────────────┐ │
│ │ binlog │ ──[1]──┼─────────┼─►│ IO Thread │ │
│ │ (mysql-bin) │ │ │ │ (đọc binlog từ │ │
│ └──────────────┘ │ │ │ master qua mạng) │ │
│ ▲ │ │ └────────┬─────────┘ │
│ │ [Dump Thread] │ │ │ write │
│ │ │ │ ▼ │
│ │ │ │ ┌──────────────────┐ │
│ │ │ │ relay log │ │
│ │ │ │ (mysql-relay) │ │
│ │ │ └────────┬─────────┘ │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ ┌──────────────────┐ │
│ │ │ │ SQL Thread │ │
│ │ │ │ (replay từng │ │
│ │ │ │ event vào DB) │ │
│ │ │ └────────┬─────────┘ │
│ │ │ ▼ │
│ │ │ ┌──────────────────┐ │
│ │ │ │ Slave DB │ │
│ │ │ │ (data synced) │ │
│ │ │ └──────────────────┘ │
└──────────────────────────┘ └──────────────────────────┘3 thread:
- Master: Dump Thread — đọc binlog và gửi qua mạng cho slave.
- Slave: IO Thread — nhận binlog từ master, ghi vào relay log (local file ở slave).
- Slave: SQL Thread — đọc relay log, replay từng event vào database của slave.
Tại sao tách 2 thread ở slave? IO thread chỉ chép network bytes, nhanh. SQL thread mới là cái chạy actual SQL — chậm hơn. Tách ra để binlog không bị stuck nếu SQL replay chậm.
Position-based vs GTID
2 cách track replication position:
Position-based (legacy):
slave dõi theo: file mysql-bin.000003, position 12345Failover khó: nếu chuyển slave sang master khác, phải tìm đúng file + position.
GTID (Global Transaction ID) — modern, recommend:
mỗi transaction có ID unique: SERVER_UUID:TXN_NUMBER
slave dõi theo: đã apply transaction X, Y, Z...Failover dễ: slave chỉ cần biết "đã apply tới GTID nào", không cần file/position.
MariaDB từ 10.0+ dùng GTID-style riêng (khác MySQL). Bài này dùng position-based cho đơn giản, MariaDB có hỗ trợ GTID nhưng setup phức tạp hơn.
Setup Master-Slave với MariaDB
Yêu cầu
- 2 server MariaDB (đã cài như Bài 01).
- Master:
192.168.32.137. - Slave:
192.168.32.138. - Network thông giữa 2 server, port 3306 mở.
Bước 1: Config trên MASTER
Sửa file config MariaDB:
sudo vim /etc/my.cnf.d/server.cnfTrong block [mysqld]:
[mysqld]
server-id = 1 # ID duy nhất, master = 1
log-bin = /var/lib/mysql/mysql-bin # Bật binary log
binlog_format = ROW # Row-based (an toàn nhất)
bind-address = 0.0.0.0 # Listen mọi interface
expire_logs_days = 7 # Auto-xoá binlog cũ sau 7 ngàyRestart MariaDB:
sudo systemctl restart mariadbVerify binlog đã bật:
mariadb -u root -p -e "SHOW VARIABLES LIKE 'log_bin';"
# +---------------+-------+
# | Variable_name | Value |
# +---------------+-------+
# | log_bin | ON |
# +---------------+-------+Bước 2: Tạo user replication trên MASTER
mariadb -u root -pCREATE USER 'repl'@'192.168.32.138' IDENTIFIED BY 'ReplPass123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.32.138';
FLUSH PRIVILEGES;User repl chỉ có quyền REPLICATION SLAVE — đủ để đọc binlog, không có quyền khác.
Bước 3: Lock master + lấy binlog position
-- Lock toàn bộ DB để không có write mới trong khi snapshot
FLUSH TABLES WITH READ LOCK;
-- Ghi lại position hiện tại
SHOW MASTER STATUS;
-- +------------------+----------+--------------+------------------+
-- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
-- +------------------+----------+--------------+------------------+
-- | mysql-bin.000001 | 12345 | | |
-- +------------------+----------+--------------+------------------+LƯU LẠI file và position này — cần khi config slave.
Bước 4: Dump DB từ MASTER
Trên terminal khác (không thoát mariadb session đang lock):
mysqldump -u root -p --all-databases --master-data=2 > /tmp/master_dump.sql--master-data=2 ghi binlog position vào dump file (dưới dạng comment, tham khảo).
Sau khi dump xong, unlock master:
UNLOCK TABLES;
EXIT;Bước 5: Copy dump sang SLAVE
scp /tmp/master_dump.sql root@192.168.32.138:/tmp/Bước 6: Config trên SLAVE
Sửa config:
sudo vim /etc/my.cnf.d/server.cnf[mysqld]
server-id = 2 # ID khác master (master=1)
relay-log = /var/lib/mysql/mysql-relay-bin
read_only = ON # Slave read-only (safety)
bind-address = 0.0.0.0Restart:
sudo systemctl restart mariadbBước 7: Import dump vào SLAVE
mariadb -u root -p < /tmp/master_dump.sqlBước 8: Setup replication trên SLAVE
mariadb -u root -pCHANGE MASTER TO
MASTER_HOST='192.168.32.137',
MASTER_USER='repl',
MASTER_PASSWORD='ReplPass123!',
MASTER_LOG_FILE='mysql-bin.000001', -- File từ bước 3
MASTER_LOG_POS=12345; -- Position từ bước 3
START SLAVE;Verify:
SHOW SLAVE STATUS\G2 dòng quan trọng phải đều là Yes:
Slave_IO_Running: Yes ← IO thread đang chạy
Slave_SQL_Running: Yes ← SQL thread đang chạy
Seconds_Behind_Master: 0 ← Replication lag (giây)
Last_IO_Error: ← rỗng = OK
Last_SQL_Error: ← rỗng = OKNếu 1 trong 2 không Yes → có lỗi. Xem error message ở Last_IO_Error hoặc Last_SQL_Error.
Test replication
Trên MASTER:
USE wordpress;
INSERT INTO wp_options (option_name, option_value) VALUES ('test_repl', 'hello');Trên SLAVE (chờ vài giây):
USE wordpress;
SELECT * FROM wp_options WHERE option_name = 'test_repl';
-- Phải thấy row 'hello'✓ Replication chạy.
Failover khi MASTER die
Quan trọng: MySQL replication open source KHÔNG tự động failover. Khi master die, phải làm manually (hoặc dùng tool như MHA, Orchestrator, ProxySQL).
Quy trình promote slave thành master mới
Trước failover:
┌────────────┐ ┌────────────┐
│ Master ☠️ │ ───► │ Slave UP ✓ │
│ (DEAD) │ │ read-only │
└────────────┘ └────────────┘
Bước 1: Stop replication trên slave
SLAVE> STOP SLAVE;
SLAVE> RESET SLAVE ALL;
Bước 2: Bỏ read-only
SLAVE> SET GLOBAL read_only = OFF;
Bước 3: Update app config trỏ về slave IP mới
Backend wp-config.php:
define('DB_HOST', '192.168.32.138'); ← IP slave (giờ là master mới)
Sau failover:
┌────────────┐ ┌──────────────┐
│ Master ☠️ │ │ NEW MASTER ✓ │
│ (DEAD) │ │ writable │
└────────────┘ └──────────────┘Sau khi MASTER cũ sống lại
Không thể đơn giản start lại master cũ — vì lúc đó có 2 master cùng write → conflict.
Quy trình rebuild:
- Treat master cũ như slave mới — dump từ new master, import vào master cũ.
- Setup replication từ new master → master cũ.
Sau khi sync xong, có 2 lựa chọn:
- Giữ topology mới (slave cũ → master mới, master cũ → slave mới).
- Switch back — thêm 1 lần failover nữa để về topology ban đầu (tốn thêm downtime).
Tự động hoá failover
Production thật thường không làm thủ công. Các tool phổ biến:
- ProxySQL — proxy ở giữa app và DB, tự động re-route khi master die.
- MaxScale (MariaDB) — proxy chuyên cho MariaDB, tự failover.
- MHA (Master High Availability) — tool cũ nhưng vẫn dùng nhiều.
- Orchestrator — quản lý topology, auto-failover, GUI đẹp.
Bài này không cover các tool này — quá phức tạp cho 1 bài. Production lớn nên đầu tư thời gian học 1 trong số đó.
Common issues với replication
1. Replication lag
Lag = slave chậm hơn master bao lâu (giây). Xem qua Seconds_Behind_Master.
Master: tx 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (đang ở tx 10)
Slave: tx 1, 2, 3, 4, 5 (đang ở tx 5)
▲
Lag = 5 transactionsNguyên nhân lag:
- Slave hardware yếu hơn master.
- SQL thread single-threaded (default) → write nhiều không kịp replay.
- Network slow giữa master và slave.
- Slow query trên slave block SQL thread.
App đọc từ slave có thể thấy stale data — vừa write vào master, đọc lại từ slave chưa thấy. Đây là vấn đề kinh điển của eventual consistency.
Fix lag:
- Bật parallel replication (multi-thread SQL apply) — MariaDB 10.0+ hỗ trợ.
- Tách read query không critical sang slave, critical read về master.
- Upgrade slave hardware.
2. Broken replication
Replication broken khi slave gặp lỗi và Slave_SQL_Running: No:
- Duplicate key: slave có row mà master đang INSERT (data drift).
- Missing table: master DROP TABLE chưa replicate, slave query → fail.
- Binlog gap: master xoá binlog cũ trước khi slave đọc.
Fix tạm thời (skip error):
STOP SLAVE;
SET GLOBAL sql_slave_skip_counter = 1;
START SLAVE;⚠️ Skip error che bug, không sửa. Production cần investigate root cause + rebuild slave nếu data drift lớn.
3. Master bị bottleneck binlog
Binlog ghi mọi write → master IO tăng. Mitigation:
- Bật
sync_binlog = 0(nguy hiểm, mất data nếu crash). - Move binlog sang disk riêng (SSD).
- Compression binlog (MySQL 8+).
Phần C — Kết hợp HA Full Stack
Mô hình hoàn chỉnh
Tổng hợp cả 3 lớp HA:
┌──────────────┐
│ Client │
└──────┬───────┘
│ https://test.com
│ DNS → VIP
▼
┌─────────────────┐
│ VIP 32.100 │
└────────┬────────┘
│
┌────────────────────┴────────────────────┐
│ (Keepalived VRRP) │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Proxy MASTER │ │ Proxy BACKUP │
│ 192.168.32.135 │ ◄─── VRRP ────────► │ 192.168.32.136 │
│ - nginx LB │ (multicast) │ - nginx LB │
│ - keepalived ⭐ │ │ - keepalived │
└────────┬─────────┘ └────────┬─────────┘
│ │
└────────────┬─────────────────────────────┘
│ (nginx load balancing)
┌────────────┴────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Backend 1 │ │ Backend 2 │
│ 192.168.32.133 │ │ 192.168.32.134 │
│ - nginx │ │ - nginx │
│ - php-fpm │ │ - php-fpm │
│ - WordPress │ │ - WordPress │
└────────┬─────────┘ └────────┬─────────┘
│ │
│ DB connections │
│ (write → master, │
│ read → có thể slave) │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ DB MASTER ⭐ │ ────► │ DB SLAVE │
│ 192.168.32.137 │binlog │ 192.168.32.138 │
│ - MariaDB │ │ - MariaDB │
│ - writable │ │ - read-only │
│ - server-id=1 │ │ - server-id=2 │
└──────────────────┘ └──────────────────┘
(replication async)Failover scenarios
Scenario 1: Proxy MASTER die
1. Proxy master die (server tắt / nginx crash / network die)
2. Backup không nhận VRRP packet trong 3s
→ tự promote thành Master
→ tiếp quản VIP 32.100
3. Client tiếp tục access https://test.com
→ DNS vẫn trả VIP 32.100
→ traffic giờ tới proxy backup (192.168.32.136)
→ nginx LB ở backup forward tới backends
⏱️ Downtime: ~3 giây (= 3 × advert_int)Scenario 2: 1 Backend die
1. Backend 1 die
2. Proxy MASTER (nginx LB) phát hiện qua passive health check
max_fails=3 lần fail liên tiếp trong fail_timeout=30s
→ mark backend 1 = DOWN
3. Mọi request tiếp theo → backend 2
4. Sau fail_timeout, nginx test lại backend 1
→ nếu UP, mark UP
→ nếu vẫn DOWN, tiếp tục giữ DOWN
⏱️ Downtime: ~vài request (tới khi đủ max_fails)Scenario 3: DB MASTER die
1. DB master (137) die
2. Backend WordPress connect 137 → connect refused / timeout
→ user thấy "Error connecting to database"
3. ⚠️ KHÔNG auto failover — phải manual:
a) SSH vào slave (138)
b) STOP SLAVE; RESET SLAVE ALL;
c) SET GLOBAL read_only = OFF;
d) Update wp-config.php trên cả 2 backend:
DB_HOST = '192.168.32.138'
e) Restart php-fpm để pickup config mới
⏱️ Downtime: phút (tùy thao tác manual của admin)Đây là điểm yếu của setup hiện tại. Production cần ProxySQL hoặc MaxScale để auto failover DB.
Scenario 4: Network split-brain
⚡ Network giữa 2 proxy bị cắt ⚡
┌──────────────────┐ ┌──────────────────┐
│ Proxy 1 │ ────────❌─────────►│ Proxy 2 │
│ (cũ là master) │ │ (cũ là backup) │
│ Vẫn nghĩ mình │ │ Không nhận VRRP │
│ là MASTER │ │ → tự promote │
│ → giữ VIP 32.100 │ │ → cũng giành │
│ │ │ VIP 32.100 │
└──────────────────┘ └──────────────────┘
│ │
│ traffic │ traffic
▼ ▼
⚠️ 2 server cùng claim VIP ⚠️
→ ARP conflict
→ traffic chia ngẫu nhiên hoặc failSplit-brain hiếm nhưng nguy hiểm. Mitigation:
- Dùng unicast VRRP qua interface độc lập (không qua switch chính).
- Thêm shared storage hoặc quorum (etcd, ZooKeeper) — overkill cho setup nhỏ.
- Monitoring chặt chẽ, alert ngay khi phát hiện 2 node cùng có VIP.
Câu chuyện thực tế
Cuối 2024, mình deploy hệ thống e-commerce có HA cho client. Setup đúng như mô hình ở trên: 2 proxy + keepalived, 2 backend, master-slave MySQL.
Demo cho khách: kill proxy master → VIP chuyển sang backup trong 3 giây → site không sập. Khách hài lòng, đưa lên production.
Một tuần sau, alert lúc 3h sáng: site sập hoàn toàn. SSH vào, mọi service đều UP — nginx OK, php-fpm OK, keepalived OK. Nhưng vào https://example.com → timeout.
Check VIP:
ip a | grep 32.100Cả 2 proxy đều không có VIP! Đây là kịch bản chưa nghĩ tới.
Debug:
journalctl -u keepalived -n 100Thấy log:
VRRP_Instance(VI_1) Received advert with higher priority 200
VRRP_Instance(VI_1) Entering BACKUP STATECó một con server khác trong cùng VLAN đang gửi VRRP với virtual_router_id = 51 và priority 200! Cả master và backup của mình đều trở thành backup, không ai giữ VIP.
Tìm hiểu: team khác trong công ty deploy keepalived cho project khác, dùng trùng virtual_router_id 51 (giống nhau là default trong tutorial). VLAN là shared → VRRP multicast tới tất cả → conflict.
Fix:
- Đổi
virtual_router_idthành số khác (ví dụ 151) cho project mình. - Đổi
auth_passthành unique string. - Báo team kia cũng chọn VRID khác.
- Document VRID đã dùng trong wiki nội bộ.
Bài học:
VRID phải unique trong cùng broadcast domain. 51 là số ai cũng dùng theo tutorial → dễ trùng. Production: dùng VRID đặc biệt như 151, 199.
Authentication không chỉ là security — là cách phân biệt nhóm. Password unique giúp 2 nhóm khác nhau không "nói chuyện" với nhau qua VRRP.
Test failover định kỳ. Không thì lúc cần thật mới biết keepalived bị break.
Monitoring VIP — alert ngay khi không có server nào giữ VIP, hoặc 2 server cùng giữ.
Lab — dựng full HA stack
Setup 6 server:
- proxy-master: 192.168.32.135 (đã có nginx từ Bài 02)
- proxy-backup: 192.168.32.136 (cài nginx + keepalived)
- backend-1: 192.168.32.133 (đã có từ Bài 01-02)
- backend-2: 192.168.32.134 (đã có từ Bài 02)
- db-master: 192.168.32.137 (đã có MariaDB)
- db-slave: 192.168.32.138 (cài MariaDB mới)
Phần A: Setup Keepalived
Trên proxy-master:
sudo dnf install -y keepalived
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
vrrp_instance VI_LEMP {
state MASTER
interface ens33
virtual_router_id 151
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass LempHA2024
}
virtual_ipaddress {
192.168.32.100
}
track_script {
check_nginx
}
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -20
}
EOF
sudo tee /etc/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
if systemctl is-active --quiet nginx; then
exit 0
else
exit 1
fi
EOF
sudo chmod +x /etc/keepalived/check_nginx.sh
sudo systemctl enable --now keepalived
ip a | grep 32.100 # Phải thấy VIPTrên proxy-backup (192.168.32.136):
# Cài lại nginx + cấu hình proxy giống Bài 02
sudo dnf install -y nginx keepalived
# ... cấu hình nginx proxy giống proxy-master ...
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
vrrp_instance VI_LEMP {
state BACKUP
interface ens33
virtual_router_id 151
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass LempHA2024
}
virtual_ipaddress {
192.168.32.100
}
}
EOF
sudo systemctl enable --now keepalived
ip a | grep 32.100 # KHÔNG thấy VIP (đang là backup)Phần B: Setup MySQL Replication
Trên db-master (137):
sudo tee -a /etc/my.cnf.d/server.cnf <<'EOF'
[mysqld]
server-id = 1
log-bin = /var/lib/mysql/mysql-bin
binlog_format = ROW
bind-address = 0.0.0.0
expire_logs_days = 7
EOF
sudo systemctl restart mariadb
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload
# Tạo user replication
mariadb -u root -p <<EOF
CREATE USER 'repl'@'192.168.32.138' IDENTIFIED BY 'ReplPass123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.32.138';
FLUSH PRIVILEGES;
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
EOF
# LƯU LẠI File và Position!
# Dump data
mysqldump -u root -p --all-databases --master-data=2 > /tmp/master.sql
# Unlock
mariadb -u root -p -e "UNLOCK TABLES;"
# Copy sang slave
scp /tmp/master.sql root@192.168.32.138:/tmp/Trên db-slave (138):
sudo dnf install -y mariadb-server
sudo tee -a /etc/my.cnf.d/server.cnf <<'EOF'
[mysqld]
server-id = 2
relay-log = /var/lib/mysql/mysql-relay-bin
read_only = ON
bind-address = 0.0.0.0
EOF
sudo systemctl enable mariadb --now
# Import dump
mariadb -u root -p < /tmp/master.sql
# Setup replication
mariadb -u root -p <<EOF
CHANGE MASTER TO
MASTER_HOST='192.168.32.137',
MASTER_USER='repl',
MASTER_PASSWORD='ReplPass123!',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=<Position từ SHOW MASTER STATUS>;
START SLAVE;
SHOW SLAVE STATUS\G
EOF
# Verify: Slave_IO_Running=Yes, Slave_SQL_Running=YesTest toàn bộ
Test VIP failover:
# Trên proxy-master sudo systemctl stop keepalived # Trên proxy-backup ip a | grep 32.100 # Phải thấy VIP đã chuyển # Client vẫn vào được https://test.comTest DB replication:
-- Master USE wordpress; INSERT INTO wp_options (option_name, option_value) VALUES ('test', 'hello'); -- Slave (chờ vài giây) SELECT * FROM wp_options WHERE option_name = 'test';Test DB failover manual:
-- Trên slave STOP SLAVE; RESET SLAVE ALL; SET GLOBAL read_only = OFF; -- Update wp-config.php trên cả 2 backend: DB_HOST = '192.168.32.138' -- Restart php-fpm
Pitfalls
VRID trùng nhau giữa nhiều nhóm keepalived trong cùng VLAN → 2 nhóm "tranh" VIP. Dùng VRID unique (vd 151, 199 thay 51).
weightquá nhỏ trong track_script → service die nhưng priority master vẫn cao hơn backup → không failover. Tính toán:weightphải đủ đểpriority_master - weight < priority_backup.auth_passtrùng giữa các nhóm → xác thực không phân biệt được. Mỗi project unique password.Interface name sai (
eth0vsens33vsenp0s3) → keepalived không gắn VIP. Checkip atrước.Multicast bị block trong cloud VPC → VRRP fail. Chuyển sang
unicast_peer.Network die nhưng track_script vẫn OK → không failover dù user không access được. Cấu hình track_script chỉ trên master, hoặc thêm script ping backup.
MySQL replication không có GTID → failover phải tìm đúng file + position, khó manual. Cân nhắc dùng GTID cho production.
Slave có write nhầm → data drift với master → replication broken. Luôn
read_only = ONtrên slave.Master xoá binlog cũ trước khi slave đọc → slave fail với "binlog không còn". Tăng
expire_logs_daysđủ lớn hoặc dùng compressed binlog.Lag replication không monitor → user write rồi đọc lại không thấy data → bug khó debug. Monitor
Seconds_Behind_Masterqua Prometheus.Master die, manual promote slave → quên đổi DB_HOST ở app → site vẫn fail. Tự động hoá qua ProxySQL hoặc DNS update.
Master cũ sống lại không sync → 2 master cùng write → data divergence. Luôn rebuild master cũ thành slave mới.
Tóm tắt
- Keepalived VRRP quản lý VIP giữa nhiều server. VIP nằm trên Master, chuyển sang Backup khi Master die.
- Failover ~3 giây (= 3 × advert_int) khi mất VRRP heartbeat.
- VRID unique trong cùng broadcast domain. Auth password unique cho từng nhóm.
- Multi-VIP per service với
track_script, mỗi service một VIP. weightâm trong track_script để giảm priority master khi service die.- MySQL Replication: Master ghi binlog → Slave IO thread đọc → SQL thread replay.
- Async (default), Semi-sync (đảm bảo data), Sync (Galera, multi-master).
- Position-based vs GTID — GTID dễ failover hơn, recommend cho production.
- Master die → manual promote slave (open source). Auto failover cần ProxySQL/MaxScale/Orchestrator.
- Replication lag = slave chậm hơn master. Monitor
Seconds_Behind_Master. - Mọi failover phải test định kỳ. Không thì lúc cần thật không biết broken từ lúc nào.
🎓 Tổng kết — Chặng 4: Web Stack & High Availability
Đi qua 3 bài:
- Bài 01 — LEMP Stack: dựng web service đầu tiên (1 server, 1 stack).
- Bài 02 — Nginx Reverse Proxy: 1 proxy → N backend, load balance + headers + SSL.
- Bài 03 — High Availability: Keepalived VRRP + MySQL Replication.
Từ "1 server WordPress chạy" → kiến trúc multi-tier production thật. Mọi pattern bạn vừa học sẽ gặp lại ở K8s — chỉ khác cách config (manifest YAML thay nginx.conf), nguyên lý y hệt:
- Service / Ingress trong K8s = nginx reverse proxy + load balancer.
- VIP của LoadBalancer Service = VRRP/Keepalived (cloud LB dưới hood chính là pattern này).
- StatefulSet với PVC + operator = MySQL replication tự động.
- HPA, PDB = patterns để đảm bảo HA cho stateless workload.
Học chặng này xong, đọc K8s sẽ thấy quen.
Lúc chưa rõ thì đọc thêm
- ProxySQL / MaxScale / Orchestrator — auto failover DB.
- HAProxy — reverse proxy alternative, mạnh ở Layer 4.
- CARP / pfSync — VRRP alternative trên BSD/pfSense.
- Galera Cluster — multi-master sync replication cho MariaDB.
- MySQL Group Replication — version mới của MySQL hỗ trợ multi-master.
- etcd, Consul — distributed key-value store, dùng cho quorum/leader election.
- Patroni — auto failover cho PostgreSQL (đáng học song song với MySQL replication).
Lời khuyên cuối
Test failover định kỳ. Mỗi tháng kill 1 server, xem hệ thống có chuyển đúng không. Không thì lúc thật xảy ra mới biết keepalived broken từ tháng trước.
Document VRID, VIP, replication topology. Wiki nội bộ. Đặt vào runbook ai cũng đọc được lúc 3h sáng.
Monitoring là bắt buộc. Prometheus + Grafana monitor:
- VRRP transition (master → backup).
- Replication lag (
Seconds_Behind_Master). - Service uptime ở cả master và backup.
- 2 server cùng giữ VIP (split-brain detection).
Auto failover phức tạp hơn nhiều so với HA passive. Đầu tư đúng tool (Orchestrator/Patroni) khi project lớn — không tự viết script auto failover trừ khi hiểu sâu.
Backup là cứu cánh cuối cùng. Replication không phải backup — nếu master DROP TABLE, slave cũng mất bảng đó ngay. Backup full + incremental, restore test định kỳ.
Chúc bạn không bao giờ phải nghe khách báo "website tao sập rồi" lúc 3h sáng! 🚀
— Minh Hưng