## Docker Basic ### 概念 #### 什麼是 Docker 與 Container - **Docker** 是一項使用容器化 (containerization) 技術,為應用程式封裝獨立運行環境的成熟部署技術。 - **Container (容器)**: - 是一個獨立的應用程式運行環境,它包含了應用程式本身、其所有依賴庫 (dependency) 以及所需的文件系統 (file system)。 - 每個容器都在一個被稱為 host machine 的主機上運行。 - 應用:實現開發、測試、生產環境的一致性,解決「在我電腦上可以跑」的問題。 #### Container vs. Virtual Machine - **設計目的**:兩者都旨在提供隔離的應用程式執行環境,但實現方式不同。 - **運作原理與架構差異**: 1. **Docker 容器**: - **架構**:多個容器直接在主機的作業系統上運行,由 `Docker` 軟體進行管理。所有容器共享同一個主機的作業系統內核 (kernel)。 - **組成**:每個容器僅包含應用程式、依賴庫和文件系統。 - **優點**: - **輕量 (Lightweight)**:由於不包含完整的作業系統,體積更小。 - **高效 (Efficient)**:啟動速度快,通常在秒級內完成。 - **資源佔用少**:共享內核,減少了系統資源的開銷。 2. **傳統虛擬機 (Virtual Machine)**: - **架構**:每個虛擬機都包含一個完整的客座作業系統 (Guest OS),運行在一個稱為 `Hypervisor` 的虛擬化層之上。 - **組成**:每個虛擬機都包含應用程式、依賴庫、文件系統以及一個完整的作業系統。 - **缺點**: - **笨重**:體積龐大,通常為 GB 等級。 - **啟動慢**:需要啟動完整的作業系統,速度較慢。 - **資源消耗大**:每個虛擬機都需要分配獨立的系統資源。 - **架構比較** - **Docker 容器架構**: ![docker.](https://hackmd.io/_uploads/H1aO4-trxe.png) - **傳統VM架構**: ![VM](https://hackmd.io/_uploads/Skft4-tHlg.png) #### Image (映像) - **基本概念**: - `Docker Image` 是一個特殊的檔案系統,除了提供容器執行時間所需的程式、函式庫、資源、設定等檔案外,還包含了一些為執行時間準備的一些設定參數(如匿名磁碟區、環境變數、使用者等)。映像不包含任何動態數據,其內容在建置之後也不會被改變。 - **軟體安裝包**:可以將 `Image` 看作軟體的安裝包 (例如 `.exe` 或 `.dmg` 檔案),而 `Container` 則是安裝後實際運行的軟體實例。 - **分層儲存**: `image` 包含作業系統完整的 root 檔案系統,因此在 Docker 設計時,就充分利用 Union FS 的技術,將其設計為分層儲存的架構。所以嚴格來說,映像並非是像一個 ISO 那樣的打包文件,映像只是一個虛擬的概念,其實際體現並非由一個文件組成,而是由一組文件系統組成,或者說由多層文件系統聯合組成。 #### Registry (註冊表) 一個集中的儲存、分發映像的服務,可以讓我們在其它伺服器上使用已建立好的映像。`Docker Registry` 中可以包含多個倉庫 `Repository`,每個倉庫可以包含多個標籤 `Tag`,每個標籤對應一個映像。通常一個倉庫會包含同一個軟體不同版本的鏡像,而標籤就常用於對應該軟體的各個版本。 ![image](https://hackmd.io/_uploads/ry440Gtrxe.png =50%x) 最常使用的 `Registry` 公開服務是官方的 `Docker Hub`,除此之外,還有 Red Hat 的 `Quay.io`、Google 的 `Google Container Registry`、GitHub 推出的 `ghcr.io`。 ### 安裝 #### 環境說明 - **原生支援**:Docker 原生是基於 Linux 內核的容器化技術 (利用 `cgroups` 和 `namespaces` 等特性)。 - **Windows/Mac 支援**: - 在 `Windows` 和 `Mac` 電腦上,`Docker` 透過虛擬化一個輕量級的 `Linux` 子系統來運行。 - 為了獲得最佳效能與相容性,建議在 `Linux` 系統的主機上進行 `Docker` 的操作。 #### 在 Linux 系統上安裝 Docker - **方法**:使用官方提供的一鍵安裝腳本,腳本會偵測您的 Linux 發行版和版本並為您設定套件管理系統。預設情況下,腳本會安裝 Docker、containerd 和 runc 的最新穩定版本。 - **步驟**: 1. **下載安裝腳本**: ```bash curl -fsSL https://get.docker.com -o install-docker.sh ``` 2. **執行安裝腳本**: ```bash sudo sh install-docker.sh ``` ### 指令 #### pull - **作用**:從 `Docker Registry` 下載指定的 `Docker Image` 到本地。 - **指令結構**: ```bash docker pull [registry]/[namespace]/[image]:[tag] ``` - **`registry` (倉庫地址)**:指定映像所在的倉庫。例如 `docker.io` 是 `Docker Hub` 的地址。如果省略預設為 `Docker Hub`。 - **`namespace` (命名空間/作者名)**:用於區分不同作者上傳的同名映像。`Docker Hub` 上的官方映像,其 `namespace` 為 `library`,可以省略。 - **`Image` 映像名**:映像的名稱,例如 `nginx`。 - **`tag` (標籤/版本號)**:指定映像的版本。如果省略或使用 `:latest`,則表示下載最新版本。 - **-\-platform 參數**: - 作用:可以指定拉取特定 `CPU` 架構的映像。 - 範例:拉取 `arm64` 架構的映像。 ```bash docker pull --platform=linux/arm64 nginx ``` #### images & rmi - **`docker images`**: - **作用**:列出本地已經下載的所有 `Docker Image`。 - **`docker rmi` (remove image)**: - **作用**:刪除本地的一個或多個 `Docker Image`。 - **語法**:`docker rmi [映像名或映像ID]` - **範例**: ```bash # 刪除 nginx 映像 sudo docker rmi nginx # 使用映像 ID 刪除 sudo docker rmi 1e5f3c5b981a ``` #### run - **作用**:這是 Docker 最核心的指令,它使用指定的映像創建一個新的容器,並運行它。 - **基本語法**:`docker run [OPTIONS] IMAGE [COMMAND] [ARG...]` - **運行模式**: 1. **前景運行 (Foreground Mode)**: - 容器會佔用當前的終端機窗口,並將日誌直接輸出到此窗口。 - 當關閉此窗口或使用 `Ctrl+C` 時,容器會停止。 - 範例:`sudo docker run nginx` 2. **背景運行 (Detached Mode)**: - 使用 `-d` 參數,容器會在背景運行,不會佔用當前終端。 - 控制台會返回一個完整的容器 `ID`。 - 範例:`sudo docker run -d nginx` - **重要參數**: - **-p (Port Mapping)**: - **作用**:將主機的端口映射到容器的端口,讓外部可以訪問容器內的服務。 - **語法**:`-p [主機端口]:[容器內端口]` - **範例**: ```bash # 將主機的 80 端口映射到容器的 80 端口 sudo docker run -d -p 80:80 nginx ``` 這樣訪問主機的 `http://localhost:80` 就等於訪問了容器內的 `nginx` 服務。 - **-v**: - **作用**:將主機的目錄(Bind Mount)或使用 Volume 掛載到容器內的目錄,實現數據的持久化保存和共享。 - **動機**:容器被刪除後,其內部的所有數據都會遺失。主機目錄或 volume 可以保存數據,即使容器被刪除,數據依然存在。 - **語法**:`-v [主機目錄]:[容器內目錄]`、`-v [volume名稱]:[容器內目錄]` - **範例**: ```bash # 1. 將主機的 /website/html 目錄掛載到容器的 /usr/share/nginx/html sudo docker run -d -v /website/html:/usr/share/nginx/html nginx:alpine ``` >Bind Mount: 修改主機 `/website/html` 目錄下的文件,會即時同步到容器內,反之亦然。適合**開發環境**,讓開發者可以即時修改程式碼並立即看到變更結果。 ```bash # 2. 將名為 my-vol 的 volume 掛載到容器的 /usr/share/nginx/html sudo docker run -d -v my-vol:/usr/share/nginx/html nginx:alpine ``` >Volume: Docker 會在內部創建一個持久化存儲區,並將其掛載到容器內。適用於**資料庫**、上傳文件等需要持久存儲的應用場景。 - **-\-name**: - **作用**:為容器指定一個自定義的、易於記憶的名稱。 - **規則**:名稱在整個主機上必須是唯一的,不能重複。 - **範例**: ```bash sudo docker run -d --name my_nginx nginx ``` - **-e (Environment Variable)**: - **作用**:向容器內部傳遞環境變量,常用於設定資料庫密碼、API 金鑰等敏感資訊。 - **範例**:啟動一個 `mongodb` 容器並設定 root 帳號密碼。 ```bash sudo docker run -d \ -e MONGO_INITDB_ROOT_USERNAME=tech \ -e MONGO_INITDB_ROOT_PASSWORD=shrimp \ mongo ``` - **-it & -\-rm**: - **`-it`**: 結合了 `-i` (interactive) 和 `-t` (tty),可以讓使用者進入容器的交互式終端 (`shell`)。 - **`--rm`**: 表示容器在停止後自動刪除。 - **組合用途**:通常用於臨時調試,進入容器查看內部狀態,退出後容器自動清理,不留痕跡。 - **範例**: ```bash sudo docker run -it --rm alpine ``` #### 容器生命週期管理 - **docker ps**: - **作用**:查看正在運行的容器。`ps` 是 `process status` 的縮寫。 - **docker ps -a**: 使用 `-a` 參數可以查看所有容器,包括已停止的。 - **docker stop**: - **作用**:停止一個正在運行的容器。 - **語法**:`docker stop [容器ID或名稱]` - **docker start**: - **作用**:啟動一個已經停止的容器。 - **說明**:啟動時,容器會保留上次停止前的所有配置,如端口映射、掛載卷等。 - **docker rm**: - **作用**:刪除一個或多個容器。 - **語法**:`docker rm [容器ID或名稱]` - **強制刪除**:對於正在運行的容器,需要加上 `-f` 參數才能強制刪除。 #### 容器調試 - **docker logs**: - **作用**:查看容器的日誌輸出。 - **語法**:`docker logs [容器ID或名稱]` - **持續追蹤**:使用 `-f` (`--follow`) 參數可以持續滾動顯示最新的日誌,類似 `tail -f`。 - **docker exec**: - **作用**:在一個正在運行的**容器內部**執行命令,非常適合用於調試。 - **語法**:`docker exec [OPTIONS] CONTAINER COMMAND` - **範例**: 1. **執行單一指令**:在 `mongodb` 容器內查看進程。 ```bash sudo docker exec [容器ID] ps -ef ``` 2. **進入交互式終端**:使用 `-it` 參數可以進入容器的 `shell` 環境。 ```bash sudo docker exec -it [容器ID] /bin/sh ``` 進入後,就可以像在一個獨立的 `Linux` 系統中一樣執行命令。 ### Dockerfile #### 概念 Dockerfile 是一個文本文件,其中包含了一系列指令,用於描述如何從一個基礎映像 (base image) 開始,一步步構建出一個新的 Docker 映像。 #### 常用指令 - **FROM**: - **作用**:指定新映像所基於的基礎映像。Dockerfile 的第一行必須是 `FROM` 指令。 - **範例**:`FROM python:3.13-slim` - **WORKDIR**: - **作用**:設定後續指令 (如 `RUN`, `COPY`, `CMD`) 在容器內執行的工作目錄。類似於 `cd` 命令。 - **範例**:`WORKDIR /app` - **COPY**: - **作用**:將主機上的文件或目錄複製到映像內的指定路徑。 - **語法**:`COPY <src> <dest>` - **範例**:`COPY . .` (將當前目錄所有文件複製到映像的工作目錄 `/app` 中) - **RUN**: - **作用**:在構建映像的過程中執行指定的命令,例如安裝依賴。 - **範例**:`RUN pip install -r requirements.txt` - **EXPOSE**: - **作用**:聲明容器在運行時會監聽的網路端口。這是一個文檔性質的指令,僅用於告知使用者,實際的端口映射仍需在 `docker run` 時使用 `-p` 參數指定。 - **範例**:`EXPOSE 8000` - **CMD**: - **作用**:指定容器啟動時預設執行的命令。一個 `Dockerfile` 中只能有一個 `CMD` 指令,如果有多個,只有最後一個會生效。 - **格式推薦**:推薦使用 `exec` 格式,以避免 `shell` 解析帶來的問題。 - **範例**:`CMD ["python3", "main.py"]` #### 構建與推送映像 - **docker build**: - **作用**:根據 `Dockerfile` 構建映像。 - **語法**:`docker build -t [image:tag] [Dockerfile path]` - **範例**:在當前目錄下構建映像。 ```bash docker build -t docker_test . ``` - **docker push**: - **作用**:將本地構建好的映像推送到遠端的 `Docker Registry` (如 `Docker Hub`)。 - **前置步驟**: 1. **登入**:使用 `docker login` 登入倉庫。 2. **標記映像**:推送前,映像名稱必須符合 `[namespace]/[image]` 的格式。可以使用 `docker tag` 命令為已有的映像創建一個新的標籤,或者在 `build` 時直接指定。 ### 網路 #### Bridge (橋接模式) - **概念**:這是 `Docker` 的預設網路模式。`Docker` 會創建一個虛擬的網路橋 (`docker0`),所有容器都連接到這個橋上,形成一個獨立的內部子網。 - **運作方式**: - 每個容器都會被分配一個該子網內的 `IP` 地址(例如 `172.17.0.x`)。 - 同一 `bridge` 網路內的容器可以通過內部 `IP` 地址互相訪問。 - 容器網路與主機網路是隔離的,外部無法直接訪問容器。需要通過 `-p` 參數進行端口映射。 - **自定義 Bridge 網路**: - **指令**:`docker network create [網路名]` - **優點**: - 可以更好地隔離不同的應用。 - 在同一個自定義網路內,容器之間可以通過**容器名**直接進行通信,`Docker` 內建了 `DNS` 服務來解析容器名到其內部 `IP`。 #### Host 模式 - **概念**:容器不再擁有自己獨立的網路空間,而是直接共享主機的網路。 - **運作方式**: - 容器直接使用主機的 `IP` 地址和端口。 - 不需要進行端口映射(`-p` 參數無效)。 - 容器內的服務直接運行在主機的端口上。 - **啟動方式**:`docker run --network host ...` - **優點**:網路效能最好,因為沒有中間的虛擬化層。 - **缺點**:犧牲了網路層面的隔離性。 - **應用場景**:適用於對網路效能要求極高且可以接受較弱隔離性的場景。 #### 網路指令 - `docker network ls`: 列出所有 `Docker` 網路。 - `docker network create [網路名]`: 創建一個新的 `bridge` 網路。 - `docker network rm [網路ID或名稱]`: 刪除一個自定義網路。 ### Compose Docker Compose 是一個用於定義和執行多容器應用程式的工具。它使用 YAML 檔案格式定義應用程式,使得管理多個 Docker 容器變得更加簡單和高效。透過 Docker Compose,開發人員可以輕鬆地啟動、停止和管理複雜的應用程式,而無需手動配置和啟動每個容器。 #### 動機與場景 - **問題背景 (Why):** 現代應用程式,特別是採用微服務(Microservices)架構的系統,通常由多個相互協作的獨立服務構成。例如一個典型的 Web 應用可能包含: - **應用伺服器 (Web Server):** 處理 HTTP 請求與業務邏輯,如 Nginx, Node.js。 - **資料庫 (Database):** 負責持久化儲存應用數據,如 MySQL, PostgreSQL, MongoDB。 - **背景工作佇列 (Background Worker):** 執行非同步或耗時的任務,如 Celery, Redis。 - **傳統方法的挑戰:** 若未使用管理工具,開發者需為每個服務手動執行 `docker run` 指令,並自行處理容器間的網路設定、依賴關係與啟動順序。此過程不僅繁瑣、易出錯,且難以在不同環境中重現。 - **解決方案 (How):** Docker Compose 透過一個宣告式(declarative)的 YAML 設定檔(預設為 `docker-compose.yml`),讓開發者能統一定義整個應用程式的架構。之後僅需透過簡單的指令,即可一鍵式地啟動、停止、重建或監控所有相關的服務容器。 #### 優點 如何解決傳統 Docker 命令在管理多容器應用時的痛點,從而簡化開發流程、提升效率,並確保環境的一致性。 **1. 簡化容器管理 (Simplified Container Management)** - **設計動機:** 旨在將複雜、指令式的多容器啟動流程,轉化為單一、宣告式的操作,大幅降低手動管理的複雜度與出錯機率。 - **傳統流程(手動操作):** 開發者需要為每個容器依序執行 `docker run`,並手動設定網路、掛載 volume、傳遞環境變數等。 ```bash # 啟動資料庫容器 docker run -d --name mongo_db -p 27017:27017 mongo:latest # 啟動 Web 應用容器,並連結到資料庫 docker run -d --name web_app --link mongo_db:mongo -v $(pwd):/app -e TZ=Asia/Taipei web_app_image # 啟動背景爬蟲容器,也連結到資料庫 docker run -d --name scraper --link mongo_db:mongo -e TZ=Asia/Taipei scraper_image ``` - **Docker Compose 流程:** 開發者僅需維護一份 `docker-compose.yml` 檔案,並執行單一指令即可完成所有服務的啟動與配置。 ```bash # 一鍵啟動所有在 docker-compose.yml 中定義的服務,並在背景運行 docker-compose up -d ``` **2. 提升開發效率 (Improved Development Efficiency)** - **核心概念:** 針對微服務架構的開發與測試週期進行優化,提供快速迭代與環境控制的能力。 - **技術流程與效益:** 1. **同步管理:** 使用 `docker-compose up`、`docker-compose down`、`docker-compose restart` 等指令,可以同時啟動、停止或重啟所有服務,顯著提升開發與除錯效率。 2. **依賴關係管理:** Docker Compose 會自動處理服務間的依賴關係。開發者可以在設定檔中明確指定服務的啟動順序。 3. **快速環境建置:** 允許開發者快速建立一個完整的、隔離的開發或測試環境,並在任務完成後迅速銷毀,不對主機系統造成污染。 - **語法範例:** - **問題:** `web` 服務的正常運行依賴於 `mongodb` 服務必須先啟動完成。 - **解決方案:** 在 `docker-compose.yml` 中使用 `depends_on` 關鍵字來定義此依賴關係。 ```yaml version: '3.8' services: web: build: . ports: - "8000:5000" depends_on: - mongodb # 確保 mongodb 服務先於 web 服務啟動 mongodb: image: mongo:latest ports: - "27017:27017" ``` **3. 環境一致性 (Environment Consistency)** - **問題背景:** 解決軟體開發中最經典的「It works on my machine」問題。此問題通常源於開發、測試、生產環境之間的細微差異,如作業系統、函式庫版本、環境變數設定不一致等。 - **設計目的:** 透過將環境配置「程式碼化(Infrastructure as Code)」,確保應用程式在任何支援 Docker 的機器上都能以完全相同的方式運行。 - **實作細節:** `docker-compose.yml` 檔案扮演了「Single Source of Truth」的角色,它統一定義了: - **服務映像檔版本:** 使用 `image: mongo:5.0` 等標籤,可以固定服務所使用的軟體版本。 - **網路與儲存配置:** 統一設定容器間的網路模式與資料卷(volume)掛載。 - **環境變數:** 透過 `environment` 區塊,為不同環境(開發、測試)設定一致的配置,如資料庫連線資訊、API Key、時區等。 ```yaml services: web_app: image: my_web_app:1.2 environment: - DATABASE_HOST=mongodb - TZ=Asia/Taipei mongodb: image: mongo:5.0 # 固定 MongoDB 版本 ``` - **應用價值:** 確保從開發人員的本機,到 CI/CD pipeline 的自動化測試,再到最終的生產環境部署,整個應用程式堆疊(stack)的配置都是可預期且一致的。 #### 指令 **常用參數:** 這些參數在執行具體的子指令(如 `up`, `down`)之前被解析,用以控制 `docker compose` 工具本身的行為。 - -f : - **功能:** 指定要使用的 Compose 範本檔案路徑。預設情況下,Compose 會在當前目錄尋找 `docker-compose.yml` 或 `compose.yml`。 - **設計動機:** 允許開發者將設定檔拆分成多個檔案,例如一個基礎設定檔 `base.yml` 和一個開發專用 `dev.override.yml`,便於管理不同環境的配置。可以多次使用此選項來疊加設定。 - -p : - **功能:** 指定專案名稱。預設使用當前目錄的名稱。 - **設計動機:** Docker Compose 會使用專案名稱作為它所建立的容器、網路等資源的前綴(例如 `myproject_web_1`),指定專案名稱可以避免在同一個環境中因目錄名稱相同而導致的衝突,也讓資源標識更具語義化。 **生命週期管理:** 管理整個應用程式堆疊(Stack)從啟動到銷毀的核心指令。這些是日常開發中最頻繁使用的指令。 - **up** : 自動化地建置、(重新)建立、啟動並附加到 `docker-compose.yml` 中定義的所有服務。 - 流程: 1. 讀取 `docker-compose.yml` 設定檔。 2. 檢查服務所需的網路是否存在,若無則建立。 3. 檢查服務所需的映像檔(image),若本地不存在且設定檔中有 `build` 指示,則執行建置;若無 `build` 指示,則從 registry `pull` 下來。 4. 建立並啟動所有服務的容器,並依照 `depends_on` 處理依賴順序。 5. 預設在前台運行,會將所有容器的 log 輸出到當前終端,方便即時偵錯。 - 常用選項: - `-d, --detach`: 在背景啟動並運行所有容器(detached mode),這是生產環境或日常開發中推薦的使用方式。 - `--force-recreate`: 強制重新建立容器,即使容器的配置或映像檔沒有任何變更。常用於確保環境徹底重置。 - `--no-recreate`: 如果容器已經存在,則不進行重新建立。此選項僅會啟動已停止的容器。 - `--no-deps`: 不自動啟動所依賴的服務。例如只重啟 `web` 服務而不影響其依賴的 `db` 服務。 - `--build`: 在啟動容器前,強制重新建置服務的映像檔。 - **down** : 停止並移除由 `up` 指令所建立的容器、網路、以及預設的 volume。 - 與 stop 的差異: - stop: 僅停止容器運行,但保留容器實體與網路等資源,可隨時透過 `start` 再次啟動。 - down: 徹底清除,停止容器後將其移除。這是一個更徹底的清理操作,能釋放所有相關資源。 - 常用選項: -v: 同時移除在 `docker-compose.yml` 中定義的具名 volume。預設情況下,`down` 不會刪除 volume 以防止資料遺失。 **服務建置/執行:** - **build:** 根據 `docker-compose.yml` 檔案中各服務的 `build` 指令,建置或重新建置其對應的 Docker image。 - **應用場景:** 當你修改了應用程式的原始碼或 `Dockerfile` 後,需要執行此指令來產生新的映像檔,然後再透過 `docker compose up` 來使用新映像檔啟動容器。 - **常用選項:** - --no-cache: 在建置映像檔時不使用任何快取(cache),確保每一層都是重新建置。適用於解決快取導致的建置問題。 - --pull: 在建置前,總是嘗試拉取基礎映像檔的更新版本。 - **run:** 為某個服務建立一個新的容器,並在其中執行一次性命令。該命令會覆蓋服務在 `docker-compose.yml` 中預設的 `command` 或 `entrypoint`。 - **應用場景:** - 執行資料庫遷移: `docker compose run --rm web python manage.py migrate` - 執行測試: `docker compose run --rm web pytest` - 預設會啟動其依賴的服務(如資料庫),確保命令能在完整的環境中運行。 - **常用選項:** - `--rm`: 命令執行完畢後自動刪除該一次性容器。 - `--no-deps`: 不自動啟動此服務所依賴的其他服務。 - `-e KEY=VAL`: 設定臨時的環境變數。 --- Reference: https://docs.docker.com/get-started/docker-overview/ https://vuepress.mirror.docker-practice.com/ https://youtu.be/_-IPi1a774E?si=k2Vo87w5Wk595e3s https://realnewbie.com/basic-concent/architecture/docker-compose-beginner-guide-sample-usage/