Linux / lab

Tài liệu gốc

Lộ trình học Linux

Từ user thành thạo đến systems engineer. Sáu phase, mỗi phase một câu hỏi cần trả lời và một thứ chạy được phải làm ra.

Nhịp~4h/tuần
Tổng40 phiên · ~20 tuần
Nguyên tắc1 phần đọc, 2 phần lab

Giả định & ràng buộc

  • Nhịp: khoảng 4h/tuần, chia thành các phiên 2 tiếng — xem kế hoạch theo phiên.
  • Tổng: 40 phiên. Với nhịp 4h/tuần là khoảng 20 tuần; chậm hơn thì giãn ra, đừng nhồi.
  • Nền giả định: dùng Linux hằng ngày, đã vận hành server thật và chạy container ở môi trường có người dùng. Bỏ qua toàn bộ phần nhập môn (cd/ls/permission cơ bản).
  • Nguyên tắc: không đọc sách tuyến tính. Mỗi phase có một câu hỏi cần trả lời và một artifact phải làm ra. Sách là tra cứu.

Nguyên tắc học

  1. Mỗi phase kết thúc bằng một thứ chạy được, không phải một tập note.
  2. Không test trên server production. Mọi thứ chạy trên VM throwaway (Phase 0).
  3. Gặp lệnh lạ → đọc man section 2/5/7 trước khi đọc blog. man 7 namespaces, man 5 proc, man 2 clone là tài liệu chất lượng cao nhất và luôn đúng version.
  4. Note theo dạng câu hỏi → thí nghiệm → kết quả, không phải tóm tắt sách.
  5. Nếu một khái niệm không giải thích được bằng lời trong 2 phút → chưa nắm, quay lại làm lab.

Phase 0 — Dựng lab (tuần 1)

Mục tiêu: có môi trường được phép phá, tái tạo trong dưới 2 phút.

  • Dựng VM Ubuntu Server bằng libvirt/virt-install trên host lab.
  • Bật serial console (console=ttyS0) để xem kernel log khi VM không boot được.
  • Học virsh snapshot-create-as / snapshot-revert. Đây là thứ khiến việc phá hệ thống trở nên rẻ.
  • Viết script lab-reset.sh: xoá VM cũ, tạo lại, cài sẵn strace ltrace perf bpftrace linux-tools-common build-essential.

Artifact: script dựng lại lab từ đầu bằng một lệnh.

Giáo trình đã soạn: Phiên 1


Phase 1 — Process, file descriptor, signal (tuần 2–5)

Đây là tầng nền. Mọi thứ về sau (container, systemd, debug) đều quy về đây.

Nội dung

  • fork/exec/wait, exit status, zombie, orphan, reparent về PID 1.
  • File descriptor thật sự là gì: bảng fd → open file description → inode. Vì sao dup2 và redirect hoạt động.
  • Process group, session, controlling terminal, vì sao Ctrl+C giết cả nhóm, vì sao nohupsetsid khác nhau.
  • Signal: signal disposition, SIGKILL/SIGSTOP không chặn được, signal trong quá trình shutdown container (SIGTERM → grace period → SIGKILL).
  • /proc/<pid>/: status, fd/, maps, cmdline, limits, cgroup.

Đọc

  • TLPI ch. 24–28 (process creation, termination, monitoring, exec), ch. 20–22 (signal).
  • man 5 proc — đọc dần, không đọc hết.

Lab

  1. Viết C nhỏ: fork → con exit → cha ngủ 30s. Quan sát zombie trong ps. Sửa bằng waitpid.
  2. Chạy một service self-host, dùng ls -l /proc/<pid>/fd liệt kê nó đang giữ file/socket nào. Đối chiếu với ss -tlnp.
  3. strace -f -e trace=openat,connect một tiến trình khởi động để xem nó đọc config từ đâu — kỹ năng debug dùng được ngay trong công việc.
  4. Gửi SIGTERM cho container, đo thời gian tới khi bị SIGKILL. Đối chiếu docker stop --time.

M-L1 — kiểm tra: giải thích được vì sao process trong container thấy PID 1 là chính nó; vì sao đóng shell làm chết process con nhưng setsid thì không.


Phase 2 — Memory, page cache, OOM (tuần 6–8)

Phase trả lời cho các sự cố production dạng “máy còn RAM mà vẫn chết” và “disk đầy nhưng xoá file không giải phóng”.

Nội dung

  • Virtual memory, page table, RSS vs VSZ vs PSS — vì sao cộng RSS của tất cả process ra số lớn hơn RAM thật.
  • Page cache, dirty page, writeback, fsync semantics. Vì sao ghi file xong mất điện là mất dữ liệu.
  • OOM killer: oom_score, oom_score_adj, cách đọc dòng OOM trong dmesg.
  • Swap và vm.swappiness; vì sao tắt swap không phải lúc nào cũng đúng.
  • cgroup v2 memory controller: memory.max, memory.high, memory.stat, memory.events.

Đọc

  • TLPI ch. 6 (memory layout), ch. 49 (mmap), ch. 13 (buffering, fsync).
  • man 5 proc phần meminfo, smaps.
  • Kernel doc: Documentation/admin-guide/cgroup-v2.rst — phần Memory.

Lab

  1. Đọc một file lớn, quan sát free -m phần buff/cache tăng. Drop cache, đo lại thời gian đọc.
  2. Tạo cgroup v2, set memory.max=100M, chạy chương trình cấp phát dần → đọc memory.events và dòng OOM trong dmesg.
  3. Mô phỏng “xoá file mà disk không giảm”: xoá file trong khi một process còn giữ fd, quan sát bằng lsof +L1. Đây là bug hay gặp với log file.

M-L2 — kiểm tra: đọc một dòng OOM kill trong dmesg và nói được process nào bị giết, vì sao nó chứ không phải process khác.


Phase 3 — Filesystem, mount, overlayfs (tuần 9–11)

Phase khớp trực tiếp với dạng sự cố kinh điển: container update xong thì dữ liệu biến mất.

Nội dung

  • VFS, inode, dentry, hardlink vs symlink, vì sao mv trong cùng filesystem là O(1).
  • Mount, bind mount, mount propagation (shared/private/slave) — nguyên nhân sâu xa của nhiều lỗi volume trong Docker.
  • overlayfs: lowerdir/upperdir/workdir. Đây chính là cơ chế image layer của Docker.
  • Docker volume vs bind mount vs anonymous volume — cái nào biến mất khi service update.
  • LVM cơ bản: PV/VG/LV, snapshot, mở rộng LV online.

Đọc

  • TLPI ch. 14 (filesystem), ch. 18 (directories, links).
  • man 8 mount phần propagation, man 5 tmpfs.
  • Kernel doc: Documentation/filesystems/overlayfs.rst.

Lab

  1. Dựng overlayfs bằng tay: 3 thư mục + mount -t overlay. Sửa file ở upper, quan sát whiteout khi xoá file của lower.
  2. Tái dựng một sự cố mất dữ liệu trên lab: chạy container có state trong anonymous volume, docker service update --force, quan sát dữ liệu đi đâu. Rồi làm lại với named volume và bind mount.
  3. Tạo LV, snapshot, phá dữ liệu, restore từ snapshot.

M-L3 — kiểm tra: vẽ được đường đi từ một file trong container tới vị trí thật của nó trên host.


Phase 4 — Namespace, cgroup, viết container từ đầu (tuần 12–15)

Phase có ROI cao nhất: sau phase này Docker Swarm không còn là hộp đen.

Nội dung

  • 8 namespace: mount, PID, net, UTS, IPC, user, cgroup, time. Cái nào Docker dùng mặc định.
  • clone(2) flags, unshare(1), nsenter(1).
  • pivot_root vs chroot, vì sao container dùng cái đầu.
  • cgroup v2 unified hierarchy, delegation, systemd quản cgroup ra sao.
  • capabilities, seccomp, no-new-privs — vì sao container không phải sandbox bảo mật thật.

Đọc

  • man 7 namespaces, man 7 user_namespaces, man 7 capabilities — đọc kỹ, đây là nguồn chính.
  • TLPI ch. 28 (clone), ch. 39 (capabilities).
  • Loạt bài namespaces của Michael Kerrisk trên LWN.

Lab

  1. unshare --pid --fork --mount-proc bash → chạy ps aux, thấy PID 1 là bash của mình.
  2. Viết mini-container: shell script, hoặc một chương trình nhỏ bằng ngôn ngữ có binding syscall (Rust với nix, Go, C) — tạo namespace, pivot_root vào rootfs giải nén từ image, set cgroup limit, exec /bin/sh.
  3. nsenter vào namespace của một container đang chạy để debug mà không cần docker exec.

M-L4 — artifact: chương trình dưới 200 dòng chạy được process cô lập PID + mount + net, có giới hạn memory, không dùng Docker.


Phase 5 — Network stack (tuần 16–19)

Phase giải thích overlay network của Swarm.

Nội dung

  • netns, veth pair, bridge, routing table, ip command toàn bộ (bỏ ifconfig/route).
  • iptables vs nftables (Ubuntu hiện dùng nft backend), chain, table, hook điểm nào trong netfilter.
  • conntrack, NAT, vì sao docker-proxy tồn tại.
  • VXLAN — nền tảng overlay network của Docker Swarm; MTU và vấn đề phân mảnh.
  • ss, tcpdump, đọc TCP handshake và retransmission.

Đọc

  • man 8 ip, man 7 netdevice.
  • Sơ đồ packet flow của netfilter (bản đồ hook) — in ra dán bàn.
  • TLPI ch. 56–61 (socket) — tra cứu khi cần.

Lab

  1. Dựng bằng tay: 2 netns nối qua veth + bridge, ping được nhau. Không dùng Docker.
  2. Thêm NAT bằng nftables để netns ra được internet.
  3. Dựng VXLAN tunnel giữa 2 VM, cho 2 netns ở 2 máy khác nhau ping nhau. So sánh với docker network inspect của overlay network thật.
  4. tcpdump một request đi qua Swarm ingress, xác định đúng các chặng NAT.

M-L5 — kiểm tra: giải thích được đường đi của gói tin từ client tới container trong Swarm, kể tên đúng các thành phần.


Phase 6 — Observability & performance (tuần 20–26)

Phase tạo khác biệt lớn nhất về mặt nghề nghiệp.

Nội dung

  • USE method (Utilization, Saturation, Errors) và cách áp cho CPU/memory/disk/network.
  • perf: sampling, perf record/report, flame graph.
  • eBPF: bpftrace one-liner, bcc tools (execsnoop, opensnoop, biolatency, tcplife).
  • Latency analysis: đo ở đâu, vì sao average vô dụng, percentile và coordinated omission.
  • Phân biệt bottleneck: CPU-bound vs I/O-bound vs lock contention.

Đọc

  • Systems Performance (Brendan Gregg, 2nd ed) — ch. 2 (methodology), 6 (CPU), 7 (memory), 9 (disk), 10 (network). Đây là sách chính của phase.
  • BPF Performance Tools (Gregg) — dùng như catalogue công cụ.

Lab

  1. Chọn một service self-host thật, áp USE method đầy đủ, viết report một trang.
  2. Gây tải bằng stress-ng / fio, xác định bottleneck trước khi xem đáp án, rồi kiểm chứng.
  3. biolatency trên một service I/O nặng, đối chiếu với latency thấy ở tầng ứng dụng.
  4. Vẽ một flame graph CPU cho một workload CPU-bound thật.

M-L6 — artifact: một bài phân tích hiệu năng thật (nêu triệu chứng → giả thuyết → phép đo → kết luận), đủ chất lượng để người khác đọc và tái lập được.


Xen kẽ (rải rác, không chiếm slot riêng)

systemd — học khi cần, không học một lượt: unit type, dependency (After vs Requires), systemd-analyze blame, timer thay cron, journalctl -u --since, resource control qua cgroup (MemoryMax=, CPUQuota=).

Boot process — một buổi: firmware → GRUB → initramfs → PID 1. Làm hỏng initramfs trên lab rồi cứu bằng rescue mode. Một lần là nhớ mãi.

Security cơ bản — AppArmor trên Ubuntu (không phải SELinux), SSH hardening, auditd khi cần điều tra.


Sau lộ trình này — nhánh kernel

Chỉ vào sau khi đã có nền OS thực hành — tự viết được một kernel nhỏ chạy trên máy ảo. Vào sớm hơn thì loãng.

  • OSTEP — miễn phí, đọc để có khung khái niệm OS.
  • Slide training của Bootlin — kernel/driver/embedded, chất lượng cao, miễn phí.
  • LFD103 (Linux Foundation) — miễn phí, hướng dẫn quy trình đóng góp kernel thật.
  • Mục tiêu thực tế: đọc hiểu được một subsystem nhỏ và gửi được một patch cleanup, không phải “hiểu cả kernel”.

Reading list — mua/lấy theo thứ tự cần

SáchDùng vàoCách dùngƯu tiên
The Linux Programming Interface (Kerrisk)Phase 1–5Tra cứu theo chương, không đọc tuyến tínhCao, mua sớm
Systems Performance 2nd ed (Gregg)Phase 6Đọc xuyên ch. 2, còn lại traCao
BPF Performance Tools (Gregg)Phase 6Catalogue công cụTrung bình
Linux System Programming (Love)Phase 1–3Bản rút gọn của TLPI nếu TLPI nặngTuỳ chọn
Understanding the Linux KernelNhánh kernelCũ nhưng khung vẫn đúngThấp, để sau
man-pages (miễn phí)Toàn bộNguồn chính, không phải phụBắt buộc

Nguồn miễn phí bám sát: man 7 *, kernel Documentation/, blog Julia Evans, LWN.


Bẫy thường gặp

  • Học lệnh thay vì học khái niệm. Thuộc 50 flag của ip mà không hiểu netns thì vẫn không debug được. Cứ hỏi “cái này ở tầng nào của kernel”.
  • cgroup v1 vs v2. Rất nhiều blog cũ viết cho v1. Ubuntu mới mặc định v2 unified. Kiểm tra bằng stat -fc %T /sys/fs/cgroup trước khi làm theo bất kỳ hướng dẫn nào.
  • iptables vs nftables. Ubuntu dùng iptables-nft — lệnh iptables vẫn chạy nhưng rule thật nằm ở nft. Xem bằng nft list ruleset để tránh nhầm lẫn.
  • Đọc nhiều, làm ít. Tỉ lệ mục tiêu: 1 phần đọc, 2 phần lab.
  • Nhồi cả TLPI. 1500 trang, đọc tuyến tính là bỏ cuộc ở tuần 3.
  • Học tách rời việc thật. Mỗi phase nên soi lại một sự cố có thật hoặc một hệ thống đang chạy — đó là thứ giữ động lực.

Mốc tổng kết

MốcTuầnĐiều kiện đạt
M-L01Lab VM dựng lại bằng một lệnh
M-L15Đọc được /proc/<pid>/, giải thích fd và signal
M-L28Phân tích được một vụ OOM từ dmesg
M-L311Dựng overlayfs tay, giải thích được đường đi file trong container
M-L415Container tự viết, chạy được, có cgroup limit
M-L519Dựng tay được overlay network qua VXLAN
M-L626Một bài phân tích hiệu năng hoàn chỉnh trên hệ thống thật