mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
3978 字
11 分钟
基于 buildpacks 技术的云原生构建
2021-12-21

一、构建往事:从 PaaS 到 Kubernetes#

第一代代表性 PaaS 平台,Heroku、VMware Cloud Foundry,都采用了 buildpack 技术构建用户应用。平台方提前为各类运行时和框架提供构建流水线,用户只需提交代码即可获得新版本应用,再通过平台 API 部署。这套模式的核心假设是:构建逻辑应该由平台统一提供,而非每个开发者各自编写

Kubernetes 改变了交付格式,所有应用统一为 OCI 镜像,但构建问题并没有因此消失,反而更突出了。OCI 镜像的灵活性意味着任何语言和框架都能跑,但大部分开发者并不擅长制作高效且安全的镜像。Dockerfile 把构建知识推给了每个开发者,这恰恰是 PaaS 时代 buildpack 试图消除的负担。

从 Cloud Foundry 延伸而来的 Cloud Native Buildpacks(CNB)项目,正是要把 buildpack 的理念重新嫁接到 OCI 镜像的世界里。同期,Cloud Foundry buildpacks 团队推出了 paketo-buildpacks 项目,帮助用户从传统环境过渡到 Kubernetes。

二、buildpack 的核心思路#

应用镜像构建,即从「源代码/可执行程序及其运行时」(后文简称「源目标」)配合基础镜像构建成应用镜像的过程。传统方式需要编写 Dockerfile,用 docker build 命令堆叠镜像层。Dockerfile 的问题不在于它不够灵活,恰恰相反,它太灵活了。每个团队各自摸索多阶段构建、缓存策略、安全加固,重复造轮子且质量参差不齐。

buildpacks 的思路是反转这个责任归属:构建逻辑由平台提供,开发者只提供源代码。builder 自动检测语言类型、选择构建逻辑、输出 OCI 镜像。开发者不需要知道什么是多阶段构建,不需要知道 npm cinpm install 的区别,不需要知道 run image 该不该装 curl,这些决策由 buildpack 的维护者代为做出。

这个代决策是有代价的:开发者让渡了对构建流程的完全控制权。后文会讨论这个取舍的边界。

三、CNB 规范核心概念#

buildpacks 的运作依赖四个概念:LifecycleBuildpackBuilderStack。它们之间的关系如下图:

graph TB subgraph Builder["Builder 镜像"] B1[Buildpack 1] B2[Buildpack 2] B3[Buildpack N] L[Lifecycle] S[Stack] end subgraph Stack["Stack 组成"] BI[Build Image<br/>构建环境] RI[Run Image<br/>运行环境] end subgraph Process["构建流程"] SC[源代码] --> Detect[Detect] Detect -->|检出| Build[Build] Build -->|产出| App[应用镜像] end B1 --> Detect B2 --> Detect B3 --> Detect L --> Detect L --> Build BI --> Build RI --> App

3.1 Lifecycle:构建流水线的编排者#

Lifecycle 是 CNB 规范的核心组件,它定义了从源代码到最终镜像的完整构建流程。它不是某个 buildpack 的执行器,而是一个独立的协调层,决定哪些 buildpack 参与构建、管理缓存、导出镜像。这种分离意味着 Lifecycle 的实现可以独立于 buildpack 迭代,buildpack 的维护者也不需要关心镜像导出的底层细节。

Lifecycle 包含五个阶段:

阶段职责说明
Detect检测阶段遍历所有 buildpack,找出能够处理当前源代码的那些
Analyze分析阶段分析之前的构建缓存,加速本次构建
Restore恢复阶段恢复之前构建的缓存层
Build构建阶段执行 buildpack 的构建逻辑,编译应用、安装依赖
Export导出阶段将构建产物打包成 OCI 镜像
# 简化的 Lifecycle 执行流程
1. /cnb/lifecycle/detector # 检测哪些 buildpack 适用
2. /cnb/lifecycle/analyzer # 分析镜像层,准备缓存
3. /cnb/lifecycle/restorer # 恢复缓存层
4. /cnb/lifecycle/builder # 执行构建
5. /cnb/lifecycle/exporter # 导出最终镜像

分阶段设计的核心收益是缓存友好。Analyze 和 Restore 阶段专门处理缓存复用,增量构建时只有变化的层需要重新构建。这比 Dockerfile 的 --cache-from 更精细,Dockerfile 的缓存以指令为单位,一条指令失效则后续所有指令的缓存全部作废;CNB 的缓存以 buildpack 的 layer 为单位,一个 buildpack 的缓存失效不影响其他 buildpack。此外,每个阶段有明确的输入输出,构建失败时可以快速定位是哪个阶段出了问题。

3.2 Buildpack:构建能力的原子单位#

Buildpack 是 CNB 生态中最基础的构建单元,每个 buildpack 专注于特定语言或框架的构建逻辑。比如有针对 Java 的 buildpack、针对 Node.js 的 buildpack、针对 Python 的 buildpack 等。

一个标准的 buildpack 必须包含两个核心脚本:

my-buildpack

bin

detect

build

buildpack.toml

detect 脚本的作用是「自荐」,告诉 Lifecycle「我能处理这类源代码」。它的逻辑通常是检查项目特征文件:

#!/bin/bash
# bin/detect 示例
# 检查是否存在 package.json,判断是否为 Node.js 项目
if [ -f "package.json" ]; then
echo "nodejs" # 输出 plan 名称
exit 0
fi
# 如果不能处理,返回非零退出码
exit 1

build 脚本则是「兑现承诺」,实际执行构建工作:

#!/bin/bash
# bin/build 示例
# 设置环境
export PATH="/cnb/process:$PATH"
# 安装依赖
npm install --production
# 设置启动命令
cat > launch.toml <<EOF
[[processes]]
type = "web"
command = "npm start"
EOF

「检测→构建」的两步模式看似简单,实则解决了一个组合问题:一个复杂的应用可能需要多个 buildpack 协同工作。比如一个 Node.js 应用可能需要「Node.js buildpack + Nginx buildpack」组合来完成前后端的构建。detect 阶段让 Lifecycle 知道哪些 buildpack 需要参与,build 阶段再按顺序执行,这比 Dockerfile 里把所有构建逻辑塞进一个文件要模块化得多。每个 buildpack 可以独立版本化、独立更新,不需要修改应用代码。

3.3 Stack:构建和运行的双镜像架构#

Stack 定义了构建环境和运行环境的镜像基础,包含两个镜像:

graph LR subgraph Stack BI["Build Image<br/>(构建环境)"] RI["Run Image<br/>(运行环境)"] end BI -->|包含| BC["构建工具链<br/>编译器、包管理器等"] RI -->|包含| RC["运行时依赖<br/>系统库、运行时等"] BI -->|共享基础层| RI

Build Image 提供编译和打包所需的完整工具链。Java 构建镜像包含 JDK、Maven/Gradle、Git;Node.js 构建镜像包含 Node.js、npm/yarn。Run Image 只提供应用运行所需的最小化环境,不包含构建工具。

# Build Image 的基础
FROM ubuntu:22.04 AS build-image
RUN apt-get update && apt-get install -y \
build-essential \
curl \
git \
# ... 更多构建工具
ENV CNB_USER_ID=1000
ENV CNB_GROUP_ID=1000
# Run Image 的基础(通常共享相同的基础镜像)
FROM ubuntu:22.04 AS run-image
RUN apt-get update && apt-get install -y \
ca-certificates \
# ... 只安装运行时必需的包

双镜像架构的本质是最小权限原则在镜像层面的体现。传统 Dockerfile 的多阶段构建也在做类似的事,用 AS builder 阶段编译,再 COPY --from=builder 到精简的运行镜像。但 Dockerfile 的多阶段构建依赖开发者自觉遵守,buildpack 把这个分离强制化了:构建工具永远不会出现在 run image 里,不是因为你记得不装,而是因为构建和运行根本不在同一个镜像里。

这种分离的代价是灵活性降低。如果你的应用在运行时需要 curl 做健康检查,你不能像 Dockerfile 那样随手加一行 RUN apk add curl,你需要自定义 run image 或使用支持健康检查的 buildpack。

3.4 Builder:构建能力的集合体#

Builder 是一个集成了 Lifecycle、多个 buildpack 和 Stack 的 OCI 镜像。它把构建所需的一切打包在一起,用户只需要推送源代码进去,就能得到构建好的镜像。

Builder 的结构通过 builder.toml 配置文件定义:

# builder.toml 示例
# 构建顺序很重要:Lifecycle 会按顺序尝试检测
[[buildpacks]]
id = "paketo-buildpacks/nodejs"
version = "1.0.0"
uri = "docker://gcr.io/paketo-buildpacks/nodejs"
[[buildpacks]]
id = "paketo-buildpacks/npm"
version = "1.0.0"
uri = "docker://gcr.io/paketo-buildpacks/npm"
[[buildpacks]]
id = "paketo-buildpacks/java"
version = "1.0.0"
uri = "docker://gcr.io/paketo-buildpacks/java"
# 定义构建顺序和分组
[[order]]
[[order.group]]
id = "paketo-buildpacks/nodejs"
[[order]]
[[order.group]]
id = "paketo-buildpacks/java"
# 指定 Stack
[stack]
id = "io.buildpacks.stacks.jammy"
build-image = "paketobuildpacks/build-jammy-base"
run-image = "paketobuildpacks/run-jammy-base"
# 使用 pack CLI 构建 builder
pack builder create my-builder \
--config builder.toml \
--path ./
# 查看构建好的 builder 内容
pack builder inspect my-builder
sequenceDiagram participant User as 用户 participant Pack as pack CLI participant Docker as Docker Daemon participant Registry as 镜像仓库 User->>Pack: pack builder create Pack->>Docker: 构建 builder 镜像 Note over Docker: 1. 拉取基础镜像<br/>2. 注入 lifecycle<br/>3. 添加 buildpacks<br/>4. 设置环境变量 Docker-->>Pack: builder 镜像 ID Pack->>Registry: 推送 builder 镜像 Registry-->>User: builder 可用

Builder 的设计把「构建能力」变成了一个可版本化、可分发的制品。平台团队可以维护一个标准 Builder,所有业务团队共用,当 Builder 升级(比如修复了某个 buildpack 的安全漏洞),所有应用只需重新构建即可受益,不需要逐个修改 Dockerfile。

四、构建流程详解#

当用户使用 pack build 命令构建应用时,完整的构建流程如下:

sequenceDiagram participant User as 用户 participant Pack as pack CLI participant Lifecycle as Lifecycle participant BP as Buildpacks participant Docker as Docker User->>Pack: pack build my-app --builder my-builder Pack->>Docker: 拉取 builder 镜像 Pack->>Docker: 创建构建容器 Note over Lifecycle: Phase 1: Detect Lifecycle->>BP: 遍历执行 detect 脚本 BP-->>Lifecycle: 返回检测结果 Note over Lifecycle: 确定构建计划 Note over Lifecycle: Phase 2: Analyze Lifecycle->>Docker: 分析之前的构建缓存 Note over Lifecycle: Phase 3: Restore Lifecycle->>Docker: 恢复缓存层 Note over Lifecycle: Phase 4: Build Lifecycle->>BP: 按顺序执行 build 脚本 BP->>BP: 安装依赖 BP->>BP: 编译代码 BP->>BP: 配置启动命令 BP-->>Lifecycle: 构建完成 Note over Lifecycle: Phase 5: Export Lifecycle->>Docker: 创建最终镜像层 Docker-->>Pack: 返回镜像 ID Pack-->>User: 构建成功

4.1 Layer 缓存机制#

CNB 的缓存机制比 Dockerfile 的层缓存更精细。每个 buildpack 可以声明自己的缓存策略,将构建产物划分为不同的层:

一个典型应用的镜像层结构:

base来自 run image

bin

lib

usr

node-runtimeNode.js 运行时

layers/paketo-buildpacks_nodejs/node

npm-cachenpm 缓存

layers/paketo-buildpacks_npm/cache

app-dependencies应用依赖

workspace/node_modules

app应用代码

workspace

Dockerfile 的缓存以 RUN/COPY 指令为单位,一条 RUN npm install 对应一整层,只要 package.json 变了,整个 node_modules 层都要重建。CNB 的缓存以 buildpack 的 layer 为单位,且 buildpack 可以在 layer 内部做更细粒度的判断:npm-cache 层和 app-dependencies 层是分开的,依赖没变时只重建 app 层。这种分层策略让增量构建的速度显著快于 Dockerfile 方式。

五、Paketo Buildpacks#

Paketo 是由 Cloud Foundry 基金会维护的一套开源 buildpacks 集合,完全遵循 CNB 规范。它的价值不在于「又多了一个 buildpack 实现」,而在于把构建最佳实践从隐性知识变成了显性制品

5.1 零 Dockerfile 的构建体验#

传统方式需要为每个项目维护 Dockerfile:

# 传统 Dockerfile 示例
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["npm", "start"]

使用 Paketo 后,只需一行命令:

# 自动检测语言、安装依赖、构建镜像
pack build my-app --builder paketobuildpacks/builder-jammy-base

这行命令背后做了什么?Lifecycle 检测到 package.json,选中 Node.js buildpack;buildpack 安装合适版本的 Node.js、执行 npm install、配置启动命令、生成 SBOM、应用多阶段构建,所有这些都需要 Dockerfile 作者手动处理的事情,buildpack 自动完成了。

5.2 安全更新自动化#

当基础镜像发现安全漏洞时,Dockerfile 方式需要修改所有项目的 Dockerfile、重新构建、重新部署。Paketo 方式只需重新构建,buildpack 自动应用最新的安全补丁,因为安全策略集中在 Builder 里而非分散在每个 Dockerfile 中。

# 只需重新构建,buildpack 自动应用最新的安全补丁
pack build my-app --builder paketobuildpacks/builder-jammy-base

这种集中管理的代价是:你信任 Paketo 团队对安全补丁的判断。如果 Paketo 的 run image 更新引入了不兼容变更,你的应用可能需要调整。这和依赖 Linux 发行版的安全更新是同一种信任模型,你把安全决策委托给了上游,换来的是不必自己跟踪每个 CVE。

5.3 最佳实践内置#

Paketo 团队持续跟踪各种语言和框架的最佳实践:

最佳实践传统 DockerfilePaketo Buildpacks
多阶段构建手动编写自动应用
安全扫描需额外配置内置 SBOM
最小化镜像需经验判断自动优化
依赖缓存手动设计自动分层缓存
启动命令手动配置自动检测

5.4 软件物料清单(SBOM)#

Paketo 自动生成 SBOM,满足供应链安全合规要求:

# 构建时自动生成 SBOM
pack build my-app --builder paketobuildpacks/builder-jammy-base
# 查看镜像中的 SBOM
pack inspect my-app --sbom

SBOM 输出示例:

{
"sbom": {
"spdxid": "SPDXRef-DOCUMENT",
"documentNamespace": "https://paketo.io/sbom/my-app",
"packages": [
{
"name": "node",
"version": "18.17.0",
"license": "MIT"
},
{
"name": "npm",
"version": "9.6.7",
"license": "Artistic-2.0"
}
]
}
}

SBOM 在 Dockerfile 方式下需要额外工具(如 Syft、Trivy)扫描镜像才能生成,且扫描结果依赖工具对镜像层的解析能力。Paketo 的 SBOM 是构建时原生生成的,准确性更高,buildpack 知道自己装了什么版本、什么许可证,不需要事后从文件系统反推。

5.5 Paketo Buildpack 家族#

Paketo 提供了丰富的语言和框架支持:

graph TB subgraph Languages["语言运行时"] Java[Java Buildpack] Node[Node.js Buildpack] Go[Go Buildpack] Python[Python Buildpack] Ruby[Ruby Buildpack] PHP[PHP Buildpack] NET[.NET Buildpack] end subgraph Frameworks["框架支持"] Spring[Spring Boot] React[React/Vue/Angular] Django[Django] Rails[Rails] end subgraph Features["附加能力"] Procfile[Procfile 支持] Health[健康检查] Config[配置注入] Secrets[密钥管理] end Languages --> Frameworks Languages --> Features

5.6 Builder 变体选择#

Paketo 提供了三种 Builder 变体,区别在于 run image 的精简程度:

Builder基础镜像大小适用场景
builder-jammy-baseUbuntu 22.04~1GB通用场景,支持最多语言
builder-jammy-fullUbuntu 22.04~2GB需要完整工具链的复杂应用
builder-jammy-tinyUbuntu 22.04~100MB最小化镜像,仅支持静态编译语言

tiny 变体的 run image 基于 distroless 思路,只包含 glibc 和动态链接器,不包含 shell、包管理器等。这意味着你无法 docker exec 进容器调试,也无法在运行时安装任何额外工具。选择 tiny 意味着你接受这个约束,换取更小的攻击面和更快的启动速度。

# 大多数 Web 应用
pack build my-web-app --builder paketobuildpacks/builder-jammy-base
# Go、Rust 等静态编译语言(最小镜像)
pack build my-cli-app --builder paketobuildpacks/builder-jammy-tiny
# 需要 .NET、PHP 等完整运行时
pack build my-dotnet-app --builder paketobuildpacks/builder-jammy-full

六、与 Dockerfile 的对比#

通过一个 Spring Boot 应用的实际案例来对比两种方式。

传统 Dockerfile 方式:

# Dockerfile
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY . .
RUN ./gradlew build -x test
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
docker build -t my-spring-app .
docker run -p 8080:8080 my-spring-app

这个 Dockerfile 看起来合理,但隐含几个问题:JVM 参数调优需要修改 Dockerfile;安全更新需要重新构建基础镜像;没有依赖缓存机制,每次都要下载依赖;COPY . . 会把不必要的文件带入构建上下文。

Paketo Buildpacks 方式:

# 一行命令完成构建
pack build my-spring-app \
--builder paketobuildpacks/builder-jammy-base \
--env BP_JVM_VERSION=17

Paketo 自动检测 Gradle/Maven、缓存依赖、配置 Spring Boot 启动参数、生成 SBOM。JVM 参数通过环境变量配置,不需要修改任何构建文件。

6.1 功能对比#

功能DockerfileCNB/Paketo
学习曲线中等
灵活性
最佳实践需手动实现内置
安全更新手动自动
缓存机制手动设计自动分层
多语言支持需为每种语言编写开箱即用
SBOM 支持需额外工具内置
调试能力直接进入容器专用工具
可移植性依赖 DockerOCI 兼容

6.2 何时选择哪种方式#

Dockerfile 的优势在于完全控制。当构建流程有特殊需求,自定义编译参数、非标准的依赖安装方式、需要操作构建环境的系统级配置,Dockerfile 可以精确控制每一步。CNB 的 buildpack 是对常见构建模式的抽象,它覆盖了 80% 的标准场景,但那 20% 的特殊需求可能找不到现成的 buildpack,需要自己编写或回退到 Dockerfile。

具体来说,选择 Dockerfile 的场景包括:需要极度定制化的构建流程、现有 buildpack 不支持的技术栈、需要复杂的构建参数控制、团队已熟悉 Dockerfile 最佳实践且构建质量可控。选择 CNB/Paketo 的场景包括:标准化的微服务架构、多语言项目统一构建、需要 SBOM 和供应链安全、开发团队不熟悉容器最佳实践、CI/CD 流水线需要简化。

Note

CNB 并非要取代 Dockerfile,而是提供了一种更高层的抽象。就像高级语言没有取代汇编,大部分场景用高级语言更高效,但需要精细控制时汇编仍然不可替代。

七、实战:完整构建流程#

下面通过一个 Node.js 应用示例,演示 CNB 的构建流程。

7.1 环境准备与示例应用#

# 安装 pack CLI
# macOS
brew install buildpacks/tap/pack
# Linux
curl -sSL https://github.com/buildpacks/pack/releases/download/v0.32.1/pack-v0.32.1-linux.tgz | tar -C /usr/local/bin --no-same-owner -xzv pack
# 验证安装
pack version
# 创建示例 Node.js 应用
mkdir my-node-app && cd my-node-app
# package.json
cat > package.json << 'EOF'
{
"name": "my-node-app",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
EOF
# server.js
cat > server.js << 'EOF'
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({ message: 'Hello from CNB!' });
});
app.listen(port, () => {
console.log(`App listening on port ${port}`);
});
EOF

7.2 执行构建与验证#

# 使用 Paketo builder 构建
pack build my-node-app \
--builder paketobuildpacks/builder-jammy-base
# 输出示例:
# ==> DETECTING
# 5 of 18 buildpacks participating
# paketo-buildpacks/ca-certificates 3.6.1
# paketo-buildpacks/node-engine 1.2.3
# paketo-buildpacks/npm-install 1.0.1
# paketo-buildpacks/node-start 1.0.1
#
# ==> ANALYZING
# Restoring 3 layers from cache
#
# ==> BUILDING
# Installing Node.js v18.17.0
# Installing npm dependencies
#
# ==> EXPORTING
# Adding layer 'paketo-buildpacks/node-engine'
# Adding layer 'paketo-buildpacks/npm-install'
# Writing config
# Adding label 'io.buildpacks.lifecycle.metadata'
# 运行构建好的镜像
docker run -p 3000:3000 my-node-app
# 测试应用
curl http://localhost:3000
# 输出: {"message":"Hello from CNB!"}
# 查看镜像信息
pack inspect my-node-app
# 查看 SBOM
pack sbom my-node-app

7.3 配置自定义参数#

CNB 通过环境变量配置构建行为,这是 buildpack 与 Dockerfile 的一个关键交互差异,Dockerfile 把配置硬编码在文件里,buildpack 把配置外置到环境变量中:

# 指定 Node.js 版本
pack build my-node-app \
--builder paketobuildpacks/builder-jammy-base \
--env BP_NODE_VERSION=20
# 设置构建时变量
pack build my-node-app \
--env NPM_CONFIG_PRODUCTION=true \
--env BP_NODE_PROJECT_PATH=./backend
# 配置运行时环境变量
pack build my-node-app \
--env BPE_MY_VAR=my-value

常用环境变量:

环境变量说明
BP_NODE_VERSION指定 Node.js 版本
BP_NODE_PROJECT_PATH指定项目路径
BP_JVM_VERSION指定 JVM 版本
BP_GO_VERSION指定 Go 版本
BPE_*运行时环境变量前缀
BP_LAUNCHPOINT指定启动入口

7.4 与 Kubernetes 集成#

在 Kubernetes 环境中,可以使用 Tektonkpack 来集成 CNB:

# kpack Image 资源示例
apiVersion: kpack.io/v1alpha2
kind: Image
metadata:
name: my-node-app
spec:
tag: registry.example.com/my-node-app
builder:
name: my-builder
kind: ClusterBuilder
source:
git:
url: https://github.com/myorg/my-node-app
revision: main

kpack 会自动监听代码仓库变化和 Builder 镜像更新,触发增量构建。这意味着当 Paketo 发布了修复安全漏洞的新版 Builder 时,kpack 会自动用新 Builder 重新构建所有应用,这正是 buildpack 集中管理安全策略的价值在 Kubernetes 上的体现。

八、最佳实践#

8.1 Builder 版本管理与缓存策略#

Builder 镜像应该固定版本,避免 latest 标签带来的不可预测性:

# 使用固定版本的 builder
pack build my-app \
--builder paketobuildpacks/builder-jammy-base:0.4.0
# 而不是 latest
pack build my-app \
--builder paketobuildpacks/builder-jammy-base:latest # 不推荐

缓存策略需要区分本地开发和 CI 环境。本地开发用卷缓存,CI 环境用镜像缓存,因为 CI 的构建节点通常是无状态的,卷缓存无法持久化:

# 启用卷缓存(本地开发)
pack build my-app \
--builder paketobuildpacks/builder-jammy-base \
--volume ~/.pack/cache:/cache
# CI 环境使用镜像缓存
pack build my-app \
--cache-image registry.example.com/cache:my-app

8.2 多环境配置与安全#

不同环境通过环境变量区分,而非维护多套构建文件:

# 开发环境
pack build my-app --env BP_NODE_VERSION=18 --env NODE_ENV=development
# 生产环境
pack build my-app \
--env BP_NODE_VERSION=18 \
--env NODE_ENV=production \
--env NPM_CONFIG_PRODUCTION=true

安全方面,优先选择 tiny 变体减小攻击面,并定期重建以应用安全补丁:

# 使用最小化 run image
pack build my-app \
--builder paketobuildpacks/builder-jammy-tiny
# 指定特定版本的 run image
pack build my-app \
--builder paketobuildpacks/builder-jammy-base \
--run-image paketobuildpacks/run-jammy-base:1.2.3

参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

基于 buildpacks 技术的云原生构建
https://blog.souloss.cn/posts/cloud-native-buildpacks/
作者
Souloss
发布于
2021-12-21
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时