Learning Objectives
- 用 docker image build -t 依 Dockerfile 建立映像檔,說明最後那個「.」(建置目錄)的意思
- 看懂建置快取(build cache),把很少變動的步驟放前面、常改的放後面
- 用 -d -p 執行映像檔,再用 container port、top 與瀏覽器測試
- 用 docker login 與 docker image push 把映像檔上傳到 Docker Hub,並用 image rm 清理本機
從 Dockerfile 到別人也能用的映像檔
上一課寫好了 Dockerfile,這一課把它變成真正的映像檔,測試它,再放上 Docker Hub 讓別人(或另一台電腦上的你)一個 pull 就能用。
Dockerfile ──docker image build──▶ 本機映像檔 ──docker container run──▶ 測試
│
└──docker image push──▶ Docker Hub ──docker image pull──▶ 其他電腦
專案目錄沿用上一課的 static_web:
static_web/
├── Dockerfile
└── index.html
docker image build
在專案目錄裡執行:
cd ~/static_web
docker image build -t ncuedu/static_web .
| 部分 | 意思 |
|---|---|
-t ncuedu/static_web |
為映像檔取名(tag);沒寫標籤就是 ncuedu/static_web:latest,也可以寫 -t ncuedu/static_web:1.0 |
ncuedu/ |
Docker Hub 的帳號名稱;之後要 push,這裡必須是你自己的帳號 |
最後的 . |
建置目錄(build context):目前所在的目錄。Docker 會把這個目錄的內容交給建置程序,COPY 只能從這裡拿檔案,預設也在這裡找 Dockerfile |
最常見的錯誤是漏打最後的點,會出現 requires 1 argument 之類的訊息。Dockerfile 放在別處或叫別的名字時,用 -f 指定:
docker image build -t ncuedu/static_web -f docker/Dockerfile .。
建置目錄裡的所有檔案都會送進建置程序,目錄太大會拖慢建置。可以在專案目錄放一個
.dockerignore,列出不需要的檔案或目錄(例如.git、node_modules),寫法類似.gitignore。
建好之後檢查:
| 指令 | 作用 | 舊寫法 |
|---|---|---|
docker image ls ncuedu/static_web |
列出這個映像檔 | docker images ncuedu/static_web |
docker image history ncuedu/static_web |
看每一層是由 Dockerfile 的哪一行產生、多大 | docker history ncuedu/static_web |
建置快取:為什麼第二次 build 快很多
Dockerfile 的每一行會產生一層(LABEL、EXPOSE、CMD 這類設定只記在映像檔資訊裡,不產生檔案)。 Docker 建置時會逐行檢查:這一行和它之前的每一行都沒變(COPY 還會比對檔案內容),就直接沿用上次的結果,這就是建置快取。
規則只有一條:某一行一旦需要重做,它後面的每一行都要重做。
修改 index.html 之後再建置一次:
FROM ubuntu:24.04 沿用快取
LABEL maintainer="[email protected]" 沿用快取
RUN apt-get update && apt-get install -y nginx 沿用快取(最花時間的一步省下來了)
COPY index.html /var/www/html/ 檔案變了,重做
EXPOSE 80 在重做的那行後面,重做
CMD ["nginx", "-g", "daemon off;"] 在重做的那行後面,重做
所以安排順序的原則是:很少變動的步驟放前面,常常修改的放後面。安裝套件放前面,複製自己的程式放最後。
如果把 COPY index.html 放在 RUN apt-get install 前面,每改一次網頁就要重新下載安裝 nginx。
Python 專案常見的寫法也是同一個道理:先只複製套件清單並安裝,最後才複製全部的程式碼。
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
只改程式、沒改 requirements.txt 時,pip install 那一層會沿用快取。
輸出的寫法:目前的 Docker(BuildKit)在沿用快取的步驟前面標
CACHED;舊版的建置程式則顯示---> Using cache。 想強制全部重做時加--no-cache。
執行與測試
docker container run -d -p 7777:80 --name static_web ncuedu/static_web
| 部分 | 意思 |
|---|---|
-d |
在背景執行,網頁伺服器可以一直開著 |
-p 7777:80 |
主機的 7777 埠對應到容器的 80 埠(nginx 在容器裡聽 80) |
--name static_web |
容器名稱 |
檢查容器:
| 指令 | 作用 |
|---|---|
docker container ls |
PORTS 欄會顯示 0.0.0.0:7777->80/tcp |
docker container port static_web |
只看這個容器的埠號對應 |
docker container top static_web |
看容器裡執行中的程序,應該有 nginx 的 master 與 worker |
測試方式有兩種:
- 經由主機的埠號:瀏覽器開
http://localhost:7777(在遠端主機上就用主機的 IP,例如http://192.168.88.100:7777),或在終端機用curl http://localhost:7777。 - 從另一個容器連過去:投影片的做法是查出 static_web 的 IP(例如
172.17.0.2),再從alpine容器執行wget -qO - http://172.17.0.2。容器之間怎麼互相找到,第 09 課會用更好的方法(自訂網路、用名字連線)。
在真的終端機走一次:build、run、push
下面是在 Ubuntu 主機上的完整流程。映像檔 ID、大小與時間都是示意,你的畫面數字會不同。ncuedu 是投影片用的帳號名稱,請換成你自己的 Docker Hub 帳號。
-
1
準備專案目錄
建立
static_web目錄,寫好index.html與上一課的Dockerfile。用cat確認內容。 -
2
建置映像檔
注意最後的
.。每一行 Dockerfile 對應一個步驟,第一次建置時 RUN 那一步要下載安裝 nginx,最花時間。 -
3
檢查映像檔與它的層
history由上往下是由新到舊:最上面幾行是你的 Dockerfile 產生的,下面是 ubuntu 基底映像檔原本的層。LABEL、EXPOSE、CMD 的大小是 0B,因為它們只是設定。 -
4
執行容器並檢查
-d讓容器在背景執行,Docker 只印出完整的容器 ID。用ls、port、top確認它正常運作。 -
5
測試網頁
用
curl連主機的 7777 埠,應該看到自己的網頁。在有桌面的電腦上也可以直接用瀏覽器開http://localhost:7777。 -
6
修改網頁並重新建置:看快取
只改了
index.html,所以 FROM 與 RUN 都沿用快取(標CACHED),只有 COPY 重做,幾秒就完成。舊容器跑的還是舊映像檔,要先移除再用新映像檔建立。 -
7
登入 Docker Hub 並上傳
先到 Docker Hub 網站註冊帳號,映像檔名稱的前半段必須和帳號相同。密碼建議改用 Docker Hub 帳號設定裡產生的存取權杖(access token)。上傳時,ubuntu 基底的層 Docker Hub 上本來就有,只會上傳你自己做出來的層。
-
8
清理本機,再從 Docker Hub 拿回來
映像檔還有容器在用時不能刪,所以先移除容器。刪掉本機映像檔後,再用
pull從 Docker Hub 下載回來,證明它真的上傳成功;其他電腦也是用同樣的方式取得。
上傳時要注意的事
| 情況 | 說明 |
|---|---|
denied: requested access to the resource is denied |
映像檔名稱的帳號不是你登入的帳號,或還沒 docker login。可以用 docker image tag ncuedu/static_web 你的帳號/static_web 另取一個名字再 push |
| 想保留多個版本 | 建置時加版本標籤 -t 你的帳號/static_web:1.0,push 時也指定標籤;只用 latest 時,新版會蓋掉舊版 |
| 公開或私人 | Docker Hub 上的儲存庫可以設成公開或私人,公開的任何人都能 pull |
| 不要放進映像檔的東西 | 密碼、API 金鑰、個人資料。映像檔的每一層都拿得出來,即使後面的步驟把檔案刪掉,前面那層還在 |
清理本機用 docker image rm(舊寫法 docker rmi)。映像檔還有容器(包括已停止的)在用時會拒絕刪除,先 docker container rm 再刪。
想一想:建置與快取
docker image build -t ncuedu/static_web . 最後的 . 代表什麼?
Dockerfile 依序是 FROM、RUN(安裝 nginx)、COPY index.html、CMD。只修改了 index.html 再建置,哪些步驟會沿用快取?
為了讓建置比較快,下面哪一種順序比較好?
想一想:測試與發布
用 docker container run -d -p 7777:80 ncuedu/static_web 啟動後,在主機上要用哪個網址看網頁?
docker image push ncuedu/static_web 出現 requested access to the resource is denied,最可能的原因是?
重點精華回顧
docker image build -t 帳號/名稱 .依 Dockerfile 建立映像檔;最後的.是建置目錄,COPY 只能從這裡拿檔案。- 用
docker image ls與docker image history檢查映像檔與每一層的來源。 - 建置快取:某一行沒變就沿用,一旦有一行重做,後面全部重做;很少變的步驟放前面,常改的放後面。
- 用
docker container run -d -p 7777:80執行,再以container port、container top、瀏覽器或curl測試;改了映像檔要用新映像檔重新建立容器。 docker login後用docker image push上傳;映像檔名稱的帳號必須是你自己的。不要把密碼或金鑰放進映像檔。docker image rm刪除本機映像檔前,要先移除使用它的容器。