Skip to content

Commit c2b60ee

Browse files
committed
work thru csanads comments
1 parent 927be0e commit c2b60ee

File tree

1 file changed

+75
-97
lines changed

1 file changed

+75
-97
lines changed

chapter_09_docker.asciidoc

Lines changed: 75 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -150,40 +150,24 @@ But that can be fiddly to set up!
150150
And nowadays, thanks to containerization, we can do better.
151151
Because containerization is a kind of even-more-virtual virtualization.
152152

153-
Conceptually, "regular" virtualization works at the hardware level,
153+
Conceptually, "regular" virtualization works at the hardware level:
154154
it gives you multiple virtual machines (VMs)
155155
that pretend to be physical computers, on a single real machine.
156156
So you can run multiple operating systems using separate VMs
157157
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
172161
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.
173164
So you can run programs inside separate virtual environments,
174165
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".
187171
You can start one up in milliseconds,
188172
and you can run hundreds on the same machine.
189173

@@ -219,15 +203,9 @@ How will containerizing our software help with the danger areas?
219203

220204
* We can use the containers to package up as much
221205
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
223208
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.
231209

232210
* We can test our containers work by running our functional tests
233211
against them.
@@ -240,6 +218,7 @@ How will containerizing our software help with the danger areas?
240218
minimised the risk of deployment to production.
241219

242220
// TODO: consider getting rid of the staging server??
221+
// CSANAD: I would keep the staging server.
243222

244223
////
245224
@@ -251,8 +230,6 @@ But there are solutions to all of these. In order:
251230
* Using a 'staging site', on the same infrastructure as the production site,
252231
can help us test out our deployments and get things right before we go to
253232
the "real" site.
254-
// CSANAD: I would keep the staging server.
255-
256233
257234
* We can also 'run our functional tests against the staging site'. That will
258235
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:
264241
like on our own PC, a 'virtualenv' is useful on the server for
265242
managing packages and dependencies when you might be running more than one
266243
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.
273244
274245
* ((("automated deployment", "benefits of")))((("automated deployment", see="also Fabric")))And
275246
finally, 'automation, automation, automation'. By using an automated
@@ -280,7 +251,6 @@ But there are solutions to all of these. In order:
280251
"preproduction" servers. Whatever we call it, the point is to have
281252
somewhere we can try our code out in an environment that's as similar as
282253
possible to the real production server.]
283-
284254
////
285255

286256

@@ -337,7 +307,6 @@ and where testing fits in.
337307

338308
* Confidently deploy to production once we have a working deployment script for staging.
339309

340-
// CSANAD: the last two points were in reverse order
341310

342311

343312
=== As Always, Start with a Test
@@ -536,9 +505,7 @@ But it's a little less well established and documented,
536505
(the Windows installation instructions are a little more DIY for example),
537506
and in the end, although I'm a fan of a plucky upstart,
538507
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!
542509
543510
You can follow along all the instructions in the book
544511
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?
577544
4. Run `python manage.py runserver`
578545

579546

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+
580550
.Dockerfile (ch09l003)
581551
====
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-
587552
[source,dockerfile]
588553
----
589554
FROM python:slim <1>
@@ -623,7 +588,7 @@ CMD python manage.py runserver <4>
623588

624589

625590

626-
==== Docker build
591+
==== Docker Build
627592

628593
You build an image with `docker build <path-containing-dockerfile>`
629594
and we'll use the `-t <tagname>` argument to "tag" our image
@@ -667,8 +632,7 @@ superlists latest 7b8e1c9fa68e 13 minutes ago 155MB
667632

668633

669634

670-
==== Docker run
671-
635+
==== Docker Run
672636

673637
Once you've built an image,
674638
you can run one or more containers based on that image, using `docker run`.
@@ -819,20 +783,12 @@ CMD python manage.py runserver
819783

820784
TIP: Forgetting the `-r` and running `pip install requirements.txt`
821785
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_.
833789

834790

835-
==== Successful run
791+
==== Successful Run
836792

837793
Let's do the `build` and `run` in a single line.
838794
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`
881837
to be run in an interactive terminal session,
882838
otherwise you'll get strange behaviours, including not being able
883839
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+
*******************************************************************************
892874

893875

894876

@@ -913,17 +895,14 @@ Nope! What's going on here? Time for a little debugging.
913895

914896
=== Debugging a Container Networking Problems
915897

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/:
917899

918900
[[firefox-unable-to-connect-screenshot]]
919901
.Cannot connect on that port
920902
image::images/firefox-unable-to-connect.png["Firefox showing the 'Unable to connect' error"]
921903

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:
927906

928907

929908
[role="skipme"]
@@ -934,8 +913,6 @@ Quit the server with CONTROL-C.
934913

935914
Aha! We notice that we're using the wrong port, the default `8000` instead of the `8888`
936915
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".
939916

940917
Let's fix that by amending the `CMD` instruction in the Dockerfile:
941918

@@ -952,7 +929,7 @@ CMD python manage.py runserver 8888
952929
====
953930

954931
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`:
956933

957934
[subs="specialcharacters,quotes"]
958935
----
@@ -962,9 +939,9 @@ Starting development server at http://127.0.0.1:8888/
962939
----
963940

964941

965-
==== Debugging web server connectivity with "curl"
942+
==== Debugging Web Server Connectivity With "curl"
966943

967-
Nope, that won't work either.
944+
A quick check in our browser will show us that nope, that doesn't work either.
968945
Let's try an even lower-level smoke test, the traditional Unix utility `curl`.
969946
It's a command-line tool for making HTTP requests. Try it on your own computer first:
970947

@@ -1063,7 +1040,7 @@ That's definitely some HTML! And the `<title>To-Do lists</title>` looks like it'
10631040
So, we can see Django is serving our site _inside_ the container,
10641041
why can't we see it _outside_??
10651042

1066-
==== Exposing Docker Ports
1043+
==== Docker Ports Mapping
10671044

10681045
The pythonspeed guide to Docker's very first section is called
10691046
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.
10761053

10771054
So we need to tell Docker to connect the internal ports to the outside ones,
10781055
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.
10821056

10831057
`docker run` takes a `-p` argument, with the syntax `OUTSIDE:INSIDE`.
10841058
So you can actually map a different port number on the inside and outside.
@@ -1120,6 +1094,7 @@ curl: (52) Empty reply from server
11201094
// Hopefully, just like above, the difference is irrelevantr but somebody please
11211095
// confirm.
11221096

1097+
11231098
==== Essential Googling the Error Message
11241099

11251100
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>>)!
12261201
[[site-in-docker-is-up]]
12271202
.The site in Docker is up!
12281203
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.
12311204

12321205

12331206
Let's see what our functional tests say:
@@ -1316,9 +1289,6 @@ RUN python manage.py migrate --noinput <1>
13161289
CMD python manage.py runserver
13171290
----
13181291
====
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.
13221292

13231293
<1> We run `migrate` using the `--noinput` argument to suppress any little "are you sure" prompts.
13241294

@@ -1389,7 +1359,15 @@ $ *docker build -t superlists . && docker run \
13891359
-it superlists*
13901360
----
13911361

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+
13931371

13941372
TIP: if you see an error saying: `django.db.utils.OperationalError`: "unable to open database file",
13951373
try stopping the container, `rm -rf src/db.sqlite3`, then re-run the migrate command

0 commit comments

Comments
 (0)