Fix frontend API connectivity: switch to nginx, add runtime env injection, update chart ports
This commit is contained in:
parent
88ec0585a7
commit
5c8481304f
@ -1,7 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
TEMPLATE="/etc/env/env.js.template"
|
# Template is in dist (copied by Dockerfile builder stage)
|
||||||
|
TEMPLATE="/usr/share/nginx/html/env.js.template"
|
||||||
TARGET="/usr/share/nginx/html/env.js"
|
TARGET="/usr/share/nginx/html/env.js"
|
||||||
|
|
||||||
if [ -f "$TEMPLATE" ]; then
|
if [ -f "$TEMPLATE" ]; then
|
||||||
@ -16,8 +17,16 @@ window.__ENV__ = {
|
|||||||
EOF
|
EOF
|
||||||
echo "✓ env.js generated at $TARGET"
|
echo "✓ env.js generated at $TARGET"
|
||||||
else
|
else
|
||||||
echo "No env.js.template found at $TEMPLATE, skipping generation"
|
echo "Warning: env.js.template not found at $TEMPLATE, creating default env.js"
|
||||||
|
# Fallback: create env.js with default value
|
||||||
|
: ${API_BASE:=/api}
|
||||||
|
cat > "$TARGET" <<EOF
|
||||||
|
window.__ENV__ = {
|
||||||
|
API_BASE: "${API_BASE}"
|
||||||
|
};
|
||||||
|
EOF
|
||||||
|
echo "✓ Default env.js created at $TARGET"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure ownership/permissions are OK
|
# Ensure ownership/permissions are OK for nginx
|
||||||
chown -R nginx:nginx /usr/share/nginx/html || true
|
chown -R nginx:nginx /usr/share/nginx/html || true
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM node:22-alpine AS builder
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@ -15,18 +15,25 @@ COPY . .
|
|||||||
# Build the application
|
# Build the application
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Production stage
|
# Copy env template to dist so it's available in production
|
||||||
FROM node:22-alpine
|
COPY public/env.js.template dist/env.js.template
|
||||||
|
|
||||||
WORKDIR /app
|
# Production stage - use nginx to serve static files and proxy API calls
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
# Install a simple HTTP server to serve the built app
|
# Copy nginx config
|
||||||
RUN npm install -g serve
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
# Copy built app from builder stage
|
# Copy built app from builder stage
|
||||||
COPY --from=builder /app/dist ./dist
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
EXPOSE 3000
|
# Copy entrypoint script to nginx entrypoint.d directory
|
||||||
|
# This will run before nginx starts and generate env.js from template
|
||||||
|
COPY 10-generate-env.sh /docker-entrypoint.d/10-generate-env.sh
|
||||||
|
|
||||||
# Serve the built app
|
# Ensure entrypoint script is executable
|
||||||
CMD ["serve", "-s", "dist", "-l", "3000"]
|
RUN chmod +x /docker-entrypoint.d/10-generate-env.sh
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# nginx will start automatically; our script in /docker-entrypoint.d runs first
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>frontend</title>
|
<title>frontend</title>
|
||||||
|
<!-- Load environment variables before app starts -->
|
||||||
|
<script src="/env.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
96
manifests/my-recipes/values.yaml
Normal file
96
manifests/my-recipes/values.yaml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
global:
|
||||||
|
namespace: my-apps
|
||||||
|
imagePullSecrets: []
|
||||||
|
# Backend configuration
|
||||||
|
backend:
|
||||||
|
name: backend
|
||||||
|
replicaCount: 1
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/my-recipes-backend
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: "master-895786b"
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8000
|
||||||
|
targetPort: 8000
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
env:
|
||||||
|
PYTHONUNBUFFERED: "1"
|
||||||
|
tag: master-895786b
|
||||||
|
# Frontend configuration
|
||||||
|
frontend:
|
||||||
|
name: frontend
|
||||||
|
replicaCount: 1
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/my-recipes-frontend
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: "master-895786b"
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
env:
|
||||||
|
API_BASE: "https://my-recipes.dvirlabs.com/api"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
tag: master-bd31ffb
|
||||||
|
# PostgreSQL configuration
|
||||||
|
postgres:
|
||||||
|
name: db
|
||||||
|
image:
|
||||||
|
repository: postgres
|
||||||
|
tag: "16"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
user: recipes_user
|
||||||
|
password: recipes_password # POC only – later use Secret/ExternalSecret
|
||||||
|
database: recipes_db
|
||||||
|
port: 5432
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 5432
|
||||||
|
targetPort: 5432
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
storageClass: "nfs-client"
|
||||||
|
size: 8Gi
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 1Gi
|
||||||
|
# Ingress (top-level, maps frontend/backend paths)
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: "nginx"
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
|
nginx.ingress.kubernetes.io/rewrite-target: "/$2"
|
||||||
|
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||||
|
hosts:
|
||||||
|
- host: my-recipes.dvirlabs.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend: frontend
|
||||||
|
# use a regex to capture and rewrite /api/... to /... on the backend
|
||||||
|
- path: /api(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend: backend
|
||||||
|
tls:
|
||||||
|
- secretName: recipes-tls
|
||||||
|
hosts:
|
||||||
|
- my-recipes.dvirlabs.com
|
||||||
47
manifests/navix/values.yaml
Normal file
47
manifests/navix/values.yaml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
frontend:
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/navix-frontend
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: master-e56328b
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 80
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: traefik
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
hosts:
|
||||||
|
- host: navix.dvirlabs.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
env:
|
||||||
|
API_BASE: "https://api-navix.dvirlabs.com/api"
|
||||||
|
MINIO_ENDPOINT: "s3.dvirlabs.com"
|
||||||
|
MINIO_BUCKET: "navix-icons"
|
||||||
|
backend:
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/navix-backend
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: master-62a2769
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8000
|
||||||
|
env:
|
||||||
|
MINIO_ACCESS_KEY: "your-access-key"
|
||||||
|
MINIO_SECRET_KEY: "your-secret-key"
|
||||||
|
MINIO_ENDPOINT: "s3.dvirlabs.com"
|
||||||
|
MINIO_BUCKET: "navix-icons"
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: traefik
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
hosts:
|
||||||
|
- host: api-navix.dvirlabs.com
|
||||||
|
paths:
|
||||||
|
- path: /api
|
||||||
|
pathType: Prefix
|
||||||
14
my-recipes-chart/Chart.yaml
Normal file
14
my-recipes-chart/Chart.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: my-recipes
|
||||||
|
description: Complete recipe management application with PostgreSQL, FastAPI backend, and React frontend
|
||||||
|
type: application
|
||||||
|
version: 1.0.0
|
||||||
|
appVersion: "1.0.0"
|
||||||
|
keywords:
|
||||||
|
- recipes
|
||||||
|
- fastapi
|
||||||
|
- react
|
||||||
|
- postgresql
|
||||||
|
maintainers:
|
||||||
|
- name: Development Team
|
||||||
|
|
||||||
69
my-recipes-chart/templates/backend-deployment.yaml
Normal file
69
my-recipes-chart/templates/backend-deployment.yaml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
|
component: backend
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.backend.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
|
component: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: {{ .Values.backend.name }}
|
||||||
|
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- containerPort: {{ .Values.backend.service.targetPort }}
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
{{- if .Values.backend.env }}
|
||||||
|
{{- range $key, $value := .Values.backend.env }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: {{ $value | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: {{ .Release.Name }}-db-credentials
|
||||||
|
startupProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /docs
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 30
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /docs
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /docs
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 2
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: {{ .Values.backend.resources.requests.cpu }}
|
||||||
|
memory: {{ .Values.backend.resources.requests.memory }}
|
||||||
|
limits:
|
||||||
|
cpu: {{ .Values.backend.resources.limits.cpu }}
|
||||||
|
memory: {{ .Values.backend.resources.limits.memory }}
|
||||||
|
|
||||||
17
my-recipes-chart/templates/backend-service.yaml
Normal file
17
my-recipes-chart/templates/backend-service.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
|
component: backend
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.backend.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.backend.service.port }}
|
||||||
|
targetPort: {{ .Values.backend.service.targetPort }}
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
||||||
@ -11,7 +11,6 @@ data:
|
|||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
meal_type TEXT NOT NULL,
|
meal_type TEXT NOT NULL,
|
||||||
time_minutes INTEGER NOT NULL,
|
time_minutes INTEGER NOT NULL,
|
||||||
made_by TEXT,
|
|
||||||
tags JSONB NOT NULL DEFAULT '[]',
|
tags JSONB NOT NULL DEFAULT '[]',
|
||||||
ingredients JSONB NOT NULL DEFAULT '[]',
|
ingredients JSONB NOT NULL DEFAULT '[]',
|
||||||
steps JSONB NOT NULL DEFAULT '[]',
|
steps JSONB NOT NULL DEFAULT '[]',
|
||||||
@ -24,9 +23,6 @@ data:
|
|||||||
CREATE INDEX IF NOT EXISTS idx_recipes_time_minutes
|
CREATE INDEX IF NOT EXISTS idx_recipes_time_minutes
|
||||||
ON recipes (time_minutes);
|
ON recipes (time_minutes);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_made_by
|
|
||||||
ON recipes (made_by);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_tags_jsonb
|
CREATE INDEX IF NOT EXISTS idx_recipes_tags_jsonb
|
||||||
ON recipes USING GIN (tags);
|
ON recipes USING GIN (tags);
|
||||||
|
|
||||||
|
|||||||
13
my-recipes-chart/templates/db-secret.yaml
Normal file
13
my-recipes-chart/templates/db-secret.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-db-credentials
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DB_HOST: {{ printf "%s-%s-headless.%s.svc.cluster.local" .Release.Name .Values.postgres.name .Values.global.namespace }}
|
||||||
|
DB_PORT: "{{ .Values.postgres.port }}"
|
||||||
|
DB_NAME: {{ .Values.postgres.database | quote }}
|
||||||
|
DB_USER: {{ .Values.postgres.user | quote }}
|
||||||
|
DB_PASSWORD: {{ .Values.postgres.password | quote }}
|
||||||
|
|
||||||
36
my-recipes-chart/templates/db-service.yaml
Normal file
36
my-recipes-chart/templates/db-service.yaml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.postgres.name }}-headless
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
component: database
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
selector:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
port: {{ .Values.postgres.port }}
|
||||||
|
targetPort: {{ .Values.postgres.port }}
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
component: database
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.postgres.service.type }}
|
||||||
|
selector:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
port: {{ .Values.postgres.service.port }}
|
||||||
|
targetPort: {{ .Values.postgres.port }}
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
86
my-recipes-chart/templates/db-statefulset.yaml
Normal file
86
my-recipes-chart/templates/db-statefulset.yaml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
component: database
|
||||||
|
spec:
|
||||||
|
serviceName: {{ .Release.Name }}-{{ .Values.postgres.name }}-headless
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
||||||
|
component: database
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: postgres
|
||||||
|
image: "{{ .Values.postgres.image.repository }}:{{ .Values.postgres.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.postgres.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- containerPort: {{ .Values.postgres.port }}
|
||||||
|
name: postgres
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
value: {{ .Values.postgres.user | quote }}
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
value: {{ .Values.postgres.password | quote }}
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
value: {{ .Values.postgres.database | quote }}
|
||||||
|
- name: PGDATA
|
||||||
|
value: /var/lib/postgresql/data/pgdata
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /var/lib/postgresql/data
|
||||||
|
- name: init-sql
|
||||||
|
mountPath: /docker-entrypoint-initdb.d
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- pg_isready -U {{ .Values.postgres.user }}
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- pg_isready -U {{ .Values.postgres.user }}
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 2
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: {{ .Values.postgres.resources.requests.cpu }}
|
||||||
|
memory: {{ .Values.postgres.resources.requests.memory }}
|
||||||
|
limits:
|
||||||
|
cpu: {{ .Values.postgres.resources.limits.cpu }}
|
||||||
|
memory: {{ .Values.postgres.resources.limits.memory }}
|
||||||
|
volumes:
|
||||||
|
- name: init-sql
|
||||||
|
configMap:
|
||||||
|
name: {{ .Release.Name }}-db-schema
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: data
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- {{ .Values.postgres.persistence.accessMode }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.postgres.persistence.size }}
|
||||||
|
{{- if .Values.postgres.persistence.storageClass }}
|
||||||
|
storageClassName: {{ .Values.postgres.persistence.storageClass | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
70
my-recipes-chart/templates/frontend-deployment.yaml
Normal file
70
my-recipes-chart/templates/frontend-deployment.yaml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
|
component: frontend
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.frontend.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
|
component: frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: {{ .Values.frontend.name }}
|
||||||
|
image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- containerPort: {{ .Values.frontend.service.targetPort }}
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
{{- /* compute API_BASE: prefer frontend.env.API_BASE, else use first ingress host */}}
|
||||||
|
{{- $apiBase := "" }}
|
||||||
|
{{- if .Values.frontend.env.API_BASE }}
|
||||||
|
{{- $apiBase = .Values.frontend.env.API_BASE }}
|
||||||
|
{{- else if .Values.ingress.hosts }}
|
||||||
|
{{- $firstHost := index .Values.ingress.hosts 0 }}
|
||||||
|
{{- $apiBase = printf "https://%s/api" $firstHost.host }}
|
||||||
|
{{- end }}
|
||||||
|
env:
|
||||||
|
- name: API_BASE
|
||||||
|
value: {{ $apiBase | quote }}
|
||||||
|
{{- /* include any other env vars provided in values.frontend.env (skip API_BASE to avoid duplicate) */}}
|
||||||
|
{{- with .Values.frontend.env }}
|
||||||
|
{{- range $key, $value := . }}
|
||||||
|
{{- if ne $key "API_BASE" }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: {{ $value | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 2
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: {{ .Values.frontend.resources.requests.cpu }}
|
||||||
|
memory: {{ .Values.frontend.resources.requests.memory }}
|
||||||
|
limits:
|
||||||
|
cpu: {{ .Values.frontend.resources.limits.cpu }}
|
||||||
|
memory: {{ .Values.frontend.resources.limits.memory }}
|
||||||
17
my-recipes-chart/templates/frontend-service.yaml
Normal file
17
my-recipes-chart/templates/frontend-service.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
|
component: frontend
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.frontend.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.frontend.service.port }}
|
||||||
|
targetPort: {{ .Values.frontend.service.targetPort }}
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
||||||
48
my-recipes-chart/templates/ingress.yaml
Normal file
48
my-recipes-chart/templates/ingress.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Name }}
|
||||||
|
namespace: {{ .Values.global.namespace }}
|
||||||
|
labels:
|
||||||
|
app: {{ .Release.Name }}
|
||||||
|
{{- with .Values.ingress.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- if .Values.ingress.className }}
|
||||||
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.ingress.tls }}
|
||||||
|
tls:
|
||||||
|
{{- range .Values.ingress.tls }}
|
||||||
|
- hosts:
|
||||||
|
{{- range .hosts }}
|
||||||
|
- {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
secretName: {{ .secretName }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- host: {{ .host | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
{{- if eq .backend "frontend" }}
|
||||||
|
name: {{ $.Release.Name }}-{{ $.Values.frontend.name }}
|
||||||
|
port:
|
||||||
|
number: {{ $.Values.frontend.service.port }}
|
||||||
|
{{- else if eq .backend "backend" }}
|
||||||
|
name: {{ $.Release.Name }}-{{ $.Values.backend.name }}
|
||||||
|
port:
|
||||||
|
number: {{ $.Values.backend.service.port }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
109
my-recipes-chart/values.yaml
Normal file
109
my-recipes-chart/values.yaml
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
global:
|
||||||
|
namespace: my-apps
|
||||||
|
imagePullSecrets: []
|
||||||
|
|
||||||
|
# Backend configuration
|
||||||
|
backend:
|
||||||
|
name: backend
|
||||||
|
replicaCount: 2
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/my-recipes-backend
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: "latest"
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8000
|
||||||
|
targetPort: 8000
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
|
||||||
|
env:
|
||||||
|
PYTHONUNBUFFERED: "1"
|
||||||
|
|
||||||
|
# Frontend configuration
|
||||||
|
frontend:
|
||||||
|
name: frontend
|
||||||
|
replicaCount: 2
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/my-recipes-frontend
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: "latest"
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
|
||||||
|
env:
|
||||||
|
API_BASE: "https://my-recipes.dvirlabs.com/api"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
|
||||||
|
# PostgreSQL configuration
|
||||||
|
postgres:
|
||||||
|
name: db
|
||||||
|
image:
|
||||||
|
repository: postgres
|
||||||
|
tag: "16-alpine"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
user: recipes_user
|
||||||
|
password: recipes_password
|
||||||
|
database: recipes_db
|
||||||
|
port: 5432
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 5432
|
||||||
|
targetPort: 5432
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
storageClass: "nfs-client"
|
||||||
|
size: 10Gi
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 1Gi
|
||||||
|
|
||||||
|
# Ingress configuration
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: "nginx"
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
|
nginx.ingress.kubernetes.io/rewrite-target: "/$2"
|
||||||
|
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||||
|
hosts:
|
||||||
|
- host: my-recipes.dvirlabs.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend: frontend
|
||||||
|
# use a regex to capture and rewrite /api/... to /... on the backend
|
||||||
|
- path: /api(/|$)(.*)
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend: backend
|
||||||
|
tls:
|
||||||
|
- secretName: recipes-tls
|
||||||
|
hosts:
|
||||||
|
- my-recipes.dvirlabs.com
|
||||||
|
|
||||||
4
navix-chart/Chart.yaml
Normal file
4
navix-chart/Chart.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: navix
|
||||||
|
version: 0.1.0
|
||||||
|
description: A DevOps dashboard called Navix
|
||||||
29
navix-chart/templates/backend-deployment.yaml
Normal file
29
navix-chart/templates/backend-deployment.yaml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: navix-backend
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: navix-backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: navix-backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: backend
|
||||||
|
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
env:
|
||||||
|
- name: MINIO_ACCESS_KEY
|
||||||
|
value: "{{ .Values.backend.env.MINIO_ACCESS_KEY }}"
|
||||||
|
- name: MINIO_SECRET_KEY
|
||||||
|
value: "{{ .Values.backend.env.MINIO_SECRET_KEY }}"
|
||||||
|
- name: MINIO_ENDPOINT
|
||||||
|
value: "{{ .Values.backend.env.MINIO_ENDPOINT }}"
|
||||||
|
- name: MINIO_BUCKET
|
||||||
|
value: "{{ .Values.backend.env.MINIO_BUCKET }}"
|
||||||
11
navix-chart/templates/backend-service.yaml
Normal file
11
navix-chart/templates/backend-service.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: navix-backend
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.backend.service.type }}
|
||||||
|
selector:
|
||||||
|
app: navix-backend
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.backend.service.port }}
|
||||||
|
targetPort: 8000
|
||||||
27
navix-chart/templates/frontend-deployment.yaml
Normal file
27
navix-chart/templates/frontend-deployment.yaml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: navix-frontend
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: navix-frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: navix-frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
env:
|
||||||
|
- name: API_BASE
|
||||||
|
value: {{ .Values.frontend.env.API_BASE | quote }}
|
||||||
|
- name: MINIO_ENDPOINT
|
||||||
|
value: {{ .Values.frontend.env.MINIO_ENDPOINT | quote }}
|
||||||
|
- name: MINIO_BUCKET
|
||||||
|
value: {{ .Values.frontend.env.MINIO_BUCKET | quote }}
|
||||||
11
navix-chart/templates/frontend-service.yaml
Normal file
11
navix-chart/templates/frontend-service.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: navix-frontend
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.frontend.service.type }}
|
||||||
|
selector:
|
||||||
|
app: navix-frontend
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.frontend.service.port }}
|
||||||
|
targetPort: 80
|
||||||
57
navix-chart/templates/ingress.yaml
Normal file
57
navix-chart/templates/ingress.yaml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{{- if .Values.frontend.ingress.enabled }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: navix-frontend
|
||||||
|
annotations:
|
||||||
|
{{- range $key, $value := .Values.frontend.ingress.annotations }}
|
||||||
|
{{ $key }}: {{ $value | quote }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
ingressClassName: {{ .Values.frontend.ingress.className }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.frontend.ingress.hosts }}
|
||||||
|
- host: {{ .host }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: navix-frontend
|
||||||
|
port:
|
||||||
|
number: {{ $.Values.frontend.service.port }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
{{- if .Values.backend.ingress.enabled }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: navix-backend
|
||||||
|
annotations:
|
||||||
|
{{- range $key, $value := .Values.backend.ingress.annotations }}
|
||||||
|
{{ $key }}: {{ $value | quote }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
ingressClassName: {{ .Values.backend.ingress.className }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.backend.ingress.hosts }}
|
||||||
|
- host: {{ .host }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: navix-backend
|
||||||
|
port:
|
||||||
|
number: {{ $.Values.backend.service.port }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
48
navix-chart/values.yaml
Normal file
48
navix-chart/values.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
frontend:
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/navix-front
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 80
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: traefik
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
hosts:
|
||||||
|
- host: navix.dvirlabs.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
env:
|
||||||
|
API_BASE: "https://navix.dvirlabs.com/api"
|
||||||
|
MINIO_ENDPOINT: "s3.dvirlabs.com"
|
||||||
|
MINIO_BUCKET: "navix-icons"
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image:
|
||||||
|
repository: harbor.dvirlabs.com/my-apps/navix-back
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8000
|
||||||
|
env:
|
||||||
|
MINIO_ACCESS_KEY: "your-access-key"
|
||||||
|
MINIO_SECRET_KEY: "your-secret-key"
|
||||||
|
MINIO_ENDPOINT: "s3.dvirlabs.com"
|
||||||
|
MINIO_BUCKET: "navix-icons"
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: traefik
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
hosts:
|
||||||
|
- host: navix.dvirlabs.com
|
||||||
|
paths:
|
||||||
|
- path: /api
|
||||||
|
pathType: Prefix
|
||||||
Loading…
x
Reference in New Issue
Block a user