jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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.

8 MIN READ Updated JUL 11, 2026

Đâ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:

VIRTUAL MACHINES CONTAINERS Hardware Host OS Hypervisor App Bins/Libs Guest OS App Bins/Libs Guest OS App Bins/Libs Guest OS Hardware Host OS Docker Engine (shared kernel) App Bins/Libs App Bins/Libs App Bins/Libs
VMs ship a full guest OS each (heavy: GBs, boots in minutes); containers share the host kernel (light: MBs, start in ms)

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 imagekhuô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:

Docker CLI docker build docker run / pull REST API Docker daemon (dockerd) Images Containers (running) run Registry Docker Hub pull / push
The CLI sends commands to the daemon over a REST API; the daemon manages images & containers and pulls/pushes to a registry

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 ──────┘

exec là 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

FlagTác dụng
-d, --detachChạy nền
-p host:containerMở/ánh xạ cổng
--name xĐặt tên container (nếu không sẽ có tên ngẫu nhiên)
-itTương tác + TTY — cho shell
--rmTự xóa container khi nó thoát
-e KEY=valĐặt biến môi trường
-v host:containerGắ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 ubuntu thoát ngay vì bash không có việc gì; thêm -it để giữ tương tác.
  • stop gửi tín hiệu để tiến trình kết thúc có kiểm soát; pause mới là đóng băng tạm thời. rm xóa container. docker ps -a cho 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:9000

2. 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
exit

3. 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 automatically

5. 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 appear

Nâng cao: Chạy hai container nginx cùng lúc ở cổng 90009001 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.