Manifest
KHOÁ DOCKERTỜ 02 / 34CHƯƠNG 01SỬA ĐỔI C
Tờ 02 · Chương 01Nhập môn

Docker EngineCài xong rồi, nhưng bạn vừa đặt cái gì lên máy mình?

Ba dòng lệnh dán từ một bài blog cũ vẫn chạy được. Cái giá trả sau là ổ đĩa đầy vào một đêm nào đó, và một cái socket có quyền ngang root mà không ai nói cho bạn biết. Tờ này mổ xẻ năm chương trình thật sự đang chạy — bằng năm bản vẽ bạn tự bấm.

Thời lượng18 phút
Bản vẽ05 hình
Trình độNhập môn
Truy cậpMiễn phí
dockertờ khai bạn điền/var/run/docker.sockdockerdnhân viên quầycontainerdbộ phận lưu trữcontainerd-shimngười trực ở lạiruncdựng xong là rútNăm chương trình · một ô cửa · không bước nào kiểm danh tính
01Hiện trường

Ba dòng lệnh dán từ một bài blog năm 2017

Bạn tìm "cài docker ubuntu", mở kết quả đầu tiên, dán ba dòng vào terminal. Máy chạy một lúc rồi im. Bạn gõ docker run hello-world.

bash · lần chạy đầu tiên
$ docker run hello-world
permission denied while trying to connect to the Docker daemon socket

Bạn tìm tiếp, thấy người ta bảo thêm sudo. Thêm vào thì chạy được. Bạn đóng terminal và đi làm việc khác.

Hai câu hỏi khác nhau

Tờ 01 trả lời container là gì. Tờ này trả lời cái gì chạy trên máy bạn để container tồn tại được. Câu thứ hai mới là thứ quyết định bạn gỡ rối được hay chỉ biết dán lệnh.

Vấn đề của ba dòng lệnh kia không phải là chúng sai. Vấn đề là sau khi chạy xong, bạn không biết mình vừa đặt lên máy năm chương trình, một socket có quyền ngang root, hai dịch vụ systemd, và một thư mục sẽ phình dần tới khi hết ổ.

02Chọn đường

Trước khi gõ lệnh nào, hãy trả lời hai câu hỏi

"Cài Docker" không phải một việc. Nó là ít nhất bốn việc khác nhau, tuỳ vào máy bạn đang ngồi và mục đích dùng. Chọn nhầm nhánh thì mọi hướng dẫn phía sau đều lệch.

HÌNH 01
Chọn bản Docker nào — bốn câu trả lời, năm kết quả
Tầng 1/3

Chưa chọn nhánh nào.

Bạn cài Docker lên máy nào?Windows 10 / 11WSL 2 đã bật chưa?đã bậtchưa bậtmacOSCần chạy image amd64?khôngLinux để bànMuốn bảng điều khiển đồhoạ?khôngVPS · máy chủ LinuxMáy có nhiều người dùng?khôngDocker Desktop · nềnWSL 2Docker Desktop · máyảo LinuxDesktop hoặc EngineEngine ·rootlessEngine ·nhóm dockerGói cần càiCái chạy thậtĐiều dễ saiBấm vào máy bạn đang dùng để bắt đầu.
Cách dùngBấm vào loại máy bạn đang dùng. Ba nhánh còn lại sẽ mờ đi để bạn thấy rõ mình đang đi đường nào.

Điểm hay bị hiểu nhầm nhất nằm ở nhánh Windows và macOS: Docker Desktop không chạy container trực tiếp trên hệ điều hành của bạn. Nó dựng sẵn một máy ảo Linux nhỏ và chạy Docker Engine bên trong đó. Cái bạn gõ lệnh là client; cái thực sự làm việc nằm trong máy ảo.

NhánhCái bạn thật sự càiContainer chạy ở đâu
Windows · WSL 2Docker DesktopMáy ảo Linux do Desktop dựng
macOS (Apple Silicon)Docker DesktopMáy ảo Linux do Desktop dựng
Ubuntu để bànDocker Engine hoặc DesktopThẳng trên nhân Linux của máy
VPS / máy chủChỉ Docker EngineThẳng trên nhân Linux của máy
03Ẩn dụ

Cái quầy và những người bạn không nhìn thấy

Hiểu nôm na

Docker là bộ phận một cửa của phường

Bạn cần sao y một giấy tờ ở phường. Bạn không đi tìm phòng lưu trữ, không tự mở tủ hồ sơ, không tự đóng dấu. Bạn ra bộ phận một cửa, điền tờ khai, nộp qua ô cửa, lấy số rồi ngồi đợi. Bên trong có người giữ kho hồ sơ, có người ký, có người mang đi đóng dấu. Bạn không gặp ai trong số đó. Bạn chỉ thấy đúng một ô cửa.

Ở phườngTrên máy bạn
Tờ khai bạn điềnLệnh docker run … gõ ở CLI
Ô cửa nhận hồ sơSocket /var/run/docker.sock
Nhân viên quầy — nhận, kiểm, quyết địnhdockerd — máy chủ API của Docker
Bộ phận lưu trữ, giữ kho và sổ theo dõicontainerd — giữ image, giữ vòng đời container
Người trực ở lại canh từng hồ sơcontainerd-shim-runc-v2 — mỗi container một cái
Cán bộ đi làm đúng một việc rồi rútrunc — dựng namespace, cgroup, rồi tự thoát
Ẩn dụ này sai ở chỗ

Ba chỗ, và cả ba đều tốn tiền nếu hiểu nhầm.

Một — quầy một cửa có kiểm tra bạn là ai. Docker socket thì không. Nó không phân quyền theo hành động: ai chạm được vào socket là làm được mọi thứ, kể cả gắn thư mục gốc của máy chủ vào một container rồi ghi thẳng vào đó. Vào nhóm docker không phải là "root nhẹ hơn" — nó là root, chỉ đi vòng.

Hai — ở phường bạn phải chờ tới lượt, và cái quầy nằm cùng toà nhà với bạn. dockerd phục vụ song song nhiều client, và client không bắt buộc ngồi cùng máy. Đặt biến DOCKER_HOST trỏ sang máy khác là bạn đang điều khiển Docker ở đầu kia đất nước bằng đúng cái CLI đó.

Ba — cán bộ thực địa về là hồ sơ khép lại. runc thì ngược hẳn: nó chết ngay sau khi container khởi động, mà container vẫn sống khoẻ. Thứ ở lại làm cha nuôi cho tiến trình bên trong là containerd-shim. Đây là lý do khởi động lại dockerd không giết container nào — chuyện mà ẩn dụ hành chính không giải thích nổi.

04Cơ chế

Từ phím Enter tới một tiến trình thật

Năm cái tên trong bảng trên không phải sơ đồ trên giấy. Chúng là năm chương trình đang có mặt trên máy, và bạn nhìn thấy được bằng ps.

HÌNH 02
Từ phím Enter tới một tiến trình thật — bảy tầng bàn giao
Bước 1/6

Bước 1. CLI không tạo container. Nó dựng lệnh của bạn thành một lời gọi HTTP POST containers create.

Bằng chứng trên máy thật$ docker versionClient:API version: 1.52Server:API version: 1.52Docker 29.1.3 · containerd 2.2.1runc 1.3.4 · Ubuntu 24.04.1dockerCLI — tiến trình của bạn/var/run/docker.sockdockerdmáy chủ APIcontainerdkho image và sổ containercontainerd-shim-runc-v2một cái cho mỗi containerruncdựng rồi rútnginxtiến trình trong containerBấm thẳng vào một tầng để nhảy tới bước của tầng đó
1 / 6
bash · ba tầng nền, nhìn từ máy chủ1/8 dòng
$ ps -eo pid,ppid,comm --sort=pid | grep -E 'dockerd|containerd'

Đọc kỹ cột thứ hai: dockerd, containerd mọi containerd-shim đều có PPID bằng 1. Không cái nào là con của cái nào. Chúng là các tiến trình độc lập nói chuyện với nhau qua socket — nên tắt cái này không kéo theo cái kia.

Mẹo nhận diện

Cách kiểm nhanh xem một máy đang dùng Docker hay Kubernetes trên cùng containerd: xem tham số -namespace của các tiến trình shim. moby là Docker, k8s.io là Kubernetes.

05Đối chiếu

Hai kho, hai kết quả rất khác nhau

Trên Ubuntu có đúng hai đường cài phổ biến, và chúng cho ra hai bộ công cụ khác nhau. Đây là chỗ nhiều người vấp mà không biết mình vấp.

HÌNH 03
Hai kho, hai bộ đồ nghề — bảy tiêu chí đối chiếu
Hàng mở 04

Hàng 4: docker buildx. Kho Ubuntu: thường không có. Kho chính thức: có, gói riêng. Cùng số phiên bản engine, khác bộ đồ nghề. Lỗi chỉ lộ ra lúc build theo một hướng dẫn hiện đại.

Kho Ubuntuapt install docker.ioKho chính thức Dockerdownload.docker.comdocker buildxthường không cócó, gói riêng
Kho Ubuntu$ docker buildx version docker: unknown command: docker buildx
Kho chính thức$ docker buildx version github.com/docker/buildx v0.x.x
Kết luậnCùng số phiên bản engine, khác bộ đồ nghề. Lỗi chỉ lộ ra lúc build theo một hướng dẫn hiện đại.
Cách dùngBấm một hàng để mở bằng chứng lệnh thật ở cả hai bên. Mỗi lần chỉ mở một hàng — mở hết cùng lúc thì bảng dài ra gấp ba và mất tác dụng nhìn một cái thấy đủ bảy tiêu chí.

Khác biệt tốn thời gian nhất là bộ plugin. Bản đóng gói theo bản phân phối thường thiếu buildx — thứ mà mọi hướng dẫn build image hiện nay đều giả định là có sẵn.

bash · máy cài từ kho Ubuntu
$ docker --version
Docker version 29.1.3, build 29.1.3-0ubuntu3~24.04.2

$ docker buildx version
docker: unknown command: docker buildx

$ docker info | sed -n '/Plugins:/,/Server:/p'
 Plugins:
  compose: Docker Compose (Docker Inc.)
    Version:  2.40.3+ds1-0ubuntu1~24.04.1
    Path:     /usr/libexec/docker/cli-plugins/docker-compose
  trust: Manage trust on Docker images (Docker Inc.)
    Version:  29.1.3
Trên máy chủ thật

Hãy chọn kho chính thức của Docker. Không phải vì bản phân phối làm ẩu, mà vì bạn muốn tự quyết thời điểm nâng cấp engine, và muốn bộ plugin khớp với tài liệu bạn đang đọc.

06Tay làm

Cài Docker Engine trên Ubuntu 24.04

Bốn bước. Chạy từng bước một, đọc kết quả từng bước, đừng dán cả khối.

bash · bước 1 — gỡ các gói cũ có thể xung đột
$ for p in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
>   sudo apt-get remove -y $p
> done

Bước này không xoá dữ liệu trong /var/lib/docker. Nó chỉ gỡ các gói cũ để chúng không tranh chấp tên lệnh với bản sắp cài.

bash · bước 2 — thêm khoá GPG chính thức
$ sudo apt-get update
$ sudo apt-get install -y ca-certificates curl
$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc
apt-key đã chết

Hướng dẫn nào còn dùng apt-key add là hướng dẫn đã hỏng. apt-key bị khai tử vì nó đưa khoá vào kho tin cậy toàn cục — khoá của Docker khi đó ký được cho mọi kho khác trên máy. Cách đúng là đặt khoá vào /etc/apt/keyrings/ rồi trỏ signed-by= cho riêng kho đó.

bash · bước 3 — khai báo kho và cài
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
      docker-buildx-plugin docker-compose-plugin
bash · bước 4 — sau khi cài
$ sudo usermod -aG docker $USER
$ newgrp docker
$ id
uid=1000(doanhcd) gid=1000(doanhcd) groups=1000(doanhcd),27(sudo),100(users),112(docker)

Không có newgrp docker (hoặc đăng xuất rồi vào lại) thì phiên terminal hiện tại vẫn giữ danh sách nhóm cũ, và bạn vẫn nhận permission denied dù lệnh usermod đã chạy đúng. Đây là lý do thật đằng sau chín mươi phần trăm câu hỏi "em làm đúng hết mà vẫn lỗi".

07Vận hành

Hai việc phải làm ngay, trước khi chạy container đầu tiên

Việc thứ nhất là giới hạn log. Mặc định Docker ghi log container ra file JSON không giới hạn dung lượng. Một dịch vụ nói nhiều chạy vài tháng là ăn hết ổ, và triệu chứng lúc đó trông không giống lỗi log chút nào.

bash · /etc/docker/daemon.json
$ sudo tee /etc/docker/daemon.json >/dev/null <<'JSON'
{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" },
  "live-restore": true
}
JSON
$ sudo systemctl restart docker
$ docker info --format '{{.LoggingDriver}} · live-restore={{.LiveRestoreEnabled}}'
json-file · live-restore=true
HÌNH 04
Từ lúc bật máy tới lúc container phục vụ — bốn làn thời gian
Mốc 1/10

Mốc 1 trên 10: systemd. Nhân Linux xong việc, systemd nhận quyền điều phối. Chưa có gì của Docker ở đây.

KỊCH BẢN A · BẬT MÁYsystemdcontainerddockerdcontainersystemddocker.socketcontainerd.servicedocker.servicenối vào containerdkhôi phục containersinh lại shimtiến trình chạyhealthcheck lần đầuphục vụ requesttrục không theo tỉ lệ — chỉ theo thứ tựLỆNH SOI MỐCsystemctl is-system-running
1 / 10
systemdNhân Linux xong việc, systemd nhận quyền điều phối. Chưa có gì của Docker ở đây.
live-restore không liên quan

Việc thứ hai là hiểu cái mình vừa nhận khi vào nhóm docker. Đây không phải chuyện lý thuyết bảo mật — nó là đường leo quyền ngắn nhất trên một máy Linux có cài Docker.

HÌNH 05
Nhóm docker dẫn tới đâu — bản đồ sáu bước
Bước đã lần 00 / 06

Chưa chọn nút nào.

usermod -aGquyền rw nhờ nhómmọi lệnh, không kiểm quyềndaemon là root nên tạo đượcthư mục gốc đã ở trong containerghi được nghĩa là làm chủtài khoản thường · uid1000nhóm docker · gid 112/var/run/docker.sockdockerd · chạy bằng rootcontainer có -v /:/hosttoàn bộ hệ thống tệp máy chủquyền root thật sự trên máyChế độ mặc định — sáu bước liền mạch, không bước nào cần mật khẩu
Chưa chọnBấm nút quyền root thật sự trên máy để thấy cả sáu bước cùng lúc — đó là đường ngắn nhất từ một tài khoản thường tới quyền root trên máy Linux có cài Docker.
Cách dùngBấm một nút bất kỳ để lần đường đi từ tài khoản thường tới nút đó. Mỗi cạnh là đúng một lệnh có thật — không có bước nào cần mật khẩu.
bash · cái socket đó thuộc về ai
$ ls -l /var/run/docker.sock
srw-rw---- 1 root docker 0 Aug 21 14:53 /var/run/docker.sock

$ getent group docker
docker:x:112:doanhcd

Chủ sở hữu là root, nhóm là docker, và nhóm có quyền đọc ghi. Ai trong nhóm docker cũng gửi được lệnh cho một tiến trình đang chạy bằng root. Không có bước xác thực nào ở giữa.

Đừng cho tiện

Đừng thêm người khác vào nhóm docker như một cách "cho tiện". Trên máy nhiều người dùng hoặc máy chủ CI, hãy dùng chế độ rootless (dockerd-rootless-setuptool.sh install, gói docker-ce-rootless-extras).

08Nghiệm thu

Bốn lệnh kiểm chứng, và cách đọc kết quả

Cài xong không có nghĩa là chạy đúng. Bốn lệnh dưới đây kiểm bốn thứ khác nhau, theo đúng thứ tự từ nông tới sâu.

bash · lệnh 1 — client và server có nói chuyện được không
$ docker version
Client:
 Version:           29.1.3
 API version:       1.52
 Go version:        go1.24.4
 OS/Arch:           linux/amd64
 Context:           default

Server:
 Engine:
  Version:          29.1.3
  API version:      1.52 (minimum version 1.44)
  OS/Arch:          linux/amd64
 containerd:
  Version:          2.2.1
 runc:
  Version:          1.3.4-0ubuntu1~24.04.1
 docker-init:
  Version:          0.19.0

Hai khối ClientServer chính là hai đầu của cái quầy trong phần ẩn dụ. Khối containerd runc xác nhận đúng chuỗi bàn giao ở phần 04 — đây cũng là bằng chứng Docker hiện đại không tự chạy container, nó uỷ quyền xuống dưới.

bash · lệnh 2 — máy đang ở cấu hình nào
$ docker info | sed -n '/^Server:/,/Kernel/p' | head -18
Server:
 Containers: 6
  Running: 6
 Images: 153
 Server Version: 29.1.3
 Storage Driver: overlayfs
  driver-type: io.containerd.snapshotter.v1
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 2
bash · lệnh 3 — chạy thật một lần
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4f55086f7dd0: Pull complete
Digest: sha256:5dd0d3e6e255913fc30f90b9f2b1d359cc2cbdb48090cc4b65f1676e203243cc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image…
 4. The Docker daemon streamed that output to the Docker client…

Bốn bước tiếng Anh mà hello-world in ra chính là chuỗi bàn giao bạn vừa xem, viết bằng lời. Điểm đáng chú ý ở dòng cuối: output đi ngược từ container qua daemon rồi mới ra terminal của bạn — bạn không nói chuyện trực tiếp với container bao giờ cả.

bash · lệnh 4 — plugin compose có đúng v2 không
$ docker compose version
Docker Compose version 2.40.3+ds1-0ubuntu1~24.04.1

Viết liền docker compose (dấu cách) là v2, chạy như một plugin của CLI. Viết docker-compose (dấu gạch nối) là v1 — bản Python đã ngừng hỗ trợ.

Compose v2 không còn version:

Từ Compose v2, file compose.yml không còn khoá version:. Khoá đó là di sản của Compose Spec cũ; giữ lại chỉ tạo cảnh báo. Thấy version: "3.8" ở đầu file nghĩa là tài liệu đó viết trước 2023.

09Bảng sửa đổi

Năm sai lầm tốn thời gian nhất khi cài

SỐSAI LẦM & CÁCH SỬA
01

Dán curl … | sudo bash cho máy chủ thật

Script tiện lợi ấy được chính Docker khuyến nghị chỉ dùng để thử nhanh, không dùng cho môi trường chạy thật. Nó tự chọn kho, tự chọn phiên bản, và bạn giao quyền root cho một nội dung tải về mà chưa đọc.

Cách sửaDùng bốn bước ở phần 06. Mất thêm hai phút, đổi lại bạn biết chính xác kho nào, khoá nào, gói nào.

02

Cài từ kho bản phân phối rồi ngạc nhiên vì thiếu công cụ

apt install docker.io cho ra engine chạy được nhưng bộ plugin không đầy đủ — thường thiếu buildx. Lỗi chỉ lộ ra lúc bạn build image theo một hướng dẫn hiện đại.

Cách sửaTrên máy để học thì chấp nhận được; trên máy làm việc và máy chủ thì dùng kho chính thức để bộ đồ nghề khớp với tài liệu.

03

Coi nhóm docker là "sudo nhẹ"

Vào nhóm docker là có quyền tương đương root, chỉ đi bằng đường khác. Bất kỳ ai trong nhóm đều gắn được thư mục gốc của máy chủ vào container rồi ghi lên đó.

Cách sửaChỉ thêm tài khoản quản trị. Máy nhiều người dùng hoặc runner CI thì chuyển sang rootless.

04

Quên nạp lại danh sách nhóm

Chạy usermod -aG docker $USER xong thử ngay trong terminal đang mở, vẫn permission denied, rồi kết luận là lệnh không ăn.

Cách sửanewgrp docker, hoặc đăng xuất rồi đăng nhập lại. Kiểm bằng id phải thấy docker trong danh sách nhóm.

05

Bỏ qua daemon.json và trả giá bằng ổ đĩa

Không giới hạn log thì /var/lib/docker/containers/*/*-json.log lớn dần không có trần. Máy hết chỗ vào một đêm nào đó, và triệu chứng đầu tiên thường là dịch vụ khác chết chứ không phải Docker.

Cách sửaĐặt max-sizemax-file ngay lúc cài, kèm live-restore: true. Bảy dòng, một lần, dùng mãi.

10Kiểm tra

Kiểm tra hiểu bài

Năm câu, không giới hạn thời gian. Mỗi câu giải thích cả đáp án đúng lẫn vì sao các đáp án kia sai — đó mới là chỗ đổi được cách nghĩ.

Phiếu nghiệm thu · Tờ 020 / 5 câu
01Bạn chạy sudo systemctl restart docker trên máy có live-restore: true. Các container đang chạy vẫn phục vụ bình thường. Vì sao?Chọn 1
02Chọn tất cả khẳng định đúng về việc thêm một tài khoản vào nhóm docker.Chọn nhiều
03File compose.yml dùng với Docker Compose v2 cần khai khoá version: ở đầu file thì Compose mới hiểu đúng cú pháp.Đúng / Sai
04Chạy docker version, bạn chỉ thấy khối Client: rồi một dòng lỗi kết nối, không có khối Server:. Điều này cho biết gì?Chọn 1
05Trên một máy Ubuntu cài Docker bằng apt install docker.io từ kho của bản phân phối, điều nào sau đây có khả năng khác so với máy cài từ kho chính thức của Docker?Chọn nhiều
Nộp cả phiếu để chấm
Kiểm hết năm câu ở trên rồi nộp phiếu — máy chủ mới là nơi chấm điểm thật.
11Tóm tắt

Năm điều mang về

01

"Docker" là năm chương trình, không phải một. CLI gửi yêu cầu, dockerd nhận, containerd giữ kho và sổ, shim canh từng container, runc dựng rồi rút.

02

Trên Windows và macOS luôn có một máy ảo Linux ở dưới. Docker Desktop dựng nó cho bạn. Container Linux cần nhân Linux, không có ngoại lệ.

03

Kho cài quyết định bộ đồ nghề bạn có. Cùng phiên bản engine, khác plugin — và bạn chỉ biết lúc thiếu.

04

Nhóm docker là quyền root. Chấp nhận có ý thức, hoặc chuyển sang rootless. Đừng chấp nhận vì không biết.

05

Giới hạn log ngay lúc cài. Đây là việc rẻ nhất trong tờ này và là việc đắt nhất nếu bỏ qua.

Xong tờ này rồi?
Đăng nhập để lưu tiến độ vào tài khoản của bạn và mở khoá chứng nhận cuối khoá. Bài học vẫn đọc được đầy đủ khi chưa đăng nhập.
Đăng nhập để lưu tiến độ