canonical / gunicorn-k8s-operator

gunicorn-k8s-operator - charm repository.
Apache License 2.0
2 stars 3 forks source link

Django Specific Enhancements #3

Open kian99 opened 2 years ago

kian99 commented 2 years ago

Enhancement Proposal

Hi,

I've been following the dev of this Gunicorn charm and it is a really cool general charm. What I'm hoping for is the ability to use it with my Django application which I feel has a pretty common workflow (even across non Django applications). Before I can do that however there are 2 things missing. To mold it to my needs, I've lifted a lot of the charm logic and made a specific charm for my application but I'm hoping I can switch over to this charm again when/if it fits my use case (my attempt at that can be seen here).

1. Nginx Sidecar In order to server static files for my Django app I need Nginx setup as a sidecar container with the static files in the container and a config file for Nginx. Currently I've emulated what's done in the Indico operator charm and I have an image that installs Nginx and populates the static files + config and I deploy that alongside my workload container.

I can think of some options to accomodate this,

An additional setting could also be added to config.yaml like NGINX_ENABLED which could potentially enable/disable the Nginx functionality if an app doesn't require it?

Alternatively, and I'm not sure if this is the right approach,

2. Running schema migrations For my application I require that schema migrations are run when the app is deployed. Currently I set my app not to startup automatically, only once Postgres is related and the schema migration is run does the app start. The flow is, Deploy app (Blocked waiting for postgres) -> Postgres related -> Schema migration run (set a peer relation value so that we don't run the migration again) -> start Gunicorn server.

The challenge here is that to make it general, every app will have it's own command and set of pre-conditions. In my application/charm, the command resembles the following,

process = container.exec(
                command=[
                    "/usr/bin/python3",
                    "/code/manage.py",
                    "migrate",
                    "--noinput",
                ],
                environment={"DATABASE_URL": db_uri},
            )

where db_uri is obtained from

pg_data = self._stored.reldata.get("pg", {})
db_uri = pg_data.get("db_uri")

and the pre-condition is that the Postgres relation exists (i.e. db_uri is populated).

In a conversation with @mthaddon the idea came up that the entrypoint of the image could be used to define the command and the pre-condition can be verified by checking that the required environment variables are set. I imagine the environment variables would be templatted as is done in the config.yaml, so setting something like the following perhaps inside your dockerfile, ENTRYPOINT DATABASE_URL={{pg.db_uri}} python3 /code/manage.py migrate --noinput

Those 2 issues are pretty much it. Happy to discuss or elaborate further on anything above. Really cool work going on here, thanks!

mthaddon commented 2 years ago

Thanks for the input. There's a fair amount to digest there, and we'll need to draw up some design documents for each one (although the second point may end up being more about documenting recommended behaviour than anything else). We'll look into this and update this ticket once we have a path forward.