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.
| Variable | Type | Protected |
|---|---|---|
KUBE_CONFIG | File | yes |
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
- GitLab CI Runners Guide - BuildKit, Crane, cache, multi-platform builds
Questions? [email protected]