BuildPack:无需编写 Dockerfile,新一代的企业镜像打包工具
公眾號關注?「奇妙的 Linux 世界」
設為「星標」,每天帶你玩轉 Linux !
過去的工作中,我們使用微服務、容器化以及服務編排構建了技術平臺。為了提升開發團隊的研發效率,我們同時還提供了 CICD 平臺,用來將代碼快速的部署到 Openshift(企業級的 Kubernetes) 集群。
部署的第一步就是應用程序的容器化,持續集成的交付物從以往的 jar 包、webpack 等變成了容器鏡像。容器化將軟件代碼和所需的所有組件(庫、框架、運行環境)打包到一起,進而可以在任何環境任何基礎架構上一致地運行,并與其他應用“隔離”。
我們的代碼需要從源碼到編譯到最終可運行的鏡像,甚至部署,這一切在 CICD 的流水線中完成。最初,我們在每個代碼倉庫中都加入了三個文件,也通過項目生成器(類似 Spring Initializer)在新項目中注入:
?Jenkinsfile.groovy:用來定義 Jenkins 的 Pipeline,針對不同的語言還會有多種版本?Manifest YAML:用于定義 Kubernetes 資源,也就是工作負載及其運行的相關描述?Dockerfile:用于構建對象
這個三個文件也需要在工作中不斷的演進,起初項目較少(十幾個)的時候我們基礎團隊還可以去各個代碼倉庫去維護升級。隨著項目爆發式的增長,維護的成本越來越高。我們對 CICD 平臺進行了迭代,將“Jenkinsfile.groovy”和 “manifest YAML”從項目中移出,變更較少的 Dockerfile 就保留了下來。
隨著平臺的演進,我們需要考慮將這唯一的“釘子戶” Dockerfile 與代碼解耦,必要的時候也需要對 Dockerfile 進行升級。因此調研了一下 buildpacks,就有了今天的這篇文章。
什么是 Dockerfile
Docker 通過讀取 Dockerfile 中的說明自動構建鏡像。Dockerfile 是一個文本文件,包含了由 Docker 可以執行用于構建鏡像的指令。我們拿之前用于測試 Tekton 的 Java 項目[1]的 Dockerfile 為例:
FROM openjdk:8-jdk-alpine RUN mkdir /app WORKDIR /app COPY target/*.jar /app/app.jar ENTRYPOINT ["sh", "-c", "java -Xmx128m -Xms64m -jar app.jar"]鏡像分層
你可能會聽過 Docker 鏡像包含了多個層。每個層與 Dockerfile 中的每個命令對應,比如?RUN、COPY、ADD。某些特定的指令會創建一個新的層,在鏡像構建過程中,假如某些層沒有發生變化,就會從緩存中獲取。
在下面的 Buildpack 中也同樣通過鏡像分層和 cache 來加速鏡像的構建。
什么是 Buildpack
BuildPack[2]?是一個程序,它能將源代碼轉換成容器鏡像的并可以在任意云環境中運行。通常 buildpack 封裝了單一語言的生態工具鏈。適用于 Java、Ruby、Go、NodeJs、Python 等。
buildpacks.ioBuilder 是什么?
一些 buildpacks 按順序組合之后就是?builder,除了 buildpacks, builder 中還加入了?生命周期[3]?和 stack 容器鏡像。
stack 容器鏡像由兩個鏡像組成:用于運行 buildpack 的鏡像 build image,以及構建應用鏡像的基礎鏡像 run image。如上圖,就是 builder 中的運行環境。
Buildpack 的工作方式
how buildpack works每個 buildpack 運行時都包含了兩個階段:
phases1. 檢測階段
通過檢查源代碼中的某些特定文件/數據,來判斷當前 buildpack 是否適用。如果適用,就會進入構建階段;否則就會退出。比如:
?Java maven 的 buildpack 會檢查源碼中是否有?pom.xml?Python 的 buildpack 會檢查源碼中是否有?requirements.txt?或者?setup.py?文件?Node buildpack 會查找?package-lock.json?文件。
2. 構建階段
在構建階段會進行如下操作:
1.設置構建環境和運行時環境2.下載依賴并編譯源碼(假如需要的話)3.設置正確的 entrypoint 和啟動腳本。
比如:
?Java maven buildpack 在檢查到有?pom.xml?文件之后,會執行?mvn clean install -DskipTests?Python buildpack 檢查到有?requrements.txt?之后,會執行?pip install -r requrements.txt?Node build pack 檢查到有?package-lock.json?后執行?npm install
BuildPack 上手
那到底如何在沒有 Dockerfile 的情況下使用 builderpack 構建鏡像的。看了上面這些,大家基本上也都能了解到這個核心就在 buildpack 的編寫和使用的。
其實現在有很多開源的 buildpack 可以用,沒有特定定制的情況下無需自己手動編寫。比如下面的幾個大廠開源并維護的 Buildpacks:
?Heroku Buildpacks[4]?Google Buildpacks[5]?Paketo[6]
但是正式詳細介紹開源的 buildpacks 之前,我們還是通過自己創建 buildpack 的方式來深入了解 Buildpacks 的工作方式。測試項目呢,我們還是用測試 Tekton 的 Java 項目[7]。
下面所有的內容都提交到了?Github[8]?上,可以訪問:https://github.com/addozhang/buildpacks-sample 獲取相關代碼。
最終的目錄buildpacks-sample結構如下:
├── builders │?? └── builder.toml ├── buildpacks │?? └── buildpack-maven │?? ├── bin │?? │?? ├── build │?? │?? └── detect │?? └── buildpack.toml └── stacks├── build│?? └── Dockerfile├── build.sh└── run└── Dockerfile創建 buildpack
pack buildpack new examples/maven \--api 0.5 \--path buildpack-maven \--version 0.0.1 \--stacks io.buildpacks.samples.stacks.bionic看下生成的?buildpack-maven?目錄:
buildpack-maven ├── bin │?? ├── build │?? └── detect └── buildpack.toml各個文件中都是默認的初試數據,并沒有什么用處。需要添加些內容:
bin/detect:
#!/usr/bin/env bash if [[ ! -f pom.xml ]]; thenexit 100 fi plan_path=$2 cat >> "${plan_path}" <<EOL [[provides]] name = "jdk" [[requires]] name = "jdk" EOLbin/build:
#!/usr/bin/env bash set -euo pipefail layers_dir="$1" env_dir="$2/env" plan_path="$3" m2_layer_dir="${layers_dir}/maven_m2" if [[ ! -d ${m2_layer_dir} ]]; thenmkdir -p ${m2_layer_dir}echo "cache = true" > ${m2_layer_dir}.toml fi ln -s ${m2_layer_dir} $HOME/.m2 echo "---> Running Maven" mvn clean install -B -DskipTests target_dir="target" for jar_file in $(find "$target_dir" -maxdepth 1 -name "*.jar" -type f); docat >> "${layers_dir}/launch.toml" <<EOL [[processes]] type = "web" command = "java -jar ${jar_file}" EOLbreak; donebuildpack.toml:
api = "0.5" [buildpack]id = "examples/maven"version = "0.0.1" [[stacks]]id = "com.atbug.buildpacks.example.stacks.maven"創建 stack
構建 Maven 項目,首選需要 Java 和 Maven 的環境,我們使用?maven:3.5.4-jdk-8-slim?作為 build image 的 base 鏡像。應用的運行時需要 Java 環境即可,因此使用?openjdk:8-jdk-slim作為 run image 的 base 鏡像。
在?stacks?目錄中分別創建?build?和?run?兩個目錄:
build/Dockerfile
FROM maven:3.5.4-jdk-8-slim ARG cnb_uid=1000 ARG cnb_gid=1000 ARG stack_id ENV CNB_STACK_ID=${stack_id} LABEL io.buildpacks.stack.id=${stack_id} ENV CNB_USER_ID=${cnb_uid} ENV CNB_GROUP_ID=${cnb_gid} # Install packages that we want to make available at both build and run time RUN apt-get update && \apt-get install -y xz-utils ca-certificates && \rm -rf /var/lib/apt/lists/* # Create user and group RUN groupadd cnb --gid ${cnb_gid} && \useradd --uid ${cnb_uid} --gid ${cnb_gid} -m -s /bin/bash cnb USER ${CNB_USER_ID}:${CNB_GROUP_ID}run/Dockerfile
FROM openjdk:8-jdk-slim ARG stack_id ARG cnb_uid=1000 ARG cnb_gid=1000 LABEL io.buildpacks.stack.id="${stack_id}" USER ${cnb_uid}:${cnb_gid}然后使用如下命令構建出兩個鏡像:
export STACK_ID=com.atbug.buildpacks.example.stacks.maven docker build --build-arg stack_id=${STACK_ID} -t addozhang/samples-buildpacks-stack-build:latest ./build docker build --build-arg stack_id=${STACK_ID} -t addozhang/samples-buildpacks-stack-run:latest ./run創建 Builder
有了 buildpack 和 stack 之后就是創建 Builder 了,首先創建?builder.toml?文件,并添加如下內容:
[[buildpacks]] id = "examples/maven" version = "0.0.1" uri = "../buildpacks/buildpack-maven" [[order]] [[order.group]] id = "examples/maven" version = "0.0.1" [stack] id = "com.atbug.buildpacks.example.stacks.maven" run-image = "addozhang/samples-buildpacks-stack-run:latest" build-image = "addozhang/samples-buildpacks-stack-build:latest"然后執行命令,注意這里我們使用了?--pull-policy if-not-present?參數,就不需要將 stack 的兩個鏡像推送到鏡像倉庫了:
pack builder create example-builder:latest --config ./builder.toml --pull-policy if-not-present測試
有了 builder 之后,我們就可以使用創建好的 builder 來構建鏡像了。
這里同樣加上了?--pull-policy if-not-present?參數來使用本地的 builder 鏡像:
# 目錄 buildpacks-sample 與 tekton-test 同級,并在 buildpacks-sample 中執行如下命令 pack build addozhang/tekton-test --builder example-builder:latest --pull-policy if-not-present --path ../tekton-test如果看到類似如下內容,就說明鏡像構建成功了(第一次構建鏡像由于需要下載 maven 依賴耗時可能會比較久,后續就會很快,可以執行兩次驗證下):
... ===> EXPORTING [exporter] Adding 1/1 app layer(s) [exporter] Reusing layer 'launcher' [exporter] Reusing layer 'config' [exporter] Reusing layer 'process-types' [exporter] Adding label 'io.buildpacks.lifecycle.metadata' [exporter] Adding label 'io.buildpacks.build.metadata' [exporter] Adding label 'io.buildpacks.project.metadata' [exporter] Setting default process type 'web' [exporter] Saving addozhang/tekton-test... [exporter] *** Images (0d5ac1158bc0): [exporter] addozhang/tekton-test [exporter] Adding cache layer 'examples/maven:maven_m2' Successfully built image addozhang/tekton-test啟動容器,會看到 spring boot 應用正常啟動:
docker run --rm addozhang/tekton-test:latest. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.2.3.RELEASE)...總結
其實現在有很多開源的 buildpack 可以用,沒有特定定制的情況下無需自己手動編寫。比如下面的幾個大廠開源并維護的 Buildpacks:
?Heroku Buildpacks[9]?Google Buildpacks[10]?Paketo[11]
上面幾個 buildpacks 庫內容比較全面,實現上會有些許不同。比如 Heroku 的執行階段使用 Shell 腳本,而 Paketo 使用 Golang。后者的擴展性較強,由 Cloud Foundry 基金會支持,并擁有由 VMware 贊助的全職核心開發團隊。這些小型模塊化的 buildpack,可以通過組合擴展使用不同的場景。
當然還是那句話,自己上手寫一個會更容易理解 Buildpack 的工作方式。
引用鏈接
[1]?測試 Tekton 的 Java 項目:?https://github.com/addozhang/tekton-test
[2]?BuildPack:?https://buildpacks.io/
[3]?生命周期:?https://buildpacks.io/docs/concepts/components/lifecycle/
[4]?Heroku Buildpacks:?https://github.com/heroku/
[5]?Google Buildpacks:?https://github.com/GoogleCloudPlatform/buildpacks
[6]?Paketo:?https://github.com/paketo-buildpacks
[7]?測試 Tekton 的 Java 項目:?https://github.com/addozhang/tekton-test
[8]?Github:?https://github.com/addozhang/buildpacks-sample
[9]?Heroku Buildpacks:?https://github.com/heroku/
[10]?Google Buildpacks:?https://github.com/GoogleCloudPlatform/buildpacks
[11]?Paketo:?https://github.com/paketo-buildpacks
本文轉載自:「云原生指北」,原文:https://tinyurl.com/jwr782uz,版權歸原作者所有。歡迎投稿,投稿郵箱: editor@hi-linux.com。
你可能還喜歡
點擊下方圖片即可閱讀
一個 TCP 連接可以發多少個 HTTP 請求?99% 的人可能都不知道!
點擊上方圖片,『美團|餓了么』外賣紅包天天免費領
更多有趣的互聯網新鮮事,關注「奇妙的互聯網」視頻號全了解!
總結
以上是生活随笔為你收集整理的BuildPack:无需编写 Dockerfile,新一代的企业镜像打包工具的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: uniapp开发小程序之上传图片(拍照或
- 下一篇: 【Python从零到壹】Python文件
