Skip to main content

Deploy to Kubernetes from GitLab CI

You have an app, a Bunker Kubernetes cluster, and you want git push to trigger an automatic deployment. This tutorial shows you how to wire everything up in 15 minutes.

What You Need​

  • A GitLab project
  • A namespace on your Bunker Kubernetes cluster
  • A kubeconfig (downloadable from the Bunker console)

Step 1: Store the kubeconfig in GitLab​

Go to your project: Settings > CI/CD > Variables.

VariableTypeProtected
KUBE_CONFIGFileyes

Paste the contents of your kubeconfig. The File type is important: GitLab creates a temporary file and puts the path in $KUBE_CONFIG. If you choose "Variable" by mistake, kubectl won't find the file.

Step 2: The Pipeline​

Copy this .gitlab-ci.yml into your project. We use BuildKit instead of Docker - if you want to know why, read the GitLab CI Runners guide.

# .gitlab-ci.yml
stages:
- build
- deploy

variables:
IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}

build:
stage: build
image:
name: moby/buildkit:rootless
entrypoint: [""]
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD | base64)\"}}}" > ~/.docker/config.json
script:
- buildctl-daemonless.sh build
--frontend dockerfile.v0
--local context=.
--local dockerfile=.
--output type=image,name=${IMAGE},push=true

deploy:
stage: deploy
image: alpine/helm:latest
before_script:
- export KUBECONFIG=$KUBE_CONFIG
script:
- helm upgrade --install my-app ./helm
--namespace my-namespace
--set image.repository=${CI_REGISTRY_IMAGE}
--set image.tag=${CI_COMMIT_SHORT_SHA}
--wait
--timeout 5m
environment:
name: production

On every push, the image is built and deployed. No more complicated than that.

Step 3: Staging + Production (Optional)​

If you want to deploy automatically to staging and keep a manual button for prod:

stages:
- build
- deploy-staging
- deploy-production

variables:
IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}

build:
stage: build
image:
name: moby/buildkit:rootless
entrypoint: [""]
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD | base64)\"}}}" > ~/.docker/config.json
script:
- buildctl-daemonless.sh build
--frontend dockerfile.v0
--local context=.
--local dockerfile=.
--output type=image,name=${IMAGE},push=true
rules:
- if: '$CI_COMMIT_BRANCH == "main"'

.deploy: &deploy
image: alpine/helm:latest
before_script:
- export KUBECONFIG=$KUBE_CONFIG
script:
- helm upgrade --install $RELEASE ./helm
--namespace $NAMESPACE
--set image.repository=${CI_REGISTRY_IMAGE}
--set image.tag=${CI_COMMIT_SHORT_SHA}
--wait

deploy-staging:
<<: *deploy
stage: deploy-staging
variables:
RELEASE: my-app-staging
NAMESPACE: staging
environment:
name: staging
url: https://staging.my-app.com
rules:
- if: '$CI_COMMIT_BRANCH == "main"'

deploy-production:
<<: *deploy
stage: deploy-production
variables:
RELEASE: my-app-prod
NAMESPACE: production
environment:
name: production
url: https://my-app.com
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual

Step 4: Promote Without Rebuild (Optional)​

You can promote the staging image to prod without rebuilding it. Useful if the build takes time or if you want to guarantee it's exactly the same image:

promote-to-prod:
stage: deploy-production
image: quay.io/skopeo/stable:latest
script:
- skopeo copy
--src-creds=${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}
--dest-creds=${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}
docker://${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
docker://${CI_REGISTRY_IMAGE}:production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual

Troubleshooting​

"Unable to connect to the server"​

Check that KUBE_CONFIG is of type File, not Variable. This is the most common error.

"Forbidden"​

The kubeconfig doesn't have rights on the namespace. Check the RBAC or ask your cluster admin.

"ImagePullBackOff"​

Kubernetes can't pull the image from the GitLab registry. You need to create a secret:

kubectl create secret docker-registry gitlab-registry \
--docker-server=registry.gitlab.com \
--docker-username=deploy-token \
--docker-password=xxx \
-n my-namespace

Then reference it in your Deployment:

spec:
imagePullSecrets:
- name: gitlab-registry

Other Errors​

Check the common errors in the Runners guide.

Going Further​

Questions? [email protected]