1
1
[[chapter_09_docker]]
2
- == Deployment Part 1: Containerization aka Docker
2
+ == Containerization aka Docker
3
+ // RITA: I'm not keen on including the word "part" in chapter titles
4
+ // especially when the chapter is also within something called a "part."
5
+ // Would it make sense to put the deployment chapters into its own Part?
6
+ // So, ch8 might go into Part 1,
7
+ // chs 9-11 would go into the new Part 2 called something like "Deployment,"
8
+ // and Part 3 would start with ch 12.
3
9
4
10
.A Note for Early Release Readers
5
11
****
@@ -107,7 +113,7 @@ Security and Configuration::
107
113
(because they expose our source code in tracebacks).
108
114
109
115
110
- One way to approach the problem is to get a server,
116
+ One way to approach the problem is to get a server
111
117
and start manually configuring and installing everything,
112
118
hacking about until it works,
113
119
and maybe think about automating things laterfootnote:[
@@ -119,8 +125,8 @@ in the world of agile/lean software development,
119
125
it's that taking smaller steps usually pays off.
120
126
121
127
How can we take smaller, safer steps towards a production deployment?
122
- Can we _simulate_ the process of moving to a server,
123
- so that we can iron out all the bugs,
128
+ Can we _simulate_ the process of moving to a server
129
+ so that we can iron out all the bugs
124
130
before we actually take the plunge?
125
131
Can we then make small changes one at a time,
126
132
solving problems one by one,
@@ -153,46 +159,46 @@ to customers outside the business.
153
159
154
160
So when you come to deploy your code to a real server in a datacentre,
155
161
it will be using virtualization.
156
- And actually you can use virtualization on your own machine,
162
+ And, actually, you can use virtualization on your own machine,
157
163
with software like Virtualbox or KVM.
158
164
159
165
But that can be fiddly to set up!
160
- And nowadays, thanks to containerization, we can do better.
161
- Because containerization is a kind of even-more-virtual virtualization.
166
+ And nowadays, thanks to containerization, we can do better
167
+ because containerization is a kind of even-more-virtual virtualization.
162
168
163
169
Conceptually, "regular" virtualization works at the hardware level:
164
170
it gives you multiple virtual machines (VMs)
165
171
that pretend to be physical computers, on a single real machine.
166
172
So you can run multiple operating systems using separate VMs
167
173
on the same physical box.
168
174
169
- Containerization work at the operating system level:
175
+ Containerization works at the operating system level:
170
176
it gives you multiple virtual operating systems that
171
177
all run on a single real OS.
172
- It lets us pack the source code and its dependencies together,
178
+ It lets us pack the source code and its dependencies together--
173
179
the entire environment required to run the application.
174
- So you can run programs inside separate virtual environments,
180
+ This allows you to run programs inside separate virtual environments,
175
181
using a single real host operating system and kernel.
176
182
177
183
Have a look at
178
184
https://www.docker.com/resources/what-container/[Docker's resources on containers]
179
- for more explanation,
185
+ for more explanation.
180
186
The upshot of this is that containers are much "cheaper".
181
187
You can start one up in milliseconds,
182
188
and you can run hundreds on the same machine.
183
189
184
190
185
191
==== Docker and your CV
186
192
187
- That's all well and good for the _theoretical_ justification.
188
- But let's get to the _real_ reason for using this technology,
193
+ That's all well and good for the _theoretical_ justification,
194
+ but let's get to the _real_ reason for using this technology,
189
195
which, as always, is:
190
196
"it's fashionable so it's going to look good on my CV."
191
197
192
198
For the purposes of this book,
193
199
that's not such a bad justification really!
194
200
195
- Yes I think it's going to be a nice way to have a "pretend"
201
+ Yes, I think it's going to be a nice way to have a "pretend"
196
202
deployment on our own machine, before we try the real one--but
197
203
also, containers are so popular nowadays,
198
204
that it's very likely that you're going to encounter them at work
@@ -267,7 +273,7 @@ But there are solutions to all of these. In order:
267
273
268
274
=== An Overview of Our Deployment Procedure
269
275
270
- Over these three chapters, I'm going to go through _a_ deployment procedure.
276
+ Over the next three chapters, I'm going to go through _a_ deployment procedure.
271
277
It isn't meant to be the _perfect_ deployment procedure,
272
278
so please don't take it as being best practice,
273
279
or a recommendation--it's meant to be an illustration,
@@ -289,8 +295,7 @@ and where testing fits in.
289
295
with passing tests.
290
296
291
297
292
-
293
-
298
+ //RITA: Consider cross-referencing the chapter by number here so we can hyperlink it for convenience.
294
299
**Next chapter: Moving to a production-ready configuration**
295
300
296
301
* Gradually, incrementally change the container configuration
@@ -302,7 +307,7 @@ and where testing fits in.
302
307
303
308
// gunicorn, DEBUG=False, secret key, etc
304
309
305
-
310
+ //RITA: Consider cross-referencing the chapter by number here so we can hyperlink it for convenience.
306
311
**Third chapter: Automating deployment to real servers**
307
312
308
313
* Gradually build up an Ansible playbook to deploy our containers on a real server.
@@ -333,20 +338,19 @@ called `TEST_SERVER`:
333
338
334
339
335
340
[role="sourcecode"]
336
- .functional_tests/tests.py (ch08l001 )
341
+ .functional_tests/tests.py (ch09l001 )
337
342
====
338
343
[source,python]
339
344
----
340
345
import os
341
346
[...]
342
347
343
348
class NewVisitorTest(StaticLiveServerTestCase):
344
-
345
349
def setUp(self):
346
350
self.browser = webdriver.Firefox()
347
- test_server = os.environ.get(' TEST_SERVER' ) #<1>
351
+ test_server = os.environ.get(" TEST_SERVER" ) #<1>
348
352
if test_server:
349
- self.live_server_url = ' http://' + test_server #<2>
353
+ self.live_server_url = " http://" + test_server #<2>
350
354
----
351
355
====
352
356
@@ -364,12 +368,11 @@ and to use a real server instead.
364
368
<2> Here's the hack: we replace `self.live_server_url` with the address of
365
369
our "real" server.
366
370
367
-
368
- NOTE: A clarification: in these chapters,
369
- we run tests _against_ our Docker container, or _against_ our staging server,
370
- but that doesn't mean we run the tests _from_ Docker or _from_ our staging server.
371
- We still run the tests from our own laptop,
372
- but they target the place that's running our code.
371
+ NOTE: A clarification: when we say we run tests _against_ our Docker container,
372
+ or _against_ our staging server,
373
+ that doesn't mean we run the tests _from_ Docker or _from_ our staging server.
374
+ We still run the tests from our own laptop,
375
+ but they target the place that's running our code.
373
376
374
377
375
378
We test that said hack hasn't broken anything by running the functional
@@ -408,14 +411,13 @@ ERROR: test_can_start_a_todo_list
408
411
(functional_tests.tests.NewVisitorTest.test_can_start_a_todo_list)
409
412
---------------------------------------------------------------------
410
413
Traceback (most recent call last):
411
- File "...goat-book/functional_tests/tests.py", line 31 , in
414
+ File "...goat-book/functional_tests/tests.py", line 38 , in
412
415
test_can_start_a_todo_list
413
416
self.browser.get(self.live_server_url)
414
417
[...]
418
+
415
419
selenium.common.exceptions.WebDriverException: Message: Reached error page: abo
416
- ut:neterror?e=connectionFailure&u=http%3A//localhost:8888/&c=UTF-8&
417
- f=regular&d=Firefox%20can%27t%20establish%20a%20connection%20to%20the%20server%
418
- 20at%20localhost.
420
+ ut:neterror?e=connectionFailure&u=http%3A//localhost:8888/[...]
419
421
420
422
421
423
Ran 1 tests in 5.518s
@@ -491,7 +493,7 @@ Status: Downloaded newer image for busybox:latest
491
493
hello world
492
494
----
493
495
494
- What's happened there is that Docker has
496
+ What's happened there is that Docker has:
495
497
496
498
* Searched for a local copy of the "busybox" image and not found it
497
499
* Downloaded the image from DockerHub
@@ -507,7 +509,7 @@ Cool! We'll find out more about all of these steps as the chapter progresses.
507
509
Impartiality commands me to also recommend https://podman.io/[Podman],
508
510
which is a like-for-like replacement for Docker.
509
511
510
- It's pretty much exactly the same as docker ,
512
+ It's pretty much exactly the same as Docker ,
511
513
arguably with a few advantages even, but I won't go into detail here.
512
514
513
515
I actually tried it out on early drafts of this chapter and it worked perfectly well.
@@ -589,10 +591,10 @@ CMD python manage.py runserver <4>
589
591
<2> The `COPY` instruction (the uppercase words are called "instructions")
590
592
lets you copy files from your own computer into the container image.
591
593
We use it to copy all our source code from the newly-created _src_ folder,
592
- into a similarly-named folder at the root of the container image
594
+ into a similarly-named folder at the root of the container image.
593
595
594
596
<3> `WORKDIR` sets the current working directory for all subsequent commands.
595
- It's a bit like doing `cd /src`
597
+ It's a bit like doing `cd /src`.
596
598
597
599
<4> Finally the `CMD` instruction tells docker which command you want it to run
598
600
by default, when you start a container based on that image.
@@ -833,7 +835,7 @@ you should find the docker process has been terminated.
833
835
834
836
=== Using the FT to Check That Our Container Works
835
837
836
- Let's see what our FTs think about this Docker version of our site.
838
+ Let's see what our FTs think about this Docker version of our site:
837
839
838
840
839
841
[role="small-code"]
@@ -846,13 +848,13 @@ selenium.common.exceptions.WebDriverException: Message: Reached error page:
846
848
about:neterror?e=connectionFailure&u=http%3A//localhost%3A8888/[...]
847
849
----
848
850
849
- Nope! What's going on here? Time for a little debugging.
851
+ What's going on here? Time for a little debugging.
850
852
851
853
852
854
853
855
=== Debugging a Container Networking Problems
854
856
855
- First let's try and take a look ourselves, in our browser, by going to http://localhost:8888/:
857
+ First, let's try and take a look ourselves, in our browser, by going to http://localhost:8888/:
856
858
857
859
[[firefox-unable-to-connect-screenshot]]
858
860
.Cannot connect on that port
@@ -1023,6 +1025,7 @@ $ *docker build -t superlists . && docker run -p 8888:8888 -it superlists*
1023
1025
Now that will _change_ the error we see, but only quite subtly (see <<firefox-connection-reset>>).
1024
1026
Things clearly aren't working yet.
1025
1027
1028
+ //RITA: If at all possible, I suggest using the light or daytime theme for all browser screenshots to make them easier to read.
1026
1029
[[firefox-connection-reset]]
1027
1030
.Cannot connect on that port
1028
1031
image::images/firefox-connection-reset.png["Firefox showing the 'Connection reset' error"]
@@ -1118,7 +1121,7 @@ $ *docker build -t superlists . && docker run -p 8888:8888 -it superlists*
1118
1121
Starting development server at http://0.0.0.0:8888/
1119
1122
----
1120
1123
1121
- We can verify it's working with `curl:
1124
+ We can verify it's working with `curl` :
1122
1125
1123
1126
[subs="specialcharacters,macros"]
1124
1127
----
@@ -1147,7 +1150,7 @@ and say "well, this is hopeless, it can't be fixed",
1147
1150
and give up.
1148
1151
1149
1152
Thankfully I have had some good role models over the years
1150
- who are much better at it than me (hi Glenn!).
1153
+ who are much better at it than me (hi, Glenn!).
1151
1154
Debugging needs the patience and tenacity of a bloodhound.
1152
1155
If at first you don't succeed,
1153
1156
you need to systematically rule out options,
@@ -1236,7 +1239,7 @@ Run 'python manage.py migrate' to apply them.
1236
1239
NOTE: If you don't see this error,
1237
1240
it's because your src folder had the database file in it, unlike mine.
1238
1241
For the sake of argument, run `rm src/db.sqlite3` and re-run the build & run commands,
1239
- and you should be able to repro the error. I promise it's instructive!
1242
+ and you should be able to reproduce the error. I promise it's instructive!
1240
1243
1241
1244
1242
1245
==== Should we run "migrate" inside the Dockerfile? No. // JAN: Not sure I understand this line. You're saying that we shouldn't run migrate inside our Dockerfile, but then in the next line you do exactly that
@@ -1254,7 +1257,7 @@ If you try it, you'll find it certainly fixes the problem:
1254
1257
WORKDIR /src
1255
1258
1256
1259
RUN python manage.py migrate --noinput <1>
1257
- CMD python manage.py runserver
1260
+ CMD python manage.py runserver 0.0.0.0:8888
1258
1261
----
1259
1262
====
1260
1263
@@ -1291,7 +1294,7 @@ For the purposes of this book, the easiest analogy to a server that's "outside"
1291
1294
is to access the database from the filesystem outside the container.
1292
1295
1293
1296
That also gives us a convenient excuse to talk about mounting files in Docker,
1294
- which is a very useful thing to be able to do (TM).
1297
+ which is a very Useful Thing to be Able to Do (TM).
1295
1298
1296
1299
1297
1300
First let's revert our change:
@@ -1304,11 +1307,10 @@ First let's revert our change:
1304
1307
[...]
1305
1308
WORKDIR /src
1306
1309
1307
- CMD python manage.py runserver
1310
+ CMD python manage.py runserver 0.0.0.0:8888
1308
1311
----
1309
1312
====
1310
1313
1311
- // JAN: I assume you meant to keep CMD python manage.py runserver 0.0.0.0:8888 as is and just remove migrations. Without that, port mapping won't work and server won't listen to external connections
1312
1314
1313
1315
Let's start by re-creating the database with `migrate`
1314
1316
(when we moved everything into `./src`, we left the database file behind):
@@ -1368,6 +1370,7 @@ Ran 3 tests in 26.965s
1368
1370
OK
1369
1371
----
1370
1372
1373
+ //RITA: I'd rather add exclamation marks than extend the word.
1371
1374
AMAZING IT ACTUALLY WORKSSSSSSSS.
1372
1375
1373
1376
Ahem, that's definitely good enough for now! Let's commit.
0 commit comments