Skip to main content

GitLab CI Tag Pipeline: Trigger GitLab CI Pipeline via Tags, Update Helm Chart Values

965 words·
GitLab GitLab CI CI Pipeline Tag Pipeline Helm
Table of Contents

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:



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