Từ init process đến cgroup v2 — kiến trúc, vòng đời service, journal logging và kỹ thuật nâng cao cho SRE/DevOps.
Linux • systemd • DevOps Deep Dive
Mastering systemd
Từ init process đến cgroup v2 — toàn bộ kiến trúc, vòng đời service, journal logging và các kỹ thuật nâng cao dành cho SRE/DevOps.
11
unit types
PID 1
init process
cgroup
v2 unified
D-Bus
IPC layer
// 01 — OVERVIEW
systemd là gì?
systemd là init system và service manager thay thế SysV init, được dùng trên hầu hết các distro hiện đại: Ubuntu, Debian, CentOS, RHEL, Arch, Fedora...
Kiến trúc tổng quan
systemd không chỉ quản lý service — đây là một hệ sinh thái hoàn chỉnh gồm nhiều daemon và công cụ tích hợp chặt chẽ.
So sánh với SysV init
Feature
SysV init
systemd
Khởi động song song
✗ sequential
✓ parallel
Dependency management
✗ manual
✓ automatic
Socket activation
✗
✓
Resource control
✗
✓ cgroups
Log management
syslog
journald
On-demand activation
✗
✓
Snapshot/rollback
✗
✓
Config format
Shell scripts
INI-style units
// PATHS QUAN TRỌNG
/lib/systemd/system/ — unit files của distro /etc/systemd/system/ — override của admin (ưu tiên cao hơn) /run/systemd/system/ — runtime units (tạm thời)
// 02 — BOOT PROCESS
Quá trình khởi động
Từ khi bấm nút nguồn đến lúc login prompt xuất hiện — mỗi giai đoạn systemd chịu trách nhiệm gì.
01
BIOS / UEFI
POST → tìm bootloader (GRUB2) trên disk
~1-3s
↓
02
GRUB2 / Bootloader
Load kernel + initramfs vào RAM, truyền kernel params
~0.5s
↓
03
Kernel initializes
Hardware init, mount tmpfs root, decompress initramfs
~0.5s
↓
04
initramfs systemd
Early systemd: mount real root fs, handle LUKS/LVM/RAID
~1s
↓
05
systemd PID 1
Switch root → /sbin/init → systemd starts, reads units
~0.2s
↓
06
default.target
Activate target graph (multi-user.target hoặc graphical.target)
~2-5s
↓
07
Userland services
SSH, networking, DB, app services khởi động song song
~1-10s
Phân tích boot time
Dùng systemd-analyze để debug boot bottleneck:
SHELL
# Tổng thời gian bootsystemd-analyze# Chi tiết từng unitsystemd-analyze blame# Critical chain (longest path)systemd-analyze critical-chain# Export SVG timelinesystemd-analyze plot > boot.svg# Verify unit file syntaxsystemd-analyze verify myapp.service
Kernel command line params
GRUB
# /etc/default/grubGRUB_CMDLINE_LINUX="quiet splash"# Debug boot — verbose systemdsystemd.log_level=debug# Boot vào emergency shellsystemd.unit=emergency.target# Skip specific unitrd.systemd.mask=plymouth.service
// PRO TIP — SRE
Boot time target: server < 30s. Dùng systemd-analyze blame tìm các unit > 2s để optimize hoặc disable. NetworkManager-wait-online.service thường là culprit lớn nhất trên cloud instances.
// 03 — UNIT FILES
Unit Files
systemd quản lý mọi thứ qua "unit" — file cấu hình dạng INI. Có 11 loại unit khác nhau.
[Unit]
Description=Database MigrationAfter=postgresql.serviceRequires=postgresql.serviceConditionPathExists=!/var/lib/myapp/.migrated
[Service]
Type=oneshot# chạy 1 lần, không backgroundRemainAfterExit=yes# giữ "active" sau khi exit 0User=myappExecStart=/opt/myapp/bin/migrate upExecStart=/bin/touch /var/lib/myapp/.migratedExecStartPost=/bin/echo "Migration complete"
[Install]
WantedBy=multi-user.target
/etc/systemd/system/worker@.service
[Unit]
Description=Worker Instance %iAfter=network.target
[Service]
Type=simpleUser=workerEnvironment=WORKER_ID=%iExecStart=/usr/bin/worker --id %i --config /etc/worker/%i.confRestart=on-failure
[Install]
WantedBy=multi-user.target# ─── Sử dụng ───# Khởi động worker instance 1, 2, 3systemctl start worker@1.servicesystemctl start worker@{1..5}.servicesystemctl enable worker@1 worker@2# Specifiers hay dùng:# %i = instance name (worker@redis → redis)# %n = full unit name# %H = hostname# %u = user name# %m = machine ID
Drop-in Override (systemctl edit)
SHELL
# Tạo drop-in overridesystemctl edit nginx.service# Tạo: /etc/systemd/system/nginx.service.d/override.conf# Override toàn bộ unit filesystemctl edit --full nginx.service# Reload sau khi editsystemctl daemon-reload
/etc/systemd/system/nginx.service.d/override.conf
[Service]
# QUAN TRỌNG: muốn override ExecStart# phải clear trước rồi set lạiExecStart=
ExecStart=/usr/sbin/nginx -g 'daemon off;'LimitNOFILE=100000MemoryMax=1G
// 04 — SERVICE LIFECYCLE
Vòng đời Service
State machine của một systemd service — từ lúc được load đến khi fail.
systemctl — Bảng lệnh đầy đủ
Command
Mô tả
systemctl start <unit>
Khởi động unit
systemctl stop <unit>
Dừng unit
systemctl restart <unit>
Restart unit
systemctl reload <unit>
Reload config (SIGHUP)
systemctl reload-or-restart
Reload nếu có thể
systemctl enable <unit>
Enable autostart
systemctl disable <unit>
Disable autostart
systemctl mask <unit>
Hoàn toàn chặn start
systemctl unmask <unit>
Bỏ chặn
Command
Mô tả
systemctl status <unit>
Xem trạng thái chi tiết
systemctl is-active <unit>
active / inactive
systemctl is-enabled <unit>
enabled / disabled
systemctl is-failed <unit>
Kiểm tra failed
systemctl list-units
List tất cả units
systemctl list-unit-files
List unit files
systemctl cat <unit>
Xem nội dung file
systemctl show <unit>
Tất cả properties
systemctl daemon-reload
Reload unit files
// 05 — TARGETS & DEPENDENCIES
Targets & Dependency Graph
Target thay thế runlevel của SysV. Dependency graph quyết định thứ tự khởi động.
Target hierarchy
Dependency keywords
Keyword
Ý nghĩa
Requires=
Bắt buộc — nếu dep fail thì unit cũng fail
Wants=
Optional — dep fail không ảnh hưởng
BindsTo=
Như Requires + nếu dep stop thì unit cũng stop
PartOf=
Propagate stop/restart từ parent
Upholds=
Liên tục restart dep nếu nó stop (systemd 249+)
After=
Thứ tự khởi động (không phải dependency)
Before=
Ngược của After=
Conflicts=
Không thể chạy cùng nhau
Condition*=
Điều kiện có/không start
Assert*=
Như Condition nhưng fail thay vì skip
// QUAN TRỌNG
After= và Requires= là độc lập nhau! Nếu chỉ có Requires=B mà không có After=B, service A và B có thể khởi động song song — dù A depends on B.
SHELL
# Xem dependency graphsystemctl list-dependencies nginx.service# Reverse: ai depends on unit nàysystemctl list-dependencies --reverse nginx.service# Chuyển giữa targets (runlevel)systemctl isolate multi-user.target# Set default targetsystemctl set-default multi-user.target
// 06 — JOURNALD & LOGGING
Journal Logging
journald thu thập logs từ kernel, systemd, services và syslog — tất cả trong một binary format có cấu trúc, có thể query theo nhiều chiều.
[Journal]
Storage=persistent# auto|volatile|persistent|noneCompress=yesSystemMaxUse=2G# max disk usageSystemKeepFree=1G# minimum free spaceSystemMaxFileSize=128M# size per journal fileMaxRetentionSec=1monthMaxFileSec=1weekRateLimitInterval=30sRateLimitBurst=10000ForwardToSyslog=no# nếu dùng rsyslogForwardToWall=no
App log ra KEY=value format → journald index theo field. Query cực nhanh: journalctl MY_FIELD=value thay vì grep qua text.
// 07 — CGROUPS V2 & RESOURCE CONTROL
cgroups v2 Integration
systemd sử dụng cgroups v2 để giới hạn và theo dõi tài nguyên của mỗi service.
cgroup hierarchy
/ (cgroup v2 root)
├── system.slice
│ ├── nginx.service
│ │ └── PID 1234 (nginx: master)
│ │ └── PID 1235 (nginx: worker)
│ └── postgresql.service
│ └── PID 5678 (postgres)
├── user.slice
│ └── user-1000.slice
│ └── session-1.scope
│ └── PID 9999 (bash)
└── machine.slice
└── docker-abc.scope
SHELL
# Xem cgroup treesystemd-cgls# Resource usage realtimesystemd-cgtop# Xem cgroup của processcat /proc/<PID>/cgroup# Tìm service của PIDsystemctl status <PID>
[Unit]
Description=Daily Backup Timer
[Timer]
OnCalendar=*-*-* 02:00:00# daily 2AMOnCalendar=Mon 09:00# Every Monday 9AMPersistent=true# chạy bù nếu bị missedRandomizedDelaySec=5m# random delay chống stampedeAccuracySec=1s
[Install]
WantedBy=timers.target
/etc/systemd/system/backup.service
[Unit]
Description=Backup Service
[Service]
Type=oneshotExecStart=/usr/local/bin/backup.shUser=backup
CALENDAR SYNTAX
# OnCalendar exampleshourly = *-*-* *:00:00daily = *-*-* 00:00:00weekly = Mon *-*-* 00:00:00monthly = *-*-01 00:00:00*:0/15 = every 15 minutesMon,Wed,Fri 18:00 = MWF at 6PM2024-*-15 12:00 = 15th of every month# Verify syntaxsystemd-analyze calendar "Mon *-*-* 09:00"# List all timerssystemctl list-timers --all
// 09 — ADVANCED FEATURES
Tính năng nâng cao
Condition & Assert
UNIT — CONDITIONS
[Unit]
# Skip (không fail) nếu condition saiConditionPathExists=/etc/myapp/configConditionFileNotEmpty=/etc/myapp/configConditionPathIsDirectory=/var/lib/myappConditionHost=prod-server-01ConditionVirtualization=!containerConditionMemory=>4GConditionCPUs=>2ConditionEnvironment=ENV=production# Assert: fail nếu sai (thay vì skip)AssertFileExists=/etc/ssl/cert.pemAssertPathIsReadWrite=/var/lib/myapp
ExecStart Variants
UNIT — EXEC PREFIXES
# Prefixes cho ExecStart/ExecStop/etc:# '-' = ignore failure (exit code != 0)ExecStartPre=-/bin/mkdir -p /var/run/myapp# '@' = pass argv[0] (execute as different name)ExecStart=@/usr/bin/myapp myapp-worker# '+' = run with full privilegesExecStart=+/usr/bin/privileged-setup# '!' = not subject to sandboxingExecStart=!/usr/bin/setup# Multiple exec: run in orderExecStartPre=/usr/bin/check-depsExecStartPre=/usr/bin/migrate-dbExecStart=/usr/bin/myappExecStartPost=/usr/bin/notify-ready
Transient Units (Runtime)
SHELL — systemd-run
# Chạy command như service tạm thờisystemd-run --unit=my-job /usr/bin/my-script# Với resource limitssystemd-run \
--unit=heavy-job \
--property=MemoryMax=1G \
--property=CPUQuota=50% \
/usr/bin/heavy-process# Timer transientsystemd-run --on-calendar="*:0/5" /usr/bin/health-check# Chạy trong user sessionsystemd-run --user --scope /usr/bin/myapp# Xem outputjournalctl -u my-job -f
sd_notify — Service Readiness
SHELL + CODE
# Type=notify: service báo sẵn sàng cho systemd# Trong code (bash)systemd-notify --readysystemd-notify STATUS="Serving 42 clients"systemd-notify WATCHDOG=1# Watchdog: systemd restart nếu service không ping
[Service]
Type=notifyWatchdogSec=30sNotifyAccess=main# | all | exec | none# Python example# import systemd.daemon# systemd.daemon.notify('READY=1')# systemd.daemon.notify('WATCHDOG=1')
User systemd (Systemd --user)
USER SERVICES
# Path: ~/.config/systemd/user/myservice.servicesystemctl --user start myservicesystemctl --user enable myservicesystemctl --user status myservicejournalctl --user -u myservice# Enable lingering: user services survive logoutloginctl enable-linger $USER# Xem user instancesystemctl --user list-units
// 10 — DEBUGGING & TROUBLESHOOTING
Debug & Troubleshooting
SRE playbook — khi mọi thứ bắt đầu cháy.
Bước 1: Xem trạng thái tổng quan
SHELL
systemctl is-system-running# running | degraded | failedsystemctl --failed# list tất cả failed unitssystemctl status# system overview
systemd-analyze verify myapp.service# syntax checksystemctl cat myapp.service# xem effective configsystemctl show myapp.service# tất cả properties# Xem environment variables của servicesystemctl show myapp -p Environment
Bước 4: Debug boot failures
SHELL
# Boot vào emergency mode (add to GRUB cmdline)systemd.unit=emergency.targetrd.break # break vào initramfs# Từ emergency shelljournalctl -xb# logs boot hiện tại với explanationjournalctl -b -0 -p err# Debug verbosesystemd.log_level=debug systemd.log_target=console
Bước 5: Strace / Debug process
SHELL
# Chạy service với stracesystemctl edit myapp.service# Trong override.conf:
[Service]
ExecStart=
ExecStart=/usr/bin/strace -f -o /tmp/myapp.strace /usr/bin/myapp# Live strace attachstrace -p $(systemctl show myapp -p MainPID --value)# Check open files / socketslsof -p $(systemctl show myapp -p MainPID --value)
Quick Reference — Common Issues
Triệu chứng
Lệnh debug
Fix phổ biến
Service start failed
journalctl -u svc -n 50
Check ExecStart path, permissions, deps
Service start loop
systemctl show svc | grep Start
Tăng StartLimitBurst hoặc fix root cause
OOM killed
journalctl -k -g "oom"
Tăng MemoryMax hoặc fix memory leak
Timeout on start
systemctl show svc -p TimeoutStartSec
Tăng TimeoutStartSec, dùng Type=notify
Unit file not found
systemctl cat svc
daemon-reload sau khi tạo file mới
Permission denied
journalctl -u svc -p err
Check User=, ProtectSystem=, paths
Slow boot
systemd-analyze blame
Disable unused units, parallel deps
Journal disk full
journalctl --disk-usage
journalctl --vacuum-size=1G
// ⚠ PRODUCTION WARNINGS
systemctl daemon-reload không restart services đang chạy — chỉ reload unit files vào memory. systemctl mask tạo symlink đến /dev/null — unit không thể start dù ai cố ý — dùng cẩn thận. After= không implies Requires= — luôn set cả hai nếu cần dependency thật sự.
Trên container (Docker, K8s), systemd thường không phải PID 1 — cần config đặc biệt hoặc dùng tini.