In this tutorial, we will learn how to create a Go package with Melange, build a Docker with Apko image, and showcase examples using GitLab CI.
Prerequisites
Before we begin, make sure you have the following tools installed on your system:
Keyfile Generation
To use Melange and Apko, we need to create a private and public key pair to sign our artifacts:
melange keygen
You should now see a melange.rsa
and melange.rsa.pub
file in your directory. These files should not be committed and added to the .gitignore
file just like the /packages
.
This directory will be created ones we run melange to build our Go Application.
Additionally create a sbom directory with a .gitkeep
file and add sbom/sbom-*.*
and image.tar
to the .gitignore
file as they will become relevant once we create the Docker image.
Creating a Go Package with Melange
Initialize a Go module:
go mod init gitlab.com/your-username/golang-apko-example
Please replace your-username
with your actual GitLab username in the go mod init
command.
Create your Go source code files and write your package logic.
Create a melange.yml
file in the root of your package directory. This file will define the build configuration for your package:
package:
name: golang-apko-example
version: 0.1.0
epoch: 0
description: Build a golang application with melange
copyright:
- license: MIT
target-architecture:
- x86_64
environment:
contents:
repositories:
- https://packages.wolfi.dev/os
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
pipeline:
- uses: go/build
with:
modroot: .
packages: .
output: golang-apko-example
- uses: strip
Customize the name
, version
and description
fields according to your package's requirements. Also, make sure you check the name of the output.
Build your package locally with Melange:
melange build --signing-key melange.rsa --runner docker melange.yml
This will generate an APKINDEX.json
, APKINDEX.tar.gz
and .apk
file in your packages directory.
Building a Docker Image with apko.yml
Create an apko.yml
file in the root of your package directory. This file will define the Docker image build configuration:
contents:
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
repositories:
- https://packages.wolfi.dev/os
packages:
- wolfi-base
- ca-certificates-bundle
- golang-apko-example@local
accounts:
groups:
- groupname: nonroot
gid: 65532
users:
- username: nonroot
uid: 65532
gid: 65532
run-as: 65532
archs:
- amd64
cmd: /usr/bin/golang-apko-example
The important line is the golang-apko-example@local
package signaling a local package should be used instead of the wolfi packages.
Build the Image with your local repository appended:
apko build --debug --sbom-path ./sbom/ --repository-append $(pwd)/packages --keyring-append=melange.rsa.pub apko.yml golang-apko-example:latest image.tar
This will build a Docker image as an image.tar with our local package and create SBOM files for our image.
To actually use the image locally we have to load and execute it:
docker load --input image.tar
docker run -it golang-apko-example:latest-amd64
Using Gitlab CI
Environment Variables
With Gitlab CI we can automate this process and make it even better.
However, before we can begin we have to save the melange.rsa
and melange.rsa.pub
as Gitlab CI Environment Files so we can use them. To do this simply go to Settings
--> CI/CD
--> Variables
and Add variable
of the Type File
our two files:
- MELANGE_RSA: File content of
melange.rsa
- MELANGE_PUB: File content of
melange.rsa.pub
You might want to store your certificates in a safer location then Gitlab Environments but lets just stick with this for now.
Gitlab CI Preparation
Now we can create a .gitlab-ci.yml
file in the root of your GitLab repository.
At first we should define some general structure:
stages:
- build
- containerize
variables:
APKO_FILE: "apko.yml"
MELANGE_FILE: "melange.yml"
FULL_IMAGE_NAME: $CI_REGISTRY_IMAGE:latest
Just like in our CLI we have 2 Steps to complete:
- Build the Golang Application
- Build the Docker image
Currently we simply create a Docker image without any versioning: The name of our image will be the name of the repository name including the path and using latest as the tag.
Building the Application with Melange
We can use the same command to build the application in Gitlab CI as we did locally. We just have to make sure that our melange.rsa key exists:
build_package:
stage: build
image:
name: cgr.dev/chainguard/melange:latest
entrypoint: [""]
before_script:
- melange version
- cat ${MELANGE_RSA} > melange.rsa
script:
- melange build --signing-key melange.rsa "${MELANGE_FILE}"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule"
artifacts:
paths:
- ./packages
Since the default entrypoint of melange is melange itself we have to overwrite to entrypoint so that we can use it in Gitlab CI.
After that we make sure that our key exists and can simply build our application.
The only thing left to do is store the packages
directory as an artifact which can use in the next step.
Publishing the Docker Image with Apko
We now need to build and publish the Docker Image to the Gitlab Registry. In this step we will need the public key instead of the private key. In addition we can use the predefined Registry Variables to log into the Gitlab Registry and publish the image:
containerize_package:
stage: containerize
image: registry.gitlab.com/stammkneipe.dev/apko-ci:latest
variables:
CI_BUILDS_DIR: $CI_BUILDS_DIR
before_script:
- apko version
- apko login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
- cat ${MELANGE_PUB} > melange.rsa.pub
script:
- apko publish --debug --sbom-path $CI_PROJECT_DIR/sbom/ --repository-append $(pwd)/packages --keyring-append melange.rsa.pub "${APKO_FILE}" "${FULL_IMAGE_NAME}"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule"
artifacts:
paths:
- "sbom/*"
This will result in a Docker Image directly instead of a tar file. You should be able to see your docker image in the Gitlab Registry (/container_registry/) with information on how to use it.
In addition we can see the SBOM files in the artifacts of the pipeline.
Conclusion
See the full example here.
Top comments (0)