Giám sát cơ bản & job định kỳ
Chi tiết đầy đủ: tasks/phase_f/task_f.9_basic_uptime_monitoring_and_alerting.md.
-
Cài toàn bộ job định kỳ (chạy lại bao nhiêu lần cũng được — script chỉ thay khối cron của nó, không đụng cron khác):
Terminal window ./scripts/install_cron.sh --dry-run # xem trước./scripts/install_cron.sh # cài / cập nhật (tạo /var/log/admin_portal, cần sudo 1 lần)./scripts/install_cron.sh --with-admob # thêm job AdMob khi đã cấu hình ADMOB_*./scripts/install_cron.sh --remove # gỡDanh sách job (
scripts/admin_portal.crontab, giờ theo giờ VPS):Lịch Job Task 03:00 hằng ngày renew_cert.sh— renew TLSF.2 03:30 hằng ngày backup_database.sh— backup DB mã hóa (rclone tùy chọn quaOFFSITE_REMOTE)S.1 03:45 hằng ngày backup_media.sh— backup volume ảnh mã hóaS.1 04:00 hằng ngày backup_config.sh— backup.env, credentials, compose, Nginx và crontab mã hóaS.3 09:00 hằng ngày check_backups.sh --quiet-ok— tuổi, checksum, credentials, pull SSH, dung lượng và tăng trưởng DBS.5 04:30 hằng ngày prune_progress_increases.py --retention-days 90— giữ 90 ngày lịch sử tăng tiến trìnhL.3 04:40 hằng ngày prune_unused_assets.py --older-than-days 7 --apply— xóa ảnh không được dùng quá 7 ngàyN.6 04:50 hằng ngày prune_client_requests.py --retention-days 30— dọn kết quả chống trùng request quá 30 ngàyO.4 mỗi 15 phút close_stale_runs.py— đóng lượt chơi bỏ dở quá 24 giờ (coi như đã dùng hết, không thưởng)O.5 mỗi 5 phút retry_iap_provider_actions.py— thử lại acknowledge/consume với Google PlayO.7 mỗi 5 phút retry_iap_pending_events.py— xử lý thông báo IAP đang chờ liên kết người chơiO.7 02:25 hằng ngày reconcile_voided_purchases.py— đối soát giao dịch bị hoàn tiền/hủyO.7 mỗi 10 phút check_alerts.py— cảnh báo ingestion / reconciliationB.9, F.9 phút 15 mỗi giờ expire_player_rewards.py— quà hộp thư quá hạnH.4 phút 20 mỗi giờ expire_approval_requests.py— yêu cầu duyệt quá hạnD.3 mỗi 15 phút (phút 5/20/35/50) close_leaderboard_periods.py— chốt kỳ tuần đã hết hạn, tạo bản thưởng nhápL.5 đầu mỗi giờ check_pending_leaderboard_distributions.py— nhắc bản nháp quá 72 giờ, không tự phátL.5/D13 00:10 hằng ngày aggregate_daily.py— số liệu Dashboard ngày UTC hôm quaB.8 01:00 hằng ngày send_scheduled_reports.py— báo cáo email (cầnSMTP_*)D.5 02:30 hằng ngày (tùy chọn) ingest_admob_revenue.py— doanh thu AdMobC.4 Log:
/var/log/admin_portal/<job>.log(đổi bằngLOG_DIR=...).check_backups.shghi từng trạng thái vàocheck_backups.log, gửi một alert gộp cho các lỗi/cảnh báo, và chạy im lặng khi mọi thứ ổn. Mỗi thứ Hai nó gửi tóm tắt all-good để kiểm tra kênh còn hoạt động. Kết quả là exit 0 (ổn), 1 (cảnh báo), 2 (lỗi). Cần cấu hình ít nhất một trongBACKUP_ALERT_TELEGRAM_CHAT_ID(kèmTELEGRAM_BOT_TOKEN),BACKUP_ALERT_SLACK_WEBHOOKhoặcBACKUP_ALERT_EMAIL; email gửi quaalerting_servicecó sẵn trong backend và cầnSMTP_*.Để đo lần kéo gần nhất, checker đọc
journalctl _COMM=sshdtheo cửa sổ UTCBACKUP_MAX_PULL_AGE_DAYS(mặc định 3 ngày), tìm sự kiệnAccepted publickey for backup-pull..last_pullđược ghi trên máy người vận hành nên VPS không đọc file đó. Nếu cron user không đọc được journal, checker báo lỗi thay vì coi lần kéo là mới; hãy chạy cron bằng root hoặc cấp quyền đọc systemd journal. Checker cũng kiểmBACKUP_DIRvà/var/lib/dockernếu chúng nằm trên các filesystem khác nhau, soconfig_*.manifest.jsonvới credential paths trong.env, kiểm mode.envlà600, và cảnh báo dump DB lớn hơn 2 lần trung bình 7 ngày. Máy VPS cầnpython3để đọc manifest và gửi payload Slack. Không cấu hình kênh alert thì cảnh báo chỉ nằm trong log, đây chưa đủ cho production. -
Cấu hình ít nhất 1 kênh nhận cảnh báo thật (Telegram, Slack hoặc email) cho cả hai loại cảnh báo: cảnh báo ứng dụng và cảnh báo backup. Làm theo §10.1.
-
(Khuyến nghị) đăng ký 1 dịch vụ uptime-check ngoài (UptimeRobot hoặc tương đương) ping
https://portal.tinysoft.io.vn/healthmỗi vài phút. -
Theo dõi dung lượng đĩa (volume Postgres và volume ảnh):
docker system df -v | grep -E "pgdata|media",df -h /var/lib/docker. Backup nằm chung đĩa với Postgres: xem thêmdu -sh /opt/backups. -
check_backups.sh(09:00 hằng ngày) cảnh báo khi backup quá hạn, hỏng, thiếu credential, lâu chưa kéo về hoặc đĩa thấp. Vẫn nên xembackup.log/backup_media.log/backup_config.logkhi có cảnh báo. -
Phase O: thường xuyên xem Portal → Bất thường kinh tế (thưởng vượt trần lượt chơi, chuyển save cũ bị cắt, hoàn tiền IAP thiếu số dư).
10.1. Cấu hình cảnh báo: Telegram, Slack, Email
Phần tiêu đề “10.1. Cấu hình cảnh báo: Telegram, Slack, Email”Có hai loại cảnh báo, cấu hình ở hai chỗ khác nhau:
| Cảnh báo ứng dụng | Cảnh báo backup | |
|---|---|---|
| Nội dung | Lỗi nhận dữ liệu, đối soát, đĩa ảnh sắp đầy | Backup lỗi/quá hạn, checksum sai, lâu chưa kéo backup về máy, đĩa backup thấp |
| Ai gửi | Backend (check_alerts.py, 10 phút/lần) | Script trên VPS (backup_*.sh, check_backups.sh) |
| Nơi nhận cấu hình ở | Portal → Alert Channels (lưu trong DB, chọn được theo game hoặc tất cả game) | Biến BACKUP_ALERT_* trong .env |
Script backup cố ý không đọc kênh trong DB: nó phải báo được cả khi backend hoặc DB đang hỏng. Muốn mọi cảnh báo về một chỗ thì dùng cùng một đích (cùng chat id / webhook / email) cho cả hai. Mỗi loại có thể bật một hoặc nhiều kênh; mọi kênh đã cấu hình đều nhận tin.
| Kênh | Cần trên server | Kênh ứng dụng (Portal) nhập | Cảnh báo backup (.env) |
|---|---|---|---|
| Telegram (khuyến nghị) | TELEGRAM_BOT_TOKEN | Chat id | BACKUP_ALERT_TELEGRAM_CHAT_ID |
| Slack | Không | Incoming Webhook URL | BACKUP_ALERT_SLACK_WEBHOOK |
SMTP_* | Địa chỉ email | BACKUP_ALERT_EMAIL |
Mọi lệnh dưới đây chạy tại thư mục cài đặt trên VPS. Sao lưu .env trước khi
sửa (cp -p .env .env.bak.$(date -u +%Y%m%d_%H%M%S)). Sửa biến của backend
(TELEGRAM_BOT_TOKEN, SMTP_*) xong phải tạo lại backend —
docker compose -f docker-compose.prod.yml up -d backend (restart không đọc
lại .env). Biến BACKUP_ALERT_* thì script đọc mỗi lần chạy, không cần làm gì thêm.
Telegram
Phần tiêu đề “Telegram”-
Tạo bot: trên Telegram, nhắn
@BotFather→/newbot, đặt tên và username (kết thúc bằngbot). Nhận bot token dạng123456789:AA.... Token là secret: không commit, không gửi công khai. -
Chọn nơi nhận và lấy chat id. Chat id là id của cuộc trò chuyện bot gửi vào:
- Chat riêng với bạn: tìm bot theo username, bấm Start (bắt buộc —
bot không tự nhắn cho người chưa từng nhắn nó, lỗi
403 bot can't initiate conversation). Chat id = user id của bạn (số dương); có thể xem nhanh bằng cách nhắn@userinfobot. - Group: tạo group, thêm bot, gửi trong group một lệnh như
/start@ten_bot. Chat id của group là số âm, thường dạng-100....
Lấy chat id từ tin vừa gửi (trong vòng 24 giờ):
Terminal window curl -s "https://api.telegram.org/bot<TOKEN>/getUpdates" | python3 -m json.tool | grep -A3 '"chat"'Kết quả
"result": []nghĩa là chưa có tin nào: nhắn lại cho bot rồi chạy lại. - Chat riêng với bạn: tìm bot theo username, bấm Start (bắt buộc —
bot không tự nhắn cho người chưa từng nhắn nó, lỗi
-
Thêm vào
.env:TELEGRAM_BOT_TOKEN=123456789:AA...BACKUP_ALERT_TELEGRAM_CHAT_ID=<chat id>rồi
docker compose -f docker-compose.prod.yml up -d backend. -
Kênh ứng dụng: Portal → Alert Channels → Thêm kênh → loại Telegram, chọn game (hoặc Tất cả game), nhập chat id.
Token chỉ nằm trong .env: không lưu vào DB, không hiện trên UI, không ghi vào
log khi gửi lỗi. Đổi token: sửa .env rồi up -d backend; kênh đã tạo giữ nguyên.
Slack
Phần tiêu đề “Slack”- Tạo webhook: tại
https://api.slack.com/apps→ Create New App → From scratch, chọn workspace → Incoming Webhooks → bật → Add New Webhook to Workspace, chọn channel nhận. Nhận URL dạnghttps://hooks.slack.com/services/T.../B.../.... URL này là secret. - Cảnh báo backup: thêm vào
.env:BACKUP_ALERT_SLACK_WEBHOOK=https://hooks.slack.com/services/... - Kênh ứng dụng: Portal → Alert Channels → Thêm kênh → loại Slack, dán webhook URL. Không cần cấu hình gì trên server.
- Cấu hình SMTP trong
.env(email không gửi được nếu thiếuSMTP_HOSThoặcSMTP_FROM_EMAIL). Ví dụ với Gmail:Với Gmail,SMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USERNAME=ban@gmail.comSMTP_PASSWORD=<App Password 16 ký tự>SMTP_FROM_EMAIL=ban@gmail.comSMTP_STARTTLS=trueBACKUP_ALERT_EMAIL=ban@gmail.com # cảnh báo backupSMTP_PASSWORDphải là App Password (Google Account → Bảo mật → Mật khẩu ứng dụng; tài khoản phải bật xác minh 2 bước), không phải mật khẩu đăng nhập. Nhà cung cấp SMTP khác (SendGrid, Mailgun, SES…) điền host/port/tài khoản tương ứng. docker compose -f docker-compose.prod.yml up -d backendđể nạpSMTP_*. Cảnh báo backup qua email cũng gửi nhờ backend, nên backend phải đang chạy.- Kênh ứng dụng: Portal → Alert Channels → Thêm kênh → loại Email, nhập địa chỉ nhận. SMTP đặt cho cả server, cũng dùng cho báo cáo email định kỳ.
Gửi thử
Phần tiêu đề “Gửi thử”Kênh ứng dụng: Portal → Alert Channels → bấm nút Gửi thử (biểu
tượng máy bay giấy) ở dòng kênh. Portal gửi một tin thử qua đúng kênh đó rồi
báo kết quả: thành công thì kiểm tra tin đã tới; thất bại thì hiện lý do kèm
cách sửa (thiếu token, chat id sai, chưa bấm Start với bot, SMTP chưa cấu
hình…). Mỗi lần gửi thử được ghi vào Audit Log (alert_channel.test), tối
đa 6 lần/phút (ALERT_CHANNEL_TEST_RATE_LIMIT).
Khi không vào được Portal, gửi thử từ VPS (thay đích tương ứng; lệnh in lỗi nếu gửi không được):
# Telegramdocker compose -f docker-compose.prod.yml exec -T backend python -c \ "import asyncio; from admin_portal.services.alerting_service import _send_telegram as s; asyncio.run(s('<chat id>', '[Admin Portal] Test', 'Kênh Telegram hoạt động'))"# Slackdocker compose -f docker-compose.prod.yml exec -T backend python -c \ "import asyncio; from admin_portal.services.alerting_service import _send_slack as s; asyncio.run(s('<webhook URL>', '[Admin Portal] Test: kênh Slack hoạt động'))"# Emaildocker compose -f docker-compose.prod.yml exec -T backend python -c \ "from admin_portal.services.alerting_service import _send_email_sync as s; s('<email>', '[Admin Portal] Test', 'Kênh email hoạt động')"Cảnh báo backup (gửi tới mọi kênh BACKUP_ALERT_* đang đặt, và in ra kênh nào lỗi):
bash -c 'source scripts/lib_backup.sh; load_env; alert "Test" "kiểm tra kênh cảnh báo backup"'check_backups.sh còn tự gửi một tin tóm tắt “mọi thứ ổn” vào mỗi thứ Hai để
xác nhận kênh vẫn hoạt động. Không cấu hình kênh nào thì cảnh báo chỉ nằm
trong log (/var/log/admin_portal/*.log) — chưa đủ cho production.
| Lỗi khi gửi thử | Nguyên nhân | Xử lý |
|---|---|---|
TELEGRAM_BOT_TOKEN is required | Chưa đặt token, hoặc đặt rồi nhưng chưa up -d backend | Kiểm .env, tạo lại backend |
Telegram API returned 401 | Token sai hoặc đã bị thu hồi | Lấy lại token ở @BotFather (/token) |
Telegram API returned 400: chat not found / 403 | Chat id sai, chưa bấm Start với bot, hoặc bot chưa ở trong group | Bấm Start / thêm bot vào group, lấy lại chat id |
Slack trả 404 / invalid_token | Webhook đã bị xóa hoặc dán thiếu | Tạo webhook mới |
SMTP_HOST and SMTP_FROM_EMAIL are required | Thiếu SMTP_* hoặc chưa up -d backend | Điền SMTP_*, tạo lại backend |
SMTPAuthenticationError | Sai tài khoản SMTP, hoặc dùng mật khẩu Gmail thay vì App Password | Tạo App Password mới |
Áp dụng cho Portal v1.2.2