iT邦幫忙

2025 iThome 鐵人賽

DAY 5
0
Cloud Native

從 Docker 到 K8s:我的 30 天雲原生筆記系列 第 5

Day 5: Dockerfile:為你的應用程式量身打造 Image

  • 分享至 

  • xImage
  •  

哈囉,大家好!歡迎來到我們實戰旅程的第五天!

經過 Day 4 的洗禮,我們已經徹底搞懂了 Docker 核心三劍客:Image、Container 與 Registry 之間的關係,甚至還窺探了 Image 分層結構的奧秘。

我們知道,Image 是 Container 的「設計藍圖」,而 Docker Hub 上有成千上萬別人畫好的藍圖。但身為一個追求卓越的工程師,我們怎麼能滿足於只用別人的東西呢?

今天,我們要學習如何使用 Dockerfile 這份「建築說明書」,從無到有,為我們自己的應用程式,量身打造一個專屬的 Image!

https://ithelp.ithome.com.tw/upload/images/20250912/20178656avHBRNilw5.png

Part 1:Dockerfile 是什麼?

在我們動手之前,先來正式認識一下今天的主角。

Dockerfile 是一個純文字檔,裡面會記錄製作一個 Image 的所有步驟。你可以把它想像成一份**「Image 建造說明書」**。

我們在這份檔案中,會用一行行的指令,告訴 Docker:

  • 我們的應用程式需要什麼樣的基礎環境?(例如:需要有 Node.js 的環境)
  • 需要安裝哪些相依套件?
  • 要把哪些程式碼複製進去?
  • 最後,該如何啟動這個應用程式?

當我們寫好這份說明書後,只要交給 docker build 這個指令,Docker 就會按照你的指示,一步步地建構出一個標準化的 Image。

Part 2:實作時間: 準備一個範例專案 (Node.js App)

https://ithelp.ithome.com.tw/upload/images/20250912/20178656urKID73MMU.png

有了對 Dockerfile 的基本認識後,我們需要一個實際的應用程式來當作範例。這裡我們準備一個超級簡單的 Node.js Express 網站。

  1. 建立專案資料夾:首先,在你的電腦上建立一個新的資料夾,取名為 my-node-app

  2. 建立 package.json:在 my-node-app 資料夾中,建立 package.json。這份檔案用來描述專案需要哪些套件、版本以及啟動指令。

    {
      "name": "my-node-app",
      "dependencies": { "express": "^4.18.2" },
      "scripts": { "start": "node server.js" }
    }
    
    
  3. 建立 server.js:在同一個資料夾中,建立 server.js,這是我們網站的主程式。

    const express = require("express");
    const app = express();
    const PORT = 3000;
    app.get("/", (req, res) => res.send("Hello from my own Docker container!"));
    app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
    
    
  4. 產生 package-lock.json (重要!)package-lock.json 會鎖定所有套件的精確版本,是確保環境一致性的關鍵。請在終端機中,進入 my-node-app 資料夾,執行一次:

    npm install
    

    執行後,你會發現資料夾中多了一個 package-lock.json 檔案。

Part 3:撰寫你的第一份 Dockerfile (建築說明書)

萬事俱備!現在,在 my-node-app 資料夾中,建立一個沒有副檔名、檔名就叫做 Dockerfile 的檔案,然後貼上以下內容。

# 使用 Node.js 官方提供的輕量版映像檔
FROM node:18-alpine

# 建立工作目錄
WORKDIR /app

# 複製 package.json 和 package-lock.json
COPY package*.json ./

# 安裝套件(依照 lock 檔案版本精準安裝)
RUN npm ci

# 複製應用程式原始碼
COPY . .

# 聲明容器內的應用程式會使用 3000 埠來對外溝通
EXPOSE 3000

# 啟動應用程式
CMD ["node", "server.js"]

Part 4:建立 Image

在專案目錄下執行:

docker build -t <你的 Docker Hub ID>/my-node-app:1.0 .
  • t 表示加上 tag,格式通常是:

    <Docker Hub ID>/<應用名稱>:<版本號>
    
  • 最後的 . 表示 Dockerfile 在當前目錄。

Docker Hub ID 可以在 Docker Hub 的 Account Settings → Username 找到。這個 ID 必須是英文或數字組成。

Part 5: 執行 Container

Image 建好後,可以用以下指令在本機啟動:

docker run -d -p 8080:3000 <你的 Docker Hub ID>/my-node-app:1.0
  • d 表示在背景執行
  • p 8080:3000 表示將本機的 8080 port 對應到 container 的 3000 port

執行後,打開瀏覽器,訪問 http://localhost:8080,你就會看到 "Hello from my own Docker container!"

https://ithelp.ithome.com.tw/upload/images/20250912/20178656LgnEfEQpyC.png

Part 6: 推送到 Docker Hub

如果想要讓其他人也能使用這個 Image,可以推送到 Docker Hub。

docker login
docker push <你的 Docker Hub ID>/my-node-app:1.0

推送成功後,在 Docker Hub 的 repository 就能看到剛剛上傳的 Image。

其他人只要執行:

docker pull <你的 Docker Hub ID>/my-node-app:1.0
docker run -d -p 8080:3000 <你的 Docker Hub ID>/my-node-app:1.0

就能跑起來。


Part 7:Dockerfile 的靈魂:快取優化 (Build Cache)

還記得我們在 Day 4 提到的「分層結構」嗎?Dockerfile 的寫法,會深刻影響建置效率。

我們的寫法是:

COPY package*.json ./
RUN npm ci
COPY . .

為什麼不一開始就 COPY . . (把當前專案目錄下的所有檔案全部複製到容器的工作目錄 /app 裡面) 呢?這正是為了善用 建置快取 (Build Cache)

Docker 會快取每一層的建置結果。當你重新 build 時,如果某一層的指令和檔案內容完全沒變,Docker 就會直接使用快取,跳過執行。

  • package.json:通常不常變動
  • RUN npm ci:只要 package.json 沒變,這步就能直接使用快取,它是最耗時的步驟
  • server.js:通常是最常修改的程式碼

這種寫法確保:當你只修改 server.js 時,前面安裝套件的耗時步驟可以跳過,只重新執行最後的 COPY . .

對開發流程來說,這是極其重要的效率優化技巧。

https://ithelp.ithome.com.tw/upload/images/20250912/20178656vs2LmmH9sj.png

Part 8:實戰情境:公司專案怎麼用 Dockerfile?

在實際專案裡,我們通常會有 前端後端 各自的 Dockerfile。

  • 前端:把 React/Vue 專案 build 成靜態檔案,再用 Nginx Image 來 serve。

  • 後端:把 Node.js/Java/Spring Boot 應用程式包進 Image,確保後端 API 環境一致。

這樣做的目的:

  1. 環境一致:不管工程師本機是 Mac、Windows、Linux,跑的都是相同的容器環境。
  2. 方便部署:只要丟 Image 到雲端伺服器或 Kubernetes,就能快速啟動服務。
  3. 團隊協作:避免「我電腦可以跑,你的卻不行」的情況。

https://ithelp.ithome.com.tw/upload/images/20250912/20178656tbmHPsKAS8.png


結論

今天,我們成功地將一個本地專案,轉化為一個標準化的、可攜的、可分享的 Docker Image。

在真實的專案中,前端 (React/Vue) 和後端 (Node.js/Java) 都會各自有自己的 Dockerfile。前端可能會將專案打包成靜態檔案,再用 Nginx Image 來提供服務;後端則會像我們今天一樣,將 API 伺服器打包。最終,不論開發者的本機環境為何,所有人都能運行完全一致的容器環境,徹底告別「我電腦可以跑」的窘境。

既然我們已經能為自己的應用程式蓋好標準化的房子 (Image),那麼下一步,就是學習如何更好地管理住在裡面的房客 (Container),例如:如何讓房客的行李(資料)不會因為搬家而遺失?

在明天的文章中,我們將深入探討運行容器的技巧,特別是 Docker 中最重要也最實用的概念之一 —— Volume (卷)!明天見!


上一篇
Day 4: Docker 核心三劍客:Image, Container 與 Registry
下一篇
Day 6: 管理容器資料:深入 Volumes 與 Bind Mounts
系列文
從 Docker 到 K8s:我的 30 天雲原生筆記6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言