Install the platform
ZEN-INTEL is installed by applying a set of Kubernetes manifests. Have your filled-in secrets from Configuration ready.
Prerequisites
Section titled “Prerequisites”Install these cluster-wide controllers first:
- CloudNativePG operator (provides PostgreSQL):
Terminal window kubectl apply --server-side -f \https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yamlkubectl rollout status deployment -n cnpg-system cnpg-controller-manager - ingress-nginx controller (provides the public load balancer and routing).
- cert-manager (optional, for automatic TLS).
1. Create the namespace
Section titled “1. Create the namespace”apiVersion: v1kind: Namespacemetadata: name: zenintelkubectl apply -f namespace.yaml2. Apply the secrets
Section titled “2. Apply the secrets”Apply the per-service secrets you prepared in Configuration.
kubectl apply -f configs/3. Deploy the data services
Section titled “3. Deploy the data services”PostgreSQL is a CloudNativePG Cluster. Redis and RabbitMQ follow in the same step.
# PostgreSQL provided by the CloudNativePG operator.# The operator must be installed cluster-wide first (see README).# CloudNativePG creates a read/write Service named postgres-rw, which the# backend and workers reach via DATABASE_URL.apiVersion: v1kind: Secretmetadata: name: zenintel-pg-app namespace: zeninteltype: kubernetes.io/basic-authstringData: # username must match spec.bootstrap.initdb.owner below username: zenintel password: CHANGEME---apiVersion: postgresql.cnpg.io/v1kind: Clustermetadata: name: postgres namespace: zenintelspec: instances: 2 # increase to 3 for production high availability bootstrap: initdb: database: zenintel owner: zenintel secret: name: zenintel-pg-app storage: size: 100Gi resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 1Gikubectl apply -f data/kubectl wait --for=condition=Ready cluster/postgres -n zenintel --timeout=300s4. Deploy the application
Section titled “4. Deploy the application”The backend and the three workers share the backend image and Secret. The ai-service and frontend have their own.
apiVersion: v1kind: Servicemetadata: name: backend namespace: zenintelspec: selector: app: backend ports: - name: http port: 8080 targetPort: 8080---apiVersion: apps/v1kind: Deploymentmetadata: name: backend namespace: zenintelspec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: backend image: 337853574763.dkr.ecr.ap-southeast-3.amazonaws.com/zenintel/backend:v1.0.0 ports: - containerPort: 8080 envFrom: - secretRef: name: zenintel-backend-secret securityContext: capabilities: add: ["NET_RAW"] readinessProbe: httpGet: path: /api/health port: 8080 initialDelaySeconds: 15 periodSeconds: 15 livenessProbe: httpGet: path: /api/health port: 8080 initialDelaySeconds: 30 periodSeconds: 30 resources: requests: cpu: 250m memory: 256Mi limits: cpu: "1" memory: 1Gi# Built from the backend image with a different command. No inbound traffic.apiVersion: apps/v1kind: Deploymentmetadata: name: ai-worker namespace: zenintelspec: replicas: 1 selector: matchLabels: app: ai-worker template: metadata: labels: app: ai-worker spec: containers: - name: ai-worker image: 337853574763.dkr.ecr.ap-southeast-3.amazonaws.com/zenintel/backend:v1.0.0 command: ["/app/ai-worker"] envFrom: - secretRef: name: zenintel-backend-secret resources: requests: cpu: 250m memory: 256Mi limits: cpu: "1" memory: 1Gi# Built from the backend image with a different command. No inbound traffic.# Needs NET_RAW for the port and network scan tools.apiVersion: apps/v1kind: Deploymentmetadata: name: asm-worker namespace: zenintelspec: replicas: 1 selector: matchLabels: app: asm-worker template: metadata: labels: app: asm-worker spec: containers: - name: asm-worker image: 337853574763.dkr.ecr.ap-southeast-3.amazonaws.com/zenintel/backend:v1.0.0 command: ["/app/asm-worker"] envFrom: - secretRef: name: zenintel-backend-secret securityContext: capabilities: add: ["NET_RAW"] resources: requests: cpu: 250m memory: 256Mi limits: cpu: "2" memory: 1Gi# Built from the backend image with a different command. No inbound traffic.apiVersion: apps/v1kind: Deploymentmetadata: name: brand-worker namespace: zenintelspec: replicas: 1 selector: matchLabels: app: brand-worker template: metadata: labels: app: brand-worker spec: containers: - name: brand-worker image: 337853574763.dkr.ecr.ap-southeast-3.amazonaws.com/zenintel/backend:v1.0.0 command: ["/app/brand-worker"] envFrom: - secretRef: name: zenintel-backend-secret resources: requests: cpu: 250m memory: 256Mi limits: cpu: "1" memory: 1Gi# Internal only. Called by the backend and workers via AI_SERVICE_URL.apiVersion: v1kind: Servicemetadata: name: ai-service namespace: zenintelspec: selector: app: ai-service ports: - name: http port: 8081 targetPort: 8081---apiVersion: apps/v1kind: Deploymentmetadata: name: ai-service namespace: zenintelspec: replicas: 2 selector: matchLabels: app: ai-service template: metadata: labels: app: ai-service spec: containers: - name: ai-service image: 337853574763.dkr.ecr.ap-southeast-3.amazonaws.com/zenintel/ai-service:v1.0.0 ports: - containerPort: 8081 envFrom: - secretRef: name: zenintel-ai-service-secret readinessProbe: tcpSocket: port: 8081 initialDelaySeconds: 10 periodSeconds: 10 resources: requests: cpu: 250m memory: 512Mi limits: cpu: "2" memory: 1GiapiVersion: v1kind: Servicemetadata: name: frontend namespace: zenintelspec: selector: app: frontend ports: - name: http port: 3000 targetPort: 3000---apiVersion: apps/v1kind: Deploymentmetadata: name: frontend namespace: zenintelspec: replicas: 2 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: frontend image: 337853574763.dkr.ecr.ap-southeast-3.amazonaws.com/zenintel/frontend:v1.0.0 ports: - containerPort: 3000 envFrom: - secretRef: name: zenintel-frontend-secret readinessProbe: tcpSocket: port: 3000 initialDelaySeconds: 5 periodSeconds: 10 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mikubectl apply -f services/5. Run database migrations
Section titled “5. Run database migrations”Apply the schema migrations shipped with the release. The exact mechanism depends on how migrations are packaged (a Job, an init container, or a one-off command). Follow the release notes.
6. Configure the ingress
Section titled “6. Configure the ingress”The ai-service is internal, so the ingress routes only to the frontend and backend.
# Requires the ingress-nginx controller. The controller provisions the public# load balancer. This resource defines routing and TLS for the hostname.# The ai-service is internal only and is not exposed through the ingress.apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: zenintel namespace: zenintel annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true"spec: ingressClassName: nginx tls: - hosts: - zen-intel.example.com secretName: zenintel-tls rules: - host: zen-intel.example.com http: paths: - path: /api pathType: Prefix backend: service: name: backend port: number: 8080 - path: / pathType: Prefix backend: service: name: frontend port: number: 3000kubectl apply -f ingress/