Case study disaster recovery này ghi lại một trong những sự cố nghiêm trọng nhất chúng tôi từng xử lý — ransomware tấn công hệ thống e-commerce của khách hàng vào 11h tối thứ 6. Nhờ có Disaster Recovery plan chuẩn bị từ trước, toàn bộ hệ thống được phục hồi trong 2 giờ, mất 0% dữ liệu. Đây là câu chuyện chi tiết từ phút 0.
Bối cảnh
Khách hàng: Công ty phân phối mỹ phẩm, kênh bán hàng chính là website e-commerce.
Hệ thống:
- 2 VPS (app server + database server) trên DigitalOcean
- Website Laravel + MySQL
- 3.000 đơn hàng/ngày, doanh thu ~2 tỉ VND/tháng
- 150.000 tài khoản khách hàng
Hệ thống DR đã chuẩn bị (trước sự cố 3 tháng):
| Component | Cấu hình |
|---|---|
| Database backup | mysqldump mỗi 6 giờ → Backblaze B2 (immutable) |
| Files backup | rsync hàng ngày → Backblaze B2 |
| Config backup | Hàng tuần → encrypted → B2 |
| Server snapshots | DigitalOcean snapshot hàng tuần |
| DR documentation | Runbook chi tiết từng bước restore |
| Test restore | Hàng tháng (lần gần nhất: 2 tuần trước) |
| Monitoring | UptimeRobot + Prometheus + Grafana + Telegram alerting |
Timeline sự cố
23:00 — Ransomware bắt đầu mã hóa
Ransomware xâm nhập qua lỗ hổng trong plugin WordPress (admin blog chạy trên cùng server). Bắt đầu mã hóa files trên app server.
23:03 — Monitoring phát hiện
UptimeRobot alert: “Website DOWN — HTTPS check failed”
Prometheus alert: “CPU 100% sustained” (ransomware đang mã hóa files)
Telegram alert gửi đến on-call engineer.
23:06 — On-call engineer acknowledge
Engineer nhận alert, SSH vào server. Nhận thấy:
- CPU 100% từ process lạ (
svchost.bin) - Nhiều files trong
/var/www/có extension.encrypted - README.txt với thông điệp đòi tiền chuộc
23:08 — Quyết định: Activate DR Plan
Thay vì cố gắng “cứu” server bị nhiễm, engineer quyết định kích hoạt Disaster Recovery Plan — tạo server mới và restore từ backup.
Lý do không cố cứu server cũ:
- Không biết ransomware đã lan đến đâu
- Server có thể có backdoor
- Restore từ backup nhanh hơn và an toàn hơn
- DR plan đã được test, có runbook sẵn
23:10 — Isolate server bị nhiễm
# Tắt network trên server bị nhiễm (prevent lateral movement)
# KHÔNG shutdown — giữ evidence cho forensics
ufw deny out to any
ufw deny in to any
23:12 — Kiểm tra backup
# Kiểm tra backup trên Backblaze B2
rclone ls b2:backup-bucket/db/ | tail -5
# → Backup mới nhất: 6 giờ trước (18:00)
rclone ls b2:backup-bucket/files/ | tail -5
# → Backup files mới nhất: 2:00 AM cùng ngày
Backup sạch vì:
- Backup trên Backblaze B2 (offsite) — ransomware không truy cập được
- B2 Object Lock bật — ransomware không xóa được backup cũ
- Backup credentials không lưu trên production server
23:15 — Tạo server mới
# Tạo 2 VPS mới trên DigitalOcean (CLI)
doctl compute droplet create app-server-new \
--region sgp1 --size s-4vcpu-8gb --image ubuntu-22-04-x64
doctl compute droplet create db-server-new \
--region sgp1 --size s-4vcpu-16gb --image ubuntu-22-04-x64
23:25 — Setup server mới
Theo DR Runbook, engineer chạy Ansible playbook (chuẩn bị sẵn) để setup server:
# Ansible playbook setup toàn bộ stack trong 15 phút
ansible-playbook -i inventory setup-production.yml
Playbook tự động:
- Cài Ubuntu packages, Nginx, PHP, MySQL
- Cấu hình firewall, SSH, fail2ban
- Cài SSL certificate
- Setup monitoring (Node Exporter, Promtail)
23:40 — Restore database
# Download backup từ B2
rclone copy b2:backup-bucket/db/website_db_20260923_180000.sql.gz /tmp/restore/
# Restore database
gunzip -c /tmp/restore/website_db_20260923_180000.sql.gz | mysql -u root website_db
# Verify
mysql -u root -e "SELECT COUNT(*) FROM website_db.orders;"
# → 892,345 records ✓
23:55 — Restore files
# Download files backup
rclone copy b2:backup-bucket/files/website_20260923.tar.gz /tmp/restore/
# Extract
tar xzf /tmp/restore/website_20260923.tar.gz -C /var/www/
# Set permissions
chown -R www-data:www-data /var/www/website
chmod -R 755 /var/www/website
00:10 — Restore config
# Download config backup
rclone copy b2:backup-bucket/config/config_20260921.tar.gz /tmp/restore/
# Extract Nginx config, PHP config, .env
tar xzf /tmp/restore/config_20260921.tar.gz -C /
# Update .env với database credentials mới
nano /var/www/website/.env
# Restart services
systemctl restart nginx php8.3-fpm mysql
00:20 — Testing
| Test | Kết quả |
|---|---|
| Homepage load | OK |
| Product listing | OK |
| Search | OK |
| User login | OK |
| Add to cart | OK |
| Checkout | OK |
| Payment (test mode) | OK |
| Admin panel | OK |
| API endpoints | OK |
00:30 — DNS cutover
# Update DNS A record trỏ về IP mới
# (Thực hiện qua Cloudflare API — propagation instant vì đã dùng Cloudflare proxy)
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records/RECORD_ID" \
-H "Authorization: Bearer API_TOKEN" \
-d '{"content":"NEW_SERVER_IP"}'
00:32 — Website hoạt động trở lại
00:32 – 01:00 — Verification
- Monitor response time, error rate
- Kiểm tra 100 random orders trong database
- Test email sending
- Verify cron jobs hoạt động
01:00 — Incident resolved
Tổng thời gian downtime: 1 giờ 57 phút
Kết quả
Thời gian phục hồi
| Phase | Thời gian | Cumulative |
|---|---|---|
| Phát hiện sự cố | 3 phút | 3 phút |
| Quyết định activate DR | 5 phút | 8 phút |
| Isolate server cũ | 2 phút | 10 phút |
| Kiểm tra backup | 3 phút | 13 phút |
| Tạo server mới | 10 phút | 23 phút |
| Setup server (Ansible) | 15 phút | 38 phút |
| Restore database | 15 phút | 53 phút |
| Restore files | 15 phút | 68 phút |
| Restore config | 10 phút | 78 phút |
| Testing | 10 phút | 88 phút |
| DNS cutover | 2 phút | 90 phút |
| Verification | 27 phút | 117 phút |
Data loss
| Data type | Backup time | Sự cố time | Data mất |
|---|---|---|---|
| Database | 18:00 | 23:00 | 5 giờ data (đơn hàng 18:00–23:00) |
| Files | 02:00 cùng ngày | 23:00 | 0 (files ít thay đổi) |
| Config | Tuần trước | 23:00 | 0 (config không đổi) |
Đơn hàng mất: 127 đơn (từ 18:00–23:00)
Khôi phục: 112/127 đơn được khôi phục từ payment gateway logs + email notifications. Chỉ mất 15 đơn chưa thanh toán.
Chi phí
| Hạng mục | Chi phí |
|---|---|
| Server mới (2 VPS) | 0 VND (chuyển sang server mới, tắt server cũ) |
| Engineer on-call (2 giờ) | Included trong dịch vụ |
| Forensics server cũ | 5 triệu VND |
| Tổng chi phí sự cố | ~5 triệu VND |
| Chi phí nếu không có DR | 200–500 triệu VND (ước tính) |
Chi phí DR plan (đã đầu tư trước)
| Hạng mục | Chi phí/tháng |
|---|---|
| Backblaze B2 storage (50GB) | 6.500 VND |
| Monitoring (Trinh Digital managed) | 5 triệu VND |
| Ansible playbook development (one-time) | 10 triệu VND |
| DR testing hàng tháng | Included |
| Tổng hàng tháng | ~5 triệu VND |
ROI: Chi phí DR 6 tháng = 30 triệu VND. Thiệt hại tránh được = 200–500 triệu VND. ROI: 667–1,567%.
Bài học
1. DR Plan phải có TRƯỚC khi cần
Không ai mua bảo hiểm SAU khi gặp tai nạn. DR plan phải chuẩn bị trước — khi sự cố xảy ra, bạn chỉ việc theo runbook.
2. Immutable backup cứu mạng
Backup trên cùng server = vô dụng khi ransomware. Immutable backup trên cloud (B2 Object Lock, S3 Object Lock) là phòng tuyến cuối cùng và quan trọng nhất.
3. Automation giảm thời gian phục hồi 10x
Setup server thủ công: 4–8 giờ. Setup bằng Ansible playbook: 15 phút. Viết playbook mất 2–3 ngày (one-time), nhưng tiết kiệm hàng giờ mỗi lần cần.
4. Test DR plan hàng tháng
DR plan không test = giấy vụn. Test hàng tháng đảm bảo: backup hoạt động, playbook chạy đúng, team biết phải làm gì, thời gian restore nằm trong SLA.
5. Tách biệt môi trường
Blog WordPress cùng server với e-commerce = 1 lỗ hổng WordPress ảnh hưởng toàn bộ. Tách biệt environments: production, staging, blog, admin.
DR Plan Template cho SME
Thông tin cơ bản
| Field | Giá trị |
|---|---|
| RTO (Recovery Time Objective) | 2 giờ |
| RPO (Recovery Point Objective) | 6 giờ |
| Backup location | Backblaze B2 (immutable) |
| DR contact (primary) | [Tên + SĐT] |
| DR contact (secondary) | [Tên + SĐT] |
Checklist DR
| # | Bước | Thời gian | Ghi chú |
|---|---|---|---|
| 1 | Isolate server bị ảnh hưởng | 5 phút | UFW deny all |
| 2 | Verify backup sạch | 5 phút | Check B2/S3 |
| 3 | Tạo server mới | 10 phút | doctl/aws cli |
| 4 | Run Ansible playbook | 15 phút | setup-production.yml |
| 5 | Restore database | 15 phút | Từ B2 |
| 6 | Restore files | 15 phút | Từ B2 |
| 7 | Restore config | 10 phút | .env, nginx |
| 8 | Test tất cả chức năng | 10 phút | Checklist test |
| 9 | DNS cutover | 2 phút | Cloudflare API |
| 10 | Monitor 30 phút | 30 phút | Dashboard |
FAQ — Câu hỏi thường gặp
Disaster Recovery Plan phức tạp không?
Không, nếu bắt đầu đơn giản. Minimum viable DR plan: (1) Backup offsite hàng ngày, (2) Danh sách server config, (3) Step-by-step restore guide, (4) Contact list. Viết trong 1–2 giờ, cập nhật khi hệ thống thay đổi.
Chi phí Disaster Recovery cho SME bao nhiêu?
Tự làm: 0–50.000 VND/tháng (cloud storage). Managed: 3–10 triệu VND/tháng (bao gồm monitoring + DR testing + response team). So với thiệt hại khi không có DR (100–500+ triệu VND/sự cố), đây là khoản đầu tư xứng đáng.
Kết luận
Disaster Recovery không phải “nếu” mà là “khi nào” — sự cố sẽ xảy ra, chỉ là sớm hay muộn. Khác biệt giữa doanh nghiệp sống sót và doanh nghiệp phá sản sau sự cố nằm ở: có DR plan hay không.
Case study này chứng minh: với DR plan đúng, ransomware attack chỉ gây downtime 2 giờ và thiệt hại 5 triệu VND — thay vì 200–500 triệu VND.
Nếu bạn chưa có Disaster Recovery plan hoặc cần xây dựng hệ thống với DR tích hợp, hãy liên hệ Trinh Digital để được tư vấn và triển khai.