Files
k_deploy_workflows/CD-README.md
Timo Behrendt 1caaef9f83 sync
2025-08-12 08:54:44 +02:00

7.9 KiB

Reusable CD Workflow for Kubernetes Services

This directory contains a reusable CD (Continuous Deployment) workflow that automatically detects and deploys your Kubernetes services, whether they use Helm + Kubernetes or just Kubernetes manifests, with flexible secret management.

Features

  • Automatic Detection: Automatically detects if your service uses Helm (helmfile.yaml) or just Kubernetes manifests
  • Conditional Deployment: Only runs Helm deployment when helmfile.yaml exists
  • Flexible Paths: Configurable paths for k8s directory and helmfile
  • Smart Secret Management: Flexible JSON-based secret creation system
  • Branch Flexibility: Deploy from any branch (defaults to main)
  • Comprehensive Summary: Provides a clear summary of what was deployed

Usage

Simply call the workflow without any parameters - it will automatically detect your service type:

jobs:
  deploy:
    uses: ./.gitea/workflows/cd.yaml

With Custom Secrets

For services that need custom secrets (like your original example):

jobs:
  deploy:
    uses: ./.gitea/workflows/cd.yaml
    with:
      custom_secrets: |
        [
          {
            "name": "oidc",
            "type": "generic",
            "data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}"
          },
          {
            "name": "root-user",
            "type": "generic", 
            "data": "{\"rootUser\": \"${{ secrets.MINIO_ROOT_USER }}\", \"rootPassword\": \"${{ secrets.MINIO_ROOT_PASSWORD }}\"}"
          }
        ]

Advanced Usage with Custom Paths

If your service uses non-standard directory names:

jobs:
  deploy:
    uses: ./.gitea/workflows/cd.yaml
    with:
      k8s_dir: 'kubernetes/'
      helmfile_path: 'helm/helmfile.yaml'
      custom_secrets: |
        [
          {
            "name": "database-credentials",
            "type": "generic",
            "data": "{\"username\": \"${{ secrets.DB_USERNAME }}\", \"password\": \"${{ secrets.DB_PASSWORD }}\"}"
          }
        ]

Deploy from Different Branch

Deploy from a staging or feature branch:

jobs:
  deploy-staging:
    uses: ./.gitea/workflows/cd.yaml
    with:
      deploy_branch: 'staging'
      custom_secrets: |
        [
          {
            "name": "staging-config",
            "type": "generic",
            "data": "{\"environment\": \"staging\", \"debug\": \"true\"}"
          }
        ]

Force Skip Helm Deployment

If you want to skip Helm deployment even when helmfile.yaml exists:

jobs:
  deploy:
    uses: ./.gitea/workflows/cd.yaml
    with:
      skip_helm_deployment: true
      custom_secrets: |
        [
          {
            "name": "app-config",
            "type": "generic",
            "data": "{\"apiUrl\": \"${{ secrets.API_URL }}\", \"apiKey\": \"${{ secrets.API_KEY }}\"}"
          }
        ]

Input Parameters

Parameter Description Default Required
k8s_dir Path to Kubernetes manifests directory k8s/ No
helmfile_path Path to helmfile.yaml helmfile.yaml No
skip_helm_deployment Skip Helm deployment even if helmfile exists false No
custom_secrets JSON array of secrets to create [] No
deploy_branch Branch to deploy from main No

Custom Secrets Format

The custom_secrets parameter accepts a JSON array where each secret object has:

{
  "name": "secret-name",
  "type": "secret-type", 
  "data": "{\"key1\": \"value1\", \"key2\": \"value2\"}"
}

Secret Object Properties

  • name (required): The name of the Kubernetes secret
  • type (optional): The type of Kubernetes secret (defaults to "generic")
  • data (required): JSON string containing the secret data

Supported Secret Types

  • generic: Generic secret (most common)
  • docker-registry: For Docker registry credentials
  • tls: For TLS certificates
  • ssh-auth: For SSH authentication
  • basic-auth: For basic authentication

Example Secret Configurations

OIDC Configuration

{
  "name": "oidc",
  "type": "generic",
  "data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}"
}

Database Credentials

{
  "name": "database-credentials",
  "type": "generic",
  "data": "{\"username\": \"${{ secrets.DB_USERNAME }}\", \"password\": \"${{ secrets.DB_PASSWORD }}\"}"
}

Docker Registry

{
  "name": "docker-registry",
  "type": "docker-registry",
  "data": "{\"username\": \"${{ secrets.DOCKER_USERNAME }}\", \"password\": \"${{ secrets.DOCKER_PASSWORD }}\"}"
}

Directory Structure Requirements

For Kubernetes-only services:

your-service/
├── k8s/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── ...
└── .gitea/workflows/your-workflow.yaml

For Helm + Kubernetes services:

your-service/
├── k8s/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── ...
├── helmfile.yaml
└── .gitea/workflows/your-workflow.yaml

What Gets Deployed

Always (if k8s/ directory exists):

  • Custom secrets creation (if specified)
  • Kubernetes manifest deployment using kubectl apply
  • Namespace extraction from repository name

Conditionally (if helmfile.yaml exists and Helm deployment not skipped):

  • Custom secrets creation (if specified)
  • Helm chart deployment using helmfile apply
  • Kubernetes manifests in Helm context

Migration from Your Original Workflow

Your original workflow had these hardcoded secrets:

  • oidc secret with OIDC_CLIENT_ID and OIDC_CLIENT_SECRET
  • root-user secret with MINIO_ROOT_USER and MINIO_ROOT_PASSWORD

To replicate this in the new workflow:

jobs:
  deploy:
    uses: ./.gitea/workflows/cd.yaml
    with:
      custom_secrets: |
        [
          {
            "name": "oidc",
            "type": "generic",
            "data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}"
          },
          {
            "name": "root-user",
            "type": "generic", 
            "data": "{\"rootUser\": \"${{ secrets.MINIO_ROOT_USER }}\", \"rootPassword\": \"${{ secrets.MINIO_ROOT_PASSWORD }}\"}"
          }
        ]

Example Workflows

See example-cd-usage.yaml for complete examples of how to use this workflow in different scenarios.

Dependencies

This workflow requires:

  • ./.gitea/actions/extract-namespace-from-repo-name action
  • KUBECONFIG secret configured in your repository
  • Access to your Kubernetes cluster
  • jq for JSON parsing (included in ubuntu-latest runner)

Troubleshooting

Helm deployment skipped unexpectedly

  • Check if helmfile.yaml exists in the expected location
  • Verify the skip_helm_deployment parameter is not set to true
  • Ensure the file path is correct if using custom paths

Kubernetes deployment skipped

  • Verify the k8s/ directory (or custom path) exists
  • Check the directory contains valid Kubernetes manifests

Secret creation failures

  • Verify the JSON format is valid
  • Check that secret names are valid Kubernetes resource names
  • Ensure the data field contains valid JSON string

Permission issues

  • Ensure the KUBECONFIG secret is properly configured
  • Verify the workflow has access to your Kubernetes cluster
  • Check that the namespace exists and is accessible

Security Considerations

  • Secret Data: The data field in custom_secrets should contain the actual secret values, not references to GitHub secrets
  • Repository Secrets: Store sensitive values in GitHub repository secrets and reference them in the workflow
  • Access Control: Ensure only authorized users can trigger deployments
  • Audit Trail: All deployments are logged and visible in the GitHub Actions history