---
# System prepended metadata

title: Docker 第二週
tags: [Node.js 企業專題班 - 2024 春季班]

---

# Docker 第二週

本週課程要點
- 上週複習
- 在雲服務上面開機器
- 關於 buildX
- Docker Network


## 1. 上週複習

- 如何選用合適的 Docker Image（Nginx 為例）
- 如何檢視一個運行中的容器（用什麼指令？）
- 如何與容器產生交互狀態
- 如何停止容器，以及關閉所有 exited 狀態中的容器
- 產生 docker image 會使用哪一種檔案


## 2. 在雲服務上面開機器

### 雲服務小知識

- IaaS、CaaS、PaaS：https://cloud.google.com/learn/paas-vs-iaas-vs-saas?hl=zh-tw
![image](https://hackmd.io/_uploads/H1v0FkPNC.png)
- Region：https://aws.amazon.com/tw/about-aws/global-infrastructure/regions_az/
![image](https://hackmd.io/_uploads/H1pT5JDV0.png)


---

Linode / Akamai

價格表：https://www.linode.com/pricing/

優點：
- 可以預先儲值、價格不會爆表
- 單純簡單（相對於大型雲服務廠商，設置簡單很多

### 2-1 常見 Linux 指令

1. **切換目錄 (`cd`)**：
    - 切換到家目錄：**`cd ~`**
    - 切換到上一層目錄：**`cd ..`**
    - 切換到根目錄：**`cd /`**
2. **列出檔案和目錄 (`ls`)**：
    - 列出當前目錄：**`ls`**
    - 列出當前目錄包括隱藏檔案：**`ls -a`**
    - 列出詳細資訊：**`ls -l`**
3. **建立和刪除目錄 (`mkdir` 和 `rmdir`)**：
    - 建立目錄：**`mkdir new_folder`**
    - 刪除空目錄：**`rmdir old_folder`**
4. **建立、查看和刪除檔案 (`touch`, `cat`, `rm`)**：
    - 建立新檔案：**`touch file.txt`**
    - 查看檔案內容：**`cat file.txt`**
    - 刪除檔案：**`rm file.txt`**
5. **複製和移動/重新命名檔案 (`cp` 和 `mv`)**：
    - 複製檔案：**`cp source.txt destination.txt`**
    - 移動或重新命名檔案：**`mv oldname.txt newname.txt`**
6. **查找檔案 (`find`)**：
    - 在當前目錄下查找檔案：**`find . -name "file.txt"`**
7. **編輯檔案 (`nano`, `vi`)**：
    - 使用 nano 編輯器：**`nano file.txt`**
    - 使用 vi 編輯器：**`vi file.txt`** （👉 注意重點補充 👈）

### 2-2 安裝 Docker
    
https://docs.docker.com/engine/install/ubuntu/


### 2-3 **Docker 服務的開關控制指令**

systemctl 是系統層面的指令

1. **啟動 Docker 服務**：
    
```bash
sudo systemctl start docker
```

2. **停止 Docker 服務**：

```bash
sudo systemctl stop docker
```

3. **重啟 Docker 服務**：

```bash
sudo systemctl restart docker
```

4. **查看 Docker 服務狀態**：

```bash
sudo systemctl status docker
```

5. **設定 Docker 服務開機自啟**：

```bash
sudo systemctl enable docker
```

6. **取消 Docker 服務開機自啟**：

```bash
sudo systemctl disable docker
```


## 3. Docker Hub

https://hub.docker.com/

登入 Docker Hub 可以上傳自己的 image

**上傳方式**

- 建立 image 名稱為 {account}/{image name}

```docker
docker build -t casper723/2024-node-course-express-sample .
```

- 使用 push 指令

```docker
docker image push casper723/2024-node-course-express-sample
```

**下載 image**

- 可以直接使用 run，或者使用 pull

```docker
docker image pull casper723/2024-node-course-express-sample
```

#### 多架構

使用 buildX：https://docs.docker.com/reference/cli/docker/buildx/

- 如果是安裝 Docker Desktop 會自動安裝 BuildX
- Linux 環境下，則需要另外安裝 BuildX


關於 AMD64 與 ARM64
- **`amd64`** 是由 AMD 開發的 64 位元處理器架構，基於擴展的 x86 指令集。這種架構也被 Intel 採用並稱為 Intel 64 或 x86-64，是當前最普遍使用的個人電腦處理器架構。
- **`arm64`** 是 ARM 架構的 64 位元版本，專為低功耗環境設計，廣泛應用於移動設備、嵌入式系統、以及越來越多的桌面和伺服器硬體。

### 3-1 BuildX

**列出所有可用架構**

```docker
docker buildx ls
```

**建構多架構的 image**

```docker
docker buildx build --platform linux/arm/v7,linux/arm64/v8,linux/amd64 -t casper723/2024-node-course-express-sample:latest . --push
```

初次使用時會出錯，會說明當前不能建構多平台，因此需要先建立新的環境(builder)才能運行。

```
docker buildx create --name multi-arch-builder --use
```

> 常見 builder 命名方式
    
#### **1. 用途描述**

如果構建器是為了特定的應用或環境設計，名稱中可以包含相關的描述，例如：

- **`web-app-builder`**
- **`android-ci-builder`**
- **`production-release-builder`**

#### **2. 平台特定**

如果構建器是為了特定的運行平台或硬體架構而設計，可以在名稱中體現這一點，如：

- **`arm64-builder`**
- **`linux-amd64-builder`**
- **`multi-arch-builder`**

正式打包上傳：
```
docker buildx build --platform linux/arm/v7,linux/arm64/v8,linux/amd64 -t casper723/2024-node-course-express-sample:latest . --push
```

### 補充加入版本號的手法

```
docker build -t myapp:1.0.0 -t myapp:1.0 -t myapp:latest .
```



## 4. 關於 Network

### 4-1 創建一個專案，需包含 MongoDB(本地端範例)

範例：https://github.com/Wcc723/docker-express-sample-mongo

準備專案及建立 mongo image

```docker
docker image pull mongo:latest
docker run -d -p 27017:27017 mongo:latest
```

實作項目：
1. 使用本地端的 Node.js 連接 MongoDB
2. 使用 Docker Container 的 Node.js 連接 MongoDB => 這個會出錯


#### 為什麼會這樣？

原因：

1. 當都屬於容器化時，不能直接連線 localhost（所在 local 已經改變）
2. 容器化時，network 需要另外設定，docker container inspect CONTAINER 查看兩個容器
3. mongoDB 沒有正確命名，node 無法直接取用


### 4-2 解方一：使用 ip 進行連接

> 了解原理

1. 請先關閉當前所有容器，僅保留 mongoDB
2. 建立兩個 node 輕量型服務（這是簡易說明，只要可以使用部分網路指令即可
    1. `docker container run -d --name node2 -p 3001:3000 casper723/2024-node-course-express-sample`
    2. `docker container run -d --name node1 -p 3000:3000 casper723/2024-node-course-express-sample`
3. 與這兩個容器產生交互
    1. docker container exec -it node1 sh
    2. docker container exec -it node2 sh
4. 分別了解當前的狀態
     - 做以下幾件事
        1. ip a → 取得自己的 ip
        2. ping 172.17.0.1 (Gateway)
        3. ping 另一台 node
        4. ping node2 
        5. docker container inspect MONGODB
        6. ping MONGODB
6. 移除其他 node 容器，mongoDB 連線改用 ip

### 4-3 解方二：實戰建議的解法 Network Bridge

> DNS 解析限制：在預設的 bridge 網絡中，Docker 不提供自動的 DNS 解析服務來允許容器透過名稱互相通訊。要啟用這種名稱解析，您需要建立一個使用者定義的網絡（比如 docker network create），這會自動設定 DNS 解析，容器名稱即可用於互相通訊。


#### 建立 Bridge 了解觀念：

1. 建立新的 network

```docker
	docker network create my-network
	docker network ls
```

2. 建立兩個 node 服務，並套用相同 network
    1. docker run -d --name node1 --network my-network -p 3000:3000 casper723/2024-node-course-express-sample
    docker run -d --name node2 --network my-network -p 3001:3000 casper723/2024-node-course-express-sample
3. 使用 inspect 檢視 node1
4. 分別進入兩個容器
    1. docker container exec  -it node1 sh
    docker container exec  -it node2 sh
    - 做以下幾件事
        1. ip a → 取得自己的 ip
        2. ping 172.17.0.1 (Gateway)
        3. ping 另一台 node
        4. ping node2 （在此可以用 CONTAINER NAME
        5. docker container inspect MONGODB
        6. ping MONGODB（無法存取另一個 network 的容器

#### 正確使用 Network 建立 MongoDB

1. 建立新的 network
    - docker network create my-network
    
2. 創建新容器並連接 network
    1. **在此的 mongoDB 沒有對外 port**
    2. node 是使用 mongoDB Container name 進行連接
    3. 兩個是共用 network

```docker
docker run -d --name my-mongodb --network my-network mongo:latest
docker run -d -p 3000:3000 -e MONGODB_URL=mongodb://my-mongodb/mydatabase --network my-network casper723/2024-node-course-express-mongo
```

沒有正確連線時的除錯：

- 兩者是否有相連同一個 network
    - docker network inspect my-network
- 容器是否出錯
    - docker container inspect CONTAINER


其他補充：


#### --restart 參數選項
- no：容器退出後不會重啟。這是默認行為。
- always：無論退出代碼是什麼，總是重啟容器。這包括正常退出（退出代碼為0）的情況。
- unless-stopped：容器只要沒有被手動停止，無論退出代碼是什麼，都會重啟。如果容器被手動停止，則不會重啟。


## 最後：請在雲服務上重新建構一次