1. Vì sao Linux thống trị server?
Hơn 96% top 1 triệu webserver chạy Linux (Netcraft 2024). Mọi cloud (AWS, GCP, Azure) backbone là Linux. Lý do:
- Free + open source — không license cost, sửa được kernel.
- Stability + security — kernel hardened qua 30 năm, ít attack surface mặc định.
- Tooling phong phú — package manager, scripting, container nguyên thủy.
- Customization — strip xuống Alpine 5MB cho container, hoặc full RHEL cho enterprise.
- POSIX standard — script chạy giữa distros (gần như).
DevOps engineer cần fluent Linux — không phải chuyên gia kernel, nhưng đủ để vận hành 24/7. Mục tiêu của chương này: bạn có thể SSH vào 1 server lạ và trong 5 phút biết nó đang chạy gì, có vấn đề gì.
1.1. Linux trong DevOps stack
- Container base image: Alpine, Debian-slim, Ubuntu, RHEL UBI.
- Cloud VM: Amazon Linux 2023, Ubuntu LTS, RHEL.
- Kubernetes node: Container OS / GKE Container-Optimized OS / Bottlerocket.
- CI runner: Ubuntu, Alpine.
2. Distros — Ubuntu, RHEL, Alpine: chọn cái nào?
| Distro | Family | Package mgr | Use case DevOps |
|---|---|---|---|
| Ubuntu / Debian | Debian | apt / dpkg | Default cloud VM, dev workstation, CI runner |
| RHEL / CentOS Stream / Rocky / AlmaLinux | Red Hat | dnf / rpm | Enterprise, banking, government |
| Amazon Linux 2023 | Red Hat (fork) | dnf | EC2 default — tích hợp AWS tốt |
| Alpine | Tự build, musl libc | apk | Container image (5MB base!), lightweight |
| Container-Optimized OS / Bottlerocket | Tối giản | (immutable, no pkg) | K8s node — read-only root, auto-update |
2.1. Khác biệt apt vs dnf vs apk
# Debian/Ubuntu (apt)
sudo apt update # refresh package list
sudo apt install -y nginx # install (-y: auto yes)
sudo apt remove nginx # uninstall (giữ config)
sudo apt purge nginx # uninstall + xóa config
apt list --installed | grep nginx
apt search nginx # tìm package
# RHEL/Amazon Linux (dnf — modern, thay yum)
sudo dnf install -y nginx
sudo dnf remove nginx
sudo dnf update # update tất cả
dnf list installed | grep nginx
dnf info nginx # chi tiết package
# Alpine (apk)
apk add --no-cache nginx # install không cache
apk del nginx # uninstall
apk info -L nginx # liệt kê file của package
--no-cache với apk hoặc rm -rf /var/lib/apt/lists/* sau apt install để giảm size container 50-100MB.
2.2. Alpine — vì sao container thích?
Alpine 3.19: base image chỉ 5MB. So với Ubuntu 22.04 ~78MB, Debian-slim ~28MB. Trong khi đó, vẫn có shell, package manager, đủ runtime cho hầu hết app.
Lưu ý: Alpine dùng musl libc thay vì glibc — đa số app Go/Rust chạy được, nhưng vài binary build cho glibc (Node.js native module, Python với numpy) có thể lỗi. Khi đó dùng python:3.12-slim (Debian-slim) thay vì python:3.12-alpine.
3. systemd — Hệ thống init hiện đại
systemd là PID 1 trên hầu hết Linux distro hiện đại (Ubuntu 16+, RHEL 7+). Nó quản lý:
- Service (daemon) — start/stop/restart, dependencies.
- Timer — thay thế cron với features mạnh hơn.
- Socket — activate service khi có connection.
- Logging — qua journald.
- Network, mount, login session...
3.1. Lệnh systemctl cốt lõi
# Service status
sudo systemctl status nginx # xem chi tiết
sudo systemctl is-active nginx # active/inactive (cho script)
sudo systemctl is-enabled nginx # enabled/disabled
# Start/stop/restart/reload
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx # full restart
sudo systemctl reload nginx # reload config (no downtime)
# Enable/disable (boot start)
sudo systemctl enable nginx # tự start khi boot
sudo systemctl disable nginx
sudo systemctl enable --now nginx # enable + start ngay
# Liệt kê
systemctl list-units --type=service # service đang chạy
systemctl list-units --type=service --state=failed # services failed
systemctl list-unit-files --type=service # tất cả service file
# Reset failure
sudo systemctl reset-failed nginx
3.2. Viết unit file cho app của bạn
# /etc/systemd/system/myapp.service
[Unit]
Description=My Node.js App
Documentation=https://github.com/me/myapp
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
# Environment
Environment="NODE_ENV=production"
Environment="PORT=3000"
EnvironmentFile=/etc/myapp/env # file format KEY=VALUE
# Resource limits
LimitNOFILE=65536 # max open files
MemoryMax=512M # OOM-kill nếu vượt
CPUQuota=50% # 50% 1 core
TasksMax=4096
# Security hardening
PrivateTmp=true # /tmp riêng
NoNewPrivileges=true
ProtectSystem=strict # /usr, /boot read-only
ProtectHome=true
ReadWritePaths=/var/log/myapp /opt/myapp/data
[Install]
WantedBy=multi-user.target # khi nào chạy: multi-user (level 3)
# Sau khi tạo file:
sudo systemctl daemon-reload # reload systemd config
sudo systemctl enable --now myapp # enable + start
# Xem log
sudo journalctl -u myapp -f # follow live (giống tail -f)
sudo journalctl -u myapp --since "10 min ago"
sudo journalctl -u myapp -p err # chỉ ERROR
3.3. systemd timer — thay thế cron
# /etc/systemd/system/backup.service
[Unit]
Description=Daily database backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backupuser
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 2am
Requires=backup.service
[Timer]
OnCalendar=*-*-* 02:00:00 # mỗi ngày 2:00 AM
RandomizedDelaySec=600 # delay random 0-10 phút (avoid stampede)
Persistent=true # nếu miss (server off), chạy bù khi boot
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
# Xem timer schedule
systemctl list-timers --all
Ưu điểm timer so với cron: log tự vào journal, dependency rõ, persistent (chạy bù), random delay tránh stampede.
4. Users, Groups, Permissions — Bảo mật cơ bản
4.1. Quản lý user
# Tạo user
sudo useradd -m -s /bin/bash -G sudo,docker alice
# -m: tạo home dir
# -s: shell mặc định
# -G: groups (cộng vào)
# Set password
sudo passwd alice
# Sửa user
sudo usermod -aG docker alice # add to docker group (-a append!)
sudo usermod -L alice # lock (không login được)
sudo usermod -U alice # unlock
# Xóa user
sudo userdel -r alice # -r: xóa luôn home
# Xem user
id alice # uid, gid, groups
getent passwd alice # info từ /etc/passwd
-a (append) khi add group. usermod -G docker alice sẽ thay thế tất cả group hiện có chỉ còn docker — alice mất sudo!
4.2. File permission — rwx
$ ls -la
-rw-r--r-- 1 alice users 1024 Jan 15 10:30 file.txt
drwxr-xr-x 2 alice users 4096 Jan 15 10:30 mydir/
# ↑↑↑↑↑↑↑↑↑↑
# d rwx rwx rwx
# │ │ │ │
# │ │ │ └─ others (everyone else)
# │ │ └─ group
# │ └─ owner (user)
# └─ type: d=dir, -=file, l=symlink
# Chmod — change permission
chmod 755 script.sh # rwxr-xr-x — owner full, group/others read+execute
chmod 644 file.txt # rw-r--r-- — owner write, others read
chmod 600 .ssh/id_rsa # rw------- — chỉ owner đọc/ghi
chmod +x script.sh # add execute cho all
chmod u+x,go-w script.sh # u=user, g=group, o=others
# Octal cheat sheet
# 7 = rwx (4+2+1)
# 6 = rw- (4+2)
# 5 = r-x (4+1)
# 4 = r-- (4)
# Chown — change owner
sudo chown alice:devops file.txt # owner=alice, group=devops
sudo chown -R alice /opt/myapp # recursive
4.3. Special permissions: setuid, setgid, sticky bit
ls -l /usr/bin/passwd
# -rwsr-xr-x ← s thay x ở user = setuid
ls -ld /tmp
# drwxrwxrwt ← t cuối = sticky bit
# setuid (4xxx): chạy file như owner (passwd cần write /etc/shadow → setuid root)
# setgid (2xxx): chạy như group
# sticky (1xxx): chỉ owner được xóa file trong dir (dùng cho /tmp)
chmod 4755 binary # setuid + 755
chmod 2755 dir # setgid + 755
chmod 1777 shareddir # sticky + 777
4.4. sudo — leo quyền có kiểm soát
# Sudo cấp quyền root tạm thời
sudo command # chạy như root
sudo -u alice command # chạy như alice
sudo -i # spawn shell root (login)
sudo -s # shell root (kế thừa env)
# Edit sudoers — KHÔNG sửa /etc/sudoers trực tiếp, dùng:
sudo visudo # validate syntax trước save
# Hoặc thêm file vào /etc/sudoers.d/
sudo nano /etc/sudoers.d/devops
# Nội dung:
# alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp
# → alice có thể restart myapp mà không cần password
4.5. ACL — quyền chi tiết hơn rwx
ls -l /var/www
# -rw-r--r--+ ← dấu + = có ACL
# Set ACL
setfacl -m u:bob:rwx /var/www/file # bob có rwx (ngoài user/group/other)
setfacl -m g:devops:rx /var/www # group devops có r-x
setfacl -d -m u:alice:rwx /var/www # default ACL — file mới sẽ kế thừa
# Xem ACL
getfacl /var/www/file
5. Process & Resource Management
5.1. ps, top, htop
# ps — snapshot of processes
ps aux # mọi process, kèm CPU/RAM
ps -ef | grep nginx # filter
ps aux --sort=-%cpu | head # top 10 CPU
ps aux --sort=-%mem | head # top 10 memory
# top — real-time
top # default
top -u alice # chỉ user alice
# Trong top:
# M = sort by memory
# P = sort by CPU
# k = kill PID
# 1 = show per-CPU stats
# q = quit
# htop — colored, interactive (cài: apt install htop)
htop
# F2 = setup, F4 = filter, F5 = tree view, F9 = kill
# Btop — modern alternative (apt install btop)
btop
5.2. Tìm và kill process
# Tìm PID
pidof nginx # PID(s) của process tên nginx
pgrep -f "node server.js" # tìm theo command line full
# Kill
kill 1234 # SIGTERM (graceful, default)
kill -9 1234 # SIGKILL (cứng, last resort)
kill -HUP 1234 # SIGHUP (reload config — nginx, systemd)
killall nginx # kill tất cả tên nginx
pkill -f "node server.js" # kill theo cmdline
# Signal phổ biến
# SIGTERM (15) — graceful, app cleanup
# SIGINT (2) — Ctrl+C
# SIGHUP (1) — reload config
# SIGKILL (9) — không kill được, kernel force kill
# SIGSTOP/SIGCONT — pause/resume
5.3. Resource: CPU, Memory, Disk
# Memory
free -h # human-readable
# total used free shared buff/cache available
# Mem: 7.7G 2.1G 3.2G 44M 2.4G 5.3G
# Swap: 2.0G 0B 2.0G
# Available = free + cached/buffered. Đừng panic khi "free" thấp.
# Disk
df -h # disk filesystem usage
df -i # inode usage (file count)
du -sh /var/log/* # size mỗi folder
du -sh /var/log/* | sort -h # sort
# I/O
iostat -x 2 5 # disk I/O, 2s interval, 5 lần
iotop # process consuming I/O (need root)
# Load average
uptime
# 14:30:01 up 5 days, 3:21, 2 users, load average: 0.52, 0.48, 0.45
# Load = số process trong runqueue. Nếu > số CPU core → bottleneck.
5.4. lsof — list open files
lsof -i :8080 # process nào đang bind port 8080
lsof -p 1234 # files mở bởi PID 1234
lsof -u alice # files của user alice
lsof /var/log/syslog # process nào đang đọc/ghi file này
lsof -i tcp # all TCP sockets
# Tìm process holding deleted file
lsof | grep deleted
# Hữu ích khi df cao nhưng du thấp — file deleted vẫn được giữ bởi process
6. Network Tools — Bộ lệnh phải thuộc
Trên server modern (Ubuntu 18+, RHEL 7+), thay ifconfig + netstat bằng ip + ss. Cũ vẫn chạy nhưng deprecated.
6.1. IP & Interface
# Liệt kê interface
ip addr # = ip a (short)
ip link # link state
ip route # routing table
# Check connectivity
ping -c 4 google.com # 4 lần
ping -c 4 -W 2 google.com # timeout 2s
# Trace route
traceroute google.com # truyền thống
mtr google.com # tốt hơn — kết hợp ping+traceroute liên tục
# DNS
dig google.com # full DNS lookup
dig +short google.com # gọn
dig @8.8.8.8 google.com # query specific server
host google.com # đơn giản hơn dig
nslookup google.com # legacy
# DNS reverse
dig -x 8.8.8.8 # PTR record
6.2. ss — socket statistics
ss -tuln # TCP/UDP listening, không resolve hostname
# -t: TCP
# -u: UDP
# -l: listening
# -n: numeric (no DNS lookup, faster)
ss -tn state established # TCP connections active
ss -tn dst :443 # connections to port 443
ss -p # show process owning socket (need root)
ss -s # summary stats
# Common questions:
# "Service nào đang nghe port 8080?"
sudo ss -tlnp | grep :8080
# "Có connection nào bị stuck SYN_SENT?"
ss -tn state syn-sent
6.3. curl — HTTP debug
# GET đơn giản
curl https://api.example.com/users/1
# Verbose — thấy headers
curl -v https://example.com
# Timing breakdown
curl -w "@-" -o /dev/null -s https://example.com <<'EOF'
DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS: %{time_appconnect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
EOF
# POST JSON
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"alice","age":30}'
# Upload file
curl -F "file=@photo.jpg" https://upload.example.com
# Follow redirect, save with original name
curl -LO https://example.com/file.zip
# Health check ngắn
curl -fsS http://localhost/health || echo "DOWN"
# -f: fail on HTTP error (4xx/5xx)
# -s: silent
# -S: show error
6.4. Firewall: ufw, firewalld, iptables
# UFW (Ubuntu) — đơn giản
sudo ufw enable
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow from 10.0.0.0/8 to any port 5432 # postgres từ private network
sudo ufw status numbered
sudo ufw delete 3 # xóa rule số 3
# firewalld (RHEL/Amazon Linux)
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
# iptables (low-level — dưới ufw/firewalld)
sudo iptables -L -n -v # list rules
7. Log Management — journalctl & /var/log
7.1. journalctl (systemd journal)
# Xem toàn bộ journal
sudo journalctl # paged from oldest
sudo journalctl -e # jump to end
sudo journalctl -f # follow (giống tail -f)
# Filter
sudo journalctl -u nginx # service nginx
sudo journalctl -u nginx --since "1 hour ago"
sudo journalctl --since "2024-01-15 10:00" --until "2024-01-15 12:00"
sudo journalctl -p err # priority error trở lên
# priority: emerg, alert, crit, err, warning, notice, info, debug
# Search
sudo journalctl -u nginx | grep "500" # tốt hơn:
sudo journalctl -u nginx -g "500" # built-in grep, faster
# Boot
sudo journalctl -b # logs from current boot
sudo journalctl -b -1 # previous boot
sudo journalctl --list-boots # list all boots
# Disk usage
journalctl --disk-usage
sudo journalctl --vacuum-size=500M # giữ tối đa 500M
sudo journalctl --vacuum-time=7d # giữ 7 ngày
# JSON output (cho log shipping)
sudo journalctl -u nginx -o json
7.2. Log truyền thống ở /var/log
| File | Nội dung |
|---|---|
| /var/log/syslog (Ubuntu) / /var/log/messages (RHEL) | System log chung |
| /var/log/auth.log / /var/log/secure | Login, sudo, ssh |
| /var/log/kern.log / dmesg | Kernel messages |
| /var/log/nginx/access.log | HTTP request log |
| /var/log/nginx/error.log | nginx error |
| /var/log/dpkg.log / /var/log/yum.log | Package install/remove |
7.3. tail / less / awk — đọc log
# Theo dõi live
tail -f /var/log/nginx/access.log
tail -F file.log # -F: re-open nếu file rotate
# Đọc 100 dòng cuối
tail -n 100 file.log
tail -100 file.log # ngắn hơn
# 100 dòng đầu
head -n 100 file.log
# Phân trang
less file.log
# Trong less: /pattern (search), n (next), G (cuối), 1G (đầu), q (quit)
# Filter với grep
tail -f access.log | grep "500" # 500 errors live
grep -i "error" /var/log/syslog # case-insensitive
grep -v "^#" config.conf # bỏ comment
grep -E "ERROR|FATAL" app.log # regex extended (=egrep)
grep -A 5 -B 2 "exception" app.log # 5 dòng sau, 2 dòng trước
# Đếm
grep -c "ERROR" app.log
# awk — extract column
awk '{print $1}' access.log # cột 1 (IP nếu Apache/nginx default format)
awk '$9 == 500' access.log # rows có status 500
awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' access.log | sort -rn | head
# → top 10 IP theo số request
7.4. Log rotation — logrotate
# Config thường ở /etc/logrotate.d/
cat /etc/logrotate.d/nginx
# /var/log/nginx/*.log {
# daily
# rotate 14 ← giữ 14 file (= 14 ngày)
# compress ← gzip cũ
# delaycompress
# missingok
# notifempty
# create 0640 nginx adm
# sharedscripts
# postrotate
# [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
# endscript
# }
# Test config
sudo logrotate -d /etc/logrotate.d/nginx # debug, không thực thi
sudo logrotate -f /etc/logrotate.d/nginx # force run ngay
Trong môi trường cloud/K8s, log thường ship đến hệ thống tập trung (ELK, Loki, CloudWatch) thay vì giữ local — nhưng vẫn cần biết cơ bản.
8. Cron — Lập lịch task
8.1. Crontab format
* * * * * command
│ │ │ │ │
│ │ │ │ └─ day of week (0-7, 0 và 7 = Sunday)
│ │ │ └─── month (1-12)
│ │ └───── day of month (1-31)
│ └─────── hour (0-23)
└───────── minute (0-59)
# Ví dụ:
0 2 * * * → 02:00 mỗi ngày
*/5 * * * * → mỗi 5 phút
0 */4 * * * → mỗi 4 giờ
0 0 * * 0 → 00:00 Chủ nhật hàng tuần
0 9-17 * * 1-5 → 9-17h thứ Hai-Sáu hàng giờ
@daily → 0 0 * * *
@hourly → 0 * * * *
@reboot → 1 lần khi boot
8.2. Quản lý crontab
# Edit crontab cho user hiện tại
crontab -e # mở editor
crontab -l # list
crontab -r # remove (cẩn thận!)
# Crontab cho user khác (cần root)
sudo crontab -u alice -e
# System cron — /etc/crontab (có thêm field user)
# m h dom mon dow user command
0 2 * * * root /usr/local/bin/backup.sh
# Mỗi file ở /etc/cron.d/ là 1 crontab
# /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.hourly/ — drop script vào
8.3. Best practice cho cron job
# 1. Output đi đâu?
0 2 * * * /opt/scripts/backup.sh > /var/log/backup.log 2>&1
# Mặc định cron mail output cho user. Redirect để tránh.
# 2. Tránh overlap (nếu job chạy lâu)
0 * * * * /usr/bin/flock -n /tmp/myjob.lock /opt/scripts/myjob.sh
# flock: chỉ chạy nếu lock available (không chạy) — tránh 2 instance cùng
# 3. Set PATH (cron chạy với PATH tối thiểu)
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 2 * * * /opt/scripts/backup.sh
# 4. Check exit code
0 2 * * * /opt/scripts/backup.sh || curl -X POST $SLACK_WEBHOOK -d "Backup failed!"
# 5. Use absolute path
# Đừng: cd ~/myapp && python script.py
# Nên: /usr/bin/python /home/alice/myapp/script.py
9. SSH cho DevOps — Phải master
9.1. Tạo và quản lý SSH key
# Tạo key (Ed25519 — modern, mạnh hơn RSA)
ssh-keygen -t ed25519 -C "alice@example.com"
# Mặc định lưu ở ~/.ssh/id_ed25519 (private) và id_ed25519.pub (public)
# Hoặc RSA cũ (compat)
ssh-keygen -t rsa -b 4096 -C "alice@example.com"
# Permissions BẮT BUỘC
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys
# SSH sẽ refuse key nếu permission quá lỏng
# Copy public key lên server
ssh-copy-id alice@server.example.com
# Equivalent: cat ~/.ssh/id_ed25519.pub | ssh alice@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
9.2. SSH config — file vàng cho DevOps
# ~/.ssh/config
Host bastion
HostName bastion.example.com
User alice
Port 22
IdentityFile ~/.ssh/id_ed25519
Host prod-web
HostName 10.0.1.5 # private IP trong VPC
User deploy
IdentityFile ~/.ssh/deploy_key
ProxyJump bastion # tunnel qua bastion!
Host *.staging.example.com
User developer
IdentityFile ~/.ssh/staging_key
StrictHostKeyChecking accept-new # auto-accept new hosts
Host github.com
HostName ssh.github.com
Port 443 # bypass firewall blocks 22
User git
# Sau đó:
ssh prod-web # tự động qua bastion!
scp file.tar.gz prod-web:/tmp/ # cũng qua bastion
9.3. ProxyJump (bastion) — pattern phổ biến
# Bastion (jump host) — server public, qua đó SSH vào private servers
#
# You ──SSH──→ Bastion ──SSH──→ Private Server (10.0.1.x)
# Cách 1: ProxyJump trong config (xem trên)
# Cách 2: command line
ssh -J alice@bastion deploy@10.0.1.5
# Cách 3 (cũ): ProxyCommand
ssh -o ProxyCommand="ssh -W %h:%p alice@bastion" deploy@10.0.1.5
9.4. SSH tunnel / port forwarding
# Local forward — truy cập DB private qua tunnel
ssh -L 5432:db.private:5432 alice@bastion
# Bây giờ localhost:5432 = db.private:5432
psql -h localhost -U appuser mydb # nối DB qua tunnel
# Remote forward — server kết nối ngược về máy bạn
ssh -R 8080:localhost:3000 alice@server
# Server có thể curl localhost:8080 → đến app dev local
# Dynamic SOCKS proxy
ssh -D 1080 alice@bastion
# Cấu hình browser SOCKS5 = localhost:1080 → traffic đi qua bastion
9.5. SSH server hardening
# /etc/ssh/sshd_config
PermitRootLogin no # không cho root SSH
PasswordAuthentication no # bắt buộc key
PubkeyAuthentication yes
PermitEmptyPasswords no
Port 2222 # đổi port (security by obscurity, ít bot scan)
MaxAuthTries 3
ClientAliveInterval 300 # ping client mỗi 5 phút
ClientAliveCountMax 2 # disconnect nếu client không phản hồi 2 lần
AllowUsers alice bob deploy # whitelist user
# Sau khi sửa:
sudo systemctl restart sshd
# TEST trong session khác trước khi đóng session hiện tại!
# Nếu sai config, mất quyền SSH server.
# fail2ban — auto block IP brute-force
sudo apt install fail2ban
# Config /etc/fail2ban/jail.local — set bantime, findtime, maxretry
10. Troubleshooting Checklist — server "có vấn đề"
Đây là playbook chuẩn khi nhận pager: "Server X chậm/crash, kiểm tra ngay!"
- Sống không?
ping server # ICMP ssh server # SSH có lên không curl -v http://server/health # HTTP có response không - Resource overload?
uptime # load average free -h # memory df -h # disk full? top # process gì ăn CPU/RAM - Service status?
systemctl --failed # service nào failed systemctl status myapp # specific service ss -tlnp # ports đang listen - Log gần đây?
journalctl --since "10 min ago" -p err journalctl -u myapp -n 100 tail -100 /var/log/syslog dmesg | tail # kernel — OOM kill, hardware errors - Network OK?
ip a # interfaces UP? ip route # default route? ss -s # connection summary ss -tn state established | wc -l # active connections # Nếu >> bình thường → có thể bị flood - Disk I/O?
iostat -x 2 5 # %util, await iotop # process gây I/O cao # %util > 80% liên tục = disk bottleneck - OOM killer?
dmesg | grep -i "killed process" journalctl -k | grep -i "out of memory" # Process bị OOM-kill thường dấu hiệu memory leak - Recent changes?
last -n 20 # ai login gần đây sudo journalctl --since "1 hour ago" | grep -i "started\|failed" ls -lt /etc/ # config thay đổi gần đây # 90% incident do change gần nhất → suspect change đầu tiên
uptime; free -h; df -h; systemctl --failed — 1 dòng cho 80% câu trả lời "có vấn đề gì lớn không".
10.1. Disk full — không xóa được
# Thường gặp: df hiện 100%, nhưng du không tìm thấy file lớn
# Nguyên nhân: file đã deleted nhưng process vẫn giữ open file descriptor
# Tìm
sudo lsof | grep deleted
# Kết quả ví dụ: nginx 1234 ... /var/log/nginx/error.log.1 (deleted)
# Sửa: restart process giữ file
sudo systemctl restart nginx
# HOẶC truncate file qua /proc:
sudo truncate -s 0 /proc/1234/fd/3
10.2. CPU 100% — process nào?
# 1. Top theo CPU
ps aux --sort=-%cpu | head
# 2. Nếu là Java/Node — thread cụ thể nào?
top -H -p <PID> # threads của process
# Nhớ TID (thread ID) của thread cao CPU → chuyển sang hex
printf '%x\n' <TID>
# 3. Java thread dump
jstack <PID> | grep -A 30 0x<hex_tid>
# 4. Profile (perf)
sudo perf top -p <PID>
11. Bài tập
- Setup VM lab: spin up Ubuntu 22.04 trên VirtualBox/UTM/Multipass. Cài user mới, set sudo, disable password SSH.
- Viết systemd service cho 1 Node.js app: chạy như non-root user, restart on failure, journal log, env vars từ file.
- Cấu hình journalctl: giới hạn 500MB, retention 7 ngày. Verify bằng
journalctl --disk-usage. - Quản lý cron:
- Cron mỗi giờ check disk usage; nếu > 85%, gửi alert qua webhook.
- Cron lúc 2:00 AM backup database, log result.
- Convert 1 cron sang systemd timer.
- SSH config: tạo file
~/.ssh/configvới 3 host: bastion, prod (qua ProxyJump), staging. SSH thành công không gõ user/host dài. - Hardening SSH: tắt PermitRootLogin và PasswordAuthentication. Đổi port 22 → 2222. Test fail2ban block 1 IP brute-force.
- Tunneling: dùng SSH tunnel kết nối local
psqlđến PostgreSQL chạy private trên cloud (qua bastion). - Network forensics: server có 1 process bí ẩn nghe port 9999. Tìm tên process, user owner, executable path, command line đầy đủ.
- Disk full simulation: tạo file lớn lấp /var đến 95%. Tìm file đang chiếm. Xóa được không nếu nginx đang ghi vào? Demo lsof | grep deleted.
- Troubleshooting drill: bạn nhận incident "API chậm 2 phút trước". Trong 5 phút đầu, chạy commands gì để chẩn đoán? Viết playbook.
12. Quiz
Quiz cuối Chương 2
Lệnh systemctl nào RELOAD config mà KHÔNG restart service?
Lệnh ĐÚNG để add user alice vào docker group mà KHÔNG mất các group khác?
-a (append) là then chốt. Thiếu nó, -G docker sẽ thay thế tất cả group hiện có chỉ còn docker — alice mất sudo, mất group quan trọng khác. Đây là một trong những lỗi phổ biến nhất gây "tự khóa mình".Permission octal 600 nghĩa là:
~/.ssh/id_rsa và các file private key. SSH sẽ refuse nếu lỏng hơn.Lệnh nào tìm process đang nghe port 8080?
ss -tlnp: -t TCP, -l listening, -n numeric (no DNS), -p process. Cần sudo để thấy process tên. Cũ hơn dùng netstat -tlnp nhưng deprecated. ps aux | grep 8080 chỉ tìm "8080" trong cmdline — không reliable.Cron expression "*/15 * * * *" nghĩa là:
*/N trong field minute = "mỗi N phút". 15 * * * * (không có /) thì mới là "phút thứ 15 mỗi giờ". 0 15 * * * mới là "15:00 mỗi ngày". 0 0 15 * * = ngày 15 hàng tháng.SIGTERM (15) khác SIGKILL (9) như thế nào?
kill) — process nhận signal, có thể catch để cleanup (đóng connection, flush buffer, save state) rồi exit. SIGKILL — kernel direct kill, process không có cơ hội cleanup, có thể để lại lock file/dangling resource. Quy tắc: TERM trước, KILL chỉ khi TERM không tác dụng.SSH ProxyJump (-J hoặc ProxyJump trong config) dùng để:
ssh prod-web tự động đi qua bastion. Tốt hơn -A (agent forward) vì không expose key cho bastion.Khi df -h hiện disk 100% nhưng du -sh /* không tìm ra file lớn:
ls nhưng inode vẫn live qua file descriptor → disk vẫn bị chiếm. Tìm bằng lsof | grep deleted. Sửa: restart process, hoặc truncate -s 0 /proc/PID/fd/N."Load average 4.5" trên server 4 core nghĩa là:
Câu nào ĐÚNG về systemd timer so với cron?
Hoàn thành Chương 2. Tiếp theo: Chương 3 — Git & Workflows →