→ From Coder to Shipper
From Coder to Shipper · DevOps

systemd — services và giữ cho mọi thứ chạy

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

Toàn cảnh · chuyện đầu tiên

Chương trình đầu tiên chạy

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ó.

⚡ Bật nguồn

máy khởi động

▶

🧠 Kernel

nạp từ đĩa, khởi phần cứng

▶

👑 systemd

PID 1 — process đầu tiên, người đứng đầu

chạy mãi mãi

▶

⚙️ Các service của bạn

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

Toàn cảnh · nó là gì

Một chương trình, ba việc

systemd là init system — process sở hữu mọi thứ sau khi boot. Nó đội cùng lúc ba chiếc mũ.

PID 1 — process đầu tiên

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ó.

Quản lý service

Đọc các file unit, khởi hay dừng từng service đúng theo chỉ dẫn.

Người dọn dẹp

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.

Toàn cảnh · vì sao có nó

Hai bài toán, một đáp án

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.

Phải sống qua lần khởi động lại

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

Phải sống qua lần crash

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.
toàn bộ chủ đề trong một câu
01
Phần 1

Mô hình tư duy

'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ô hình · hãy hình dung

systemd là cha của mọi thứ

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.

👑 systemd — PID 1

người giám sát · chạy suốt vòng đời máy

▼ ▼ ▼

🌐 nginx

phục vụ web

🐍 app.service

app Python của bạn

🗄 postgresql

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

Mô hình · ý tưởng cốt lõi

Một service = một tờ chỉ dẫn text thường

'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.

app.service

Tên file chính là tên service. systemd đọc nó và nhận trọn mô tả công việc.

Chạy cái gì

Đúng lệnh, user chạy, thư mục làm việc, các biến môi trường.

Khi nào chạy

Có tự khởi khi boot hay không (WantedBy=multi-user.target).

Xảy ra gì khi nó chết

Chính sách khởi lại: luôn quay lại, chỉ khi lỗi, hoặc cứ nằm đó.

Mô hình · trình tự khởi động

Từ nút nguồn đến site của bạn

  1. 1

    Kernel khởi systemd

    systemd thành PID 1 — process đầu tiên, gốc của mọi thứ trên máy.

  2. 2

    systemd đọc các file unit

    Nó quét /etc/systemd/system/ và /lib/systemd/system/ để tìm các file .service.

  3. 3

    Các service đã enable tự khởi

    Mọi unit có phần [Install] ghi 'khởi tôi khi boot' đều chạy lên, theo đúng thứ tự.

  4. 4

    Giám sát bắt đầu

    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.

Mô hình · sự khác biệt

Chương trình terminal của bạn không phải service

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.

Chương trình terminal

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.

Service systemd

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ự.

Mô hình · phạm vi

Mọi thứ trên server đều là service

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.

nginx

Web server — service phục vụ site của bạn.

postgresql

Database — service giữ dữ liệu.

app.service

App Python/Flask của bạn — service mà bạn tự viết file unit cho nó.

sshd

SSH — đúng cánh cửa bạn dùng để quản trị server.

cron

Công việc định kỳ — bản sao lưu lúc 3 giờ sáng.

docker

Chính runtime container — cũng chỉ là một service như bao service khác.

02
Phần 2

systemctl — chiếc điều khiển từ xa

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õ.

systemctl · các động từ

Bốn động từ

Tất cả gõ theo một kiểu: sudo systemctl <động từ> <service>.

start

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.

stop

Dừng nó ngay. Nó vẫn tự khởi ở lần boot kế, trừ khi bạn disable.

restart

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.

status

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ố.

systemctl · nhìn thôi

systemctl status — kiểm tra sức khỏe

Một lệnh: trạng thái service, đã chạy bao lâu, và mấy dòng log gần nhất.

ops@vps — zsh
$ sudo systemctl status app
● app.service - My Flask App
Loaded: loaded (/etc/systemd/system/app.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2026-10-26 09:12:01 UTC; 3h 14min ago
Main PID: 1042 (python3)
Memory: 48.2M
CGroup: /system.slice/app.service
└─1042 /usr/bin/python3 /opt/app/server.py
Oct 26 09:12:01 vps systemd[1]: Started app.service - My Flask App.
"active (running)" = alive · the last lines come from the journal
systemctl · sự nhầm lẫn kinh điển

ENABLE vs START — sự nhầm lẫn số 1

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.

START — ngay bây giờ

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.

ENABLE — mỗi lần boot

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.

systemctl · bức tranh đầy đủ

Hai câu hỏi, hai lệnh

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.

ops@vps — zsh
$ sudo systemctl enable app
Created symlink /etc/systemd/system/multi-user.target.wants/app.service → /etc/systemd/system/app.service.
$ systemctl is-active app
active
$ systemctl is-enabled app
enabled
active = running right now · enabled = will start at boot
systemctl · bảng tra

Những lệnh bạn thực sự sẽ dùng

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êuLệnh
Nó chạy không? sai ở đâu?systemctl status app
Khởi lại sau khi deploysudo systemctl restart app
Khởi nó ngay bây giờsudo systemctl start app
Tự khởi mỗi lần bootsudo systemctl enable app
Không bao giờ tự khởi nữasudo systemctl disable app
Kiểm tra dạng có/không cho scriptsystemctl is-active app
systemctl · restart

restart: nó làm gì, khi nào bạn bấm

Nó làm gì

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.

Khi nào bạn bấm

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'.

03
Phần 3

File unit — tờ chỉ dẫn

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.

Unit files · toàn bộ

Một service hoàn chỉnh, 14 dò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.

app.service
[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

Unit files · phần 1

[Unit] — tôi là ai

Metadata và thứ tự: systemd nói về service này thế nào, và khi nào nó được phép khởi.

Description=

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ố.

After=

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.

Phần bạn hiếm khi đụng tới

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.

Unit files · phần 2

[Service] — chạy tôi thế nào

Trái tim của file: lệnh thực sự và môi trường nó chạy.

ExecStart=

Đú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'.

User=

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.

WorkingDirectory=

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ộ.

Environment=

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ì.

Unit files · phần 3

[Install] — khởi tôi khi nào

Một dòng làm mọi việc. Đây là phần mà lệnh 'enable' đọc.

WantedBy=multi-user.target

'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 làm gì với nó

enable app đọc WantedBy và tạo symlink trong multi-user.target.wants/ — symlink đó CHÍNH là 'tự khởi khi boot'.

Unit files · quy trình

Sửa → reload → enable → kiểm tra

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.

  1. 1

    Viết hoặc sửa file unit

    sudo nano /etc/systemd/system/app.service

  2. 2

    Bảo systemd đọc lại

    sudo systemctl daemon-reload — thiếu bước này, systemd vẫn chạy theo chỉ dẫn cũ.

  3. 3

    Enable (và khởi)

    sudo systemctl enable --now app — enable cho mỗi lần boot, --now khởi luôn ngay.

  4. 4

    Kiểm tra

    systemctl status app — 'active (running)' là đích đến.

Unit files · vị trí

Hai thư mục, một luật: /etc thắng

/etc/systemd/system/

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.

/lib/systemd/system/

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.

04
Phần 4

journalctl — cửa sổ nhật ký

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.

journalctl · ý tưởng

Journal: một log cho mọi thứ

Mọi thứ được bắt lại

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.

Có chỉ mục theo service

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.

Đây là cách bạn debug

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õ.

journalctl · một service

Toàn bộ câu chuyện của một service

ops@vps — zsh
$ journalctl -u app -n 30
Oct 26 09:12:01 vps systemd[1]: Started app.service - My Flask App.
Oct 26 09:12:01 vps app[1042]: * Serving Flask app 'server'
Oct 26 09:12:01 vps app[1042]: * Running on http://0.0.0.0:5000
Oct 27 02:41:07 vps app[1042]: 10.0.0.5 - GET /api/orders 200 312 0.0421s
Oct 27 03:02:19 vps app[1042]: ERROR: connection refused: database
Oct 27 03:02:19 vps app[1042]: Traceback (most recent call last):
-u app = only this service · -n 30 = the last 30 lines
journalctl · trực tiếp

Theo dõi trực tiếp

-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.

ops@vps — zsh
$ journalctl -u app -f
Following log of app.service (press Ctrl+C to stop)…
Oct 27 09:14:02 vps systemd[1]: Started app.service - My Flask App.
Oct 27 09:14:02 vps app[2210]: * Serving Flask app 'server'
Oct 27 09:14:02 vps app[2210]: * Running on http://0.0.0.0:5000
Oct 27 09:14:05 vps app[2210]: 10.0.0.5 - GET / 200 1024 0.0031s
watching the restart land, line by line
journalctl · bảng tra

Sáu cờ, mọi phiên debug

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
05
Phần 5

Keep-alive — không bao giờ nằm chết

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'.

keep-alive · chính sách

Restart= — công tắc hồi sinh

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 đó.

Restart=always

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.

Restart=on-failure

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.

Restart=on-abnormal

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.

Restart=no

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.

RestartSec=2

Chờ 2 giây giữa các lần hồi sinh, để vòng lặp crash không quay tít CPU.

keep-alive · xem nó xảy ra

Nó chết lúc 3 giờ sáng — rồi tự dậy

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.

ops@vps — zsh
$ sudo kill -9 2210 # simulate a crash
$ journalctl -u app -f
Oct 27 03:00:00 vps systemd[1]: app.service: Main process exited, code=killed, status=9/KILL
Oct 27 03:00:00 vps systemd[1]: app.service: Failed with result 'signal'.
Oct 27 03:00:02 vps systemd[1]: Started app.service - My Flask App.
Oct 27 03:00:02 vps app[2288]: * Running on http://0.0.0.0:5000
2 seconds from death to serving — nobody was watching
keep-alive · vòng hằng ngày

Vòng xử lý sự cố 60 giây

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.

  1. 1

    Xác nhận nó down

    systemctl status app — 'failed' hay 'inactive' xác nhận điều đó.

  2. 2

    Đọc lời cuối của nó

    journalctl -u app -n 50 — traceback, lỗi, nguyên nhân.

  3. 3

    Sửa 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.

  4. 4

    Kéo nó dậy

    sudo systemctl restart app

  5. 5

    Kiểm tra và theo dõi

    systemctl status app, rồi journalctl -u app -f trong khi bạn gửi một request kiểm tra.

Toàn cảnh · câu chuyện trọn vẹn

Người giám sát giữ site bạn sống

Hai cách một service có thể chết. systemd xử lý cả hai, tự động — để site luôn chạy.

🔌 Khởi động lại

cập nhật, mất điện — máy khởi động lại

💥 Crash

một bug giết process

▶

👑 systemd

enabled → khởi nó khi boot

Restart=always → hồi sinh nó khi crash

▶

🌐 Site của bạn

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

Toàn cảnh · ngoài phạm vi

Những gì cố tình bỏ qua

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.

Targets & wants

Đồ thị phụ thuộc chính thức (multi-user.target, Wants/Requires). After= + WantedBy đã đáp ứng những gì bạn cần.

Timers & sockets

systemd timer (thay thế cron) và socket activation — mạnh mẽ, nhưng cron vẫn ổn cho lúc này.

networkd & cgroups

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.

Docker vs systemd

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.
góc nhìn của người vận hành

Site chạy. Bạn ngủ ngon.

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.