告別環境部署惡夢:精通 Docker Image,打造一致且可攜的應用程式

告別環境部署惡夢:精通 Docker Image,打造一致且可攜的應用程式

在現代軟體開發與部署的浪潮中,容器化技術已成為不可或缺的一環,而 Docker 更是其中的佼佼者。它徹底改變了我們建構、交付及執行應用程式的方式。在這整個生態系統的核心,便是 Docker Image (映像檔)。許多初學者常常對 Image (dockerimage)、Container (容器) 和 Dockerfile 這些東西感到困惑,不了解它們之間的確切關係與運作模式。

本文旨在提供一份全面且深入的 Docker Image 指南。我們將從最基礎的概念出發,詳細拆解 Docker Image 的本質,闡述它與 Docker Container 之間的緊密關係,並逐步引導您學習如何獲取、建立、管理及分享您的 Docker Image。無論您是剛踏入 Docker 世界的新手,還是希望鞏固核心知識的開發者,這篇文章都將為您提供清晰的指引和實用的知識,讓您能更自信、更有效率地駕馭 Docker。

Docker Image 的核心概念

什麼是 Docker Image?

Docker Image (docker 映像檔) 是一個輕量級、獨立且可執行的軟體套件,它包含了執行一個應用程式所需的一切元素:程式碼、執行環境 (runtime)、函式庫、環境變數和設定檔。您可以將它想像成一個「藍圖」或「快照」,這個藍圖詳細描述了一個軟體在特定環境下應該如何運行的所有細節。

更具體地說,一個docker 映像是唯讀 (read-only) 的範本。當您需要執行您的應用程式時,Docker 會根據這個唯讀的範本,建立一個可執行的執行個體,也就是我們熟知的 Docker Container (docker 容器)。這個設計確保了應用程式在任何地方都能以完全相同的方式運行,無論是在開發人員的筆記型電腦、測試伺服器,還是在雲端的正式環境中。這種一致性徹底解決了「在我電腦上可以跑,但在你那邊不行」的古老難題。

Image 與 Container 的關係:類別與實例

為了更好地理解 Image 和 Container 的關係,我們可以借用物件導向程式設計 (Object-Oriented Programming) 的概念,並透過下方的圖表來理解:

  • Docker Image 就好比是一個 類別 (Class)。它定義了一個物件的屬性 (properties) 和方法 (methods),但它本身並不是一個活生生的實體。
  • Docker Container 則像是這個類別的 實例 (Instance/Object)。它是 Image 的一個具體化、可運行的實例。您可以從同一個 Image 建立出任意多個獨立運作的 Container,就像您可以從同一個 Class 建立出多個 Object 一樣。

每個 Container 都是從 Image 建立的,並且在 Image 的唯讀層之上,增加了一個可寫層 (writable layer)。這意味著,您在容器內部所做的任何變更 (例如:寫入新檔案、修改設定),都只會影響該 Container 本身,而不會影響到原始的 Image 或其他從同一個 Image 建立的 Container。這種隔離性是容器化技術的關鍵優勢之一。

特性 Docker Image (映像檔) Docker Container (容器)
本質 唯讀的範本、藍圖、快照,也就是應用程式的映像 Image 的一個可執行的執行個體
類比 類別 (Class) 實例 (Instance/Object)
狀態 靜態的、不可變的 (Immutable) 動態的、可變的 (Mutable)
生命週期 被建立、儲存、分享 被啟動、執行、停止、刪除
儲存結構 由多個唯讀層 (read-only layers) 組成 在 Image 的唯讀層之上增加一個可寫層 (writable layer)
用途 用於打包和分發應用程式及其相依性 用於實際執行應用程式

獲取與建立 Docker Image

掌握了基本概念後,接下來我們將探討如何取得並建立您自己的 Docker Image。主要有兩種途徑:從遠端倉庫下載現成的 Image,以及透過 Dockerfile 自行客製化建立 Image。

從 Docker Hub 下載 Image

當我們今天要要使用 Docker Image 時,Docker Hub 就是一個很重要的下載來源與image 來源。這個的 docker hub是由 Docker 公司維護的官方公共映像檔倉庫,這個網站也是全世界最大的容器映像檔註冊中心。在 docker hub 上存放了數以萬計由官方組織 (如 Node.js、Python、Ubuntu) 或社群開發者所建立的 Image。將 Docker Hub 視為一個雲端服務,是獲取基礎 Image 最快速、最便捷的方式。

下載指令:docker pull

docker pull 指令用於從遠端倉庫 (預設為 Docker Hub) 下載指定的 Image 到您的本地端。

其基本語法為:

docker pull <image_name>:<tag>
  • <image_name>:映像檔的名稱,例如 nginx、ubuntu、hello-world。
  • <tag>:標籤,通常用來表示 Image 的版本號,也可以用來區分舊版本。例如 1.21、20.04。如果省略標籤,Docker 會預設抓取名為 latest 的標籤,這通常指向該 Image 的最新版本。

範例:下載 Nginx 的 Alpine 版本

Alpine Linux 是一個極度輕量化的 Linux 發行版,使用它作為基礎 Image 可以大幅縮小最終 Image 的體積。

docker pull nginx:alpine

執行後,Docker 會開始下載指定的 Image 層,完成後您就可以在本地端使用這個 Image 了。

使用 Dockerfile 建立客製化 Image

當現成的 Image 無法完全滿足您的需求時,您就需要建立自己的客製化 Image。Dockerfile 正是為此而生的工具。

Dockerfile 是一個純文字檔案,裡面包含了一系列的指令和參數,用來定義如何一步步地自動化建構一個 Docker Image。其中以FROM 指令為開頭,每一條指令都會在 Image 中建立一個新的層。

一個基本的 Node.js 專案 Dockerfile 範例:

# 步驟 1: 選擇一個基礎映像檔
FROM node:18-alpine

# 步驟 2: 設定在容器內的工作目錄
WORKDIR /usr/src/app

# 步驟 3: 複製 package.json 和 package-lock.json 檔案
# 這樣可以利用 Docker 的層快取機制,只有在套件依賴變更時才重新安裝
COPY package*.json ./

# 步驟 4: 安裝專案所需的套件
RUN npm install

# 步驟 5: 將專案的所有程式碼複製到工作目錄
COPY . .

# 步驟 6: 向外部暴露應用程式所使用的連接埠
EXPOSE 3000

# 步驟 7: 定義容器啟動時要執行的命令
CMD [ "npm", "start" ]

建立 Image 指令:docker build

當您撰寫好 Dockerfile 後,就可以使用 docker build 指令來建構 Image。

其基本語法為:

docker build -t <new_image_name>:<tag> <path_to_dockerfile_directory>
  • -t:–tag 的縮寫,用來為新建立的 Image 指定名稱和標籤。
  • <path_to_dockerfile_directory>:Dockerfile 所在的目錄路徑。最後的 . 表示「目前目錄」,這是最常見的用法。這個路徑也定義了「建構上下文 (Build Context)」,Docker 會將此資料夾路徑下的所有本地檔案打包傳送給 Docker 引擎進行建構。每個使用者都可以透過此方式建立客製化的Image。

範例:建構上述 Node.js 專案的 Image

假設 Dockerfile 和專案檔案都在目前目錄下:

docker build -t my-nodejs-app:1.0 .

建構成功後,您就可以使用 docker images 指令看到您剛剛建立的 my-nodejs-app:1.0 了。

管理您的 Docker Image

下載和建立了 Image 之後,使用者還需要學會如何管理它們。除了透過指令列,docker desktop也提供了圖形化介面來管理這些內容。

常用管理指令

  • docker images 或 docker image ls
    列出您本地端儲存的所有 Docker Image,會顯示 REPOSITORY (名稱)、TAG (標籤)、IMAGE ID (唯一識別碼)、CREATED (建立時間) 和 SIZE (大小) 等資訊。
  • docker run <image_name>:<tag>
    從指定的 Image 建立並啟動一個新的容器。這是最常用的指令之一,結合了 docker create 和 docker start 的功能。
  • docker create <image_name>:<tag>
    僅從指定的 Image 建立一個新的容器,但不會啟動它。
  • docker ps
    列出目前正在運行的容器。
  • docker ps a
    列出所有容器,包含正在運行和已停止的。
  • docker stop <container_id_or_name>
    停止一個或多個正在運行的容器。
  • docker rmi <image_id_or_name:tag> 或 docker image rm <image_id_or_name:tag>
    刪除一個或多個指定的本地 Image。請注意,如果有一個正在運行的 Container 是基於該 Image 建立的,您必須先使用 docker stop 停止並刪除該 Container,才能成功刪除 Image。
  • docker tag <source_image>:<tag> <target_image>:<tag>
    為一個現有的 Image 建立一個新的標籤。這在您準備將 Image 推送到不同的儲存庫(亦稱為 docker registry)或給予不同版本號時非常有用。例如,在推送到 Docker Hub 之前,您需要將 Image 標記為 <your_dockerhub_username>/<repository_name>:<tag> 的格式。
  • docker push <image_name>:<tag>
    將一個本地的 Image 上傳到遠端倉庫 (如 Docker Hub)。上傳前,您需要先使用 docker login 指令登入,並且 Image 名稱必須符合遠端倉庫的命名規範。
  • docker history <image_name>:<tag>
    顯示指定 Image 的建構歷史,也就是組成該 Image 的每一層以及建立它們的指令。

常見問題 (FAQ)

Q1: Docker Image 和虛擬機 (VM) 映像檔有什麼不同?

A1: 主要區別在於抽象層級和資源消耗。虛擬機映像檔包含一個完整的客作業系統 (Guest OS),因此體積龐大 (通常是 GB 等級),啟動慢,且資源消耗高。而安裝 docker後,Docker Image 共享主機的作業系統核心 (Host OS Kernel),它只打包了應用程式所需的函式庫和執行環境,因此非常輕量 (通常是 MB 等級),啟動速度極快 (秒級甚至毫秒級),資源佔用也少得多。

Q2: 為什麼 docker build 命令最後需要一個 .?

A2: 這個 . 代表「建構上下文 (Build Context)」的路徑,指定為目前目錄。當執行 docker build 時,Docker Client 會將這個上下文路徑中的所有檔案和目錄打包,傳送給 Docker Daemon (服務端)。Dockerfile 中的 COPY 或 ADD 指令所能存取的檔案,都僅限於這個上下文中的檔案。這是一個常見的誤解,很多人以為 . 是用來指定 Dockerfile 的位置,但實際上指定 Dockerfile 位置的參數是 -f。在某些系統中,執行 docker 指令可能需要 sudo 權限才能存取特定目錄。

Q3: latest 標籤總是代表最新的版本嗎?

A3: 不一定。latest 只是一個預設的標籤,它的具體指向完全由 Image 的發布者決定。雖然慣例上它會指向最新的穩定版本,但這並非強制規則。在正式的生產環境中,強烈建議總是指定一個明確的版本號標籤 (例如 nginx:1.21.6-alpine),而不是使用 latest,以確保建構的可預測性和穩定性。

Q4: 如何清理不再使用的 Docker Image 以釋放磁碟空間?

A4: 您可以使用 docker image prune 指令。docker image prune 會刪除所有懸空 (dangling) 的 Image (即沒有任何標籤,也沒有被任何 Container 使用的 Image)。如果您想刪除所有未被任何現有 Container 使用的 Image (不僅是懸空的),可以使用 docker image prune -a。這是一個快速釋放空間的有效方法。

總結

Docker Image 是整個容器化工作流程的基石。它透過將應用程式及其所有相依性打包成一個標準化、不可變的單元,實現了前所未有的可攜性和一致性。

在本文中,我們從 Docker Image 的核心定義出發,釐清了它與 Container 之間的「類別與實例」關係。我們學習了兩種獲取 Image 的主要方法:從 Docker Hub pull 現成的 Image,以及透過撰寫 Dockerfile build 出客製化的 Image。最後,我們介紹了一系列管理本地 Image 的關鍵指令。

精通 Docker Image 的概念與操作,是成為一名高效能開發者或維運工程師的必經之路。希望這份詳細的指南能幫助您打下堅實的基礎,讓您在容器化的世界中游刃有餘,更專注於創造價值,而非解決環境問題。

資料來源

返回頂端