jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Docker for Developers · Part 1 — Containers, Images & the Mental Model

Start Docker from zero: what a container really is (vs a VM), how images and layers work, the core docker run workflow, and your first hands-on container — with exercises to drill the commands.

This is Part 1 of a 10-part series that takes you from “I’ve heard of Docker” to confidently building, running, and debugging containerized apps — Docker, Docker Compose, and Kubernetes {Đây là Phần 1 của series 10 bài đưa bạn từ “mới nghe nói về Docker” đến tự tin build, chạy và debug ứng dụng container — Docker, Docker Compose và Kubernetes}. Every part ends with exercises; do them, don’t just read {Mỗi phần đều kết thúc bằng bài tập; hãy làm, đừng chỉ đọc}.

The problem Docker solves is the oldest joke in software {Vấn đề Docker giải quyết là câu đùa lâu đời nhất trong ngành phần mềm}: “but it works on my machine” {“nhưng máy tôi chạy được mà”}. Docker packages your app with its entire environment so the box that runs on your laptop is the exact same box that runs in production {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}.


1. What is a container? {Container là gì?}

A container is an isolated process (or group of processes) running on your machine, tricked into believing it has its own filesystem, network, and process list {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}. It is not a virtual machine {Nó không phải một máy ảo}.

The difference matters {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)

A VM virtualizes hardware and ships a whole guest operating system {Một VM ảo hóa phần cứng và mang theo cả một hệ điều hành khách}. A container virtualizes the operating system — all containers share the host kernel, so they’re tiny and start almost instantly {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ì}.

Under the hood on Linux, containers are just processes isolated with namespaces (what a process can see) and limited with cgroups (what a process can use) {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)}. On macOS/Windows, Docker runs these inside a lightweight Linux VM for you {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 — the key distinction {Image vs Container — phân biệt cốt lõi}

These two words confuse every beginner, so anchor them now {Hai từ này làm rối mọi người mới, nên hãy nắm chắc ngay}:

  • An image is a read-only template — your app plus its dependencies, frozen {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”}.
  • A container is a running instance of an image {Một container là một thực thể đang chạy của một image}.

If you know OOP, it’s exactly class vs object {Nếu bạn biết OOP, đây chính là class vs object}: one image → many containers {một image → nhiều container}.

  IMAGE  (a recipe, on disk)            CONTAINERS (running dishes)
 ┌─────────────────────┐               ┌──────────┐  docker run
 │  node:20-alpine     │   ── run ──►  │ web #1   │
 │  + your app code    │               ├──────────┤
 │  + npm install      │   ── run ──►  │ web #2   │
 │  (read-only layers) │               ├──────────┤
 └─────────────────────┘   ── run ──►  │ web #3   │
                                       └──────────┘

Images are built from layers {Image được build từ các layer}

An image is a stack of read-only layers {Một image là một chồng các layer chỉ-đọc}. Each instruction in a build adds a layer {Mỗi chỉ thị khi build thêm một layer}. When you run a container, Docker adds a thin writable layer on top {Khi bạn chạy container, Docker thêm một layer ghi-được mỏng lên trên}. This layering is why images are small to share (layers are cached and reused) and why containers are cheap to start {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}.


3. Install & verify {Cài đặt & kiểm tra}

Install Docker Desktop (macOS/Windows) or Docker Engine (Linux) from docker.com {Cài Docker Desktop (macOS/Windows) hoặc Docker Engine (Linux)}. Then verify {Sau đó kiểm tra}:

docker --version          # client version
docker info               # is the daemon running?
docker run hello-world    # the canonical smoke test

If docker run hello-world prints a friendly message, your installation works end to end {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}. If you get Cannot connect to the Docker daemon, the engine isn’t running — start Docker Desktop (or sudo systemctl start docker on Linux) {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. Your first real container {Container thật đầu tiên}

Let’s run a web server — the nginx image — and visit it in a browser {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

Open http://localhost:8080 and you’ll see the nginx welcome page {Mở http://localhost:8080 và bạn sẽ thấy trang chào mừng của nginx}. You just {Bạn vừa}: pulled the nginx image from Docker Hub, started a container from it, and mapped your port 8080 to its port 80 — without installing nginx on your machine {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}.

What just happened, step by step {Đ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

Behind that one command, the Docker client talks to the Docker daemon (the background engine), which pulls images from a registry and runs them as containers {Đằ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. The core workflow & essential commands {Quy trình lõi & các lệnh thiết yếu}

Ninety percent of daily Docker is this handful of commands {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

The lifecycle is a simple state machine {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 is your best friend for debugging {exec là bạn thân nhất khi debug}. docker exec -it web sh drops you into a shell inside the container so you can inspect files, env vars, and network from its point of view {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ó}. We’ll lean on this heavily in Part 8 {Chúng ta sẽ dùng nó rất nhiều ở Phần 8}.


6. Anatomy of docker run — the flags you’ll actually use {Giải phẫu docker run — các flag bạn thực sự dùng}

FlagWhat it does {Tác dụng}
-d, --detachRun in the background {Chạy nền}
-p host:containerPublish a port {Mở/ánh xạ cổng}
--name xName the container (else you get a random name) {Đặt tên container (nếu không sẽ có tên ngẫu nhiên)}
-itInteractive + TTY — for shells {Tương tác + TTY — cho shell}
--rmAuto-remove the container when it exits {Tự xóa container khi nó thoát}
-e KEY=valSet an environment variable {Đặt biến môi trường}
-v host:containerMount a volume / folder (Part 3) {Gắn volume / thư mục (Phần 3)}

A throwaway interactive container is great for experimenting {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. Common beginner pitfalls {Các bẫy thường gặp của người mới}

  • port is already allocated — something else uses that host port. Pick another: -p 8081:80 {cổng host đang bị dùng. Chọn cổng khác: -p 8081:80}.
  • The container “exits immediately” — a container lives only as long as its main process {container chỉ sống khi tiến trình chính còn chạy}. docker run ubuntu exits at once because bash has nothing to do; add -it to keep it interactive {docker run ubuntu thoát ngay vì bash không có việc gì; thêm -it để giữ tương tác}.
  • Confusing stop and rmstop pauses; rm deletes. docker ps -a shows the stopped ones still taking space {stop là tạm dừng; rm là xóa. docker ps -a cho thấy các container đã dừng vẫn chiếm chỗ}.
  • Editing files inside a container and losing them — the writable layer dies with the container. Persisting data is Part 3 {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}.

Cheat sheet {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

Do these in order; each builds the muscle memory you’ll need later {Làm theo thứ tự; mỗi bài rèn phản xạ bạn sẽ cần về sau}.

1. Run an nginx container named myweb in the background on host port 9000, confirm it’s running, then open it in your browser {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}.

Solution {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. Open a shell inside myweb, find the nginx welcome file at /usr/share/nginx/html/index.html, and print its contents {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}.

Solution {Lời giải}
docker exec -it myweb sh
# inside the container:
cat /usr/share/nginx/html/index.html
exit

3. Follow myweb’s logs live, refresh the page in your browser a few times, and watch the access logs appear {Theo dõi log của myweb trực tiếp, refresh trang vài lần và xem access log hiện ra}.

Solution {Lời giải}
docker logs -f myweb
# refresh http://localhost:9000 — each request shows a log line. Ctrl+C to stop following.

4. Start a throwaway Ubuntu container, install nothing, just run cat /etc/os-release to prove it’s a different OS userland than your host, then let it auto-remove {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}.

Solution {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. Clean up: stop and remove myweb, then confirm it’s gone from docker ps -a {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}.

Solution {Lời giải}
docker stop myweb
docker rm myweb
docker ps -a              # myweb should no longer appear

Stretch {Nâng cao}: run two nginx containers at once on ports 9000 and 9001 from the same image — proof that one image backs many containers {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}.


Key takeaways {Điểm chính}

  • A container is an isolated process sharing the host kernel — lighter and faster than a VM {Container là một tiến trình bị cô lập dùng chung kernel host — nhẹ và nhanh hơn VM}.
  • Image = template (class); container = running instance (object) {Image = khuôn mẫu (class); container = thực thể đang chạy (object)}.
  • Master run, ps, logs, exec, stop, rm — they cover most daily work {Thành thạo run, ps, logs, exec, stop, rm — chúng bao phủ phần lớn công việc}.
  • A container lives only as long as its main process {Container chỉ sống khi tiến trình chính còn chạy}.

Next up {Tiếp theo}

Part 2 — Images & the Dockerfile: you’ll stop using other people’s images and build your own — layers, the build cache, multi-stage builds, and .dockerignore {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}.