@@ -41,14 +41,25 @@ so that people can access it using a regular URL.
41
41
42
42
Perhaps more importantly, we shouldn't use the Django dev server for production;
43
43
it's not designed for real-life workloads.
44
- Instead, we'll use the popular Gunicorn Python/WSGI server.
44
+ Instead, we'll use the popular Gunicorn Python WSGI HTTP server.
45
+
46
+ NOTE: Django's `runserver` is built and optimised for local development and debugging.
47
+ It's designed to handle one user at a time,
48
+ it handles automatic reloading upon saving of the source code,
49
+ but it isn't optimised for performance,
50
+ nor has it been hardened against security vulnerabilities.
45
51
46
52
((("DEBUG settings")))
47
53
In addition, several options in _settings.py_ are currently unacceptable.
48
- `DEBUG=True`, is strongly recommended against for production,
54
+ `DEBUG=True`, is strongly discouraged for production,
49
55
we'll want to set a unique `SECRET_KEY`,
50
56
and, as we'll see, other things will come up.
51
57
58
+ NOTE: DEBUG=True is considered a security risk,
59
+ because the django debug page will display sensitive information like
60
+ the values of variables, and most of the settings in settings.py.
61
+
62
+
52
63
Let's go through and see if we can fix things one by one.
53
64
54
65
// SEBASTIAN: How about linking to Django's documentation
@@ -68,6 +79,47 @@ which I guess is what you'd want next if you already had a pony...
68
79
We'll need to first install Gunicorn into our container,
69
80
and then use it instead of `runserver`:
70
81
82
+ //001
83
+
84
+ [subs="specialcharacters,quotes"]
85
+ ----
86
+ $ *pip install gunicorn*
87
+ Collecting gunicorn
88
+ [...]
89
+ Successfully installed gunicorn-21.2.0
90
+ ----
91
+
92
+ [[what-is-wsgi]]
93
+ .What is WSGI?
94
+ *******************************************************************************
95
+ Gunicorn is a WSGI (usually pronounced as whizgee) HTTP server.
96
+ Let's go through what this means:
97
+
98
+ * An HTTP server handles incoming HTTP requests and sends back responses to the
99
+ clients.
100
+
101
+ * WSGI, the Web Server Gateway Interface, is a set of rules defining how an HTTP
102
+ server (which can be Gunicorn itself, or Nginx, Apache, etc) and a Python
103
+ program should communicate.
104
+
105
+ So, Gunicorn is an HTTP server that knows and uses the WSGI to "talk" to Python programs.
106
+ And these Python programs are likewise expected to work in a specific way.
107
+ You can check out a simple example https://gunicorn.org/[on Gunicorn's website] under "Installation".
108
+
109
+ https://peps.python.org/pep-3333[If you are interested, you can read the entire
110
+ specification here].
111
+
112
+ // CSANAD: I felt like some explanation regarding what Gunicorn and WSGI are,
113
+ // was missing.
114
+
115
+ *******************************************************************************
116
+
117
+
118
+ Gunicorn will need to know a path to a "WSGI server" (see <<what-is-wsgi>>),
119
+ which is usually a function called `application`.
120
+ Django provides one in 'superlists/wsgi.py'.
121
+ Let's change the command our image runs:
122
+
71
123
[role="sourcecode"]
72
124
.Dockerfile (ch10l001)
73
125
====
@@ -83,6 +135,9 @@ WORKDIR /src
83
135
CMD gunicorn --bind :8888 superlists.wsgi:application <2>
84
136
----
85
137
====
138
+ // CSANAD: shouldn't we try it out before adding this to the Dockerfile?
139
+ // `cd src` and then
140
+ // `gunicorn --bind :8888 superlists.wsgi:application`
86
141
87
142
<1> Installation is a standard pip install.
88
143
@@ -172,6 +227,11 @@ MIDDLEWARE = [
172
227
173
228
----
174
229
====
230
+ // CSANAD: I would add a few thoughts on the significance of the order of
231
+ // middlewares.
232
+
233
+ And if you take another manual look at your site after rebuilding the
234
+ container, things should look much healthier.
175
235
176
236
And then we need to add it to our pip installs in the Dockerfile:
177
237
@@ -437,7 +497,8 @@ Environment variables also have the advantage of working for non-Django stuff to
437
497
==== Setting DEBUG=True and SECRET_KEY
438
498
439
499
//RITA: What do you mean by "this"? Please add a word or two for context.
440
- There's lots of ways you might do this.
500
+ There are lots of ways you might do this.
501
+
441
502
Here's what I propose; it may seem a little fiddly,
442
503
but I'll provide a little justification for each choice.
443
504
Let them be an inspiration (but not a template) for your own choices!
@@ -459,6 +520,10 @@ else:
459
520
SECRET_KEY = "insecure-key-for-dev"
460
521
----
461
522
====
523
+ // CSANAD: I think variable names like "something_false" are confusing, since
524
+ // we need to set something to true so that they mean false.
525
+ // How about `DJANGO_ENV_PRODUCTION` or something similar?
526
+ // TODO yes i like this
462
527
463
528
<1> We say we'll use an environment variable called `DJANGO_DEBUG_FALSE`
464
529
to switch debug mode off, and in effect require production settings
@@ -479,6 +544,14 @@ against accidentally forgetting to set one.
479
544
TIP: Better to fail hard than allow a typo in an environment variable name to
480
545
leave you running with insecure settings.
481
546
547
+ // CSANAD: I think it would worth pointing out the development environment
548
+ // does not use Docker, launching the dev server should be done from
549
+ // the reader's host system. I think this isn't immediately obvious, e.g. I
550
+ // thought all along that from now on we would only run the server from Docker.
551
+ // If we end up making a TIP or similar about it, I think we should also mention
552
+ // in a development environment relying on containerization, programmers usually
553
+ // mount the whole /src minimizing the time-consuming rebuilding of their images.
554
+
482
555
==== Setting environment variables inside the Dockerfile
483
556
484
557
Now let's set that environment variable in our Dockerfile using the `ENV` directive:
@@ -630,6 +703,10 @@ $ *docker build -t superlists . && docker run \
630
703
An FT run (or just looking at the site) reveals that we've had a regression
631
704
in our static files:
632
705
706
+ // CSANAD: worth mentioning there was another clue. in the logs, once Gunicorn
707
+ // starts running you can see:
708
+ // `UserWarning: No directory at: /src/static/`
709
+
633
710
[role="small-code"]
634
711
[subs="specialcharacters,macros"]
635
712
----
@@ -661,6 +738,7 @@ ENV DJANGO_DEBUG_FALSE=1
661
738
CMD gunicorn --bind :8888 superlists.wsgi:application
662
739
----
663
740
====
741
+ // CSANAD: I think it would be important to use a non-privileged user in Docker
664
742
665
743
// DAVID: It would be nice to explain the difference between RUN and CMD.
666
744
@@ -701,6 +779,12 @@ $ *git status*
701
779
$ *git commit -am "Add collectstatic to dockerfile, and new location to gitignore"*
702
780
----
703
781
782
+ // CSANAD: now would be a good time to check our changes and notice git marked
783
+ // src/static as unstaged, so we should update .gitignore accordingly:
784
+ // from `static` to `src/static`.
785
+ // Or we could add this step to Chapter 09 at "Move all our code into a src
786
+ // folder"
787
+
704
788
705
789
=== Switching to a nonroot user
706
790
@@ -718,7 +802,6 @@ USER nonroot
718
802
----
719
803
720
804
721
-
722
805
=== Configuring logging
723
806
724
807
One last thing we'll want to do is make sure that we can get logs out of our server.
@@ -780,7 +863,7 @@ but it's more of shock to see that they are no longer appearing in the terminal
780
863
If you're like me you might find yourself wondering if we really _did_ see them earlier
781
864
and starting to doubt your own sanity.
782
865
But the explanation is that Django's
783
- https://docs.djangoproject.com/en/5.0 /ref/logging/#default-logging-configuration[default logging configuration]
866
+ https://docs.djangoproject.com/en/4.2 /ref/logging/#default-logging-configuration[default logging configuration]
784
867
changes when DEBUG is turned off:
785
868
786
869
This means we need to interact with the standard library's `logging` module,
@@ -793,7 +876,7 @@ but sometimes you just wanna say "just print stuff to stdout pls",
793
876
and you wish that configuring the simplest thing was a little easier.].
794
877
795
878
Here's pretty much the simplest possible logging config
796
- which just prints everything to the console (ie standard out).
879
+ which just prints everything to the console (i.e. standard out).
797
880
798
881
799
882
[role="sourcecode"]
@@ -835,7 +918,7 @@ django.db.utils.OperationalError: no such table: lists_list
835
918
836
919
Re-create the database with `./src/manage.py migrate` and we'll be back to a working state.
837
920
838
- Don't forget to commit our changes to _settings.py_,
921
+ Don't forget to commit our changes to _settings.py_ and _Dockerfile_ ,
839
922
and I think we can call it job done!
840
923
We've at least touched on many or most of the things you might need to think about
841
924
when considering production-readiness,
0 commit comments