Chương trình khởi động server, khởi chạy các service, và lặng lẽ kéo chúng dậy mỗi khi chúng chết. Một mô hình tư duy + bốn lệnh = vận hành hằng ngày.
← / → để chuyển slide · chuyển EN/VI bên dưới
Khi server bật nguồn, việc đầu tiên của kernel là khởi đúng một chương trình: systemd. Mọi thứ khác trên máy đều chờ nó.
máy khởi động
nạp từ đĩa, khởi phần cứng
PID 1 — process đầu tiên, người đứng đầu
chạy mãi mãi
nginx · database · app của bạn — do systemd khởi
không ai khởi systemd — kernel khởi nó, và nó khởi mọi người còn lại
systemd là init system — process sở hữu mọi thứ sau khi boot. Nó đội cùng lúc ba chiếc mũ.
Chương trình đầu tiên kernel khởi. Mọi process khác trên máy, suy cho cùng, đều là 'cháu chắt' của nó.
Đọc các file unit, khởi hay dừng từng service đúng theo chỉ dẫn.
Giám sát mọi service nó đã khởi — và khởi lại khi chúng crash, dù là 3 giờ chiều hay 3 giờ sáng.
Trước khi 'chạy một chương trình trên server' dùng được, hai điều phải đúng. systemd tồn tại để đảm bảo cả hai.
Ai đó khởi động lại máy — bản cập nhật, lần mất điện. Site của bạn phải tự chạy lại mà không cần ai đăng nhập.
→ giải quyết bằng: ENABLE
Một bug, hết bộ nhớ, lần deploy lỗi — process chết. Nó phải tự quay lại, ngay lập tức.
→ giải quyết bằng: Restart=always
Server của bạn phải chạy khi bạn ngủ. systemd là đồng nghiệp không bao giờ ngủ: nó đánh thức cả đội khi boot và kéo dậy bất kỳ ai ngã xuống.
'Service' thực sự là gì, và vì sao chương trình trong terminal của bạn không phải vậy.
Mỗi process trên Linux có một process cha. Dõi các process cha lên trên, bạn luôn kết thúc ở systemd — gốc của cây process.
người giám sát · chạy suốt vòng đời máy
phục vụ web
app Python của bạn
database
giết một service là các process của nó chết theo — rồi systemd có thể kéo nó dậy ngay
'Service' chẳng có gì ma thuật: nó là một file nhỏ (unit) bảo systemd cách chạy một chương trình và giữ nó sống.
Tên file chính là tên service. systemd đọc nó và nhận trọn mô tả công việc.
Đúng lệnh, user chạy, thư mục làm việc, các biến môi trường.
Có tự khởi khi boot hay không (WantedBy=multi-user.target).
Chính sách khởi lại: luôn quay lại, chỉ khi lỗi, hoặc cứ nằm đó.
systemd thành PID 1 — process đầu tiên, gốc của mọi thứ trên máy.
Nó quét /etc/systemd/system/ và /lib/systemd/system/ để tìm các file .service.
Mọi unit có phần [Install] ghi 'khởi tôi khi boot' đều chạy lên, theo đúng thứ tự.
systemd canh chừng từng service suốt vòng đời máy — khởi lại bất kỳ cái nào crash.
python server.py trong terminal và một service systemd có thể chạy cùng một code — nhưng số phận rất khác.
Buộc vào phiên của bạn: đóng terminal hay rớt SSH là nó chết. Nó crash? Chết cho đến khi ai đó tự chạy lại.
Tuyệt để dev — chí mạng trên server.
Tách khỏi mọi terminal. Sống qua lúc bạn đăng xuất, qua lần máy khởi động lại — và tự khởi lại khi crash.
Đây mới là 'chạy ở production' thật sự.
Trên máy Linux, bạn không 'cài rồi chạy' phần mềm — bạn thêm các service. Cả máy là một đội service.
Web server — service phục vụ site của bạn.
Database — service giữ dữ liệu.
App Python/Flask của bạn — service mà bạn tự viết file unit cho nó.
SSH — đúng cánh cửa bạn dùng để quản trị server.
Công việc định kỳ — bản sao lưu lúc 3 giờ sáng.
Chính runtime container — cũng chỉ là một service như bao service khác.
Một lệnh nói chuyện với systemd. Bốn động từ đáp ứng 90% những gì bạn sẽ gõ.
Tất cả gõ theo một kiểu: sudo systemctl <động từ> <service>.
Khởi service ngay — chỉ cho lần boot này. Lần khởi động kế không nhớ, trừ khi bạn enable.
Dừng nó ngay. Nó vẫn tự khởi ở lần boot kế, trừ khi bạn disable.
Dừng + khởi, trong một thao tác. Lệnh quen thuộc mỗi ngày sau khi deploy hoặc đổi config.
Còn sống không? Từ bao giờ? Nó log gì gần đây? Lệnh đầu tiên của mọi sự cố.
Một lệnh: trạng thái service, đã chạy bao lâu, và mấy dòng log gần nhất.
Hai công tắc, hai thời điểm khác nhau: một về 'ngay bây giờ', một về 'mỗi lần boot'. Chúng hoạt động độc lập.
sudo systemctl start app bật process trong phiên hiện tại. Khởi động lại máy thì nó biến mất — trừ khi nó cũng đã enable.
Tưởng tượng: nhấn nút nguồn của một chiếc điều hòa.
sudo systemctl enable app bảo systemd: tự khởi cái này ở mỗi lần boot. Nó KHÔNG khởi nó ngay lúc này.
Tưởng tượng: cắm điều hòa vào ổ điện trên tường.
is-active hỏi 'bây giờ?', is-enabled hỏi 'khi boot?'. Kiểm tra cả hai — service có thể ở bất kỳ tổ hợp nào trong bốn.
Phần còn lại hiếm khi dùng. Những lệnh này thành phản xạ trong một tuần.
| Mục tiêu | Lệnh |
|---|---|
| Nó chạy không? sai ở đâu? | systemctl status app |
| Khởi lại sau khi deploy | sudo systemctl restart app |
| Khởi nó ngay bây giờ | sudo systemctl start app |
| Tự khởi mỗi lần boot | sudo systemctl enable app |
| Không bao giờ tự khởi nữa | sudo systemctl disable app |
| Kiểm tra dạng có/không cho script | systemctl is-active app |
Dừng service, rồi khởi lại từ đầu. Có một khoảng trống ngắn — vài request đang chạy có thể rớt. Điều này bình thường.
Process nhận PID mới; các kết nối cũ không sống qua được.
Sau khi deploy (code mới), sau khi đổi config, hoặc khi status thấy lạ dù service vẫn 'active'.
Nếu phần sửa là file config mà app chỉ đọc lúc khởi, thì restart CHÍNH là nút 'áp dụng'.
Text thường, ba phần, vài dòng quan trọng. Bạn sẽ tự viết hoặc nhờ AI — nhưng phải đọc được chúng.
Đây là toàn bộ file unit cho app Flask của bạn. Mọi dòng bạn cần đều nằm trong này.
[Unit]
Description=My Flask App
After=network.target
[Service]
User=www-data
WorkingDirectory=/opt/app
ExecStart=/usr/bin/python3 /opt/app/server.py
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
[Unit] = tôi là ai · [Service] = chạy tôi thế nào · [Install] = khởi tôi khi nào
Metadata và thứ tự: systemd nói về service này thế nào, và khi nào nó được phép khởi.
Nhãn cho người đọc, hiện trong mọi output status. Dòng đầu tiên bạn thấy trong mọi sự cố.
Thứ tự: khởi tôi sau những cái này (After=network.target = mạng đã lên). Đây là thứ tự, không phải phụ thuộc cứng.
Với mục đích của ta, Description + After=network.target là trọn phần. Copy từ một example đang chạy.
Trái tim của file: lệnh thực sự và môi trường nó chạy.
Đúng lệnh, dùng đường dẫn tuyệt đối. Đường dẫn tương đối không chạy được — server không có 'thư mục hiện tại'.
Ai chạy nó. Không dùng root cho app — www-data hay user riêng giới hạn phạm vi ảnh hưởng của mọi bug.
Nơi process bắt đầu — quan trọng nếu code của bạn mở đường dẫn tương đối hay ghi file cục bộ.
Biến process nhìn thấy: FLASK_ENV=production, DATABASE_URL=… AI hay sinh ra chúng — bạn phải hiểu chúng làm gì.
Một dòng làm mọi việc. Đây là phần mà lệnh 'enable' đọc.
'Khởi tôi khi hệ thống đạt trạng thái đa user bình thường' = khi boot. Đó là dòng duy nhất bạn sẽ dùng.
enable app đọc WantedBy và tạo symlink trong multi-user.target.wants/ — symlink đó CHÍNH là 'tự khởi khi boot'.
systemd giữ các file unit trong bộ nhớ. Sau mỗi lần sửa, bạn phải bảo nó đọc lại — quên bước đó, phần sửa của bạn vô hình.
sudo nano /etc/systemd/system/app.service
sudo systemctl daemon-reload — thiếu bước này, systemd vẫn chạy theo chỉ dẫn cũ.
sudo systemctl enable --now app — enable cho mỗi lần boot, --now khởi luôn ngay.
systemctl status app — 'active (running)' là đích đến.
Của bạn. Các file do quản trị viên chỉnh, gồm app.service bạn viết. Đây là nơi bạn tạo và sửa service.
Nếu một tên tồn tại ở cả hai thư mục, cái này thắng.
Của họ. Đi kèm mọi gói đã cài — nginx.service, sshd.service, postgresql.service nằm ở đây, được viết sẵn.
Thường bạn chỉ đọc chúng. Muốn đè = copy sang /etc và sửa bản copy.
stdout và stderr của mọi service đổ về một cuốn nhật ký chung của cả hệ thống. Một lệnh mở cửa sổ tới bất kỳ cái nào.
App của bạn in ra stdout hay stderr? Nó có trong journal. Mọi service khác cũng vậy — không cần đi săn file log rải rác.
journalctl -u app lọc ra đúng một service — như thể mỗi service có file log riêng, mà không cần file nào.
Một service đang down? Những lời cuối cùng của nó nằm trong journal, chỉ cách bạn vài phím gõ.
-f = theo dõi: dòng mới chảy vào đúng lúc xảy ra. Bản ops của tail -f — trong lúc deploy, ngắm nó khởi lên.
| Bạn muốn gì | Cờ |
|---|---|
| Chỉ một service | -u app |
| N dòng cuối, không phải toàn lịch sử | -n 50 |
| Theo dõi dòng mới trực tiếp | -f |
| Từ một thời điểm | --since "10 min ago" |
| Chỉ dòng lỗi | -p err |
| Nhảy thẳng tới cuối, lật bằng phím mũi tên | -e |
Một dòng duy nhất phân biệt 'chết lúc 3 giờ sáng' với 'tự dậy lúc 3 giờ sáng + 2 giây'.
Mặc định, một process crash sẽ nằm chết — đúng như chương trình terminal của bạn. Một dòng thay đổi điều đó.
Luôn kéo nó dậy, bất kể nó chết kiểu gì. Lựa chọn mặc định cho mọi thứ nên chạy.
Chỉ kéo dậy nếu nó chết xấu (mã lỗi, tín hiệu crash). Sạch hơn — nhưng 'always' là thói quen an toàn.
Chỉ khi bị tín hiệu hay hết giờ. Bạn hiếm khi cần các mức chi tiết này.
Mặc định. Cứ nằm yên. Với một service nên chạy, đó là một sự cố đang chờ đến.
Chờ 2 giây giữa các lần hồi sinh, để vòng lặp crash không quay tít CPU.
Giết process (giả lập crash). systemd nhận ra trong một giây và khởi lại. Journal kể trọn câu chuyện.
Có thứ gì đó đang down. Bạn có năm lệnh và khoảng một phút. Đây là toàn bộ công việc, 9 lần trong 10.
systemctl status app — 'failed' hay 'inactive' xác nhận điều đó.
journalctl -u app -n 50 — traceback, lỗi, nguyên nhân.
Deploy lỗi? Rollback. Thiếu biến môi trường? Thêm vào file unit, rồi daemon-reload.
sudo systemctl restart app
systemctl status app, rồi journalctl -u app -f trong khi bạn gửi một request kiểm tra.
Hai cách một service có thể chết. systemd xử lý cả hai, tự động — để site luôn chạy.
cập nhật, mất điện — máy khởi động lại
một bug giết process
enabled → khởi nó khi boot
Restart=always → hồi sinh nó khi crash
chạy · chạy · chạy — 24/7
bạn deploy code — systemd giữ nó thở qua mọi lần khởi động lại và crash
systemd là một hộp công cụ rất sâu. Đây là những tính năng thật — và không phải bước tiếp theo của bạn.
Đồ thị phụ thuộc chính thức (multi-user.target, Wants/Requires). After= + WantedBy đã đáp ứng những gì bạn cần.
systemd timer (thay thế cron) và socket activation — mạnh mẽ, nhưng cron vẫn ổn cho lúc này.
Quản lý mạng và giới hạn tài nguyên từ systemd. Thật và sâu — không phải mô hình tư duy bạn cần trước.
Container có chính sách restart riêng — mô hình tư duy giống hệt, lệnh khác. Đã học ở chủ đề Docker.
Bạn viết code. Bạn deploy code. systemd đảm bảo code cứ chạy — qua mọi lần khởi động lại, mọi crash, và cả 3 giờ sáng. Bạn giao hàng; nó giám sát.
status → journalctl → restart: vòng hằng ngày đã nằm trong tay bạn. Tiếp theo: ghép toàn bộ stack thành một bản deploy.