Getting started with AppCat 2.0

The framework behind AppCat 2.0 is called Helmetica. It’s a framework that helps with deploying multiple instances of the same Helm chart with ease!

Prepare dev environment (Athanor)

Please check Athanor. Once it’s running, you can continue with this getting started guide.

Using an upstream Helm chart (prima materia)

The easiest way to get started is to take an existing chart and deploy it.

An already existing chart is called a prima materia in the Helmetica framework. It’s the first material, the source of truth.

To make a chart available via the Helmetica framework, it needs three manifests.

cat <<EOF | kubectl apply -f -
apiVersion: helmetica.io/v1
kind: CustomResourceDefinitionSource
metadata:
  name: v0.1.redis
  namespace: hel-chrysopoeia
spec:
  crdNames:
    kind: Redis
    plural: redis
  reference:
    apiVersion: source.toolkit.fluxcd.io/v1
    kind: OCIRepository
    name: redis-v0-1
  versionDiscovery:
    reference:
      apiVersion: image.toolkit.fluxcd.io/v1
      kind: ImageRepository
      name: redis-v0-1
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
  name: redis-v0-1
  namespace: hel-chrysopoeia
spec:
  exclusionList:
  - ^.*\.sig$
  - ^sha256-.+$
  image: ghcr.io/helmetica-framework/redis-ha
  interval: 12h
  provider: generic
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
  name: redis-v0-1
  namespace: hel-chrysopoeia
spec:
  interval: 12h
  provider: generic
  ref:
    semver: "4.39.1"
  timeout: 60s
  url: oci://ghcr.io/helmetica-framework/redis-ha
EOF

Then you can deploy an instance.

cat <<EOF | kubectl apply -f -
apiVersion: v0.1.redis.helmetica-bundles.io/bundle
kind: Redis
metadata:
  name: redis
  namespace: default
spec:
  approval:
    strategy: Automatic
  version: 4.39.1
  values:
    replicas: 1
EOF

After a few minutes, the instance should be ready.

kubectl get redis -w

Wrapped chart (reagent)

Deploying already existing charts is nice to get something going quickly. However, Helmetica provides a template for a wrapper chart, called ferment, which provides additional features to help with day-2 operations:

  • Easy way to add operations

  • Backups via K8up

  • Ingress and network policies

  • And more

Ferment is only the scaffold. Wrapping a specific prima materia in it produces a reagent: the actual chart for one service, which is what you build, push and ship.

To turn a prima materia into a reagent, you can use a CLI tool called transmuter. Once inside the devcontainer cd into /workspaces/athanor/reagents.

transmuter transmute \
--name redis \
--prima-materia-url oci://ghcr.io/helmetica-framework/redis-ha \
--prima-materia-version 4.39.1

This creates a redis/ directory in your current working directory, containing the reagent chart. All following commands are run from inside it.

cd redis

The generated values.yaml only contains the backup retention so far. Add the following lines to it:

service:
  replicas: 1

The prima materia is pulled in as a chart dependency with the alias service, see Chart.yaml. That’s why its values are nested under the service key here, while the upstream chart itself expects them at the top level.

Let’s build and push it.

helm dependency build .
helm package .
helm push redis-0.0.1.tgz oci://localhost:5000 --insecure-skip-tls-verify

And the definitions.

cat <<EOF | kubectl apply -f -
apiVersion: helmetica.io/v1
kind: CustomResourceDefinitionSource
metadata:
  name: v0.1.newredis
  namespace: hel-chrysopoeia
spec:
  crdNames:
    kind: NewRedis
    plural: newredis
  reference:
    apiVersion: source.toolkit.fluxcd.io/v1
    kind: OCIRepository
    name: newredis-v0-1
  versionDiscovery:
    reference:
      apiVersion: image.toolkit.fluxcd.io/v1
      kind: ImageRepository
      name: newredis-v0-1
---
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
  name: newredis-v0-1
  namespace: hel-chrysopoeia
spec:
  exclusionList:
  - ^.*\.sig$
  - ^sha256-.+$
  image: registry.kube-system:5000/redis
  interval: 12h
  provider: generic
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
  name: newredis-v0-1
  namespace: hel-chrysopoeia
spec:
  interval: 12h
  provider: generic
  ref:
    semver: "0.0.1"
  timeout: 60s
  url: oci://registry.kube-system:5000/redis
EOF

The path /redis comes from the chart name, which transmuter took from --name. The kind and plural below are deliberately different from it, to show that they’re your choice and don’t have to repeat the chart name.

Finally, a claim.

cat <<EOF | kubectl apply -f -
apiVersion: v0.1.newredis.helmetica-bundles.io/bundle
kind: NewRedis
metadata:
  name: newredis
  namespace: default
spec:
  approval:
    strategy: Automatic
  version: 0.0.1
  values: {}
EOF

Again, watch the instance until it’s ready.

kubectl get newredis -w

This takes a few minutes.

Note that the values set in the reagent chart are now applied by default. The instance does not have any values defined in the spec, so it will only deploy a single replica.

kubectl -n $(kubectl get newredis newredis -o jsonpath='{.status.instanceNamespace}') get statefulset

Cleaning up

Delete the instances first. Their instance namespace, and everything in it, goes away with them.

kubectl delete redis redis
kubectl delete newredis newredis

Then the definitions.

kubectl -n hel-chrysopoeia delete customresourcedefinitionsource v0.1.redis v0.1.newredis
kubectl -n hel-chrysopoeia delete imagerepository.image.toolkit.fluxcd.io redis-v0-1 newredis-v0-1
kubectl -n hel-chrysopoeia delete ocirepository.source.toolkit.fluxcd.io redis-v0-1 newredis-v0-1

Deleting a CustomResourceDefinitionSource leaves its generated CRD in place. If you want the kind gone as well, delete it explicitly.

kubectl delete crd redis.v0.1.redis.helmetica-bundles.io
kubectl delete crd newredis.v0.1.newredis.helmetica-bundles.io