慎气 发表于 昨天 16:34

从代码到容器:Cloud Native Buildpacks技术解析

Cloud Native Buildpacks是什么?

Cloud Native Buildpacks(简称CNB)是一种标准化、云原生的容器镜像构建系统,其核心目标是:

[*]消除Dockerfile的手动编写
[*]提供可重复、安全且高效的容器镜像构建流程
[*]实现应用与基础镜像的解耦
[*]自动化依赖管理和安全更新

Cloud Native Buildpacks由Heroku构思,在2018年成为CNCF项目,形成现代云原生标准,被Heroku、Cloud Foundry 和其他 PaaS(例如 Google App Engine、Gitlab、Knative、Deis、Dokku 和 Drie)采用。

CNB的架构和原理

核心概念

组件
功能说明
Lifecycle
Lifecycle管理整个构建过程,由多阶段构成
Builder
包含Buildpacks、生命周期组件的构建环境(容器)
Buildpack
模块化构建逻辑单元(可组合),负责处理应用程序源代码,安装依赖项,配置环境并生成最终的可运行单位
 
CNB Lifecycle核心构建步骤


[*]Detect(检测)



[*]扫描项目代码结构(如pom.xml/build.gradle)
[*]匹配适用的Buildpacks(如Java/Maven Buildpack)
[*]输出Buildpack执行顺序


[*]Analyze(分析)



[*]对比新旧构建元数据(layer.toml)
[*]识别可复用的缓存层(如Maven依赖)
[*]决定需要重建的层


[*]Restore(恢复)



[*]从缓存中还原依赖层(如JDK安装、.m2仓库)
[*]加速后续构建过程


[*]Build(构建)



[*]执行Buildpacks的编译逻辑(如mvn package)
[*]生成新的应用层(如编译后的JAR包)
[*]更新层元数据(store.toml)


[*]Export(导出)



[*]将各层打包为OCI镜像
[*]注入启动配置(如JAVA_TOOL_OPTIONS)
[*]输出最终镜像到镜像仓库

OCI镜像构建机制

CNB通过独特的分层策略构建符合OCI标准的镜像。

每个Buildpack都会检查源代码并提供相关的依赖项。然后,会根据应用程序的源代码和这些依赖项生成一个镜像。
在构建过程中,构建时基础镜像成为执行Buildpack的环境,而运行时基础镜像成为最终应用程序镜像的基础。
Buildpacks可以与特定的构建时基础镜像捆绑在一起,从而生成Builder镜像。Builder提供了一种便捷的Buildpacks分发方式。


实战:使用CNB构建spring-petclinic应用

下面我们使用Spring官方的宠物诊所(spring-petclinic)应用为例演示CNB构建部署应用的过程。
本地构建和运行

CNB相关的镜像托管在DockerHub上。因此要求您从本地可以访问DockerHub。
以下以在阿里云的香港/新加坡等非大陆地域的ECS实例作为试验环境。

[*]环境准备
# 在阿里云香港地域购买一台ECS实例。临时使用可以购买抢占式实例,用完即释放
# 使用Alibaba Cloud Linux3公共镜像创建ECS实例,开公网
# 安装docker和常用工具,启动docker
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache
yum install -y curl git docker-ce
systemctl enable docker && systemctl start docker
# 安装pack CLI。从yum中安装的pack版本太旧无法使用。需要从github下载最新版本
PACK_VERSION="0.36.4"
DOWNLOAD_URL="https://github.com/buildpacks/pack/releases/download/v${PACK_VERSION}/pack-v${PACK_VERSION}-linux.tgz"
curl -fsSL "$DOWNLOAD_URL" -o pack-v${PACK_VERSION}-linux.tgz
tar xzf pack-v0.36.4-linux.tgz
mv pack /usr/local/bin/
[*]构建应用
# 克隆代码到本地
git clone https://github.com/spring-projects/spring-petclinic.git
cd spring-petclinic
# 使用pack将应用代码构建成容器镜像;这里使用了heroku提供的CNB builder
pack build petclinic --path . --builder heroku/builder:24
[*]构建过程解析
# pack build petclinic --path . --builder heroku/builder:24
24: Pulling from heroku/builder
Digest: sha256:1f8c4f74030b31af122cbcccd9da975bd136a1076af2e3567bbf1a27c50ac0a3
Status: Image is up to date for heroku/builder:24
24: Pulling from heroku/heroku
Digest: sha256:c697e8808410892d54125b850854b908105dbae1c8e335255b9523c7f4e75516
Status: Image is up to date for heroku/heroku:24
===> ANALYZING
Image with name "petclinic" not found
===> DETECTING
2 of 4 buildpacks participating
heroku/jvm   6.1.2
heroku/maven 6.1.2
===> RESTORING
===> BUILDING

Your application does not explicitly specify an OpenJDK version. The latest
long-term support (LTS) version will be installed. This currently is OpenJDK 21.
This default version will change when a new LTS version is released. Your
application might fail to build with the new version. We recommend explicitly
setting the required OpenJDK version for your application.
To set the OpenJDK version, add or edit the system.properties file in the root
directory of your application to contain:
java.runtime.version = 21


Maven wrapper detected, skipping installation.

$ ./mvnw -DskipTests clean install
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
Scanning for projects...
Downloading from spring-snapshots: https://repo.spring.io/snapshot/org/springframework/boot/spring-boot-starter-parent/3.4.2/spring-boot-starter-parent-3.4.2.pom
Downloading from spring-milestones: https://repo.spring.io/milestone/org/springframework/boot/spring-boot-starter-parent/3.4.2/spring-boot-starter-parent-3.4.2.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/3.4.2/spring-boot-starter-parent-3.4.2.pom
.......... 此处省略非常多的maven build日志..........
.......... spring-petclinic的依赖很多,需要耐心等待下载和编译..........
Downloaded from central: https://repo.maven.apache.org/maven2/com/github/luben/zstd-jni/1.5.6-3/zstd-jni-1.5.6-3.jar (6.7 MB at 48 MB/s)
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time:35.909 s
Finished at: 2025-04-15T12:03:56Z
------------------------------------------------------------------------
===> EXPORTING
Adding layer 'heroku/jvm:openjdk'
Adding layer 'heroku/jvm:runtime'
Adding layer 'buildpacksio/lifecycle:launch.sbom'
Added 1/1 app layer(s)
Adding layer 'buildpacksio/lifecycle:launcher'
Adding layer 'buildpacksio/lifecycle:config'
Adding layer 'buildpacksio/lifecycle:process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Setting default process type 'web'
Saving petclinic...
*** Images (37a5a5d4a590):
      petclinic
Adding cache layer 'heroku/jvm:openjdk'
Adding cache layer 'heroku/maven:repository'
Successfully built image petclinic从上面的日志可以看出,从代码到容器镜像,构建过程包含了以下步骤:

[*]下载builder镜像
[*]分析代码仓库是否存在缓存
[*]检测代码仓库适合的buildpack
[*]安装依赖的OpenJDK
[*]安装依赖的maven,由于此项目使用了maven wrapper,跳过了安装maven
[*]执行./mvnw -DskipTests clean install
[*]输出运行环境和构建结果到petclinic镜像中

[*]镜像验证
我们查看一下本地的docker镜像,可以发现petclinic就是我们刚刚构建出来的目标容器镜像。
而其他3个镜像都是CNB构建使用的镜像。
# docker images
REPOSITORY               TAG       IMAGE ID       CREATED      SIZE
heroku/heroku            24      7c60575a1268   7 days ago   493MB
buildpacksio/lifecycle   0.20.7    c083cc1d50e2   45 years ago   35.6MB
petclinic                latest    37a5a5d4a590   45 years ago   821MB
heroku/builder         24      2a52b7dc6e23   45 years ago   1.2GB在本地运行镜像(注意需要指定应用监听的端口):
# docker run -p 8080:8080 petclinic
Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=80.0 -Dfile.encoding=UTF-8
            |\      _,,,--,,_
             /,`.-'`'   ._\-;;,_
_______ __|,4-) )_   .;.(__`'-'__   ___ __    _ ___ _______
|       | '---''(_/._)-'(_\_)   |   |   |   ||| |   |       |
|    _|    ___|_   _|       |   |   |   |   |_| |   |       | __ _ _
|   |_| |   |___|   | |       |   |   |   |       |   |       | \ \ \ \
|    ___|    ___| |   | |      _|   |___|   |_    |   |      _|\ \ \ \
|   |   |   |___|   | |   |_|       |   | | |   |   |   |_    ) ) ) )
|___|   |_______| |___| |_______|_______|___|_||__|___|_______|/ / / /
==================================================================/_/_/_/
:: Built with Spring Boot :: 3.4.2
2025-04-15T12:10:51.412ZINFO 1 --- [         main] o.s.s.petclinic.PetClinicApplication   : Starting PetClinicApplication v3.4.0-SNAPSHOT using Java 21.0.5 with PID 1 (/workspace/target/spring-petclinic-3.4.0-SNAPSHOT.jar started by heroku in /workspace)
2025-04-15T12:10:51.421ZINFO 1 --- [         main] o.s.s.petclinic.PetClinicApplication   : No active profile set, falling back to 1 default profile: "default"
2025-04-15T12:10:53.002ZINFO 1 --- [         main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2025-04-15T12:10:53.176ZINFO 1 --- [         main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 160 ms. Found 2 JPA repository interfaces.
2025-04-15T12:10:54.196ZINFO 1 --- [         main] o.s.b.w.embedded.tomcat.TomcatWebServer: Tomcat initialized with port 8080 (http)
2025-04-15T12:10:54.216ZINFO 1 --- [         main] o.apache.catalina.core.StandardService   : Starting service
2025-04-15T12:10:54.216ZINFO 1 --- [         main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine:
2025-04-15T12:10:54.263ZINFO 1 --- [         main] o.a.c.c.C...[/]       : Initializing Spring embedded WebApplicationContext
2025-04-15T12:10:54.265ZINFO 1 --- [         main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2774 ms
2025-04-15T12:10:54.664ZINFO 1 --- [         main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2025-04-15T12:10:54.934ZINFO 1 --- [         main] com.zaxxer.hikari.pool.HikariPool      : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:3480bd0f-aaa2-489a-9469-99dc4aa2fc52 user=SA
2025-04-15T12:10:54.936ZINFO 1 --- [         main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2025-04-15T12:10:55.120ZINFO 1 --- [         main] o.hibernate.jpa.internal.util.LogHelper: HHH000204: Processing PersistenceUnitInfo
2025-04-15T12:10:55.174ZINFO 1 --- [         main] org.hibernate.Version                  : HHH000412: Hibernate ORM core version 6.6.5.Final
2025-04-15T12:10:55.219ZINFO 1 --- [         main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
2025-04-15T12:10:55.463ZINFO 1 --- [         main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2025-04-15T12:10:55.550ZINFO 1 --- [         main] org.hibernate.orm.connections.pooling    : HHH10001005: Database info:
      Database JDBC URL
      Database driver: undefined/unknown
      Database version: 2.3.232
      Autocommit mode: undefined/unknown
      Isolation level: undefined/unknown
      Minimum pool size: undefined/unknown
      Maximum pool size: undefined/unknown
2025-04-15T12:10:56.718ZINFO 1 --- [         main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2025-04-15T12:10:56.722ZINFO 1 --- [         main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2025-04-15T12:10:57.213ZINFO 1 --- [         main] o.s.d.j.r.query.QueryEnhancerFactory   : Hibernate is in classpath; If applicable, HQL parser will be used.
2025-04-15T12:10:58.875ZINFO 1 --- [         main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 14 endpoints beneath base path '/actuator'
2025-04-15T12:10:58.974ZINFO 1 --- [         main] o.s.b.w.embedded.tomcat.TomcatWebServer: Tomcat started on port 8080 (http) with context path '/'
2025-04-15T12:10:58.991ZINFO 1 --- [         main] o.s.s.petclinic.PetClinicApplication   : Started PetClinicApplication in 8.166 seconds (process running for 8.841)
2025-04-15T12:11:07.392ZINFO 1 --- o.a.c.c.C...[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-04-15T12:11:07.392ZINFO 1 --- o.s.web.servlet.DispatcherServlet      : Initializing Servlet 'dispatcherServlet'
2025-04-15T12:11:07.394ZINFO 1 --- o.s.web.servlet.DispatcherServlet      : Completed initialization in 2 ms从浏览器可以正常访问:

在阿里云上构建和运行

手工使用pack命令构建在进行部署是比较麻烦的。首先你需要有一个可以访问DockerHub的构建机器,其次还需要有一个docker image registry来托管容器镜像,最后应用部署的工作还要自己来做。
阿里云应用管理支持使用CNB构建应用并部署到云服务器(ECS)上,并支持在代码更新后更新应用,实现GitOps。整个过程完全基于控制台GUI,一键提交部署,非常方便。
应用管理在ECS控制台、OOS控制台都有入口。其中“创建应用”->“通过Git仓库创建”使用了Cloud Native Buildpacks技术。

[*]您需要有一个Git平台的账号(Github或Gitee)。输入账号后,可以选择您账号下的代码仓库,也可以输入一个公开仓库的地址(由于Git平台对非登录的API调用限流很严格,所以公开仓库也需要使用您的平台账号去访问)。注意随意找的代码仓库很可能不支持CNB构建,建议您先使用示例的代码库尝试。


[*]输入应用名称、分组名称以及分组部署的地域。
[*]选择代码分支,会展示该分支最新的Git Commit。这个Commit就代表了我们部署的程序版本。
我们的应用最终会以容器的形式运行,如果代码执行需要参数,运行参数需要通过容器环境变量的形式传入。
如果当前代码是通过别的方式(比如配置文件、命令行参数)传入参数,需要改造成支持环境变量传入。
应用监听端口有以下作用:


[*]作为-p参数传递给docker run命令
[*]作为环境变量传递给docker run命令
[*]会在云服务器安全组中增加规则,放行入方向对该端口的公网访问

下面图示的配置,最终运行容器的命令如下:
docker run -e EnvName=EnvValue -e PORT=8080 -p 8080:8080 <镜像名称>

[*]应用管理会新建一台ECS服务器来部署应用,您可以按需调整云服务器配置。


[*]点击“创建”按钮后就启动创建了。创建完成后会输出应用访问的URL和登录ECS的URL。由于应用是异步启动的,如果无法访问请稍等片刻。
代码更新后,在“更新应用”->“更新应用程序”里可以拉取最新代码更新容器镜像并重新部署应用。

点击“查看日志”,可以看到CNB构建的日志和云服务器创建的日志:

Cloud Native Buildpacks的局限

CNB看起来很美好,但不是任意代码库都能够被CNB支持。

[*]应用需要符合一定的规范,Buildpack才能够识别出应用的依赖、应用的构建过程和运行命令。比如说代码库中常常需要增加一个Procfile文件来指定应用的启动命令。
[*]CNB支持的语言版本、SDK、运行环境、软件版本是有一定范围的,老旧版本和有安全漏洞的版本可能不被支持。
因此在正式使用CNB之前,应用开发者应该了解CNB实现对应用代码的要求。
以Heroku的CNB Builder构建Java应用来举例:

[*]代码仓库的根目录中需要包含pom.xml。
[*]应用代码支持通过Maven或Gradle构建。
[*]应用支持的JDK版本符合要求(目前支持8、11、17、21以及22版本)。
CNB支持的运行时组件的版本会不断更新。如果您在阿里云上使用CNB部署应用遇到问题,可以在应用管理支持钉群(群号:10880003624)里中反馈或提交工单,我们会帮助您解决。
延伸阅读


[*]Cloud Native Buildpacks官网文档:https://buildpacks.io/docs/
[*]阿里云从Git仓库部署应用:https://help.aliyun.com/zh/oos/user-guide/create-an-application-through-a-git-repository

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 从代码到容器:Cloud Native Buildpacks技术解析