apko and melange are 2 interesting tools coming from chainguard.dev aiming for declarative builds of alpine packages and containers based on the apk ecosystem.
melange
is for building apk packages, generating, and signing repository indexes.
It consists of: package metadata,
packages to be installed in the build environment,
and a linear pipeline of steps,
using either reusable stages declared in yaml on disk,
or inline scripts.
A config for building a Go application might look like:
1# package metadata
2package:
3 name: my-package
4 description: some description
5 version: 0.0.1
6 epoch: 0
7 target-architecture:
8 - all
9 copyright:
10 - paths:
11 - "*"
12 license: MIT
13 # attestation
14 # dependencies:
15 # runtime:
16 # provides:
17 # scriptlets:
18 # pre-install:
19 # post-install:
20 # ...
21
22# build environment
23environment:
24 contents:
25 repositories:
26 - https://dl-cdn.alpinelinux.org/alpine/edge/main
27 - https://dl-cdn.alpinelinux.org/alpine/edge/community
28 packages:
29 # always necessary
30 - alpine-baselayout-data
31 # i think things break without this
32 - busybox
33 # connect to the outside world, eg GOPROXY
34 - ca-certificates-bundle
35 # buildvcs stamping
36 - git
37 # build tool
38 - go
39
40pipeline:
41 # melange comes with some basic declarative pipelines
42 # (someone else wrote the script in yaml for you)
43 # but you can always just include a shell script
44 - runs: |
45 mkdir -p "${{targets.destdir}}"
46 go build -trimpath -ldflags='-s -w' -o "${{targets.destdir}}/usr/bin/" .
melange
relies on some alpine tools,
on other systems docker can be used to run it with something like:
1$ docker run --privileged -v $(PWD):/work distroless.dev/melange:latest \
2 build \
3 --arch x86_64 \
4 --signing-key build/melange.rsa \
5 --out-dir /work/build/packages \
6 melange.yaml
/work
is the preferred workspace, used with defaults--privileged
is necessary for bubblewrap
to do some sort of containing--sigining-key
should be used to sign the index if used with apko
(only accepts signed repos)apko
composes packages into container images.
It appears that any complex config will need to be done by building a package
and including that.
1contents:
2 # repositories to pull packages from
3 # NOTE: these need to be signed
4 repositories:
5 - https://dl-cdn.alpinelinux.org/alpine/edge/main
6 - https://dl-cdn.alpinelinux.org/alpine/edge/community
7 # syntax for local repo
8 - "@local /work/build/packages"
9 packages:
10 - alpine-baselayout
11 - weechat
12 # pull from local repo
13 - example-package@local
14
15# dockerfile ENTRYPOINT
16entrypoint:
17 command: "fly-weechat"
18
19# run as non root
20accounts:
21 run-as: 10000
22 users:
23 - username: user
24 uid: 10000
25 groups:
26 - groupname: user
27 gid: 10000
28
29# setup a home for our user
30environment:
31 HOME: /home/user
32
33paths:
34 - path: /home/user
35 type: directory
36 uid: 10000
37 gid: 10000
38 permissions: 0o755
This also has a dependency on alpine tools,
so here comes docker again.
Alternatively, use publish
to push directly to a repository.
1$ docker run -v $(PWD):/work distroless.dev/apko:latest \
2 build \
3 --keyring-append build/melange.rsa.pub \
4 apko.yaml \
5 ${IMAGE_NAME}:${IMAGE_REF} \
6 build/image.tar
apko
only accepts signed repositories, otherwise the error is not very helpfulWhile they are declarative,
unlike ko which inspired it,
it's not quite there in terms of no runtime config (flags) needed.
So we're back to writing a Makefile
to remember all the commands.
1IMAGE_NAME := "my.registry.example/img"
2IMAGE_REF := "latest"
3IMAGE_OUT := "image.tar"
4
5SRC := $(shell find . -name '*.go')
6
7$(SRC) go.mod go.sum melange.yaml apko.yaml:
8
9gen-keys: build/melange.rsa
10build/melange.rsa:
11 mkdir -p build/
12 docker run --privileged -v $(PWD):/work distroless.dev/melange:latest \
13 keygen build/melange.rsa
14
15build-apk: build/packages/x86_64/APKINDEX.tar.gz
16build/packages/x86_64/APKINDEX.tar.gz: gen-keys $(SRC) go.mod go.sum melange.yaml
17 docker run --privileged -v $(PWD):/work distroless.dev/melange:latest \
18 build \
19 --arch x86_64 \
20 --signing-key build/melange.rsa \
21 --out-dir /work/build/packages \
22 melange.yaml
23
24build-img: build/image.tar
25build/image.tar: build-apk apko.yaml
26 docker run -v $(PWD):/work distroless.dev/apko:latest \
27 build \
28 --keyring-append build/melange.rsa.pub \
29 apko.yaml \
30 ${IMAGE_NAME}:${IMAGE_REF} \
31 build/image.tar
32
33load-img: build-img
34 docker load -i build/image.tar
35
36.PHONY: run-img
37run-img: load-img
38 docker run --rm -it ${IMAGE_NAME}:${IMAGE_REF}