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_PASSWORD: "$PASSWORD"
|
||||
POSTGRES_DB: lahuasca
|
||||
DATABASE_URL: "postgres://lahuasca:${PASSWORD}@postgres:5432/lahuasca"
|
||||
EOF
|
||||
|
||||
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} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
output: 'standalone',
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user