Open BeritJanssen opened 4 years ago
On the branch feature/dockerize
, there's a first version. This should build, but so far I can only access the backend, and not the frontend via the browser.
TO DOs:
I followed this tutorial for the Django/Postgres part, and this one for Angular.
- [ ] for production use, add a server (gunicorn / nginx?)
I was thinking to do this in development mode, too. I suggest an nginx server that reverse proxies to both backend and frontend.
Development mode:
/static
and /api
to the backendProduction mode:
python manage.py collectstatic
gunicorn
, still on the same portconnect
server that always returns the same index.html
(which is enough to implement at first), or if we ever need to be so fancy, an Angular Universal server that pre-renders the HTML for the given path./static
and /media
directly (web servers like nginx have to option to search in multiple directories for a given path, so the build outputs of the backend and frontend don't need to be pooled in a single directory for this purpose. However, the nginx container does need to access them. Can that be done?)/api
to the backendAbove, I left out the functional test suite, which should probably run on yet another container. However, this container has no difference between production and development and does not need to be part of the docker composition. It can test the application on any web address, but if addressing the local docker composition, it should interface with the nginx server just like a regular user.
Dockerization with an approach like the above does have some drawbacks compared to the undockerized situation. Most importantly, since frontend html pages are served directly from the frontend instead of being routed through the backend, session management and authorization are not as straightforward. A simple, but somewhat ugly solution is to work around this by issueing additional requests from the frontend to the backend. A cleaner, but trickier solution is to handle this from the reverse proxy server instead. When forwarding a request to the frontend, it can send an additional request to the backend in parallel to handle session management and then combine the information from both sources when sending the response to the client. Since both variants require that the backend has some kind of dedicated endpoint for session manipulation, starting with the ugly simple solution can be a first step towards the clean solution.
Of course, I do think that the benefits outweigh the drawbacks:
@jgonggrijp , thanks for lining out your vision for this! Why not run the nginx server on the same container as the frontend? That way we wouldn't need any logic to decide whether the backend or the frontend should handle a request, right?
Why not run the nginx server on the same container as the frontend?
Because that wouldn't be modular, i.e., it wouldn't fit nicely with #2. In my opinion, the frontend should only be responsible for frontend stuff, and if you create a project without a backend, you don't need the additional middlewoman that nginx is meant to provide.
That way we wouldn't need any logic to decide whether the backend or the frontend should handle a request, right?
I don't see that. Surely we still want to send some requests to the backend container, regardless of what container nginx is run from, and we still need to decide whether to do that or not based on the request path?
Hey, great that you're working on / thinking about this! Life should be a lot easier, and at least more homogeneous (as far as moving from development to production is concerned), if all of this is implemented. Nice! Two questions, just because I am curious:
1) Is there any particular reason why we'd use nginx over Apache? It introduces yet another new application, presumably (I am not familiar with it at all) with its own set of things-you-need-to-know(-but-don't-if-you're-new-to-it). Given that there will probably still be older applications on regular servers as well, I'm pretty sure Apache will be around for quite some time, i.e. is there a good reason to introduce nginx next to it?.
2) Does having three docker containers imply three terminals in which we have to start them separately? If not, where does (terminal) output end up?
Good questions, Alex. Nginx is much more lightweight than Apache. Having two Apache instances on a single server is quite undesirable (especially with the added overhead of three docker containers) while having one Apache instance and one or more Nginx instances is no problem at all. The Nginx developers even encourage you to run it next to other web servers.
Just to avoid any confusion in this regard, Backoffice will still deploy with Apache. Nginx would act as a gateway between Apache and our application.
The entire constellation of docker containers can be started at once with docker compose up
, in which case I think all the output will go to a single terminal (@BeritJanssen please correct me if I'm wrong). However, you can also start each container separately if you want.
Currently, the branch feature/dockerize
is using base python:3.6-alpine. I just found this blogpost which states that even though alpine leads to smaller images and faster build times for other environments, such as Go, for Python, it increases build time and image size. Indeed, if you use python:3.6, this will default to debian-buster. So I will update the Dockerfile of the backend to user buster(-slim) instead.
@jgonggrijp : do you have suggestions of how to handle static files from the frontend, such as logos? I'm a bit stuck with that.
@BeritJanssen I'll take a look at it today.
@BeritJanssen See #20. Playing with this, I identified a bunch of things that I think need to be done next:
collectstatic
in development mode.Needless to say, I can help with these and future steps.
Somewhat further down the line, I think it might be nice to just upload our finished images somewhere for deployment, so no building needs to take place on the server.
Strange that you report backend isn't working. In my local versions of the cookiecutter, this works (both for an empty test app and embedded in khatt).
I tried visiting localhost:80/api/
with an empty test app. It errored out on the ground that backend
is not in the allowed hosts. It's not really surprising, given that the settings.py
hasn't been adjusted to the new situation yet. No worry though, it will be easy to fix.
Alright, yes, indeed, I fixed that in the khatt repo. Drawback of testing in one repo and updating another at the same time... Will push that.
Okay, some further steps. The backend works, even though static files (css) from the backend are not accessible yet (noticeable when navigating to localhost/api), and the paths to access rest-framework resources are not available: backend:8000/api/something/
doesn't work but localhost/api/something/
does. This is probably to be fixed in the nginx.conf
.
I made bind mounts out of the backend and frontend code, which also takes care of the livereload step (need to reload the browser after backend changes, but this is the case for local development without Docker, too).
Some progress: accessing static files of the backend (style sheets etc.) now works, using collectstatic
. The directory of static files is shared between the backend and nginx container using a named volume.
The proxy_pass config for 'api' is also set (this should still become configurable to another backend app name of choice).
@BeritJanssen I would like to be involved in this, as I already have a vision of how to approach it. Perhaps discuss on Signal/Teams?