Hướng dẫn sử dụng emday

Cài đặt và vận hành emday trên server Linux: theo dõi IP public, CPU, RAM, ổ cứng, process, chứng chỉ TLS và script tuỳ ý — nhận thông báo qua Telegram, Lark, ntfy, Slack, Discord.

Tên gọi: khi server đổi IP, emday nhắn cho bạn — “em đây, IP mới nè!” 😉

1. Cài đặt

Cách nhanh nhất — một lệnh, tự nhận diện OS/kiến trúc máy, tự kiểm tra sha256:

curl -fsSL https://madnh.github.io/emday/install.sh | sh
💡 Muốn đọc script trước khi chạy (thói quen tốt!): mở install.sh — nó chỉ tải file release, so checksum rồi install vào /usr/local/bin, không đụng gì khác. Cài phiên bản cụ thể: EMDAY_VERSION=0.1.0 sh; đổi chỗ cài: EMDAY_INSTALL_DIR=...

Hoặc cài thủ công từ bản release mới nhất (chọn linux_amd64 hoặc linux_arm64 tuỳ máy — xem bằng uname -m):

# thay VERSION bằng phiên bản thực tế, ví dụ 0.1.0
curl -fsSL -o emday.tar.gz \
  https://github.com/madnh/emday/releases/download/vVERSION/emday_VERSION_linux_amd64.tar.gz
tar -xzf emday.tar.gz emday
sudo install -m 0755 emday /usr/local/bin/emday
emday version

Chỉ cần đúng một file binary — không phải cài thêm gì khác.

2. Khởi tạo cấu hình

sudo emday init

Chạy bằng sudo, emday tạo thư mục /etc/emday chứa toàn bộ những gì nó cần:

/etc/emday/
├── emday.yaml           # file cấu hình (file chính bạn sửa)
├── config.md            # tài liệu cấu hình, sinh tự động
├── emday.env.example    # mẫu khai secret — điền giá trị rồi copy (xem mục 4)
├── state.json           # bộ nhớ của engine (emday tự quản — đừng sửa)
├── queue/               # thông báo chưa gửi được (tự quản)
└── tmp/                 # chỗ tạm cho exec script (tự quản)
💡 init là lệnh duy nhất tạo thư mục cấu hình, và nó từ chối ghi đè thư mục đã có. Mọi lệnh khác báo lỗi rõ ràng khi chưa init — không bao giờ âm thầm tạo dữ liệu sai chỗ.

3. Cấu hình

Mở /etc/emday/emday.yaml — file mẫu có sẵn comment giải thích từng phần. Cấu trúc gồm 3 khối: sources (thu thập gì) → rules (khi nào báo) → notifiers (báo đi đâu).

3.1. Theo dõi IP public (use case gốc)

sources:
  wan:
    type: public-ip               # IP public → metric wan.v4
    interval: 5m
    mode: [v4]                    # thêm "v6" nếu cần IPv6
    endpoints_v4:                 # nơi hỏi "IP của tôi là gì" — bạn tự chọn,
      - https://api.ipify.org     # thử lần lượt tới khi có kết quả hợp lệ
      - https://checkip.amazonaws.com

  lan:
    type: local-ip                # IP LAN của card mạng → metric lan.eth0_v4
    interval: 30s                 # đọc từ kernel, không gọi mạng
    interfaces: [eth0]            # xem tên card: ip -br addr

rules:
  - metric: wan.v4
    on_change: true               # báo mỗi khi IP public đổi
    notify: [tele]
  - metric: lan.eth0_v4
    on_change: true               # báo khi IP LAN đổi
    notify: [tele]

emday chỉ chấp nhận response là đúng một địa chỉ IP hợp lệ — endpoint trả về HTML hay trang lỗi sẽ bị bỏ qua và thử endpoint kế tiếp, không bao giờ bị nhầm thành "IP đổi".

3.2. Ngưỡng CPU / RAM / ổ cứng

sources:
  cpu:    {type: cpu, interval: 30s}
  memory: {type: memory}
  disk:
    type: disk
    interval: 5m
    mounts: {root: "/", data: "/data"}

rules:
  - metric: cpu.percent
    condition: "value >= 90"      # biểu thức trên biến `value`
    for: 5m                       # phải cao liên tục 5 phút mới báo (chống nhiễu)
    notify: [tele]
  - metric: disk.root.percent
    condition: "value >= 90"
    level: error
    notify: [tele]

Khi hết vượt ngưỡng, emday tự gửi thông báo resolved ✅. Bảng cú pháp điều kiện:

MuốnViết
So sánh sốvalue > 90, value <= 10, value == 0
So sánh chuỗivalue == "failed", value != "ok"
Chứa / bắt đầu / kết thúcvalue contains "err", value startsWith "eth"
Thuộc danh sáchvalue in ["a", "b"], value not in ["ok"]
Kết hợp... and ..., ... or ..., not (...)

Thử cú pháp ngay không cần sửa config:

emday test-rule 'value >= 90' --value 95        # in ra: true
emday test-rule 'value not in ["ok"]' --value failed

3.3. Chứng chỉ TLS sắp hết hạn

sources:
  cert:
    type: cert
    interval: 6h                  # mặc định của loại này — cert đổi theo ngày,
    timeout: 10s                  # không cần probe mỗi phút
    endpoints:                    # alias -> host[:port], port mặc định 443
      www: "example.com"
      api: "api.example.com:8443"

rules:
  - metric: cert.www.days_left
    condition: "value <= 14"      # còn 14 ngày là phải gia hạn
    level: error
    notify: [tele]
  - metric: cert.www.status
    condition: 'value != "ok"'    # hết hạn / sai host / chain hỏng / không tới được
    for: 15m                      # bỏ qua trục trặc mạng thoáng qua
    notify: [tele]
  - metric: cert.www.issuer
    on_change: true               # cert bị thay giữa chừng
    notify: [tele]

Mỗi alias sinh ra ba metric: days_left (số ngày còn lại, âm khi đã hết hạn), status, và issuer. status nhận một trong các giá trị ok, expired, hostname-mismatch, untrusted, dns-failure, refused, timeout, handshake-error, connect-error.

⚠️ Source này cần trust store của hệ thống — đây là yêu cầu duy nhất nó đặt lên host. Máy chủ cài bình thường thì có sẵn; image container tối giản thì thường không. Đo trên ubuntu:24.04 chưa có gói ca-certificates: mọi endpoint đều ra untrusted, kể cả cert hoàn toàn tốt. Cài ca-certificates là về lại ok. Kiểm tra bằng ls /etc/ssl/certs/ca-certificates.crt trước khi kết luận một cert là không tin cậy.
💡 Nên đặt rule cho cả status, đừng chỉ canh days_left: khi host không tới được thì days_left không có giá trị để so sánh, rule im lặng — đúng lúc bạn cần biết nhất. status thì luôn được ghi nhận, kể cả khi probe thất bại.

emday bắt tay TLS nhưng không gửi request nào, lấy chứng chỉ xong là đóng kết nối. Việc kiểm tra chain chạy riêng sau đó, nên "hết hạn" và "chain không tin cậy" là hai câu trả lời độc lập — không cái nào che cái nào. Rule issuer + on_change là thứ duy nhất bắt được trường hợp chứng chỉ bị thay bằng proxy chặn giữa: cert mới hợp lệ, còn hạn dài, mọi rule khác đều im.

3.4. Đích thông báo

Telegram — tạo bot với @BotFather, thêm bot vào group, lấy chat id:

notifiers:
  tele:
    type: telegram
    token_env: EMDAY_TG_TOKEN     # token nằm trong biến môi trường (xem mục 4)
    chat_id: "-100123456"

Lark — trong group: Settings → Bots → Add bot → Custom bot, copy webhook URL. Nếu bot bật chế độ chữ ký (signature) thì khai thêm secret_env:

notifiers:
  lark:
    type: lark
    url: https://open.larksuite.com/open-apis/bot/v2/hook/xxxx
    secret_env: EMDAY_LARK_SECRET   # TÊN biến env chứa secret (không phải giá trị secret!)
    # — hoặc ghi thẳng secret (file config nên là root, chmod 600):
    # secret: "xxxxxxxxxxxx"

ntfy — nhận push lên điện thoại không cần đăng ký, chỉ cần đặt topic khó đoán:

notifiers:
  phone:
    type: ntfy
    url: https://ntfy.sh/ten-topic-bi-mat-cua-ban

Ngoài ra còn slack, discord, và webhook (tuỳ biến hoàn toàn) — xem emday docs notifiers.

Với slack/discord/webhook, URL chính là bí mật (token nằm trong đường dẫn). Muốn giữ URL ngoài file config, khai url_env (TÊN biến env chứa URL) thay cho url:

notifiers:
  slack:
    type: slack
    url_env: EMDAY_SLACK_URL   # nạp qua systemd EnvironmentFile — xem mục 4

3.5. Kiểm tra trước khi chạy

emday check-config              # validate config + compile mọi điều kiện
export EMDAY_TG_TOKEN=xxxx      # tạm thời, để test
emday test-notify tele          # gửi thử một tin — kiểm tra điện thoại!

4. Chạy như service (systemd)

sudo emday install
sudo emday start
emday status                    # running

emday tự đăng ký vào systemd. Service không kế thừa shell của bạn, nên mọi biến env của notifier (token_env, secret_env, url_env) phải khai trực tiếp cho service.

Bạn không cần nhớ tên biến: emday init đã tạo sẵn /etc/emday/emday.env.example liệt kê đủ các biến EMDAY_*, cái nào chưa dùng thì để dạng comment. Chỉ việc điền giá trị:

sudoedit /etc/emday/emday.env.example
# /etc/emday/emday.env.example — dạng KEY=VALUE, không có `export`
EMDAY_TG_TOKEN=123456:ABC-token-cua-bot
#EMDAY_LARK_SECRET=xxxx
#EMDAY_SLACK_URL=https://hooks.slack.com/services/T/B/X

Rồi copy nó tới chỗ service đọc. Unit do emday install sinh ra đã sẵn đọc /etc/sysconfig/emday, nên không cần sửa unit:

sudo install -m 0600 /etc/emday/emday.env.example /etc/sysconfig/emday
sudo systemctl restart emday

Nếu bạn tự viết unit thì trỏ EnvironmentFile= vào bản copy của mình:

sudo install -m 0600 /etc/emday/emday.env.example /etc/emday/emday.env
sudo systemctl edit emday
# nội dung override:
[Service]
EnvironmentFile=/etc/emday/emday.env
sudo systemctl restart emday
⚠️ emday KHÔNG tự đọc file này. Nó chỉ là bản mẫu — điền xong mà chưa copy tới chỗ EnvironmentFile= trỏ vào thì service vẫn không thấy secret. Và vì biến chỉ được đọc lúc process khởi động, sửa file xong phải systemctl restart emday — daemon-reload không đủ.
💡 Không source nào cần root — nên chạy service bằng user riêng (User=emday) cho an toàn. Toàn bộ chuyện triển khai fleet (systemd, non-root, secret, pin version, gate check-config) gói trong emday docs deploy.
💡 Muốn chạy thử trước khi cài service: sudo emday run (chạy foreground, log ra màn hình, Ctrl-C để dừng).

5. Mở rộng bằng script của bạn

Bất kỳ script nào cũng thành nguồn dữ liệu: ghi KEY=VALUE vào file $EMDAY_OUTPUT (giống $GITHUB_OUTPUT của GitHub Actions). stdout để log thoải mái, không bị parse.

#!/bin/sh
# /opt/scripts/check-backup.sh
age=$(( ($(date +%s) - $(stat -c %Y /backup/latest.tar.gz)) / 3600 ))
echo "BACKUP_AGE_HOURS=$age" >> "$EMDAY_OUTPUT"

# logic đặc thù chỉ script này hiểu → tự bắn thông báo trực tiếp:
if [ "$age" -gt 26 ] && [ -f /backup/.full-day ]; then
  echo "NOTIFY_ERROR=Full backup quá hạn (${age}h)" >> "$EMDAY_OUTPUT"
fi
sources:
  backup:
    type: exec
    command: /opt/scripts/check-backup.sh
    interval: 5m
    timeout: 30s
    notify: [tele]              # đích cho các dòng NOTIFY_*

rules:
  - metric: backup.BACKUP_AGE_HOURS
    condition: "value > 24"
    notify: [tele]
  - metric: backup._ok          # script chết cũng biết
    condition: "value == 0"
    notify: [tele]

6. Chẩn đoán khi có vấn đề

emday doctor --verdict

Lệnh này tuyệt đối chỉ đọc, trả lời lần lượt: thư mục cấu hình nào đang được dùng? config có hợp lệ không? daemon đã chạy chưa? có thông báo nào đang kẹt trong hàng đợi không? — kèm kết luận và bước xử lý tiếp theo.

Triệu chứngKiểm tra
Không nhận được thông báoemday doctor --verdict — nhìn mục queue; nếu kẹt thì emday test-notify <tên> để xem lỗi cụ thể (sai token? sai URL?)
Muốn biết rule có bắn khôngjournalctl -u emday | grep -E 'alert |delivered' — mỗi thông báo ghi 2 dòng: lúc rule khớp và lúc gửi thành công. Bị chặn bởi cooldown cũng có dòng riêng nói rõ lý do
Service không chạyemday status, rồi journalctl -u emday -n 50
Sửa config xong quên kiểm traemday check-config — luôn chạy sau mỗi lần sửa
Điều kiện rule không như ýemday test-rule '...' --value ...
Lark báo code 19021: sign match fail...Đồng hồ server lệch quá 1 giờ (timedatectl — bật NTP: sudo timedatectl set-ntp true), hoặc secret sai / env chưa set ở nơi emday chạy (emday doctor sẽ cảnh báo env thiếu)

7. Tài liệu đầy đủ (trong chính binary)

Mỗi chủ đề là một file Markdown nằm ngay trong binary (emday docs <chủ-đề>) — cũng chính là file bạn (hoặc AI agent) đọc được trên GitHub. Trang này chỉ liệt kê, bấm vào để tới đúng file:

Tra cứu từng builtin source (metric chính xác, quyền cần, rule mẫu, cạm bẫy):

🤖 Dùng Claude Code / AI agent trên server? Bảo nó chạy emday docs agent — một trang đó đủ để agent tự cấu hình, viết script mở rộng và chẩn đoán emday, không cần tài liệu nào khác.

8. Nâng cấp & gỡ cài đặt

# nâng cấp: thay binary rồi khởi động lại
sudo emday stop
sudo install -m 0755 emday-moi /usr/local/bin/emday
sudo emday start

# gỡ cài đặt
sudo emday stop && sudo emday uninstall
sudo rm /usr/local/bin/emday
sudo rm -rf /etc/emday          # xoá luôn cấu hình + state nếu muốn