From e47150f80aa5e1900244dd5df5c5137ac5b6a10d Mon Sep 17 00:00:00 2001 From: William Valentin Date: Sun, 7 Sep 2025 20:28:23 -0700 Subject: [PATCH] feat: Add container registry support and Kustomize foundation - Add registry secret template for private container registry authentication - Fix frontend deployment to use imagePullSecrets for private registry - Enhance deploy-k8s.sh with registry authentication handling - Add PVC storage size validation to prevent storage reduction errors - Add graceful StatefulSet update error handling - Fix template variable substitution for DOCKER_IMAGE - Remove conflicting static PVC file that had unprocessed template variables - Add Kustomize structure as alternative to shell script templates: - Base configuration with common resources - Development overlay with dev-specific configurations - Support for environment-specific image tags and resource limits Registry setup requires setting REGISTRY_USERNAME, REGISTRY_PASSWORD, and optionally REGISTRY_HOST in .env file for private registry authentication. --- k8s-kustomize/base/frontend-deployment.yaml | 45 +++++++++ k8s-kustomize/base/kustomization.yaml | 60 ++++++++++++ k8s-kustomize/overlays/dev/kustomization.yaml | 83 ++++++++++++++++ k8s/couchdb-pvc.yaml | 14 --- k8s/frontend-deployment.yaml.template | 4 +- k8s/registry-secret.yaml.template | 10 ++ scripts/deploy-k8s.sh | 97 ++++++++++++++++++- 7 files changed, 296 insertions(+), 17 deletions(-) create mode 100644 k8s-kustomize/base/frontend-deployment.yaml create mode 100644 k8s-kustomize/base/kustomization.yaml create mode 100644 k8s-kustomize/overlays/dev/kustomization.yaml delete mode 100644 k8s/couchdb-pvc.yaml create mode 100644 k8s/registry-secret.yaml.template diff --git a/k8s-kustomize/base/frontend-deployment.yaml b/k8s-kustomize/base/frontend-deployment.yaml new file mode 100644 index 0000000..27625cd --- /dev/null +++ b/k8s-kustomize/base/frontend-deployment.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rxminder-frontend + labels: + component: frontend +spec: + replicas: 1 + selector: + matchLabels: + component: frontend + template: + metadata: + labels: + component: frontend + spec: + imagePullSecrets: + - name: rxminder-registry-secret + containers: + - name: frontend + image: frontend-image + ports: + - containerPort: 80 + envFrom: + - configMapRef: + name: rxminder-config + resources: + requests: + memory: '32Mi' + cpu: '20m' + limits: + memory: '64Mi' + cpu: '40m' + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 30 + periodSeconds: 30 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 5 diff --git a/k8s-kustomize/base/kustomization.yaml b/k8s-kustomize/base/kustomization.yaml new file mode 100644 index 0000000..5c22108 --- /dev/null +++ b/k8s-kustomize/base/kustomization.yaml @@ -0,0 +1,60 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: rxminder-base + +namespace: rxminder + +resources: + - frontend-deployment.yaml + - frontend-service.yaml + - couchdb-statefulset.yaml + - couchdb-service.yaml + - couchdb-pvc.yaml + - ingress.yaml + - configmap.yaml + - network-policy.yaml + - hpa.yaml + - db-seed-job.yaml + +# Common labels applied to all resources +commonLabels: + app: rxminder + version: v1.0.0 + +# Generate ConfigMap from environment files +configMapGenerator: + - name: rxminder-config + envs: + - config.env + +# Generate Secret for CouchDB +secretGenerator: + - name: couchdb-secret + literals: + - username=admin + - password=changeme + type: Opaque + + # Generate registry secret from credentials + - name: rxminder-registry-secret + files: + - .dockerconfigjson=registry-config.json + type: kubernetes.io/dockerconfigjson + +# Images to be used (can be overridden in overlays) +images: + - name: frontend-image + newName: gitea-http.taildb3494.ts.net/will/rxminder + newTag: latest + - name: couchdb-image + newName: couchdb + newTag: 3.3.2 + +# Replicas (can be overridden in overlays) +replicas: + - name: rxminder-frontend + count: 1 + - name: rxminder-couchdb + count: 1 diff --git a/k8s-kustomize/overlays/dev/kustomization.yaml b/k8s-kustomize/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..73b72b4 --- /dev/null +++ b/k8s-kustomize/overlays/dev/kustomization.yaml @@ -0,0 +1,83 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: rxminder-dev + +namespace: rxminder-dev + +resources: + - ../../base + +# Development-specific labels +commonLabels: + environment: dev + +# Override images for development +images: + - name: frontend-image + newName: gitea-http.taildb3494.ts.net/will/rxminder + newTag: dev + - name: couchdb-image + newName: couchdb + newTag: 3.3.2 + +# Development replicas (lower for resource conservation) +replicas: + - name: rxminder-frontend + count: 1 + +# Development-specific patches +patches: + - target: + kind: Deployment + name: rxminder-frontend + patch: |- + - op: replace + path: /spec/template/spec/containers/0/resources/requests/memory + value: "16Mi" + - op: replace + path: /spec/template/spec/containers/0/resources/limits/memory + value: "32Mi" + - op: add + path: /spec/template/spec/containers/0/env + value: + - name: NODE_ENV + value: "development" + - name: LOG_LEVEL + value: "debug" + + - target: + kind: Ingress + name: rxminder-ingress + patch: |- + - op: replace + path: /spec/rules/0/host + value: "rxminder-dev.local" + + - target: + kind: PersistentVolumeClaim + name: rxminder-couchdb-pvc + patch: |- + - op: replace + path: /spec/resources/requests/storage + value: "1Gi" + +# Development-specific ConfigMap +configMapGenerator: + - name: rxminder-config + literals: + - NODE_ENV=development + - API_URL=http://rxminder-couchdb-service:5984 + - LOG_LEVEL=debug + - DEBUG=true + behavior: replace + +# Development secrets (use weak passwords for dev) +secretGenerator: + - name: couchdb-secret + literals: + - username=admin + - password=devpass123 + type: Opaque + behavior: replace diff --git a/k8s/couchdb-pvc.yaml b/k8s/couchdb-pvc.yaml deleted file mode 100644 index 6df65ac..0000000 --- a/k8s/couchdb-pvc.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ${APP_NAME}-couchdb-pvc - labels: - app: ${APP_NAME} - component: database -spec: - accessModes: - - ReadWriteOnce - storageClassName: ${STORAGE_CLASS} - resources: - requests: - storage: ${STORAGE_SIZE} diff --git a/k8s/frontend-deployment.yaml.template b/k8s/frontend-deployment.yaml.template index 5983f90..a6635c3 100644 --- a/k8s/frontend-deployment.yaml.template +++ b/k8s/frontend-deployment.yaml.template @@ -17,9 +17,11 @@ spec: app: ${APP_NAME} component: frontend spec: + imagePullSecrets: + - name: ${APP_NAME}-registry-secret containers: - name: frontend - image: ${DOCKER_IMAGE:-gitea-http.taildb3494.ts.net/will/${APP_NAME}:latest} + image: ${DOCKER_IMAGE} ports: - containerPort: 80 envFrom: diff --git a/k8s/registry-secret.yaml.template b/k8s/registry-secret.yaml.template new file mode 100644 index 0000000..c5aed16 --- /dev/null +++ b/k8s/registry-secret.yaml.template @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ${APP_NAME}-registry-secret + labels: + app: ${APP_NAME} + component: registry +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: ${REGISTRY_AUTH_BASE64} diff --git a/scripts/deploy-k8s.sh b/scripts/deploy-k8s.sh index 50e17ca..e3d28a8 100755 --- a/scripts/deploy-k8s.sh +++ b/scripts/deploy-k8s.sh @@ -2,6 +2,17 @@ # Kubernetes deployment script with environment variable substitution # This script processes template files and applies them to Kubernetes +# +# Registry Authentication Setup: +# To pull images from a private registry, set these environment variables: +# REGISTRY_USERNAME - Username for the container registry +# REGISTRY_PASSWORD - Password/token for the container registry +# REGISTRY_HOST - Registry hostname (default: gitea-http.taildb3494.ts.net) +# +# Example in .env file: +# REGISTRY_USERNAME=your-username +# REGISTRY_PASSWORD=your-password-or-token +# REGISTRY_HOST=gitea-http.taildb3494.ts.net set -euo pipefail @@ -72,6 +83,20 @@ substitute_templates() { done } +# Function to create registry authentication +create_registry_auth() { + if [[ -n "${REGISTRY_USERNAME:-}" && -n "${REGISTRY_PASSWORD:-}" ]]; then + local registry_host="${REGISTRY_HOST:-gitea-http.taildb3494.ts.net}" + local auth_string=$(echo -n "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD}" | base64 -w 0) + local docker_config="{\"auths\":{\"${registry_host}\":{\"auth\":\"${auth_string}\"}}}" + export REGISTRY_AUTH_BASE64=$(echo -n "$docker_config" | base64 -w 0) + print_info "Registry authentication configured for $registry_host" + else + print_warning "Registry credentials not provided - skipping registry secret creation" + export REGISTRY_AUTH_BASE64="" + fi +} + # Function to validate required environment variables validate_env() { local required_vars=("INGRESS_HOST") @@ -105,6 +130,44 @@ ensure_namespace() { fi } +# Function to convert Kubernetes storage units to bytes +storage_to_bytes() { + local storage="$1" + local number=$(echo "$storage" | sed 's/[^0-9]*//g') + local unit=$(echo "$storage" | sed 's/[0-9]*//g') + + case "$unit" in + "Ki"|"K") echo $((number * 1024)) ;; + "Mi"|"M") echo $((number * 1024 * 1024)) ;; + "Gi"|"G") echo $((number * 1024 * 1024 * 1024)) ;; + "Ti"|"T") echo $((number * 1024 * 1024 * 1024 * 1024)) ;; + "") echo "$number" ;; + *) echo "0" ;; + esac +} + +# Function to check if PVC storage can be updated +can_update_pvc_storage() { + local pvc_file="$1" + local pvc_name=$(grep "name:" "$pvc_file" | head -1 | awk '{print $2}') + local new_storage=$(grep "storage:" "$pvc_file" | awk '{print $2}') + + # Check if PVC exists + if kubectl get pvc "$pvc_name" -n "$NAMESPACE" &> /dev/null; then + local current_storage=$(kubectl get pvc "$pvc_name" -n "$NAMESPACE" -o jsonpath='{.status.capacity.storage}') + + # Convert storage sizes to bytes for comparison + local current_bytes=$(storage_to_bytes "$current_storage") + local new_bytes=$(storage_to_bytes "$new_storage") + + if [[ "$new_bytes" -lt "$current_bytes" ]]; then + print_warning "Skipping PVC $pvc_name: cannot reduce storage from $current_storage to $new_storage" + return 1 + fi + fi + return 0 +} + # Function to apply Kubernetes manifests apply_manifests() { local manifest_dir="$1" @@ -129,8 +192,29 @@ apply_manifests() { if [[ -d "$TEMP_DIR" ]]; then for manifest_file in "$TEMP_DIR"/*.yaml; do if [[ -f "$manifest_file" ]]; then - print_info "Applying template: $(basename "$manifest_file")" - kubectl apply -f "$manifest_file" -n "$NAMESPACE" + local basename_file=$(basename "$manifest_file") + + # Special handling for PVC files + if [[ "$basename_file" == *"pvc.yaml" ]] && grep -q "kind: PersistentVolumeClaim" "$manifest_file"; then + if can_update_pvc_storage "$manifest_file"; then + print_info "Applying template: $basename_file" + kubectl apply -f "$manifest_file" -n "$NAMESPACE" + fi + # Special handling for registry secret - skip if no auth provided + elif [[ "$basename_file" == *"registry-secret.yaml" ]] && [[ -z "${REGISTRY_AUTH_BASE64:-}" ]]; then + print_info "Skipping registry secret: no registry credentials provided" + else + print_info "Applying template: $basename_file" + if ! kubectl apply -f "$manifest_file" -n "$NAMESPACE" 2>/dev/null; then + # Handle StatefulSet update failures gracefully + if [[ "$basename_file" == *"statefulset.yaml" ]] && grep -q "kind: StatefulSet" "$manifest_file"; then + print_warning "StatefulSet update failed (likely due to immutable fields) - continuing deployment" + else + # Re-run the command to show the actual error for non-StatefulSet resources + kubectl apply -f "$manifest_file" -n "$NAMESPACE" + fi + fi + fi fi done fi @@ -255,9 +339,18 @@ main() { # Load environment variables load_env "$env_file" + # Set default values for required variables + export APP_NAME="${APP_NAME:-rxminder}" + export STORAGE_CLASS="${STORAGE_CLASS:-longhorn}" + export STORAGE_SIZE="${STORAGE_SIZE:-5Gi}" + export DOCKER_IMAGE="${DOCKER_IMAGE:-gitea-http.taildb3494.ts.net/will/rxminder:latest}" + # Set default namespace if not provided in environment NAMESPACE="${NAMESPACE:-rxminder}" + # Create registry authentication if credentials are provided + create_registry_auth + # Validate required environment variables validate_env