---
# System prepended metadata

title: Docker and Docker Compose

---

# Docker

:::warning
Q & A

什麼是 Docker? 什麼是 Container?
:::

:::warning
Q & A

Container 有哪些特性?
:::

:::info
**[OCI (Open Container Initiative)](https://opencontainers.org)**

是一個由 Linux Foundation 支持的開放標準組織，成立於 2015 年，目標是:

> 制定並維護容器映像檔 (Image) 與執行環境 (Runtime) 的標準規範，使不同平台之間能夠相容

**[runtime-spec](https://github.com/opencontainers/runtime-spec)**

訂定一個容器如何在作業系統上執行，只要遵循 OCI runtime-spec 的 runtime 都能執行相同的容器設定

**[image-spec](https://github.com/opencontainers/image-spec)**

訂定映像檔如何被封裝跟儲存，讓不同 runtime 都能用相同規格製作與解析映像檔

**[distribution-spec](https://github.com/opencontainers/distribution-spec)**

訂定映像檔在 registry (例如 Docker Hub、Harbor、GHCR) 之間如何被上傳、下載與管理的標準
:::

## 基本操作

`docker <resource> [options] <action> <image:tag>`

```shell
# 查看映像檔
docker image ls
docker images

# 查看執行中的容器
docker ps

# 查看所有容器 (包括已停止的)
docker ps -a

# 停止容器
docker container stop <container_id>
docker stop <container_id>

# 啟動已停止的容器
docker container start <container_id>
docker start <container_id>

# 移除容器
docker container rm <container_id>
docker rm <container_id>

# 移除映像檔
docker image rm <image_id>
docker rmi <image_id>

# 查看容器 log
docker container logs <container_id>
docker logs <container_id>

# 即時查看容器 log
docker logs -f <container_id>

# 進入執行中的容器
docker container exec -it <container_id> bash
docker exec -it <container_id> bash

# 查看容器資源使用情況
docker container stats
docker stats

# 清理未使用的資源
docker system prune
```

## Volume

:::info
將容器應用程式設計成 **無狀態 (stateless)** 是一種 best practice，這邊的狀態 (state) 指的是應用程式在某個時刻儲存的資料或上下文資訊，所有狀態資料要放在**外部**，需要時再注入容器當中，目的是讓系統能夠在任何時候開、關容器，而不影響整體服務
:::

為了能夠讓無狀態的容器去方便取用這些有狀態的資料，要透過 volume 去實現

### 建立 volume

`docker volume create <volume_name>`

`docker volume ls`

### 在 container 使用 volume

先使用 nginx 掛載資料夾，並檢查及修改內容

`docker run --rm --name web-server -d -p 8080:80 -v <volume_name>:/usr/share/nginx/html nginx`

然後再用 apache 掛載執行，看看結果是否一樣

`docker run --rm --name web-server -d -p 8080:80 -v <volume_name>:/usr/local/apache2/htdocs httpd`

## 如何建立 image?

在當前資料夾建立一 `index.html`

```html!
<!DOCTYPE html>
<html>
  <head>
    <title>Hello from Ubuntu + Nginx</title>
  </head>
  <body>
    <h1>Nginx running on Ubuntu!</h1>
  </body>
</html>
```

再建立一叫做 `Dockerfile` 的檔案

```dockerfile
FROM ubuntu:24.04

# 不讓 apt-get 要求互動輸入
ENV DEBIAN_FRONTEND=noninteractive

# 更新套件清單並安裝 nginx
RUN apt-get update && \
    apt-get install -y --no-install-recommends nginx && \
    rm -rf /var/lib/apt/lists/*

# 刪除預設網頁，複製自己的
RUN rm -rf /var/www/html/*
COPY index.html /var/www/html/

# 開放 80 port
EXPOSE 80

# 啟動 Nginx (用 foreground 模式讓容器持續執行)
CMD ["nginx", "-g", "daemon off;"]
```

建立 image

`docker build -t ubuntu-nginx .`

執行看看

`docker run -d -p 8080:80 ubuntu-nginx`

:::info
為何 Dockerfile 安裝套件使用的是 `apt-get` 而不是 `apt`?

`apt-get` 是早期 Debian/Ubuntu 系統中提供的套件管理指令，專門給 腳本與自動化工具使用，後來新增 `apt` 來提升使用者體驗。

而在 `docker build` 的過程中沒辦法做任何的互動式操作，且 `apt-get` 在版本更動的過程輸出格式沒有什麼變動，而 `apt` 為了更好的使用者體驗，輸出格式每次的改動都算滿大的，所以 `apt-get` 會更適合做自動化及分析 debug

而且最重要的 [Ubuntu 官方文件](https://manpages.ubuntu.com/manpages/noble/man8/apt.8.html)在 `SCRIPT USAGE AND DIFFERENCES FROM OTHER APT TOOLS` 有明確的建議
:::

# Docker Compose

使用 `yaml` 的格式定義多容器組成的應用程式的組合方式，有了這個檔案就能夠使用 `docker compose <action> [options]` 來建立或是管理應用程式

建立一 `compose.yaml` 檔案

## Demo

```yaml
name: dbms
services:
  database:
    image: postgres
    environment:
      POSTGRES_PASSWORD: password
    ports:
      - 5432:5432
  web-client:
    image: clidey/whodb
    ports:
      - 8080:8080
```

啟動應用程式

`docker compose up -d`

觀察應用程式的執行狀態

`docker compose ps`

觀察應用程式的 log

`docker compose logs`