---
id: docker-cli
title: "Docker CLI"
track: docker
trackTitle: "Docker"
order: 2
level: core
status: published
estimatedMinutes: 55
prerequisites:
  - docker-concepts
next: docker-dockerfile
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "docker container run"
    url: https://docs.docker.com/reference/cli/docker/container/run/
  - title: "docker container ps"
    url: https://docs.docker.com/reference/cli/docker/container/ps/
  - title: "docker container stop"
    url: https://docs.docker.com/reference/cli/docker/container/stop/
  - title: "docker container logs"
    url: https://docs.docker.com/reference/cli/docker/container/logs/
  - title: "docker container exec"
    url: https://docs.docker.com/reference/cli/docker/container/exec/
  - title: "docker container rm"
    url: https://docs.docker.com/reference/cli/docker/container/rm/
  - title: "docker image rm"
    url: https://docs.docker.com/reference/cli/docker/image/rm/
  - title: "docker image pull"
    url: https://docs.docker.com/reference/cli/docker/image/pull/
  - title: "docker image tag"
    url: https://docs.docker.com/reference/cli/docker/image/tag/
  - title: "docker container cp"
    url: https://docs.docker.com/reference/cli/docker/container/cp/
  - title: "docker container commit"
    url: https://docs.docker.com/reference/cli/docker/container/commit/
---

# Docker CLI

Bài trước nói image, container và daemon. Bài này đi các lệnh dùng hàng ngày, kèm option hay gặp. Tag `nginx:1.27-alpine` được pin cho lab. Tag đang có trên Hub nằm ở [Docker Hub — nginx](https://hub.docker.com/_/nginx).

Cú pháp chung: `docker <lệnh> [OPTIONS] <đối tượng> [COMMAND] [ARG...]`. Option đứng trước tên container hoặc image. Phần sau tên container là lệnh và argument chạy bên trong, không phải option của Docker.

## Để làm gì

Chạy một image có sẵn, lọc đúng container, đọc log, vào filesystem khi process chính còn sống, dừng đúng tên, xóa container rồi mới xóa image.

## Gồm những gì

- `docker run`, `ps`, `stop`, `logs`, `exec`, `rm`, `rmi`, `pull`, `tag`.
- `-p host:container`: số host trước.
- `docker cp` hai chiều. Container chạy hoặc đã dừng đều được. Không tự tạo thư mục cha.
- `docker commit` chụp layer ghi thành image. Volume không vào. Không thay Dockerfile.
- Option hay dùng của từng lệnh, và chỗ đặt argument.
- Dockerfile và Compose có bài riêng. `docker system prune` xóa nhiều thứ một lúc, không dùng trong lab này.

## Dùng khi nào

Cần một service tạm trên máy dev, muốn biết container vừa thoát vì sao, hoặc chạy một lệnh bên trong filesystem của container.

Đừng `docker rm` database khi dữ liệu còn nằm trong layer ghi. Dữ liệu cần giữ thì gắn volume, bài đó ở sau.

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

`docker run hello-world` làm trọn một vòng. Máy chưa có image thì daemon pull từ Docker Hub, tạo container, chạy, in vài dòng rồi thoát. `docker ps` lúc này trống vì nó chỉ liệt kê container đang chạy. `docker ps -a` vẫn còn dòng `Exited`. Container thoát không tự biến mất, trừ khi lúc chạy có `--rm`.

### `docker run` — tạo rồi start

```text
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
```

`IMAGE` là khuôn. `COMMAND` và `ARG` ghi đè lệnh mặc định của image, không phải option của Docker.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `--name` | tên | Đặt tên cố định. Lệnh sau khỏi dùng ID |
| `-d`, `--detach` | không | Trả terminal ngay, process vẫn chạy |
| `-p`, `--publish` | `host:container` | Host nhận TCP, chuyển vào port trong container |
| `--rm` | không | Xóa container khi nó dừng |
| `-e`, `--env` | `KEY=value` | Biến môi trường cho process chính |
| `-i`, `--interactive` | không | Giữ stdin. Cần khi lệnh đọc input |
| `-t`, `--tty` | không | Cấp terminal. Ghép `-it` khi mở shell |
| `-w`, `--workdir` | đường dẫn | Thư mục làm việc lúc start |

`--publish` ghi port trên host trước, port trong container sau. Nginx trong image nghe `80`. Trên máy dev mở `http://127.0.0.1:8080`. Publish không giúp container khác gọi nhau. Đó là bài network.

```bash
docker run --name lab-nginx -d -p 8080:80 nginx:1.27-alpine
docker run --rm -e GREETING=hello alpine:3.20 printenv GREETING
docker run --rm -it -w /tmp alpine:3.20 sh
```

Lệnh thứ hai: `--rm` và `-e` là option của `run`. `printenv GREETING` là command và argument chạy trong container. Lệnh thứ ba mở shell rồi thoát thì container biến mất vì `--rm`.

### `docker ps` — liệt kê

```text
docker ps [OPTIONS]
```

Không nhận tên container. Muốn một dòng thì dùng `--filter`.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `-a`, `--all` | không | Cả container đã thoát |
| `-q`, `--quiet` | không | Chỉ in ID. Hay ghép với `rm` |
| `-f`, `--filter` | `key=value` | Lọc theo `name`, `status`, `ancestor` |
| `--format` | template | Chỉ lấy cột cần, ví dụ `{{.Names}} {{.Status}}` |
| `-n`, `--last` | số | n container tạo gần nhất, mọi trạng thái |
| `-s`, `--size` | không | Kích thước layer ghi |

```bash
docker ps
docker ps -a
docker ps --filter name=lab-nginx
docker ps -a --filter status=exited
docker ps -aq --filter ancestor=nginx:1.27-alpine
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
```

`status=exited` lấy container đã thoát. `ancestor` lấy mọi container tạo từ image đó. `-q` chỉ in ID, dùng khi xóa hàng loạt.

### `docker stop` — dừng êm

```text
docker stop [OPTIONS] CONTAINER [CONTAINER...]
```

Gửi `SIGTERM` tới PID 1, chờ, rồi `SIGKILL` nếu process chưa thoát. `docker kill` gửi `SIGKILL` ngay. Dùng khi stop không xong, không dùng làm bước thường.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `-t`, `--timeout` | giây | Thời gian chờ trước khi `SIGKILL`. Mặc định 10 |
| `-s`, `--signal` | tên hoặc số | Signal gửi trước, thay `SIGTERM` |

```bash
docker stop lab-nginx
docker stop -t 20 lab-nginx
docker stop lab-nginx lab-redis
```

### `docker logs` — stdout của process chính

```text
docker logs [OPTIONS] CONTAINER
```

Đọc stdout và stderr của PID 1. Không cần `exec`. Process do `exec` tạo không vào đây.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `-f`, `--follow` | không | Bám dòng mới, giống `tail -f` |
| `-n`, `--tail` | số hoặc `all` | Chỉ n dòng cuối |
| `-t`, `--timestamps` | không | Thêm timestamp |
| `--since` | thời điểm hoặc `42m` | Từ lúc đó trở đi |
| `--until` | thời điểm hoặc `42m` | Đến lúc đó |

```bash
docker logs lab-nginx
docker logs -f lab-nginx
docker logs --tail 20 -t lab-nginx
docker logs --since 10m lab-nginx
```

`Ctrl+C` chỉ thoát `logs -f`, không dừng container.

### `docker exec` — process thêm, container phải đang chạy

```text
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
```

`CONTAINER` là tên hoặc ID. `COMMAND` phải là file thực thi trong image. `ARG` là argument của lệnh đó. Process này sống khi PID 1 còn sống. Container restart thì process `exec` không chạy lại. File sửa lúc `exec` không vào image.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `-i`, `--interactive` | không | Giữ stdin. Thiếu thì lệnh đọc input bị treo |
| `-t`, `--tty` | không | Cấp terminal. Cần cho shell tương tác |
| `-d`, `--detach` | không | Chạy lệnh nền, trả ID process |
| `-e`, `--env` | `KEY=value` | Biến chỉ cho process `exec` này, không đổi process chính |
| `-u`, `--user` | `name` hoặc `uid[:gid]` | User chạy lệnh |
| `-w`, `--workdir` | đường dẫn | Thư mục làm việc của lệnh này |
| `--privileged` | không | Nới capability. Chỉ khi thật sự cần debug kernel |

`-i` và `-t` hay đi cùng: `-it`. Mở shell thì cần cả hai. Một lệnh in rồi thoát thì không cần.

```bash
docker exec lab-nginx nginx -v
docker exec lab-nginx cat /etc/nginx/nginx.conf
docker exec -it lab-nginx sh
docker exec -u nginx lab-nginx whoami
docker exec -w /etc/nginx lab-nginx pwd
docker exec -e DEBUG=1 lab-nginx printenv DEBUG
docker exec -d lab-nginx touch /tmp/from-exec
```

Lệnh đầu: `nginx` là command, `-v` là argument của Nginx, không phải option của Docker. Option của `exec` phải đứng trước tên container.

Cái này chạy:

```bash
docker exec lab-nginx sh -c "echo a && echo b"
```

Cái này không chạy. Daemon coi cả chuỗi là một tên file:

```bash
docker exec lab-nginx "echo a && echo b"
```

Container đã `Exited` hoặc đang `Paused` thì `exec` từ chối. Start hoặc unpause trước.

### `docker rm` — xóa container, không xóa image

```text
docker rm [OPTIONS] CONTAINER [CONTAINER...]
```

Xóa một hoặc nhiều container. Container đang chạy thì `rm` từ chối. `rm -f` gửi `SIGKILL` rồi xóa. Image vẫn còn.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `-f`, `--force` | không | Dừng bằng `SIGKILL` rồi xóa |
| `-v`, `--volumes` | không | Xóa luôn anonymous volume gắn với container. Named volume giữ lại |

`--link` là kiểu mạng cũ trên bridge mặc định, bỏ qua.

```bash
docker rm lab-nginx
docker rm -f lab-nginx
docker rm lab-a lab-b lab-c
docker rm -v lab-tmp
docker rm $(docker ps -aq --filter status=exited)
```

Dòng cuối: `ps -aq` in ID, `rm` nhận nhiều ID. Trên PowerShell, `$(...)` vẫn chạy được với Docker. Đừng `rm -f $(docker ps -aq)` khi còn container đang giữ dữ liệu.

`rm` và `run --rm` khác nhau. `--rm` xóa khi container tự dừng. `rm` xóa lúc bạn ra lệnh.

### `docker rmi` — xóa image trên máy, không xóa trên registry

```text
docker rmi [OPTIONS] IMAGE [IMAGE...]
```

`IMAGE` là `repo:tag`, image ID, hoặc digest. Còn container nào trỏ image đó, kể cả `Exited`, thì `rmi` từ chối. Xóa container trước. `rmi` không gỡ image trên Docker Hub.

Một image có nhiều tag thì `rmi repo:tag` chỉ gỡ tag đó. Layer chưa xóa khi còn tag khác. `rmi -f` kèm ID gỡ mọi tag của ID đó rồi xóa layer.

| Option | Argument | Việc làm |
| --- | --- | --- |
| `-f`, `--force` | không | Gỡ dù còn tag trùng, hoặc còn container đã dừng. Lab này không cần |
| `--no-prune` | không | Gỡ tag, giữ layer cha không còn tag |
| `--platform` | `os/arch` | Chỉ xóa một biến thể multi-arch, ví dụ `linux/amd64` |

```bash
docker images
docker rmi hello-world
docker rmi hello-world:latest alpine:3.20
docker rmi fd484f19954f
docker rmi nginx:1.27-alpine
```

Thứ tự đúng khi dọn một vòng lab:

```bash
docker stop lab-nginx
docker rm lab-nginx
docker rmi hello-world
```

`rmi` khi còn `lab-nginx` sẽ báo image đang được dùng. Đừng `rmi -f` để bỏ qua bước đó. Force che lỗi, lần sau khó nhớ vì sao image biến.

### `docker pull` — chỉ tải image

```text
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
```

Chưa tạo container. `docker run` cũng pull nếu thiếu, nên pull riêng khi muốn có image trước, hoặc muốn một tag cụ thể thay vì `latest`.

| Option | Argument | Việc làm |
| --- | --- | --- |
| không ghi tag | — | Lấy `latest`. Đừng làm trên lab và production |
| `-q`, `--quiet` | không | Chỉ in digest, bớt log pull |
| `--platform` | `os/arch` | Ép biến thể, ví dụ `linux/amd64` trên máy ARM |
| `-a`, `--all-tags` | không | Mọi tag của repo. Nặng, ít khi cần |

```bash
docker pull nginx:1.27-alpine
docker pull --quiet alpine:3.20
docker pull --platform linux/amd64 alpine:3.20
```

### `docker tag` — thêm tên, không pull lại

```text
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
```

Cùng một image ID, thêm một `repo:tag` mới. Không tải layer lần nữa. Bỏ tag nguồn thì Docker hiểu `latest`. Lab kéo `busybox:musl` rồi đặt thêm tên khác thì `docker tag busybox:musl busybox:media` — đó là ví dụ dạng, không phải tên lab bắt buộc.

```bash
docker pull nginx:1.27-alpine
docker tag nginx:1.27-alpine nginx:lab
docker images
```

Hai dòng `nginx:1.27-alpine` và `nginx:lab` cùng IMAGE ID. `rmi` một tag thì tag kia vẫn giữ layer.

### `docker cp` — copy file, container chạy hoặc đã dừng

```text
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
```

Hai chiều: host → container hoặc ngược lại. Docs: container có thể đang chạy hoặc đã dừng. Path trong container tính từ `/`; dấu `/` đầu có cũng được. `docker cp` không tạo thư mục cha của `DEST_PATH` nếu chưa có — phải có sẵn hoặc `exec mkdir` trước. Quyền file copy vào container mặc định là root (`UID:GID` 0). `-L` đi theo symlink nguồn. `-a` giữ uid/gid nguồn.

```bash
docker cp ./index.html lab-nginx:/usr/share/nginx/html/index.html
docker cp lab-nginx:/etc/nginx/nginx.conf ./nginx.conf
```

Không copy được một số path đặc biệt (`/proc`, `/sys`, `/dev`, tmpfs). Docs bảo dùng `tar` qua `exec` cho các chỗ đó.

### `docker commit` — image từ layer ghi, không phải Dockerfile

```text
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
```

Chụp thay đổi trong layer ghi của container thành image mới. Mount volume **không** vào image. Mặc định Docker pause container lúc commit để giảm hỏng dữ liệu; `--no-pause` tắt. `-c` / `--change` gắn thêm vài chỉ thị Dockerfile (`CMD`, `ENV`, `EXPOSE`, …).

```bash
docker commit lab-nginx nginx:from-container
```

`commit` hữu ích khi debug một filesystem đang sống. Image production viết bằng Dockerfile: lặp lại được, đọc được, CI build được. `exec` sửa file rồi `commit` mất lịch sử lệnh, volume thì lại không đi theo.

## Lệnh và code mẫu

Vòng container thoát ngay:

```bash
docker run hello-world
docker ps
docker ps -a --filter ancestor=hello-world
```

Vòng Nginx. Bỏ `--rm` để còn container mà `rm`:

```bash
docker pull nginx:1.27-alpine
docker run --name lab-nginx --detach --publish 8080:80 nginx:1.27-alpine
docker ps --filter name=lab-nginx --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
curl.exe -sS -D - http://127.0.0.1:8080 -o /dev/null
docker logs --tail 5 -t lab-nginx
docker exec lab-nginx nginx -v
docker exec -u nginx -w /etc/nginx lab-nginx pwd
docker exec lab-nginx sh -c "echo a && echo b"
docker stop -t 5 lab-nginx
docker ps -a --filter name=lab-nginx
docker rm lab-nginx
docker rmi hello-world
```

`docker rmi hello-world` chỉ xóa image bài tập. Giữ `nginx:1.27-alpine` vì các bài sau còn dùng. `rmi` in image id khi xóa được.

Trên PowerShell, `curl` có thể là alias của `Invoke-WebRequest`. Dùng `curl.exe`.

## Thực hành

1. Chạy `hello-world`. So `docker ps` với `docker ps -a`. Lọc bằng `--filter ancestor=hello-world`.
2. Pull đúng tag Nginx, chạy `lab-nginx`, gọi `curl.exe` hoặc mở `http://127.0.0.1:8080`.
3. `docker exec lab-nginx nginx -v`. Thử thêm `-u nginx` và `-w /tmp`.
4. `docker logs --tail 5 -t lab-nginx`.
5. `docker stop`, kiểm tra `ps -a` còn dòng `Exited`, rồi `docker rm`.
6. `docker rmi hello-world`. Đừng xóa image Nginx. Thử `rmi` khi còn container để đọc lỗi một lần.
7. `docker tag nginx:1.27-alpine nginx:lab` rồi `docker images` — cùng ID, hai tag.
8. `docker cp` một file nhỏ vào `lab-nginx`. Thử `cp` khi container đã `stop` — docs cho phép.
9. `docker commit lab-nginx nginx:from-container`. Đừng dùng `commit` thay Dockerfile ở bài sau.

## Kết quả mong đợi

- `hello-world` in thông báo pull và chạy xong. `docker ps` không có nó. `docker ps -a` có trạng thái exited trước khi bạn `rm`.
- `lab-nginx` map `0.0.0.0:8080->80/tcp` hoặc dạng tương đương. `curl` nhận `200`.
- `docker exec lab-nginx nginx -v` in phiên bản. `exec` với `-w /tmp` in `/tmp`.
- Sau `docker rm lab-nginx`, `docker ps -a` không còn tên đó. Image `hello-world` không còn. Image Nginx vẫn còn.
- `rmi` khi còn container báo image đang được dùng.

## Checklist

- [ ] Đặt option của Docker trước tên container. Command và argument đứng sau.
- [ ] `docker ps` và `docker ps -a` đọc ra hai danh sách khác nhau. Lọc được bằng `--filter`.
- [ ] `exec` cần container đang chạy. `-it` cho shell, không cần cho lệnh in rồi thoát.
- [ ] `rm` xóa container. `rmi` xóa image. Còn container trỏ image thì `rmi` từ chối.
- [ ] Pull một tag cụ thể, không để mặc định `latest`.
- [ ] Đọc `8080:80` thành port host và port container.
- [ ] `docker tag` chỉ thêm tên, không pull lại.
- [ ] `docker cp` hai chiều; không tự `mkdir` cha. Container dừng vẫn copy được.
- [ ] `docker commit` bỏ volume. Image lặp lại thì viết Dockerfile.

## Thẻ ôn

### docker-cli-ps
kind: distinguish
cue: `docker ps` trống chưa có nghĩa là container đã mất. Container thoát vẫn còn, trạng thái Exited, xem bằng `docker ps -a`.

Q: Bạn chạy `docker run hello-world`. Lệnh in xong rồi thoát. Gõ `docker ps` thì danh sách trống. Container vừa chạy còn trên máy không, và làm sao xem nó?

A: `docker ps` chỉ liệt kê container đang chạy. Hello-world chạy một phát rồi thoát, nên nó không còn trong danh sách đó, nhưng Docker vẫn giữ container ở trạng thái Exited cho đến khi bạn `docker rm`. Xem bản đầy đủ bằng `docker ps -a`. Nếu lúc `docker run` có `--rm`, container tự xóa ngay khi dừng, lúc đó `ps -a` cũng không còn nó.

### docker-cli-publish
kind: command
cue: Trong `8080:80`, số trước dấu hai chấm là port trên máy host, số sau là port process đang nghe trong container.

Q: Image Nginx nghe port 80 bên trong container. Trên máy dev bạn muốn mở trình duyệt tại `http://127.0.0.1:8080`. `--publish` viết thế nào, và đảo hai số thì request đi đâu?

A: Viết `8080:80`. Số đứng trước dấu hai chấm là port Docker mở trên host. Số đứng sau là port process đang nghe trong container. Trình duyệt vào 8080 trên máy bạn, Docker chuyển tiếp vào port 80 của Nginx. Đảo thành `80:8080` thì host mở port 80, còn process trong image vẫn nghe 80, nên hai đầu không gặp nhau.

### docker-cli-rm-rmi
kind: distinguish
cue: `docker rm` xóa container. `docker rmi` xóa image trên máy. Còn container nào trỏ image đó thì phải xóa container trước.

Q: Bạn muốn dọn một image cũ trên máy. `docker rm` và `docker rmi` mỗi lệnh xóa cái gì? Vì sao `rmi` đôi khi từ chối dù container đã Exited?

A: `docker rm` xóa một container, không đụng tới image đã dùng để tạo nó. `docker rmi` xóa image trên máy này, không gỡ bản đang nằm trên registry. Nếu còn container trỏ image đó, kể cả container đã Exited và không chạy nữa, `rmi` từ chối vì image vẫn đang được tham chiếu. Xóa container trước, rồi mới xóa image.

### docker-cli-exec-exited
kind: trap
cue: `docker exec` chỉ chạy được khi container đang chạy. Container Exited thì `docker start` trước, hoặc tạo container mới.

Q: Bạn muốn vào một container để xem file, gõ `docker exec`, và Docker từ chối vì container đang Exited. Bước tiếp theo là gì?

A: `exec` gắn một process mới vào container đang chạy. Container đã thoát thì không còn process để gắn vào. `docker start` container đó, đợi nó chạy lại, rồi exec. Nếu container đó không cần giữ, tạo một container mới từ cùng image. Đừng tưởng `exec` sẽ tự bật container đã dừng.

### docker-cli-exec-shell
kind: trap
cue: `docker exec` không chạy shell cho bạn. Muốn `&&` thì gọi `sh -c`, và option của Docker phải đứng trước tên container.

Q: Bạn gõ `docker exec lab-nginx "echo a && echo b"` và lệnh fail. Docker đang hiểu chuỗi trong ngoặc là gì, và câu đúng viết thế nào?

A: Phần sau tên container là command và argument, không phải một câu shell. Daemon coi cả chuỗi `"echo a && echo b"` là tên một file cần chạy, nên không tìm thấy. Muốn shell hiểu `&&`, hãy để shell làm việc đó: `docker exec lab-nginx sh -c "echo a && echo b"`. Option của chính Docker, nếu có, đứng trước tên container. Command đứng sau.

### docker-cli-stop
kind: command
cue: `docker stop` gửi SIGTERM cho process chính, chờ mặc định 10 giây, rồi mới SIGKILL. `docker kill` giết ngay.

Q: Bạn cần dừng một container đang phục vụ request. `docker stop` làm gì với process chính, chờ bao lâu, và khi nào mới dùng `docker kill`?

A: `docker stop` gửi SIGTERM tới PID 1, tức process chính, để process kịp đóng socket và ghi nốt dữ liệu. Mặc định nó chờ 10 giây. Hết giờ mà process chưa thoát, Docker gửi SIGKILL. `docker kill` bỏ qua bước chờ và gửi SIGKILL ngay, process không kịp dọn. Dùng `kill` khi `stop` không xong, không dùng làm cách dừng hàng ngày.

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

**`docker rmi nginx:1.27-alpine` khi `lab-nginx` vẫn còn, kể cả đã stop.** Daemon báo image đang được container dùng. `docker rm` container đó trước. Docs `docker image rm` có `--force`, lab này không cần.

**`docker exec` khi container đã `Exited`.** Daemon từ chối. `docker start lab-nginx` rồi `exec` lại. Hoặc tạo container mới.

**Đặt `-v` sau tên container trong `exec`.** `-v` lúc đó là argument của lệnh trong container, hoặc Docker báo unknown flag tùy chỗ đứng. Option của `exec` phải trước tên. `-v` là option của `rm` và `run`, không phải của `exec`.

**`docker exec lab-nginx "echo a && echo b"`.** Daemon tìm file tên đúng chuỗi đó. Viết `sh -c "echo a && echo b"`.

**`docker run --name lab-nginx` lần hai khi tên cũ vẫn còn.** Docker từ chối vì trùng tên. `docker ps -a`, `docker rm` bản cũ, rồi chạy lại.

**Đảo `--publish` thành `80:8080` với image Nginx.** Host mở port `80`, process trong image vẫn nghe `80`. Muốn tránh privileged port trên host thì map `8080:80`.

**Sửa file bằng `exec` rồi coi đó là lần deploy.** Container tạo lại từ cùng image không có sửa đó. Muốn giữ thay đổi thì đưa vào Dockerfile hoặc volume.

**`docker cp` vào path cha chưa tồn tại.** Docs: không tạo parent. `mkdir` trong container trước, hoặc copy vào thư mục đã có.

**`docker commit` rồi đẩy image đó lên production.** Volume không vào. Lịch sử layer là một snapshot, không đọc được như Dockerfile. Viết lại Dockerfile.

**`docker tag alpine alpine:lab` khi máy chưa có `alpine:latest`.** Tag nguồn phải có sẵn. `pull` đúng tag trước, rồi `tag`.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày Deploy Nginx Container, Copy File to Docker Container, Pull Docker Image, Create Docker Image From Container, Docker EXEC, Docker Ports Mapping. 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) — thứ tự lab: `run`, `ps`/`stop`, `rm`/`rmi`, `pull`/`exec`.
3. [docker container run](https://docs.docker.com/reference/cli/docker/container/run/) — `--name`, `--rm`, `--publish`, `-e`, `-it`, `-w`.
4. [docker container ps](https://docs.docker.com/reference/cli/docker/container/ps/), [stop](https://docs.docker.com/reference/cli/docker/container/stop/), [logs](https://docs.docker.com/reference/cli/docker/container/logs/) — lọc, timeout, `--tail` / `--since`.
5. [docker container exec](https://docs.docker.com/reference/cli/docker/container/exec/) — `CONTAINER COMMAND [ARG...]`, `-it`, `-e`, `-u`, `-w`, `-d`.
6. [docker container rm](https://docs.docker.com/reference/cli/docker/container/rm/) — `-f`, `-v`, xóa nhiều container.
7. [docker image pull](https://docs.docker.com/reference/cli/docker/image/pull/), [docker image tag](https://docs.docker.com/reference/cli/docker/image/tag/), [docker image rm](https://docs.docker.com/reference/cli/docker/image/rm/) — tag / ID / digest; `tag` chỉ thêm tên.
8. [docker container cp](https://docs.docker.com/reference/cli/docker/container/cp/) — hai chiều, container chạy hoặc dừng, không tạo parent.
9. [docker container commit](https://docs.docker.com/reference/cli/docker/container/commit/) — volume không vào image; mặc định pause.
10. [Docker Hub — nginx](https://hub.docker.com/_/nginx) — tag và port image chính thức nghe.

## Tiếp theo

[Dockerfile](./02-dockerfile.md)
