Docker for Developers · Part 1 — Containers, Images & the Mental Model
Hiểu đúng container, image, layer và vòng đời docker run; so sánh với VM, thực hành container đầu tiên và các lệnh cốt lõi.
Đây là Phần 1 của chặng nền tảng đưa bạn từ “mới nghe Docker” đến tự tin build, chạy, harden và debug ứng dụng container qua Docker → Compose → Kubernetes. Mỗi phần đều kết thúc bằng bài tập; hãy làm, đừng chỉ đọc.
Vấn đề Docker giải quyết là câu đùa lâu đời nhất trong ngành phần mềm: “nhưng máy tôi chạy được mà”. Docker đóng gói ứng dụng cùng toàn bộ môi trường của nó, để cái “hộp” chạy trên laptop của bạn cũng chính là cái hộp chạy trên production.
Mô hình xuyên suốt series: image là artifact release bất biến, container là process runtime có thể thay thế, volume/network/config là hợp đồng vận hành. Khi bạn nghĩ theo ba lớp đó, Docker không còn là mớ flag rời rạc; nó trở thành cách đóng gói và vận hành phần mềm có kỷ luật.
1. Container là gì?
Một container là một tiến trình (hoặc nhóm tiến trình) bị cô lập chạy trên máy bạn, được “đánh lừa” rằng nó có hệ thống file, mạng và danh sách tiến trình riêng. Nó không phải một máy ảo.
Sự khác biệt này quan trọng:
Một VM ảo hóa phần cứng và mang theo cả một hệ điều hành khách. Một container ảo hóa hệ điều hành — mọi container dùng chung kernel của host, nên chúng rất nhẹ và khởi động gần như tức thì.
Bên dưới trên Linux, container chỉ là tiến trình được cô lập bằng namespaces (tiến trình nhìn thấy gì) và giới hạn bằng cgroups (tiến trình được dùng bao nhiêu). Trên macOS/Windows, Docker chạy chúng bên trong một VM Linux nhẹ giúp bạn.
2. Image vs Container — phân biệt cốt lõi
Hai từ này làm rối mọi người mới, nên hãy nắm chắc ngay:
- Một image là khuôn mẫu chỉ-đọc — ứng dụng của bạn cộng với các phụ thuộc, đã “đóng băng”.
- Một container là một thực thể đang chạy của một image.
Nếu bạn biết OOP, đây gần giống class vs object: một image → nhiều container. Điểm khác biệt quan trọng là image không chỉ là code; nó còn chứa filesystem, dependency, default command và metadata chạy.
IMAGE (a recipe, on disk) CONTAINERS (running dishes)
┌─────────────────────┐ ┌──────────┐ docker run
│ node:24-alpine │ ── run ──► │ web #1 │
│ + your app code │ ├──────────┤
│ + npm install │ ── run ──► │ web #2 │
│ (read-only layers) │ ├──────────┤
└─────────────────────┘ ── run ──► │ web #3 │
└──────────┘
Image được build từ các layer
Một image là một chồng các layer chỉ-đọc. Các chỉ thị làm thay đổi filesystem tạo nội dung layer; instruction chỉ đổi metadata không nhất thiết tạo filesystem layer mới. Khi chạy container, runtime thêm một layer ghi-được mỏng lên trên. Cơ chế phân lớp này là lý do image nhỏ gọn để chia sẻ (layer được cache và tái dùng) và container rẻ để khởi động.
Góc nhìn senior: đừng coi layer ghi-được là nơi lưu dữ liệu nghiệp vụ. Nó là scratch space của một process. Dữ liệu cần sống qua redeploy phải đi qua volume, database hoặc external storage — ta xử lý ở Phần 3.
3. Cài đặt & kiểm tra
Cài Docker Desktop (macOS/Windows) hoặc Docker Engine (Linux). Sau đó kiểm tra:
docker --version # client version
docker info # is the daemon running?
docker run hello-world # the canonical smoke test
Nếu docker run hello-world in ra một thông điệp thân thiện, cài đặt của bạn hoạt động trọn vẹn. Nếu gặp Cannot connect to the Docker daemon, engine chưa chạy — mở Docker Desktop (hoặc sudo systemctl start docker trên Linux).
4. Container thật đầu tiên
Hãy chạy một web server — image nginx — và mở nó trong trình duyệt:
docker run --name web -d -p 8080:80 nginx
Mở http://localhost:8080 và bạn sẽ thấy trang chào mừng của nginx. Bạn vừa: kéo image nginx từ Docker Hub, khởi động một container từ nó, và ánh xạ cổng 8080 của bạn vào cổng 80 của nó — mà không cần cài nginx lên máy.
Điều vừa xảy ra, từng bước:
docker run --name web -d -p 8080:80 nginx
│ │ │ └── image to use (pulled if missing)
│ │ └──────────── publish host:container port
│ └─────────────── detached (run in background)
└─────────────────────────── give the container a name
Đằng sau một lệnh đó, client Docker nói chuyện với daemon Docker (engine chạy nền), daemon kéo image từ registry và chạy chúng thành container:
5. Quy trình lõi & các lệnh thiết yếu
90% công việc Docker hằng ngày là nhúm lệnh này:
docker ps # list RUNNING containers
docker ps -a # list ALL containers (incl. stopped)
docker images # list local images
docker pull <image> # download an image
docker logs web # see a container's stdout/stderr
docker logs -f web # ...and follow it live
docker exec -it web bash # open a shell INSIDE the running container
docker stop web # graceful stop (SIGTERM)
docker start web # start it again
docker rm web # remove a stopped container
docker rmi nginx # remove an image
Vòng đời là một máy trạng thái đơn giản:
docker run docker stop docker rm
(create+start) ───────► running ──────────► stopped ────────► (gone)
▲ │
└─── docker start ──────┘
execlà bạn thân nhất khi debug.docker exec -it web shđưa bạn vào một shell bên trong container để xem file, biến môi trường và mạng từ góc nhìn của nó. Chúng ta sẽ dùng nó rất nhiều ở Phần 8.
6. Giải phẫu docker run — các flag bạn thực sự dùng
| Flag | Tác dụng |
|---|---|
-d, --detach | Chạy nền |
-p host:container | Mở/ánh xạ cổng |
--name x | Đặt tên container (nếu không sẽ có tên ngẫu nhiên) |
-it | Tương tác + TTY — cho shell |
--rm | Tự xóa container khi nó thoát |
-e KEY=val | Đặt biến môi trường |
-v host:container | Gắn volume / thư mục (Phần 3) |
Một container tương tác dùng-một-lần rất tốt để thử nghiệm:
docker run --rm -it ubuntu bash
# you're now inside a fresh Ubuntu; type `exit` and it's gone
7. Các bẫy thường gặp của người mới
- cổng host đang bị dùng. Chọn cổng khác:
-p 8081:80. - container chỉ sống khi tiến trình chính còn chạy.
docker run ubuntuthoát ngay vìbashkhông có việc gì; thêm-itđể giữ tương tác. stopgửi tín hiệu để tiến trình kết thúc có kiểm soát;pausemới là đóng băng tạm thời.rmxóa container.docker ps -acho thấy container đã dừng vẫn chiếm chỗ.- Sửa file trong container rồi mất — layer ghi-được chết cùng container. Lưu dữ liệu bền vững là Phần 3.
Bảng tra nhanh
# run & inspect
docker run --name app -d -p 8080:80 nginx
docker ps docker ps -a
docker logs -f app docker exec -it app sh
docker stats docker inspect app
# lifecycle & cleanup
docker stop app && docker rm app
docker images docker rmi <image>
docker system prune # remove unused containers/images/networks
Bài tập / Exercises
Làm theo thứ tự; mỗi bài rèn phản xạ bạn sẽ cần về sau.
1. Chạy một container nginx tên myweb chạy nền ở cổng host 9000, xác nhận nó đang chạy, rồi mở trong trình duyệt.
Lời giải
docker run --name myweb -d -p 9000:80 nginx
docker ps # see myweb in the list
# visit http://localhost:90002. Mở shell bên trong myweb, tìm file chào mừng tại /usr/share/nginx/html/index.html và in nội dung.
Lời giải
docker exec -it myweb sh
# inside the container:
cat /usr/share/nginx/html/index.html
exit3. Theo dõi log của myweb trực tiếp, refresh trang vài lần và xem access log hiện ra.
Lời giải
docker logs -f myweb
# refresh http://localhost:9000 — each request shows a log line. Ctrl+C to stop following.4. Khởi động một container Ubuntu dùng-một-lần, chạy cat /etc/os-release để chứng minh nó là userland OS khác với host, rồi để nó tự xóa.
Lời giải
docker run --rm -it ubuntu cat /etc/os-release
# prints Ubuntu's release info, then the container is removed automatically5. Dọn dẹp: dừng và xóa myweb, rồi xác nhận nó biến mất khỏi docker ps -a.
Lời giải
docker stop myweb
docker rm myweb
docker ps -a # myweb should no longer appearNâng cao: Chạy hai container nginx cùng lúc ở cổng 9000 và 9001 từ cùng một image — bằng chứng một image làm nền cho nhiều container.
Điểm chính
- Container là một tiến trình bị cô lập dùng chung kernel host — nhẹ và nhanh hơn VM.
- Image = khuôn mẫu (class); container = thực thể đang chạy (object).
- Thành thạo
run,ps,logs,exec,stop,rm— chúng bao phủ phần lớn công việc. - Container chỉ sống khi tiến trình chính còn chạy.
Tiếp theo
Phần 2 — Image & Dockerfile: bạn sẽ ngừng dùng image của người khác và tự build image của mình — layer, build cache, multi-stage build và .dockerignore.