Welcome back!

In this guide you will learn how to use GitOps to deliver your applications to a staging and a production environment on a Civo Kubernetes cluster. As a bonus, you will also learn how to create preview environments for your feature branches.

This tutorial builds on GitOps on Civo using Gimlet CLI, Part One that introduced a working GitOps setup.

Part Two uses Gimlet CLI to manage environments declaratively from your application source code repository. Gimlet CLI is a command line tool that packages a set of conventions and matching workflows so you can manage your GitOps based release processes effectively. You can read more about it on https://gimlet.io

Prerequisites

Now that you have covered the prerequisites, let's get started!

First, let's revisit the pod tato Head sample application

the pod tato Head sample application

In Part One, we used the pod tato Head sample app as it had a Kubernetes deployment manifest prepared. In this tutorial we are going to use this very same application, but going to use the packaged Helm chart deployment method instead.

Helm is a package manager for Kubernetes that uses charts to define the set-up and running of Kubernetes applications.

While Helm introduces some complexity to our setup, it is widely adopted in the community and it is well documented. This tutorial will explain all related steps should you not know Helm in detail. Also, Gimlet CLI uses Helm only in part, specifically for templating and packaging manifests, where it does a tremendous job.

But first we have to introduce environments to our setup.

Introducing environments to GitOps

In Part One we created a staging logical environment, but we didn't think much about it. We needed a place to write to the application manifests to, and Gimlet CLI enforced this convention.

Before we introduce a production environment, let's take a step back and look at how we could manage environments effectively.

In Part One and in scripts in general, we speak in procedural terms: write manifests to GitOps, set this variable, roll back the changes, if this, then that.

In a software delivery however, we use different words: staging has this fix, production has that new feature, a setting is enabled on production. To manage environments effectively, we have to capture this terminology in our configuration too.

Gimlet CLI captures environment specific settings in an environment configuration file.

Capture staging configuration in the .gimlet/staging.yaml environment file

Gimlet CLI introduces environment files to the application source code repository. In the environment file application developers capture every setting that is specific for a given environment.

Let's now capture the pod tato Head sample app settings in a new file called .gimlet/staging.yaml

#.gimlet/staging.yaml
app: podtato-head
env: staging
namespace: staging
chart:
  name: ../charts/hello-server
  version: 0.1.0
values:
  replicaCount: 1

The above yaml starts with some metadata, then references a Helm chart, and captures the chart values under the values field.

The Helm chart contains all the needed deployment manifests, and exposes a set of settings that can be controlled per environment. With the naming, the chart, and its values pinned down, all deployment information is captured for the staging environment. We are ready to deploy it with GitOps!

But the Gimlet environment file is not directly understood by the Flux GitOps controller we deployed earlier. To make a Kubernetes deployment manifest from it, use Gimlet CLI's gimlet manifest template

Use gimlet manifest template to render the deployment manifests

Once you defined your environment, the gimlet manifest template command renders a Kubernetes deployment manifest from it. It operates just like the helm template command, in case you are familiar with it: The command locates or fetches the chart, then templates the chart files with the given values. Let's run the command, taking in the .gimlet/staging.yaml file and outputting a manifests.yaml file:

$ gimlet manifest template \
  -f .gimlet/staging.yaml \
  -o manifests.yaml

You can inspect the generated yaml, then write it to the GitOps repository with the gimlet gitops write command, which should be familiar from Part One:

$ gimlet gitops write -f manifests.yaml \
  --env staging \
  --app podtato-head \
  --gitops-repo-path ../civo-gitops \
  -m "Deploying from Gimlet environment"

Some more thoughts before introducing the production environment

With the environment file, developers control deployment settings from their application source code, while the GitOps repository remains the source of truth for the deployed version. Should you need to change a setting, do that in your application repo. Do you need to debug something or track the deploy history? Look in the GitOps repository.

The Gimlet environment file however requires a Helm chart for your application. While the pod tato Head application has one, maintaining a chart for each of your application is a considerable effort.

That's why Gimlet maintains a generic Helm chart that suits most web service or background jobs, and allows the configuration of common settings. It's called OneChart and you can read about it here. OneChart mimics the practices of larger teams who maintain a deployment blueprint that teams can base their deployments on.

And finally, let's create a production environment

If you followed the tutorials, by now you have read about all the steps that are required to introduce a production instance of the sample application.

First let's bootstrap the production GitOps environment:

$ gimlet gitops bootstrap \
  --env production \
  --gitops-repo-path civo-gitops \
  --gitops-repo-url git@github.com:laszlocph/civo-gitops.git # make sure to point this to your repository name

This will create the right folder structure in the GitOps repository, and you have to add the production deploy key to Github. With Gimlet CLI every environment works with its own deployment key to increase security. If you need a hint on how to add the deployment key, check out this section in Part One of this tutorial.

Next, to actually create a production configuration of the sample application, add a new Gimlet environment file in your application repository.

#.gimlet/production.yaml
app: podtato-head
env: production
namespace: production
chart:
  name: ../charts/hello-server
  version: 0.1.0
values:
  replicaCount: 2

Notice that in production, we deploy two replicas of the application.

Once you write the manifests to the GitOps repository, you will have just set up a production and a staging environment with the pod tato Head application deployed in both:

$ gimlet gitops write -f manifests.yaml \
  --env production \
  --app podtato-head \
  --gitops-repo-path ../civo-gitops \
  -m "Deploying from Gimlet environment to production"

This is neat, and you can get far by using static environments, like we did with staging and production.

But Gimlet CLI also supports variables in the environment file, and by feeding in variables from your CI process, you can make the environment configuration dynamic.

You can use the variables to update the deployed image tag on every CI run,

#.gimlet/production.yaml
app: podtato-head
env: production
namespace: production
chart:
  name: ../charts/hello-server
  version: 0.1.0
values:
  replicaCount: 2
  image:
    tag: {{ .GITHUB_SHA }} # to get the git SHA from the environment with Github Actions

or with a little creativity, you can make a dedicated environment for your feature branches or pull requests too.

Bonus: preview environments for feature branches

With Gimlet CLI's variable support, you can use every variable from your environment, or you can feed in a set of new ones with the --vars flag of gimlet manifest template

By carefully choosing a dynamic name for an environment - to avoid naming collisions - you can have a dynamic environment for every feature branch or pull request you have:

#.gimlet/feature-builds.yaml
app: podtato-head-{{ .BRANCH }}
env: preview
namespace: preview
chart:
  name: ../charts/hello-server
  version: 0.1.0
values:
  replicaCount: 2
  image:
    tag: {{ .GITHUB_SHA }} # to get the git SHA from the environment with Github Actions

Closing words

In this guide you learned how to create a staging and a production environment by adding Gimlet environment files to your application source code repository.

With the environment file, developers control deployment settings from their application source code. They can make changes using git, and have pull requests to review the environment configuration, just like with source code.

.
├── .gimlet
│   ├── staging.yaml
│   ├── production.yaml
│   └── feature-builds.yaml

You also learned how to use variables to update the image tag, and to create dynamic environments for your feature builds.

Now you can repeat this exercise for your own applications.

Should you need more information, you can read the reference of gimlet manifest template and if you need a Helm chart for your application, you can take a look at OneChart and its supported use cases.

Onwards!