-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jericop/initial-commit
Initial commit
- Loading branch information
Showing
8 changed files
with
360 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
name: Build and push to local registry | ||
on: pull_request | ||
jobs: | ||
build: | ||
runs-on: ubuntu-22.04 | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: docker/setup-qemu-action@v3 | ||
- uses: docker/setup-buildx-action@v3 | ||
with: | ||
platforms: linux/amd64,linux/arm64 | ||
- uses: buildpacks/github-actions/[email protected] | ||
- uses: buildpacks/github-actions/[email protected] | ||
- id: local-registry-push | ||
run: | | ||
./pack-with-buildkit.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Build and push to ghcr.io | ||
on: | ||
push: | ||
branches: | ||
- "main" | ||
env: | ||
PUBLISH_TO_GHCR_IO_IMAGE_URI: ghcr.io/jericop/inline-app:latest | ||
jobs: | ||
build: | ||
runs-on: ubuntu-22.04 | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: docker/setup-qemu-action@v3 | ||
- uses: docker/setup-buildx-action@v3 | ||
with: | ||
platforms: linux/amd64,linux/arm64 | ||
- uses: buildpacks/github-actions/[email protected] | ||
- uses: buildpacks/github-actions/[email protected] | ||
- uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GHCR_TOKEN }} | ||
- id: ghcr-push | ||
run: | | ||
./pack-with-buildkit.sh | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
cnb-build-files | ||
Dockerfile | ||
lifecycle-commands.log | ||
pack-build.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Pin the lifecycle version so we can be sure of the behavior if we run this script in the future | ||
FROM chainguard/crane:latest as crane | ||
FROM buildpacksio/lifecycle:0.16.3 as lifecycle | ||
FROM ghcr.io/jericop/builder-jammy:latest | ||
USER root | ||
|
||
COPY --from=crane /usr/bin/crane /usr/local/bin/crane | ||
COPY --from=lifecycle /cnb/lifecycle /cnb/lifecycle | ||
COPY ./cnb-build-files/cnb/buildpacks /cnb/buildpacks | ||
COPY ./cnb-build-files/layers /layers | ||
|
||
RUN mkdir -p /cache | ||
RUN chown -R cnb:cnb /workspace /cache | ||
RUN chown -R cnb:cnb /layers /platform | ||
RUN find /layers | ||
|
||
USER cnb | ||
|
||
ENV CNB_PLATFORM_API=0.9 | ||
|
||
COPY ./ /workspace | ||
|
||
WORKDIR /workspace | ||
|
||
# Add RUN command(s) below here to run the lifecycle commands and build and export the app to a registry | ||
# It also fixes the architecture because it always gets set to amd64 when running in buildkit. | ||
|
||
RUN <<RUN_EOF | ||
|
||
export image_arch=amd64 | ||
if [ $(arch) = "aarch64" ]; then | ||
export image_arch=arm64 | ||
fi | ||
|
||
export image_uri="localhost:55000/inline-app:$image_arch" | ||
|
||
echo "#!/bin/bash" > /workspace/lifecycle-build.sh | ||
cat <<IN_BUILDKIT_LIFECYCLE_SCRIPT_EOF >> /workspace/lifecycle-build.sh | ||
|
||
set -euo pipefail | ||
set -x | ||
|
||
/cnb/lifecycle/analyzer -log-level debug -stack /layers/stack.toml -run-image ghcr.io/jericop/run-jammy:latest localhost:55000/inline-app:$image_arch | ||
/cnb/lifecycle/detector -app /workspace -log-level debug | ||
/cnb/lifecycle/restorer -cache-dir /cache -log-level debug | ||
/cnb/lifecycle/builder -app /workspace -log-level debug | ||
/cnb/lifecycle/exporter -log-level debug -app /workspace -cache-dir /cache -stack /layers/stack.toml localhost:55000/inline-app:$image_arch | ||
|
||
crane pull $image_uri image.tar | ||
|
||
mkdir -p contents | ||
tar -xvf image.tar -C contents | ||
cd contents | ||
ls | ||
cat manifest.json | jq -r '.[0].Config' | ||
current_config=\$(cat manifest.json | jq -r '.[0].Config') | ||
echo \$current_config | ||
cat \$current_config | jq -c ".architecture = \"\${image_arch}\" | .os = \"linux\"" | tr -d '\n' > newconfig.json | ||
new_config="sha256:$(sha256sum newconfig.json | cut -d' ' -f1)" | ||
rm -f "\${current_config}" | ||
mv newconfig.json "\${new_config}" | ||
cat manifest.json| jq -c ".[0].Config = \"\${new_config}\"" | tr -d '\n' > newmanifest.json | ||
mv -f newmanifest.json manifest.json | ||
tar -cvf ../fixed-arch-image.tar * | ||
cd .. | ||
|
||
crane push fixed-arch-image.tar $image_uri | ||
|
||
rm -rf image.tar fixed-arch-image.tar contents | ||
|
||
IN_BUILDKIT_LIFECYCLE_SCRIPT_EOF | ||
|
||
# Run the lifecycle script and ensure correct architecture is set | ||
chmod +x /workspace/lifecycle-build.sh | ||
/workspace/lifecycle-build.sh | ||
|
||
RUN_EOF | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Pin the lifecycle version so we can be sure of the behavior if we run this script in the future | ||
FROM chainguard/crane:latest as crane | ||
FROM buildpacksio/lifecycle:0.16.3 as lifecycle | ||
FROM ghcr.io/jericop/builder-jammy:latest | ||
USER root | ||
|
||
COPY --from=crane /usr/bin/crane /usr/local/bin/crane | ||
COPY --from=lifecycle /cnb/lifecycle /cnb/lifecycle | ||
COPY ./cnb-build-files/cnb/buildpacks /cnb/buildpacks | ||
COPY ./cnb-build-files/layers /layers | ||
|
||
RUN mkdir -p /cache | ||
RUN chown -R cnb:cnb /workspace /cache | ||
RUN chown -R cnb:cnb /layers /platform | ||
RUN find /layers | ||
|
||
USER cnb | ||
|
||
ENV CNB_PLATFORM_API=0.9 | ||
|
||
COPY ./ /workspace | ||
|
||
WORKDIR /workspace | ||
|
||
# Add RUN command(s) below here to run the lifecycle commands and build and export the app to a registry | ||
# It also fixes the architecture because it always gets set to amd64 when running in buildkit. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,35 @@ | ||
# cnb-lfx-buildkit-poc | ||
# cnb-lfx-buildkit-poc | ||
|
||
This repo is an initial proof-of-concept for the following LFX mentorship project. | ||
|
||
[CNCF - Cloud Native Buildpacks: Proof of concept making multiarch images with buildkit (2024 Term 1)](https://mentorship.lfx.linuxfoundation.org/project/2c5ced86-d23b-41f5-aec3-59730e29f092) | ||
|
||
It is heavliy inspired by the following article. While the article is invaluable for understanding how the lifecycle works, it requires a fair amount of manual steps, which must be repeated if any changes are made to buildpacks. For the sake of simplicity, I am using an inline buildpack and running `pack build` once in order to copy out the necessary files. | ||
|
||
* https://medium.com/buildpacks/unpacking-cloud-native-buildpacks-ff51b5a767bf | ||
|
||
## Primary objective | ||
|
||
The primary objective is to create a Dockerfile that has all of the lifecycle commands needed to build and push architecture-specific images to a registry. `Dockerfile-example` is an example of how it works. You can then combine the architecture-specific images into a manifest list (multi-arch) image. | ||
|
||
## `pack-with-buildkit.sh` | ||
|
||
This script will do the following: | ||
|
||
* Sets up a buildkit builder with `host` network access | ||
* Starts a local registry on a random port | ||
* Run `pack build` using the `project.toml` which has an inline buildpack that copies the files we need to /workspace | ||
* Copies the files we need out of the container built with pack so we can use them in a new build (with buildkit) | ||
* Generates a `Dockerfile` from `Dockerfile-initial` that will look like `Dockerfile-example` | ||
* Runs `docker buildx build --platform linux/amd64,linux/arm64` to execute the lifecycle commands through buildkit | ||
* This builds and publishes images to the local registry with `amd64` and `arm64` tags | ||
* There is some hackery that happens to fix the architecture (for now) | ||
* Finally it creates a manifest list with the `amd64` and `arm64` tagged images | ||
|
||
## Requirements | ||
|
||
* Multi-arch builders are required. I'm using ones that I build and publish. | ||
|
||
## Published image(s) | ||
|
||
* ghcr.io/jericop/inline-app:latest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -euo pipefail | ||
|
||
repo_name="mybuildpack" | ||
registry_name="local-registry" | ||
builder_name="${registry_name}" | ||
|
||
cleanup() { | ||
docker buildx stop "${builder_name}" > /dev/null 2>&1 | ||
docker buildx rm "${builder_name}" > /dev/null 2>&1 | ||
|
||
log_message "Stopping local registry" | ||
docker stop "${registry_name}" > /dev/null 2>&1 | ||
} | ||
|
||
log_message() { | ||
echo "$1" | ||
} | ||
|
||
# https://github.com/docker/buildx/issues/166#issuecomment-1804970076 | ||
# First, we need to create our own buildx builder that uses the host nework and docker-container driver so that we can get multiarch support | ||
log_message "Setting up docker buildx builder" | ||
docker buildx use "${builder_name}" > /dev/null 2>&1 || docker buildx create --name "${builder_name}" --driver docker-container --driver-opt network=host --bootstrap --use > /dev/null 2>&1 | ||
|
||
# Next, we need to check if a registry is already running | ||
log_message "Setting up a local registry on random port" | ||
docker container inspect "${registry_name}" > /dev/null 2>&1 || docker run -d -e REGISTRY_STORAGE_DELETE_ENABLED=true -p 0:5000 --rm --name "${registry_name}" registry:2 > /dev/null 2>&1 | ||
|
||
# Get local registry port since we asked docker to assign a random port by choosing port 0 | ||
registry_port=$(docker inspect "${registry_name}" | jq -r '.[0].NetworkSettings.Ports["5000/tcp"][0].HostPort') | ||
log_message "Local registry is listening on port ${registry_port}" | ||
|
||
# Rather than creating the necessary files manually, we let pack generate and the inline buildpack | ||
# copy them to /layers/build-files we publish to a local registry so we can capture | ||
pack build localhost:$registry_port/inline-app \ | ||
--publish --verbose --network host 2>&1 | tee pack-build.log \ | ||
| grep -B3 -A4 "Args: '/cnb/lifecycle" > lifecycle-commands.log | ||
|
||
docker pull localhost:$registry_port/inline-app | ||
|
||
if [ -d cnb-build-files ]; then | ||
rm -rf cnb-build-files | ||
fi | ||
mkdir -p cnb-build-files | ||
|
||
# Copy the files created by pack during the build from the volume and into the local directory | ||
docker run --rm --entrypoint bash --user root \ | ||
--volume $(pwd)/cnb-build-files:/hostmnt \ | ||
localhost:$registry_port/inline-app \ | ||
-c 'cp -R /workspace/build-files/* /hostmnt/' | ||
|
||
# We copy the initial Dockerfile (without lifecycle commands) to Dockerfile | ||
cp Dockerfile-initial Dockerfile | ||
|
||
# Then we add a run command (heredoc) with the lifecycle commands to build and push the architecture-specific images | ||
cat <<ADD_RUN_COMMAND_TO_DOCKERFILE_EOF >> Dockerfile | ||
RUN <<RUN_EOF | ||
export image_arch=amd64 | ||
if [ \$(arch) = "aarch64" ]; then | ||
export image_arch=arm64 | ||
fi | ||
export image_uri="localhost:$registry_port/inline-app:\$image_arch" | ||
echo "#!/bin/bash" > /workspace/lifecycle-build.sh | ||
cat <<IN_BUILDKIT_LIFECYCLE_SCRIPT_EOF >> /workspace/lifecycle-build.sh | ||
set -euo pipefail | ||
set -x | ||
ADD_RUN_COMMAND_TO_DOCKERFILE_EOF | ||
|
||
# Add the lifecycle commands from the saved log output of `pack build` we ran above | ||
while read cmd; do | ||
if echo $cmd | grep -q "localhost:$registry_port/inline-app\$"; then | ||
echo "$cmd:\$image_arch" >> Dockerfile | ||
else | ||
echo "$cmd" >> Dockerfile | ||
fi | ||
done < <(grep Args: lifecycle-commands.log | cut -d"'" -f2) | ||
|
||
cat <<'CONTINUE_ADD_RUN_COMMAND_TO_DOCKERFILE_EOF' >> Dockerfile | ||
crane pull $image_uri image.tar | ||
mkdir -p contents | ||
tar -xvf image.tar -C contents | ||
cd contents | ||
ls | ||
cat manifest.json | jq -r '.[0].Config' | ||
current_config=\$(cat manifest.json | jq -r '.[0].Config') | ||
echo \$current_config | ||
cat \$current_config | jq -c ".architecture = \"\${image_arch}\" | .os = \"linux\"" | tr -d '\n' > newconfig.json | ||
new_config="sha256:$(sha256sum newconfig.json | cut -d' ' -f1)" | ||
rm -f "\${current_config}" | ||
mv newconfig.json "\${new_config}" | ||
cat manifest.json| jq -c ".[0].Config = \"\${new_config}\"" | tr -d '\n' > newmanifest.json | ||
mv -f newmanifest.json manifest.json | ||
tar -cvf ../fixed-arch-image.tar * | ||
cd .. | ||
crane push fixed-arch-image.tar $image_uri | ||
rm -rf image.tar fixed-arch-image.tar contents | ||
IN_BUILDKIT_LIFECYCLE_SCRIPT_EOF | ||
# Run the lifecycle script and ensure correct architecture is set | ||
chmod +x /workspace/lifecycle-build.sh | ||
/workspace/lifecycle-build.sh | ||
RUN_EOF | ||
CONTINUE_ADD_RUN_COMMAND_TO_DOCKERFILE_EOF | ||
|
||
# After all of that, we are finally ready to build the multi-arch images using the lifecycle with buildkit. | ||
# We are not publishing with this command because the lifecyle will publish the images to the local registry for us. | ||
docker buildx build --tag ignored --platform linux/amd64,linux/arm64 . | ||
|
||
# Now we can combine the linux/amd64 and linux/arm64 images into a manifest list and push it to the local registry | ||
docker buildx imagetools create \ | ||
--tag localhost:$registry_port/inline-app:latest \ | ||
localhost:$registry_port/inline-app:amd64 \ | ||
localhost:$registry_port/inline-app:arm64 | ||
|
||
crane manifest localhost:$registry_port/inline-app:latest | jq | ||
|
||
if [[ ! -z "${PUBLISH_TO_GHCR_IO_IMAGE_URI:-}" ]]; then | ||
crane copy localhost:$registry_port/inline-app:latest ghcr.io/jericop/inline-app:latest | ||
log_message "Published image to: $PUBLISH_TO_GHCR_IO_IMAGE_URI" | ||
fi | ||
|
||
# This image can then be copied to another registry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
[project] | ||
name = "inline-app" | ||
|
||
# stack images need to be multi-arch for this to work | ||
[build] | ||
builder = "ghcr.io/jericop/builder-jammy:latest" | ||
|
||
[[build.buildpacks]] | ||
id = "inline/shell-buildpack" | ||
|
||
[build.buildpacks.script] | ||
api = "0.8" | ||
inline = """ | ||
set -x | ||
# sleep 300 | ||
build_files=/workspace/build-files | ||
mkdir -p $build_files/cnb | ||
mkdir -p $build_files/layers | ||
ls /cnb/*.toml | ||
cp -r /cnb/*.toml $build_files/cnb/ | ||
cp -r /cnb/buildpacks $build_files/cnb/ | ||
cp /cnb/order.toml $build_files/layers/ | ||
cp /layers/*.toml $build_files/layers/ | ||
find $build_files | ||
cat <<EOF > ${1}/launch.toml | ||
[[processes]] | ||
type = 'web' | ||
command = 'bin/bash' | ||
default = true | ||
EOF | ||
""" |