GitLab Repositories Overivew #
-
Code Repository: k8s/example-project-2
-
Helm Chart Repository: k8s/example-project-2-helm
GitLab Code Repository #
CI Pipeline Manifest #
- .gitlab-ci.yml
variables:
# Define the image name for frontend
FRONTEND_IMAGE_SHA: $CI_REGISTRY_IMAGE/frontend:$CI_COMMIT_TAG
# Repository URL of the "Helm chart repository"
GIT_REPO_URL: "git@gitlab.jklug.work:k8s/example-project-2-helm.git"
stages:
- build
- update_helm_chart
build_container:
image: docker:stable # Official Docker CLI image (used to build, tag, and push images)
stage: build
rules:
- if: '$CI_COMMIT_TAG' # Trigger only when a tag is pushed
variables:
DOCKER_DRIVER: overlay2 # Storage driver
DOCKER_HOST: tcp://docker:2375 # Docker CLI communicate with DinD
DOCKER_TLS_CERTDIR: ""
services:
- name: docker:27.5.1-dind # DnD version
command: ["--tls=false"]
before_script:
# Log in to the GitLab Container registry using CI credentials
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
# Build the frontend Docker image / enable caching from the previously pulled image / tag the new image with the current commit SHA and as "latest"
- docker build -f Dockerfiles/Dockerfile --cache-from $CI_REGISTRY_IMAGE/frontend:latest --tag $FRONTEND_IMAGE_SHA --tag $CI_REGISTRY_IMAGE/frontend:latest .
# Push the newly built image to the GitLab Container registry
- docker push $FRONTEND_IMAGE_SHA
# Push the image tagged as "latest" to the GitLab Container registry
- docker push $CI_REGISTRY_IMAGE/frontend:latest
update_helm_chart:
image: alpine:latest
stage: update_helm_chart
rules:
- if: '$CI_COMMIT_TAG'
needs: # Run this job only if the build jobs succeed
- job: build_container
variables:
HELM_REPO_BRANCH: "main"
script:
# Install required tools
- apk add --no-cache yq git openssh
# Configure SSH for Git
- mkdir -p ~/.ssh
- echo "$example_project_2_key" > ~/.ssh/id_rsa # Use the correct variable name
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H gitlab.jklug.work >> ~/.ssh/known_hosts
# Clone the external repository
- git clone --branch $HELM_REPO_BRANCH $GIT_REPO_URL helm-chart-repo
- cd helm-chart-repo
# Verify the Helmchart does exist
- ls -R
- ls -R helm-chart
# Update values.yaml with new container images
- yq e ".image_frontend.repository = \"$CI_REGISTRY_IMAGE/frontend\" | .image_frontend.tag = \"$CI_COMMIT_TAG\"" -i helm-chart/values.yaml
# Update Chart.yaml with new version
- yq e ".version = \"$CI_COMMIT_TAG\" | .appVersion = \"$CI_COMMIT_TAG\"" -i helm-chart/Chart.yaml
# Update Commit Message
- yq e ".message = \"$CI_COMMIT_MESSAGE\"" -i helm-chart/Chart.yaml
# Add and commit the changes
- git config --global user.email "ci@example.com"
- git config --global user.name "GitLab CI"
- git add helm-chart/values.yaml # Add values.yaml
- git add helm-chart/Chart.yaml # Add Chart.yaml
- git commit -m "Update Helm chart with image $CI_COMMIT_TAG"
# Push the changes back to the external repository
- git push origin $HELM_REPO_BRANCH
Dockerfile #
- Dockerfiles/Dockerfile
# Use the official Caddy image as the base
FROM caddy:alpine
# Create a non-root user "caddy"
RUN addgroup -S caddy && adduser -S -G caddy caddy
# Adjust permissions
RUN mkdir -p /usr/share/caddy && \
chown -R caddy:caddy /usr/share/caddy /config /data
# Copy website files into the container
COPY app/public/ /usr/share/caddy
# Switch to the non-root user
USER caddy
# Expose the default Caddy port
EXPOSE 80
HTML File #
- app/public/index.html
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Some HTML</h1>
<p>hi there</p>
</body>
</html>
GitLab Helm Chart Repository #
File and Folder Structure #
The file and folder structure looks like this:
k8s/example-project-1-helm
├── helm-chart
│ ├── Chart.yaml
│ ├── templates
│ │ ├── deployment.yaml
│ │ ├── ingress.yaml
│ │ └── service.yaml
│ └── values.yaml
Deployment #
- helm-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app-frontend
labels:
app: example-app
spec:
replicas: 1
selector:
matchLabels:
app: example-app-frontend
template:
metadata:
labels:
app: example-app-frontend
spec:
imagePullSecrets:
- name: "{{ .Values.imagePullSecrets | first }}" # Correctly access the first item in the array
containers:
- name: example-app-frontend
image: "{{ .Values.image_frontend.repository }}:{{ .Values.image_frontend.tag }}"
ports:
- containerPort: 80
NodePort Service #
- helm-chart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: example-app-frontend
spec:
type: NodePort
ports:
- port: 8080
targetPort: 80
nodePort: 30080
selector:
app: example-app-frontend
Values #
- helm-chart/values.yaml
# Define the image details
image_frontend:
repository: "gitlab-registry.jklug.work/k8s/example-project-2/frontend" # GitLab Registry; will be updated by the pipeline
tag: "latest" # Tag placeholder; will be updated by the pipeline
imagePullSecrets:
- gitlab-registry-secret # The Kubernetes secret name
Chart #
- helm-chart/Chart.yaml
apiVersion: v2
name: example-project
description: A Helm chart for example-project
type: application
version: 1.0.0
appVersion: latest
message:
version: 0.1.0
Versions are expected to follow Semantic Versioning (https://semver.org/)
GitLab Deploy Key: For CI Pipeline #
Create SSH Key Pair #
Create an SSH keys pair, that is used for authentication, so that the pipeline in the code repository can update the Helm chart in the chart repository:
# Create SSH RSA key pair: 4096 bit
ssh-keygen -t rsa -b 4096 -f example_project_2_key
# Copy the public SSH key
cat example_project_2_key.pub
# Copy the private SSH key
cat example_project_2_key
Add Public Key to Helm Repository #
Add the public SSH key to the GitLab Helm chart repository static-websites/tag-pipeline-helm
:
-
Go to: (Project) “Settings” > “Repository”
-
Expand the “Deploy keys” section
-
Click “Add new key”
-
Define the title
example_project_2_key
-
“Key”: Paste the value of the public SSH key
example_project_2_key.pub
-
Select “Grant write permissions to this key”
-
Click “Add key”
Add Private Key to Pipeline Project #
-
Add the private SSh key as variable to the GitLab code and pipeline repository
static-websites/tag-pipeline
: -
Go to: (Project) “Settings” > “CI/CD”
-
Expand the “Variables” section
-
Click “Add variable”
-
Select type: “Variable (default)”
-
Unflag “Protect variable”
-
Define a key name
example_project_2_key
-
Paste the value of the private key
example_project_2_key
-
Click “Add variable”
Tigger the Pipeline #
Create a Tag #
# Create a tag
git tag v1.0.1
# Push the tag
git push origin v1.0.1
Verify Helm Chart Repo after Push #
After the pipeline run through, the values in the Helm chart repository should change like this:
- helm-chart/ values.yaml
# Define the image details
image_frontend:
repository: "gitlab-registry.jklug.work/k8s/example-project-2/frontend" # GitLab Registry; will be updated by the pipeline
tag: "v1.0.1" # Tag placeholder; will be updated by the pipeline
imagePullSecrets:
- gitlab-registry-secret # The Kubernetes secret name
- helm-chart/Chart.yaml
apiVersion: v2
name: example-project
description: A Helm chart for example-project
type: application
version: v1.0.1
appVersion: v1.0.1
message: |
Frist commit v1.0.1