Add Dockerfile, K8s manifests, and deploy.sh for plexnuke k3s cluster
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
|||||||
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
ENV PORT=3000
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
@@ -62,6 +62,7 @@ stringData:
|
|||||||
POSTGRES_USER: lahuasca
|
POSTGRES_USER: lahuasca
|
||||||
POSTGRES_PASSWORD: "$PASSWORD"
|
POSTGRES_PASSWORD: "$PASSWORD"
|
||||||
POSTGRES_DB: lahuasca
|
POSTGRES_DB: lahuasca
|
||||||
|
DATABASE_URL: "postgres://lahuasca:${PASSWORD}@postgres:5432/lahuasca"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "Database secret updated in namespace $NAMESPACE"
|
echo "Database secret updated in namespace $NAMESPACE"
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Deploy la-huasca-ts to the plexnuke k3s cluster and expose it on lahuasca.com
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./deploy.sh
|
||||||
|
# REGISTRY_USER=muken REGISTRY_PASS=... ./deploy.sh
|
||||||
|
#
|
||||||
|
# Prerequisites on the target machine (plexnuke):
|
||||||
|
# - kubectl configured for the k3s cluster
|
||||||
|
# - docker or buildah/push capability (we build locally and push)
|
||||||
|
# - registry secret exists or REGISTRY_USER/REGISTRY_PASS are exported
|
||||||
|
|
||||||
|
NAMESPACE="la-huasca"
|
||||||
|
IMAGE="gitea.ajavasoft.com/muken/la-huasca-ts"
|
||||||
|
TAG="${TAG:-$(git rev-parse --short HEAD)}"
|
||||||
|
HOSTS=("lahuasca.com" "www.lahuasca.com")
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Ensure the namespace exists
|
||||||
|
kubectl create namespace "$NAMESPACE" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Build Next.js standalone output and Docker image
|
||||||
|
echo "==> Building Docker image $IMAGE:$TAG ..."
|
||||||
|
DOCKER_BUILDKIT=1 docker build -t "$IMAGE:$TAG" -t "$IMAGE:latest" .
|
||||||
|
|
||||||
|
# Push to registry
|
||||||
|
echo "==> Pushing image ..."
|
||||||
|
docker push "$IMAGE:$TAG"
|
||||||
|
docker push "$IMAGE:latest"
|
||||||
|
|
||||||
|
# Generate JWT secret if not present
|
||||||
|
if ! kubectl get secret la-huasca-app-secrets -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||||
|
JWT_SECRET="$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9' | head -c 64)"
|
||||||
|
kubectl create secret generic la-huasca-app-secrets \
|
||||||
|
--namespace "$NAMESPACE" \
|
||||||
|
--from-literal=JWT_SECRET="$JWT_SECRET"
|
||||||
|
echo "==> Created la-huasca-app-secrets with JWT_SECRET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create database secret if missing (run deploy-db.sh first to populate it)
|
||||||
|
if ! kubectl get secret la-huasca-db-secrets -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||||
|
echo "WARNING: la-huasca-db-secrets not found. Run ./deploy-db.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build registry pull secret if credentials are provided
|
||||||
|
if [ -n "${REGISTRY_USER:-}" ] && [ -n "${REGISTRY_PASS:-}" ]; then
|
||||||
|
REGISTRY_AUTH="$(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_PASS" | base64 -w 0)"
|
||||||
|
export REGISTRY_USER REGISTRY_PASS REGISTRY_AUTH
|
||||||
|
echo "==> Updating registry pull secret ..."
|
||||||
|
envsubst \u003c "./k8s/registry-secret.yaml" | kubectl apply -f -
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update image tag in manifest and apply
|
||||||
|
export TAG
|
||||||
|
export NAMESPACE
|
||||||
|
envsubst \u003c "./k8s/app.yaml" | kubectl apply -f -
|
||||||
|
|
||||||
|
# Wait for rollout
|
||||||
|
echo "==> Waiting for rollout ..."
|
||||||
|
kubectl rollout status deployment/la-huasca-nextjs -n "$NAMESPACE" --timeout=180s
|
||||||
|
|
||||||
|
# Print summary
|
||||||
|
IP="$(kubectl get service traefik -n kube-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo 'node IP')"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Deploy complete."
|
||||||
|
echo " Image: $IMAGE:$TAG"
|
||||||
|
echo " Hosts: ${HOSTS[*]}"
|
||||||
|
echo " LB IP: $IP"
|
||||||
|
echo ""
|
||||||
|
echo "To seed the database, run:"
|
||||||
|
echo " kubectl exec -n $NAMESPACE deploy/la-huasca-nextjs -- curl -s -X POST http://localhost:3000/api/db/init"
|
||||||
|
echo ""
|
||||||
|
echo "To bootstrap mmendoza admin user:"
|
||||||
|
echo " kubectl exec -n $NAMESPACE deploy/la-huasca-nextjs -- curl -s -X POST http://localhost:3000/api/bootstrap-user"
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: ${NAMESPACE}
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: la-huasca-nextjs
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: la-huasca-nextjs
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: la-huasca-nextjs
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: la-huasca-registry
|
||||||
|
containers:
|
||||||
|
- name: nextjs
|
||||||
|
image: gitea.ajavasoft.com/muken/la-huasca-ts:${TAG}
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
env:
|
||||||
|
- name: DATABASE_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: la-huasca-db-secrets
|
||||||
|
key: DATABASE_URL
|
||||||
|
- name: JWT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: la-huasca-app-secrets
|
||||||
|
key: JWT_SECRET
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 10
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: la-huasca-nextjs
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: la-huasca-nextjs
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 3000
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: la-huasca-nextjs
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
rules:
|
||||||
|
- host: lahuasca.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: la-huasca-nextjs
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- host: www.lahuasca.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: la-huasca-nextjs
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: la-huasca-registry
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
type: kubernetes.io/dockerconfigjson
|
||||||
|
stringData:
|
||||||
|
.dockerconfigjson: |
|
||||||
|
{
|
||||||
|
"auths": {
|
||||||
|
"gitea.ajavasoft.com": {
|
||||||
|
"username": "${REGISTRY_USER}",
|
||||||
|
"password": "${REGISTRY_PASS}",
|
||||||
|
"auth": "${REGISTRY_AUTH}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
|
output: 'standalone',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
module.exports = nextConfig;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user