Giao diện
systemd — Quản Lý Service trên Linux Intermediate
Bạn deploy xong app lên server, chạy python app.py &, rồi tắt SSH. Sáng hôm sau khách hàng gọi: "App chết rồi!". Bạn SSH lại, chạy lại. Tuần sau — lặp lại.
Đây không phải lỗi của app. Đây là lỗi của cách bạn chạy app. Production server không chạy bằng nohup và & — nó chạy bằng systemd.
systemd là thứ phân biệt giữa một developer "chạy được trên máy mình" và một engineer "deploy được lên production". Bài này sẽ biến bạn từ người thứ nhất thành người thứ hai.
1. systemd — Bộ Não Của Linux Server
🎯 Mục tiêu
- Hiểu systemd là gì và tại sao nó là PID 1
- Nắm mental model: units, targets, dependencies
- Phân biệt các loại unit: service, timer, socket, mount
systemd là PID 1
Khi Linux boot, kernel khởi động đúng một process duy nhất — đó là systemd (PID 1). Mọi process khác trên hệ thống đều là con cháu của nó. systemd là process đầu tiên khởi động và cuối cùng tắt. Nó quản lý tất cả: services, timers, mounts, network, logging.
Nếu systemd chết → toàn bộ hệ thống chết. Đó là lý do nó được thiết kế cực kỳ robust.
Mental Model — Cấu Trúc systemd
systemd (PID 1)
├── Targets (giống "runlevels")
│ ├── multi-user.target ← Server mode (no GUI)
│ ├── graphical.target ← Desktop mode
│ └── rescue.target ← Emergency repair
│
├── Service Units (.service)
│ ├── nginx.service
│ ├── postgresql.service
│ └── myapp.service ← Your app!
│
├── Timer Units (.timer) ← Cron replacement
├── Socket Units (.socket) ← Socket activation
└── Mount Units (.mount) ← Filesystem mountsCách đọc sơ đồ này:
- Targets = nhóm các services cần chạy cùng nhau.
multi-user.targetlà target phổ biến nhất trên server — nó kéo theo tất cả service cần thiết để server hoạt động (network, SSH, database, app…). - Service Units = những chương trình chạy nền. Nginx phục vụ web, PostgreSQL chạy database,
myapp.servicechạy app của bạn. - Timer Units = thay thế cron, nhưng mạnh hơn (có logging, dependency management).
- Socket Units = khởi động service chỉ khi có connection đến (lazy activation).
- Mount Units = quản lý việc mount filesystem.
Target — "Bạn muốn server ở trạng thái nào?"
Target là cách systemd trả lời câu hỏi: "Server nên chạy những gì?"
| Target | Mô tả | Khi nào dùng |
|---|---|---|
multi-user.target | Server mode, không GUI | Production server (99% trường hợp) |
graphical.target | Desktop đầy đủ | Máy dev có GUI |
rescue.target | Single-user, minimal | Sửa lỗi khẩn cấp |
emergency.target | Bare minimum, root shell | Hệ thống hỏng nặng |
bash
# Xem target hiện tại
systemctl get-default
# → multi-user.target (trên server)
# Đổi default target
sudo systemctl set-default multi-user.target⚠️ Cạm bẫy
Problem: Nhiều developer mới mắc hai sai lầm kinh điển:
Dùng
nohupvà&để chạy production app — không auto-restart khi crash, không log management, không dependency ordering. App chết lúc 3 giờ sáng, không ai biết cho đến khi khách hàng gọi.Khởi động service thủ công sau mỗi lần reboot — server reboot vì maintenance, quên chạy
systemctl start myapp. Kết quả: app down hàng giờ cho đến khi ai đó phát hiện.
Root cause: Không hiểu rằng systemctl enable tạo symlink để service tự động start khi boot.
Solution: Luôn dùng systemd cho production services. Luôn enable sau khi setup.
bash
# ✅ Đúng: enable + start
sudo systemctl enable --now myapp.service
# ❌ Sai: chỉ start, quên enable → mất sau reboot
sudo systemctl start myapp.service2. Viết .service File — Chạy App Như Service
🎯 Mục tiêu
- Viết được unit file
.servicehoàn chỉnh cho production - Hiểu từng section:
[Unit],[Service],[Install] - Áp dụng security hardening và resource limits
Ví dụ hoàn chỉnh — FastAPI Backend
Giả sử bạn có một FastAPI app cần chạy production trên server. Đây là file .service đầy đủ:
ini
# /etc/systemd/system/myapp.service
[Unit]
Description=MyApp FastAPI Backend
Documentation=https://github.com/team/myapp
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=exec
User=deploy
Group=deploy
WorkingDirectory=/opt/myapp
Environment=PYTHONPATH=/opt/myapp
Environment=APP_ENV=production
EnvironmentFile=/opt/myapp/.env
ExecStart=/opt/myapp/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StartLimitBurst=3
StartLimitIntervalSec=60
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/myapp /opt/myapp/data
# Resource limits
MemoryMax=512M
CPUQuota=80%
[Install]
WantedBy=multi-user.targetGiải thích từng section
[Unit] — Metadata và Dependencies
| Directive | Giải thích |
|---|---|
Description | Mô tả ngắn, hiện trong systemctl status |
Documentation | Link tài liệu (optional nhưng nên có) |
After | Chỉ start sau khi các unit này đã start. Đây là thứ tự, không phải dependency |
Wants | "Tôi muốn unit này cũng chạy, nhưng nếu nó fail thì tôi vẫn start". Soft dependency |
💡 After vs. Wants vs. Requires
After=X→ "Start sau X" (ordering only, không bắt X phải chạy)Wants=X→ "Cố gắng start X, nhưng tôi vẫn chạy nếu X fail" (soft dependency)Requires=X→ "X phải chạy. Nếu X fail → tôi cũng fail" (hard dependency)
Trong thực tế, After + Wants là combo phổ biến nhất. Dùng Requires chỉ khi app thật sự không thể hoạt động thiếu dependency đó.
[Service] — Cách Chạy, Restart Policy, Security
| Directive | Giải thích |
|---|---|
Type=exec | systemd theo dõi main process. Dùng cho hầu hết app hiện đại |
User=deploy | Chạy với user deploy, KHÔNG BAO GIỜ chạy với root |
WorkingDirectory | cd vào thư mục này trước khi chạy |
Environment | Set biến môi trường. Mỗi biến một dòng |
EnvironmentFile | Load biến từ file .env (giữ secrets ra khỏi unit file) |
ExecStart | Command chạy app. Phải dùng absolute path |
ExecReload | Command reload config mà không restart (graceful) |
Restart=on-failure | Auto-restart khi exit code ≠ 0 |
RestartSec=5 | Đợi 5 giây trước khi restart (tránh restart storm) |
StartLimitBurst=3 | Tối đa 3 lần restart… |
StartLimitIntervalSec=60 | …trong 60 giây. Vượt quá → systemd ngưng restart |
Security hardening — những dòng này ngăn service làm điều ngoài ý muốn:
| Directive | Giải thích |
|---|---|
NoNewPrivileges=true | Không thể escalate privileges (quan trọng!) |
ProtectSystem=strict | Filesystem read-only, trừ /dev, /proc, /sys |
ProtectHome=true | Không truy cập được /home, /root, /run/user |
ReadWritePaths=... | Whitelist các path được phép ghi |
Resource limits — ngăn service ăn hết tài nguyên server:
| Directive | Giải thích |
|---|---|
MemoryMax=512M | Giới hạn RAM. Vượt quá → OOM kill |
CPUQuota=80% | Giới hạn CPU usage |
[Install] — Khi Nào Start
ini
[Install]
WantedBy=multi-user.targetDòng này nghĩa là: "Khi systemctl enable myapp, hãy tạo symlink để myapp tự động start cùng multi-user.target (tức là khi server boot vào chế độ bình thường)."
📌 Quy trình tạo service mới
bash
# 1. Tạo file service
sudo nano /etc/systemd/system/myapp.service
# 2. Reload systemd để nó biết có file mới
sudo systemctl daemon-reload
# 3. Enable (tự start khi boot) + start ngay
sudo systemctl enable --now myapp.service
# 4. Kiểm tra
sudo systemctl status myapp.service3. systemctl — Điều Khiển Service
🎯 Mục tiêu
- Thành thạo các lệnh
systemctlthiết yếu - Hiểu workflow: edit → daemon-reload → restart
- Đọc được output của
systemctl status
Các lệnh thiết yếu
bash
# ═══════════════════════════════════════════════
# RELOAD — Sau khi sửa .service file
# ═══════════════════════════════════════════════
sudo systemctl daemon-reload # BẮT BUỘC sau mỗi lần edit .service
# ═══════════════════════════════════════════════
# LIFECYCLE — Start / Stop / Restart
# ═══════════════════════════════════════════════
sudo systemctl start myapp # Khởi động service
sudo systemctl stop myapp # Dừng service (graceful)
sudo systemctl restart myapp # Stop + Start (có downtime)
sudo systemctl reload myapp # Reload config, KHÔNG restart (zero-downtime)
# ═══════════════════════════════════════════════
# BOOT — Enable / Disable tự động start
# ═══════════════════════════════════════════════
sudo systemctl enable myapp # Tự start khi boot
sudo systemctl disable myapp # Không tự start khi boot
sudo systemctl enable --now myapp # Enable + Start ngay (tiện!)
# ═══════════════════════════════════════════════
# STATUS — Lệnh QUAN TRỌNG NHẤT
# ═══════════════════════════════════════════════
sudo systemctl status myappĐọc output của systemctl status
bash
$ sudo systemctl status myapp
● myapp.service - MyApp FastAPI Backend
Loaded: loaded (/etc/systemd/system/myapp.service; enabled; preset: disabled)
Active: active (running) since Mon 2024-01-15 08:30:00 UTC; 2h ago
Main PID: 12345 (uvicorn)
Tasks: 4 (limit: 4096)
Memory: 128.5M (max: 512.0M)
CPU: 1min 23.456s
CGroup: /system.slice/myapp.service
├─12345 /opt/myapp/.venv/bin/python /opt/myapp/.venv/bin/uvicorn main:app
└─12350 /opt/myapp/.venv/bin/python /opt/myapp/.venv/bin/uvicorn main:app
Jan 15 08:30:00 prod-server systemd[1]: Started MyApp FastAPI Backend.
Jan 15 08:30:01 prod-server uvicorn[12345]: INFO: Uvicorn running on http://0.0.0.0:8000Cách đọc:
| Dòng | Ý nghĩa |
|---|---|
Active: active (running) | ✅ Service đang chạy bình thường |
enabled | ✅ Sẽ tự start khi boot |
Main PID: 12345 | Process ID chính — dùng để kill nếu cần |
Memory: 128.5M (max: 512.0M) | RAM đang dùng / giới hạn |
Tasks: 4 | Số threads/processes |
| Các dòng log cuối | Những dòng log gần nhất từ journalctl |
Lệnh nâng cao cho troubleshooting
bash
# Liệt kê TẤT CẢ service đang chạy
systemctl list-units --type=service --state=running
# Liệt kê service bị fail
systemctl list-units --type=service --state=failed
# Xem dependency tree của service
systemctl list-dependencies myapp.service
# Xem property cụ thể
systemctl show myapp.service -p MainPID,MemoryCurrent,RestartCount
# Mask service (ngăn start bằng mọi cách — kể cả dependency)
sudo systemctl mask dangerous-service
sudo systemctl unmask dangerous-service⚠️ daemon-reload — Đừng bao giờ quên!
Sau khi edit file .service, BẮT BUỘC phải chạy sudo systemctl daemon-reload trước khi restart. Nếu không, systemd vẫn dùng config cũ trong memory.
Đây là nguồn gốc của vô số phiên debug: "Tôi sửa config rồi mà sao không có tác dụng?" → Quên daemon-reload.
4. journalctl — Đọc Log Như Detective
🎯 Mục tiêu
- Dùng
journalctlđể đọc log service hiệu quả - Filter theo thời gian, severity, service
- Biết khi nào dùng
-f, khi nào dùng--since
journalctl là công cụ đọc log tập trung của systemd. Mọi output (stdout, stderr) từ service đều được capture vào journal — không cần config log file riêng.
Các lệnh journalctl thiết yếu
bash
# ═══════════════════════════════════════════════
# XEM LOG CỦA MỘT SERVICE
# ═══════════════════════════════════════════════
journalctl -u myapp.service # Tất cả log của myapp
# ═══════════════════════════════════════════════
# FOLLOW REAL-TIME (như tail -f)
# ═══════════════════════════════════════════════
journalctl -u myapp.service -f # Xem log real-time
# Ctrl+C để thoát
# ═══════════════════════════════════════════════
# FILTER THEO THỜI GIAN
# ═══════════════════════════════════════════════
journalctl -u myapp.service -b # Log từ lần boot gần nhất
journalctl -u myapp.service --since "1 hour ago"
journalctl -u myapp.service --since "2024-01-15 08:00" --until "2024-01-15 09:00"
journalctl -u myapp.service --since today
# ═══════════════════════════════════════════════
# FILTER THEO SEVERITY
# ═══════════════════════════════════════════════
journalctl -u myapp.service -p err # Chỉ errors
journalctl -u myapp.service -p warning # Warnings trở lên
# Các level: emerg, alert, crit, err, warning, notice, info, debug
# ═══════════════════════════════════════════════
# SỐ DÒNG & FORMAT
# ═══════════════════════════════════════════════
journalctl -u myapp.service -n 50 # 50 dòng gần nhất
journalctl -u myapp.service -o json | head -5 # JSON (cho parsing)
journalctl -u myapp.service --no-pager # Không dùng pager (cho scripts)Ví dụ thực tế — Debug service crash
Bạn nhận alert lúc 2 giờ sáng: "myapp down". Đây là workflow debug:
bash
# Bước 1: Service đang ở trạng thái gì?
sudo systemctl status myapp
# → Active: failed (Result: exit-code)
# Bước 2: Xem log gần nhất
journalctl -u myapp -n 30
# → Thấy traceback Python
# Bước 3: Xem log quanh thời điểm crash
journalctl -u myapp --since "2 hours ago" -p err
# → Thấy lỗi bắt đầu từ 01:45 AM
# Bước 4: Xem log chi tiết hơn
journalctl -u myapp --since "01:40" --until "01:50"
# → Phát hiện OOM kill vì memory leak💡 journalctl vs log files truyền thống
Trước systemd: Mỗi app ghi log vào file riêng (/var/log/myapp.log). Phải tự setup log rotation, permission, format.
Với journalctl: Log tập trung, tự rotate, có index để tìm nhanh, filter được theo time/severity/unit. Không cần cấu hình thêm gì.
Tuy nhiên, nhiều app production vẫn ghi log vào file (để ship sang ELK Stack, Datadog…). Trong trường hợp đó, journalctl vẫn capture stdout/stderr, còn app tự ghi file riêng.
5. 🔥 Production Incident — "Service Restart Vô Hạn"
🔥 Incident thực tế: Restart Loop
Scenario: Sau deployment, team nhận alert Slack: CPU spike trên prod-server-01. Khi SSH vào kiểm tra:
bash
$ systemctl status myapp
● myapp.service - MyApp FastAPI Backend
Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
Active: activating (auto-restart) (Result: exit-code)
Process: 15234 ExecStart=/opt/myapp/.venv/bin/uvicorn main:app ... (code=exited, status=1/FAILURE)
Main PID: 15234 (code=exited, status=1/FAILURE)
Status: "Restarts: 42"Service đang trong trạng thái activating (auto-restart) — khởi động, crash, restart, crash, restart… 42 lần và đang tiếp tục.
Investigation
bash
# Bước 1: Xem tại sao service crash
$ journalctl -u myapp -n 30 --no-pager
...
Jan 15 14:32:01 prod-server uvicorn[15234]: Traceback (most recent call last):
Jan 15 14:32:01 prod-server uvicorn[15234]: File "/opt/myapp/main.py", line 3
Jan 15 14:32:01 prod-server uvicorn[15234]: ModuleNotFoundError: No module named 'pydantic'
Jan 15 14:32:01 prod-server uvicorn[15234]: ERROR: Application startup failed.
Jan 15 14:32:06 prod-server systemd[1]: myapp.service: Main process exited, code=exited, status=1/FAILURE
Jan 15 14:32:06 prod-server systemd[1]: myapp.service: Scheduled restart job, restart counter is at 42.Root Cause
Deploy script đã update code (git pull) nhưng quên chạy pip install -r requirements.txt trong virtualenv. Version mới của code import pydantic v2, nhưng virtualenv vẫn có pydantic v1 (hoặc không có).
→ App crash ngay khi import → exit code 1 → systemd thấy Restart=on-failure → restart → crash lại → restart loop vô hạn.
Tại sao nó loop?
Nếu .service file không có StartLimitBurst và StartLimitIntervalSec:
crash → restart (5s) → crash → restart (5s) → crash → restart (5s) → ...Vòng lặp này không bao giờ dừng. Mỗi lần restart, app crash trong < 1 giây, rồi systemd đợi RestartSec=5 rồi thử lại. CPU spike vì liên tục spawn process mới.
Fix
bash
# Bước 1: DỪNG service ngay (ngắt restart loop)
sudo systemctl stop myapp
# Bước 2: Fix root cause — cài dependencies
cd /opt/myapp
source .venv/bin/activate
pip install -r requirements.txt
# Bước 3: Test thủ công trước khi start service
/opt/myapp/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
# → Chạy OK? Ctrl+C
# Bước 4: Start service lại
sudo systemctl start myapp
sudo systemctl status myapp
# → Active: active (running) ✅Phòng ngừa
Thêm StartLimitBurst và StartLimitIntervalSec vào .service file:
ini
[Service]
Restart=on-failure
RestartSec=5
StartLimitBurst=3 # Tối đa 3 lần restart...
StartLimitIntervalSec=60 # ...trong 60 giâyNghĩa là: nếu service crash 3 lần trong 60 giây → systemd ngừng restart và đánh dấu service là failed. Bạn sẽ thấy rõ trong monitoring thay vì để nó loop ngầm.
✅ Bài học rút ra
- Luôn kiểm tra
journalctlTRƯỚC khi làm bất cứ điều gì khi service lỗi - Deploy script phải bao gồm cài dependencies, không chỉ pull code
StartLimitBurst+StartLimitIntervalSeclà bắt buộc cho production services- Test thủ công trước khi start service lại — đừng "start rồi cầu nguyện"
6. systemd Timers — Thay Thế Cron
🎯 Mục tiêu
- Hiểu systemd timers là gì và tại sao tốt hơn cron
- Viết được một timer unit cơ bản
Timer Unit — Ví dụ backup database hàng ngày
Mỗi timer cần hai file: một .timer (lịch chạy) và một .service (việc cần làm).
File timer:
ini
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily database backup
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.targetFile service tương ứng:
ini
# /etc/systemd/system/backup.service
[Unit]
Description=Run database backup script
[Service]
Type=oneshot
User=backup
ExecStart=/opt/scripts/backup-db.shbash
# Kích hoạt timer
sudo systemctl enable --now backup.timer
# Kiểm tra tất cả timers
systemctl list-timers --all
# Xem khi nào timer chạy lần tiếp theo
systemctl list-timers backup.timerTại sao timers tốt hơn cron?
| Tính năng | cron | systemd timer |
|---|---|---|
| Logging | Phải tự redirect (>> /var/log/...) | Tự động vào journalctl |
| Dependencies | Không hỗ trợ | After=, Wants= đầy đủ |
| Resource limits | Không có | MemoryMax, CPUQuota… |
| Missed runs | Bỏ qua nếu máy tắt | Persistent=true chạy bù |
| Monitoring | Phải đọc log file | systemctl list-timers thấy ngay |
| Error handling | Email (nếu config) | OnFailure= trigger unit khác |
💡 Khi nào vẫn dùng cron?
Cron vẫn OK cho:
- Script đơn giản, chạy nhanh, không cần monitoring
- Môi trường không có systemd (container minimal, một số embedded systems)
- User-level cron jobs (
crontab -e) khi không có quyền root
Nhưng cho production scheduled tasks → luôn dùng systemd timers.
7. Exercises — Luyện Tập
🎯 Mục tiêu
- Spot the Bug: tìm lỗi trong
.servicefile - Diagnose: đọc
systemctl statusoutput và xác định nguyên nhân
🐛 Spot the Bug — Tìm lỗi trong .service file
Đọc file .service sau và tìm 3 lỗi sẽ khiến service không hoạt động đúng trên production:
ini
# /etc/systemd/system/webapp.service
[Unit]
Description=Web Application
After=network.target
[Service]
ExecStart=uvicorn main:app --host 0.0.0.0 --port 8000
Restart=always
User=root
[Install]
WantedBy=multi-user.target🔍 Xem đáp án
Lỗi 1: ExecStart không dùng absolute path
ini
# ❌ Sai
ExecStart=uvicorn main:app --host 0.0.0.0 --port 8000
# ✅ Đúng — phải dùng absolute path
ExecStart=/opt/webapp/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000systemd yêu cầu absolute path cho ExecStart. Nó không dùng $PATH như shell bình thường.
Lỗi 2: Thiếu WorkingDirectory
ini
# ✅ Thêm dòng này
WorkingDirectory=/opt/webappKhông có WorkingDirectory, uvicorn sẽ tìm main:app ở / (root directory) → ModuleNotFoundError.
Lỗi 3: User=root — KHÔNG BAO GIỜ chạy app với root
ini
# ❌ Sai — security risk nghiêm trọng
User=root
# ✅ Đúng — dùng user riêng với quyền tối thiểu
User=deploy
Group=deployChạy app với root nghĩa là nếu app bị hack, attacker có full control hệ thống. Luôn tạo user riêng với quyền tối thiểu (principle of least privilege).
Bonus: Restart=always nên là Restart=on-failure
Restart=always sẽ restart kể cả khi bạn cố ý systemctl stop. Thường on-failure là đủ — chỉ restart khi app crash (exit code ≠ 0).
🔍 Diagnose — Đọc status output
Bạn nhận được output sau khi kiểm tra service. Chẩn đoán nguyên nhân:
bash
$ systemctl status webapp
● webapp.service - Web Application
Loaded: loaded (/etc/systemd/system/webapp.service; enabled)
Active: failed (Result: exit-code) since Mon 2024-01-15 10:00:05 UTC
Process: 8901 ExecStart=/opt/webapp/.venv/bin/uvicorn main:app (code=exited, status=217/USER)
Main PID: 8901 (code=exited, status=217/USER)
Jan 15 10:00:05 prod-server systemd[1]: webapp.service: Failed with result 'exit-code'.🔍 Xem đáp án
Nguyên nhân: Exit status 217/USER
Status code 217/USER nghĩa là user được chỉ định trong .service file không tồn tại trên hệ thống.
bash
# Kiểm tra
id deploy
# → id: 'deploy': no such user
# Fix: tạo user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin deploy
# Restart service
sudo systemctl restart webappTip: Tra cứu systemd exit codes tại man systemd.exec hoặc search "systemd exit status 217". Mỗi con số có ý nghĩa cụ thể:
200= Chung chung203=EXEC(binary không tìm thấy)217=USER(user không tồn tại)226=NAMESPACE(namespace setup failed)
8. Production Anti-Patterns
⚠️ Cạm bẫy
Problem: Chạy app production bằng nohup và &:
bash
# ❌ "Production deployment" kiểu amateur
ssh prod-server
cd /opt/myapp
nohup python app.py > /var/log/myapp.log 2>&1 &
echo "Deployed! 🎉"
exitTại sao đây là thảm họa chờ xảy ra:
| Vấn đề | nohup & | systemd |
|---|---|---|
| App crash | Chết luôn. Không ai biết | Auto-restart với Restart=on-failure |
| Server reboot | App không tự start | enable → tự start khi boot |
| Log rotation | Log file phình to vô hạn | journalctl tự rotate |
| Resource limits | Không có — app ăn hết RAM → OOM | MemoryMax, CPUQuota |
| Dependencies | Không có — app start trước database | After=, Wants= ordering |
| Security | Thường chạy với user SSH (có thể là root!) | User=deploy, NoNewPrivileges |
| Monitoring | ps aux | grep app 🤮 | systemctl status, alerting integration |
Worst case thực tế: Bạn SSH vào server bằng user root, chạy nohup python app.py &. App có bug cho phép file upload. Attacker upload reverse shell → có quyền root → game over.
Solution: Luôn dùng systemd. Không có ngoại lệ cho production.
⚡ Performance — Phân tích boot time
Dùng systemd-analyze để tìm service nào khởi động chậm nhất:
bash
# Tổng thời gian boot
systemd-analyze
# Service nào chậm nhất? (sorted by time)
systemd-analyze blame
# → 12.345s postgresql.service
# → 8.234s docker.service
# → 3.456s nginx.service
# Critical chain — tìm bottleneck trong boot sequence
systemd-analyze critical-chain
# Hiện dependency chain, highlight service nào block boot
# Vẽ SVG diagram (mở trên browser)
systemd-analyze plot > boot-analysis.svgNếu server boot chậm, đây là công cụ đầu tiên bạn nên dùng. Thường thì culprit là database services hoặc disk check (fsck).
Tổng Kết
| Khái niệm | Lệnh / File | Khi nào dùng |
|---|---|---|
| Tạo service | /etc/systemd/system/myapp.service | Deploy app mới |
| Reload config | systemctl daemon-reload | Sau khi edit .service |
| Start + Enable | systemctl enable --now myapp | Setup lần đầu |
| Xem trạng thái | systemctl status myapp | Luôn luôn — lệnh đầu tiên khi debug |
| Đọc log | journalctl -u myapp -f | Debug realtime |
| Log theo thời gian | journalctl -u myapp --since "1 hour ago" | Post-mortem |
| Scheduled tasks | .timer + .service | Thay cron cho production |
| Boot analysis | systemd-analyze blame | Server boot chậm |
Nguyên tắc vàng: Trên production, mọi thứ chạy nền đều phải là systemd service. Không nohup, không screen, không tmux cho production workloads.