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

本文涉及的产品
轻量应用服务器 2vCPU 4GiB,适用于搭建Web应用/小程序
轻量应用服务器 2vCPU 4GiB,适用于网站搭建
轻量应用服务器 2vCPU 1GiB,适用于搭建电商独立站
简介: Cloud Native Buildpacks(CNB)是一种标准化、云原生的容器镜像构建系统,旨在消除手动编写Dockerfile,提供可重复、安全且高效的构建流程。它通过分层策略生成符合OCI标准的镜像,实现应用与基础镜像解耦,并自动化依赖管理和更新。阿里云应用管理支持通过CNB技术一键部署应用至ECS,简化构建和运行流程。

Cloud Native Buildpacks是什么?

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

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

image.png

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

image.png

CNB的架构和原理

核心概念

组件

功能说明

Lifecycle

Lifecycle管理整个构建过程,由多阶段构成

Builder

包含Buildpacks、生命周期组件的构建环境(容器)

Buildpack

模块化构建逻辑单元(可组合),负责处理应用程序源代码,安装依赖项,配置环境并生成最终的可运行单位


CNB Lifecycle核心构建步骤

  1. Detect(检测)
  • 扫描项目代码结构(如pom.xml/build.gradle)
  • 匹配适用的Buildpacks(如Java/Maven Buildpack)
  • 输出Buildpack执行顺序
  1. Analyze(分析)
  • 对比新旧构建元数据(layer.toml)
  • 识别可复用的缓存层(如Maven依赖)
  • 决定需要重建的层
  1. Restore(恢复)
  • 从缓存中还原依赖层(如JDK安装、.m2仓库)
  • 加速后续构建过程
  1. Build(构建)
  • 执行Buildpacks的编译逻辑(如mvn package)
  • 生成新的应用层(如编译后的JAR包)
  • 更新层元数据(store.toml)
  1. Export(导出)
  • 将各层打包为OCI镜像
  • 注入启动配置(如JAVA_TOOL_OPTIONS)
  • 输出最终镜像到镜像仓库

OCI镜像构建机制

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

image.png

每个Buildpack都会检查源代码并提供相关的依赖项。然后,会根据应用程序的源代码和这些依赖项生成一个镜像。

在构建过程中,构建时基础镜像成为执行Buildpack的环境,而运行时基础镜像成为最终应用程序镜像的基础。

Buildpacks可以与特定的构建时基础镜像捆绑在一起,从而生成Builder镜像。Builder提供了一种便捷的Buildpacks分发方式。


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

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

本地构建和运行

CNB相关的镜像托管在DockerHub上。因此要求您从本地可以访问DockerHub。

以下以在阿里云的香港/新加坡等非大陆地域的ECS实例作为试验环境。

  1. 环境准备
# 在阿里云香港地域购买一台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/
  1. 构建应用
# 克隆代码到本地
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
  1. 构建过程解析
[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镜像中
  1. 镜像验证

我们查看一下本地的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

从浏览器可以正常访问:

image.png

在阿里云上构建和运行

手工使用pack命令构建在进行部署是比较麻烦的。首先你需要有一个可以访问DockerHub的构建机器,其次还需要有一个docker image registry来托管容器镜像,最后应用部署的工作还要自己来做。

阿里云应用管理支持使用CNB构建应用并部署到云服务器(ECS)上,并支持在代码更新后更新应用,实现GitOps。整个过程完全基于控制台GUI,一键提交部署,非常方便。

应用管理在ECS控制台OOS控制台都有入口。其中“创建应用”->“通过Git仓库创建”使用了Cloud Native Buildpacks技术。

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

image.png

  1. 输入应用名称、分组名称以及分组部署的地域。
  2. 选择代码分支,会展示该分支最新的Git Commit。这个Commit就代表了我们部署的程序版本。

我们的应用最终会以容器的形式运行,如果代码执行需要参数,运行参数需要通过容器环境变量的形式传入。

如果当前代码是通过别的方式(比如配置文件、命令行参数)传入参数,需要改造成支持环境变量传入。

应用监听端口有以下作用:

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

下面图示的配置,最终运行容器的命令如下:

docker run -e EnvName=EnvValue -e PORT=8080 -p 8080:8080 <镜像名称>

image.png

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

image.png

  1. 点击“创建”按钮后就启动创建了。创建完成后会输出应用访问的URL和登录ECS的URL。由于应用是异步启动的,如果无法访问请稍等片刻。

代码更新后,在“更新应用”->“更新应用程序”里可以拉取最新代码更新容器镜像并重新部署应用。

image.png

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

image.png

Cloud Native Buildpacks的局限

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

  1. 应用需要符合一定的规范,Buildpack才能够识别出应用的依赖、应用的构建过程和运行命令。比如说代码库中常常需要增加一个Procfile文件来指定应用的启动命令。
  2. CNB支持的语言版本、SDK、运行环境、软件版本是有一定范围的,老旧版本和有安全漏洞的版本可能不被支持。

因此在正式使用CNB之前,应用开发者应该了解CNB实现对应用代码的要求。

Heroku的CNB Builder构建Java应用来举例:

  • 代码仓库的根目录中需要包含pom.xml
  • 应用代码支持通过MavenGradle构建。
  • 应用支持的JDK版本符合要求(目前支持8、11、17、21以及22版本)。

CNB支持的运行时组件的版本会不断更新。如果您在阿里云上使用CNB部署应用遇到问题,可以在应用管理支持钉群(群号:10880003624)里中反馈或提交工单,我们会帮助您解决。

延伸阅读

相关文章
|
3月前
|
存储 容器
46.[HarmonyOS NEXT RelativeContainer案例三] 打造自适应容器:内容驱动的智能尺寸调整技术
在HarmonyOS NEXT的UI开发中,创建能够根据内容自动调整尺寸的容器是实现灵活布局的关键。RelativeContainer结合自适应尺寸设置,可以实现内容驱动的智能尺寸调整,使UI更加灵活且易于维护。本教程将详细讲解如何创建自适应尺寸的RelativeContainer,帮助你掌握这一实用技术。
127 5
|
2月前
|
缓存 Java Docker
如何对应用代码进行优化以提高在Docker容器中的性能?
如何对应用代码进行优化以提高在Docker容器中的性能?
204 1
|
1月前
|
Kubernetes Cloud Native 持续交付
Docker:轻量级容器化技术解析
Docker:轻量级容器化技术解析
|
1月前
|
运维 测试技术 Docker
Docker:轻量级容器化技术革命
Docker:轻量级容器化技术革命
|
3月前
|
存储 缓存 安全
Java 集合容器常见面试题及详细解析
本文全面解析Java集合框架,涵盖基础概念、常见接口与类的特点及区别、底层数据结构、线程安全等内容。通过实例讲解List(如ArrayList、LinkedList)、Set(如HashSet、TreeSet)、Map(如HashMap、TreeMap)等核心组件,帮助读者深入理解集合容器的使用场景与性能优化。适合准备面试或提升开发技能的开发者阅读。
65 0
|
9月前
|
Kubernetes Cloud Native 微服务
探索云原生技术:容器化与微服务架构的融合之旅
本文将带领读者深入了解云原生技术的核心概念,特别是容器化和微服务架构如何相辅相成,共同构建现代软件系统。我们将通过实际代码示例,探讨如何在云平台上部署和管理微服务,以及如何使用容器编排工具来自动化这一过程。文章旨在为开发者和技术决策者提供实用的指导,帮助他们在云原生时代中更好地设计、部署和维护应用。
|
6月前
|
存储 虚拟化 Docker
|
6月前
|
开发工具 虚拟化 git
自学软硬件第755 docker容器虚拟化技术youtube视频下载工具
docker容器虚拟化技术有什么用?怎么使用?TubeTube 项目使用youtube视频下载工具
|
7月前
|
人工智能 安全 API
容器化AI模型的安全防护实战:代码示例与最佳实践
本文基于前文探讨的容器化AI模型安全威胁,通过代码示例展示如何在实际项目中实现多层次的安全防护措施。以一个基于TensorFlow的图像分类模型为例,介绍了输入验证、模型加密、API认证和日志记录的具体实现方法,并结合最佳实践,如使用安全容器镜像、限制权限、网络隔离等,帮助构建更安全的AI服务。
|
8月前
|
Kubernetes Linux 虚拟化
入门级容器技术解析:Docker和K8s的区别与关系
本文介绍了容器技术的发展历程及其重要组成部分Docker和Kubernetes。从传统物理机到虚拟机,再到容器化,每一步都旨在更高效地利用服务器资源并简化应用部署。容器技术通过隔离环境、减少依赖冲突和提高可移植性,解决了传统部署方式中的诸多问题。Docker作为容器化平台,专注于创建和管理容器;而Kubernetes则是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。两者相辅相成,共同推动了现代云原生应用的快速发展。
2326 11

热门文章

最新文章