---
# System prepended metadata

title: Docker 語法

---

# Docker 語法

### Docker Images 作用
1. **靜態檔案**: Docker Image 是一個read-only的模板，用於建立container。它包含了運行應用程式所需的一切：包括程式碼、依賴項、庫、環境變數、設定檔等等。

2. **版本控制和共用**: Docker Images 可以版本化，你可以根據需要建立不同版本的image。你也可以將映像推送到 Docker Hub 或私有倉庫中，方便在不同的環境中共用和使用。

3. **建置基礎**: image是透過 `Dockerfile` 建構的，`Dockerfile` 中包含了指令，用於定義如何建立該image。你可以從基礎鏡像（如官方的 Python 或 Ubuntu 映像）開始，逐步加入所需的依賴和應用程式。

### Containers 作用
1. **執行時期實例**: Docker Container 是 Docker Image 的一個可運行的實例。當你啟動一個容器時，Docker 會基於指定的image啟動一個獨立的環境（類似於虛擬機，但更輕量），這個環境包含映像中的所有內容，並且是可read、write的。

2. **隔離和資源控制**: container提供了應用程式運行時的隔離，確保它們在彼此之間以及與主機系統之間隔離。你可以為container分配 CPU、記憶體等資源，確保它們不會互相干擾。

3. **短暫性和可擴展性**: 容器通常是短暫的，你可以很容易地停止、刪除它們，然後再基於同一鏡像啟動新的容器。這使得應用程式的部署和擴展變得非常容易。

### 區別



|      | Image | Container |
| ---- | ----- | --------- |
| 性質 | static的document      |   動態的instance        |
|  用途   | 建置和分發應用程式的環境      | 實際運作的環境          |
| 可讀寫性 | read-only  | read、write      |




### Docker Compose 的作用
> Docker Compose 是 Docker 的一個工具，它用來定義和管理多個container的應用程式。使用 Docker Compose 時，你可以透過一個 `docker-compose.yml` 檔案定義應用程式的服務、網路、磁碟區等配置，並用一條指令啟動整個應用程式
>
1. **多容器管理**: Docker Compose 允許你在一個設定檔中定義多個服務（容器），並透過一個指令 (`docker-compose up`) 一次啟動它們。對於複雜的應用程序，如微服務架構，這特別有用。

2. **環境配置**: 你可以在 `docker-compose.yml` 檔案中定義每個容器的環境變數、連接埠對映、磁碟區掛載等設定。這使得配置和管理多容器應用變得非常簡單和直覺。

3. **依賴管理**: 透過 Docker Compose，你可以指定service之間的依賴關係。例如，可以設定資料庫服務在應用程式服務啟動之前先行啟動。

4. **不存在的image可以透過 Docker Compose 會自動去抓**
當你在`docker-compose.yml` 檔案中定義了一個服務，並指定了image的名稱，如果該image在你的本機上不存在，Docker Compose 會自動從Docker Registry（例如Docker Hub）中pull這個image。這個過程是自動完成的，無需手動操作。



- Image: 只有執行的程序和所需的檔案
- Container: 有獨立的OS環境去執行這些程序
- Dockerfile: 寫好腳本可自動化部署應用程式




### Docker commit 作用
> 使用 `docker commit`的主要目的是將在container中更改保後的結果存為一個新的 Docker image，這樣你就可以保留這些更改，並在以後重複使用，不然一旦離開container後，先前的更改也都會消失

### 具體用途和意義：

1. **保留修改後的環境**:
   當你在容器中進行了一些更改（例如安裝軟體包、修改配置文件、生成新的數據或設置環境變量）後，這些更改只會存在於container的生命週期內。一旦你刪除或stop容器，這些更改就會丟失。透過 `docker commit`，你可以將這些更改保存為一個新的image，這樣你就能在以後基於這個image啟動新的container，而不必每次都重新配置環境。

2. **創建自訂的image**:
   如果你需要為你的應用程式創建一個定制的環境，可能需要安裝特定的軟體、庫或工具。透過在容器中完成這些配置並使用 `docker commit`，你可以創建一個包含所有必要配置的定制映像。以後每次你都可以使用這個映像啟動容器，而不必重複相同的安裝和配置過程。

3. **備份工作狀態**:
   在開發過程中，你可能在容器中進行了許多實驗或配置調整。透過 `docker commit`，你可以隨時保存容器的當前狀態。如果後續的更改導致問題，你可以隨時回到之前保存的狀態，類似於創建了一個系統快照。

```
docker commit <容器ID或容器名稱> <想要的Image名稱>:<標籤>
```
eg. 
```
docker commit d373235cc633 ollama_respond_time
```
> 將container ID:d373235cc633 建立一個image名為ollama_respond_time

### 示例場景：
假設你在一個容器中安裝了一個複雜的應用程式，並且配置了大量的依賴項。你不希望在其他伺服器上每次都重複這些步驟。通過 `docker commit`，你可以將這個容器保存為一個新的映像，接著可以在其他環境中快速部署這個應用程式，確保所有依賴項和配置都保持一致。

### 總結
`docker commit` 允許你將容器中所有的更改打包成一個新的映像，這樣你就可以保留、分發、共享或在其他地方重複使用這些更改的成果。這是 Docker 提供的一種便捷工具，特別適合在開發和調試階段使用。
```
docker run hello-world    
```
- docker run: 建立一個container 並執行 (而container必須基於image建立，所以後面必須接image名稱)
- image: hello-world
> 步驟
> 1. 先確認 hello-world 是否存於本機的repository，本機找不到的話，就從遠端repository下載
> 2. 確認好image存在或下載完成後，即可建立container並執行

### 查看container相關資訊
```
docker ps -a
```
> ps 即 process status，用來顯示目前運行中的 Docker 容器的資訊。它提供了關於所有正在運行的容器的詳細概述，包括容器的 ID、創建時間、使用的鏡像、命令、最近的狀態、端口映射以及自定義的名稱等信息

![image](https://hackmd.io/_uploads/HyaBJVUlR.png)
> 欄位NAMES 預設是亂碼組合，為形容詞加上科學家或駭客的人名組合


![image](https://hackmd.io/_uploads/ryhD-E8eA.png)

### 移除Container
> 可使用 Container ID 或 NAMES
```
docker rm 410b0e4955b1
```
或
```
docker rm vigorous_moser
```

### 移除image
> 移除image前，必須先確認對應的container是否先移除，不然可能會失敗 (因為image被container包住，要先移除外殼)
```
docker rmi hello-world
```

### run : 參數說明 or docker run --help 常用：
-i ：則讓容器的標準輸入保持打開
-t：讓Docker分配一個虛擬終端（pseudo-tty）並綁定到容器的標準輸入上
-d：背景執行
-e：設定環境變數(AAA=BBB)
-p：Port 對應(host port:container port)
-v：資料對應(host folder:container folder)
--name：設定容器名稱

### exec : 參數說明 or docker exec --help 常用：
-i ：則讓容器的標準輸入保持打開
-t：讓Docker分配一個虛擬終端（pseudo-tty）並綁定到容器的標準輸入上
-e：設定環境變數(AAA=BBB)



## 不使用docker run 指令的步驟
### 1. 先查看image是否存在
```
docker images   //查看所有images
```


```
docker images busybox //查看特定images是否存在
```
### 2. 下載image
```
docker pull busybox
```



### 3. 建立container
```
docker create -i -t --name foo busybox
```
- -i、-t: 當需要跟container的應用程式互動時，需要加入的參數
- name: 可以幫container命名，必須唯一
- 最後一個參數: 為image的名稱:busybox

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


### 4. 執行container
```
docker start -i foo
```
> 就馬上進入了container的環境 (一律使用Linux語法去跟container去互動)

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

#### 想要離開container環境中: 
按 `Ctrl + P` 與 `Ctrl + Q` 即可

- 離開後，可用 `docker ps` 觀察狀態是 up ，代表正常運行中
- 離開container後，又回去環境中: `docker attach foo`


### 5. 停止container
#### 方法1: 
1. 在container 環境中，輸入`exit`
![image](https://hackmd.io/_uploads/SkkwO4LgR.png)
> 就代表以正常結束
2. 再用 `docker rm` 指令移除container 即可 
```
docker rm foo
```

#### 方法2:
1. 1. 在container 環境中，按 `Ctrl + P` 與 `Ctrl + Q`

![image](https://hackmd.io/_uploads/rklVQqV8xC.png)
> 此時，還可以靠 `docker attach` 回到container環境中

2. 輸入 ```docker stop foo```

![image](https://hackmd.io/_uploads/Syjs9EIxA.png)
> STATUS 0: 正常結束、非0:有錯誤

3. 再用 `docker rm` 指令移除container 即可 
```
docker rm foo
```

## Docker 管理小技巧
### 1. 自動移除container
```
docker run --rm --name web httpd
```
- Apache 的image名為httpd
- 加上參數 `--rm` 在中止它(Ctrl-C後，會自動移除container)

![image](https://hackmd.io/_uploads/Hk-K048eA.png)

### 2. 背景下執行container
> 使用參數 `-d` 或 `--detach`
> 
![image](https://hackmd.io/_uploads/S1s-xBIlA.png)

或

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



### 強制移除啟用中(Up)的container
> 加上參數 `-f` 即可，(不然正常Up的container不能直接移除，必須狀態是Exited 才可
```
docker rm -f web
```

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

### `curl` (Client URL)
> 用於在網路上傳輸資料。它支援許多不同的協議，包括 HTTP、HTTPS、FTP、FTPS 等。 curl 最常用於執行 Web 伺服器的請求，可以用來下載網頁、文件，或與 REST API 互動。

> eg. 擷取網頁內容
```
curl http://example.com
```

##### 如果你已經在你的Ubuntu VM中安裝了Docker，並且只需要重新啟動它
```
sudo systemctl start docker
```

#### 設置Docker開機自啟動（可選）
```
sudo systemctl enable docker
```





## 利用Docker 抓遊戲
1. 獲取遊戲程式碼: ex.貪吃蛇
```
git clone https://github.com/patorjk/JavaScript-Snake.git
```
2. 創建 Dockerfile
> 要跟此遊戲程式碼資料夾放一起。
> 以下為Dockerfile檔案內容，檔名為Dockerfile
```
# 使用官方的 Nginx images
FROM nginx:alpine

# 將當前目錄下的文件複製到容器內的Nginx服務器目錄
COPY . /usr/share/nginx/html
```
> - 從 Docker Hub 下載官方的 Nginx 映像，作為image 
> - 複製目前目錄（即 JavaScript-Snake 目錄）中的所有內容到鏡像內的 /usr/share/nginx/html 目錄下。

- 在 Windows 命令列中，你可以執行以下命令來確認你的目前目錄並列出目錄內容：
```
cd C:\Users\yifun\JavaScript-Snake  # 確保在正確的目錄
dir  # 列出目錄內容，查看是否有 Dockerfile
```
3. 建置 Docker 映像
```
docker build -t snake-game .
```
> - docker build 告訴 Docker 開始建置過程。
> -  -t snake-game 為新建立的映像設定了一個標籤（tag），這裡使用的是 "snake-game"。 
> - 最後的 . 表示 Docker 建置上下文的位置在目前目錄。

4. 運行 Docker 容器
```
docker run --name snake -d -p 8080:80 snake-game
```
- **`docker run`** 告訴 Docker 運行一個新的容器。
- **`-name snake`** 為新的容器設置了名稱 "snake"。
- **`d`** 表示啟動一個守護程式模式的容器（在背景執行）。
- **`p 8080:80`** 是端口映射，把主机的 8080 端口映射到容器的 80 端口，Nginx 默認監聽在 80 端口。
- **`snake-game`** 指定使用你之前建構的 "snake-game" 镜像。

5. 玩遊戲
開啟瀏覽器，訪問 `http://localhost:8080` 即可玩遊戲
> 這意味著 Nginx 正在你的 Docker 容器中運行，並成功地提供了遊戲檔案。

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


6. 清除資源
```
docker rm -f snake2           # 刪容器
```
```
docker rmi snake-game         # 刪image
```
---
### Docker image 去 Run
1. 在CMD中進入該資料夾位置
`cd "資料夾位置"`

2. 建立image
```
docker build -t 映像檔名稱 .
```

3. 啟動image
> 要先確認一下容器端口與主機映射到的位置是多少
( 看Dockerfile 和 app.py)
```
docker run -p 8000:8000 audio_to_text
```

4. 搜尋 http://localhost:8000/



---
### 將Flask 框架的網頁推上docker hub供使用
> 概念: 將整個資料夾包成image，push到docker hub上供任何人使用，若有人要使用的話，則先建立一個docker container，將資源pull下來至本地端使用

流程:
1. 資料夾中先寫好Dockerfile
ex. 結構如下
![image](https://hackmd.io/_uploads/rJdZVqX_C.png)

```
# 使用官方Python映像
FROM python:3.9-slim

# 設置工作目錄
WORKDIR /app

# 複製當前目錄的內容到工作目錄
COPY . /app

# 安裝所需的Python庫
RUN pip install --no-cache-dir -r requirements.txt

# 暴露 Flask 預設的運行端口
EXPOSE 5000

# 定義環境變量
ENV FLASK_APP=app.py

# 運行Flask應用
CMD ["flask", "run", "--host=0.0.0.0"]

```


2. 進入該資料夾中
```
D:

cd audio_to_text
```

3. 建立image
`docker build -t audio_to_text .`

4. 登陸Docker Hub
```
docker login
```

5. 標記Docker image
`docker tag audio_to_text YOUR_DOCKERHUB_USERNAME/audio_to_text:latest`



```
docker tag audio_to_text xxx/audio_to_text:latest
```


6. 推送docker image 到Docker hub上
```
docker push YOUR_DOCKERHUB_USERNAME/audio_to_text:latest
```

eg. 

`docker push xxx/audio_to_text:latest
`

Image成功在Docker Hub上運行

---

#### 其他人若想使用
1. 從Docker Hub上拉下來使用
> 將 xxx/audio_to_text:latest 映像從Docker Hub拉取到本地Docker存儲庫
```
docker pull YOUR_DOCKERHUB_USERNAME/audio_to_text:latest
```
eg. 
```
docker pull xxx/audio_to_text:latest
```

2. 運行container
> 創建並啟動一個新的容器，將本地端口5000映射到容器內部的端口5000
```
docker run -d -p 5000:5000 YOUR_DOCKERHUB_USERNAME/audio_to_text:latest
```

eg. 
```
docker run -d -p 8000:8000 xxx/audio_to_text:latest
```

eg. 可自動存資料夾在本地端
```
docker run -d -p 8000:8000 -v D:\store\uploads:/app/uploads -v D:\store\processed:/app/processed -v D:\store\transcripts:/app/transcripts audio_to_text
```


記得要開8000 port
> 先在VM 中允許
```
sudo ufw enable   //啟用ufw firewall

sudo ufw allow 8000/tcp   //允許來自8000 port 的TCP 流量

sudo ufw reload  //設定完後，重新載入規則

sudo ufw status  //檢查ufw狀態

```


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


若memory不夠，可以下指令去增加
```
docker run -d --memory="8g" -p 8000:8000 your_image

```

---

### 應用整合到現有的Nginx和Docker Compose環境中的完整步驟：
> 為了讓公司電腦也可以使用

### 步驟1：更新Docker Compose文件
> 在 docker-compose.yml 中為你的 Flask 應用設定一個非 80 的端口映射，比如 8000，這樣 Nginx 就可以將請求從 80 端口轉發到這個端口

```yaml
services:
  audio_tool:
    image: <your-username>/<image-name>:latest
    env_file:
      - .env  # 如果希望從一個文件中加載所有環境變量
    environment:
      - FLASK_ENV=production
      - SECRET_KEY=your_secret_key
    ports:
      - "8000:8000"
    mem_limit: 8g
    restart: always

```

或

```
services:
  audio_tool:
    image: <your-username>/<image-name>:latest
    command: ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "--timeout", "3600", "app:app"]
    ports:
      - "8000:8000"
    mem_limit: 8g
    restart: always

```

### 步驟2：配置Nginx
配置你的 Nginx 以將從 80 端口接收到的請求轉發到 Docker 容器的 8000 端口，修改`nginx/conf.d/default.conf.template`文件，添加一段配置以將`/audio_tool`的請求轉發到你的應用。將以下配置插入到合適的`server`塊中：

```nginx
server {
    listen 80;
    server_name example.com;  # 你的域名或 IP (VM公有IP)

    location /audio_tool {
        proxy_pass http://localhost:8000;  # 轉發到 Flask 應用
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

這段配置告訴Nginx將所有指向`/audio_tool`的請求轉發到名為`audio_tool`的服務，該服務在容器中運行並監聽8000端口。

### 步驟3：重啟和部署
```
sudo systemctl restart nginx
```

### 步驟4：重啟和部署
修改完成後，重啟Docker Compose來使配置生效：

```bash
docker-compose down
docker-compose up -d
```

### 步驟5：測試和驗證
一旦容器和服務重新啟動，通過訪問以下URL來測試你的配置是否正確：

```
http://your_server_domain_or_IP/audio_tool
```

這樣的配置將使你的Flask應用通過Nginx反向代理在80端口上可訪問，而不會干擾到其他已經在使用的服務。



### 


### 