Skip to content

Quick start

Setting up

To start using this library, first initialize the working directory using tk init.

This will create a directory structure similar to this:

.
├── environments
│   └── default
├── jsonnetfile.json
├── jsonnetfile.lock.json
├── lib
│   └── k.libsonnet
└── vendor
    ├── 1.23 -> github.com/jsonnet-libs/k8s-libsonnet/1.23
    ├── github.com
    └── ksonnet-util -> github.com/grafana/jsonnet-libs/ksonnet-util

Next, we need to install the cloudflight-libsonnet library using jb:

jb install github.com/cloudflightio/cloudflight-libsonnet@main

The last step is to adapt the predefined k.libsonnet. When initializing, Tanka simply imports the k8s-libsonnet library here. Since some of our customizations depend on other libraries which have to be imported at this point (such as openshift-libsonnet or prometheus-libsonnet), you replace the contents of k.libsonnet with the following:

import 'github.com/cloudflightio/cloudflight-libsonnet/k.libsonnet'

Using the library

To use the components provided by this library (or other Kubernetes components), import our prelude.libsonnet.

Let's use the library to deploy a simple java based application, backed by an MariaDB instance.

Creating the application

Applications should be defined in the lib folder. To configure a java application, we create a file called lib/test-java.libsonnet with the contents below.

local k = (import 'cloudflight-libsonnet/prelude.libsonnet');
{
  _config+:: {
    myApplication: {
      name: 'my-application',
      image: error '$._config.myApplication.image must be defined',
    },
  },
  myApplication: {
    deployment: k.util.java.deployment.new(
      name=$._config.myApplication.name,
      image=$._config.myApplication.image,
      containerMixin=k.core.v1.container.livenessProbe.withInitialDelaySeconds(60),
    ),
    service: k.util.serviceFor(self.deployment),
    route: k.util.routeFor(self.service, 'hello.example.com'),
  },
}

Now we can import this into our environment file environments/default/main.jsonnet

(import 'test-java.libsonnet')
+ {
  _config+: {
    myApplication+: {
      image: 'helloworld:latest',
    },
  },
}

Provided your .kube/config and environments/default/spec.json are set up correctly, you can deploy this by running tk apply environments/default.

Adding the database

Most applications want to store data somewhere so let's add a database next. First, we import the database of choice in the main.jsonnet file.

(import 'cloudflight-libsonnet/databases/mariadb.libsonnet')
+ (import 'test-java.libsonnet')
+ {
  _config+: {
    mariadb+: {
      user: 'application-user',
      password: 'hunter2',
      database: 'my-application',
    },
    myApplication+: {
      image: 'helloworld:latest',
    },
  },
}

To connect our application to the database, we need to configure the deployment on a lower level. This is because of the fact, that the plain util.java.deployment does not make any assumptions about databases.

As documented in the java module, we build the deployment ourselves, but mix in some environment variables.

local k = (import 'cloudflight-libsonnet/prelude.libsonnet');
{
  _config+:: {
    myApplication: {
      name: 'my-application',
      image: error '$._config.myApplication.image must be defined',
      dbUser: error '$._config.myApplication.dbUser must be defined',
      dbPasswordRef: error '$._config.myApplication.dbPasswordRef must be a valid secretSecretKeyRef',
      dbUrl: error '$._config.myApplication.dbUrl must be defined',
    },
  },
  myApplication: {
    deployment: k.apps.v1.deployment.new(
      name=$._config.myApplication.name,
      replicas=1,
      containers=[
        k.util.java.container.new($._config.myApplication.name, $._config.myApplication.image)
        + k.core.v1.container.withEnvMixin([
          {
            name: 'SPRING_DATASOURCE_PASSWORD',
            valueFrom: { secretKeyRef: $._config.myApplication.dbPasswordRef },
          },
          {
            name: 'SPRING_DATASOURCE_USERNAME',
            value: $._config.myApplication.dbUser,
          },
          {
            name: 'SPRING_DATASOURCE_URL',
            value: $._config.myApplication.dbUrl,
          },
        ]),
      ]
    ),
    service: k.util.serviceFor(self.deployment),
    route: k.util.routeFor(self.service, 'hello.example.com'),
  },
}

In the last step, we fill the new config parameters with values.

(import 'cloudflight-libsonnet/databases/mariadb.libsonnet')
+ (import 'test-java.libsonnet')
+ {
  _config+: {
    mariadb+: {
      user: 'application-user',
      password: 'hunter2',
      database: 'my-application',
    },
    myApplication+: {
      image: 'helloworld:latest',
      dbUser: $._config.mariadb.user,
      dbPasswordRef: $.mariadb.passwordSecretKeyRef,
      dbUrl: 'jdbc:mysql://' + $.mariadb.service.metadata.name + '/' + $._config.mariadb.database,
    },
  },
  mariadb2: (import 'cloudflight-libsonnet/databases/mariadb.libsonnet') + {
    _config+: {
      mariadb+: {
        name: 'foo',
        user: 'foo-user',
        password: 'foo-user',
      },
    },
  },
}

And we're done!

To recap: we have configured an application, added a database and connected it to our application. The way we connected the applications is transparent, and we could (as long as the application supports it) change the database entirely in our main.jsonnet. This way, we can use different database setups across environment, while keeping the application configuration the same.

For more information, check out the documentation of the java utilities and the MariaDB module.


Last update: 2022-08-08