Skip to content

Commit 64eb8a4

Browse files
committed
feat: add imagefactory docker compose example
1 parent 9cd1e1c commit 64eb8a4

File tree

5 files changed

+245
-0
lines changed

5 files changed

+245
-0
lines changed

examples/imagefactory/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.env
2+
keys/*
3+
schematics/*

examples/imagefactory/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Siderolabs self-hosted imagefactory example
2+
3+
This code runs [sidero imagefactory](https://github.com/siderolabs/image-factory) in [docker compose](https://docs.docker.com/compose/).
4+
5+
It also deploys a few companion components:
6+
* upstream `ghcr.io` [registry](https://distribution.github.io/distribution/) [mirror](https://distribution.github.io/distribution/recipes/mirror/) to avoid potential upstream rate limitings and speed up builds by caching previously pulled image layers
7+
* a script that applies prepared `talos` [image schematics](https://www.talos.dev/v1.8/learn-more/image-factory/#schematics)
8+
* a [registry](https://distribution.github.io/distribution/) used as storage and cache for the generated images
9+
10+
# how to use
11+
12+
tested with `talos` version `v1.8.1`, via [iPXE boot](https://www.talos.dev/v1.8/talos-guides/install/bare-metal-platforms/pxe/) and directly applying generated images to disk via `hcloud` (use it's [packer](https://developer.hashicorp.com/packer/integrations/hetznercloud/hcloud) integration and point [this](https://github.com/siderolabs/contrib/blob/9cd1e1c9d2469b77d2278eb07e7f61c09bb32d40/examples/terraform/hcloud/packer/hcloud_talosimage.pkr.hcl#L18) URL to your `imagefactory` instance).
13+
14+
## preparation
15+
16+
some preparation is required.
17+
18+
### signing keys
19+
see [official docs](https://github.com/siderolabs/image-factory?tab=readme-ov-file#development).
20+
21+
```shell
22+
mkdir -pv keys
23+
openssl ecparam -name prime256v1 -genkey -noout -out keys/cache-signing-key.key
24+
```
25+
26+
### schematics
27+
28+
Refer to the [official docs](https://www.talos.dev/v1.8/learn-more/image-factory/#schematics) on how to create these.
29+
The [script](./scripts/sync-schematics.sh) will find and apply all files in `schematics/*.yaml`.
30+
31+
Example:
32+
```yaml
33+
# schematics/example.yaml
34+
customization:
35+
extraKernelArgs:
36+
- gfxmode=1280x1024
37+
- console=ttyS0,115200
38+
- net.ifnames=0
39+
- talos.platform=metal
40+
systemExtensions:
41+
officialExtensions:
42+
- siderolabs/amd-ucode
43+
- siderolabs/fuse3
44+
- siderolabs/intel-ucode
45+
- siderolabs/iscsi-tools
46+
- siderolabs/qemu-guest-agent
47+
- siderolabs/tailscale
48+
- siderolabs/util-linux-tools
49+
meta:
50+
- key: 12
51+
value: |
52+
machineLabels:
53+
env: prod
54+
type: controlplane
55+
```
56+
57+
### environment variables
58+
59+
copy the [docker compose env file](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#env-file) and adjust the example values.
60+
61+
```shell
62+
cp env.example .env
63+
vim .env
64+
```
65+
66+
Adjust all domains and `EXT_IP` to where you want to expose your `imagefactory` instance.
67+
This is relevant for payloads sent to `iPXE` clients and URLs generated in the UI.
68+
69+
## run
70+
71+
after preparation is done, run `docker compose up -d`.
72+
73+
# miscellaneous & troubleshooting
74+
75+
This is a community contribution so expect no official support.
76+
Some trouble I ran into:
77+
78+
## TLS
79+
80+
The configuration used does *not* deploy TLS, so you should put this behind something like a reverse proxy that does.
81+
82+
## connection timeouts
83+
84+
Image generation can take some time, so clients might have to increase their connection timeout limits. If images are cached, [TTFB](https://en.wikipedia.org/wiki/Time_to_first_byte) is very short, if not `TTFB` can take up to several minutes.
85+
86+
## iPXE and https
87+
88+
If you want to [iPXE](https://ipxe.org)-boot from this via `https`, keep in mind that by default `iPXE` does *not* support `https` and you need to compile your own, enabling [this](https://ipxe.org/buildcfg/download_proto_https) flag. This is a pitfall for reverse proxies that automatically redirect plaintext `http` requests to `https`.
89+
90+
## URLs not working
91+
92+
There is a tiny problem in the `imagefactory` frontend: The URLs generated contain the external domain used and it is duplicated for some reason. This is particularly mean because the URL _visible_ in the UI looks correct, but the `HTML` `href` is not.
93+
Make sure to sanitize the URL before use, the resulting URL works as expected. Not sure yet as to _why_ this happens (misconfiguration or might be a bug).
94+
Querying the `API` seems to return the correct URL.
95+
96+
## registry resource consumption
97+
98+
* When building a large number of images, make sure to provide sufficient storage to the `registry` container and monitor `docker volumes` as it grows in size quite rapidly.
99+
* the image generation process is compute heavy and can take some time, depending on the compute power available.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
services:
2+
# generated images are pushed to this registry
3+
registry:
4+
image: registry:2
5+
ports:
6+
- ${EXTERNAL_IP:-127.0.0.1}:5000:5000
7+
volumes:
8+
# hint: when generating a large number of different schemas, this volume can grow quite large
9+
- registry:/var/lib/registry:rw
10+
# upstream ghcr mirror, caches previously pulled image layers and prevents rate limiting
11+
registry-ghcr:
12+
image: registry:2
13+
environment:
14+
REGISTRY_PROXY_REMOTEURL: http://ghcr.io
15+
volumes:
16+
- registry-ghcr:/var/lib/registry:rw
17+
# triggers image builds for all schematics defined in `./schematics/*.yaml`
18+
schematics:
19+
image: alpine:3
20+
environment:
21+
IMAGE_FACTORY_URL: http://imagefactory:6000
22+
REGISTRY_URL: registry:5000
23+
TALOS_VERSION: ${SCHEMATICS_TALOS_VERSION?}
24+
ARCH: ${SCHEMATICS_TALOS_ARCH?}
25+
VALIDATE: ${SCHEMATICS_VALIDATE?}
26+
SLEEP_TIME: ${SCHEMATICS_SLEEP_TIME?}
27+
command: >
28+
/scripts/sync-schematics.sh
29+
volumes:
30+
- ${PWD}/scripts:/scripts:ro
31+
- ${PWD}/schematics:/schematics:ro
32+
# container running the actual imagefactory
33+
imagefactory:
34+
image: ghcr.io/siderolabs/image-factory:${IMGFAC_VERSION?}
35+
# required for losetup
36+
privileged: true
37+
volumes:
38+
- ${PWD}/keys:/keys:ro
39+
# required for losetup
40+
- /dev:/dev
41+
ports:
42+
- ${EXTERNAL_IP:-127.0.0.1}:6000:6000
43+
command: >
44+
-http-port=:6000
45+
-external-url=${IMGFAC_EXTERNAL_URL?}
46+
-external-pxe-url=http://${IMGFAC_EXTERNAL_URL?}
47+
-cache-signing-key-path=/keys/cache-signing-key.key
48+
-cache-repository=registry:5000/cache
49+
-insecure-cache-repository=true
50+
-image-registry=registry-ghcr:5000
51+
-insecure-image-registry=true
52+
-schematic-service-repository=registry:5000/image-factory/schematic
53+
-insecure-schematic-service-repository
54+
-installer-internal-repository=registry:5000/siderolabs
55+
-insecure-installer-internal-repository=true
56+
-installer-external-repository=${IMGFAC_EXT_REPO?}/siderolabs
57+
-secureboot=${IMGFAC_SECUREBOOT?}
58+
59+
volumes:
60+
registry:
61+
registry-tls:
62+
registry-ghcr:

examples/imagefactory/env.example

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
EXTERNAL_IP=127.0.0.1
2+
3+
SCHEMATICS_TALOS_VERSION=1.8.1
4+
SCHEMATICS_TALOS_ARCH=amd64
5+
SCHEMATICS_VALIDATE=false
6+
SCHEMATICS_SLEEP_TIME=600
7+
8+
IMGFAC_VERSION=v0.5.0
9+
IMGFAC_EXTERNAL_URL=imgfac.example.com
10+
IMGFAC_EXT_REPO=reg.imgfac.example.com
11+
IMGFAC_SECUREBOOT=false
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/bin/ash
2+
set -eo pipefail
3+
4+
trap "exit 0" SIGINT SIGTERM
5+
6+
: ${IMAGE_FACTORY_URL:?}
7+
: ${REGISTRY_URL:?}
8+
: ${TALOS_VERSION:?}
9+
: ${ARCH:?}
10+
: ${VALIDATE:?}
11+
: ${SLEEP_TIME:?}
12+
13+
apk add crane yq
14+
15+
RESULTS_FILE="${RESULTS_FILE:-/tmp/results}"
16+
while true; do
17+
echo '' > "${RESULTS_FILE}"
18+
for SCHEMATIC in /schematics/*.yaml ; do
19+
# this triggers image generation based on the schema provided
20+
# docs: https://github.com/siderolabs/image-factory?tab=readme-ov-file#post-schematics
21+
echo "apply ${SCHEMATIC}"
22+
RESPONSE_FILE=/tmp/wget-response.json
23+
wget \
24+
--header 'Content-Type: application/yaml' \
25+
-O "${RESPONSE_FILE}" \
26+
--post-file=${SCHEMATIC} \
27+
${IMAGE_FACTORY_URL}/schematics \
28+
29+
# parse the image ID from the response
30+
SCHEMA_ID=$(yq .id < "${RESPONSE_FILE}")
31+
if test -z "${SCHEMA_ID}" ; then
32+
echo 'SCHEMA_ID was empty'
33+
exit 1
34+
fi
35+
TMP_FILE="/tmp/${SCHEMA_ID}.tar"
36+
rm "${RESPONSE_FILE}"
37+
38+
# docs: https://github.com/siderolabs/image-factory?tab=readme-ov-file#get-imageschematicversionpath
39+
echo 'download container'
40+
wget \
41+
-O ${TMP_FILE} \
42+
${IMAGE_FACTORY_URL}/image/${SCHEMA_ID}/${TALOS_VERSION}/installer-${ARCH}.tar
43+
44+
# optional: this calls `crane validate <image>`, validating the generated image is well formed
45+
# docs: https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane_validate.md
46+
if [ "${VALIDATE}" == 'true' ] ; then
47+
echo 'validate container'
48+
crane validate --tarball ${TMP_FILE}
49+
fi
50+
51+
echo 'publish container'
52+
crane push \
53+
--insecure \
54+
${TMP_FILE} \
55+
${REGISTRY_URL}/installer/${SCHEMA_ID}:${TALOS_VERSION}
56+
57+
rm -v ${TMP_FILE}
58+
echo "${SCHEMATIC} ${SCHEMA_ID}" >> "${RESULTS_FILE}"
59+
done
60+
61+
# this prints the image IDs resulting from each schema,
62+
# which can then be handed out to clients.
63+
echo "--- results ---"
64+
cat "${RESULTS_FILE}"
65+
echo "---------------"
66+
67+
echo "all done, sleep ${SLEEP_TIME} sec."
68+
sleep ${SLEEP_TIME} &
69+
wait $!
70+
done

0 commit comments

Comments
 (0)