diff --git a/text/0000-multiarch-builders-and-package.md b/text/0000-multiarch-builders-and-package.md new file mode 100644 index 000000000..979bcf94d --- /dev/null +++ b/text/0000-multiarch-builders-and-package.md @@ -0,0 +1,579 @@ +# Meta +[meta]: #meta +- Name: Multi-arch support for builders and buildpack packages +- Start Date: 2023-09-14 +- Author(s): @jjbustamante +- Status: Draft +- RFC Pull Request: (leave blank) +- CNB Pull Request: (leave blank) +- CNB Issue: (leave blank) +- Supersedes: (put "N/A" unless this replaces an existing RFC, then link to that RFC) + +# Summary +[summary]: #summary + +The problem for adding support for multi-arch buildpacks can be divided into three parts: +1. Support buildpack authors to **migrate their existing buildpacks** to support multi-arch. +2. Support buildpack authors to **create new buildpacks and builders** that handle multi-arch from the beginning. +3. Support application developers to **create application images** using multi-arch buildpacks and builders. + +The purpose of this RFC is to solve the statement 2, adding the capability to the commands: + +- `pack buildpack package` +- `pack builder create` + +to create individuals OCI images artifacts per each os and arch (builders and buildpack packages) and handle the creation for the [image index,](https://github.com/opencontainers/image-spec/blob/master/image-index.md) +that combines them into one single consumable tag for end-users. + +# Definitions +[definitions]: #definitions + +- Buildpack: A buildpack is a set of executables that inspects your app source code and creates a plan to build and run your application. +- Builder: A builder is an image that contains all the components necessary to execute a build. A builder image is created by taking a build image and adding a lifecycle, buildpacks, and files that configure aspects of the build including the buildpack detection order and the location(s) of the run image +- Image Manifest: The image manifest provides a configuration and set of layers for a single container image for a specific architecture and operating system. See [spec](https://github.com/opencontainers/image-spec/blob/main/manifest.md) +- Image Index: The image index is a higher-level manifest which points to specific image manifests, ideal for one or more platforms. See [spec](https://github.com/opencontainers/image-spec/blob/main/image-index.md) + +# Motivation +[motivation]: #motivation + +- Why should we do this? + +The uses of ARM architecture in the cloud and edge computing has been growing rapidly. The CNCF community has been also growing in the last years, and there is a need to support multi-arch for all the projects. The buildpacks community is not an exception, issues like: +- [It would be nice to support easily creating a manifest list packages and builders](https://github.com/buildpacks/pack/issues/1460) +- [Provide a way to specify desired platform when creating packages and builders](https://github.com/buildpacks/pack/issues/1459) +- [Multi arch image build support](https://github.com/buildpacks/pack/issues/1570) + +Or the conversations around this topic in our [slack channel](https://cloud-native.slack.com/archives/C032LNSMY0P), even the [talk at Kubecon NA 2022](https://www.youtube.com/watch?v=Sdr5axlOnDI&list=PLj6h78yzYM2O5aNpRM71NQyx3WUe1xpTn&index=76) demonstrate the interest from the community in this feature. + +- What use cases does it support? + +Currently, buildpack authors can build and package their buildpacks for different OS and Architectures, but when they distribute them the URI for a buildpack can’t disambiguate, +they need to use different tags to differentiate between them. Tools like `docker buildx imagetools create` helps to create an [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) to combine them but increasing their build pipelines complexity. +For example, take a look at this recent blog [post](https://deploy-preview-53--elegant-borg-5bd068.netlify.app/blog/steps-we-took-for-a-basic-arm64-support-in-buildpacks) or the [instructions](https://github.com/dmikusa/paketo-arm64/) created from @dmikusa to build an ARM64 builder. + +For those buildpack authors that are using `cross-compile` languages like [go](https://go.dev/) or [rust](https://www.rust-lang.org/) or maybe bash scripts, adding the capability to `pack buildpack package` and `pack builder create` to create multi-arch images +and also handle the creation of an [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) will simplify their CI/CD pipelines and make the experience more suitable. + +- What is the expected outcome? + +`pack buildpack package` and `pack builder create` commands will be updated in a way that they will handle the creation of multi-arch OCI images + +# What it is +[what-it-is]: #what-it-is + +The end-users for this proposal are **Buildpack authors**, we expect to improve their user experience when creating multi-architecture buildpacks and builders as follows + +## Multi-arch example + +Let's suppose a **Buildpack author** has a `buildpack.toml` created with a content similar to this one: + +```toml +# Buildpack API version +api = "0.12" + +# Buildpack ID and metadata +[buildpack] + id = "examples/my-multiarch-buildpack" + version = "0.0.1" + +# List of targets operating systems, architectures and versions + +[[targets]] +os = "linux" +arch = "amd64" + +[[targets.distributions]] +name = "" + +[[targets]] +os = "linux" +arch = "arm64" + +[[targets.distributions]] +name = "" + +[[targets]] +os = "windows" +arch = "amd64" + +[[targets.distributions]] +name = "" +versions = ["10.0.20348.1970"] +``` + +And organizes the binaries according to their os/arch with a structure similar to this one: + +```bash +my-multiarch-buildpack +├── buildpack.toml +├── linux-amd64 +│ └── bin +│ ├── build +│ └── detect +├── linux-arm64 +│ └── bin +│ ├── build +│ └── detect +└── windows-amd64 + └── 10.0.20348.1970 + └── bin + ├── build.bat + └── detect.bat +``` + +Now `pack` will be able to package them separately for each os/arch family, following our [guide](https://buildpacks.io/docs/buildpack-author-guide/package-a-buildpack/) +we will need to create a `package.toml` file. + +The `package.toml` file will be similar to the one describe in our documentation: + +```toml +[buildpack] +uri = "examples/my-multiarch-buildpack" +# OR a .tgz with the previous folder structure +uri = "my-multiarch-buildpack.tgz" +``` + +But in this case, we actually **remove** the [platform](https://buildpacks.io/docs/reference/config/package-config/) section because it will be taken from the `buildpack.toml`. + +Packaging a multi-arch buildpack will require the output to be **publish** to a registry or **saved on disk** in OCI layout format. + +```bash +pack buildpack package my-buildpack --config ./package.toml --publish +# Or +pack buildpack package my-buildpack.cnb --config ./package.toml --format file +``` + +In these cases each `target` entry corresponds to a different buildpack image that is exported into an [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) + +On the other hand, when a user exports the buildpack package into the docker daemon + +```bash +pack buildpack package my-buildpack --config ./package.toml +``` + +`pack` will not be able to create the [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md), and it will export the buildpack that corresponds to the `host` machine `os/arch` + +In case of packing a **Builder**, we assume the buildpack authors migrated their `builder.toml` and [remove the stack](https://github.com/buildpacks/pack/issues/1303) concept. + +A sample `builder.toml` file looks like: + +```toml +# Buildpacks to include in builder, these buildpacks MUST be multi-arch and point to Image Index +[[buildpacks]] +uri = "" + +[run] +# Runtime images - in case of multi-arch images it must point to Image Index +[[run.images]] +image = "index.docker.io/paketobuildpacks/run-jammy-tiny:latest" + +[build] +# This image is used at build-time, in case of multi-arch images it must point to Image Index +image = "docker.io/paketobuildpacks/build-jammy-tiny:0.2.3" +``` + +As we can see, the proposal is based on the assumption that the `run-image`, `build-image` and `buildpacks` to include in the builder are multi-arch artifacts, and we can reach them by reading an [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) + +#### Package a multi-arch builder and publish it to a registry + +```bash +pack builder create my-jammy-builder --config ./builder.toml \ + --platform "linux/amd64" \ + --platform "linux/arm64" \ + --publish +``` + +In this case: + +`pack` will follow the builder creation process for **each provided platform**, pulling the correct (based on os/arch) buildpacks, build and run images and creating a different builder image that is exported into an [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) + +## More examples + +For each example, let's suppose a `package.toml` exits and `pack buildpack package` command is invoke, we will show the expected output for each case. + +### Example 1 + +folder structure: + +```bash +├── bin +│ ├── build +│ └── detect +├── buildpack.toml +└── package.toml +``` + +`buildpack.toml`: + +```toml +[[targets]] +os = "linux" +``` + +Or + +```toml +[[targets]] +os = "linux" +arch = "amd64" +``` + +Output: +- **One** OCI image will be created with the provides `os/arch` +- No [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) will be created, only an [image manifest](https://github.com/opencontainers/image-spec/blob/main/manifest.md) + +### Example 2 + +folder structure: + +```bash +├── bin +│ ├── build +│ └── detect +├── buildpack.toml +└── package.toml +``` + +`buildpack.toml`: + +```toml +[[targets]] +os = "linux" +arch = "amd64" + +[[targets]] +os = "linux" +arch = "arm64" +``` + +Output: + +- **Two** OCI images will be created, **with the same content**, but their [configuration](https://github.com/opencontainers/image-spec/blob/main/config.md#properties) file will have different **architecture** +- An [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) will be created + +### Example 3 + +folder structure: + +```bash +├── buildpack.toml +├── linux-amd64 +│ └── bin +│ ├── build +│ └── detect +├── linux-arm64 +│ └── bin +│ ├── build +│ └── detect +└── package.toml +``` + +`buildpack.toml`: + +```toml +[[targets]] +os = "linux" +arch = "amd64" + +[[targets]] +os = "linux" +arch = "arm64" +``` + +Output: + +- **Two** OCI images will be created, **with content** taken according to the `os/arch` +- An [image index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) will be created + +# How it Works +[how-it-works]: #how-it-works + + + +## Buildpack Package + +### Current `package.toml` structure + +The current schema for the [package.toml](https://buildpacks.io/docs/reference/config/package-config/) file + +```toml +[buildpack] +uri = "" + +[extension] +uri = "" + +[[dependencies]] +uri = "" +image = "" + +[platform] +os = "[linux|windows]" +``` + +```bash +buildpack +├── bin +│ ├── build +│ └── detect +└── buildpack.toml +``` + +### New structure `package.toml` structure + +The proposal is to remove the `[platform]` section because that information can be taken from the `[targets]` in `buildpack.toml` + +```toml +[buildpack] +uri = "" + +[extension] +uri = "" + +[[dependencies]] +uri = "" +image = "" + +``` + +#### buildpack.toml + +Based on the [RFC-0096](https://github.com/buildpacks/rfcs/blob/main/text/0096-remove-stacks-mixins.md) we replaced the concept of `stacks` in `buildpack.toml` by a `target` which looks like: + +```toml +[[targets]] +os = "" +arch = "" +variant = "" +[[targets.distributions]] +name = "" +versions = [""] +``` + +The proposal is to use this information to infer a folder structure like the following: + +```bash +. +├── buildpack.toml +└── {os}-{arch} // optional + └── {variant} // optional + ├── {version-1} // optional + │ └── bin + │ ├── build + │ └── detect + └── {version-2} + └── bin + ├── build + └── detect +``` + +To determine where are the binaries and files that must exported to final OCI images according to their `os/arch` + +Preserving the following defaults: +- If the `targets` list is empty and `/bin/build` is present + - A target with `os = "linux"` and `arch = "amd64"` is assumed by tools reading `buildpack.toml`. +- If the `targets` list is empty and `/bin/build.bat` or `/bin/build.exe` is present + - A target with `os = "windows"` and `arch = "amd64"` is assumed by tools reading `buildpack.toml`. + +## Builder + + +# Migration +[migration]: #migration + + + +# Drawbacks +[drawbacks]: #drawbacks + + + +# Alternatives +[alternatives]: #alternatives + + + +# Prior Art +[prior-art]: #prior-art + +- This RFC is a continuation of the work started with the proposal to add commands to handle manifest list in pack, see the [RFC](https://github.com/buildpacks/rfcs/pull/283) +- Paketo [RFC #288](https://github.com/paketo-buildpacks/rfcs/pull/288) to publish multi-arch buildpacks + +# Unresolved Questions +[unresolved-questions]: #unresolved-questions + +> **Note** +> These are some questions I need to answer +> +> What are the intermediate images for each platform named/called? What happens if you don't put --append here? (from Terence) +> +> What happen if my binaries are the same for every os/arch, do I have to copy all the files? +> +> What happen if I want to exclude some buildpack for a particular target? +> +> What happen if I want to include the same file or folder for every image, do I have to copy then inside the {os}-{arch} folder? + + + + +# Spec. Changes (OPTIONAL) +[spec-changes]: #spec-changes + + + +# History +[history]: #history + + + + + + + + + + + + +