Bỏ qua để đến nội dung

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.

  1. 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ịchJobTask
    03:00 hằng ngàyrenew_cert.sh — renew TLSF.2
    03:30 hằng ngàybackup_database.sh — backup DB mã hóa (rclone tùy chọn qua OFFSITE_REMOTE)S.1
    03:45 hằng ngàybackup_media.sh — backup volume ảnh mã hóaS.1
    04:00 hằng ngàybackup_config.sh — backup .env, credentials, compose, Nginx và crontab mã hóaS.3
    09:00 hằng ngàycheck_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àyprune_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àyprune_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àyprune_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útclose_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útretry_iap_provider_actions.py — thử lại acknowledge/consume với Google PlayO.7
    mỗi 5 phútretry_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àyreconcile_voided_purchases.py — đối soát giao dịch bị hoàn tiền/hủyO.7
    mỗi 10 phútcheck_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àyaggregate_daily.py — số liệu Dashboard ngày UTC hôm quaB.8
    01:00 hằng ngàysend_scheduled_reports.py — báo cáo email (cần SMTP_*)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ằng LOG_DIR=...). check_backups.sh ghi từng trạng thái vào check_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 trong BACKUP_ALERT_TELEGRAM_CHAT_ID (kèm TELEGRAM_BOT_TOKEN), BACKUP_ALERT_SLACK_WEBHOOK hoặc BACKUP_ALERT_EMAIL; email gửi qua alerting_service có sẵn trong backend và cần SMTP_*.

    Để đo lần kéo gần nhất, checker đọc journalctl _COMM=sshd theo cửa sổ UTC BACKUP_MAX_PULL_AGE_DAYS (mặc định 3 ngày), tìm sự kiện Accepted 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ểm BACKUP_DIR và /var/lib/docker nếu chúng nằm trên các filesystem khác nhau, so config_*.manifest.json với credential paths trong .env, kiểm mode .env là 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ần python3 để đọ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.

  2. 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.

  3. (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/health mỗi vài phút.

  4. 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êm du -sh /opt/backups.

  5. 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 xem backup.log/backup_media.log/backup_config.log khi có cảnh báo.

  6. 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ụngCảnh báo backup
Nội dungLỗi nhận dữ liệu, đối soát, đĩa ảnh sắp đầyBackup lỗi/quá hạn, checksum sai, lâu chưa kéo backup về máy, đĩa backup thấp
Ai gửiBackend (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ênhCần trên serverKênh ứng dụng (Portal) nhậpCảnh báo backup (.env)
Telegram (khuyến nghị)TELEGRAM_BOT_TOKENChat idBACKUP_ALERT_TELEGRAM_CHAT_ID
SlackKhôngIncoming Webhook URLBACKUP_ALERT_SLACK_WEBHOOK
EmailSMTP_*Địa chỉ emailBACKUP_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.

  1. Tạo bot: trên Telegram, nhắn @BotFather → /newbot, đặt tên và username (kết thúc bằng bot). Nhận bot token dạng 123456789:AA.... Token là secret: không commit, không gửi công khai.

  2. 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.

  3. 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.

  4. 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.

  1. 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ạng https://hooks.slack.com/services/T.../B.../.... URL này là secret.
  2. Cảnh báo backup: thêm vào .env:
    BACKUP_ALERT_SLACK_WEBHOOK=https://hooks.slack.com/services/...
  3. 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.
  1. Cấu hình SMTP trong .env (email không gửi được nếu thiếu SMTP_HOST hoặc SMTP_FROM_EMAIL). Ví dụ với Gmail:
    SMTP_HOST=smtp.gmail.com
    SMTP_PORT=587
    SMTP_USERNAME=ban@gmail.com
    SMTP_PASSWORD=<App Password 16 ký tự>
    SMTP_FROM_EMAIL=ban@gmail.com
    SMTP_STARTTLS=true
    BACKUP_ALERT_EMAIL=ban@gmail.com # cảnh báo backup
    Với Gmail, SMTP_PASSWORD phả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.
  2. docker compose -f docker-compose.prod.yml up -d backend để nạp SMTP_*. Cảnh báo backup qua email cũng gửi nhờ backend, nên backend phải đang chạy.
  3. 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ỳ.

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):

Terminal window
# Telegram
docker 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'))"
# Slack
docker 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'))"
# Email
docker 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):

Terminal window
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ânXử lý
TELEGRAM_BOT_TOKEN is requiredChưa đặt token, hoặc đặt rồi nhưng chưa up -d backendKiểm .env, tạo lại backend
Telegram API returned 401Token sai hoặc đã bị thu hồiLấy lại token ở @BotFather (/token)
Telegram API returned 400: chat not found / 403Chat id sai, chưa bấm Start với bot, hoặc bot chưa ở trong groupBấm Start / thêm bot vào group, lấy lại chat id
Slack trả 404 / invalid_tokenWebhook đã bị xóa hoặc dán thiếuTạo webhook mới
SMTP_HOST and SMTP_FROM_EMAIL are requiredThiếu SMTP_* hoặc chưa up -d backendĐiền SMTP_*, tạo lại backend
SMTPAuthenticationErrorSai tài khoản SMTP, hoặc dùng mật khẩu Gmail thay vì App PasswordTạo App Password mới

Áp dụng cho Portal v1.2.2