From ddbceeb7b5f664606e99ee09c0918d753cea914d Mon Sep 17 00:00:00 2001 From: Joakim Olsson Date: Fri, 16 Oct 2026 10:23:16 +0200 Subject: [PATCH] feat: deploy to Frostmoln production Replace the AWS deploy-prod job (failing since CI left AWS) with deploy-prod-frostmoln, and run under the restricted Pod Security profile with the namespace's oci-registry pull secret. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H6eVuR4Yt3m7TbDTDwyZJi --- .gitea/workflows/ci.yaml | 22 +++++++++++--- Dockerfile | 2 +- ...rod.yaml => autoscale-prod-frostmoln.yaml} | 0 k8s/deploy.yaml | 18 +++++++++--- k8s/ingress-prod-frostmoln.yaml | 20 +++++++++++++ k8s/ingress-prod.yaml | 29 ------------------- k8s/pdb-prod-frostmoln.yaml | 9 ++++++ nginx-conf/default.conf | 2 +- 8 files changed, 63 insertions(+), 39 deletions(-) rename k8s/{autoscale-prod.yaml => autoscale-prod-frostmoln.yaml} (100%) create mode 100644 k8s/ingress-prod-frostmoln.yaml delete mode 100644 k8s/ingress-prod.yaml create mode 100644 k8s/pdb-prod-frostmoln.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 8b9412f..9b42f52 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -18,16 +18,30 @@ jobs: - name: Build and push run: unset GITEA_TOKEN && build && push - deploy-prod: + deploy-prod-frostmoln: needs: build if: gitea.ref == 'refs/heads/main' runs-on: ubuntu-latest env: - BUILDTOOLS_CONTENT: ${{ secrets.BUILDTOOLS_CONTENT }} + BUILDTOOLS_CONTENT: ${{ secrets.BUILDTOOLS_FROSTMOLN_PRODUCTION }} GITEA_REPOSITORY: ${{ gitea.repository }} + FM_TENANT: 9ae61342-0f76-476f-8176-e46f6b0b0c32 environment: prod steps: - uses: actions/checkout@v7 - uses: buildtool/setup-buildtools-action@v1 - - name: Deploy - run: deploy prod + - name: Install fm + run: | + sudo install -d -m 0755 /etc/apt/keyrings + sudo curl -fsSL -o /etc/apt/keyrings/frostmoln.gpg https://get.frostmoln.cloud/deb/frostmoln.gpg + sudo curl -fsSL -o /etc/apt/sources.list.d/frostmoln.sources https://get.frostmoln.cloud/deb/frostmoln.sources + sudo apt-get update && sudo apt-get install -y fm + - name: Deploy to Frostmoln production + env: + FROSTMOLN_API_KEY: ${{ secrets.FROSTMOLN_PRODUCTION_DEPLOY_KEY }} + # The cluster API is private: the kubeconfig's exec plugin opens an fm + # tunnel on demand. fm reads the key from its config file, not the env. + run: | + fm auth api-key set "$FROSTMOLN_API_KEY" > /dev/null + fm kubernetes cluster kubeconfig ${{ vars.FROSTMOLN_PRODUCTION_CLUSTER_ID }} --exec --file "$RUNNER_TEMP/kubeconfig" + KUBECONFIG_CONTENT="$(cat "$RUNNER_TEMP/kubeconfig")" deploy prod-frostmoln -n dancefinder diff --git a/Dockerfile b/Dockerfile index 8b6ea69..cba9128 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ COPY . /build RUN npm run postinstall && npm run generate && npm run lint #RUN yarn start:ci & yarn wait && yarn test:cypress -FROM amd64/nginx:1.31.7@sha256:227f31dac037a2f1cdbd17ca9eec940a76c6c1b3187f5285eca2c3edb5d19c31 +FROM nginxinc/nginx-unprivileged:1.31.7@sha256:94193d5c4c5df716f412bb08eb97956d98fe6b59886f0bc57d6d3a3402a88a02 MAINTAINER Joakim Olsson COPY --from=builder /build/.output/public/ /usr/share/nginx/html/ diff --git a/k8s/autoscale-prod.yaml b/k8s/autoscale-prod-frostmoln.yaml similarity index 100% rename from k8s/autoscale-prod.yaml rename to k8s/autoscale-prod-frostmoln.yaml diff --git a/k8s/deploy.yaml b/k8s/deploy.yaml index b4da2cd..e4543ae 100644 --- a/k8s/deploy.yaml +++ b/k8s/deploy.yaml @@ -22,6 +22,13 @@ spec: labels: app.kubernetes.io/name: dancefinder-app spec: + imagePullSecrets: + - name: oci-registry + securityContext: + runAsNonRoot: true + runAsUser: 101 + seccompProfile: + type: RuntimeDefault affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: @@ -36,17 +43,21 @@ spec: topologyKey: kubernetes.io/hostname containers: - name: dancefinder-app + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] readinessProbe: httpGet: path: /health - port: 80 + port: 8080 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 5 imagePullPolicy: Always image: ${IMAGE} ports: - - containerPort: 80 + - containerPort: 8080 resources: requests: cpu: 10m @@ -64,9 +75,8 @@ metadata: labels: app.kubernetes.io/name: dancefinder-app spec: - type: NodePort selector: app.kubernetes.io/name: dancefinder-app ports: - port: 80 - targetPort: 80 + targetPort: 8080 diff --git a/k8s/ingress-prod-frostmoln.yaml b/k8s/ingress-prod-frostmoln.yaml new file mode 100644 index 0000000..1191f79 --- /dev/null +++ b/k8s/ingress-prod-frostmoln.yaml @@ -0,0 +1,20 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: dancefinder-app + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt +spec: + ingressClassName: traefik + rules: + - host: "dancefinder.unbound.se" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: dancefinder-app + port: + number: 80 diff --git a/k8s/ingress-prod.yaml b/k8s/ingress-prod.yaml deleted file mode 100644 index 37fc8ac..0000000 --- a/k8s/ingress-prod.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: dancefinder-app-ingress - annotations: - alb.ingress.kubernetes.io/group.name: "default" - alb.ingress.kubernetes.io/group.order: "101" - alb.ingress.kubernetes.io/scheme: internet-facing - alb.ingress.kubernetes.io/target-type: instance - alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS": 443}]' - alb.ingress.kubernetes.io/ssl-redirect: "443" - alb.ingress.kubernetes.io/healthcheck-path: "/health" - alb.ingress.kubernetes.io/healthcheck-interval-seconds: "30" - alb.ingress.kubernetes.io/healthcheck-timeout-seconds: "5" - alb.ingress.kubernetes.io/healthy-threshold-count: "2" - alb.ingress.kubernetes.io/unhealthy-threshold-count: "2" -spec: - ingressClassName: "alb" - rules: - - host: "dancefinder.unbound.se" - http: - paths: - - path: /* - pathType: ImplementationSpecific - backend: - service: - name: dancefinder-app - port: - number: 80 diff --git a/k8s/pdb-prod-frostmoln.yaml b/k8s/pdb-prod-frostmoln.yaml new file mode 100644 index 0000000..4c64655 --- /dev/null +++ b/k8s/pdb-prod-frostmoln.yaml @@ -0,0 +1,9 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: dancefinder-app +spec: + minAvailable: 1 + selector: + matchLabels: + app.kubernetes.io/name: dancefinder-app diff --git a/nginx-conf/default.conf b/nginx-conf/default.conf index 2155c95..fc2952e 100644 --- a/nginx-conf/default.conf +++ b/nginx-conf/default.conf @@ -1,5 +1,5 @@ server { - listen 80; + listen 8080; server_name localhost; sendfile off; charset utf-8;