Trước khi viết Dockerfile đầu tiên, có một thứ quan trọng hơn: hiểu Docker thực sự chạy như thế nào. docker ps treo 30 giây, docker-compose up một kiểu lỗi trên máy này nhưng máy kia lại chạy ổn — những chuyện này thường không phải do sai câu lệnh, mà do không rõ Docker client nói chuyện với ai, daemon nằm ở đâu, và container được tạo ra từ cái gì.

Docker không phải một process đơn lẻ, mà là một hệ thống client-server với nhiều thành phần phối hợp. Bài này vẽ ra bức tranh tổng thể đó.


  flowchart TB
    CLI["Docker CLI<br/>docker build/run/ps..."]
    API["Docker API<br/>REST over Unix socket<br/>hoặc TCP (TLS)"]
    containerd["containerd<br/>quản lý container lifecycle"]
    runc["runc<br/>tạo container từ OCI bundle"]
    CLI -->|"HTTP request"| API
    API --> containerd
    containerd --> runc

Engine: không phải một process

Nhiều người nghĩ “Docker” là một process duy nhất chạy container. Thực tế, Docker Engine là tập hợp nhiều thành phần:

Docker daemon (dockerd) lắng nghe API request, quản lý các đối tượng Docker (images, containers, networks, volumes). Đây là “não” — nó không tự chạy container, nó ủy quyền cho containerd.

containerd quản lý toàn bộ container lifecycle: pull image, tạo container, start/stop. Từ Docker Engine v29 (cuối 2025), containerd image store trở thành mặc định — image không còn lưu qua graph driver cũ nữa mà qua containerd’s snapshotter. Với Docker Desktop, thay đổi này đã có từ 2024.

runc là OCI runtime — lớp thấp nhất, tạo container từ OCI bundle (filesystem + config.json) và gọi Linux kernel namespace/cgroup. Mỗi container là một runc process con.

Khi bạn gõ docker run nginx:

  1. CLI gửi HTTP POST đến daemon socket
  2. Daemon bảo containerd “tạo container từ image nginx”
  3. containerd bảo runc tạo OCI bundle và gọi kernel
  4. runc tạo namespace, cgroup, mount rootfs → container chạy
AI agents cũng dùng Docker API. Docker Desktop 4.74+ có Gordon — AI agent tích hợp, dùng chính REST API này để debug container, tạo Dockerfile, kiểm tra log. Nếu bạn hiểu API model, bạn hiểu luôn cách AI agent tương tác với Docker.

Client: ai đang nói chuyện với daemon?

Docker CLI (docker) là client — nó không chạy container, nó chỉ gửi lệnh. Mặc định, CLI kết nối đến Unix socket /var/run/docker.sock trên Linux hoặc named pipe trên Windows.

Ba cách kết nối phổ biến:

# 1. Unix socket (mặc định, localhost)
docker ps
# Gửi request đến unix:///var/run/docker.sock

# 2. TCP với TLS (remote daemon)
docker -H tcp://192.168.1.100:2376 --tls ps

# 3. Docker context (quản lý nhiều endpoint)
docker context create remote --docker "host=tcp://192.168.1.100:2376"
docker context use remote
docker ps  # Tự động dùng context đã chọn

DOCKER_HOST environment variable cũng làm việc tương tự, nhưng docker context mạnh hơn — nó lưu endpoint, TLS config, và có thể switch nhanh giữa local, remote, cloud.

Lỗi “Cannot connect to the Docker daemon” thường gặp nhất là do Docker Desktop chưa start trên Windows/Mac, hoặc user hiện tại không có quyền đọc socket /var/run/docker.sock trên Linux. Thêm user vào group docker thì fix được — nhưng đừng làm vội, đọc tiếp phần socket security bên dưới.

Docker Context: quản lý nhiều Docker host

Khi bạn làm việc với nhiều môi trường (local dev, staging server, production node), Docker context cho phép switch endpoint không cần thay biến môi trường:

# Liệt kê context hiện có
$ docker context ls
NAME        TYPE        DESCRIPTION
default *   moby        Current DOCKER_HOST based configuration
staging     moby        Staging server at 10.0.1.50

# Switch sang staging
$ docker context use staging
# Bây giờ docker ps hiển thị container trên staging server

Context cũng tích hợp với cloud: docker context create ecs (AWS ECS), docker context create aci (Azure Container Instances). Bạn có thể chạy docker compose up và container được deploy lên cloud thay vì local.


Socket security: /var/run/docker.sock là chìa khóa vương quốc

Docker daemon chạy với quyền root (trừ rootless mode — sẽ bàn ở Phần 14). Unix socket /var/run/docker.sock cũng thuộc về root:docker group. Ai đọc được socket này có thể:

# Mount socket vào container
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
# Container có toàn quyền quản lý Docker trên host

Đây là một trong những anti-pattern nguy hiểm nhất — thấy nhiều trong CI pipeline và monitoring setup (kiểu “cần Docker trong Docker để build image”). Container có socket mount có thể:

  • Chạy docker run --privileged để escape container hoàn toàn
  • Đọc tất cả environment variable của container khác qua docker inspect
  • Pull/push image tùy ý

Thay vì mount socket, dùng:

  • BuildKit (mặc định từ Engine v23) — không cần Docker socket để build
  • Kaniko, Buildah — build image trong container không cần daemon
  • Docker-off-a-Docker pattern — mount socket chỉ khi build, xóa ngay sau đó
Docker Sandboxes (2026). Docker vừa ra mắt Sandboxes dùng microVM (VMM custom, chạy native trên Apple Hypervisor.framework / Windows Hypervisor Platform / Linux KVM). Mỗi agent được cấp Docker daemon riêng trong microVM, không mount socket — tường lửa thực sự giữa container workload và host. Đây là hướng đi mới cho AI agent chạy Docker an toàn.

Tổng kết

Docker không phải một process, mà là pipeline: CLI → daemon → containerd → runc → kernel. Mỗi lớp có trách nhiệm riêng, và hiểu pipeline này giúp bạn debug nhanh hơn rất nhiều khi có lỗi.

Ba điều cần nhớ từ bài này:

  1. Docker daemon là server, CLI là client — không kết nối được đến daemon thì mọi lệnh đều fail
  2. Docker context giúp quản lý nhiều endpoint gọn gàng, không cần nhớ IP với port
  3. Mount Docker socket vào container là lỗ hổng bảo mật — tránh pattern này, dùng giải pháp thay thế

Câu hỏi hay gặp

Docker Engine khác gì Docker Desktop?

Docker Engine là binary dockerd chạy trên Linux server, chỉ có CLI + daemon. Docker Desktop là ứng dụng GUI trên Windows/Mac, bao gồm Engine chạy trong VM Linux nhỏ, kèm theo Docker Compose, Kubernetes, Docker Scout, Gordon AI agent, và credential helper cho registry. Trên production, dùng Engine; trên máy dev, Desktop tiện hơn.

Làm sao biết CLI đang nói chuyện với daemon nào?

docker info --format '{{.Name}}'         # Tên host daemon
docker context ls                         # Context hiện tại (dấu *)
docker version --format '{{.Server.Version}}'  # Engine version

Có nên expose Docker API qua TCP không?

Chỉ nếu bắt buộc, và luôn bật TLS. Mặc định Docker API không authenticate. Expose port 2375 (không TLS) ra internet đồng nghĩa với việc tặng root access cho cả thế giới. Nếu cần remote access, dùng SSH tunnel thay vì mở port:

ssh -NL 2375:/var/run/docker.sock user@remote
# Giờ docker -H localhost:2375 ps sẽ chạy trên remote qua SSH

Docker context lưu config ở đâu?

~/.docker/contexts/ — mỗi context là một thư mục chứa meta.json và TLS certificates. Context default dùng DOCKER_HOSTDOCKER_TLS_VERIFY env vars, không có folder riêng.


Bài tiếp theo (Nền móng): Phần 2: Namespace & cgroup: nền móng container, đi sâu vào kernel primitives — PID namespace, network namespace, cgroup v2, và tại sao container không phải “máy ảo nhẹ”.