From 7d6845574cdc26a36db783e4b576871265d37ae4 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Mon, 19 Jan 2026 16:31:11 +0100 Subject: [PATCH] Add new infra architecture 6 --- Tiltfile | 253 ++++++++++++++++- docs/PILOT_LAUNCH_GUIDE.md | 215 ++++++++++++++ gateway/Dockerfile | 26 +- .../tekton-helm/templates/pipeline-ci.yaml | 17 +- .../templates/task-kaniko-build.yaml | 46 ++- infrastructure/cicd/tekton-helm/values.yaml | 5 + .../dev/k8s-manifests/kustomization.yaml | 31 +- .../prod/k8s-manifests/kustomization.yaml | 8 +- .../gateway-service.yaml | 0 .../kustomization.yaml | 3 - .../infrastructure/nominatim/nominatim.yaml | 158 ----------- .../infrastructure/unbound/unbound.yaml | 81 ------ .../configs/coredns-unbound-patch.yaml | 38 +++ .../configs/mailu-certificates-secret.yaml | 26 ++ .../platform/mail/mailu-helm/dev/values.yaml | 25 +- .../platform/mail/mailu-helm/prod/values.yaml | 22 +- .../mailu-helm/scripts/deploy-mailu-prod.sh | 260 +++++++++++++++++ .../platform/mail/mailu-helm/values.yaml | 49 ++-- .../networking/dns/unbound-helm/Chart.yaml | 18 ++ .../dns/unbound-helm/dev/values.yaml | 36 +++ .../dns/unbound-helm/prod/values.yaml | 50 ++++ .../dns/unbound-helm/templates/_helpers.tpl | 63 +++++ .../unbound-helm/templates/deployment.yaml | 95 +++++++ .../dns/unbound-helm/templates/service.yaml | 24 ++ .../templates/serviceaccount.yaml | 13 + .../networking/dns/unbound-helm/values.yaml | 95 +++++++ .../nominatim/nominatim-helm/Chart.yaml | 19 ++ .../nominatim/nominatim-helm/dev/values.yaml | 38 +++ .../nominatim/nominatim-helm/prod/values.yaml | 45 +++ .../nominatim-helm/templates/_helpers.tpl | 87 ++++++ .../nominatim-helm/templates/configmap.yaml | 13 + .../nominatim-helm/templates/init-job.yaml} | 35 ++- .../nominatim-helm/templates/pvc.yaml | 37 +++ .../nominatim-helm/templates/service.yaml | 20 ++ .../nominatim-helm/templates/statefulset.yaml | 113 ++++++++ .../nominatim/nominatim-helm/values.yaml | 113 ++++++++ privkey.pem | 28 ++ scripts/prepull-base-images.sh | 2 - services/ai_insights/Dockerfile | 20 +- services/alert_processor/Dockerfile | 20 +- services/auth/Dockerfile | 20 +- services/demo_session/Dockerfile | 20 +- services/distribution/Dockerfile | 20 +- services/external/Dockerfile | 20 +- services/forecasting/Dockerfile | 20 +- services/inventory/Dockerfile | 20 +- services/notification/Dockerfile | 20 +- services/orchestrator/Dockerfile | 20 +- services/orders/Dockerfile | 20 +- services/pos/Dockerfile | 20 +- services/procurement/Dockerfile | 20 +- services/production/Dockerfile | 20 +- services/recipes/Dockerfile | 20 +- services/sales/Dockerfile | 20 +- services/suppliers/Dockerfile | 20 +- services/tenant/Dockerfile | 20 +- services/training/Dockerfile | 20 +- skaffold.yaml | 265 ++++++++++++++++-- 58 files changed, 2360 insertions(+), 492 deletions(-) rename infrastructure/platform/{infrastructure => gateway}/gateway-service.yaml (100%) rename infrastructure/platform/{infrastructure => gateway}/kustomization.yaml (52%) delete mode 100644 infrastructure/platform/infrastructure/nominatim/nominatim.yaml delete mode 100644 infrastructure/platform/infrastructure/unbound/unbound.yaml create mode 100644 infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml create mode 100644 infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml create mode 100755 infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh create mode 100644 infrastructure/platform/networking/dns/unbound-helm/Chart.yaml create mode 100644 infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml create mode 100644 infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml create mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl create mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml create mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml create mode 100644 infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml create mode 100644 infrastructure/platform/networking/dns/unbound-helm/values.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/Chart.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/dev/values.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/prod/values.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/templates/_helpers.tpl create mode 100644 infrastructure/platform/nominatim/nominatim-helm/templates/configmap.yaml rename infrastructure/platform/{infrastructure/nominatim/nominatim-init-job.yaml => nominatim/nominatim-helm/templates/init-job.yaml} (67%) create mode 100644 infrastructure/platform/nominatim/nominatim-helm/templates/pvc.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/templates/service.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/templates/statefulset.yaml create mode 100644 infrastructure/platform/nominatim/nominatim-helm/values.yaml create mode 100644 privkey.pem diff --git a/Tiltfile b/Tiltfile index 8b812a0f..53768fb7 100644 --- a/Tiltfile +++ b/Tiltfile @@ -186,21 +186,40 @@ dockerhub_username = 'uals' # Default username if 'DOCKERHUB_USERNAME' in os.environ: dockerhub_username = os.environ['DOCKERHUB_USERNAME'] +# Base image registry configuration for Dockerfile ARGs +# This controls where the base Python image is pulled from during builds +base_registry = 'localhost:5000' # Default for local dev +python_image = 'python_3.11-slim' # Local registry uses underscores + +if 'BASE_REGISTRY' in os.environ: + base_registry = os.environ['BASE_REGISTRY'] +if 'PYTHON_IMAGE' in os.environ: + python_image = os.environ['PYTHON_IMAGE'] + +# For Docker Hub mode, use canonical image names +if use_dockerhub: + base_registry = 'docker.io' + python_image = 'python:3.11-slim' + if use_dockerhub: print(""" DOCKER HUB MODE ENABLED Images will be pushed to Docker Hub: docker.io/%s + Base images will be pulled from: %s/%s Make sure you're logged in: docker login To disable: unset USE_DOCKERHUB or set USE_DOCKERHUB=false - """ % dockerhub_username) + """ % (dockerhub_username, base_registry, python_image)) default_registry('docker.io/%s' % dockerhub_username) else: print(""" LOCAL REGISTRY MODE Using local registry for faster builds: localhost:5001 + Base images will be pulled from: %s/%s This registry is created by kubernetes_restart.sh script To use Docker Hub: export USE_DOCKERHUB=true - """) + To change base registry: export BASE_REGISTRY= + To change Python image: export PYTHON_IMAGE= + """ % (base_registry, python_image)) default_registry('localhost:5001') # ============================================================================= @@ -301,6 +320,11 @@ def build_python_service(service_name, service_path): 'bakery/' + service_name, context='.', dockerfile='./services/' + service_path + '/Dockerfile', + # Build arguments for environment-configurable base images + build_args={ + 'BASE_REGISTRY': base_registry, + 'PYTHON_IMAGE': python_image, + }, # Only watch files relevant to this specific service + shared code only=[ './services/' + service_path, @@ -398,6 +422,11 @@ docker_build( 'bakery/gateway', context='.', dockerfile='./gateway/Dockerfile', + # Build arguments for environment-configurable base images + build_args={ + 'BASE_REGISTRY': base_registry, + 'PYTHON_IMAGE': python_image, + }, # Only watch gateway-specific files and shared code only=[ './gateway', @@ -474,14 +503,68 @@ k8s_image_json_path( # Redis & RabbitMQ k8s_resource('redis', resource_deps=['security-setup'], labels=['01-infrastructure']) k8s_resource('rabbitmq', resource_deps=['security-setup'], labels=['01-infrastructure']) -k8s_resource('nominatim', labels=['01-infrastructure']) # MinIO Storage k8s_resource('minio', resource_deps=['security-setup'], labels=['01-infrastructure']) k8s_resource('minio-bucket-init', resource_deps=['minio'], labels=['01-infrastructure']) # Unbound DNSSEC Resolver - Infrastructure component for Mailu DNS validation -k8s_resource('unbound-resolver', resource_deps=['security-setup'], labels=['01-infrastructure']) +local_resource( + 'unbound-helm', + cmd=''' + echo "Deploying Unbound DNS resolver via Helm..." + echo "" + + # Check if Unbound is already deployed + if helm list -n bakery-ia | grep -q unbound; then + echo "Unbound already deployed, checking status..." + helm status unbound -n bakery-ia + else + echo "Installing Unbound..." + + # Determine environment (dev or prod) based on context + ENVIRONMENT="dev" + if [[ "$(kubectl config current-context)" == *"prod"* ]]; then + ENVIRONMENT="prod" + fi + + echo "Environment detected: $ENVIRONMENT" + + # Install Unbound with appropriate values + if [ "$ENVIRONMENT" = "dev" ]; then + helm upgrade --install unbound infrastructure/platform/infrastructure/unbound-helm \ + -n bakery-ia \ + --create-namespace \ + -f infrastructure/platform/networking/dns/unbound-helm/values.yaml \ + -f infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml \ + --timeout 5m \ + --wait + else + helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \ + -n bakery-ia \ + --create-namespace \ + -f infrastructure/platform/networking/dns/unbound-helm/values.yaml \ + -f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \ + --timeout 5m \ + --wait + fi + + echo "" + echo "Unbound deployment completed" + fi + + echo "" + echo "Unbound DNS Service Information:" + echo " Service Name: unbound-dns.bakery-ia.svc.cluster.local" + echo " Ports: UDP/TCP 53" + echo " Used by: Mailu for DNS validation" + echo "" + echo "To check pod status: kubectl get pods -n bakery-ia | grep unbound" + ''', + resource_deps=['security-setup'], + labels=['01-infrastructure'], + auto_init=True # Auto-deploy with Tilt startup +) # Mail Infrastructure (Mailu) - Manual trigger for Helm deployment local_resource( @@ -490,6 +573,80 @@ local_resource( echo "Deploying Mailu via Helm..." echo "" + # ===================================================== + # Step 1: Ensure Unbound is deployed and get its IP + # ===================================================== + echo "Checking Unbound DNS resolver..." + if ! kubectl get svc unbound-dns -n bakery-ia &>/dev/null; then + echo "ERROR: Unbound DNS service not found!" + echo "Please deploy Unbound first by triggering 'unbound-helm' resource" + exit 1 + fi + + UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}') + echo "Unbound DNS service IP: $UNBOUND_IP" + + # ===================================================== + # Step 2: Configure CoreDNS to forward to Unbound + # ===================================================== + echo "" + echo "Configuring CoreDNS to forward external queries to Unbound for DNSSEC validation..." + + # Check current CoreDNS forward configuration + CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'forward \. [0-9.]*' | awk '{print $3}') + + if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then + echo "Updating CoreDNS to forward to Unbound ($UNBOUND_IP)..." + + # Patch CoreDNS ConfigMap + kubectl patch configmap coredns -n kube-system --type merge -p "{ + \"data\": { + \"Corefile\": \".:53 {\\n errors\\n health {\\n lameduck 5s\\n }\\n ready\\n kubernetes cluster.local in-addr.arpa ip6.arpa {\\n pods insecure\\n fallthrough in-addr.arpa ip6.arpa\\n ttl 30\\n }\\n prometheus :9153\\n forward . $UNBOUND_IP {\\n max_concurrent 1000\\n }\\n cache 30 {\\n disable success cluster.local\\n disable denial cluster.local\\n }\\n loop\\n reload\\n loadbalance\\n}\\n\" + } + }" + + # Restart CoreDNS + kubectl rollout restart deployment coredns -n kube-system + echo "Waiting for CoreDNS to restart..." + kubectl rollout status deployment coredns -n kube-system --timeout=60s + echo "CoreDNS configured successfully" + else + echo "CoreDNS already configured to forward to Unbound" + fi + + # ===================================================== + # Step 3: Create self-signed TLS certificate for Mailu Front + # ===================================================== + echo "" + echo "Checking Mailu TLS certificates..." + + if ! kubectl get secret mailu-certificates -n bakery-ia &>/dev/null; then + echo "Creating self-signed TLS certificate for Mailu Front..." + + # Generate certificate in temp directory + TEMP_DIR=$(mktemp -d) + cd "$TEMP_DIR" + + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout tls.key -out tls.crt \ + -subj "/CN=mail.bakery-ia.local/O=bakery-ia" 2>/dev/null + + kubectl create secret tls mailu-certificates \ + --cert=tls.crt \ + --key=tls.key \ + -n bakery-ia + + rm -rf "$TEMP_DIR" + echo "TLS certificate created" + else + echo "Mailu TLS certificate already exists" + fi + + # ===================================================== + # Step 4: Deploy Mailu via Helm + # ===================================================== + echo "" + # Check if Mailu is already deployed if helm list -n bakery-ia | grep -q mailu; then echo "Mailu already deployed, checking status..." @@ -516,31 +673,102 @@ local_resource( --create-namespace \ -f infrastructure/platform/mail/mailu-helm/values.yaml \ -f infrastructure/platform/mail/mailu-helm/dev/values.yaml \ - --timeout 10m \ - --wait + --timeout 10m else helm upgrade --install mailu mailu/mailu \ -n bakery-ia \ --create-namespace \ -f infrastructure/platform/mail/mailu-helm/values.yaml \ -f infrastructure/platform/mail/mailu-helm/prod/values.yaml \ - --timeout 10m \ - --wait + --timeout 10m fi echo "" echo "Mailu deployment completed" fi + # ===================================================== + # Step 5: Wait for pods and show status + # ===================================================== + echo "" + echo "Waiting for Mailu pods to be ready..." + sleep 10 + + echo "" + echo "Mailu Pod Status:" + kubectl get pods -n bakery-ia | grep mailu + echo "" echo "Mailu Access Information:" - echo " Admin Panel: https://mail.[domain]/admin" - echo " Webmail: https://mail.[domain]/webmail" - echo " SMTP: mail.[domain]:587 (STARTTLS)" - echo " IMAP: mail.[domain]:993 (SSL/TLS)" + echo " Admin Panel: https://mail.bakery-ia.local/admin" + echo " Webmail: https://mail.bakery-ia.local/webmail" + echo " SMTP: mail.bakery-ia.local:587 (STARTTLS)" + echo " IMAP: mail.bakery-ia.local:993 (SSL/TLS)" + echo "" + echo "To create admin user:" + echo " kubectl exec -it -n bakery-ia deployment/mailu-admin -- flask mailu admin admin bakery-ia.local 'YourPassword123!'" echo "" echo "To check pod status: kubectl get pods -n bakery-ia | grep mailu" ''', + resource_deps=['unbound-helm'], # Ensure Unbound is deployed first + labels=['01-infrastructure'], + auto_init=False, # Manual trigger only +) + +# Nominatim Geocoding - Manual trigger for Helm deployment +local_resource( + 'nominatim-helm', + cmd=''' + echo "Deploying Nominatim geocoding service via Helm..." + echo "" + + # Check if Nominatim is already deployed + if helm list -n bakery-ia | grep -q nominatim; then + echo "Nominatim already deployed, checking status..." + helm status nominatim -n bakery-ia + else + echo "Installing Nominatim..." + + # Determine environment (dev or prod) based on context + ENVIRONMENT="dev" + if [[ "$(kubectl config current-context)" == *"prod"* ]]; then + ENVIRONMENT="prod" + fi + + echo "Environment detected: $ENVIRONMENT" + + # Install Nominatim with appropriate values + if [ "$ENVIRONMENT" = "dev" ]; then + helm upgrade --install nominatim infrastructure/platform/nominatim/nominatim-helm \ + -n bakery-ia \ + --create-namespace \ + -f infrastructure/platform/nominatim/nominatim-helm/values.yaml \ + -f infrastructure/platform/nominatim/nominatim-helm/dev/values.yaml \ + --timeout 10m \ + --wait + else + helm upgrade --install nominatim infrastructure/platform/nominatim/nominatim-helm \ + -n bakery-ia \ + --create-namespace \ + -f infrastructure/platform/nominatim/nominatim-helm/values.yaml \ + -f infrastructure/platform/nominatim/nominatim-helm/prod/values.yaml \ + --timeout 10m \ + --wait + fi + + echo "" + echo "Nominatim deployment completed" + fi + + echo "" + echo "Nominatim Service Information:" + echo " Service Name: nominatim-service.bakery-ia.svc.cluster.local" + echo " Port: 8080" + echo " Health Check: http://nominatim-service:8080/status" + echo "" + echo "To check pod status: kubectl get pods -n bakery-ia | grep nominatim" + echo "To check Helm release: helm status nominatim -n bakery-ia" + ''', labels=['01-infrastructure'], auto_init=False, # Manual trigger only ) @@ -723,7 +951,6 @@ k8s_resource('demo-session-migration', resource_deps=['demo-session-db'], labels # ============================================================================= k8s_resource('external-data-init', resource_deps=['external-migration', 'redis'], labels=['08-data-init']) -k8s_resource('nominatim-init', labels=['08-data-init']) # ============================================================================= # APPLICATION SERVICES diff --git a/docs/PILOT_LAUNCH_GUIDE.md b/docs/PILOT_LAUNCH_GUIDE.md index 8b6154aa..d2be916e 100644 --- a/docs/PILOT_LAUNCH_GUIDE.md +++ b/docs/PILOT_LAUNCH_GUIDE.md @@ -719,6 +719,221 @@ kubectl describe clusterissuer letsencrypt-production From Email: noreply@yourdomain.com ``` +### Option C: Self-Hosted Mailu (RECOMMENDED for Production) + +**Features:** +- ✅ Full control over email infrastructure +- ✅ No external dependencies or rate limits +- ✅ Built-in antispam (rspamd) with DNSSEC validation +- ✅ Webmail interface (Roundcube) +- ✅ IMAP/SMTP with TLS +- ✅ Admin panel for user management +- ✅ Integrated with Kubernetes + +**Why Mailu for Production:** +- Complete email stack (Postfix, Dovecot, Rspamd, ClamAV) +- DNSSEC validation for email authentication (DKIM/SPF/DMARC) +- No monthly email limits or third-party dependencies +- Professional email addresses: admin@bakewise.ai, noreply@bakewise.ai + +#### Prerequisites + +Before deploying Mailu, ensure: +1. **Unbound DNS is deployed** (for DNSSEC validation) +2. **CoreDNS is configured** to forward to Unbound +3. **DNS records are configured** for your domain + +#### Step 1: Configure DNS Records + +Add these DNS records for your domain (e.g., bakewise.ai): + +``` +Type Name Value TTL +A mail YOUR_VPS_IP Auto +MX @ mail.bakewise.ai (priority 10) Auto +TXT @ v=spf1 mx a -all Auto +TXT _dmarc v=DMARC1; p=reject; rua=... Auto +``` + +**DKIM record** will be generated after Mailu is running - you'll add it later. + +#### Step 2: Deploy Unbound DNS Resolver + +Unbound provides DNSSEC validation required by Mailu for email authentication. + +```bash +# On VPS - Deploy Unbound via Helm +helm upgrade --install unbound infrastructure/platform/networking/dns/unbound-helm \ + -n bakery-ia \ + --create-namespace \ + -f infrastructure/platform/networking/dns/unbound-helm/values.yaml \ + -f infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml \ + --timeout 5m \ + --wait + +# Verify Unbound is running +kubectl get pods -n bakery-ia | grep unbound +# Should show: unbound-xxx 1/1 Running + +# Get Unbound service IP (needed for CoreDNS configuration) +UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}') +echo "Unbound DNS IP: $UNBOUND_IP" +``` + +#### Step 3: Configure CoreDNS for DNSSEC + +Mailu requires DNSSEC validation. Configure CoreDNS to forward external queries to Unbound: + +```bash +# Get the Unbound service IP +UNBOUND_IP=$(kubectl get svc unbound-dns -n bakery-ia -o jsonpath='{.spec.clusterIP}') + +# Patch CoreDNS to forward to Unbound +kubectl patch configmap coredns -n kube-system --type merge -p "{ + \"data\": { + \"Corefile\": \".:53 {\\n errors\\n health {\\n lameduck 5s\\n }\\n ready\\n kubernetes cluster.local in-addr.arpa ip6.arpa {\\n pods insecure\\n fallthrough in-addr.arpa ip6.arpa\\n ttl 30\\n }\\n prometheus :9153\\n forward . $UNBOUND_IP {\\n max_concurrent 1000\\n }\\n cache 30 {\\n disable success cluster.local\\n disable denial cluster.local\\n }\\n loop\\n reload\\n loadbalance\\n}\\n\" + } +}" + +# Restart CoreDNS to apply changes +kubectl rollout restart deployment coredns -n kube-system +kubectl rollout status deployment coredns -n kube-system --timeout=60s + +# Verify DNSSEC is working +kubectl run -it --rm debug --image=alpine --restart=Never -- \ + sh -c "apk add drill && drill -D google.com" +# Should show: ;; flags: ... ad ... (ad = authenticated data = DNSSEC valid) +``` + +#### Step 4: Create TLS Certificate Secret + +Mailu Front pod requires a TLS certificate: + +```bash +# Generate self-signed certificate for internal use +# (Let's Encrypt handles external TLS via Ingress) +TEMP_DIR=$(mktemp -d) +cd "$TEMP_DIR" + +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout tls.key -out tls.crt \ + -subj "/CN=mail.bakewise.ai/O=bakewise" + +kubectl create secret tls mailu-certificates \ + --cert=tls.crt \ + --key=tls.key \ + -n bakery-ia + +rm -rf "$TEMP_DIR" + +# Verify secret created +kubectl get secret mailu-certificates -n bakery-ia +``` + +#### Step 5: Deploy Mailu via Helm + +```bash +# Add Mailu Helm repository +helm repo add mailu https://mailu.github.io/helm-charts +helm repo update mailu + +# Deploy Mailu with production values +helm upgrade --install mailu mailu/mailu \ + -n bakery-ia \ + --create-namespace \ + -f infrastructure/platform/mail/mailu-helm/values.yaml \ + -f infrastructure/platform/mail/mailu-helm/prod/values.yaml \ + --timeout 10m + +# Wait for pods to be ready (may take 5-10 minutes for ClamAV) +kubectl get pods -n bakery-ia -l app.kubernetes.io/instance=mailu -w +``` + +#### Step 6: Create Admin User + +```bash +# Create initial admin user +kubectl exec -it -n bakery-ia deployment/mailu-admin -- \ + flask mailu admin admin bakewise.ai 'YourSecurePassword123!' + +# Credentials: +# Email: admin@bakewise.ai +# Password: YourSecurePassword123! +``` + +#### Step 7: Configure DKIM + +After Mailu is running, get the DKIM key and add it to DNS: + +```bash +# Get DKIM public key +kubectl exec -n bakery-ia deployment/mailu-admin -- \ + cat /dkim/bakewise.ai.dkim.pub + +# Add this as a TXT record in your DNS: +# Name: dkim._domainkey +# Value: (the key from above) +``` + +#### Step 8: Verify Email Setup + +```bash +# Check all Mailu pods are running +kubectl get pods -n bakery-ia | grep mailu +# Expected: All 10 pods in Running state + +# Test SMTP connectivity +kubectl run -it --rm smtp-test --image=alpine --restart=Never -- \ + sh -c "apk add swaks && swaks --to test@example.com --from admin@bakewise.ai --server mailu-front.bakery-ia.svc.cluster.local:25" + +# Access webmail (via port-forward for testing) +kubectl port-forward -n bakery-ia svc/mailu-front 8080:80 +# Open: http://localhost:8080/webmail +``` + +#### Production Email Endpoints + +| Service | URL/Address | +|---------|-------------| +| Admin Panel | https://mail.bakewise.ai/admin | +| Webmail | https://mail.bakewise.ai/webmail | +| SMTP (STARTTLS) | mail.bakewise.ai:587 | +| SMTP (SSL) | mail.bakewise.ai:465 | +| IMAP (SSL) | mail.bakewise.ai:993 | + +#### Troubleshooting Mailu + +**Issue: Admin pod CrashLoopBackOff with "DNSSEC validation" error** +```bash +# Verify CoreDNS is forwarding to Unbound +kubectl get configmap coredns -n kube-system -o yaml | grep forward +# Should show: forward . + +# If not, re-run Step 3 above +``` + +**Issue: Front pod stuck in ContainerCreating** +```bash +# Check for missing certificate secret +kubectl describe pod -n bakery-ia -l app.kubernetes.io/component=front | grep -A5 Events + +# If missing mailu-certificates, re-run Step 4 above +``` + +**Issue: Admin pod can't connect to Redis** +```bash +# Verify externalRedis is disabled in values +helm get values mailu -n bakery-ia | grep -A5 externalRedis +# Should show: enabled: false + +# If enabled: true, upgrade with correct values +helm upgrade mailu mailu/mailu -n bakery-ia \ + -f infrastructure/platform/mail/mailu-helm/values.yaml \ + -f infrastructure/platform/mail/mailu-helm/prod/values.yaml +``` + +--- + ### WhatsApp Business API Setup **Features:** diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 663a4d26..1d13b20b 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -1,10 +1,28 @@ -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Gateway Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# +# Usage: +# Dev: docker build --build-arg BASE_REGISTRY=localhost:5000 --build-arg PYTHON_IMAGE=python_3.11-slim . +# Prod: docker build --build-arg BASE_REGISTRY=ghcr.io/your-org --build-arg PYTHON_IMAGE=python:3.11-slim . +# ============================================================================= + +# Build arguments - can be overridden at build time +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +# Stage 1: Copy shared libraries +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +# Stage 2: Main service +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} # Create non-root user for security RUN groupadd -r appgroup && useradd -r -g appgroup appuser diff --git a/infrastructure/cicd/tekton-helm/templates/pipeline-ci.yaml b/infrastructure/cicd/tekton-helm/templates/pipeline-ci.yaml index 4ac513d5..3d9dbe7c 100644 --- a/infrastructure/cicd/tekton-helm/templates/pipeline-ci.yaml +++ b/infrastructure/cicd/tekton-helm/templates/pipeline-ci.yaml @@ -1,6 +1,7 @@ # Main CI Pipeline for Bakery-IA # This pipeline orchestrates the build, test, and deploy process # Includes: fetch -> detect changes -> test -> build -> update gitops +# Supports environment-configurable base images for dev/prod flexibility apiVersion: tekton.dev/v1beta1 kind: Pipeline @@ -28,7 +29,7 @@ spec: description: Git revision/commit hash - name: registry type: string - description: Container registry URL + description: Container registry URL for pushing built images - name: git-branch type: string description: Target branch for GitOps updates @@ -41,6 +42,15 @@ spec: type: string description: Dry run mode - don't push changes default: "false" + # Base image configuration for environment-specific builds + - name: base-registry + type: string + description: "Base image registry URL (e.g., docker.io for prod, localhost:5000 for dev)" + default: "{{ .Values.pipeline.build.baseRegistry }}" + - name: python-image + type: string + description: "Python base image name and tag (e.g., python:3.11-slim for prod)" + default: "{{ .Values.pipeline.build.pythonImage }}" tasks: # Stage 1: Fetch source code @@ -107,6 +117,11 @@ spec: value: $(params.registry) - name: git-revision value: $(params.git-revision) + # Environment-configurable base images + - name: base-registry + value: $(params.base-registry) + - name: python-image + value: $(params.python-image) # Stage 5: Update GitOps manifests - name: update-gitops-manifests diff --git a/infrastructure/cicd/tekton-helm/templates/task-kaniko-build.yaml b/infrastructure/cicd/tekton-helm/templates/task-kaniko-build.yaml index 172b9bcf..e77736ab 100644 --- a/infrastructure/cicd/tekton-helm/templates/task-kaniko-build.yaml +++ b/infrastructure/cicd/tekton-helm/templates/task-kaniko-build.yaml @@ -1,5 +1,6 @@ # Tekton Kaniko Build Task for Bakery-IA CI/CD # This task builds and pushes container images using Kaniko +# Supports environment-configurable base images via build-args apiVersion: tekton.dev/v1beta1 kind: Task @@ -21,10 +22,18 @@ spec: description: Comma-separated list of services to build - name: registry type: string - description: Container registry URL + description: Container registry URL for pushing built images - name: git-revision type: string description: Git revision to tag images with + - name: base-registry + type: string + description: Base image registry URL (e.g., docker.io, ghcr.io/org) + default: "docker.io" + - name: python-image + type: string + description: Python base image name and tag + default: "python:3.11-slim" results: - name: build-status description: Status of the build operation @@ -37,24 +46,51 @@ spec: script: | #!/bin/bash set -e - + + echo "===================================================================" + echo "Kaniko Build Configuration" + echo "===================================================================" + echo "Target Registry: $(params.registry)" + echo "Base Registry: $(params.base-registry)" + echo "Python Image: $(params.python-image)" + echo "Git Revision: $(params.git-revision)" + echo "===================================================================" + # Split services parameter by comma IFS=',' read -ra SERVICES <<< "$(params.services)" - + # Build each service for service in "${SERVICES[@]}"; do service=$(echo "$service" | xargs) # Trim whitespace if [ -n "$service" ] && [ "$service" != "none" ]; then + echo "" echo "Building service: $service" + echo "-------------------------------------------------------------------" + + # Determine Dockerfile path (services vs gateway) + if [ "$service" = "gateway" ]; then + DOCKERFILE_PATH="$(workspaces.source.path)/gateway/Dockerfile" + else + DOCKERFILE_PATH="$(workspaces.source.path)/services/$service/Dockerfile" + fi + /kaniko/executor \ - --dockerfile="$(workspaces.source.path)/services/$service/Dockerfile" \ + --dockerfile="$DOCKERFILE_PATH" \ --destination="$(params.registry)/$service:$(params.git-revision)" \ --context="$(workspaces.source.path)" \ + --build-arg="BASE_REGISTRY=$(params.base-registry)" \ + --build-arg="PYTHON_IMAGE=$(params.python-image)" \ --cache=true \ --cache-repo="$(params.registry)/cache" + + echo "Successfully built: $(params.registry)/$service:$(params.git-revision)" fi done - + + echo "" + echo "===================================================================" + echo "Build completed successfully!" + echo "===================================================================" echo "success" > $(results.build-status.path) resources: limits: diff --git a/infrastructure/cicd/tekton-helm/values.yaml b/infrastructure/cicd/tekton-helm/values.yaml index baef888b..45741cae 100644 --- a/infrastructure/cicd/tekton-helm/values.yaml +++ b/infrastructure/cicd/tekton-helm/values.yaml @@ -19,6 +19,11 @@ pipeline: build: cacheTTL: "24h" verbosity: "info" + # Base image registry configuration + # For dev: localhost:5000 with python_3.11-slim + # For prod: docker.io with python:3.11-slim + baseRegistry: "docker.io" + pythonImage: "python:3.11-slim" # Test configuration test: diff --git a/infrastructure/environments/dev/k8s-manifests/kustomization.yaml b/infrastructure/environments/dev/k8s-manifests/kustomization.yaml index a7944c4f..1066c7f9 100644 --- a/infrastructure/environments/dev/k8s-manifests/kustomization.yaml +++ b/infrastructure/environments/dev/k8s-manifests/kustomization.yaml @@ -11,7 +11,9 @@ metadata: resources: - ../../../environments/common/configs - - ../../../platform/infrastructure + # NOTE: nominatim is NOT included here - it's deployed manually via Tilt trigger 'nominatim-helm' + # - ../../../platform/nominatim + - ../../../platform/gateway - ../../../platform/cert-manager - ../../../platform/networking/ingress/overlays/dev - ../../../platform/storage @@ -36,22 +38,7 @@ patches: - op: replace path: /data/DEBUG value: "true" - # Suspend nominatim in dev to save resources - - target: - kind: StatefulSet - name: nominatim - patch: |- - - op: replace - path: /spec/replicas - value: 0 - # Suspend nominatim-init job in dev (not needed when nominatim is scaled to 0) - - target: - kind: Job - name: nominatim-init - patch: |- - - op: replace - path: /spec/suspend - value: true + # NOTE: nominatim patches removed - nominatim is now deployed via Helm (tilt trigger nominatim-helm) labels: - includeSelectors: true @@ -83,10 +70,7 @@ images: - name: bitnami/kubectl newName: localhost:5000/bitnami_kubectl_latest newTag: latest - # DNS resolver - - name: mvance/unbound - newName: localhost:5000/mvance_unbound_latest - newTag: latest + # Alpine variants - name: alpine newName: localhost:5000/alpine_3.19 @@ -111,10 +95,7 @@ images: - name: minio/mc newName: localhost:5000/minio_mc_release.2024-11-17t19-35-25z newTag: latest - # Geocoding - - name: mediagis/nominatim - newName: localhost:5000/mediagis_nominatim_4.4 - newTag: latest + # NOTE: nominatim image override removed - nominatim is now deployed via Helm # Python base image - name: python newName: localhost:5000/python_3.11-slim diff --git a/infrastructure/environments/prod/k8s-manifests/kustomization.yaml b/infrastructure/environments/prod/k8s-manifests/kustomization.yaml index fa1495d6..c4f62280 100644 --- a/infrastructure/environments/prod/k8s-manifests/kustomization.yaml +++ b/infrastructure/environments/prod/k8s-manifests/kustomization.yaml @@ -221,9 +221,7 @@ images: newTag: latest - name: bitnami/kubectl newTag: latest - # DNS resolver - - name: mvance/unbound - newTag: latest + # Alpine variants - name: alpine newTag: "3.19" @@ -241,9 +239,7 @@ images: newTag: RELEASE.2024-11-07T00-52-20Z - name: minio/mc newTag: RELEASE.2024-11-17T19-35-25Z - # Geocoding - - name: mediagis/nominatim - newTag: "4.4" + # NOTE: nominatim image override removed - nominatim is now deployed via Helm # Python base image - name: python newTag: 3.11-slim diff --git a/infrastructure/platform/infrastructure/gateway-service.yaml b/infrastructure/platform/gateway/gateway-service.yaml similarity index 100% rename from infrastructure/platform/infrastructure/gateway-service.yaml rename to infrastructure/platform/gateway/gateway-service.yaml diff --git a/infrastructure/platform/infrastructure/kustomization.yaml b/infrastructure/platform/gateway/kustomization.yaml similarity index 52% rename from infrastructure/platform/infrastructure/kustomization.yaml rename to infrastructure/platform/gateway/kustomization.yaml index c95f8a01..fe0f2f8d 100644 --- a/infrastructure/platform/infrastructure/kustomization.yaml +++ b/infrastructure/platform/gateway/kustomization.yaml @@ -3,6 +3,3 @@ kind: Kustomization resources: - gateway-service.yaml - - nominatim/nominatim.yaml - - nominatim/nominatim-init-job.yaml - - unbound/unbound.yaml diff --git a/infrastructure/platform/infrastructure/nominatim/nominatim.yaml b/infrastructure/platform/infrastructure/nominatim/nominatim.yaml deleted file mode 100644 index 9aaf2ed6..00000000 --- a/infrastructure/platform/infrastructure/nominatim/nominatim.yaml +++ /dev/null @@ -1,158 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: nominatim-config - namespace: bakery-ia - labels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding -data: - NOMINATIM_PBF_URL: "http://download.geofabrik.de/europe/spain-latest.osm.pbf" - NOMINATIM_REPLICATION_URL: "https://download.geofabrik.de/europe/spain-updates" - NOMINATIM_IMPORT_STYLE: "address" - NOMINATIM_THREADS: "4" - NOMINATIM_FLATNODE_FILE: "/nominatim-flatnode/flatnode.bin" - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: nominatim-data - namespace: bakery-ia - labels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 50Gi - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: nominatim-flatnode - namespace: bakery-ia - labels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 20Gi - ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: nominatim - namespace: bakery-ia - labels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding - app.kubernetes.io/part-of: bakery-ia -spec: - serviceName: nominatim-service - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding - template: - metadata: - labels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding - spec: - containers: - - name: nominatim - image: mediagis/nominatim:4.4 - ports: - - containerPort: 8080 - name: http - volumeMounts: - - name: nominatim-data - mountPath: /var/lib/postgresql - - name: nominatim-flatnode - mountPath: /nominatim-flatnode - env: - - name: NOMINATIM_PBF_URL - valueFrom: - configMapKeyRef: - name: nominatim-config - key: NOMINATIM_PBF_URL - - name: NOMINATIM_REPLICATION_URL - valueFrom: - configMapKeyRef: - name: nominatim-config - key: NOMINATIM_REPLICATION_URL - - name: NOMINATIM_IMPORT_STYLE - valueFrom: - configMapKeyRef: - name: nominatim-config - key: NOMINATIM_IMPORT_STYLE - - name: NOMINATIM_THREADS - valueFrom: - configMapKeyRef: - name: nominatim-config - key: NOMINATIM_THREADS - - name: NOMINATIM_FLATNODE_FILE - valueFrom: - configMapKeyRef: - name: nominatim-config - key: NOMINATIM_FLATNODE_FILE - resources: - requests: - memory: "2Gi" - cpu: "1" - limits: - memory: "4Gi" - cpu: "2" - livenessProbe: - httpGet: - path: /status - port: 8080 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /status - port: 8080 - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 5 - volumes: - - name: nominatim-data - persistentVolumeClaim: - claimName: nominatim-data - - name: nominatim-flatnode - persistentVolumeClaim: - claimName: nominatim-flatnode - ---- -apiVersion: v1 -kind: Service -metadata: - name: nominatim-service - namespace: bakery-ia - labels: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding -spec: - selector: - app.kubernetes.io/name: nominatim - app.kubernetes.io/component: geocoding - ports: - - port: 8080 - targetPort: 8080 - protocol: TCP - name: http - type: ClusterIP diff --git a/infrastructure/platform/infrastructure/unbound/unbound.yaml b/infrastructure/platform/infrastructure/unbound/unbound.yaml deleted file mode 100644 index 47c31a7f..00000000 --- a/infrastructure/platform/infrastructure/unbound/unbound.yaml +++ /dev/null @@ -1,81 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: unbound-resolver - namespace: bakery-ia - labels: - app.kubernetes.io/name: unbound-resolver - app.kubernetes.io/component: dns - app.kubernetes.io/part-of: bakery-ia -spec: - replicas: 1 # Scale to 2+ in production with anti-affinity - selector: - matchLabels: - app.kubernetes.io/name: unbound-resolver - app.kubernetes.io/component: dns - template: - metadata: - labels: - app.kubernetes.io/name: unbound-resolver - app.kubernetes.io/component: dns - spec: - containers: - - name: unbound - image: mvance/unbound:latest - ports: - - containerPort: 53 - name: dns-udp - protocol: UDP - - containerPort: 53 - name: dns-tcp - protocol: TCP - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "300m" - memory: "384Mi" - readinessProbe: - exec: - command: - - sh - - -c - - drill @127.0.0.1 -p 53 +dnssec example.org || nslookup -type=A example.org 127.0.0.1 - initialDelaySeconds: 10 - periodSeconds: 30 - livenessProbe: - exec: - command: - - sh - - -c - - drill @127.0.0.1 -p 53 +dnssec example.org || nslookup -type=A example.org 127.0.0.1 - initialDelaySeconds: 30 - periodSeconds: 60 - securityContext: - capabilities: - add: ["NET_BIND_SERVICE"] - ---- -apiVersion: v1 -kind: Service -metadata: - name: unbound-dns - namespace: bakery-ia - labels: - app.kubernetes.io/name: unbound-resolver - app.kubernetes.io/component: dns -spec: - type: ClusterIP - ports: - - name: dns-udp - port: 53 - targetPort: 53 - protocol: UDP - - name: dns-tcp - port: 53 - targetPort: 53 - protocol: TCP - selector: - app.kubernetes.io/name: unbound-resolver - app.kubernetes.io/component: dns \ No newline at end of file diff --git a/infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml b/infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml new file mode 100644 index 00000000..e2ab881a --- /dev/null +++ b/infrastructure/platform/mail/mailu-helm/configs/coredns-unbound-patch.yaml @@ -0,0 +1,38 @@ +# CoreDNS ConfigMap patch to forward external DNS queries to Unbound for DNSSEC validation +# This is required for Mailu Admin which requires DNSSEC-validating DNS resolver +# +# Apply with: kubectl apply -f coredns-unbound-patch.yaml +# Then restart CoreDNS: kubectl rollout restart deployment coredns -n kube-system +# +# Note: The Unbound service IP (10.104.127.213) may change when the cluster is recreated. +# The setup script will automatically update this based on the actual Unbound service IP. +apiVersion: v1 +kind: ConfigMap +metadata: + name: coredns + namespace: kube-system +data: + Corefile: | + .:53 { + errors + health { + lameduck 5s + } + ready + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + ttl 30 + } + prometheus :9153 + forward . UNBOUND_SERVICE_IP { + max_concurrent 1000 + } + cache 30 { + disable success cluster.local + disable denial cluster.local + } + loop + reload + loadbalance + } diff --git a/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml b/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml new file mode 100644 index 00000000..7c4af6d7 --- /dev/null +++ b/infrastructure/platform/mail/mailu-helm/configs/mailu-certificates-secret.yaml @@ -0,0 +1,26 @@ +# Self-signed TLS certificate secret for Mailu Front +# This is required by the Mailu Helm chart even when TLS is disabled (tls.flavor: notls) +# The Front pod mounts this secret for internal certificate handling +# +# For production, replace with proper certificates from cert-manager or Let's Encrypt +# This script generates a self-signed certificate valid for 365 days +# +# To regenerate manually: +# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ +# -keyout tls.key -out tls.crt \ +# -subj "/CN=mail.bakery-ia.local/O=bakery-ia" +# kubectl create secret tls mailu-certificates \ +# --cert=tls.crt --key=tls.key -n bakery-ia +apiVersion: v1 +kind: Secret +metadata: + name: mailu-certificates + namespace: bakery-ia + labels: + app.kubernetes.io/name: mailu + app.kubernetes.io/component: certificates +type: kubernetes.io/tls +data: + # Placeholder - will be generated dynamically by the setup script + tls.crt: "" + tls.key: "" diff --git a/infrastructure/platform/mail/mailu-helm/dev/values.yaml b/infrastructure/platform/mail/mailu-helm/dev/values.yaml index 3419047a..edda468b 100644 --- a/infrastructure/platform/mail/mailu-helm/dev/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/dev/values.yaml @@ -1,20 +1,23 @@ # Development-tuned Mailu configuration global: - # Use the unbound service IP - will be replaced during deployment - custom_dns_servers: "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + # Using Kubernetes cluster DNS for name resolution + # Unbound service is available at unbound-dns.bakery-ia.svc.cluster.local + custom_dns_servers: "10.96.0.10" # Kubernetes cluster DNS IP + +# Redis configuration - use built-in Mailu Redis (no authentication needed) +externalRedis: + enabled: false # Component-specific DNS configuration +# Admin uses Kubernetes DNS (ClusterFirst) to resolve internal services like Redis +# DNSSEC validation is handled at the application level by rspamd admin: - dnsPolicy: "None" - dnsConfig: - nameservers: - - "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + dnsPolicy: "ClusterFirst" +# RSPAMD needs Unbound for DNSSEC validation (DKIM/SPF/DMARC checks) +# Using ClusterFirst with search domains + Kubernetes DNS which can forward to Unbound rspamd: - dnsPolicy: "None" - dnsConfig: - nameservers: - - "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + dnsPolicy: "ClusterFirst" # Domain configuration for dev domain: "bakery-ia.local" @@ -96,7 +99,7 @@ ingress: # TLS flavor for dev (may use self-signed) tls: - flavor: "cert" + flavor: "notls" # Disable TLS for development # Welcome message (disabled in dev) welcomeMessage: diff --git a/infrastructure/platform/mail/mailu-helm/prod/values.yaml b/infrastructure/platform/mail/mailu-helm/prod/values.yaml index 95eb1506..4a2b4f32 100644 --- a/infrastructure/platform/mail/mailu-helm/prod/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/prod/values.yaml @@ -1,20 +1,20 @@ # Production-tuned Mailu configuration global: - # Use the unbound service IP - will be replaced during deployment - custom_dns_servers: "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + # Using Kubernetes cluster DNS for name resolution + custom_dns_servers: "10.96.0.10" # Kubernetes cluster DNS IP -# Component-specific DNS configuration +# Redis configuration - use built-in Mailu Redis (no authentication needed for internal) +externalRedis: + enabled: false + +# DNS configuration for production +# Use Kubernetes DNS (ClusterFirst) which forwards to Unbound via CoreDNS +# This is configured automatically by the mailu-helm Tilt resource admin: - dnsPolicy: "None" - dnsConfig: - nameservers: - - "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + dnsPolicy: "ClusterFirst" rspamd: - dnsPolicy: "None" - dnsConfig: - nameservers: - - "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + dnsPolicy: "ClusterFirst" # Domain configuration for production domain: "bakewise.ai" diff --git a/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh b/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh new file mode 100755 index 00000000..b7a78017 --- /dev/null +++ b/infrastructure/platform/mail/mailu-helm/scripts/deploy-mailu-prod.sh @@ -0,0 +1,260 @@ +#!/bin/bash +# ============================================================================= +# Mailu Production Deployment Script +# ============================================================================= +# This script automates the deployment of Mailu mail server for production. +# It handles: +# 1. Unbound DNS deployment (for DNSSEC validation) +# 2. CoreDNS configuration (forward to Unbound) +# 3. TLS certificate secret creation +# 4. Mailu Helm deployment +# 5. Admin user creation +# +# Usage: +# ./deploy-mailu-prod.sh [--domain DOMAIN] [--admin-password PASSWORD] +# +# Example: +# ./deploy-mailu-prod.sh --domain bakewise.ai --admin-password 'SecurePass123!' +# ============================================================================= + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default values +DOMAIN="${DOMAIN:-bakewise.ai}" +ADMIN_PASSWORD="${ADMIN_PASSWORD:-}" +NAMESPACE="bakery-ia" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MAILU_HELM_DIR="$(dirname "$SCRIPT_DIR")" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --domain) + DOMAIN="$2" + shift 2 + ;; + --admin-password) + ADMIN_PASSWORD="$2" + shift 2 + ;; + --help) + echo "Usage: $0 [--domain DOMAIN] [--admin-password PASSWORD]" + echo "" + echo "Options:" + echo " --domain Domain for Mailu (default: bakewise.ai)" + echo " --admin-password Password for admin@DOMAIN user" + echo "" + exit 0 + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + exit 1 + ;; + esac +done + +print_step() { + echo -e "\n${BLUE}==>${NC} ${GREEN}$1${NC}" +} + +print_warning() { + echo -e "${YELLOW}WARNING:${NC} $1" +} + +print_error() { + echo -e "${RED}ERROR:${NC} $1" +} + +print_success() { + echo -e "${GREEN}✓${NC} $1" +} + +# ============================================================================= +# Step 0: Prerequisites Check +# ============================================================================= +print_step "Step 0: Checking prerequisites..." + +if ! command -v kubectl &> /dev/null; then + print_error "kubectl not found. Please install kubectl." + exit 1 +fi + +if ! command -v helm &> /dev/null; then + print_error "helm not found. Please install helm." + exit 1 +fi + +if ! kubectl get namespace "$NAMESPACE" &>/dev/null; then + print_warning "Namespace $NAMESPACE does not exist. Creating..." + kubectl create namespace "$NAMESPACE" +fi + +print_success "Prerequisites check passed" + +# ============================================================================= +# Step 1: Deploy Unbound DNS Resolver +# ============================================================================= +print_step "Step 1: Deploying Unbound DNS resolver..." + +if kubectl get deployment unbound -n "$NAMESPACE" &>/dev/null; then + print_success "Unbound already deployed" +else + helm upgrade --install unbound "$MAILU_HELM_DIR/../../networking/dns/unbound-helm" \ + -n "$NAMESPACE" \ + -f "$MAILU_HELM_DIR/../../networking/dns/unbound-helm/values.yaml" \ + -f "$MAILU_HELM_DIR/../../networking/dns/unbound-helm/prod/values.yaml" \ + --timeout 5m \ + --wait + + print_success "Unbound deployed" +fi + +# Wait for Unbound to be ready +kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=unbound -n "$NAMESPACE" --timeout=120s + +# Get Unbound service IP +UNBOUND_IP=$(kubectl get svc unbound-dns -n "$NAMESPACE" -o jsonpath='{.spec.clusterIP}') +echo "Unbound DNS service IP: $UNBOUND_IP" + +# ============================================================================= +# Step 2: Configure CoreDNS to Forward to Unbound +# ============================================================================= +print_step "Step 2: Configuring CoreDNS for DNSSEC validation..." + +# Check current CoreDNS forward configuration +CURRENT_FORWARD=$(kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -o 'forward \. [0-9.]*' | awk '{print $3}' || echo "") + +if [ "$CURRENT_FORWARD" != "$UNBOUND_IP" ]; then + echo "Updating CoreDNS to forward to Unbound ($UNBOUND_IP)..." + + kubectl patch configmap coredns -n kube-system --type merge -p "{ + \"data\": { + \"Corefile\": \".:53 {\\n errors\\n health {\\n lameduck 5s\\n }\\n ready\\n kubernetes cluster.local in-addr.arpa ip6.arpa {\\n pods insecure\\n fallthrough in-addr.arpa ip6.arpa\\n ttl 30\\n }\\n prometheus :9153\\n forward . $UNBOUND_IP {\\n max_concurrent 1000\\n }\\n cache 30 {\\n disable success cluster.local\\n disable denial cluster.local\\n }\\n loop\\n reload\\n loadbalance\\n}\\n\" + } + }" + + # Restart CoreDNS + kubectl rollout restart deployment coredns -n kube-system + kubectl rollout status deployment coredns -n kube-system --timeout=60s + + print_success "CoreDNS configured to forward to Unbound" +else + print_success "CoreDNS already configured for Unbound" +fi + +# ============================================================================= +# Step 3: Create TLS Certificate Secret +# ============================================================================= +print_step "Step 3: Creating TLS certificate secret..." + +if kubectl get secret mailu-certificates -n "$NAMESPACE" &>/dev/null; then + print_success "TLS certificate secret already exists" +else + TEMP_DIR=$(mktemp -d) + cd "$TEMP_DIR" + + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout tls.key -out tls.crt \ + -subj "/CN=mail.$DOMAIN/O=$DOMAIN" 2>/dev/null + + kubectl create secret tls mailu-certificates \ + --cert=tls.crt \ + --key=tls.key \ + -n "$NAMESPACE" + + rm -rf "$TEMP_DIR" + print_success "TLS certificate secret created" +fi + +# ============================================================================= +# Step 4: Deploy Mailu via Helm +# ============================================================================= +print_step "Step 4: Deploying Mailu via Helm..." + +# Add Mailu Helm repository +helm repo add mailu https://mailu.github.io/helm-charts 2>/dev/null || true +helm repo update mailu + +# Deploy Mailu +helm upgrade --install mailu mailu/mailu \ + -n "$NAMESPACE" \ + -f "$MAILU_HELM_DIR/values.yaml" \ + -f "$MAILU_HELM_DIR/prod/values.yaml" \ + --timeout 10m + +print_success "Mailu Helm release deployed" + +# ============================================================================= +# Step 5: Wait for Pods to be Ready +# ============================================================================= +print_step "Step 5: Waiting for Mailu pods to be ready..." + +echo "This may take 5-10 minutes (ClamAV takes time to initialize)..." + +# Wait for admin pod first (it's the key dependency) +kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=admin -n "$NAMESPACE" --timeout=300s || { + print_error "Admin pod failed to start. Checking logs..." + kubectl logs -n "$NAMESPACE" -l app.kubernetes.io/component=admin --tail=50 + exit 1 +} + +print_success "Admin pod is ready" + +# Show pod status +echo "" +echo "Mailu Pod Status:" +kubectl get pods -n "$NAMESPACE" | grep mailu + +# ============================================================================= +# Step 6: Create Admin User +# ============================================================================= +print_step "Step 6: Creating admin user..." + +if [ -z "$ADMIN_PASSWORD" ]; then + # Generate a random password + ADMIN_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=' | head -c 16) + echo -e "${YELLOW}Generated admin password: $ADMIN_PASSWORD${NC}" + echo -e "${YELLOW}Please save this password securely!${NC}" +fi + +kubectl exec -n "$NAMESPACE" deployment/mailu-admin -- \ + flask mailu admin admin "$DOMAIN" "$ADMIN_PASSWORD" 2>/dev/null || { + print_warning "Admin user may already exist or failed to create" +} + +print_success "Admin user configured" + +# ============================================================================= +# Summary +# ============================================================================= +echo "" +echo "==============================================" +echo -e "${GREEN}Mailu Deployment Complete!${NC}" +echo "==============================================" +echo "" +echo "Admin Credentials:" +echo " Email: admin@$DOMAIN" +echo " Password: $ADMIN_PASSWORD" +echo "" +echo "Access URLs (configure Ingress/DNS first):" +echo " Admin Panel: https://mail.$DOMAIN/admin" +echo " Webmail: https://mail.$DOMAIN/webmail" +echo " SMTP: mail.$DOMAIN:587 (STARTTLS)" +echo " IMAP: mail.$DOMAIN:993 (SSL)" +echo "" +echo "Next Steps:" +echo " 1. Configure DNS records (A, MX, SPF, DMARC)" +echo " 2. Get DKIM key: kubectl exec -n $NAMESPACE deployment/mailu-admin -- cat /dkim/$DOMAIN.dkim.pub" +echo " 3. Add DKIM TXT record to DNS" +echo " 4. Configure Ingress for mail.$DOMAIN" +echo "" +echo "To check pod status:" +echo " kubectl get pods -n $NAMESPACE | grep mailu" +echo "" diff --git a/infrastructure/platform/mail/mailu-helm/values.yaml b/infrastructure/platform/mail/mailu-helm/values.yaml index 991df28b..ef48cde2 100644 --- a/infrastructure/platform/mail/mailu-helm/values.yaml +++ b/infrastructure/platform/mail/mailu-helm/values.yaml @@ -3,8 +3,9 @@ # Global DNS configuration for DNSSEC validation global: - # This will be replaced with the actual Unbound service IP during deployment - custom_dns_servers: "unbound-dns.bakery-ia.svc.cluster.local" # Using service DNS name instead of IP + # Using Unbound DNS resolver directly for DNSSEC validation + # Unbound service is available at unbound-dns.bakery-ia.svc.cluster.local + custom_dns_servers: "10.104.127.213" # Unbound service IP # Domain configuration domain: "DOMAIN_PLACEHOLDER" @@ -25,7 +26,7 @@ postmaster: "admin" # TLS configuration tls: - flavor: "cert" + flavor: "notls" # Disable TLS for development # Limits configuration limits: @@ -64,24 +65,24 @@ logLevel: "INFO" # Network configuration subnet: "10.42.0.0/16" -# Redis configuration - using external Redis (shared cluster Redis) +# Redis configuration - using internal Redis (built-in) externalRedis: - enabled: true - host: "redis-service.bakery-ia.svc.cluster.local" - port: 6380 + enabled: false + # host: "redis-service.bakery-ia.svc.cluster.local" + # port: 6380 adminQuotaDbId: 15 adminRateLimitDbId: 15 rspamdDbId: 15 -# Database configuration - using external database +# Database configuration - using default SQLite (built-in) externalDatabase: - enabled: true - type: "postgresql" - host: "postgres-service.bakery-ia.svc.cluster.local" - port: 5432 - database: "mailu" - username: "mailu" - password: "E8Kz47YmVzDlHGs1M9wAbJzxcKnGONCT" + enabled: false + # type: "postgresql" + # host: "postgres-service.bakery-ia.svc.cluster.local" + # port: 5432 + # database: "mailu" + # username: "mailu" + # password: "E8Kz47YmVzDlHGs1M9wAbJzxcKnGONCT" # Persistence configuration persistence: @@ -210,16 +211,8 @@ networkPolicy: app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/component: controller -# DNS Policy Configuration for DNSSEC validation -# These settings ensure Mailu components use the Unbound DNS resolver -dnsPolicy: "None" -dnsConfig: - nameservers: - - "unbound-dns.bakery-ia.svc.cluster.local" # Points to the Unbound service in the bakery-ia namespace - options: - - name: ndots - value: "5" - - name: timeout - value: "5" - - name: attempts - value: "3" \ No newline at end of file +# DNS Policy Configuration +# Use Kubernetes DNS (ClusterFirst) for internal service resolution +# DNSSEC validation for email is handled by rspamd component +# Note: For production with DNSSEC needs, configure CoreDNS to forward to Unbound +dnsPolicy: "ClusterFirst" \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/Chart.yaml b/infrastructure/platform/networking/dns/unbound-helm/Chart.yaml new file mode 100644 index 00000000..eaf9b5d8 --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: unbound +description: A Helm chart for deploying Unbound DNS resolver for Bakery-IA +type: application +version: 0.1.0 +appVersion: "1.19.1" +maintainers: + - name: Bakery-IA Team + email: devops@bakery-ia.com +keywords: + - dns + - resolver + - caching + - unbound +home: https://www.nlnetlabs.nl/projects/unbound/ +sources: + - https://github.com/NLnetLabs/unbound + - https://hub.docker.com/r/mvance/unbound \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml new file mode 100644 index 00000000..3623730b --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/dev/values.yaml @@ -0,0 +1,36 @@ +# Development values for unbound DNS resolver +# Using same configuration as production for consistency + +# Use official image for development (same as production) +image: + repository: "mvance/unbound" + tag: "latest" + pullPolicy: "IfNotPresent" + +# Resource settings (slightly lower than production for dev) +resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "300m" + memory: "384Mi" + +# Single replica for development (can be scaled if needed) +replicaCount: 1 + +# Development annotations +podAnnotations: + environment: "development" + managed-by: "helm" + +# Probe settings (same as production but slightly faster) +probes: + readiness: + initialDelaySeconds: 10 + periodSeconds: 30 + command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" + liveness: + initialDelaySeconds: 30 + periodSeconds: 60 + command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml new file mode 100644 index 00000000..db86d8ee --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/prod/values.yaml @@ -0,0 +1,50 @@ +# Production-specific values for unbound DNS resolver +# Overrides for the production environment + +# Use official image for production +image: + repository: "mvance/unbound" + tag: "latest" + pullPolicy: "IfNotPresent" + +# Production resource settings (higher limits for reliability) +resources: + requests: + cpu: "200m" + memory: "256Mi" + limits: + cpu: "500m" + memory: "512Mi" + +# Production-specific settings +replicaCount: 2 + +# Production annotations +podAnnotations: + environment: "production" + critical: "true" + +# Anti-affinity for high availability in production +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - unbound + topologyKey: "kubernetes.io/hostname" + +# Production probe settings (more conservative) +probes: + readiness: + initialDelaySeconds: 15 + periodSeconds: 30 + command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" + liveness: + initialDelaySeconds: 45 + periodSeconds: 60 + command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl b/infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl new file mode 100644 index 00000000..6ca19e9e --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/templates/_helpers.tpl @@ -0,0 +1,63 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "unbound.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "unbound.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "unbound.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "unbound.labels" -}} +helm.sh/chart: {{ include "unbound.chart" . }} +{{ include "unbound.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "unbound.selectorLabels" -}} +app.kubernetes.io/name: {{ include "unbound.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: dns +app.kubernetes.io/part-of: bakery-ia +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "unbound.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "unbound.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml new file mode 100644 index 00000000..b66d7951 --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/templates/deployment.yaml @@ -0,0 +1,95 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "unbound.fullname" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "unbound.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "unbound.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "unbound.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "unbound.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: dns-udp + containerPort: {{ .Values.service.ports.dnsUdp }} + protocol: UDP + - name: dns-tcp + containerPort: {{ .Values.service.ports.dnsTcp }} + protocol: TCP + {{- if .Values.probes.readiness.enabled }} + readinessProbe: + exec: + command: + - sh + - -c + - {{ .Values.probes.readiness.command | quote }} + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + {{- end }} + {{- if .Values.probes.liveness.enabled }} + livenessProbe: + exec: + command: + - sh + - -c + - {{ .Values.probes.liveness.command | quote }} + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.extraInitContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.extraContainers }} + containers: + {{- toYaml . | nindent 8 }} + {{- end }} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml new file mode 100644 index 00000000..31a0d379 --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/templates/service.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.global.dnsServiceName }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "unbound.labels" . | nindent 4 }} + {{- with .Values.serviceAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: dns-udp + port: {{ .Values.service.ports.dnsUdp }} + targetPort: {{ .Values.service.ports.dnsUdp }} + protocol: UDP + - name: dns-tcp + port: {{ .Values.service.ports.dnsTcp }} + targetPort: {{ .Values.service.ports.dnsTcp }} + protocol: TCP + selector: + {{- include "unbound.selectorLabels" . | nindent 4 }} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml b/infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml new file mode 100644 index 00000000..1826774d --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "unbound.serviceAccountName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "unbound.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/infrastructure/platform/networking/dns/unbound-helm/values.yaml b/infrastructure/platform/networking/dns/unbound-helm/values.yaml new file mode 100644 index 00000000..55c4da6b --- /dev/null +++ b/infrastructure/platform/networking/dns/unbound-helm/values.yaml @@ -0,0 +1,95 @@ +# Default values for unbound DNS resolver +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# Global settings +global: + # DNS service name for other services to reference + dnsServiceName: "unbound-dns" + namespace: "bakery-ia" + +# Unbound image configuration +image: + repository: "mvance/unbound" + tag: "latest" + pullPolicy: "IfNotPresent" + +# Deployment configuration +replicaCount: 1 + +# Resource limits and requests +resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "300m" + memory: "384Mi" + +# Security context +securityContext: + capabilities: + add: ["NET_BIND_SERVICE"] + +# Service configuration +service: + type: "ClusterIP" + ports: + dnsUdp: 53 + dnsTcp: 53 + +# Health probes configuration +probes: + readiness: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 30 + command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" + liveness: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 60 + command: "drill @127.0.0.1 -p 53 example.org || echo 'DNS query test'" + +# Additional environment variables +env: {} + +# Additional volume mounts +volumeMounts: [] + +# Additional volumes +volumes: [] + +# Node selector +nodeSelector: {} + +# Tolerations +tolerations: [] + +# Affinity +affinity: {} + +# Pod annotations +podAnnotations: {} + +# Service annotations +serviceAnnotations: {} + +# Custom unbound configuration +config: + enabled: false + +# Additional containers (sidecars) +extraContainers: [] + +# Additional init containers +extraInitContainers: [] + +# Service account configuration +serviceAccount: + create: false + annotations: {} + name: "" + +# Pod security context +podSecurityContext: {} \ No newline at end of file diff --git a/infrastructure/platform/nominatim/nominatim-helm/Chart.yaml b/infrastructure/platform/nominatim/nominatim-helm/Chart.yaml new file mode 100644 index 00000000..6fcb2f1e --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +name: nominatim +description: A Helm chart for deploying Nominatim geocoding service for Bakery-IA +type: application +version: 0.1.0 +appVersion: "4.4" +maintainers: + - name: Bakery-IA Team + email: devops@bakery-ia.com +keywords: + - geocoding + - nominatim + - openstreetmap + - maps + - address +home: https://nominatim.org/ +sources: + - https://github.com/mediagis/nominatim-docker + - https://hub.docker.com/r/mediagis/nominatim diff --git a/infrastructure/platform/nominatim/nominatim-helm/dev/values.yaml b/infrastructure/platform/nominatim/nominatim-helm/dev/values.yaml new file mode 100644 index 00000000..59235f3f --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/dev/values.yaml @@ -0,0 +1,38 @@ +# Development values for Nominatim geocoding service +# Disabled by default in dev to save resources + +# Use local registry image for development +image: + repository: "localhost:5000/mediagis_nominatim_4.4" + tag: "latest" + pullPolicy: "IfNotPresent" + +# Disabled in dev (set to 0 replicas) +replicaCount: 0 + +# Init job disabled in dev +initJob: + enabled: false + +# Lower resources for dev (when enabled) +resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "1" + memory: "2Gi" + +# Smaller PVCs for dev +persistence: + data: + enabled: true + size: "10Gi" + flatnode: + enabled: true + size: "5Gi" + +# Development annotations +podAnnotations: + environment: "development" + managed-by: "helm" diff --git a/infrastructure/platform/nominatim/nominatim-helm/prod/values.yaml b/infrastructure/platform/nominatim/nominatim-helm/prod/values.yaml new file mode 100644 index 00000000..fa84c477 --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/prod/values.yaml @@ -0,0 +1,45 @@ +# Production values for Nominatim geocoding service +# Full configuration for production deployment + +# Use official Docker Hub image for production +image: + repository: "mediagis/nominatim" + tag: "4.4" + pullPolicy: "IfNotPresent" + +# Single replica for production (can be scaled if needed) +replicaCount: 1 + +# Init job enabled in production +initJob: + enabled: true + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + +# Production resources +resources: + requests: + cpu: "1" + memory: "2Gi" + limits: + cpu: "2" + memory: "4Gi" + +# Full-size PVCs for production +persistence: + data: + enabled: true + size: "50Gi" + flatnode: + enabled: true + size: "20Gi" + +# Production annotations +podAnnotations: + environment: "production" + managed-by: "helm" diff --git a/infrastructure/platform/nominatim/nominatim-helm/templates/_helpers.tpl b/infrastructure/platform/nominatim/nominatim-helm/templates/_helpers.tpl new file mode 100644 index 00000000..f46b9ca8 --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/templates/_helpers.tpl @@ -0,0 +1,87 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "nominatim.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "nominatim.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "nominatim.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "nominatim.labels" -}} +helm.sh/chart: {{ include "nominatim.chart" . }} +{{ include "nominatim.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "nominatim.selectorLabels" -}} +app.kubernetes.io/name: {{ include "nominatim.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: geocoding +app.kubernetes.io/part-of: bakery-ia +{{- end -}} + +{{/* +ConfigMap name +*/}} +{{- define "nominatim.configMapName" -}} +{{- printf "%s-config" (include "nominatim.fullname" .) -}} +{{- end -}} + +{{/* +Service name +*/}} +{{- define "nominatim.serviceName" -}} +{{- default (printf "%s-service" (include "nominatim.fullname" .)) .Values.service.name -}} +{{- end -}} + +{{/* +Data PVC name +*/}} +{{- define "nominatim.dataPvcName" -}} +{{- printf "%s-data" (include "nominatim.fullname" .) -}} +{{- end -}} + +{{/* +Flatnode PVC name +*/}} +{{- define "nominatim.flatnodePvcName" -}} +{{- printf "%s-flatnode" (include "nominatim.fullname" .) -}} +{{- end -}} + +{{/* +Init job name +*/}} +{{- define "nominatim.initJobName" -}} +{{- printf "%s-init" (include "nominatim.fullname" .) -}} +{{- end -}} diff --git a/infrastructure/platform/nominatim/nominatim-helm/templates/configmap.yaml b/infrastructure/platform/nominatim/nominatim-helm/templates/configmap.yaml new file mode 100644 index 00000000..9c96ab3a --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/templates/configmap.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "nominatim.configMapName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "nominatim.labels" . | nindent 4 }} +data: + NOMINATIM_PBF_URL: {{ .Values.config.pbfUrl | quote }} + NOMINATIM_REPLICATION_URL: {{ .Values.config.replicationUrl | quote }} + NOMINATIM_IMPORT_STYLE: {{ .Values.config.importStyle | quote }} + NOMINATIM_THREADS: {{ .Values.config.threads | quote }} + NOMINATIM_FLATNODE_FILE: {{ .Values.config.flatnodeFile | quote }} diff --git a/infrastructure/platform/infrastructure/nominatim/nominatim-init-job.yaml b/infrastructure/platform/nominatim/nominatim-helm/templates/init-job.yaml similarity index 67% rename from infrastructure/platform/infrastructure/nominatim/nominatim-init-job.yaml rename to infrastructure/platform/nominatim/nominatim-helm/templates/init-job.yaml index 3d3b9868..ea95503a 100644 --- a/infrastructure/platform/infrastructure/nominatim/nominatim-init-job.yaml +++ b/infrastructure/platform/nominatim/nominatim-helm/templates/init-job.yaml @@ -1,24 +1,25 @@ +{{- if .Values.initJob.enabled }} apiVersion: batch/v1 kind: Job metadata: - name: nominatim-init - namespace: bakery-ia + name: {{ include "nominatim.initJobName" . }} + namespace: {{ .Values.global.namespace }} labels: - app.kubernetes.io/name: nominatim-init + {{- include "nominatim.labels" . | nindent 4 }} app.kubernetes.io/component: data-init - app.kubernetes.io/part-of: bakery-ia spec: - ttlSecondsAfterFinished: 86400 + ttlSecondsAfterFinished: {{ .Values.initJob.ttlSecondsAfterFinished }} template: metadata: labels: - app.kubernetes.io/name: nominatim-init + app.kubernetes.io/name: {{ include "nominatim.initJobName" . }} app.kubernetes.io/component: data-init spec: restartPolicy: OnFailure containers: - name: nominatim-import - image: mediagis/nominatim:4.4 + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} command: - sh - -c @@ -50,34 +51,30 @@ spec: - name: NOMINATIM_PBF_URL valueFrom: configMapKeyRef: - name: nominatim-config + name: {{ include "nominatim.configMapName" . }} key: NOMINATIM_PBF_URL - name: NOMINATIM_IMPORT_STYLE valueFrom: configMapKeyRef: - name: nominatim-config + name: {{ include "nominatim.configMapName" . }} key: NOMINATIM_IMPORT_STYLE - name: NOMINATIM_THREADS valueFrom: configMapKeyRef: - name: nominatim-config + name: {{ include "nominatim.configMapName" . }} key: NOMINATIM_THREADS - name: NOMINATIM_FLATNODE_FILE valueFrom: configMapKeyRef: - name: nominatim-config + name: {{ include "nominatim.configMapName" . }} key: NOMINATIM_FLATNODE_FILE resources: - requests: - memory: "8Gi" - cpu: "4" - limits: - memory: "16Gi" - cpu: "8" + {{- toYaml .Values.initJob.resources | nindent 10 }} volumes: - name: nominatim-data persistentVolumeClaim: - claimName: nominatim-data + claimName: {{ include "nominatim.dataPvcName" . }} - name: nominatim-flatnode persistentVolumeClaim: - claimName: nominatim-flatnode + claimName: {{ include "nominatim.flatnodePvcName" . }} +{{- end }} diff --git a/infrastructure/platform/nominatim/nominatim-helm/templates/pvc.yaml b/infrastructure/platform/nominatim/nominatim-helm/templates/pvc.yaml new file mode 100644 index 00000000..1de9d934 --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/templates/pvc.yaml @@ -0,0 +1,37 @@ +{{- if .Values.persistence.data.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "nominatim.dataPvcName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "nominatim.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.persistence.data.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.data.size }} + {{- if .Values.persistence.data.storageClassName }} + storageClassName: {{ .Values.persistence.data.storageClassName }} + {{- end }} +{{- end }} +--- +{{- if .Values.persistence.flatnode.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "nominatim.flatnodePvcName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "nominatim.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.persistence.flatnode.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.flatnode.size }} + {{- if .Values.persistence.flatnode.storageClassName }} + storageClassName: {{ .Values.persistence.flatnode.storageClassName }} + {{- end }} +{{- end }} diff --git a/infrastructure/platform/nominatim/nominatim-helm/templates/service.yaml b/infrastructure/platform/nominatim/nominatim-helm/templates/service.yaml new file mode 100644 index 00000000..af920457 --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/templates/service.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "nominatim.serviceName" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "nominatim.labels" . | nindent 4 }} + {{- with .Values.serviceAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + {{- include "nominatim.selectorLabels" . | nindent 4 }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.port }} + protocol: TCP + name: http + type: {{ .Values.service.type }} diff --git a/infrastructure/platform/nominatim/nominatim-helm/templates/statefulset.yaml b/infrastructure/platform/nominatim/nominatim-helm/templates/statefulset.yaml new file mode 100644 index 00000000..562071b3 --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/templates/statefulset.yaml @@ -0,0 +1,113 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "nominatim.fullname" . }} + namespace: {{ .Values.global.namespace }} + labels: + {{- include "nominatim.labels" . | nindent 4 }} +spec: + serviceName: {{ include "nominatim.serviceName" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "nominatim.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "nominatim.selectorLabels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: nominatim + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + ports: + - containerPort: {{ .Values.service.port }} + name: http + volumeMounts: + - name: nominatim-data + mountPath: /var/lib/postgresql + - name: nominatim-flatnode + mountPath: /nominatim-flatnode + env: + - name: NOMINATIM_PBF_URL + valueFrom: + configMapKeyRef: + name: {{ include "nominatim.configMapName" . }} + key: NOMINATIM_PBF_URL + - name: NOMINATIM_REPLICATION_URL + valueFrom: + configMapKeyRef: + name: {{ include "nominatim.configMapName" . }} + key: NOMINATIM_REPLICATION_URL + - name: NOMINATIM_IMPORT_STYLE + valueFrom: + configMapKeyRef: + name: {{ include "nominatim.configMapName" . }} + key: NOMINATIM_IMPORT_STYLE + - name: NOMINATIM_THREADS + valueFrom: + configMapKeyRef: + name: {{ include "nominatim.configMapName" . }} + key: NOMINATIM_THREADS + - name: NOMINATIM_FLATNODE_FILE + valueFrom: + configMapKeyRef: + name: {{ include "nominatim.configMapName" . }} + key: NOMINATIM_FLATNODE_FILE + {{- range $key, $value := .Values.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + {{- if .Values.probes.liveness.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: {{ .Values.probes.liveness.port }} + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + {{- end }} + {{- if .Values.probes.readiness.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: {{ .Values.probes.readiness.port }} + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + {{- end }} + volumes: + - name: nominatim-data + persistentVolumeClaim: + claimName: {{ include "nominatim.dataPvcName" . }} + - name: nominatim-flatnode + persistentVolumeClaim: + claimName: {{ include "nominatim.flatnodePvcName" . }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/infrastructure/platform/nominatim/nominatim-helm/values.yaml b/infrastructure/platform/nominatim/nominatim-helm/values.yaml new file mode 100644 index 00000000..91d4dc59 --- /dev/null +++ b/infrastructure/platform/nominatim/nominatim-helm/values.yaml @@ -0,0 +1,113 @@ +# Default values for Nominatim geocoding service +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# Global settings +global: + namespace: "bakery-ia" + +# Nominatim image configuration +image: + repository: "mediagis/nominatim" + tag: "4.4" + pullPolicy: "IfNotPresent" + +# StatefulSet configuration +replicaCount: 1 + +# Nominatim configuration +config: + # Spain OSM data source + pbfUrl: "http://download.geofabrik.de/europe/spain-latest.osm.pbf" + # Updates replication source + replicationUrl: "https://download.geofabrik.de/europe/spain-updates" + # Import style (address for geocoding-focused usage) + importStyle: "address" + # Number of threads for indexing + threads: "4" + # Flatnode file path + flatnodeFile: "/nominatim-flatnode/flatnode.bin" + +# Service configuration +service: + type: "ClusterIP" + port: 8080 + name: "nominatim-service" + +# Resource limits and requests for main service +resources: + requests: + cpu: "1" + memory: "2Gi" + limits: + cpu: "2" + memory: "4Gi" + +# Init job resource limits (higher for initial import) +initJob: + enabled: true + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "8" + memory: "16Gi" + # Time to keep job after completion (86400 = 1 day) + ttlSecondsAfterFinished: 86400 + +# Persistent Volume Claims +persistence: + data: + enabled: true + size: "50Gi" + accessMode: "ReadWriteOnce" + # storageClassName: "" # Use default storage class + flatnode: + enabled: true + size: "20Gi" + accessMode: "ReadWriteOnce" + # storageClassName: "" # Use default storage class + +# Health probes configuration +probes: + liveness: + enabled: true + path: "/status" + port: 8080 + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readiness: + enabled: true + path: "/status" + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + +# Additional environment variables +env: {} + +# Node selector +nodeSelector: {} + +# Tolerations +tolerations: [] + +# Affinity +affinity: {} + +# Pod annotations +podAnnotations: {} + +# Service annotations +serviceAnnotations: {} + +# Pod security context +podSecurityContext: {} + +# Container security context +securityContext: {} diff --git a/privkey.pem b/privkey.pem new file mode 100644 index 00000000..1c65efdb --- /dev/null +++ b/privkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCWcDUo744vZK0u +YVHv1GDpLGXt749LwfFBL2K6ZL2Kiln3r5IAyrsFsKGLqDhnaI7oTOfiHdW83yHT +9AbnZojYimH00Hzss+Z8VmzQWUtNC6F8K3uMXnjzdIYIzKAX47qZOw8V/0e0csC5 +limyB5K/3Ln7eJyyPcHv3vWYl7wKpL2scw+g5AzHi70u68aGhtXsQOmL4IjzaHIi +xNyJVmiv8cd/JMUgUAvoSNekUSV8sSd/Tr7QSASXS+rFnCiFbAd2IiDGBdJJc9PM +M3uOzOiZnZlnU3yd+/xojAz9c9T8XGpb0VAlQofq81+MmbUH9tH7i38XDyu9yEkj +0Jmvr8fNAgMBAAECggEAG9I0gpWK3gjoXiNxW8ETCwxC7XXYMlJzo3J397CZvP1J +Fh1KCwty+uJnfcRmaflUNHHEkHSkCoHZZ7FBdZnoxOMSqovEDqJWhbgV95IL8vur +qaMA/jyacaui/g2ZrdFXuO7LI25KfHycV7YFj/8GjKTtYCw2DmCNdSbulIG7LNxy +ARBHbx3dKUn/t3ahylRsOyedRvF2j207ZTq9xkvcMLRzbSVFSrgYclQuqtQ6TjDL +F3WUXx59fxTSrwjV7QYzSPZuSfw5wBlBF5yiRzuJU4YN5wQvrLGG9qgimO8/+uds +6QbnIEUNe73oxw207zdywlMGyRd5vui6FtToF9YQoQKBgQDFgBoVGz+G2Ng4St9x +KujUKnloaxaa/Nmj0OoxcoCZljUAicA2gi5KeRMOMxrkVYLQBM13iA8uo7j3YPp9 +axqZHwadplsvUh2+vCZKLA/JTjGQzSihxnBpKk0He/+wFOQ6V9AKN7ffUYIyim6n +3zLupxZ3y9Rfgdp6HdCb9nm/hwKBgQDC/4U9cxy+jr13rgKs3hrpQ6Qt2loenBM5 +Ziu2ughH0pWoAIfSYK3whxtny2JrAhwQrABEpfGZVw1r9gvZnd0rGupbEUAaLCmd +vyjdfdI7iJrPYYQaMJIWiMi2EF5+7GHi898i95eMXQuxuDA0mIkK1UOp7n+C7qd+ +QwWXDehLCwKBgCSYyDyBABSMugQ0W5Ms0FgARt8CeP3fPLUOUVc4UHwlSU0AOY3g +MZO7O7y125XUAplpSmmL3MRXsj6kycXTwun8xc0QtnTeUoS1eKLl50b2JlkeqxjP +HKVgIUXxxD9sn53wB6zdBkVrZSTYYgjZYya174PjUUchWMqoG6/KzGShAoGARDU0 +2gXF7DHpvE81yFn4d9edOhzCoSpe3xkJ+WShON5EUvu8hq4iqZvYzjmqN1wJjRtd +DKYvGEAHBdiO1JQPpOBJUYl7Lqx78h3HoZI9U225GQk3OCH9N7yo2GZpZ2Qv6T78 +sjKA5Cw3xvZyhjNE3HE0teAi4h2woM24ytmmg3kCgYEAptN1OocgACqd8DaTDOWl +PYVWEyT2kUmjhaF6jxBxP6QnWBAEup7iWHPc12B3aVs/DKczxSfjSIia8uQac9A/ +eymSX05W+jP6sowZLmUq07cUdzx5AiC/eFz3xmZpftCsFgESLpuucu5CJ9uNKrkA +tgmk0sFQoyqY0TdXwDumIpU= +-----END PRIVATE KEY----- diff --git a/scripts/prepull-base-images.sh b/scripts/prepull-base-images.sh index 1b46502a..e2760cdc 100755 --- a/scripts/prepull-base-images.sh +++ b/scripts/prepull-base-images.sh @@ -53,8 +53,6 @@ BASE_IMAGES=( "ghcr.io/mailu/postfix:2024.06" "ghcr.io/mailu/dovecot:2024.06" "ghcr.io/mailu/rspamd:2024.06" - # DNS resolver (Unbound) - "mvance/unbound:latest" ) # Local registry configuration diff --git a/services/ai_insights/Dockerfile b/services/ai_insights/Dockerfile index 3e5af067..77fd43bf 100644 --- a/services/ai_insights/Dockerfile +++ b/services/ai_insights/Dockerfile @@ -1,11 +1,21 @@ -# AI Insights Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# AI Insights Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/alert_processor/Dockerfile b/services/alert_processor/Dockerfile index 9c050e62..5b5b2e97 100644 --- a/services/alert_processor/Dockerfile +++ b/services/alert_processor/Dockerfile @@ -1,11 +1,21 @@ -# Alert Processor Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Alert Processor Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/auth/Dockerfile b/services/auth/Dockerfile index fa95d11a..56075824 100644 --- a/services/auth/Dockerfile +++ b/services/auth/Dockerfile @@ -1,11 +1,21 @@ -# Auth Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Auth Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} # Create non-root user for security RUN groupadd -r appgroup && useradd -r -g appgroup appuser diff --git a/services/demo_session/Dockerfile b/services/demo_session/Dockerfile index 4dfbcb1d..56201d1d 100644 --- a/services/demo_session/Dockerfile +++ b/services/demo_session/Dockerfile @@ -1,11 +1,21 @@ -# Demo Session Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Demo Session Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/distribution/Dockerfile b/services/distribution/Dockerfile index 66be8ee6..57386c0e 100644 --- a/services/distribution/Dockerfile +++ b/services/distribution/Dockerfile @@ -1,11 +1,21 @@ -# Distribution Service Dockerfile -# Stage 1: Copy shared libraries -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Distribution Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Stage 2: Main service -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/external/Dockerfile b/services/external/Dockerfile index d5102adc..8f044d9d 100644 --- a/services/external/Dockerfile +++ b/services/external/Dockerfile @@ -1,11 +1,21 @@ -# External Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# External Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/forecasting/Dockerfile b/services/forecasting/Dockerfile index dc3da2c1..7eb8c870 100644 --- a/services/forecasting/Dockerfile +++ b/services/forecasting/Dockerfile @@ -1,11 +1,21 @@ -# Forecasting Service Dockerfile with MinIO Support -# Multi-stage build for optimized production image -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Forecasting Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/inventory/Dockerfile b/services/inventory/Dockerfile index 15b802b4..fcb6e446 100644 --- a/services/inventory/Dockerfile +++ b/services/inventory/Dockerfile @@ -1,11 +1,21 @@ -# Inventory Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Inventory Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/notification/Dockerfile b/services/notification/Dockerfile index fcc89bb2..4c313fa0 100644 --- a/services/notification/Dockerfile +++ b/services/notification/Dockerfile @@ -1,11 +1,21 @@ -# Notification Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Notification Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/orchestrator/Dockerfile b/services/orchestrator/Dockerfile index 6d71ba51..fef2df85 100644 --- a/services/orchestrator/Dockerfile +++ b/services/orchestrator/Dockerfile @@ -1,11 +1,21 @@ -# Orchestrator Service Dockerfile -# Stage 1: Copy shared libraries -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Orchestrator Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Stage 2: Main service -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/orders/Dockerfile b/services/orders/Dockerfile index 65df3a32..ad22d094 100644 --- a/services/orders/Dockerfile +++ b/services/orders/Dockerfile @@ -1,11 +1,21 @@ -# Orders Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Orders Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/pos/Dockerfile b/services/pos/Dockerfile index f61425cc..f2508bf8 100644 --- a/services/pos/Dockerfile +++ b/services/pos/Dockerfile @@ -1,11 +1,21 @@ -# Pos Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# POS Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/procurement/Dockerfile b/services/procurement/Dockerfile index 4f31b0b3..42de317e 100644 --- a/services/procurement/Dockerfile +++ b/services/procurement/Dockerfile @@ -1,11 +1,21 @@ -# Procurement Service Dockerfile -# Stage 1: Copy shared libraries -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Procurement Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Stage 2: Main service -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/production/Dockerfile b/services/production/Dockerfile index b962d314..52c4c97e 100644 --- a/services/production/Dockerfile +++ b/services/production/Dockerfile @@ -1,11 +1,21 @@ -# Production Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Production Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/recipes/Dockerfile b/services/recipes/Dockerfile index 8b24dd2f..4f228bdd 100644 --- a/services/recipes/Dockerfile +++ b/services/recipes/Dockerfile @@ -1,11 +1,21 @@ -# Recipes Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Recipes Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/sales/Dockerfile b/services/sales/Dockerfile index 4fb736f9..cee6d782 100644 --- a/services/sales/Dockerfile +++ b/services/sales/Dockerfile @@ -1,11 +1,21 @@ -# Sales Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Sales Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/suppliers/Dockerfile b/services/suppliers/Dockerfile index 60a63d0a..1d9f1794 100644 --- a/services/suppliers/Dockerfile +++ b/services/suppliers/Dockerfile @@ -1,11 +1,21 @@ -# Suppliers Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Suppliers Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/tenant/Dockerfile b/services/tenant/Dockerfile index cbce384c..6dcb10e2 100644 --- a/services/tenant/Dockerfile +++ b/services/tenant/Dockerfile @@ -1,11 +1,21 @@ -# Tenant Dockerfile -# Add this stage at the top of each service Dockerfile -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Tenant Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Then your main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/services/training/Dockerfile b/services/training/Dockerfile index 1b2ed41f..161be6de 100644 --- a/services/training/Dockerfile +++ b/services/training/Dockerfile @@ -1,11 +1,21 @@ -# Training Service Dockerfile with MinIO Support -# Multi-stage build for optimized production image -FROM localhost:5000/python_3.11-slim AS shared +# ============================================================================= +# Training Service Dockerfile - Environment-Configurable Base Images +# ============================================================================= +# Build arguments for registry configuration: +# - BASE_REGISTRY: Registry URL (default: docker.io for Docker Hub) +# - PYTHON_IMAGE: Python image name and tag (default: python:3.11-slim) +# ============================================================================= + +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim + +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} AS shared WORKDIR /shared COPY shared/ /shared/ -# Main service stage -FROM localhost:5000/python_3.11-slim +ARG BASE_REGISTRY=docker.io +ARG PYTHON_IMAGE=python:3.11-slim +FROM ${BASE_REGISTRY}/${PYTHON_IMAGE} WORKDIR /app diff --git a/skaffold.yaml b/skaffold.yaml index 8cb6c234..79b114cf 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -3,6 +3,23 @@ kind: Config metadata: name: bakery-ia +# ============================================================================= +# BUILD CONFIGURATION +# ============================================================================= +# Environment-configurable base images via Docker ARG: +# - BASE_REGISTRY: Registry URL for base images +# - PYTHON_IMAGE: Python image name and tag +# +# Dev (default): BASE_REGISTRY=localhost:5000, PYTHON_IMAGE=python_3.11-slim +# Prod: BASE_REGISTRY=docker.io, PYTHON_IMAGE=python:3.11-slim +# +# Usage: +# skaffold dev # Uses dev settings (local registry) +# skaffold dev -p debug # Dev with port forwarding +# skaffold run -p prod # Production build with Docker Hub +# BASE_REGISTRY=ghcr.io/myorg skaffold run -p prod # Production with custom registry +# ============================================================================= + build: local: push: false @@ -15,88 +32,136 @@ build: context: . docker: dockerfile: gateway/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - # Frontend + # Frontend (no Python base image needed) - image: bakery/dashboard context: ./frontend docker: dockerfile: Dockerfile.kubernetes - # Microservices + # Microservices - all use configurable Python base image - image: bakery/auth-service context: . docker: dockerfile: services/auth/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/tenant-service context: . docker: dockerfile: services/tenant/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/training-service context: . docker: dockerfile: services/training/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/forecasting-service context: . docker: dockerfile: services/forecasting/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/sales-service context: . docker: dockerfile: services/sales/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/external-service context: . docker: dockerfile: services/external/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/notification-service context: . docker: dockerfile: services/notification/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/inventory-service context: . docker: dockerfile: services/inventory/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/recipes-service context: . docker: dockerfile: services/recipes/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/suppliers-service context: . docker: dockerfile: services/suppliers/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/pos-service context: . docker: dockerfile: services/pos/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/orders-service context: . docker: dockerfile: services/orders/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/production-service context: . docker: dockerfile: services/production/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/alert-processor context: . docker: dockerfile: services/alert_processor/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim - image: bakery/demo-session-service context: . docker: dockerfile: services/demo_session/Dockerfile + buildArgs: + BASE_REGISTRY: localhost:5000 + PYTHON_IMAGE: python_3.11-slim deploy: kustomize: @@ -110,7 +175,7 @@ deploy: - host: command: ["sh", "-c", "echo '======================================'"] - host: - command: ["sh", "-c", "echo '🔐 Bakery IA Secure Deployment'"] + command: ["sh", "-c", "echo 'Bakery IA Secure Deployment'"] - host: command: ["sh", "-c", "echo '======================================'"] - host: @@ -130,19 +195,19 @@ deploy: - host: command: ["sh", "-c", "echo ''"] - host: - command: ["kubectl", "apply", "-f", "infrastructure/environments/dev/k8s-manifests/base/secrets.yaml"] + command: ["kubectl", "apply", "-f", "infrastructure/environments/common/configs/secrets.yaml"] - host: - command: ["kubectl", "apply", "-f", "infrastructure/environments/dev/k8s-manifests/base/secrets/postgres-tls-secret.yaml"] + command: ["kubectl", "apply", "-f", "infrastructure/platform/storage/postgres/secrets/postgres-tls-secret.yaml"] - host: - command: ["kubectl", "apply", "-f", "infrastructure/environments/dev/k8s-manifests/base/secrets/redis-tls-secret.yaml"] + command: ["kubectl", "apply", "-f", "infrastructure/platform/storage/redis/secrets/redis-tls-secret.yaml"] - host: - command: ["kubectl", "apply", "-f", "infrastructure/environments/dev/k8s-manifests/base/configs/postgres-init-config.yaml"] + command: ["kubectl", "apply", "-f", "infrastructure/platform/storage/postgres/configs/postgres-init-config.yaml"] - host: - command: ["kubectl", "apply", "-f", "infrastructure/environments/dev/k8s-manifests/base/configmaps/postgres-logging-config.yaml"] + command: ["kubectl", "apply", "-f", "infrastructure/platform/storage/postgres/configs/postgres-logging-config.yaml"] - host: command: ["sh", "-c", "echo ''"] - host: - command: ["sh", "-c", "echo '✅ Security configurations applied'"] + command: ["sh", "-c", "echo 'Security configurations applied'"] - host: command: ["sh", "-c", "echo ''"] after: @@ -151,7 +216,7 @@ deploy: - host: command: ["sh", "-c", "echo '======================================'"] - host: - command: ["sh", "-c", "echo '✅ Deployment Complete!'"] + command: ["sh", "-c", "echo 'Deployment Complete!'"] - host: command: ["sh", "-c", "echo '======================================'"] - host: @@ -159,15 +224,15 @@ deploy: - host: command: ["sh", "-c", "echo 'Security Features Enabled:'"] - host: - command: ["sh", "-c", "echo ' ✅ TLS encryption for all database connections'"] + command: ["sh", "-c", "echo ' - TLS encryption for all database connections'"] - host: - command: ["sh", "-c", "echo ' ✅ Strong 32-character passwords'"] + command: ["sh", "-c", "echo ' - Strong 32-character passwords'"] - host: - command: ["sh", "-c", "echo ' ✅ Persistent storage (PVCs) - no data loss'"] + command: ["sh", "-c", "echo ' - Persistent storage (PVCs) - no data loss'"] - host: - command: ["sh", "-c", "echo ' ✅ pgcrypto extension for column encryption'"] + command: ["sh", "-c", "echo ' - pgcrypto extension for column encryption'"] - host: - command: ["sh", "-c", "echo ' ✅ PostgreSQL audit logging enabled'"] + command: ["sh", "-c", "echo ' - PostgreSQL audit logging enabled'"] - host: command: ["sh", "-c", "echo ''"] - host: @@ -179,20 +244,13 @@ deploy: - host: command: ["sh", "-c", "echo ''"] -# Default deployment uses dev overlay with full security features -# Access via ingress: http://localhost (or https://localhost) -# -# Available profiles: -# - dev: Local development with full security (default) -# - debug: Local development with port forwarding for debugging -# - prod: Production deployment with production settings -# -# Usage: -# skaffold dev # Uses secure dev overlay -# skaffold dev -p debug # Use debug profile with port forwarding -# skaffold run -p prod # Use prod profile for production - +# ============================================================================= +# PROFILES +# ============================================================================= profiles: + # --------------------------------------------------------------------------- + # DEV PROFILE - Local development with local registry + # --------------------------------------------------------------------------- - name: dev activation: - command: dev @@ -207,6 +265,9 @@ profiles: paths: - infrastructure/environments/dev/k8s-manifests + # --------------------------------------------------------------------------- + # DEBUG PROFILE - Dev with port forwarding for debugging + # --------------------------------------------------------------------------- - name: debug activation: - command: debug @@ -237,14 +298,158 @@ profiles: port: 8000 localPort: 8001 + # --------------------------------------------------------------------------- + # PROD PROFILE - Production deployment with Docker Hub base images + # --------------------------------------------------------------------------- + # Usage: + # skaffold run -p prod # Default Docker Hub + # BASE_REGISTRY=ghcr.io/myorg skaffold run -p prod # Custom registry + # --------------------------------------------------------------------------- - name: prod build: local: - push: false + push: true tagPolicy: gitCommit: variant: AbbrevCommitSha + artifacts: + # Gateway - Production base images + - image: bakery/gateway + context: . + docker: + dockerfile: gateway/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + # Frontend + - image: bakery/dashboard + context: ./frontend + docker: + dockerfile: Dockerfile.kubernetes + + # Microservices - Production base images (docker.io/python:3.11-slim) + - image: bakery/auth-service + context: . + docker: + dockerfile: services/auth/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/tenant-service + context: . + docker: + dockerfile: services/tenant/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/training-service + context: . + docker: + dockerfile: services/training/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/forecasting-service + context: . + docker: + dockerfile: services/forecasting/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/sales-service + context: . + docker: + dockerfile: services/sales/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/external-service + context: . + docker: + dockerfile: services/external/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/notification-service + context: . + docker: + dockerfile: services/notification/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/inventory-service + context: . + docker: + dockerfile: services/inventory/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/recipes-service + context: . + docker: + dockerfile: services/recipes/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/suppliers-service + context: . + docker: + dockerfile: services/suppliers/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/pos-service + context: . + docker: + dockerfile: services/pos/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/orders-service + context: . + docker: + dockerfile: services/orders/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/production-service + context: . + docker: + dockerfile: services/production/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/alert-processor + context: . + docker: + dockerfile: services/alert_processor/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + + - image: bakery/demo-session-service + context: . + docker: + dockerfile: services/demo_session/Dockerfile + buildArgs: + BASE_REGISTRY: docker.io + PYTHON_IMAGE: "python:3.11-slim" + deploy: kustomize: paths: - - infrastructure/environments/prod/k8s-manifests \ No newline at end of file + - infrastructure/environments/prod/k8s-manifests