# 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端口上可訪問,而不會干擾到其他已經在使用的服務。 ### ###