BuildPack 打包

無需 dockerfile,使用 buildpacks 打包鏡像

書接上文,聰明如你已經發現項目中沒有定義 dockerfile,但我們依然能打鏡像,是如何做到的呢?正如上面提到的 gradle 的 spring 插件創建了 bootBuildImage,通過 buildpacks 構建 OCI 鏡像。

概念

buildpacks 讓你可以把源文件轉換爲安全、高效、預生產的容器鏡像。

buildpacks 是什麼?

buildpacks 爲應用程序提供框架和運行時支持。buildpacks 檢查你的應用以決定需要哪些依賴,並恰當的配置這些應用以便能在各種雲環境中運行。

工作方式

每個 buildpack 由兩個階段組成。

檢測(detect)階段

檢測階段會檢查你的源碼是否適合使用 buildpack。如果適合,就直接進入構建(build)階段。如果不適合,則直接跳過 buildpack 中的構建階段。

例如:

  • buildpack 如何在 Python 項目中找到 requirements.txtsetup.py 文件,則通過檢測
  • buildpack 如果在 Node 項目中找到 package-lock.json 文件,則通過檢測

構建(build)階段

構建階段會檢測你的代碼以完成以下操作:

  • 設置構建時和運行時環境
  • 下載依賴然後編譯你的源碼(如果有必要的話)
  • 設置恰當的程序入口和啓動腳本

例如:

  • Python 項目中的 buildpack 如果檢測到 requirements.txt 文件,會執行 pip install -r requirements.txt 命令,以安裝 requirements.txt 文件中的依賴。
  • Node 項目中的 buildpack 如果檢測到 package-lock.json 文件,會執行 npm install 命令。

構建者是什麼樣子?

構建者是多個 buildpack 文件、基礎構建(build)鏡像、運行(run)鏡像的有序組合。這些構建者參與到你的源碼中然後構建,輸出 app 鏡像。構建鏡像爲構建者提供基礎環境(例如,一個帶有構建工具的 Ubuntu Bionic 操作系統鏡像),運行鏡像爲 app 鏡像在運行期間提供基礎環境。構建鏡像和運行鏡像的組合叫做棧。

究其根本,構建者使用生命週期(lifecycle)爲所有 buildpack 運行檢測階段,以便爲所有通過檢測階段的 buildpack 運行構建階段。

這讓我們可以通過一個構建者就可以自動檢測和構建各種各樣的應用程序。

例如,假如 demo-builder 包含 Python 和 Node buildpack。那麼

  • 如果你的項目只包含 requirements.txt 文件,demo-builder 將只運行 Python 構建步驟。
  • 如果你的項目只包含 package-lock.json 文件,demo-builder 將只允許 Node 構建步驟。
  • 如果你的項目同時包含 package-lock.jsonrequirements.txt 文件,demo-builder 將同時運行 Python 和 Node 構建步驟。
  • 如果你的項目既不包含 requirements.txt 文件,也不包含 package-lock.json 文件,那麼 demo-builder 將檢測失敗,並退出構建。

組件

構建者

構建者是什麼?

構建者是一個包含執行構建時需要的各種組件的鏡像。構建者鏡像由構建鏡像、生命週期、多個 buildpack、各方面的構建配置文件(包含 buildpack 檢測順序和運行鏡像的位置)組成。

解剖構建者

構建者由以下組件組成:

  • 多個 buildpack
  • 生命週期
  • 棧構建鏡像

buildpack

buildpack 是什麼?

buildpack 是一個工作單元,該工作單元仔細檢查你的源代碼然後制定構建、運行應用程序的計劃。

通常多個 buildpack 文件是一個至少包含 3 個文件的集合:

  • buildpack.toml——提供 buildpack 的元數據
  • bin/detect——決定是否可以應用 buildpack
  • bin/build——執行 buildpack 邏輯
元 buildpack

還有一種不同類型的 buildpack,通常稱爲元 buildpack。它只有一個 buildpack.toml 文件,該文件包含有序的配置,而配置的內容是對其它 buildpack 的引用。當組合比較複雜的檢測策略時,元 buildpack 特別有用。

解剖 buildpack

有兩個必不可少的階段可以讓多個 buildpack 文件創建可運行的鏡像。

檢測

平臺根據你的源碼順序測試 buildpack 組。第一個認爲自己適合你的源碼的 buildpack 組將成爲你的 app 的 buildpack 文件集合。每個 buildpack 的檢測標準不同——例如,NPM buildpack 查找package.json 文件,Go buildpack 查找 Go 源文件。

構建

在構建過程中,buildpack 文件對最終應用程序鏡像有所貢獻。貢獻內容包括設置鏡像的環境變量、創建包含二進制(例如:node、python、ruby)的層、添加應用程序依賴(例如:運行npm installpip install -r requirements.txtbundle install)。

分佈

buildpack 文件可以在鏡像註冊表或者 Docker 後臺進程的基礎上打包爲 OCI 鏡像。元 buildpack 文件也可以做到。

buildpack 組

buildpack 組是什麼?

buildpack 組是一個特定的 buildpack 文件列表,該列表以適合構建應用程序的順序組合在一起。由於 buildpack 文件是模塊化並且可重用的,因此 builpack 組可以讓你把多個模塊化 buildpack 文件連在一起。
例如,你有一個 buildpack 文件用於安裝 Java,一個 buildpack 文件使用 Maven 構建應用程序。這兩個 builpack 文件可以合併到一個組中實現更高級的功能,特別是第一個文件安裝 Java,第二個文件使用 Java 運行 Maven,這不就是 Java 的構建構建工具嗎。

因爲你在構建者或元 buildpack 中可以有多個 buildpack 組,並且你可以重用 buildpack 文件,所以你可以再用一個 buildpack 組,該組重用提供 Java 的 buildpack,但是使用 Gradle 提供的 buildpack 構建你的應用程序。如此一來,你在創建高級功能的時候就不需要複製了。

解剖 buildpack 組

buildpack 組是一個 buildpack 條目列表,按順序定義了 buildpack 的順序執行。

buildpack 條目通過 id 和版本進行定義。該條目可以被標記爲是可選的。雖然在一個 buildpack 組中可能有一個或多個 buildpack,但在一個構建者或元 buildpack 中可以有多個 buildpack 組。

使用多個 buildpack 組進行檢測

構建者或元 buildpack 可能包含多個 buildpack 組。當生命週期執行檢測過程時,它會按指定的順序執行每一組 buildpack。對於每個 buildpack 組來說,生命週期會執行組中的每個 buildpack 的檢測階段(可以並行執行)然後聚合結果。生命週期會選擇第一個所有必須的 buildpack 檢測通過的組。

例如,如果構建者有 A、B和C buildpack 組。生命週期將通過 A 運行檢測。如果 A 中所有必需的 buildpack 都通過檢測,那麼生命週期就會選擇 A。在那種情況下,B 和 C 不會進行處理。如果 A 在必須的 buildpack 中有任何失敗,生命週期將轉向處理 B。如果 B 在必須的 buildpack 中有任何失敗,那麼生命週期將轉向處理 C。如果 C 有失敗,那麼整體檢測處理將失敗。

如果 buildpack 組只有元 buildpack,那麼元 buildpack 可能反過來包含更多的 buildpack 組,這些組通過Order Resolution規則展開,所以元 buildpack 中的每個 buildpack 組將與其它 buildpack 組中 buildpack 一起工作。

例如:

  • 構建者有 buildpack 組 A,包含 buildpack X,Y 和 Z
  • Y 是元 buildpack 包含 buildpack 組 B 和 C
  • buildpack 組 B 包含 buildpack T 和 U
  • buildpack 組 C 包含 buildpack V 和 W

生命週期將此展開爲以下 buildpack 組:

  • X、T、U、Z
  • X、V、W、Z

未包含 Y 的原因是元 buildpack 只提供組,元 buildpack 不參與構建過程或者構建、檢測程序。

生命週期

生命週期協調 buildpack 執行,然後將生成的文件的打包進最終 app 鏡像中。

檢測

尋找一個有序的 buildpack 組,以便在構建階段使用。

檢測是生命週期的第一個階段。由檢測儀完成。在本階段中,檢測儀尋找有序的 buildpack 組,以便在構建階段使用。在構建環境中調用檢測儀不需要參數,並且不能使用 root 權限運行。輸入文件order.toml,兩個輸出文件分別是group.tomlplan.toml

除非傳入某些標誌,否則檢測儀使用以下默認配置:

  • 定義順序的路徑:/cnb/order.toml
  • 定義輸出組的路徑:<layers>/group.toml
  • 輸出構建計劃的路徑:<layers>/plan.toml

完整的標誌列表和配置看這裏

order.toml

order.toml 是一個包含組列表的文件。每個組是一個 buildpack 列表。檢測儀讀取 order.toml 然後尋找第一個通過檢測的組。如果所有的組都失敗了,那麼檢測就失敗了。

組中的 buildpack 要麼被標記會可選的,要麼被標記爲必須的。爲了通過檢測處理,必須滿足兩個條件:

  • 檢測腳本必須成功(退出代碼是 0)通過所有必須的 buildpack。
  • 檢測儀可以創建構建計劃(寫入 plan.toml),包含所有組中必須的 buildpack。

第一個通過以上兩個步驟的組將寫入 group.toml 中,並將其構建計劃寫入 plan.toml 中。

注意:如果檢測腳本執行可選 buildpack 失敗了,buildpack 組仍然可以通過檢測處理並且可以被選中。該組要被選中,至少要有一個 buildpack (無論是可選的還是必須的)成功的通過檢測。

group.toml

選中的組如果可以創建 plan.toml,則會被寫入 group.toml 中。buildpack 將會與 order.toml 中相同的順序,並且過濾掉所有可選的失敗的 buildpack,然後寫入 group.toml 中。

plan.toml

每個 buildpack 可以定義兩個列表,分別是提供依賴列表和要求依賴列表(或者通過 or 隔離的鍵值對列表)。這些列表(如果不爲空)叫做構建計劃。檢測儀從選中的組中讀取 buildpack(在過濾掉執行探測腳本失敗的 buildpack 之後)。檢測儀檢查所有的可選項然後嘗試創建一個包含條目列表的文件,每個條目都有提供和要求列表以滿足所有 buildpack 的需求。每個可選項都稱爲一次試驗,輸出文件叫做 plan.toml。

提供和要求有兩個限制條件:

  • buildpack 提供的依賴,無論是 buildpack 本身還是 buildpack 所在的組都是必須的。
  • buildpack 要求的依賴,必須通過 buildpack 本身提供或者 buildpack 所在的組的其它 buildpack 提供。

對於必要的 buildpack 來說,上面兩個條件如果有一個失敗,實驗也會失敗並且檢測儀會尋找下一個實驗。對於可選的 buildpack 來說,上面兩個條件如果有一個失敗,那邊在最終計劃中應該排除該 buildpack。如果所有的試驗都失敗了,代表着 buildpack 所在的組失敗了(檢測儀將轉向下一個組)。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
20 所有 buildpack 組都未檢測到 w/o 錯誤
21 所有 buildpack 組都未檢測到 buildpack 發送錯誤
22-29 檢測特定生命週期錯誤

分析

恢復 buildpack 用於優化構建和導出階段的文件。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
20-39 分析特定生命週期錯誤

恢復

從緩存中恢復層。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
40-49 恢復特定生命週期錯誤

構建

將應用程序源代碼轉換爲可運行的構件,這些構件可以打包到容器中。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
51 Buildpack 構建錯誤
50,52-59 構建特定生命週期錯誤

導出

創建最終 OCI 鏡像。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
60-69 導出特定生命週期錯誤

鏡像

導出器通過參數的方式接收標誌,該標誌引用 OCI 鏡像註冊表或 Docker 守護進程,並將其寫入 app 鏡像中。

report.toml

導出器還將寫一個 report.toml 文件,該文件包含導出鏡像的相關信息,比如該鏡像的概要以及清單的大小(如果導出的是 OCI 註冊表)或識別器,以及 buildpack 提供的構建 BOM。輸出的報告的位置可以通過 -report 標誌進行指定;默認地址是<layers>/report.toml——注意它不會在導出的鏡像的文件系統中出現。

創建

在單個命令中運行檢測、分析、恢復、構建以及導出。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
20-29 檢測特定生命週期錯誤
30-39 分析特定生命週期錯誤
40-49 恢復特定生命週期錯誤
50-59 構建特定生命週期錯誤
60-69 導出特定生命週期錯誤

啓動

最終 OCI 鏡像的入口。負責啓動應用程序進程。

退出代碼

退出碼 結果
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
80-89 啓動特定生命週期錯誤

變基(rebase)

把應用程序的層變基到新的運行鏡像。

退出代碼

退出碼 結果
0 成功
11 平臺 API 不兼容錯誤
12 buildpack API 不兼容錯誤
1-10,13-19 普通生命週期錯誤
70-79 變基特定生命週期錯誤

平臺

平臺是什麼?

平臺使用生命週期、buildpack(打包在構建者中)以及應用程序源碼生成一個 OCI 鏡像。

示例

平臺可能包括的示例:

  • 本地命令行工具使用 buildpack 創建 OCI 鏡像。比如 Pack CLI 工具
  • 持續集成服務的插件,該插件使用 buildpack 創建 OCI 鏡像。比如 tekton 提供的 buildpack 插件
  • 雲應用程序平臺在部署之前使用 buildpack 構建源碼。比如 kpack 平臺

API

平臺技術規範詳細的描述了平臺的能力,以及平臺如何和生命週期、構建者交互。當前平臺的 API 版本是 0.4。

棧是什麼?

棧是把兩個打算一起工作的鏡像組合在一起:

  1. 棧中的構建鏡像提供了基礎鏡像,該鏡像構造了構建環境。構建環境是容器化環境,生命週期(從而打包)在該環境中執行。
  2. 棧中的運行鏡像提供了基礎鏡像,應用程序通過該鏡像進行構建。

如果你使用 pack 命令行界面,運行 pack stack suggest會推薦一個棧(同時還有每個棧相關的構建、運行鏡像)列表,這些棧可以用於執行 pack builder create命令。

使用棧

構建者使用棧,並通過構建者的配置文件進行配置:

[[buildpacks]]
  # ...

[[order]]
  # ...

[stack]
  id = "com.example.stack"
  build-image = "example/build"
  run-image = "example/run"
  run-image-mirrors = ["gcr.io/example/run", "registry.example.com/example/run"]

通過必要的 [stack] 小節,構建者作者可以配置棧的 ID、構建鏡像、運行鏡像(包括任何鏡像)。

run-image-mirrors

run-image-mirrors 爲運行鏡像提供了可選位置,以便在構建(或變基)期間使用。當通過構建者容器運行構建時,打包會選擇使用 app 鏡像指定的位置(如果在鏡像名稱中沒有指定註冊表的主機地址,則將使用 DockerHub)。這在發佈生成的 app 鏡像時很有用(通過 --publish 標誌或通過 docker push),app 的基礎鏡像和 app 的鏡像在同一個註冊表中,這將減少推送 app 鏡像所需的數據傳輸量。

在以下示例中,假設構建者配置文件和上面的一樣,被選中運行的鏡像是registry.example.com/example/run

$ pack build registry.example.com/example/app

當命名 app 不指定註冊表時,比如 example/app,將導致example/run作爲 app 的運行鏡像。

$ pack build example/app

操作

構建

構建解釋

構建是指通過你的源碼執行一個或多個 buildpack,然後生成一個可運行的 OCI 鏡像。每個 buildpack 檢查都檢查源碼然後提供相關的依賴。然後根據 app 的源碼和那些依賴生成鏡像。

buildpack 兼容一個或多個棧。一個棧指定了一個構建鏡像和一個運行鏡像。在構建過程中,構建鏡像是 buildpack 執行的環境,運行鏡像是最終 app 鏡像的基礎鏡像。

多個 buildpack 可以通過指定的棧的構建鏡像綁定在一起,即構建者(注意,是構建者)鏡像。構建者爲指定棧的 buildpack 提供最便捷的分佈。

變基

變基解釋

當 app 的棧的運行鏡像變更時,變基可以讓 app 開發者或運營商快速地更新 app 鏡像。通過對分層的鏡像變基,我們避免了重新構建 app。

鏡像變基的核心部分是一個簡單的工程。通過檢查 app 鏡像,變基可以判定 app 的基礎鏡像是否有新版本(無論是本地還是註冊表中)。如果有,變基通過更新 app 鏡像的元數據層以引用新的基礎鏡像版本。

示例:app 鏡像變基

考慮有一個 app 鏡像 my-app:my-tag 使用默認的構造者進行初次構建。該構建者的棧使用的運行鏡像爲 pack/run。運行以下命令更新 my-app:my-tag 的基礎鏡像爲 pack/run 的最新版本。

$ pack rebase my-app:my-tag

提示:pack rebase 有一個 --publish 標誌可以用於發佈更新後的 app 鏡像到註冊表中。當使用註冊表時,與 Docker 守護進程相比, --publish 是最優的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章