Sitecore Deployments with Kubernetes and GitOps

Deploying Sitecore applications to Kubernetes is a little trickier than the old-fashioned “copy files to a VM” deployments - fortunately, by using Kubernetes, we’re now in the cool kids club and the cool kids have some cool toys.

Big Picture

Sitecore Container DevOps Process

Here’s what we’re going to do:

  1. Have Azure Pipelines build our code
  2. Take the output of that build and build a new version of our container image
  3. Update the manifest with the new image tag
  4. Apply the update manifest to our cluster
  5. ???
  6. PROFIT!

Let’s get started!

Building Our Code

This is the same process you’ve done a million (or a billion) times - call your bff msbuild.exe, let it do its thing, and save the output to a folder.

Here it is in all its Azure Pipelines YAML glory:

- task: VSBuild@1
  displayName: Build solution
  inputs:
    solution: "Solution.sln"
    msbuildArgs: '/m /p:DeployOnBuild=true /p:WebPublishMethod=FileSystem /p:TransformWebConfigEnabled=False /p:MarkWebConfigAssistFilesAsExclude=false /p:DeployDefaultTarget=WebPublish /p:SkipInvalidConfigurations=true /p:publishUrl="$(Build.ArtifactStagingDirectory)\code"'
    platform: "Any CPU"
    configuration: "Release"

Build a New Container Image

Okay great, now we have our code that in non-container world, we would just copy to a VM and be done.

Surprisingly (or maybe unsurprisingly), it’s not much different here, though instead of a VM, it’s an image.

Create yourself a Dockerfile that basically takes the Sitecore image, instruct it to copy your build artifacts into the /inetpub/wwwroot folder, then be done with it. This is where you’d want to do your config transforms and whatnot as well if that’s what strikes your fancy.

Essentially, the end goal is to create a Dockerfile that will build a runnable image with all your code in it.

Tag that sucker with a new tag to differentiate from other versions (timestamps can be your friend here) and push it to your container registry.

Update the Manifest

This is where it starts getting a little tricker. Remember in your Kubernetes manifest you defined your container image - it looked something like this?

containers:
  - name: sitecore-cm
    image: sitecore-cm:10.1-1809

Well, you need to update it. You’ve got a couple of options here.

Lazy Method (that will get you yelled at)

The kubectl command line tool has a command to set the image of your Deployment or StatefulSet which is almost a little too easy to use. Instead of trying to parse through your YAML and trying to merge in some craziness, you can do something like this:

kubectl set image deployment/sitecore-cm sitecore-cm=sitecore-cm:newtag-12345

This will update the image of your deployment to be the image with the tag that you want, create a new pod with that new image, wait for it to spin up and pass liveness/readiness probes, then terminate the old pod.

Cool! That sounds like exactly what we want! Great, I’m going home.

But wait. It updates the deployment on the cluster, but what happens when someone decides to come along and do a kubectl apply -f sitecore-cm.yaml? The image tag that’s in the YAML file is what’s going to get deployed and now you’re back to whatever image the original YAML file had. Seems risky.

Better Method

Sitecore supporting Kubernetes allows us to do some pretty cool things - one of the big cool things in the DevOps/SRE world is GitOps.

GitOps - what is it

It’s CI for your Kubernetes cluster. The idea is that your Git repository with your IAC is the digital twin of your Kubernetes cluster. When you commit a Kubernetes manifest change to your repository, a process is triggered and syncs your Git repository with your Kubernetes cluster. You can do this with your broader cloud infrastructure as well (or if you’re feeling really dangerous, configuring your cloud infrastructure through Kubernetes CRDs) but we’ll stick with just deploying Kubernetes manifests for now.

Basically, GitOps is the result of operations people discovering version control. It’s stuff us developers have been doing for ages and now that there’s this idea of infrastructure-as-code (IAC), they’re learning all about things we’ve been taking for granted, like Git.

How do I use it with Sitecore?

You know how Sitecore provides you with all the Kubernetes manifests to deploy onto AKS? Commit those to a Git repository and you’re already halfway there. Actually don’t do that. There are all kinds of secrets in those txt files and that’s not something you want to be commiting to a repository. Fix this by using an Azure Key Vault and then commit the rest into a Git repository.

Then, install your favorite GitOps tool and configure it. The big ones are ArgoCD and Flux - personally, I use ArgoCD but at the core they do the same thing. Point your project to your repository and your cluster and let it do its thing. All the manifests in your Git repository will now be applied to your cluster!

Couldn’t I have just done a kubectl apply?

Yeah, but what fun is that?

Actually, this allows you to do a couple of things -

  1. Traceability: know who and when any changes to the Kubernetes configurations occurs
  2. Reliability: as part of the deployment process, automated checks can be put into place
  3. Security: having a single point of management for your Kubernetes cluster is going to be more secure than distributing credentials to your team. This allows you to only have to grant credentials to your Git repository and not the Kubernetes API server itself.

Lock down your Kubernetes API

You should do this. Sure, there’s auth and other security goodies, but do you really want your Kubernetes API exposed to the internet to anyone? Probably a bad idea. Secure your API with some firewalls - or even better, with AKS make it a private cluster - then limit only your GitOps server to access it. This means you don’t need random developer/ops person to have .kubeconfig creds.

Version control and rollbacks

Something breaks! Why won’t the site load? What’s going on? Who touched this last?

Hooray for Git! Rollback to a previous commit, sync it up, and be on your way. Then, fire up git blame and do what the command says - go find the perpetrator and BLAME.

Application deployment pipeline integration

If you’re building a new image every time you’re running CI, you’re going to need to update the image tag in your manifest and apply it in order to update your running site. This can be done with Kustomize if you’re using straight Kubernetes YAML files or through Helm if you’re using that. Once the configuration has been updated with the correct image tag, you’ll commit those changes to your Git repo and trigger a sync with your GitOps server to make your Kubernetes instance match your Git repository.

Hooks

There are some basic hooks that come along with these GitOps platforms - for example, I have a hook to execute a Kubernetes Job to do a Unicorn sync whenever a sync finishes. Since we all know that these Sitecore containers can often take 3-4 minutes to come up sometimes, putting this as part of your process and letting the platform handle it instead you doing it manually afterwards is a lot nicer. There are typically also hooks for pre-sync and immediately post-sync as well as post-healthcheck.

So how do I get started?

Go learn about GitOps and stay tuned to learn about ArgoCD!