= How to build a runtime using buildpack :icons: font :revdate: {docdate} :toc: left :toclevels: 2 ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: :important-caption: :heavy_exclamation_mark: :caution-caption: :fire: :warning-caption: :warning: endif::[]
https://github.com/redhat-buildpacks/testing/actions/workflows/quarkus.yaml[image:https://github.com/redhat-buildpacks/testing/actions/workflows/quarkus.yaml/badge.svg[]] https://github.com/redhat-buildpacks/testing/actions/workflows/pack.yaml[image:https://github.com/redhat-buildpacks/testing/actions/workflows/pack.yaml/badge.svg[]] https://github.com/redhat-buildpacks/testing/actions/workflows/tekton.yaml[image:https://github.com/redhat-buildpacks/testing/actions/workflows/tekton.yaml/badge.svg[]] https://github.com/redhat-buildpacks/testing/actions/workflows/shipwright.yaml[image:https://github.com/redhat-buildpacks/testing/actions/workflows/shipwright.yaml/badge.svg[]] https://github.com/redhat-buildpacks/testing/pulse[image:https://img.shields.io/github/commit-activity/m/redhat-buildpacks/testing[]]
== Introduction
The goal of this project is to test/validate different approaches to build a runtime using:
== Project Testing Matrix
The following table details the versions of the different tools/images used within the GitHub e2e workflows or examples of this project.
This project currently verifies the following scenario:
|=== | Tool/Image | Version | Tested | Note
Lifecycle | https://github.com/buildpacks/lifecycle/releases/tag/v0.20.1[0.20.1] | Yes |
---|
Platform | https://github.com/buildpacks/spec/blob/platform/0.13/platform.md[0.13.0] | Yes |
---|
Pack cli | https://github.com/buildpacks/pack/releases/tag/v0.35.1[v0.35.1] | Yes |
---|
| Java buildpack library | https://github.com/snowdrop/java-buildpack-client | Yes (indirectly using Quarkus container https://quarkus.io/guides/container-image#buildpack[build]) | Support platform 0.4 !
| Paketo Builder Tiny (jammy) builder (DEPRECATED but still needed for Quarkus <= 3.9) | https://github.com/paketo-buildpacks/builder-jammy-tiny/releases/tag/v0.0.203[0.0.203] | Yes | Package lifecycle 0.20.1
| Paketo Build base ubi image | https://github.com/paketo-community/ubi-base-stack/releases/tag/v0.0.77[0.0.77] | Yes | Base image to be used to build
| Paketo Run base ubi image | https://github.com/paketo-community/ubi-base-stack/releases/tag/v0.0.77[0.0.77] | Yes | Base image to be used to run a container
| Paketo community UBI builder | https://github.com/paketo-community/builder-ubi-base/releases/tag/v0.0.92[0.0.92] | Yes | Include Node.js, Quarkus Java buildpacks.
Paketo Node.js Extension for ubi | https://github.com/paketo-community/ubi-nodejs-extension/releases/tag/v0.3.4[0.3.4] | Yes |
---|
Paketo Java Extension for ubi | https://github.com/paketo-community/ubi-java-extension/releases/tag/v0.2.0[0.2.0] | Yes |
---|---|---|
=== |
== 0. Prerequisites
kind
cluster and an unsecure container registry using the following bash scripts:NOTE: Use the command +... | bash -s -h+
to see the usage
== 1. Quarkus Buildpacks
First, create a Quarkus Hello
example using the following maven command executed in a terminal.
Note: Edit the pom.xml file to change the JDK version from 21 to 17 <maven.compiler.release>17</maven.compiler.release>
, otherwise the buildpack build will fail
In a separate terminal, curl the HTTP endpoint
To build the container image, do the build using the ubi builder image paketocommunity/builder-ubi-base:0.0.92
and pass +BP_***+
env variables in order to configure properly the Quarkus buildpack build process:
Quarkus releases <= 3.9 can only build images for platform spec 0.4
and lifecycle 0.17
like this one: https://github.com/paketo-buildpacks/builder-jammy-tiny/releases/tag/v0.0.203.
The extension
feature, which allows to install OpenJDK instead of Liberica, is only available since platform 0.10
and lifecycle 0.18
.
Next, start the container and curl the endpoint
== 2. Pack client
To validate this scenario top of the existing quarkus-hello project, we will use the https://buildpacks.io/docs/tools/pack/[pack client].
Trick: You can discover the builder images available using the command pack builder suggest
;-)
Next, start the container and curl the endpoint +curl http://localhost:8080/hello+
== 3. Tekton
See the https://tekton.dev/[project documentation] for more information about how to install and use it.
To use Tekton, it is needed to have a k8s cluster (>= 1.24), a local docker registry
*.local
otherwise buildpacks lifecycle will report this error during the execution of the analyse
phase +failed to get previous image: connect to repo store 'kind-registry:5000/buildpack/app': Get "https://kind-registry:5000/v2/": http: server gave HTTP response to HTTPS client+
Next, install the latest official release (or a specific release)
and optionally, you can also install the dashboard
Expose the dashboard service externally using an ingress route and open the url in your browser: tekton-ui.127.0.0.1.nip.io
0/1 node error
during the execution of the task cloning the GitHub repository as described https://github.com/tektoncd/pipeline/issues/3545[here]
[,bash]When the platform is ready, you can install the needed Tasks
:
extension
mechanismkubectl delete -f https://raw.githubusercontent.com/redhat-buildpacks/catalog/main/tekton/task/buildpacks-phases/01/buildpacks-phases.yaml kubectl delete -f https://raw.githubusercontent.com/redhat-buildpacks/catalog/main/tekton/task/buildpacks-extension-phases/01/buildpacks-extension-phases.yaml
Create a dockercfg's secret using your registry credentials
REG_NAME="https://index.docker.io/v1/"
REG_USERNAME="
Set next the following variables:
It is time to create a Pipelinerun
to build the Quarkus application
IMAGE_NAME=kind-registry.local:5000/quarkus-hello
BUILDER_IMAGE=paketocommunity/builder-ubi-base:0.0.92 CNB_BUILD_IMAGE=paketocommunity/build-ubi-base:0.0.77 CNB_RUN_IMAGE=paketocommunity/run-ubi-base:0.0.77
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ws-pvc spec: accessModes:
apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: name: buildpacks-phases labels: app.kubernetes.io/description: "Buildpacks-PipelineRun" spec: params:
Follow the execution of the pipeline using the dashboard: http://tekton-ui.127.0.0.1.nip.io/#/namespaces/default/taskruns
, http://tekton-ui.127.0.0.1.nip.io/#/namespaces/default/pipelineruns
or using the client: tkn pipelinerun logs -f
When the pipeline is finished and no error is reported, then launch the container
== 4. Shipwright
See the project documentation for more information: https://github.com/shipwright-io/build
To use shipwright, it is needed to have a k8s cluster, local docker registry and tekton installed (>= v0.48.+)
Next, deploy the release 0.12
of shipwright
Next, install the Buildpacks BuildStrategy
using the following command:
Build
CR using as source the Quarkus Getting started repository:To view the Build which you just created:
Trigger a BuildRun
:
kubectl delete buildrun -lbuild.shipwright.io/name=buildpack-quarkus-build kubectl delete -f k8s/shipwright/unsecured/v1beta1/pvc.yml
Wait until your BuildRun is completed, and then you can view it as follows:
When the task is finished and no error is reported, then launch the container
=== Secured container registry (NOT MAINTAINED ANYMORE)
If you prefer to use a secure registry, then some additional steps are needed such as
Install a secured container registry
NOTE: To install a secured (HTTPS and authentication) docker registry, pass the parameter: --secure-registry
Generate a docker-registry secret
NOTE: This secret will be used by the serviceAccount of the build's pod to access the container registry
Create a serviceAccount that the platform will use to perform the build and able to be authenticated using the secret's credentials with the registry
Add the selfsigned certificate to a configMap. It will be mounted as a volume to set the env var SSL_CERT_DIR
used by the go-containerregistry lib (of lifecycle)
to access the registry using the HTTPS/TLS protocol.
Deploy the ClusterBuildStrategy
file from the secured folder as it includes a new volume to mount the certificate
apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: buildpacks spec: volumes:
=== All steps
Setup first the kind cluster and docker registry
NOTE: To install a secured (HTTPS and authentication) docker registry, pass the parameter: --secure-registry
Next, install Tekton and Shipwright
And finally, deploy the resources using either an unsecured
or secured
container registry
. Unsecured
Deploy the needed resources
DIR="unsecured" kubectl delete buildrun -lbuild.shipwright.io/name=buildpack-quarkus-build kubectl delete -f k8s/shipwright/${DIR}/v1beta1/build.yml kubectl delete -f k8s/shipwright/${DIR}/v1beta1/clusterbuildstrategy.yml kubectl delete -f k8s/shipwright/${DIR}/v1beta1/pvc.yml
. Secured (TO BE REVIEWED !!)
Deploy the needed resources
DIR="secured" kubectl create configmap certificate-registry \ --from-file=kind-registry.crt=./k8s/shipwright/${DIR}/binding/ca-certificates/kind-registry.local.crt
REGISTRY_HOST="kind-registry.local:5000" REGISTRY_USER=admin REGISTRY_PASSWORD=snowdrop kubectl create secret docker-registry registry-creds \ --docker-server="${REGISTRY_HOST}" \ --docker-username="${REGISTRY_USER}" \ --docker-password="${REGISTRY_PASSWORD}"
To clean up
== 5. RHTAP
This section is not maintained anymore.
=== Prerequisite
AppStudio
to your local ~/.kube/config file and been authenticated using https://docs.google.com/document/d/1hFvQDH1H6MGNqTGfcZpyl2h8OIaynP8sokZohCS0Su0/edit#heading=h.bksi3q7km0i[oidc login]AppStudio
GitHub application to your GitHub Org and select it to be used for all the repositories. More information is available https://pipelinesascode.com/docs/install/github_apps/[here].=== Env variables
In order to play/execute the commands defined hereafter, it is needed to define some env variables. Feel free to change them according to your GitHub organisation, tenant namespace, etc
GITHUB_ORG_NAME=halkyonio GITHUB_REPO_TEMPLATE=https://github.com/redhat-buildpacks/catalog.git GITHUB_REPO_DEMO_NAME=rhtap-buildpack-demo-1 GITHUB_REPO_DEMO_TITLE="RHTAP Buildpack Demo 1" BRANCH=main
APPLICATION_NAME=$GITHUB_REPO_DEMO_NAME COMPONENT_NAME="quarkus-hello"
DEVFILE_URL=https://raw.githubusercontent.com/devfile-samples/devfile-sample-code-with-quarkus/main/devfile.yaml
PAC_NAME=$COMPONENT_NAME PAC_YAML_FILE=".tekton/$GITHUB_REPO_DEMO_NAME-push.yaml" PAC_EVENT_TYPE="push" # Values could be "push, pull_request"
TENANT_NAMESPACE="
SOURCE_SUB_PATH="." CNB_LOG_LEVEL="debug" CNB_BUILDER_IMAGE="paketocommunity/builder-ubi-base:0.0.92" CNB_BUILD_IMAGE="paketocommunity/build-ubi-base:0.0.77" CNB_RUN_IMAGE="paketocommunity/run-ubi-base:0.0.77"
=== HowTo
To create a new GitHub repository and import the needed files, perform the following actions:
Git auth
gh auth login --with-token <YOUR_GITHUB_TOKEN>
Create a GitHub repository
gh repo delete $GITHUB_ORG_NAME/$GITHUB_REPO_DEMO_NAME --yes gh repo create \ --clone $GITHUB_ORG_NAME/$GITHUB_REPO_DEMO_NAME \ --public
mkdir .tekton curl -sOL https://raw.githubusercontent.com/redhat-buildpacks/catalog/main/tekton/pipelinerun/rhtap/pipelinerun-buildpacks-template.yaml mv pipelinerun-buildpacks-template.yaml .tekton/$GITHUB_REPO_DEMO_NAME-push.yaml
sed -i.bak "s/#GITHUB_ORG_NAME#/$GITHUB_ORG_NAME/g" $PAC_YAML_FILE sed -i.bak "s/#GITHUB_REPO_NAME#/$GITHUB_REPO_DEMO_NAME/g" $PAC_YAML_FILE sed -i.bak "s/#APPLICATION_NAME#/$APPLICATION_NAME/g" $PAC_YAML_FILE sed -i.bak "s/#COMPONENT_NAME#/$COMPONENT_NAME/g" $PAC_YAML_FILE sed -i.bak "s/#PAC_NAME#/$PAC_NAME/g" $PAC_YAML_FILE sed -i.bak "s/#TENANT_NAMESPACE#/$TENANT_NAMESPACE/g" $PAC_YAML_FILE sed -i.bak "s|#REGISTRY_URL#|$REGISTRY_URL|g" $PAC_YAML_FILE sed -i.bak "s|#BUILD_ID#|$BUILD_ID|g" $PAC_YAML_FILE sed -i.bak "s|#EVENT_TYPE#|$PAC_EVENT_TYPE|g" $PAC_YAML_FILE
sed -i.bak "s|#SOURCE_SUB_PATH#|$SOURCE_SUB_PATH|g" $PAC_YAML_FILE sed -i.bak "s|#CNB_LOG_LEVEL#|$CNB_LOG_LEVEL|g" $PAC_YAML_FILE sed -i.bak "s|#CNB_BUILDER_IMAGE#|$CNB_BUILDER_IMAGE|g" $PAC_YAML_FILE sed -i.bak "s|#CNB_BUILD_IMAGE#|$CNB_BUILD_IMAGE|g" $PAC_YAML_FILE sed -i.bak "s|#CNB_RUN_IMAGE#|$CNB_RUN_IMAGE|g" $PAC_YAML_FILE
# PAC_FILE_NAME="$GITHUB_REPO_DEMO_NAME-push" yq -o=json '.' .tekton/$PAC_FILE_NAME.yaml > .tekton/$PAC_FILE_NAME.json jq --argjson array "[$CNB_ENV_VARS]" '(.spec.params[] | select(.name=="cnbBuildEnvVars")).value |= $array' .tekton/$PAC_FILE_NAME.json > temp.json cat temp.json | yq -P > .tekton/$PAC_FILE_NAME.yaml
mv ./hello/* ./ mv ./hello/{.dockerignore,.gitignore} ./ mv ./hello/.mvn ./ rm -rf ./hello SSH_REPO_NAME=$(gh repo view https://github.com/$GITHUB_ORG_NAME/$GITHUB_REPO_DEMO_NAME --json sshUrl --jq .sshUrl) git remote set-url origin $SSH_REPO_NAME https://github.com/$GITHUB_ORG_NAME/$GITHUB_REPO_DEMO_NAME
Open the Component created using the RHTAP console and edit the build
to send a PR to your git project
Push a commit top of the GitHub repository created (change the build id from 0 -> &, etc), open the activity
tab of the RHTAP console and you should see that
a custom build has been started for pull and push :-)
Alternatively, Import it as documented here: https://redhat-appstudio.github.io/docs.appstudio.io/Documentation/main/how-to-guides/Import-code/proc_importing_code/
NOTE: Use one of the RHTAP bash scripts aiming to automate the whole process described : ./scripts/rhtap-demo{1,2,3}
=== Todo
=== Issue
==== Full image path not supported
The lifecycle component and most probably google container library (used by lifecycle to access the registry) do not support such advanced feature: https://kubernetes.io/docs/concepts/containers/images/#kubelet-credential-provider
The consequence is that if several secrets are attached to the appstudio-pipeline
service account and subsequently by the pod running lifecycle, then
lifecycle, at the analysis step, will raise an issue if it doesn't get as first entry of the auths:
config file (from mounted secrets) the full image path matching the image name declared
as output image.
To work around the issue of the full image path not supported by lifecycle (and google-containr), path the secret
CFG=$(cat <<EOF {"auths":{"quay.io":{"auth":"cmVkaG...aRkFGNTQ="}}} EOF )
SECRET_NAME=$COMPONENT_NAME TENANT_NAMESPACE="cmoullia-tenant" PATCH_STRING="[{'op': 'replace', 'path': '/data/.dockerconfigjson', 'value': '$BASE64_ENCODED_VALUE'}]"