- Webapp using a ReactJS frontend and a Python+FastAPI backend.
- Database is a standard PostgreSQL relational database
- All of our code for the app is compiled to a single OCI container (aka a Docker container)
- App runs on Kubernetes to for self-healing and container orchestration
- App compilation, container image building, and deploying to kubernetes is scripted by Github Actions CI, and runs automatically when the master branch is updated.
- Frontend is compiled to static HTML, CSS, and minified JS files
- These static files are packaged into the container image to be served to users' browsers
- Api documentation is accessed by the endpoint /api/v1/docs i.e. when running the site localy on localhost:8080/api/v1/docs
- Api endpoints are written with FastAPI
- FastAPI handles calls for static files too, and serves up the static files for the frontend.
- Uvicorn runs in the container and listens to incoming HTTP, and then calls FastAPI to handle the request
- Database queries and schema is managed with SQLAlchemy ORM and Alembic migrations
- Photos are uploaded to object storage
- Database runs in a container in kubernetes
- Database migrations are done with a Kubernetes Job
- All schema changes must be designed so that old versions of the app are compatible with the new database schema.
- An init container runs in the app Pod and makes sure that the database is fully migrated before the new app version can be start
- https://andrewlock.net/deploying-asp-net-core-applications-to-kubernetes-part-7-running-database-migrations/
- Database backups should run every hour to back up to object storage.
- Only the first backup of each day is kept after a month
- Only the first backup of each month is kept after a year
- TODO: We need to specify our backup retention policy in our GDPR policy
- Kustomize is used to configure different Kubernetes environments, making changes to the database configuration to allow for running the app in a kubernetes test environment
- Alerts
- Grafana+loki stack for monitoring
- Masters and devs should be emailed if an alert goes off
- Alert criteria
- 500 or exceptions from FastAPI
- Database backups haven't run
- CPU or memory are full on node
- CrashLoop/database migration is stuck
- External uptime checker should check external accessibility and alert system health
- Check backend health endpoint
- Special microservice that has an endpoint that checks that the monitoring is healthy
- All code should have automatic tests so that assumptions are verified, and regressions are caught.
- Frontend javascript code is tested inside the container when compiling the app
- Backend code is tested inside the container
- Kubernetes manifests are tested to make sure that are valid once the container is built, but before the chart is deployed to production
The development environment is designed to be identical to the production environment.
The app runs in a Docker container that is similar to the production container, and the database and helper services also run in containers alongside the main app.
As with the production environment, these containers are managed with Kubernetes (k8s). While the production k8s cluster runs on a Hetzner Cloud instance, the development k8s cluster runs on your own computer using Kind. Both environments use similar Kubernetes YAML files to define the containers.
For rapid rebuilding and hot reloading, we use Tilt, which automatically rebuilds the development environment when the code changes. Tilt is made by Docker, inc. and is very useful for doing development in containers.
- Install Docker, Tilt, Kubectl, and Kind.
- Start your Kubernetes cluster
- Run
kind create cluster --wait 5mto create your Kubernetes cluster. It should take a minute or two to download everything and start the cluster. - Verify that your cluster is working by running
kubectl version. You can also runkubectl get pods -Ato see all containers running in Kubernetes. (Note that all of Kubernetes and its running containers are all running in a Docker container named kindest/node)
- Run
- Start your development environment
- cd into the root directory for this git repo (e.g.
cd ~/IdeaProjects/tmeit-website) - Run
./startdev.sh. This will build all the containers and start up the tmeit website in your k8s cluster. (Potential issue: If you are running Fedora Linux, Docker buildkit doesn't play nice with SELinux. We could use Podman, but I think it's just easier if we disable SELinux temporarily. Runsudo setenforce 0to temporarily disable the SELinux security system.) - Click the link that tilt gives you to see the website containers starting
- (Optional) Disable any containers you don't need for your testing, such as the mailserver, the worker, or redis, in order to speed up redployments.
- (Optional) If you get errors like
failed to create fsnotify watcher: too many open fileson Linux, increase your file limits withulimit -n 8192,sudo sysctl -w fs.inotify.max_user_instances=1024, andsudo sysctl -w fs.inotify.max_user_watches=12288. Note that these new limits are reset when your computer is restarted. - Once the containers have started, open your development website at localhost:8080
- cd into the root directory for this git repo (e.g.
- Tear down your development environment
- (Optional) Stop the development containers with
tilt down - Delete your Kubernetes cluster with
kind delete cluster(Kubernetes will run in the background otherwise, eating your CPU and RAM)
- (Optional) Stop the development containers with
- Everything in master is automatically pushed to prod
- Make your changes in a new branch
- Make sure you increment the release version number in your branch by running
python release_utils/set_new_version.py X.Y.Z- Set the version number at the end of the command where
x.y.zis - This project uses semantic versioning
- Set the version number at the end of the command where
- Make a PR on Github to merge your changes into master
- Your PR will be automatically tested, and only accepted if all linting and testing passes.
- New release is published and pushed to production once your PR is merged into master.
- Container is built and published to Github's container repository
- Kubernetes manifests with updated container tag are pushed to production cluster
- Code archive, container, and kubernetes manifests will be published as a new release on Github for archival