Kubernetes Deploy
The Kubernetes deployment jobs are used to deploy applications to Kubernetes clusters using Helm charts from the Welance charts repository. These jobs support multiple application types with environment-specific configurations.
Overview
The k8s-deploy jobs include:
.deploy-craft-k8s: Deploy Craft CMS applications.deploy-wordpress-k8s: Deploy WordPress applications.deploy-directus-k8s: Deploy Directus CMS applications.deploy-node-k8s: Deploy Node.js applications.deploy-s3-k8s: Deploy S3 static website proxies.deploy-operator-k8s: Deploy Welance Operator
All jobs:
- Install required tools (Helm, kubectl, yq, AWS CLI)
- Configure Kubernetes cluster access
- Deploy using Helm charts from the Welance charts repository
- Support environment-specific configurations (develop, staging, production)
- Use different versioning strategies per environment
- Configure ingress, storage, and security contexts
Variables
The following variables can be configured:
| Variable | Description | Default | Required |
|---|---|---|---|
PROJECT_ID | Project identifier for namespace and hostnames | '' | Yes |
APP_ENVIRONMENT | Target environment | '' | Yes |
STORAGE_USER_ID | User/Group ID for storage permissions | - | Yes |
Variable Details
- PROJECT_ID: Used to:
- Create Kubernetes namespace:
{PROJECT_ID}-{APP_ENVIRONMENT} - Generate hostnames:
{PROJECT_ID}.{environment}.welance.com
- Create Kubernetes namespace:
- APP_ENVIRONMENT: Target environment. Options:
'develop'- Development environment'staging'- Staging environment'production'- Production environment
- STORAGE_USER_ID: User ID, Group ID, and FSGroup for security contexts and storage permissions
GitLab CI/CD Variables
The jobs use the following built-in GitLab CI/CD variables:
CI_COMMIT_REF_NAME: Branch or tag nameCI_PIPELINE_ID: Pipeline ID (for develop versioning)CI_PIPELINE_IID: Pipeline internal ID (for staging versioning)CI_COMMIT_SHA: Commit SHACI_PROJECT_URL: Project URLCI_REGISTRY_IMAGE: Container registry image path
Required Secrets
The following secrets must be configured in GitLab CI/CD variables:
NOPROD_KUBECONFIG: Base64-encoded kubeconfig for non-production clustersPROD_KUBECONFIG: Base64-encoded kubeconfig for production clusters
Common Job Structure
All Kubernetes deployment jobs follow this structure:
Before Script
-
Install Helm:
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh && ./get_helm.sh -
Install AWS CLI:
pip install awscli -
Install kubectl:
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x ./kubectl && mv ./kubectl /usr/local/bin -
Install yq:
wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq -
Configure Kubernetes access:
mkdir -p ~/.kube
helm repo add welance-charts https://welance.gitlab.io/platform/pipelines/helm/welance-charts/
helm repo update
if [ ${APP_ENVIRONMENT} = 'production' ]; then
echo "$PROD_KUBECONFIG" | base64 -d > ~/.kube/config
else
echo "$NOPROD_KUBECONFIG" | base64 -d > ~/.kube/config
fi
chmod 600 ~/.kube/config && export KUBECONFIG=~/.kube/config
Version Tagging Strategy
Different environments use different versioning strategies:
- develop:
{CI_COMMIT_REF_NAME}-{CI_PIPELINE_ID}(e.g.,main-12345) - staging:
{RELEASE}.rc{CI_PIPELINE_IID}(e.g.,1.0.0.rc42) where RELEASE is extracted from branch name - production:
{RELEASE}(e.g.,1.0.0) where RELEASE is extracted from branch name
Storage File System IDs
Different environments use different EFS file system IDs:
- develop:
fs-0e7fcf2d3c61da878 - staging:
fs-0f2d81976ef2bf737 - production:
fs-0c98ff7929df55f36
Job Types
.deploy-craft-k8s
Deploys Craft CMS applications to Kubernetes.
Usage
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
STORAGE_USER_ID: '1000'
deploy-craft-dev:
extends: .deploy-craft-k8s
stage: deploy
only:
- develop
Helm Chart Configuration
- Chart:
welance-charts/craft - Namespace:
{PROJECT_ID}-{APP_ENVIRONMENT} - Environment file:
.env.{APP_ENVIRONMENT}(e.g.,.env.dev,.env.staging,.env.production) - Values file:
./helm-chart/values-{APP_ENVIRONMENT}.yaml
Common Helm Values Set
All environments set:
- Image tag, commit SHA, branch, and URL
- Security context (runAsUser, runAsGroup, fsGroup)
- Storage configuration (gid, uid, fid)
- Ingress configuration with Cloudflare TLS
- Service configuration
.deploy-wordpress-k8s
Deploys WordPress applications to Kubernetes.
Usage
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
STORAGE_USER_ID: '1000'
deploy-wordpress-dev:
extends: .deploy-wordpress-k8s
stage: deploy
only:
- develop
Differences from Craft
- Uses
welance-charts/wordpresschart - Same configuration structure as Craft
.deploy-directus-k8s
Deploys Directus CMS applications to Kubernetes.
Usage
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
STORAGE_USER_ID: '1000'
deploy-directus-dev:
extends: .deploy-directus-k8s
stage: deploy
only:
- develop
Differences from Craft
- Uses
welance-charts/directuschart - Same configuration structure as Craft
.deploy-node-k8s
Deploys Node.js applications to Kubernetes.
Usage
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
deploy-node-dev:
extends: .deploy-node-k8s
stage: deploy
only:
- develop
Differences from Craft
- Uses
welance-charts/nodechart - Does not set security context or storage configuration (simpler configuration)
- Same ingress and service configuration
.deploy-s3-k8s
Deploys S3 static website proxies to Kubernetes.
Usage
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
deploy-s3-dev:
extends: .deploy-s3-k8s
stage: deploy
only:
- develop
Differences from Craft
- Uses
welance-charts/s3chart - Does not set image tag, commit info, or security context
- Service port is 80 (instead of 8080)
- Service name is
web(instead ofservice-web) - Simpler configuration focused on ingress proxy
.deploy-operator-k8s
Deploys Welance Operator to Kubernetes.
Usage
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
deploy-operator-dev:
extends: .deploy-operator-k8s
stage: deploy
only:
- develop
Differences from Craft
- Uses
welance-charts/welance-operatorchart - Minimal configuration (only image tag and repository)
- No ingress, storage, or security context configuration
- Only sets environment file for develop environment
Environment-Specific Configuration
Develop Environment
Version: {CI_COMMIT_REF_NAME}-{CI_PIPELINE_ID}
Hostname: {PROJECT_ID}.dev.welance.com
Storage FID: fs-0e7fcf2d3c61da878
Example:
APP_ENVIRONMENT: 'develop'
# Results in:
# - Namespace: p222-01-develop
# - Hostname: p222-01.dev.welance.com
# - Version: main-12345
Staging Environment
Version: {RELEASE}.rc{CI_PIPELINE_IID} (e.g., 1.0.0.rc42)
Hostname: {PROJECT_ID}.staging.welance.com
Storage FID: fs-0f2d81976ef2bf737
Example:
APP_ENVIRONMENT: 'staging'
CI_COMMIT_REF_NAME: 'release/1.0.0'
CI_PIPELINE_IID: '42'
# Results in:
# - Namespace: p222-01-staging
# - Hostname: p222-01.staging.welance.com
# - Version: 1.0.0.rc42
Production Environment
Version: {RELEASE} (e.g., 1.0.0)
Hostname: {PROJECT_ID}.production.welance.com
Storage FID: fs-0c98ff7929df55f36
Example:
APP_ENVIRONMENT: 'production'
CI_COMMIT_REF_NAME: 'release/1.0.0'
# Results in:
# - Namespace: p222-01-production
# - Hostname: p222-01.production.welance.com
# - Version: 1.0.0
Common Helm Values
All application deployments (except S3 and Operator) set:
Image Configuration
image.tag: Version tagimage.repository:$CI_REGISTRY_IMAGEimage.commit.sha:$CI_COMMIT_SHAimage.commit.branch:$CI_COMMIT_REF_NAMEimage.commit.url:$CI_PROJECT_URL/-/commit/$CI_COMMIT_SHA
Security Context
deployment.securityContext.runAsUser:$STORAGE_USER_IDdeployment.securityContext.runAsGroup:$STORAGE_USER_IDdeployment.securityContext.fsGroup:$STORAGE_USER_ID
Storage Configuration
storage.gid:$STORAGE_USER_IDstorage.uid:$STORAGE_USER_IDstorage.fid: Environment-specific file system ID
Ingress Configuration
ingresses.web.hosts[0].host:{PROJECT_ID}.{environment}.welance.comingresses.web.tls[0].hosts[0]: Same as hostingresses.web.tls[0].secretName:cloudflare-tlsingresses.web.hosts[0].paths[0].path:/ingresses.web.hosts[0].paths[0].pathType:Prefixingresses.web.hosts[0].paths[0].servicePort:8080(or80for S3)ingresses.web.hosts[0].paths[0].serviceName:service-web(orwebfor S3)
Environment File
--set-file envFileContent=.env.{APP_ENVIRONMENT}: Loads environment-specific.envfile
Complete Pipeline Example
variables:
PROJECT_ID: 'p222-01'
APP_ENVIRONMENT: 'develop'
STORAGE_USER_ID: '1000'
stages:
- build
- deploy
# Build job creates .env files
build:
stage: build
script:
- echo "DB_HOST=db.example.com" > .env.dev
- echo "DB_HOST=db.staging.example.com" > .env.staging
- echo "DB_HOST=db.prod.example.com" > .env.production
artifacts:
paths:
- .env.dev
- .env.staging
- .env.production
- helm-chart/
.deploy-craft-k8s:
image: python:latest
before_script:
- curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && chmod 700 get_helm.sh && ./get_helm.sh
- pip install awscli
- curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
- chmod +x ./kubectl && mv ./kubectl /usr/local/bin
- wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq
- mkdir -p ~/.kube
- helm repo add welance-charts https://welance.gitlab.io/platform/pipelines/helm/welance-charts/
- helm repo update
- if [ ${APP_ENVIRONMENT} = 'production' ]; then echo "$PROD_KUBECONFIG" | base64 -d > ~/.kube/config; else echo "$NOPROD_KUBECONFIG" | base64 -d > ~/.kube/config; fi
- chmod 600 ~/.kube/config && export KUBECONFIG=~/.kube/config
script:
- |-
if [[ $APP_ENVIRONMENT = 'develop' ]]; then
export VERSION=$CI_COMMIT_REF_NAME-$CI_PIPELINE_ID
helm upgrade --install --create-namespace -n $PROJECT_ID-$APP_ENVIRONMENT \
--set image.tag=$VERSION \
--set image.commit.sha="$CI_COMMIT_SHA" \
--set image.commit.branch="$CI_COMMIT_REF_NAME" \
--set-string image.commit.url="$CI_PROJECT_URL/-/commit/$CI_COMMIT_SHA" \
--set deployment.securityContext.runAsUser=$STORAGE_USER_ID \
--set deployment.securityContext.runAsGroup=$STORAGE_USER_ID \
--set deployment.securityContext.fsGroup=$STORAGE_USER_ID \
--set storage.gid=$STORAGE_USER_ID \
--set storage.uid=$STORAGE_USER_ID \
--set storage.fid=fs-0e7fcf2d3c61da878 \
--set ingresses.web.hosts[0].host="${PROJECT_ID}.dev.welance.com" \
--set ingresses.web.tls[0].hosts[0]="${PROJECT_ID}.dev.welance.com" \
--set ingresses.web.tls[0].secretName=cloudflare-tls \
--set image.repository=$CI_REGISTRY_IMAGE \
--set ingresses.web.hosts[0].paths[0].path=/ \
--set ingresses.web.hosts[0].paths[0].pathType=Prefix \
--set ingresses.web.hosts[0].paths[0].servicePort=8080 \
--set ingresses.web.hosts[0].paths[0].serviceName=service-web \
$PROJECT_ID-$APP_ENVIRONMENT welance-charts/craft \
--set-file envFileContent=.env.dev \
-f ./helm-chart/values-$APP_ENVIRONMENT.yaml
fi
dependencies:
- build
deploy-craft-dev:
extends: .deploy-craft-k8s
stage: deploy
only:
- develop
Prerequisites
- Kubernetes cluster: Accessible Kubernetes cluster (production or non-production)
- Kubeconfig: Base64-encoded kubeconfig files stored as CI/CD variables
- Helm charts repository: Welance charts repository must be accessible
- Environment files:
.env.{environment}files must exist for the target environment - Values files:
helm-chart/values-{environment}.yamlfiles must exist - Container images: Images must be built and pushed to the container registry
- Cloudflare TLS: TLS secret
cloudflare-tlsmust exist in the cluster - EFS file systems: EFS file systems must be configured and accessible
Security Considerations
- Kubeconfig: Store
NOPROD_KUBECONFIGandPROD_KUBECONFIGas masked CI/CD variables - Namespace isolation: Each project and environment gets its own namespace
- Storage permissions: Storage user IDs should be configured appropriately
- TLS certificates: Cloudflare TLS certificates are managed separately
- Image registry: Ensure container registry access is properly configured
Notes
- The job uses
helm templateto validate before actual deployment (echoed command) - Namespaces are created automatically with
--create-namespace - Different storage file system IDs are used per environment
- Version tagging differs by environment (develop uses branch-pipeline, staging uses release-rc, production uses release)
- Ingress hostnames follow the pattern:
{PROJECT_ID}.{environment}.welance.com - All jobs use Cloudflare TLS certificates via the
cloudflare-tlssecret - The Python image is used as a base for tool installation
- Helm charts are pulled from the centralized Welance charts repository
- Environment-specific values files allow customization per environment