---
id: docker-concepts
title: "Container và Docker"
shortTitle: "Container và Docker"
track: docker
trackTitle: "Docker"
order: 1
level: core
status: published
estimatedMinutes: 25
prerequisites:
  - networking-cli-tools
next: docker-cli
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "Docker overview"
    url: https://docs.docker.com/get-started/docker-overview/
  - title: "Install Docker Engine on Ubuntu"
    url: https://docs.docker.com/engine/install/ubuntu/
  - title: "Linux post-installation steps"
    url: https://docs.docker.com/engine/install/linux-postinstall/
  - title: "Install Docker Desktop on Windows"
    url: https://docs.docker.com/desktop/setup/install/windows-install/
---

# Container và Docker

App Node chạy trên laptop vì máy đó đã có đúng bản Node và đủ module. Copy thư mục sang máy khác, thiếu một thứ là app vỡ. Container đóng process cùng phần filesystem nó cần, để máy đích chỉ cần Docker.

Bài này chưa dạy lệnh vận hành. Bài sau mới `docker run`.

## Để làm gì

Phân biệt container với VM, image với container, và biết lệnh `docker` chỉ là client gửi việc cho daemon.

## Gồm những gì

- Container dùng kernel của host. VM có kernel riêng.
- Client `docker` và daemon `dockerd`.
- Linux: gói `docker-ce` + `docker-compose-plugin`. `systemctl status` / `start` / `enable docker`.
- Image, container, registry.

Dockerfile, volume, Compose nằm ở các bài sau.

## Dùng khi nào

Trước khi gõ lệnh, hoặc khi ai đó nói "cài Docker là app chạy" mà không nói image nào và tag nào.

Một container trên một laptop chưa phải Kubernetes. Nhiều máy, tự thay bản hỏng, là chuyện của bài orchestration.

## Kiến thức cốt lõi

Container không phải máy ảo thu nhỏ. VM có hypervisor và một kernel khách, nên boot lâu và nặng. Container dùng kernel của host. Namespace làm process, mạng và filesystem bên trong nhìn như một máy riêng. Cgroup đặt trần CPU và RAM. Container lên trong vài giây vì không boot kernel mới. Process trong container vẫn là process trên kernel host, nên container không thay một biên an ninh kiểu VM.

Docker chia hai phần. Lệnh `docker` là client. Nó gửi API tới daemon `dockerd`. Daemon mới tạo image, container, network và volume. Trên Windows, Docker Desktop chạy daemon Linux trong máy ảo WSL2. Bạn gõ lệnh ở PowerShell, container chạy kernel Linux bên trong WSL2. `docker version` in hai khối, Client và Server. Không có Server nghĩa là daemon chưa chạy.

Image là bản read-only, xếp bằng nhiều layer. Container là một lần chạy của image, thêm một layer ghi ở trên. Sửa file trong container không đụng image. Xóa container là mất phần ghi đó, trừ khi dữ liệu đã nằm trên volume.

Registry là kho image, không phải chỗ app đang chạy. Docker Hub là registry mặc định. Tên `nginx` là dạng ngắn của `docker.io/library/nginx`. Tag sau dấu hai chấm chọn một bản, ví dụ `nginx:1.27-alpine`. Bỏ tag thì Docker lấy `latest`, và `latest` đổi khi có bản mới được đẩy lên.

`docker run` nối các bước đó: máy chưa có image thì pull từ registry, tạo container, rồi start. Bài sau tách từng lệnh để nhìn từng bước.

Trên Linux server, cài Docker Engine từ repo của Docker, không lấy gói `docker.io` của distro. Docs Ubuntu liệt kê `docker.io` (và vài gói khác) là bản không chính thức, phải gỡ trước vì xung đột. Gói chính thức: `docker-ce`, `docker-ce-cli`, `containerd.io`, `docker-buildx-plugin`, `docker-compose-plugin`. Compose hiện là plugin CLI; lệnh là `docker compose`, không phải binary `docker-compose` cũ.

Sau `apt install`, docs bảo `systemctl status docker`. Chưa chạy thì `systemctl start docker`. Ubuntu/Debian: unit thường đã enable lúc cài. Distro khác: `systemctl enable docker.service` (và `containerd.service`) để lên cùng boot. Script `https://get.docker.com` chỉ cho máy test/dev, không dùng production: không tùy biến được, kéo bản stable mới nhất.

`docker version` có khối Server, hoặc `systemctl is-active docker` in `active`, mới là daemon sống. Cài xong mà chưa start thì mọi `docker run` sau fail.

## Lệnh và code mẫu

Chỉ kiểm tra daemon, chưa pull image:

```bash
docker version
```

Trên PowerShell lệnh vẫn là `docker`. Báo không tìm thấy lệnh thì Docker Desktop chưa cài, hoặc chưa có trong PATH. Docs cài trên Windows chọn backend WSL2. Docker Desktop không được hỗ trợ trên Windows Server.

Linux lab (repo đã cấu hình, chỉ cài gói rồi bật service):

```bash
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl status docker
sudo systemctl start docker
sudo systemctl enable docker.service
docker version
```

`enable` trên Ubuntu thường thừa vì docs nói service đã start lúc boot. Vẫn chạy được. Chưa có repo Docker thì làm đúng trang [Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/), đừng `apt install docker.io`.

## Thực hành

1. Chạy `docker version`.
2. Tìm khối Server. Không có thì mở Docker Desktop, đợi engine lên, chạy lại.
3. Viết một câu: container dùng kernel host, VM có kernel riêng.
4. Viết một câu: registry giữ image, daemon mới chạy container.
5. Trên Linux: `systemctl is-active docker`. Inactive thì `start`. Đừng cài `docker.io`.

## Kết quả mong đợi

`docker version` có cả Client và Server. Chưa có container mới nào do bài này tạo.

## Checklist

- [ ] Nhìn `docker version` và biết daemon có đang chạy không.
- [ ] Giải thích container khác VM ở chỗ kernel.
- [ ] Gọi đúng thứ: image là khuôn, container là lần chạy, registry là kho.
- [ ] Linux: `docker-ce` + plugin Compose. `systemctl start docker` nếu status chưa active. Không cài `docker.io`.

## Thẻ ôn

### docker-concepts-kernel
kind: distinguish
cue: Container dùng kernel của máy host. VM thì có kernel riêng, nên biên an ninh không giống nhau.

Q: Bạn đang chọn cách đóng gói một app. Đồng nghiệp nói container cũng là một máy ảo nhỏ, chỉ nhẹ hơn. Hai thứ khác nhau ở chỗ nào, và vì sao không được coi container như một biên an ninh kiểu VM?

A: Container không mang kernel riêng. Process bên trong vẫn là process chạy trên kernel của máy host, chỉ bị gom vào một nhóm namespace và cgroup. VM thì có kernel riêng, hypervisor đứng giữa VM và máy host. Vì vậy container nhẹ hơn và khởi động nhanh hơn, nhưng nó không thay lớp cách ly của VM. Một process thoát khỏi container vẫn đang ở trên kernel host.

### docker-concepts-roles
kind: distinguish
cue: Image là khuôn read-only. Container là một lần chạy. Registry chỉ là kho chứa image.

Q: Trong một pipeline, người ta nói "đẩy image lên registry" rồi "chạy container". Ba thứ image, container và registry mỗi thứ đang giữ cái gì, và chỗ nào là app đang chạy?

A: Image là bản read-only, xếp từ nhiều layer. Nó là khuôn, chưa phải process đang chạy. Container là một lần chạy của image đó: Docker thêm một layer ghi ở trên, process sống trong lần chạy này. Registry là kho để máy khác kéo image về, không phải nơi app nhận request. Xóa container không xóa image. Xóa image trên máy không gỡ bản đang nằm trên registry.

### docker-concepts-daemon
kind: trap
cue: `docker version` chỉ còn khối Client nghĩa là daemon chưa chạy. Lệnh sau sẽ không nối được engine.

Q: Bạn mở terminal, gõ `docker version`, và chỉ thấy khối Client, không thấy khối Server. Lệnh `docker run` ngay sau đó sẽ ra sao, và việc đầu cần làm là gì?

A: Docker CLI chỉ là client. Việc tạo container do daemon, tức engine, đảm nhiệm. Khi engine tắt, client không có chỗ để nối, nên lệnh sau báo không kết nối được Docker daemon, hoặc treo chờ. Trên máy dev, mở Docker Desktop và đợi engine lên hẳn, rồi chạy lại `docker version` cho đến khi thấy cả Client lẫn Server.

### docker-concepts-latest
kind: default
cue: Không ghi tag thì Docker lấy `latest`, và tag đó đổi mỗi khi có bản mới được đẩy lên.

Q: Một lab viết `docker pull nginx` mà không ghi tag. Hôm nay máy A kéo về chạy được. Tuần sau máy B kéo cùng dòng lệnh đó. Vì sao hai máy có thể không chạy cùng một bản, và lab trong lộ trình xử lý chỗ này thế nào?

A: Khi không ghi tag, Docker hiểu là tag `latest`. Đó không phải "bản ổn định mãi mãi". Ai đẩy image mới và gắn lại tag `latest` thì lần pull sau nhận bản khác, dù câu lệnh không đổi. Hai lần pull cách nhau có thể ra hai image. Lab pin tag cụ thể để mọi người và mọi lần chạy lấy đúng một bản.

### docker-concepts-one-process
kind: trap
cue: Một container có một process chính. Process đó chết thì container dừng, nên đừng nhét nhiều service vào như một VM.

Q: Có người viết một container chạy systemd, bên trong vừa Nginx vừa database, vì nghĩ container là một VM nhỏ. Process chính của container chết thì chuyện gì xảy ra với các service còn lại?

A: Docker theo dõi một process chính. Process đó thoát, container chuyển sang dừng, dù bên trong còn process khác. Container không được thiết kế để thay một máy có init system nuôi nhiều service. Mỗi process chính nên là một container. Nhiều service thì nhiều container, rồi cho chúng nói chuyện qua network, không gom hết vào một container rồi cài systemd.

### docker-concepts-install-start
kind: command
cue: Linux: `docker-ce` + `docker-compose-plugin`, không `docker.io`. Cài xong `systemctl status docker`; chưa chạy thì `start`.

Q: Máy Ubuntu lab cần Docker Engine và Compose. Cài gói nào, gói distro nào phải tránh, và lệnh nào chứng minh daemon đã sống trước khi `docker run`?

A: Cài từ repo Docker: `docker-ce`, `docker-ce-cli`, `containerd.io`, `docker-buildx-plugin`, `docker-compose-plugin`. Docs Ubuntu liệt kê `docker.io` là gói không chính thức, xung đột — gỡ trước, đừng `apt install docker.io`. Compose là plugin: lệnh `docker compose`. Sau cài: `systemctl status docker`. Chưa chạy thì `systemctl start docker`. Ubuntu thường đã enable lúc boot; distro khác thêm `systemctl enable docker.service`. `docker version` phải có khối Server. Script `get.docker.com` chỉ cho máy thử, không phải production.

## Lỗi thường gặp

**Nhét systemd và vài service vào một container vì tưởng nó là VM.** Container có một process chính. Process đó chết là container dừng. Nhiều process thì nhiều container.

**Chỉ thấy Client trong `docker version`.** Daemon tắt. Lệnh sau báo không nối được Docker daemon, hoặc treo. Mở Docker Desktop rồi thử lại.

**Không ghi tag, mặc định `latest`.** Hai lần pull cách nhau một tuần có thể ra hai image khác nhau. Lab trong lộ trình pin tag.

**`apt install docker.io` rồi bảo đã cài Docker.** Đó là gói distro, không phải `docker-ce`. Docs Ubuntu bảo gỡ vì xung đột với bản chính thức.

**Cài xong, `docker version` chỉ còn Client.** Daemon chưa start. `systemctl start docker`, rồi kiểm tra lại.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày Install Docker Packages And Start Docker Service. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [KodeKloud — Crash Course: Docker For Absolute Beginners](https://learn.kodekloud.com/learn/courses/crash-course-docker-for-absolute-beginner) — phạm vi và thứ tự: container, Docker hoạt động thế nào, container khác VM, image và registry.
3. [Docker overview](https://docs.docker.com/get-started/docker-overview/) — client, daemon, image, container, registry.
4. [Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/) — `docker-ce`, không `docker.io`; `systemctl status` / `start`; `get.docker.com` không dùng production.
5. [Linux post-installation](https://docs.docker.com/engine/install/linux-postinstall/) — `systemctl enable docker.service`.
6. [Install Docker Desktop on Windows](https://docs.docker.com/desktop/setup/install/windows-install/) — backend WSL2, và Desktop không chạy trên Windows Server.

## Tiếp theo

[Docker CLI](./01-cli.md)
