@@ -150,40 +150,24 @@ But that can be fiddly to set up!
150
150
And nowadays, thanks to containerization, we can do better.
151
151
Because containerization is a kind of even-more-virtual virtualization.
152
152
153
- Conceptually, "regular" virtualization works at the hardware level,
153
+ Conceptually, "regular" virtualization works at the hardware level:
154
154
it gives you multiple virtual machines (VMs)
155
155
that pretend to be physical computers, on a single real machine.
156
156
So you can run multiple operating systems using separate VMs
157
157
on the same physical box.
158
- // CSANAD: to me, the wording sounded like it meant the multiple OS-s
159
- // ran inside (each) VM. Maybe "using separate VMs" is a little
160
- // more clear.
161
-
162
- Containers work at the operating system level: packing the source code and its
163
- dependencies together, often with the entire environment required to run the
164
- application, while using the host system's kernel. This is what we call
165
- containers.
166
- // CSANAD: I felt like some explanation like this on the term "container"
167
- // was missing. Probably a simple hand-drawn image visualizing the
168
- // differences between virtualization and containerization could be also
169
- // helpful.
170
-
171
- They can give you multiple virtual operating systems that
158
+
159
+ Containerization work at the operating system level:
160
+ it gives you multiple virtual operating systems that
172
161
all run on a single real OS.
162
+ It lets us pack the source code and its dependencies together,
163
+ the entire environment required to run the application.
173
164
So you can run programs inside separate virtual environments,
174
165
using a single real host operating system and kernel.
175
- // CSANAD: rephrased it a little, since containers aren't necessarily
176
- // separate operating systems. Sometimes they are just a few
177
- // processes running in isolation. This, in my opinion aligns better
178
- // with the way Docker worded it.
179
- For further clarification, have a look at
180
- https://www.docker.com/resources/what-container/[Docker's Resources on
181
- containers]
182
-
183
- The upshot of this is that containers are much "cheaper" in terms of
184
- system resources.
185
- // CSANAD: probably nobody would believe you were talking about
186
- // money, but I think it's more specific this way.
166
+
167
+ Have a look at
168
+ https://www.docker.com/resources/what-container/[Docker's resources on containers]
169
+ for more explanation,
170
+ The upshot of this is that containers are much "cheaper".
187
171
You can start one up in milliseconds,
188
172
and you can run hundreds on the same machine.
189
173
@@ -219,15 +203,9 @@ How will containerizing our software help with the danger areas?
219
203
220
204
* We can use the containers to package up as much
221
205
of the functionality of our application as possible,
222
- which in turn will minimise the amount of configuration
206
+ like a production-ready web server and static files system.
207
+ This in turn will minimise the amount of configuration
223
208
we need to do to our actual servers.
224
- // CSANAD: I'm not sure about this point. Can you be a bit more
225
- // specific or maybe give a few examples?
226
- // I feel like the first point is about having the dependencies along
227
- // with the source code and setting open ports, users set up for running
228
- // the program, DB-related settings, etc. This one is a little vague,
229
- // I'm not sure what functionalities would save additional configuration
230
- // of the servers.
231
209
232
210
* We can test our containers work by running our functional tests
233
211
against them.
@@ -240,6 +218,7 @@ How will containerizing our software help with the danger areas?
240
218
minimised the risk of deployment to production.
241
219
242
220
// TODO: consider getting rid of the staging server??
221
+ // CSANAD: I would keep the staging server.
243
222
244
223
////
245
224
@@ -251,8 +230,6 @@ But there are solutions to all of these. In order:
251
230
* Using a 'staging site', on the same infrastructure as the production site,
252
231
can help us test out our deployments and get things right before we go to
253
232
the "real" site.
254
- // CSANAD: I would keep the staging server.
255
-
256
233
257
234
* We can also 'run our functional tests against the staging site'. That will
258
235
reassure us that we have the right code and packages on the server, and
@@ -264,12 +241,6 @@ But there are solutions to all of these. In order:
264
241
like on our own PC, a 'virtualenv' is useful on the server for
265
242
managing packages and dependencies when you might be running more than one
266
243
Python [keep-together]#application#.
267
- // CSANAD: I would keep virtualenv in containers. First, because it doesn't hurt and
268
- // I have no knowledge of it resulting in any overhead. Second, because pip warns
269
- // against globally installed packages, and ignoring warnings is bad.
270
- // Finally, because it "feels" right:
271
- // my application is set up so that if I decide to switch over from containers to VM-s, I
272
- // don't really need to touch dependency management.
273
244
274
245
* ((("automated deployment", "benefits of")))((("automated deployment", see="also Fabric")))And
275
246
finally, 'automation, automation, automation'. By using an automated
@@ -280,7 +251,6 @@ But there are solutions to all of these. In order:
280
251
"preproduction" servers. Whatever we call it, the point is to have
281
252
somewhere we can try our code out in an environment that's as similar as
282
253
possible to the real production server.]
283
-
284
254
////
285
255
286
256
@@ -337,7 +307,6 @@ and where testing fits in.
337
307
338
308
* Confidently deploy to production once we have a working deployment script for staging.
339
309
340
- // CSANAD: the last two points were in reverse order
341
310
342
311
343
312
=== As Always, Start with a Test
@@ -536,9 +505,7 @@ But it's a little less well established and documented,
536
505
(the Windows installation instructions are a little more DIY for example),
537
506
and in the end, although I'm a fan of a plucky upstart,
538
507
Docker is open source too,
539
- so I decided to stick with Docker. But you could definitely check it out!
540
- // CSANAD: Based on the premises in the sentence and the fact that we are using Docker
541
- // all along the chapter, I think it was meant to be like this.
508
+ so I decided to stick with it. But you could definitely check it out!
542
509
543
510
You can follow along all the instructions in the book
544
511
by just substituing the `docker` binary for `podman` in all the CLI instructions,
@@ -577,13 +544,11 @@ What do we need to do? Something like this, right?
577
544
4. Run `python manage.py runserver`
578
545
579
546
547
+ We create a new file called _Dockerfile_ in the base folder of our repo,
548
+ next to the `src/` directory we made earlier:
549
+
580
550
.Dockerfile (ch09l003)
581
551
====
582
-
583
- // CSANAD: I think just a few words would be nice here, such as: "let's create
584
- // a new file called `Dockerfile` next to the `src/` directory we've just
585
- // created."
586
-
587
552
[source,dockerfile]
588
553
----
589
554
FROM python:slim <1>
@@ -623,7 +588,7 @@ CMD python manage.py runserver <4>
623
588
624
589
625
590
626
- ==== Docker build
591
+ ==== Docker Build
627
592
628
593
You build an image with `docker build <path-containing-dockerfile>`
629
594
and we'll use the `-t <tagname>` argument to "tag" our image
@@ -667,8 +632,7 @@ superlists latest 7b8e1c9fa68e 13 minutes ago 155MB
667
632
668
633
669
634
670
- ==== Docker run
671
-
635
+ ==== Docker Run
672
636
673
637
Once you've built an image,
674
638
you can run one or more containers based on that image, using `docker run`.
@@ -819,20 +783,12 @@ CMD python manage.py runserver
819
783
820
784
TIP: Forgetting the `-r` and running `pip install requirements.txt`
821
785
is such a common error, that I recommend you do it _right now_
822
- and get familiar with the error message,
823
- because (at the time of writing), it's not very self-explanatory.
824
- And it's a mistake I still make, _all the time_.
825
-
826
- // CSANAD: This is no longer true, the error message is very helpful by now:
827
- //
828
- // $ pip install requirements.txt
829
- // ERROR: Could not find a version that satisfies the requirement requirements.txt (from versions: none)
830
- // HINT: You are attempting to install a package literally named "requirements.txt" (which cannot exist). Consider using the '-r' flag to install the packages listed in requirements.txt
831
- // ERROR: No matching distribution found for requirements.txt
832
-
786
+ and get familiar with the error message
787
+ (which is thankfully much more helpful than it used to be).
788
+ It's a mistake I still make, _all the time_.
833
789
834
790
835
- ==== Successful run
791
+ ==== Successful Run
836
792
837
793
Let's do the `build` and `run` in a single line.
838
794
This is a pattern I used quite often when developing a Dockerfile,
@@ -881,14 +837,40 @@ WARNING: Make sure you use the `-it` flags to the Docker `run`
881
837
to be run in an interactive terminal session,
882
838
otherwise you'll get strange behaviours, including not being able
883
839
to interrupt the docker process with _Ctrl-C_.
884
- In case it already happened, you can terminate it using
885
- `docker stop <container_id>`.
886
- Also, it's worth mentioning that you can specify just enough characters
887
- of the container ID that uniquely identifies the container - e.g. if you
888
- have a container with the ID `abcdef123456` and no other container ID
889
- starts with `a`, you can use just `a` to reference this container in your
890
- Docker commands:
891
- `docker stop a`.
840
+ See <<how-to-stop-a-docker-container>> for an escape hatch.
841
+
842
+
843
+ [[how-to-stop-a-docker-container]]
844
+ .How to Stop a Docker Container
845
+ *******************************************************************************
846
+ If you've got a container that's "hanging" in a terminal window,
847
+ you can kill it from another one.
848
+
849
+ The docker daemon lets you list all the currently running containers
850
+ with `docker ps`:
851
+
852
+ [role="skipme small-code"]
853
+ ----
854
+ $ *docker ps
855
+ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
856
+ 0818e1b8e9bf superlists "/bin/sh -c 'python …" 4 seconds ago Up 4 seconds hardcore_moore
857
+ ----
858
+
859
+ This tells us a bit about each container, including a unique ID,
860
+ and a randomly-generated name (you can override that if you want to).
861
+
862
+ We can use the ID or the name to kill the container with `docker kill`:
863
+
864
+ [role="skipme"]
865
+ ----
866
+ $ docker kill 0818e1b8e9bf
867
+ 0818e1b8e9bf
868
+ ----
869
+
870
+ And if you go back to your other terminal window,
871
+ you should find the docker process has been terminated.
872
+
873
+ *******************************************************************************
892
874
893
875
894
876
@@ -913,17 +895,14 @@ Nope! What's going on here? Time for a little debugging.
913
895
914
896
=== Debugging a Container Networking Problems
915
897
916
- First let's try and take a look ourselves, in our browser. Let's visit http://localhost:8888/:
898
+ First let's try and take a look ourselves, in our browser, by going to http://localhost:8888/:
917
899
918
900
[[firefox-unable-to-connect-screenshot]]
919
901
.Cannot connect on that port
920
902
image::images/firefox-unable-to-connect.png["Firefox showing the 'Unable to connect' error"]
921
903
922
- // CSANAD: Some more specific instructions were missing. We didn't specify the address.
923
- // Based on the following text I think it's supposed to be
924
- // http://localhost:8888 here but of course, it would fail with any other port as well.
925
-
926
- Now let's take another look at the output from our `docker run`. Here's what appeared right at the end:
904
+ Now let's take another look at the output from our `docker run`.
905
+ Here's what appeared right at the end:
927
906
928
907
929
908
[role="skipme"]
@@ -934,8 +913,6 @@ Quit the server with CONTROL-C.
934
913
935
914
Aha! We notice that we're using the wrong port, the default `8000` instead of the `8888`
936
915
that we specified in the `TEST_SERVER` env var.
937
- // CSANAD: since we haven't specified :8000 explicitly either, maybe this sentence is
938
- // clearer with this extra word "default".
939
916
940
917
Let's fix that by amending the `CMD` instruction in the Dockerfile:
941
918
@@ -952,7 +929,7 @@ CMD python manage.py runserver 8888
952
929
====
953
930
954
931
Ctrl+C the current dockerized container process if it's still running in your terminal,
955
- give it another `build && run`:
932
+ then give it another `build && run`:
956
933
957
934
[subs="specialcharacters,quotes"]
958
935
----
@@ -962,9 +939,9 @@ Starting development server at http://127.0.0.1:8888/
962
939
----
963
940
964
941
965
- ==== Debugging web server connectivity with "curl"
942
+ ==== Debugging Web Server Connectivity With "curl"
966
943
967
- Nope , that won 't work either.
944
+ A quick check in our browser will show us that nope , that doesn 't work either.
968
945
Let's try an even lower-level smoke test, the traditional Unix utility `curl`.
969
946
It's a command-line tool for making HTTP requests. Try it on your own computer first:
970
947
@@ -1063,7 +1040,7 @@ That's definitely some HTML! And the `<title>To-Do lists</title>` looks like it'
1063
1040
So, we can see Django is serving our site _inside_ the container,
1064
1041
why can't we see it _outside_??
1065
1042
1066
- ==== Exposing Docker Ports
1043
+ ==== Docker Ports Mapping
1067
1044
1068
1045
The pythonspeed guide to Docker's very first section is called
1069
1046
https://pythonspeed.com/articles/docker-connection-refused/[Connection Refused],
@@ -1076,9 +1053,6 @@ from the ports _outside_ the container, the ones we can see on our host machine.
1076
1053
1077
1054
So we need to tell Docker to connect the internal ports to the outside ones,
1078
1055
to "publish" or "map" them, in Docker terminology.
1079
- // CSANAD: "expose" is different, it's for inter-container communication and it's
1080
- // done by the EXPOSE instruction in the Dockerfile. The -p is short for
1081
- // --publish and this is port mapping.
1082
1056
1083
1057
`docker run` takes a `-p` argument, with the syntax `OUTSIDE:INSIDE`.
1084
1058
So you can actually map a different port number on the inside and outside.
@@ -1120,6 +1094,7 @@ curl: (52) Empty reply from server
1120
1094
// Hopefully, just like above, the difference is irrelevantr but somebody please
1121
1095
// confirm.
1122
1096
1097
+
1123
1098
==== Essential Googling the Error Message
1124
1099
1125
1100
The need to map ports and the `-p` argument to `docker run` are something you just learn,
@@ -1226,8 +1201,6 @@ A quick visual inspection confirms--the site is up (<<site-in-docker-is-up>>)!
1226
1201
[[site-in-docker-is-up]]
1227
1202
.The site in Docker is up!
1228
1203
image::images/twp2_0903.png["The front page of the site, at least, is up"]
1229
- // CSANAD: This isn't the staging site yet! These screenshots and some of the text
1230
- // seems to be not updated.
1231
1204
1232
1205
1233
1206
Let's see what our functional tests say:
@@ -1316,9 +1289,6 @@ RUN python manage.py migrate --noinput <1>
1316
1289
CMD python manage.py runserver
1317
1290
----
1318
1291
====
1319
- // CSANAD: I prefer specifying the path (/venv/bin/python) too, but we already set the PATH to in-
1320
- // clude /venv/bin and also, we haven't been using this explicit path in the Dockerfile.
1321
- // So, for the sake of consistency, I suggest removing it here as well.
1322
1292
1323
1293
<1> We run `migrate` using the `--noinput` argument to suppress any little "are you sure" prompts.
1324
1294
@@ -1389,7 +1359,15 @@ $ *docker build -t superlists . && docker run \
1389
1359
-it superlists*
1390
1360
----
1391
1361
1392
- //CSANAD: sessions.0001_initial is the last migration on the last line, removed `[...]`
1362
+ ////
1363
+ footgun: if you don't have db.sqlite3
1364
+
1365
+ docker: Error response from daemon: error while creating mount source path '/Users/harry.percival/workspace/Book-TDD-Web-Dev-Python/source/chapter_09_docker/superlists/src/db.sqlite3': chown /Users/harry.percival/workspace/Book-TDD-Web-Dev-Python/source/chapter_09_docker/superlists/src/db.sqlite3: permission denied.
1366
+ ERRO[0000] error waiting for container: context canceled
1367
+
1368
+ ❯ ls src/db.sqlite3/
1369
+ ////
1370
+
1393
1371
1394
1372
TIP: if you see an error saying: `django.db.utils.OperationalError`: "unable to open database file",
1395
1373
try stopping the container, `rm -rf src/db.sqlite3`, then re-run the migrate command
0 commit comments