HỌC BẰNG THỰC HÀNH
← Lộ trình học
01 · Linux Administration

Vận hành web service bằng systemd

Thực hiện trên server của bạn, rồi kiểm tra từng tiêu chí.

Cốt lõi

Vì sao cần học

Chạy web bằng một terminal thì logout là tiến trình dừng, crash thì không ai khởi động lại, reboot thì mất hẳn. systemd là trình quản lý dịch vụ trên hầu hết bản Linux hiện nay: nó khởi động dịch vụ khi boot, restart khi lỗi, gom log và giới hạn quyền của tiến trình. Hiểu systemd cũng giúp hiểu Kubernetes sau này, vì kubelet và container runtime đều chạy bằng systemd.

Mục tiêu

  • Viết unit file cho một dịch vụ dài hạn, chạy bằng system user riêng.
  • Phân biệt start với enable, reload với daemon-reload.
  • Đọc trạng thái và log bằng systemctl status và journalctl.
  • Chứng minh dịch vụ còn chạy sau crash và sau reboot.

Điều kiện tiên quyết

  • Bài User, group và thư mục dùng chung.

Mental model

Unit là khai báo trạng thái mong muốn. Bạn mô tả chạy gì, bằng user nào, khi nào restart, phụ thuộc vào gì. systemd chịu trách nhiệm đạt trạng thái đó.

start và enable là hai việc khác nhau. start chạy ngay bây giờ. enable tạo symlink vào multi-user.target.wants/ để lần boot sau tự chạy. enable --now làm cả hai. Một dịch vụ được start mà không enable sẽ biến mất sau reboot. Đây là lỗi production rất phổ biến.

systemd đọc unit file vào bộ nhớ. Sửa file xong phải systemctl daemon-reload, rồi restart dịch vụ để áp dụng.

Thực hành (Guided Lab)

1. Thử chạy bằng tay

sudo install -d -m 755 /srv/kdc-web
echo '<h1>KDC-WEB</h1>' | sudo tee /srv/kdc-web/index.html
python3 -m http.server 8080 --bind 127.0.0.1 --directory /srv/kdc-web
Giải thích lệnh và tham số

python3 -m http.server chạy module web server có sẵn của Python: -m nhận tên module, khác -m đặt quyền của install. 8080 là cổng; --bind 127.0.0.1 chỉ nghe loopback nên máy khác không truy cập trực tiếp; --directory /srv/kdc-web chọn thư mục phục vụ. curl <URL> gửi yêu cầu HTTP và in nội dung phản hồi. Ctrl+C dừng server đang chạy bằng tay.

Mở terminal khác, chạy curl http://127.0.0.1:8080/. Quay lại, nhấn Ctrl+C: dịch vụ chết theo terminal. Đây là vấn đề systemd sẽ giải quyết.

2. System user cho dịch vụ

sudo useradd --system --no-create-home --shell /usr/sbin/nologin kdc-web
getent passwd kdc-web
Giải thích lệnh và tham số

useradd --system tạo tài khoản dịch vụ, --no-create-home không tạo home, --shell /usr/sbin/nologin chọn chương trình từ chối đăng nhập tương tác. getent passwd kdc-web in bản ghi tài khoản: trường cuối cần là đường dẫn nologin.

Dịch vụ không cần đăng nhập, không cần home. Nếu bị khai thác, kẻ tấn công chỉ có quyền của kdc-web.

3. Viết unit file

sudo tee /etc/systemd/system/kdc-web.service <<'EOF'
[Unit]
Description=KDC demo web
After=network.target

[Service]
User=kdc-web
Group=kdc-web
WorkingDirectory=/srv/kdc-web
ExecStart=/usr/bin/python3 -m http.server 8080 --bind 127.0.0.1 --directory /srv/kdc-web
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now kdc-web.service
systemctl status kdc-web.service --no-pager
curl -s http://127.0.0.1:8080/
Giải thích lệnh và tham số
Lệnh / tùy chọnÝ nghĩa
systemctl daemon-reloadYêu cầu systemd đọc lại unit và drop-in sau khi sửa file; chưa restart ứng dụng.
systemctl enable --now ...enable cấu hình tự chạy khi boot; --now chạy ngay trong lần gọi này.
systemctl status ... --no-pagerXem trạng thái unit; --no-pager in thẳng ra terminal, không mở trình xem phân trang.
curl -s <URL>-s tắt progress và thông báo lỗi của curl, vẫn in nội dung phản hồi. Khi điều tra lỗi, dùng curl -sS <URL> để vẫn thấy lỗi (-S).

Unit cần báo active (running) và curl cần trả KDC-WEB. Các dòng [Service], User=, ExecStart=, Restart= là cấu hình systemd, không phải lệnh gõ trực tiếp vào shell.

4. Chứng minh tự restart

sudo systemctl kill --signal=SIGKILL kdc-web.service
sleep 3
systemctl status kdc-web.service --no-pager
journalctl -u kdc-web.service --no-pager -n 20
Giải thích lệnh và tham số

systemctl kill --signal=SIGKILL gửi signal KILL tới tiến trình của unit; systemd sẽ áp dụng Restart=on-failure. sleep 3 đợi 3 giây. journalctl -u ... -n 20 xem 20 dòng log gần nhất của đúng unit; --no-pager tránh trình xem phân trang. systemctl stop là yêu cầu dừng chủ động, nên không dùng để mô phỏng crash.

Trong log có dòng tiến trình bị kill và dòng systemd khởi động lại sau 2 giây. Lưu ý systemctl stop không kích hoạt restart, vì đó là dừng có chủ đích.

5. Reboot

sudo systemctl reboot

Đăng nhập lại, bật lại tunnel và agent (bài 00), rồi kiểm tra curl -s http://127.0.0.1:8080/.

Tự kiểm tra (Verification)

systemctl is-active kdc-web.service
systemctl is-enabled kdc-web.service
systemctl show kdc-web.service -p User -p Restart
curl -s http://127.0.0.1:8080/
Giải thích lệnh và tham số

is-active hỏi trạng thái hiện tại, is-enabled hỏi cấu hình tự khởi động. systemctl show ... -p User -p Restart chỉ in hai thuộc tính: -p ở systemctl là property, không phải parents như mkdir -p.

Chọn server và bấm Kiểm tra kết quả. Nếu sửa unit file sau khi reboot, tiêu chí reboot sẽ yêu cầu reboot lại để chứng minh cấu hình mới vẫn đúng.

Lỗi thường gặp

  • Sửa unit mà quên daemon-reload: systemctl status cảnh báo "unit file changed on disk".
  • status=203/EXEC: đường dẫn trong ExecStart sai hoặc không phải đường dẫn tuyệt đối.
  • status=217/USER: user khai báo trong User= chưa tồn tại.
  • Port đã có tiến trình khác dùng: sudo ss -ltnp 'sport = :8080' cho biết tiến trình nào.

Ghi chú production

  • Thêm các chỉ thị hardening và đo bằng systemd-analyze security kdc-web.service: NoNewPrivileges=yes, ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes.
  • Cấu hình ứng dụng đặt trong EnvironmentFile= riêng, quyền 640, không ghi vào unit file. Bài Break/Fix "service sai port" sẽ cho thấy thứ tự ưu tiên giữa Environment= và EnvironmentFile=.

Dọn dẹp

Giữ kdc-web nếu bạn muốn dùng lại; các bài sau dùng port khác nên không xung đột. Muốn xóa:

sudo systemctl disable --now kdc-web.service
sudo rm /etc/systemd/system/kdc-web.service
sudo systemctl daemon-reload
sudo rm -rf /srv/kdc-web
sudo userdel kdc-web
Giải thích lệnh và tham số

systemctl disable --now vừa gỡ cấu hình tự khởi động vừa dừng unit ngay. Xóa unit file rồi daemon-reload để systemd nhận thay đổi. userdel xóa tài khoản dịch vụ; ở đây không thêm -r vì tài khoản không có home.

Tài liệu chính thức