Automating Deployments with GitHub Actions
2022-03-24 — 4 min read automation github actions helm kubernetes
When writing my last blog post, I really wanted to share a draft of it (and not
just the source code) before publishing. To do that, deployed my standard helm
template to its own namespace and, after showing it, removed the namespace.
However, I had to hand-craft those scripts, which meant the results weren’t
reproducible. I already wasn’t really satisfied with how my publishing script
required a manual run, and didn’t ensure it matched main
. I wanted a better
solution.
The first step was to set up a deployment for main. Since this blog is hosted on
GitHub, GitHub Actions seem to be the best option. This action, triggered by any
push to main
, would need to build a docker image, publish it to the container
registry, and update my helm template.
First, the trigger:
on:
push:
branches:
- main
Logging in to the container registry was a bit tricky, but following the guide by Microsoft for Azure GitHub Actions I was able to create the secrets. You can see my full deploy script on GitHub, but the login scripts included:
- name: 'Log in to docker registry'
uses: azure/docker-login@v1
with:
login-server: ${{ env.registryLoginServer }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: azure/aks-set-context@v2.0
with:
cluster-name: ${{ env.azClusterName }}
resource-group: ${{ env.azClusterResourceGroup }}
The docker build is standard and a bit boring, as is the helm deploy command for
the main
build. However… I wasn’t done yet. I really wanted to be able to
preview branches as they show on the site itself. (I’d already run into one bug
with how Next.js loads new URLs when using next export
with a standard static
files server…) I needed a couple more pipleines.
This time, since I wanted to preview a PR, I needed a PR trigger:
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
We can reuse most of the yaml from our original main
build, but the helm
deployment becomes a bit different. Since I wanted a per-PR site to preview, I
needed to create a new helm release, and I didn’t want it to collide with
anything else. Setting up a new namespace in my Kubernetes cluster seems like
the way to go!
- name: 'Deploy'
run: |
helm upgrade --install \
-n ${{ env.k8sNamespacePrefix }}${{ github.event.pull_request.number }} $releaseName --create-namespace \
--repo https://mdekrey.github.io/helm-charts single-container \
--set-string "image.repository=$registryLoginServer/$imageName" \
--set-string "image.tag=${{ github.sha }}" \
--set-string "ingress.hosts[0].host=pr-${{ github.event.pull_request.number }}.dekrey.net" \
--values ./deployment/values.yaml
This looked pretty good, though I really wanted to notify when the site was available. I found a ton of published actions that allow you to easily add a comment to the PR, but I’d rather use something official and definitely maintained. Eventually I found the official github-script repository, and they had a pretty basic example that almost did what I needed. The final result looks like this:
- name: Report PR url
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Branch ready for preview at https://pr-${{ github.event.pull_request.number }}.dekrey.net'
})
With that, we’re almost done! But, when creating new resources, we want to always clean up after ourselves. On the PR closed (whether merged or not), we want to be sure to delete both the helm resource and the kubernetes namespace.
on:
pull_request:
types: [closed]
Again, we’ll use the same login scripts as before, but this time, we’ll make sure to issue the delete commands.
- name: 'Clean up Kubernetes'
run: |
helm delete -n ${{ env.k8sNamespacePrefix }}${{ github.event.pull_request.number }} ${{ env.releaseName }} --wait
kubectl delete ns ${{ env.k8sNamespacePrefix }}${{ github.event.pull_request.number }}
And that’s it! Now I have a site that not only builds my PRs, but deploys a testing branch so I can preview it and really make sure it works on the server before hitting that merge button.