Add Spindle container image build #12

open
opened by psychedeli.ca targeting main from psychedeli.ca/knot-docker: spindle

This PR adds tngl/spindle as a new image, using the same Dockerfile as the Knot server but in an additional stage of the build. By building the images from the same Dockerfile, we can re-use the clone from a single tag and cache more artifacts between builds. It also introduces a docker bake workflow and a docker-bake.hcl file to describe images built off the master branch and given the :edge tag (which is an emerging convention in container tag nomenclature).

The build succeeded, but I got a bunch of warnings about inconsistent case (my editor was yelling at me when it encountered lowercase Dockerfile instructions):

 24 warnings found (use docker --debug to expand):
 - ConsistentInstructionCasing: Command 'CMD' should match the case of the command majority (lowercase) (line 66)
 - ConsistentInstructionCasing: Command 'FROM' should match the case of the command majority (lowercase) (line 10)
 - ConsistentInstructionCasing: Command 'FROM' should match the case of the command majority (lowercase) (line 13)
 - ConsistentInstructionCasing: Command 'ARG' should match the case of the command majority (lowercase) (line 57)
 - ConsistentInstructionCasing: Command 'COPY' should match the case of the command majority (lowercase) (line 63)
 - FromAsCasing: 'AS' and 'from' keywords' casing do not match (line 16)
 - ConsistentInstructionCasing: Command 'RUN' should match the case of the command majority (lowercase) (line 11)
 - ConsistentInstructionCasing: Command 'LABEL' should match the case of the command majority (lowercase) (line 49)
 - ConsistentInstructionCasing: Command 'LABEL' should match the case of the command majority (lowercase) (line 52)
 - ConsistentInstructionCasing: Command 'LABEL' should match the case of the command majority (lowercase) (line 53)
 - ConsistentInstructionCasing: Command 'RUN' should match the case of the command majority (lowercase) (line 61)
 - ConsistentInstructionCasing: Command 'WORKDIR' should match the case of the command majority (lowercase) (line 65)
 - ConsistentInstructionCasing: Command 'RUN' should match the case of the command majority (lowercase) (line 14)
 - ConsistentInstructionCasing: Command 'FROM' should match the case of the command majority (lowercase) (line 46)
 - ConsistentInstructionCasing: Command 'EXPOSE' should match the case of the command majority (lowercase) (line 47)
 - ConsistentInstructionCasing: Command 'LABEL' should match the case of the command majority (lowercase) (line 50)
 - ConsistentInstructionCasing: Command 'ARG' should match the case of the command majority (lowercase) (line 56)
 - ConsistentInstructionCasing: Command 'RUN' should match the case of the command majority (lowercase) (line 59)
 - ConsistentInstructionCasing: Command 'VOLUME' should match the case of the command majority (lowercase) (line 67)
 - ConsistentInstructionCasing: Command 'HEALTHCHECK' should match the case of the command majority (lowercase) (line 68)
 - ConsistentInstructionCasing: Command 'ENV' should match the case of the command majority (lowercase) (line 17)
 - ConsistentInstructionCasing: Command 'LABEL' should match the case of the command majority (lowercase) (line 51)
 - ConsistentInstructionCasing: Command 'LABEL' should match the case of the command majority (lowercase) (line 54)
 - ConsistentInstructionCasing: Command 'RUN' should match the case of the command majority (lowercase) (line 60)

...so I was wondering if I could commit this other change that formats the Dockerfile properly? But I figured I'd let y'all see the changes first before they're obfuscated by all that reformatting.

+35 -6
Dockerfile
···
from golang:1.24-alpine as builder
-
env KNOT_REPO_SCAN_PATH=/home/git/repositories
env CGO_ENABLED=1
-
arg TAG='v1.10.0-alpha'
+
arg TAG='v1.11.0-alpha'
workdir /app
run apk add git gcc musl-dev
run git clone -b ${TAG} https://tangled.org/@tangled.org/core .
-
run go build -o /usr/bin/knot -ldflags '-s -w -extldflags "-static"' ./cmd/knot
+
+
FROM builder AS build-knot
+
RUN go build -o /usr/bin/knot -ldflags '-s -w -extldflags "-static"' ./cmd/knot
+
+
FROM builder AS build-spindle
+
RUN go build -o /usr/bin/spindle ./cmd/spindle
-
from alpine:edge
+
from alpine:edge AS knot
+
ENV KNOT_REPO_SCAN_PATH=/home/git/repositories
expose 5555
expose 22
···
run useradd -u $UID -g $GID -d /home/git git
run openssl rand -hex 16 | passwd --stdin git
run mkdir -p /home/git/repositories && chown -R git:git /home/git
-
copy --from=builder /usr/bin/knot /usr/bin
+
copy --from=build-knot /usr/bin/knot /usr/bin
run mkdir /app && chown -R git:git /app
healthcheck --interval=60s --timeout=30s --start-period=5s --retries=3 \
cmd curl -f http://localhost:5555 || exit 1
+
entrypoint ["/init"]
-
entrypoint ["/init"]
+
FROM alpine:edge AS spindle
+
EXPOSE 6555
+
+
LABEL org.opencontainers.image.title="spindle"
+
LABEL org.opencontainers.image.description="ci server for tangled"
+
LABEL org.opencontainers.image.source="https://tangled.org/@tangled.org/knot-docker"
+
LABEL org.opencontainers.image.url="https://tangled.org"
+
LABEL org.opencontainers.image.vendor="tangled.org"
+
LABEL org.opencontainers.image.licenses="MIT"
+
+
ARG UID=1000
+
ARG GID=1000
+
+
RUN adduser --system --uid $UID spindle
+
RUN addgroup --system --gid $UID spindle
+
RUN mkdir -p /app && chown -R spindle:spindle /app
+
+
COPY --from=build-spindle /usr/bin/spindle /usr/bin
+
+
WORKDIR /app
+
CMD ["spindle"]
+
VOLUME ["/app"]
+
HEALTHCHECK --interval=60s --timeout=30s --start-period=5s --retries=3 \
+
CMD curl -f http://localhost:6555 || exit 1
+49
docker-bake.hcl
···
+
variable "UID" {
+
default = "1000"
+
}
+
+
variable "GID" {
+
default = "1000"
+
}
+
+
group "edge" {
+
targets = ["knot-edge", "spindle-edge"]
+
}
+
+
target "knot-edge" {
+
context = "."
+
target = "knot"
+
args = {
+
TAG = "master"
+
UID = UID
+
GID = GID
+
}
+
tags = ["tngl/knot:edge"]
+
}
+
+
target "spindle-edge" {
+
context = "."
+
target = "spindle"
+
args = {
+
TAG = "master"
+
UID = UID
+
GID = GID
+
}
+
tags = ["tngl/spindle:edge"]
+
}
+
+
target "all" {
+
name = "${APP}-${replace(TAG, ".", "-")}"
+
context = "."
+
matrix = {
+
APP = ["knot", "spindle"]
+
TAG = ["v1.11.0-alpha", "v1.10.0-alpha", "v1.9.0-alpha", "v1.8.0-alpha"]
+
}
+
target = "${APP}"
+
args = {
+
TAG = "${TAG}"
+
UID = UID
+
GID = GID
+
}
+
tags = ["tngl/${APP}:${TAG}"]
+
}
+38 -10
docker-compose.yml
···
+
name: tangled
services:
knot:
+
image: tngl/knot:latest
build:
-
context: .
-
args:
-
UID: 1000
-
GID: 1000
+
target: knot
+
args: &args
+
UID: ${UID:-1000}
+
GID: ${GID:-1000}
+
TAG: ${TAG:-v1.11.0-alpha}
+
tags:
+
- tngl/spindle:latest
+
- tngl/spindle:${TAG:-v1.11.0-alpha}
environment:
KNOT_SERVER_HOSTNAME: ${KNOT_SERVER_HOSTNAME}
KNOT_SERVER_OWNER: ${KNOT_SERVER_OWNER}
···
- "5555:5555"
- "2222:22"
restart: always
+
spindle:
+
image: tngl/spindle:latest
+
build:
+
target: spindle
+
args: *args
+
tags:
+
- tngl/spindle:latest
+
- tngl/spindle:${TAG:-v1.11.0-alpha}
+
environment:
+
SPINDLE_SERVER_HOSTNAME: ${SPINDLE_SERVER_HOSTNAME}
+
SPINDLE_SERVER_OWNER: ${KNOT_SERVER_OWNER}
+
volumes:
+
- ./logs:/var/log/spindle
+
- ./spindle:/app
+
ports:
+
- "6555:6555"
frontend:
image: caddy:alpine
-
command: >
-
caddy
-
reverse-proxy
-
--from ${KNOT_SERVER_HOSTNAME}
-
--to knot:5555
depends_on:
-
- knot
+
knot:
+
condition: service_healthy
+
spindle:
+
condition: service_healthy
+
configs:
+
- source: caddyfile
+
target: /etc/caddy/Caddyfile
ports:
- ${KNOT_SERVER_PORT:-443}:443
- ${KNOT_SERVER_PORT:-443}:443/udp
···
- ./caddy_data:/data
restart: always
profiles: ["caddy"]
+
configs:
+
caddyfile:
+
content: |
+
${KNOT_SERVER_HOSTNAME} { reverse_proxy knot:5555 }
+
${SPINDLE_SERVER_HOSTNAME} { reverse_proxy spindle:6555 }
+55 -16
readme.md
···
> This is a community maintained repository, support is not guaranteed.
Docker container and compose setup to run a [Tangled](https://tangled.org) knot
-
and host your own repository data.
+
and spindle, hosting your own repository data and CI.
## Pre-built Images
-
There is a [repository](https://hub.docker.com/r/tngl/knot) of pre-built images
+
There is a [repository](https://hub.docker.com/r/tngl) of pre-built images
for tags starting at `v1.8.0-alpha` if you prefer.
```
docker pull tngl/knot:v1.10.0-alpha
+
docker pull tngl/spindle:v1.10.0-alpha
```
Note that these are *not* official images, you use them at your own risk.
-
## Building The Image
+
## Building The Images
-
By default the `Dockerfile` will build the latest tag, but you can change it
-
with the `TAG` build argument.
+
Both the knot and spindle images are built using the same `Dockerfile`, since
+
they're sourced from the same codebase and can therefore share a lot of the
+
build steps (such as `go mod download`), caching results between them. You
+
can build the images locally by running `docker bake`:
```sh
-
docker build -t knot:latest --build-arg TAG=master .
+
docker bake
```
-
The command above for example will build the latest commit on the `master`
-
branch.
+
Optionally, choose a target image to build:
+
+
```sh
+
docker bake knot
+
docker bake spindle
+
```
+
+
By default this will build the latest tag, but you can target a specific tag
+
like so:
+
+
```sh
+
docker bake knot-v1-10-0-alpha spindle-v1-10-0-alpha
+
```
+
+
The command above for example will build the `v1.10.0-alpha` tag for both the
+
`knot` and `spindle`. They're expressed as individual bake targets, so you can
+
also optionally specify just one to build at a specific version.
+
+
You can also build all tagged releases:
+
+
```sh
+
docker bake all
+
```
-
By default it will also create a `git` user with user and group ID 1000:1000,
+
By default it will also create a `git` / `spindle` user with user and group ID 1000:1000,
but you can change it with the `UID` and `GID` build arguments.
```sh
-
docker build -t knot:latest --build-arg UID=$(id -u) GID=$(id -g)
+
docker bake UID=$(id -u) GID=$(id -g)
```
The command above for example will create a user with the host user's UID and GID.
This is useful if you are bind mounting the repositories and app folder on the host,
as in the provided `docker-compose.yml` file.
+
You can also build the latest commit on `master` for both services by using
+
the `-edge` targets. These will build to a tag named `:edge` to distinguish
+
it from the `:latest` release:
+
+
```sh
+
docker bake edge
+
#
+
# or, with a specific target image...
+
#
+
docker bake edge-knot
+
docker bake edge-spindle
+
```
+
<hr style="margin-bottom: 20px; margin-top: 10px" />
When using compose, these can be specified as build arguments which will be
···
## Setting Up The Image
-
The simplest way to set up your own knot is to use the provided compose file
-
and run the following:
+
The simplest way to set up your own knot and spindle is to use the provided
+
compose file and run the following:
```sh
-
export KNOT_SERVER_HOSTNAME=example.com
-
export KNOT_SERVER_OWNER=did:plc:yourdidgoeshere
-
export KNOT_SERVER_PORT=443
-
docker compose up -d
+
export KNOT_SERVER_HOSTNAME="knot.example.com"
+
export SPINDLE_SERVER_HOSTNAME="spindle.example.com"
+
export KNOT_SERVER_OWNER="did:plc:yourdidgoeshere"
+
export KNOT_SERVER_PORT="443"
+
+
docker compose up --detach
```
This will setup everything for you including a reverse proxy.