Deployment to Staging #18

Merged
tobru merged 5 commits from deployment-to-test into main 2025-03-21 13:27:00 +00:00
15 changed files with 204 additions and 12 deletions

View file

@ -4,4 +4,5 @@
# Don't add credentials and other local stuff
.env
media
db.sqlite3
db.sqlite3
src/db.sqlite3

View file

@ -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 }}

2
.gitignore vendored
View file

@ -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/*

View file

@ -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"]

View file

@ -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
@ -35,11 +35,12 @@ 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
## Code style and linting
Servala uses several linters / formatters to keep the project style consistent for you.
Run them like this:
@ -54,7 +55,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.
@ -69,10 +70,19 @@ 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
### Testing
## Deployment
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
TODO

View file

@ -0,0 +1,2 @@
resources:
- vshnpostgresql.yaml

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,3 @@
resources:
- deployment.yaml
- service.yaml

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -29,7 +29,7 @@
# Handle static files
handle /static/* {
uri strip_prefix /static
root * /app/staticfiles
root * /app/src/static.dist
file_server
}

View file

@ -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