Introduction
In a previous post, I discussed running Kubernetes locally on my Mac M1 using tools like orbstack
and kind
, complete with local DNS resolution.
Problem
My primary challenge was running GitLab locally. While several methods exist, such as using docker
with docker-compose
, these approaches often led to startup issues and failed to work reliably.
I then explored using the GitLab Helm chart, with comprehensive documentation available here. My goals included achieving local DNS resolution and correctly handling self-signed certificates. In this case orbstack
proved to be highly effective.
Solution 1
The solution leverages the tool orbstack
Initial Setup
Orbstack simplifies the process by making the domain k8s.orb.local
available on your local DNS on a Mac. This allows us to deploy GitLab using the Helm chart with the correct configuration right from the start.
Here is the example code. I am using the values-minikube-minimum.yaml
, which has been patched to work with orbstack
.
kubectl config use-context orbstack
kubectl create ns gitlab
helm repo add gitlab https://charts.gitlab.io/
helm repo update
# https://docs.gitlab.com/charts/releases/7_0.html
# as the chart version is mapped here to the app/GitLab version
helm upgrade --install gitlab gitlab/gitlab \
--timeout 120s \
--namespace gitlab \
--version 7.11.2 \
-f https://gitlab.com/gitlab-org/charts/gitlab/-/raw/v7.11.2/examples/values-minikube-minimum.yaml \
--set global.hosts.domain="k8s.orb.local" \
--set global.hosts.externalIP="" \
--set nginx-ingress.enabled=true
# wait until the webservice is up
kubectl -n gitlab rollout status --timeout 2m deploy gitlab-webservice-default
# test without valid cert chain: -k flag
curl -fsSkIL gitlab.k8s.orb.local
In the second step, I store the Certificate chain (includes the CA), as I need it for the Mac Keychain to see the Root CA as valid:
# crt with chain
kubectl get secret -n gitlab gitlab-wildcard-tls-chain -o jsonpath={'.data.gitlab\.k8s\.orb\.local\.crt'} | base64 --decode > gitlab.k8s.orb.local.crt
# add it to the Mac keychain
security add-trusted-cert -d -r trustRoot -k $HOME/Library/Keychains/login.keychain-db gitlab.k8s.orb.local.crt
And it shoud look like this and trusted
Now test the access again, but with a valid keychain
# Note: no -k
curl -fsSIL gitlab.k8s.orb.local
If you get the following error on Mac curl: (35) OpenSSL/3.0.13: error:16000069:STORE routines::unregistered scheme
, it is because of the version of curl
. For me it was:
curl --version
curl 8.7.1 (aarch64-apple-darwin23.5.0) libcurl/8.7.1 OpenSSL/3.0.13 zlib/1.3.1 brotli/1.1.0 zstd/1.5.6 libidn2/2.3.7 libpsl/0.21.5 libssh2/1.11.0 nghttp2/1.61.0
Release-Date: 2024-03-27
I fixed it by running the command from a docker container:
docker run -it --network host -v $(pwd):/build alpine:3 sh
apk update && apk add curl
curl --version
cp /build/gitlab.k8s.orb.local.crt /usr/local/share/ca-certificates/
update-ca-certificates
WARNING: ca-cert-gitlab.k8s.orb.local.pem does not contain exactly one certificate or CRL: skipping
# this message can be ignored. CRT is loaded neverthelesse
# see https://github.com/gliderlabs/docker-alpine/issues/30#issuecomment-303480386
curl -IL gitlab.k8s.orb.local
In action, it looks like this.
Now open your browser on https://gitlab.k8s.orb.local and login with user root
and password, which you retrieve as follows 🎉
kubectl get secret -n gitlab gitlab-gitlab-initial-root-password -ojsonpath='{.data.password}' | base64 --decode ; echo
The login screen looks as follows:
Now that I have GitLab running and are logged in as root
, let’s create a Personal access token (PAT)
Then you can test the access:
# Note: no -k flag, as the crt is in our mac keychain
export GITLAB_TOKEN=<token>
curl -IL -H "PRIVATE-TOKEN: $GITLAB_TOKEN" https://gitlab.k8s.orb.local/api/v4/version | jq
will give the details
{
"version": "16.11.2-ee",
"revision": "d210b947e3e",
"kas": {
"enabled": true,
"externalUrl": "wss://kas.k8s.orb.local",
"version": "16.11.2"
},
"enterprise": true
}
Docker-in-Docker (DinD) jobs
Download the file https://gitlab.com/gitlab-org/charts/gitlab/-/raw/v7.11.2/examples/values-minikube-minimum.yaml
and then I adapt it as follows. But note to adapt the path
of your
docker socket. You can find this out via
docker context ls
NAME DESCRIPTION DOCKER ENDPOINT
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
orbstack * OrbStack unix:///Users/<user>/.orbstack/run/docker.sock
and replace this in the following path gitlab-runner.runner.config
then in path = "..."
.
See now the full example:
# values-minikube.yaml
# This example intended as baseline to use Minikube for the deployment of GitLab
# - Minimized CPU/Memory load, can fit into 3 CPU, 6 GB of RAM (barely)
# - Services that are not compatible with how Minikube runs are disabled
# - Some services entirely removed, or scaled down to 1 replica.
# - Configured to use 192.168.99.100, and nip.io for the domain
# Minimal settings
global:
ingress:
configureCertmanager: false
class: "nginx"
hosts:
domain: k8s.orb.local # <== Change here
# No external IP
# externalIP: 192.168.99.100 # <== Change here
# Disable Rails bootsnap cache (temporary)
rails:
bootsnap:
enabled: false
shell:
# Configure the clone link in the UI to include the high-numbered NodePort
# value from below (`gitlab.gitlab-shell.service.nodePort`)
port: 32022
# Don't use certmanager, we'll self-sign
certmanager:
install: false
# Use the `ingress` addon, not our Ingress (can't map 22/80/443)
nginx-ingress:
enabled: true # <== Change here
# Save resources, only 3 CPU
prometheus:
install: false
gitlab-runner: # <== Change here the whole block
install: true
# "gitlab-wildcard-tls-chain" assumes your release name is "gitlab". If it is set to something else,
# replace "gitlab" below with your own release name.
certsSecretName: gitlab-wildcard-tls-chain
runners:
privileged: true
# for dind https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#docker-in-docker-with-tls-disabled-in-kubernetes
# fix https://gitlab.com/gitlab-org/gitlab-runner/-/issues/2578#note_603009975
config: |
[[runners]]
[runners.kubernetes]
[runners.kubernetes.volumes]
[[runners.kubernetes.volumes.host_path]]
name = "docker-socket"
path = "/Users/mavogel/.orbstack/run/docker.sock"
mount_path = "/var/run/docker.sock"
read_only = false
# Reduce replica counts, reducing CPU & memory requirements
gitlab:
webservice:
minReplicas: 1
maxReplicas: 1
sidekiq:
minReplicas: 1
maxReplicas: 1
gitlab-shell:
minReplicas: 1
maxReplicas: 1
# Map gitlab-shell to a high-numbered NodePort to support cloning over SSH since
# Minikube takes port 22. However on orbstack, we could use port 22, but we leave it
service:
type: NodePort
nodePort: 32022
registry:
hpa:
minReplicas: 1
maxReplicas: 1
As a next step, I will add runners with dind
, so I can make use of the docker socket 💡 within the GitLab runner.
helm upgrade --install gitlab gitlab/gitlab \
--timeout 120s \
--namespace gitlab \
--version 7.11.2 \
-f adapted-values-minikube-minimum.yaml
You can now see a gitlab-gitlab-runner-*
pod running. The content of the lower split are the logs of that pod and how it successfully registers with the Gitlab instance 🎉
Now, of course, we want to test this, and the best way is to create an example project with the following .gitlab-ci.yml
:
docker:test:
image: docker:24.0.5
variables:
# I use the DOCKER_HOST default value as I mount the socket
# and connect insecurely
DOCKER_TLS_CERTDIR: ""
script:
- printenv | grep DOCKER | sort
- docker version
If we now run this pipeline, we see the following output that confirms that the docker cli client in the container could successfully connect to the underlying server/daemon we mounted into the runner 🎉
Teardown
Of course, we want to tear everything down after our tests, to free the resources. You can achieve this as follows:
helm uninstall gitlab -n gitlab --wait
# watch teardown
watch kubectl get po -A
kubectl delete ns gitlab
At the next rollout of the helm
chart you will still have the data for Gitlab, because it is
stored in a pv
.
If you want to fully clean your Gitlab data, remove the pvc's
as well:
kubectl get pvc -n gitlab
# remove them
kubectl delete pvc -n gitlab -l release=gitlab
kubectl delete pvc -n gitlab -l app.kubernetes.io/instance=gitlab
Now everything is clean 🗑️
Solution 2
If you cannot or are not allowed to use orbstack
due to company rules, you can also use kind. See also the official Gitlab docs for more details, as it is also the recommended way to develop its helm chart.
Ok, let’s start it off.
Start kind
with the following config kind-gitlab-ssl.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
# to be able to run dind in the GitLab pipelines
# see https://github.com/kubernetes-sigs/kind/issues/1281#issuecomment-578052484
extraMounts:
- hostPath: /var/run/docker.sock # <-- or use the orbstack socket
containerPath: /var/run/docker.sock
extraPortMappings:
# containerPort below must match the values file:
# nginx-ingress.controller.service.nodePorts.https
# Change hostPort if port 443 is already in use.
- containerPort: 32443
hostPort: 443
listenAddress: "0.0.0.0"
# containerPort below must match the values file:
# nginx-ingress.controller.service.nodePorts.ssh
# Using high-numbered hostPort assuming port 22 is
# already in use.
- containerPort: 32022
hostPort: 32022
listenAddress: "0.0.0.0"
with the command:
kind create cluster \
--name gitlab \
--image kindest/node:v1.29.2 \
--config kind-gitlab-ssl.yaml \
--wait 1m
When the cluster is up and running, you can use the example files from the Gitlab helm repository:
# get your ip:
## mac: ipconfig getifaddr en0
## linux: hostname -i
export MY_IP=$(ipconfig getifaddr en0)
# switch the k8s context
kubectl config use-context kind-gitlab
# install Gitlab
helm upgrade --install gitlab gitlab/gitlab \
--timeout 120s \
--namespace gitlab \
--set global.hosts.domain=$MY_IP.nip.io \
--set global.hosts.externalIP="" \
-f https://gitlab.com/gitlab-org/charts/gitlab/-/raw/v7.11.2/examples/kind/values-base.yaml \
-f https://gitlab.com/gitlab-org/charts/gitlab/-/raw/v7.11.2/examples/kind/values-ssl.yaml \
--set nginx-ingress.enabled=true
The teardown is simpler here, as we can delete the whole cluster:
kind delete cluster --name gitlab
Conclusion
In this blog post, I shared my exploration of how to set up GitLab locally to evolve
pipelines and its jobs by using orbstack
or kind
and the corresponding helm
chart.
Enjoy ❤️
Like what you read? You can hire me, or drop me a message to see which services 💻 may help you 👇