From dafd73b7d5e38bdefb3228ff359eb25c427796e6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Mar 2025 12:09:14 +0100 Subject: [PATCH 1/5] deployment automation for staging --- ...-deploy.yaml => build-deploy-staging.yaml} | 28 ++++++++- .gitignore | 2 + README.md | 12 ++-- .../base/database/kustomization.yaml | 2 + .../base/database/vshnpostgresql.yaml | 19 ++++++ .../kustomize/base/portal/deployment.yaml | 62 +++++++++++++++++++ .../kustomize/base/portal/kustomization.yaml | 3 + deployment/kustomize/base/portal/service.yaml | 15 +++++ .../kustomize/overlays/staging/ingress.yaml | 22 +++++++ .../overlays/staging/kustomization.yaml | 13 ++++ .../overlays/staging/portal-deployment.yaml | 14 +++++ 11 files changed, 187 insertions(+), 5 deletions(-) rename .forgejo/workflows/{build-deploy.yaml => build-deploy-staging.yaml} (50%) create mode 100644 deployment/kustomize/base/database/kustomization.yaml create mode 100644 deployment/kustomize/base/database/vshnpostgresql.yaml create mode 100644 deployment/kustomize/base/portal/deployment.yaml create mode 100644 deployment/kustomize/base/portal/kustomization.yaml create mode 100644 deployment/kustomize/base/portal/service.yaml create mode 100644 deployment/kustomize/overlays/staging/ingress.yaml create mode 100644 deployment/kustomize/overlays/staging/kustomization.yaml create mode 100644 deployment/kustomize/overlays/staging/portal-deployment.yaml diff --git a/.forgejo/workflows/build-deploy.yaml b/.forgejo/workflows/build-deploy-staging.yaml similarity index 50% rename from .forgejo/workflows/build-deploy.yaml rename to .forgejo/workflows/build-deploy-staging.yaml index a9ddbcb..67ab07c 100644 --- a/.forgejo/workflows/build-deploy.yaml +++ b/.forgejo/workflows/build-deploy-staging.yaml @@ -1,4 +1,4 @@ -name: Build and Deploy +name: Build and Deploy Staging on: push: @@ -35,3 +35,29 @@ jobs: tags: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_NAME }}:latest cache-from: type=gha cache-to: type=gha,mode=max + + deploy: + needs: build + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + environment: + name: staging + url: https://staging.portal.servala.com/ + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Deploy to OpenShift + uses: docker://quay.io/appuio/oc:v4.16 + with: + entrypoint: /bin/bash + args: | + -c "oc login --token=${OPENSHIFT_TOKEN} --server=${OPENSHIFT_URL} && \ + oc -n ${NAMESPACE} apply --overwrite -k deployment/kustomize/overlays/staging && \ + oc -n ${NAMESPACE} rollout restart deployment/servala" + env: + NAMESPACE: ${{ vars.NAMESPACE_PORTAL_STAGING }} + KUBECONFIG: /tmp/kube_config + OPENSHIFT_TOKEN: ${{ secrets.OPENSHIFT_TOKEN }} + OPENSHIFT_URL: ${{ secrets.OPENSHIFT_URL }} diff --git a/.gitignore b/.gitignore index ab3e8ce..307b1dc 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,5 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +# Deployment Stuff +deployment/secrets/* \ No newline at end of file diff --git a/README.md b/README.md index 28700d2..9af34d2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Paths: * `docs/modules/ROOT/nav.adoc`: Site navigation (new pages need to be added there) Writing documentation is best done by running `make docs-preview` and connecting to the site at http://localhost:2020/. -The browser addon [LiveReload - Web extension](https://addons.mozilla.org/en-US/firefox/addon/livereload-web-extension/) will help while editing with automated page reload in the browser. +The browser add-on [LiveReload - Web extension](https://addons.mozilla.org/en-US/firefox/addon/livereload-web-extension/) will help while editing with automated page reload in the browser. ## Development setup @@ -39,7 +39,7 @@ This will start the development server on http://localhost:8000. TODO -### Code style and linting +## Code style and linting Servala uses several linters / formatters to keep the project style consistent for you. Run them like this: @@ -54,7 +54,7 @@ uv run flake8 src/ # Python linter The repository features a [pre-commit](https://pre-commit.com/) configuration which helps to properly format the source code before committing. It's recommended to install and use it. -### Docker +## Docker The project provides a Dockerfile which builds a production-ready container image. It uses [Caddy](https://caddyserver.com/) to serve static files and connect to [Gunicorn](https://gunicorn.org/), the Python WSGI application server. @@ -73,6 +73,10 @@ docker run --rm -ti -p 8080:8080 --name=servala-portal --rm --env-file .env loca TODO: Persistence -### Testing +## Deployment + +TODO + +## Testing TODO diff --git a/deployment/kustomize/base/database/kustomization.yaml b/deployment/kustomize/base/database/kustomization.yaml new file mode 100644 index 0000000..3bf5d5c --- /dev/null +++ b/deployment/kustomize/base/database/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - vshnpostgresql.yaml diff --git a/deployment/kustomize/base/database/vshnpostgresql.yaml b/deployment/kustomize/base/database/vshnpostgresql.yaml new file mode 100644 index 0000000..e17b4a2 --- /dev/null +++ b/deployment/kustomize/base/database/vshnpostgresql.yaml @@ -0,0 +1,19 @@ +apiVersion: vshn.appcat.vshn.io/v1 +kind: VSHNPostgreSQL +metadata: + name: servala +spec: + parameters: + service: + majorVersion: "16" + pgSettings: + timezone: Europe/Zurich + size: + plan: standard-2 + backup: + schedule: "30 23 * * *" + retention: 12 + encryption: + enabled: true + writeConnectionSecretToRef: + name: database-creds diff --git a/deployment/kustomize/base/portal/deployment.yaml b/deployment/kustomize/base/portal/deployment.yaml new file mode 100644 index 0000000..f738475 --- /dev/null +++ b/deployment/kustomize/base/portal/deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: servala + name: servala +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: servala + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: servala + spec: + containers: + - name: servala + image: servala-2nkgm.app.codey.ch/servala/servala-portal:latest + imagePullPolicy: Always + readinessProbe: + httpGet: + path: /admin/ + port: 8080 + periodSeconds: 60 + livenessProbe: + httpGet: + path: /admin/ + port: 8080 + periodSeconds: 60 + initialDelaySeconds: 5 + ports: + - name: http + containerPort: 8080 + protocol: TCP + envFrom: + - secretRef: + name: servala + env: + - name: SERVALA_DB_USER + valueFrom: + secretKeyRef: + name: database-creds + key: POSTGRESQL_USER + - name: SERVALA_DB_PASSWORD + valueFrom: + secretKeyRef: + name: database-creds + key: POSTGRESQL_PASSWORD + - name: SERVALA_DB_HOST + valueFrom: + secretKeyRef: + name: database-creds + key: POSTGRESQL_HOST + - name: SERVALA_DB_PORT + valueFrom: + secretKeyRef: + name: database-creds + key: POSTGRESQL_PORT diff --git a/deployment/kustomize/base/portal/kustomization.yaml b/deployment/kustomize/base/portal/kustomization.yaml new file mode 100644 index 0000000..6d1374a --- /dev/null +++ b/deployment/kustomize/base/portal/kustomization.yaml @@ -0,0 +1,3 @@ +resources: + - deployment.yaml + - service.yaml diff --git a/deployment/kustomize/base/portal/service.yaml b/deployment/kustomize/base/portal/service.yaml new file mode 100644 index 0000000..bd8bb47 --- /dev/null +++ b/deployment/kustomize/base/portal/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: servala + labels: + app: servala +spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: http + selector: + app: servala + type: ClusterIP diff --git a/deployment/kustomize/overlays/staging/ingress.yaml b/deployment/kustomize/overlays/staging/ingress.yaml new file mode 100644 index 0000000..835a17e --- /dev/null +++ b/deployment/kustomize/overlays/staging/ingress.yaml @@ -0,0 +1,22 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + cert-manager.io/cluster-issuer: letsencrypt-production + name: servala +spec: + rules: + - host: staging.portal.servala.com + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: servala + port: + number: 8080 + tls: + - hosts: + - staging.portal.servala.com + secretName: ingress-cert diff --git a/deployment/kustomize/overlays/staging/kustomization.yaml b/deployment/kustomize/overlays/staging/kustomization.yaml new file mode 100644 index 0000000..d746bb1 --- /dev/null +++ b/deployment/kustomize/overlays/staging/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +labels: + - includeSelectors: true + pairs: + app.kubernetes.io/instance: test + app.kubernetes.io/name: servala +resources: + - ../../base/portal + - ../../base/database + - ingress.yaml +patches: + - path: portal-deployment.yaml diff --git a/deployment/kustomize/overlays/staging/portal-deployment.yaml b/deployment/kustomize/overlays/staging/portal-deployment.yaml new file mode 100644 index 0000000..e435bb3 --- /dev/null +++ b/deployment/kustomize/overlays/staging/portal-deployment.yaml @@ -0,0 +1,14 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: servala +spec: + template: + spec: + containers: + - name: servala + env: + - name: SERVALA_ENVIRONMENT + value: staging + - name: SERVALA_ALLOWED_HOSTS + value: staging.portal.servala.com From e56447ece68df0f7259ead30755e4ee3c39cece9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Mar 2025 12:23:11 +0100 Subject: [PATCH 2/5] dont include local sqlite file in docker build --- .dockerignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index c739237..6d31198 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,4 +4,5 @@ # Don't add credentials and other local stuff .env media -db.sqlite3 \ No newline at end of file +db.sqlite3 +src/db.sqlite3 From fb66d5666cb9a9f7391c5a2764a1ef290e42a9f8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Mar 2025 12:23:34 +0100 Subject: [PATCH 3/5] properly support staticfiles in docker --- Dockerfile | 4 ++-- docker/Caddyfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4e50b07..fd54964 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,7 +34,7 @@ RUN uv sync --frozen \ && mkdir -p /app/config/caddy /app/run/caddy /app/run/gunicorn \ && chgrp -R 0 /app \ && chmod -R g=u /app \ - && chmod g+w /app/config/caddy/Caddyfile - # && SECRET_KEY= uv run src/manage.py collectstatic --noinput + && chmod g+w /app/config/caddy/Caddyfile \ + && SECRET_KEY= uv run src/manage.py collectstatic --noinput CMD ["/usr/local/bin/run.sh"] \ No newline at end of file diff --git a/docker/Caddyfile b/docker/Caddyfile index bfe1b1c..7c90125 100644 --- a/docker/Caddyfile +++ b/docker/Caddyfile @@ -29,7 +29,7 @@ # Handle static files handle /static/* { uri strip_prefix /static - root * /app/staticfiles + root * /app/src/static.dist file_server } From 07812efe82cba63aba0c55f865aada44a561c1ca Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Mar 2025 14:16:11 +0100 Subject: [PATCH 4/5] configure uv cache to support openshift --- docker/run.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/run.sh b/docker/run.sh index f3c7f9a..4ddb2b2 100644 --- a/docker/run.sh +++ b/docker/run.sh @@ -6,6 +6,9 @@ mkdir -p /app/run/caddy /app/run/gunicorn # Set Caddy config location export XDG_CONFIG_HOME="/app/config" +# Set uv cache location so that it runs properly on OpenShift +export UV_CACHE_DIR="/app/.uvcache" + echo "Applying database migrations" uv run src/manage.py migrate From f2580675a0a3ccc6cb9f2364404412ea3af2841f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Mar 2025 14:05:33 +0100 Subject: [PATCH 5/5] document deployment to staging --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9af34d2..18a5d6f 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,10 @@ uv run src/manage.py runserver This will start the development server on http://localhost:8000. -### Configuration +## Configuration -TODO +Configuration happens using environment variables. +See the available parameters in `.env.example`. ## Code style and linting @@ -69,13 +70,18 @@ Running: ```bash docker run --rm -ti -p 8080:8080 --name=servala-portal --rm --env-file .env local/servala-portal +docker exec -it servala-portal uv run src/manage.py createsuperuser ``` -TODO: Persistence +Then access it with http://localhost:8080/ and the Django admin with http://localhost:8080/admin ## Deployment -TODO +The code is automatically built and deployed on a push to the main branch. +See `.forgejo/workflows/build-deploy-staging.yaml` for the actual workflow. + +Deployment files are in the `deployment/kustomize` folder and makes use of [Kustomize](https://kustomize.io/) to account for differences between the deployment stages. +Stages are configured with overlays in `deployment/kustomize/overlays/$environment`. ## Testing