DEV Community

Cover image for Building a Go Package with Melange and a Docker Image with Apko
Patrick Domnick
Patrick Domnick

Posted on

Building a Go Package with Melange and a Docker Image with Apko

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/*"
Enter fullscreen mode Exit fullscreen mode

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)