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核心构建步骤
- 扫描项目代码结构(如pom.xml/build.gradle)
- 匹配适用的Buildpacks(如Java/Maven Buildpack)
- 输出Buildpack执行顺序
- 对比新旧构建元数据(layer.toml)
- 识别可复用的缓存层(如Maven依赖)
- 决定需要重建的层
- 从缓存中还原依赖层(如JDK安装、.m2仓库)
- 加速后续构建过程
- 执行Buildpacks的编译逻辑(如mvn package)
- 生成新的应用层(如编译后的JAR包)
- 更新层元数据(store.toml)
- 将各层打包为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
复制代码- [root@cnb-demo spring-petclinic]# 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
- [Warning: No OpenJDK version specified]
- 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
- [Installing OpenJDK 21.0.5]
- [Installing Maven]
- Maven wrapper detected, skipping installation.
- [Executing Maven]
- $ ./mvnw -DskipTests clean install
- Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
- [INFO] Scanning for projects...
- [INFO] 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
- [INFO] 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
- [INFO] 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的依赖很多,需要耐心等待下载和编译..........
- [INFO] 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)
- [INFO] ------------------------------------------------------------------------
- [INFO] BUILD SUCCESS
- [INFO] ------------------------------------------------------------------------
- [INFO] Total time: 35.909 s
- [INFO] Finished at: 2025-04-15T12:03:56Z
- [INFO] ------------------------------------------------------------------------
- ===> 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构建使用的镜像。- [root@cnb-demo spring-petclinic]# 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
复制代码 在本地运行镜像(注意需要指定应用监听的端口):- [root@cnb-demo spring-petclinic]# 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.412Z INFO 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.421Z INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : No active profile set, falling back to 1 default profile: "default"
- 2025-04-15T12:10:53.002Z INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
- 2025-04-15T12:10:53.176Z INFO 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.196Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
- 2025-04-15T12:10:54.216Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
- 2025-04-15T12:10:54.216Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.34]
- 2025-04-15T12:10:54.263Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
- 2025-04-15T12:10:54.265Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2774 ms
- 2025-04-15T12:10:54.664Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
- 2025-04-15T12:10:54.934Z INFO 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.936Z INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
- 2025-04-15T12:10:55.120Z INFO 1 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
- 2025-04-15T12:10:55.174Z INFO 1 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.6.5.Final
- 2025-04-15T12:10:55.219Z INFO 1 --- [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
- 2025-04-15T12:10:55.463Z INFO 1 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
- 2025-04-15T12:10:55.550Z INFO 1 --- [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
- Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
- 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.718Z INFO 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.722Z INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
- 2025-04-15T12:10:57.213Z INFO 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.875Z INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoints beneath base path '/actuator'
- 2025-04-15T12:10:58.974Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
- 2025-04-15T12:10:58.991Z INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : Started PetClinicApplication in 8.166 seconds (process running for 8.841)
- 2025-04-15T12:11:07.392Z INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
- 2025-04-15T12:11:07.392Z INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
- 2025-04-15T12:11:07.394Z INFO 1 --- [nio-8080-exec-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
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |