From 565c27e6fbe228360dca561ec89e671158a980ef Mon Sep 17 00:00:00 2001 From: Christian Landgren Date: Mon, 18 Sep 2023 23:25:34 +0200 Subject: [PATCH] Host web in kubernetes instead of Vercel (#647) * chore: upgrade to node 16.13 * feat: add kubernetes manifests to run the web in kubernetes instead of Vercel * fix: rearrange build scripts for speed * feat: add readiness prope to never replace a working site with a failed one * fix: add headers for hsts etc * fix: add unsafe-inline * fix: duplicate entities inline-style * fix: add ipv6 support in web * fix: Ingress should be ClusterIP, not LoadBalancer * Add resources * feat: switch to main domain * fix: hsts preload requires www to also be encrypted --- .nvmrc | 2 +- apps/website/.dockerignore | 2 + apps/website/Dockerfile | 44 ++++++++++++++ apps/website/k8s/kustomization.yaml | 4 ++ apps/website/k8s/web.yaml | 93 +++++++++++++++++++++++++++++ apps/website/skaffold.yaml | 13 ++++ 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 apps/website/.dockerignore create mode 100644 apps/website/Dockerfile create mode 100644 apps/website/k8s/kustomization.yaml create mode 100644 apps/website/k8s/web.yaml create mode 100644 apps/website/skaffold.yaml diff --git a/.nvmrc b/.nvmrc index 9a0c3d3f..e272c59e 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v14.15.4 +v16.13 diff --git a/apps/website/.dockerignore b/apps/website/.dockerignore new file mode 100644 index 00000000..31312707 --- /dev/null +++ b/apps/website/.dockerignore @@ -0,0 +1,2 @@ +k8s +k8s diff --git a/apps/website/Dockerfile b/apps/website/Dockerfile new file mode 100644 index 00000000..1ef94fc5 --- /dev/null +++ b/apps/website/Dockerfile @@ -0,0 +1,44 @@ +# Install dependencies only when needed +FROM node:16-alpine AS builder +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat autoconf automake build-base curl git libtool make nodejs npm pkgconf nasm yasm optipng +WORKDIR /app +COPY package.json yarn.lock ./ +RUN yarn install --frozen-lockfile + +COPY . . +ENV NEXT_TELEMETRY_DISABLED 1 + +RUN yarn build + +# Production image, copy all the files and run next +FROM node:16-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production + +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 + +# You only need to copy next.config.js if you are NOT using the default configuration +# COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/next.* ./ +COPY --from=builder /app/*.js ./ +COPY --from=builder /app/*.ts ./ +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry. +ENV NEXT_TELEMETRY_DISABLED 1 + +CMD ["yarn", "start"] \ No newline at end of file diff --git a/apps/website/k8s/kustomization.yaml b/apps/website/k8s/kustomization.yaml new file mode 100644 index 00000000..7be3890a --- /dev/null +++ b/apps/website/k8s/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - web.yaml diff --git a/apps/website/k8s/web.yaml b/apps/website/k8s/web.yaml new file mode 100644 index 00000000..e4418981 --- /dev/null +++ b/apps/website/k8s/web.yaml @@ -0,0 +1,93 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: skolplattformen-web +--- +apiVersion: v1 +kind: Service +metadata: + name: skolplattformen-web + namespace: skolplattformen-web +spec: + ports: + - port: 3000 + type: ClusterIP + selector: + app: skolplattformen-web +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: skolplattformen-web + namespace: skolplattformen-web +spec: + selector: + matchLabels: + app: skolplattformen-web + template: + metadata: + labels: + app: skolplattformen-web + spec: + containers: + - name: skolplattformen-web + resources: + requests: + cpu: 100m + memory: 100Mi + image: skolplattformen/web + ports: + - containerPort: 3000 + + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 10 + periodSeconds: 10 + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: skolplattformen-web + namespace: skolplattformen-web + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' + nginx.ingress.kubernetes.io/from-to-www-redirect: 'true' + nginx.ingress.kubernetes.io/http2-push-preload: 'true' + nginx.ingress.kubernetes.io/proxy-body-size: '500m' + nginx.ingress.kubernetes.io/proxy-pass-headers: 'Location' + nginx.ingress.kubernetes.io/configuration-snippet: | + more_set_headers "X-Content-Type-Options: nosniff"; + more_set_headers "X-Frame-Options: DENY"; + more_set_headers "X-Xss-Protection: 0"; + more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload"; + more_set_headers "Cross-Origin-Resource-Policy: same-site"; + more_set_headers "Referrer-Policy strict-origin"; + external-dns.alpha.kubernetes.io/hostname: new.skolplattformen.org. + +spec: + ingressClassName: nginx + tls: + - hosts: + - skolplattformen.org + - www.skolplattformen.org + secretName: web-secret-tls + rules: + - host: skolplattformen.org + http: + paths: + - pathType: Prefix + path: '/' + backend: + service: + name: skolplattformen-web + port: + number: 3000 diff --git a/apps/website/skaffold.yaml b/apps/website/skaffold.yaml new file mode 100644 index 00000000..21ada813 --- /dev/null +++ b/apps/website/skaffold.yaml @@ -0,0 +1,13 @@ +apiVersion: skaffold/v4beta1 +kind: Config +metadata: + name: skolplattformen-web +build: + artifacts: + - image: skolplattformen/web + context: . +manifests: + rawYaml: + - k8s/web.yaml +deploy: + kubectl: {}