---
# System prepended metadata

title: Docker

---

# Docker
###### tags: `ncnu`, `lsa`, `docker`, `container`

[TOC]

## Container vs VM 快速比較

### 架構差異

![image](https://hackmd.io/_uploads/B1H7c1B6ex.png)



### 關鍵差異表

| 特性 | 虛擬機 (VM) | Docker 容器 |
|------|------------|------------|
| 啟動時間 | 數分鐘 | 數秒 |
| 資源占用 | 高 (完整 OS) | 低 (共享核心) |
| 映像檔大小 | GB 級 | MB 級 |
| 隔離程度 | 完全隔離 | 程式級隔離 |
| 適用場景 | 需要不同 OS、強隔離需求 | 微服務、CI/CD、快速部署 |

---

## Docker 核心概念

### 三大元件

1. **Dockerfile**
   - 定義如何建構 image 的「食譜」
   - 包含作業系統、套件、程式碼、環境設定

2. **Image (映像檔)**
   - 唯讀模板，由多層 (layers) 組成
   - 可版本化、分享、重複使用
   - 使用 UnionFS / OverlayFS 技術疊加層級

3. **Container (容器)**
   - Image 的執行實例
   - 相互隔離但共享 OS 核心
   - 在 image 唯讀層之上加一個可寫層

---

## Container 隔離技術

### 1. **Namespaces** (隔離可見範圍)

限制 container 能「看到」什麼：

| Namespace | 功能 |
|-----------|------|
| **PID** | 獨立行程 ID 空間 |
| **Network** | 獨立網路設備、IP、Port |
| **Mount** | 獨立檔案系統掛載 |
| **UTS** | 獨立 hostname |
| **IPC** | 獨立行程間通訊 |
| **User** | 獨立 UID/GID 映射 |

### 2. **cgroups** (限制資源使用量)

控制 container 能「用多少」資源：
- CPU 時間分配
- 記憶體上限
- 磁碟 I/O 限制
- 網路頻寬控制

**範例**：
```bash
# 限制記憶體 512MB
docker run --memory 512MB nginx

# 限制 CPU 使用率 50%
docker run --cpu-quota=50000 --cpu-period=100000 ubuntu
```

---

## Docker 架構

```
[ 使用者 ]
     ↓
[ Docker Client (CLI) ] ← 你下指令的地方
     ↓ (透過 UNIX Socket / REST API)
[ Docker Daemon (dockerd) ] ← 核心管理者
     ↓
[ Container Runtime ]
     ↓
[ Linux Kernel ]
     ↓
[ 硬體 ]
```

### 關鍵元件

- **Docker Client**：使用者介面 (CLI / Desktop)
- **Docker Daemon**：背景服務，管理 image/container/network/volume
- **Docker Registry**：儲存 image 的倉庫 (如 Docker Hub)

---

## 容器化的五大優勢

1. ✅ **不破壞主機**：隔離環境，系統風險低
2. ✅ **一鍵部署**：一行指令架起完整服務
3. ✅ **輕量高速**：秒級啟動，資源消耗少
4. ✅ **高度可攜**：開發、測試、正式環境一致
5. ✅ **易於擴展**：搭配 Docker Compose/Kubernetes 自動化管理

---

## 實際應用場景

### ✅ 適合用 Container

- 快速開發和測試環境
- CI/CD 流程
- 雲端原生應用
- 需要快速擴展的應用
- 範例：網頁程式設計、軟體工程中快速架設 PostgreSQL 和 Apache，方便測試軟體

### ❌ 適合用 VM

- 需要不同作業系統(如 macOS、FreeBSD、Nintendo Switch、PS3)
- 強隔離要求(如下載學習資料但擔心有毒)
- 傳統單體應用(如 Word、PowerPoint、Excel)
- 需要特定核心版本或模組

---

## 快速上手指令

```bash
# 拉取映像檔
docker pull nginx

# 執行容器 (背景模式 + Port 對應)
docker run -d -p 8080:80 nginx

# 查看運行中的容器
docker ps

# 進入容器內部
docker exec -it <container_id> bash

# 停止容器
docker stop <container_id>

# 刪除容器
docker rm <container_id>
```

---

## 總結

| 比較項目 | 虛擬機 | Docker 容器 |
|----------|--------|------------|
| 啟動速度 | 慢 (分鐘級) | 快 (秒級) |
| 資源效率 | 低 | 高 |
| 隔離強度 | 強 (硬體級) | 中 (程式級) |
| 適用場景 | 需要完整 OS | 應用程式部署 |

**核心概念**：
- 容器 = 輕量化虛擬化
- 共享 kernel，但隔離執行環境
- 透過 Namespaces (限制視野) + cgroups (限制資源) 實現隔離

## 虛擬化與容器技術

### 虛擬化技術概述

虛擬化技術經歷了從傳統虛擬機到現代容器化的演進。



#### 容器化的優勢

- **不會實際修改到主機系統**：容器運行環境與主機隔離，所有變更只在容器中生效，降低系統風險
- **一行指令就可以架起完整的服務**：藉由預先打包好的映像檔，使用簡單指令即可快速部署服務，大幅簡化設定流程
- **輕量化**：容器只包含必要的程式庫與應用程式，佔用資源少、啟動速度快
- **不需要模擬整個作業系統**：容器共用主機核心，僅抽象應用運行環境，與虛擬機相比更有效率
- **高移植性**：在不同環境下執行容器不需額外調整，便於在開發、測試與正式部署間快速切換
- **易於管理與擴展**：可透過容器編排工具(如 Docker Compose、Kubernetes)自動化部署、擴展及維護多個服務
- **有助於持續整合/持續部署(CI/CD)流程**：方便自動化測試、打包、部署，提升開發與運維效率

#### 適用場景

**適合使用容器的場景**：
- 微服務架構
- 快速開發和測試環境
- CI/CD 流程
- 雲端原生應用
- 需要快速擴展的應用
- 範例：網頁程式設計、軟體工程中快速架設 PostgreSQL 和 Apache，方便測試軟體

**適合使用虛擬機的場景**：
- 需要不同作業系統(如 macOS、FreeBSD、Nintendo Switch、PS3)
- 強隔離要求(如下載學習資料但擔心有毒)
- 傳統單體應用(如 Word、PowerPoint、Excel)
- 需要特定核心版本或模組

---

## Docker 基礎實作

### Docker 安裝

#### 在 Ubuntu 24.04 LTS 上安裝 Docker

**快速安裝方式(推薦)**：

```bash
# 下載並執行官方安裝腳本
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh ./get-docker.sh
```

#### 設定使用者權限(選擇性)
> 什麼時候會選擇這麼做
```bash
# 將目前使用者加入 docker 群組
sudo usermod -aG docker $USER

# 重新登入或執行以下命令生效
newgrp docker

# 測試安裝
docker --version
```

### Docker 基本操作

#### Hello World 範例

```bash
# 執行第一個 Docker 容器
docker run hello-world
```

這個命令會：
1. 檢查本地是否有 hello-world 映像檔
2. 如果沒有，則從 Docker Hub 下載
3. 建立並執行容器
4. 顯示歡迎訊息並退出

#### 常用 Docker 命令

```bash
# 查看映像檔
docker images

# 查看執行中的容器
docker ps

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

# 停止容器
docker stop <container_id>

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

# 移除容器
docker rm <container_id>

# 移除映像檔
docker rmi <image_id>

# 查看容器日誌
docker logs <container_id>

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

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

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

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

#### 在 Ubuntu 虛擬機中跑 Docker 版本的 Ubuntu 容器

```bash
# 執行互動式 Ubuntu 容器
docker run -it ubuntu:24.04 bash

# 在容器內執行命令
apt update
apt install vim
```

### 在 Docker 中架設 Nginx

#### Docker Run

##### 基本運行
```bash
# 最簡單的方式 - 使用官方 image
docker run -d \
  --name my-nginx \
  -p 8081:80 \
  nginx:latest
```

##### 完整版本(掛載自訂內容)
```bash
docker run -d \
  --name my-nginx \
  -p 80:80 \
  -p 443:443 \
  -v ~/my-website/html:/usr/share/nginx/html:ro \
  -v ~/my-website/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx:latest
```

##### 參數說明
- `-d`: 背景執行
- `--name`: 容器名稱
- `-p 80:80`: 對應 host 的 80 port 到容器的 80 port
- `-v`: 掛載 volume(`:ro` 代表唯讀)
- `nginx:latest`: 使用最新版官方 image

---
nginx


#### Docker Compose(官方範例)

##### 基礎版 `docker-compose.yml`
```yaml
version: '3.8'

services:
  nginx:
    image: nginx:latest
    container_name: my-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./html:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    restart: unless-stopped
```

---

#### 完整專案結構範例

```
project/
├── docker-compose.yml
├── html/
│   └── index.html
├── nginx/
│   ├── nginx.conf
│   ├── conf.d/
│   │   └── default.conf
│   ├── ssl/
│   │   ├── cert.pem
│   │   └── key.pem
│   └── logs/
│       ├── access.log
│       └── error.log
```

### Docker 持久化的幾種方式

#### Volume(推薦做法)
```bash
# 建立 volume
docker volume create nginx-data

# 使用 volume 掛載
docker run -d \
  -p 80:80 \
  -v nginx-data:/usr/share/nginx/html \
  nginx
```

**為什麼推薦?**
- 資料獨立於容器生命週期
- 多個容器可以共享
- 方便備份和遷移
- Docker 會幫你管理

---

#### Dockerfile(最佳實踐)
```dockerfile
FROM ubuntu:22.04

# 安裝 nginx
RUN apt update && \
    apt install -y nginx && \
    apt clean && \
    rm -rf /var/lib/apt/lists/*

# 複製設定檔
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

**優點:**
- ✅ 可追蹤(Git 版本控制)
- ✅ 可重現(任何人都能 build)
- ✅ 透明(所有步驟都寫明)
- ✅ 容易維護和更新

---

#### Docker Commit(不推薦)
```bash
# 在容器內做修改後
docker commit container-id reg-nginx:v1
```

**為什麼不推薦?**

| 問題 | 說明 | 實際影響 |
|------|------|----------|
| **無法追蹤** | 不知道做了什麼修改 | 出問題時無法 debug |
| **無法重現** | 別人拿到 image 也不知道怎麼做的 | 團隊協作困難 |
| **Layer 疊加** | 每次修改都加一層,刪檔案也算 | 容器越來越肥大 |
| **缺乏文件** | 沒有 Dockerfile 當文件 | 接手的人會很痛苦 |

##### Layer 疊加範例:
```bash
# 原始 image: 100MB
docker commit -> 加了 nginx (150MB)
docker commit -> 改設定檔 (151MB)  
docker commit -> 刪了某個檔案 (151MB) # 注意!沒有變小!
docker commit -> 又裝了東西 (180MB)
```

刪除檔案為什麼 image 不會變小？因為 Docker 的 **Union File System** 是用「遮罩」的方式標記刪除，舊的 layer 還在!

---

#### Docker Save/Export(僅用於搬遷)

```bash
# save: 保留 image metadata
docker save nginx:latest > nginx.tar

# export: 只保留檔案系統,丟失 metadata
docker export container-id > container.tar
```

**什麼時候用?**
- 需要在**沒網路**的環境部署
- 臨時備份
- ⚠️ 但不應該當作主要的版本管理方式

---

:::info
### 匯出與匯入 Docker Image

#### 匯出 Docker image

```bash
# 先確認容器 ID
docker ps -a

# 將容器提交(commit)為新的映像檔
# 格式: docker commit <容器ID或名稱> <新映像檔名稱>:<標籤>
docker commit my-nginx-container my-nginx-image:v1.0

# 確認映像檔已建立
docker images

# 將映像檔匯出為 tar 檔案
docker save -o my-nginx-image.tar my-nginx-image:v1.0

# 確認檔案已建立
ls -lh my-nginx-image.tar
```

#### 匯入 Docker image

```bash
# 匯入映像檔
docker load -i my-nginx-image.tar

# 執行匯入的映像檔
docker run -it -p 8080:80 --name restored-nginx my-nginx-image:v1.0 bash

# 進入容器後啟動 Nginx
service nginx start
```
:::
---

## 本週作業

將 Nginx 中的 Load balance 放進 Docker 中，截圖連線結果及 Docker status

