feat: deploy to Frostmoln production
The AWS deploy-prod job has failed since CI left AWS (no k8s.unbound.se context on the runners). Deploys now go to the Frostmoln production cluster (namespace sites), the way Shiny deploys to Frostmoln staging. Frostmoln enforces restricted Pod Security, so the served image is nginx-unprivileged (uid 101, port 8080) with a matching securityContext. The ALB ingress becomes a Traefik one with a Let's Encrypt certificate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ma8eHhW59hLAJuLzBnLvUu
This commit is contained in:
1 parent
b8b30a4fbe
commit
a41e44367a
5 files changed
+37
-17
No files matched your search
@@ -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 to production
|
||||
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 sites
|
||||
+2
-1
@@ -13,7 +13,8 @@ COPY ./ /site
|
||||
WORKDIR /site
|
||||
RUN hugo
|
||||
|
||||
FROM amd64/nginx:1.31.6-alpine@sha256:5f4b1cbc2fac6baad5fd73efa91aef5f40ee315cacc5af719402ae21bb573776
|
||||
# Unprivileged (uid 101, port 8080): Frostmoln enforces restricted Pod Security.
|
||||
FROM nginxinc/nginx-unprivileged:1.31.6-alpine@sha256:884e0b7f42f303641b0f4e78755aea647521dca0b89ca3be7992ad78b5c07434
|
||||
ADD default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /site/public /usr/share/nginx/html
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
|
||||
access_log /dev/stdout combined;
|
||||
|
||||
+12
-4
@@ -22,6 +22,11 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: unbound-site
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 101
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
@@ -39,14 +44,18 @@ spec:
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 5
|
||||
imagePullPolicy: Always
|
||||
image: ${IMAGE}
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: 8080
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
@@ -63,9 +72,8 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: unbound-site
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app.kubernetes.io/name: unbound-site
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
targetPort: 8080
|
||||
@@ -1,15 +1,12 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: unbound-site-ingress
|
||||
name: unbound-site
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "alb"
|
||||
alb.ingress.kubernetes.io/group.name: default
|
||||
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"
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: "unbound.se"
|
||||
http:
|
||||
Reference in new issue
Block a user