Overview #
I’m using a Kubeadm based K8s cluster with one controller and two worker nodes, with MetalLB and Nginx Ingress.
# Kubernetes Cluster
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
ubuntu1 Ready control-plane 9m38s v1.28.11 192.168.30.10 <none> Ubuntu 24.04 LTS 6.8.0-35-generic containerd://1.7.18
ubuntu2 Ready worker 7m19s v1.28.11 192.168.30.11 <none> Ubuntu 24.04 LTS 6.8.0-35-generic containerd://1.7.18
ubuntu3 Ready worker 7m15s v1.28.11 192.168.30.12 <none> Ubuntu 24.04 LTS 6.8.0-35-generic containerd://1.7.18
192.168.70.4 # Local GitLab Instance "gitlab.jklug.work"
192.168.70.5 # GitLab Runner
Argo CD CLI #
I’m installing the Argo CD CLI in my Kubernetes Controller node.
Installation #
Find the latest stable release:
https://github.com/argoproj/argo-cd/tags
# Install Argo CD CLI
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/download/v2.11.4/argocd-linux-amd64
# Change permissions
chmod +x argocd
# Move the binary
sudo mv argocd /usr/local/bin/
# Verify the installation / check version
argocd version
Configuration #
Argo CD DNS Entry #
Make sure the host where the Argo CD CLI is deploy can resolve the DNS name of the Argo CD server:
# Add DNS entry to /etc/hosts
sudo tee -a /etc/hosts <<EOF
192.168.30.201 argocd.jklug.work
EOF
Set Argo CD Server Address #
# Set the the Argo CD server address
argocd login argocd.jklug.work
# Shell output:
Username: admin
Password: # Enter the admin pw
'admin:login' logged in successfully
Context 'argocd.jklug.work' updated
Kubernetes Prerequisites #
GitLab DNS Entry #
Add DNS entries for GitLab & the GitLab registry to the Kubernetes cluster nodes:
# Add DNS entry to /etc/hosts
sudo tee -a /etc/hosts <<EOF
192.168.70.4 gitlab.jklug.work
192.168.70.4 gitlab-registry.jklug.work
EOF
CoreDNS ConfigMap #
Backup the ConfigMap #
# Export the current ConfigMap
kubectl get cm coredns -n kube-system -o yaml > coredns-configmap-backup.yaml
Add GitLab DNS Entry #
Create a DNS entry that points to GitLab:
# Add the following DNS section to the CoreDNS ConfigMap
hosts {
192.168.70.4 gitlab.jklug.work
192.168.70.4 gitlab-registry.jklug.work
fallthrough
}
# Edit the CoreDNS ConfigMap
kubectl edit cm coredns -n kube-system
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
hosts {
192.168.70.4 gitlab.jklug.work
192.168.70.4 gitlab-registry.jklug.work
fallthrough
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2024-07-05T17:36:16Z"
name: coredns
namespace: kube-system
resourceVersion: "224"
uid: 0c9d9c5a-c49f-4392-a465-ffb1d047811c
# Restart CoreDNS
kubectl rollout restart deployment coredns -n kube-system
Verify DNS Resolution #
# Run a busybox pod
kubectl run busybox --image=busybox --restart=Never --stdin --tty
# Test the GitLab DNS resolution
nslookup gitlab.jklug.work
# Shell output:
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: gitlab.jklug.work
Address: 192.168.70.4
# Exit the container terminal
exit
# Delete the busybox pod
kubectl delete pod busybox
Argo CD Prerequisites #
Scan GitLab Host Keys #
From the node where the Argo CLI is installed (or any other host that can resolve the GitLab DNS name), scan the host keys of GitLab:
# List the GitLab host keys
ssh-keyscan gitlab.jklug.work
Shell output:
# gitlab.jklug.work:22 SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
# gitlab.jklug.work:22 SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
gitlab.jklug.work ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDHAQj15hmfv3OwTY3RAPwx1UlZ8p4qgtAHiZ9hngfJTSScO1kf40oL3Ek5NVKSGYZQ4w6ozBFHKO3l6tHn8nPeD7mUk/nW2U5w9yYpcRyFknn/u/Z0QreHAkI8fg6LI4n+2QYFF1rZbIemtCG33FozwrWKJ+/UsJYLnuQ2fenjcvkwPYx7NKV07RtQ3xYvkFVdWQGFJK8pLG9UcsanwZbH2nVPbv3i9KKI9xxWmJDh9JOoLhG6JipNN4Q4CoodfR9k9A2PY88dEykMInSGzFddOqbHLyISO8H1oJrofPzovPR07f+bDBK6iGIqRSW00k6mM0RFkPPo9tulLJ87DgB84jVrtYGp71wmV9PQ8jPB1uaDx5JtRNc0G+IWlIzTy8hFW9djELdTdQmfxeaCceyn1AmuXhpwZin64WTqztXj29s1olZ0+Uchh2FGpEjvlVqveeMmgAQkQezidqVHKKwinW1zdeSaBaZkS0JpLtxNpA86vBnhtYE8Z4CaQAvoQXU=
# gitlab.jklug.work:22 SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
gitlab.jklug.work ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPUxIo1glUPlmYJDbAOvHlRd/qjxdIEJCtBlcFLCMXECbRdp9IN/qePZdFtOnMWWVNvi8qy+7V8XbIFbzHoYwcg=
# gitlab.jklug.work:22 SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
gitlab.jklug.work ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH6GrjO8VqbiFMwtOfaEuKd3bV2vb7jH4r5Xl9PW1TFY
# gitlab.jklug.work:22 SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
Add GitLab Host Keys to Argo CD #
Open the Argo CD webinterface an add the GitLab host keys:
- Go to: “Settings” > “Repository certificates and known hosts” > “ADD SSH KNOWN HOSTS”
- Paste the GitLab SSH keys in the text box, one entry per line
- Click “CREATE” to save the entry
GitLab #
Argo CLI SSH Key #
Create a SSH key on the Argo CD CLI host and add the public key to GitLab:
# Create RSA Key: 4096 bit
ssh-keygen -t rsa -b 4096
Add SSH Key to GitLab #
-
Go to: (Profile) > “Edit profile” > “SSH Keys”
-
Click “Add new key”
-
Paste the public SSH key
-
Define a title like “Argo CLI”
-
Click “Add key”
GitLab Deployment Repository #
Folder Structure #
The folder structure of the GitLab repository looks like this:
├── deployments
│ └── custom-nginx-deployment.yaml
├── Dockerfile
└── website
└── index.html
Dockerfile #
Dockerfile
FROM nginx:1.18
COPY website/. /usr/share/nginx/html
EXPOSE 80
HTML File #
website/index.html
<!DOCTYPE html>
<html>
<head>
<title>Gitlab Container</title>
</head>
<body>
<h1>Nginx Container for K8s</h1>
<p>Kubernetes Deployment</p>
</body>
</html>
CI Pipeline #
.gitlab-ci.yml
stages:
- build
build:
stage: build
image: docker:20.10.16
variables:
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
services:
- docker:20.10.16-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:latest .
- docker tag $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
Deployment Manifest #
deployments/custom-nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-nginx-deployment
namespace: default
spec:
selector:
matchLabels:
app: custom-nginx
replicas: 5
template:
metadata:
labels:
app: custom-nginx
spec:
containers:
- name: custom-nginx
image: gitlab-registry.jklug.work/root/argocd-gitlabci:main
ports:
- containerPort: 80
imagePullSecrets:
- name: gitlab-registry-secret
---
apiVersion: v1
kind: Service
metadata:
name: custom-nginx-deployment
namespace: default
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: custom-nginx
CI Pipeline Logs #
Check the GitLab CI pipeline logs:
...
$ docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
The push refers to repository [gitlab-registry.jklug.work/root/argocd-gitlabci]
27a50e29d6d4: Preparing
4fa6704c8474: Preparing
4fe7d87c8e14: Preparing
6fcbf7acaafd: Preparing
f3fdf88f1cb7: Preparing
7e718b9c0c8c: Preparing
7e718b9c0c8c: Waiting
27a50e29d6d4: Pushed
4fa6704c8474: Pushed
6fcbf7acaafd: Pushed
4fe7d87c8e14: Pushed
f3fdf88f1cb7: Pushed
7e718b9c0c8c: Pushed
main: digest: sha256:e5bfc112504cfb3ec64e6c5cb09f9c261d15145b4008d128baad3411477f79e5 size: 1569
Cleaning up project directory and file based variables
00:01
Job succeeded
GitLab Registry Token #
Create a GitLab Registry token, used for Argo CD to access the GitLab Registry:
-
Go to: Profile > Access Tokens
-
Click “Add new token”
-
Define a token name like
ArgoCD
-
Define the scope of the token: “read_registry” Grants read-only access to container registry images on private projects.
-
Click “Create personal access token”
# Copy the token
glpat-x8LDGfwZYJrmp5yDmqCr
Test GitLab Registry Connection #
Optional: Test the GitLab Registry connection. This can be done from any host that can resolve the GitLab Registry DNS name and has Docker installed.
# Manually connect to the registry: Syntax
docker login gitlab-registry.jklug.work -u <your-gitlab-username> -p <your-personal-access-token>
# Manually connect to the registry: Example
docker login gitlab-registry.jklug.work -u root -p glpat-x8LDGfwZYJrmp5yDmqCr
# Shell output:
Login Succeeded
Optional: Manually pull the image from the GitLab registry.
# Manually pull the image
docker pull gitlab-registry.jklug.work/root/argocd-gitlabci:main
Deploy with Argo CD #
GitLab Registry Secret #
# Create a secret with the GitLab access token
kubectl create secret docker-registry gitlab-registry-secret \
--docker-server=gitlab-registry.jklug.work \
--docker-username=root \
--docker-password=glpat-x8LDGfwZYJrmp5yDmqCr \
--docker-email=juergen@jklug.work \
--namespace=default
# Shell output:
secret/gitlab-registry-secret created
Verify the Secret #
# List secret details
kubectl get secret gitlab-registry-secret -o yaml
Connect GitLab Repository: #
# Connect a GitLab repository via SSH
argocd repo add git@gitlab.jklug.work:root/argocd-gitlabci.git --ssh-private-key-path /home/ubuntu/.ssh/id_rsa
# Shell output:
Repository 'git@gitlab.jklug.work:root/argocd-gitlabci.git' added
The GitLab repository should now be availble in the Argo CD webinterface under: “Settings” > “Repositories”
Create an Application in Argo CD #
Create an Argo CD application named “argocd-gitlabci” that points to the Kubernetes manifests in the specified GitLab repository and deploys them to the default namespace of the Kubernetes cluster.
# Deploy the GitLab repository
argocd app create argocd-gitlabci \
--repo git@gitlab.jklug.work:root/argocd-gitlabci.git \
--path deployments \
--dest-server https://kubernetes.default.svc \
--dest-namespace default
# Shell output:
application 'argocd-gitlabci' created
The application should now be available in the “Applications” section of the Argo CD webinterface.
Sync / Deploy the Application #
# Deploy resources into the Kubernetes cluster
argocd app sync argocd-gitlabci
# Undeploy
argocd app delete argocd-gitlabci
Verify the Deployment #
# List pods in "default" namespace
kubectl get pods
# Shell output
NAME READY STATUS RESTARTS AGE
custom-nginx-deployment-7545cf6cd4-686ml 1/1 Running 0 8s
custom-nginx-deployment-7545cf6cd4-8kwfp 1/1 Running 0 8s
custom-nginx-deployment-7545cf6cd4-dsc8k 1/1 Running 0 8s
custom-nginx-deployment-7545cf6cd4-px9rz 1/1 Running 0 8s
custom-nginx-deployment-7545cf6cd4-qdz5c 1/1 Running 0 8s
# List services in "default" namespace
kubectl get svc
# Shell output
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
custom-nginx-deployment LoadBalancer 10.100.255.19 192.168.30.202 80:32477/TCP 7s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d2h
# Curl the LoadBalancer service
curl 192.168.30.202:80
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Gitlab Container</title>
</head>
<body>
<h1>Nginx Container for K8s</h1>
<p>Kubernetes Deployment</p>
</body>
</html>