問題敘述
我有下列的服務要透過 make file 來編譯 Dockerfile ,由於那些服務有版本的區別,而且不同的版本都需要同時存在. 也就是說 app:v1 跟 app:v2 必需要同時能存在,並且要能夠讓 make file 能夠支援新的版本 app:v3 的產生. 舉例來說,我之後只要輸入 make build-images
就要能夠自動跑出 app1-v1, app1-v2, app2-v1 並且能夠根據這些 build target 自動 build 相關的 docker image
- build
- app1
- v1
- Dockerfile
- v2
- Dockerfile
- v1
- app2
- v1
- Dockerfile
- v1
- app1
如何得到 build target
首先我們要能搜集所有的 build targets ,透過一系列的 build target 再來跑相關的 make process.
BUILD_DOCKERFILES := $(sort $(wildcard build/*/*/Dockerfile))
這一段是找到所有具有 Dockerfile 的檔案.. 在這裡會找出…
- build/app1/v1/Dockerfile
- build/app1/v2/Dockerfile
- build/app2/v1/Dockerfile
這時候我們要做一點小處理,要去除 Dockerfile
這個不需要的檔案名稱.這個要透過上一篇提過的 %
來處理. 快速講解一下,下面的例子是說,不論何種字串只要是 xxxx/Dockerfile 都會被取代成 xxxx
BUILD_DIRS := $(patsubst %/Dockerfile,%,$(BUILD_DOCKERFILES))
這時候 BUILD_DIRS
就會轉換為:
- build/app1/v1
- build/app1/v2
- build/app2/v1
再來,我們要整理成 build-app-v1
的格式,這個可以透過 subst
來替代
BUILD_APP_VER := $(subst /,-,$(BUILD_DIRS))
這樣就會得到:
- build-app1-v1
- build-app1-v2
- build-app2-v1
最後,我們要其轉換成 app1-v1, app1-v2 … 一樣透過 %
來替換
BUILD_NAMES := $(patsubst build-%,%,$(BUILD_APP_VER))
最後我們要得到 build target 希望是 build-app1-v1, build-app1-v2…
BUILD_TARGETS := $(addprefix notebook-image-,$(BUILD_NAMES))
這樣就可以得到我們要的結果.
最後整理一下…
BUILD_DOCKERFILES := $(sort $(wildcard build/*/*/Dockerfile))
BUILD_DIRS := $(patsubst %/Dockerfile,%,$(BUILD_DOCKERFILES))
BUILD_APP_VER := $(subst /,-,$(BUILD_DIRS))
BUILD_NAMES := $(patsubst build-%,%,$(BUILD_APP_VER))
BUILD_TARGETS := $(addprefix notebook-image-,$(BUILD_TARGETS))
開始撰寫 build target 本體要做的事情
這時候因為我們得到 build-app1-v1
, build-app1-v2
跟 build-app2-v1
. 我們就要來展開要做的事情.我們要透過展開 build-app1-v1
來編譯出 docker.io/evanlin/app:v1
的 docker tag image
一個錯誤的範例
build-%: build/$(subst -,/,%)/Dockerfile $(shell find build -type f)
...
先解釋一下,在 build-%:
後面的就是條件式 prerequisites ,也就是必須要符合條件內才會繼續往下執行.在這裡條件是 透過找所有 build
子目錄所有檔案,來確認是否有符合 build/app1/v1/Dockerfile
的檔案.
起出這樣看起來很正常,我們想要透過替換字串來讓 %
裡面的內容來修改 app1-v1
為 app1/v1
但是這時候會發現無法替換.
Secondary Expansion
經過查詢 GNU Make: Secondary Expansion 可以達成我的需求.所以修改如下:
.SECONDEXPANSION:
build-%: build/$$(subst -,/,%)/Dockerfile $(shell find build -type f)
...
這樣就能夠找到,也能夠繼續之後的處理.