@@ -81,14 +81,15 @@ We can separate out "deployment" into two tasks:
81
81
- _Provisioning_ a new server to be able to host the code
82
82
- _Deploying_ a new version of the code to an existing server
83
83
84
- Ultimately, infrastructure-as-code tools can let you automate both of these,
85
- but for the purposes of this book, we can live with manual provisioning.
84
+ Infrastructure-as-code tools can let you automate both of these,
85
+ but the provisioning parts tend to be quite vendor-specific,
86
+ so for the purposes of this book, we can live with manual provisioning.
86
87
87
- I should probably stress once more that deployment is something that varies a lot,
88
- and that as a result there are few universal best practices for how to do it.
89
- So, rather than trying to remember the specifics of what I'm doing here,
90
- you should be trying to understand the rationale,
91
- so that you can apply the same kind of thinking in the specific future circumstances you encounter.
88
+ NOTE: I should probably stress once more that deployment is something that varies a lot,
89
+ and that as a result there are few universal best practices for how to do it.
90
+ So, rather than trying to remember the specifics of what I'm doing here,
91
+ you should be trying to understand the rationale,
92
+ so that you can apply the same kind of thinking in the specific future circumstances you encounter.
92
93
93
94
94
95
==== Choosing Where to Host Our Site
@@ -136,7 +137,7 @@ any solution should be fine, as long as:
136
137
137
138
I'm recommending Ubuntu as a distro because it's popular and I'm used to it.
138
139
If you know what you're doing, you can probably get away with using
139
- something else, but you're on your own .
140
+ something else, but I won't be able to help you as much if you get stuck .
140
141
141
142
((("Linux servers")))
142
143
If you've never started a Linux server before and you have absolutely no idea
@@ -158,7 +159,7 @@ NOTE: Some people get to this chapter, and are tempted to skip the domain bit,
158
159
In these instructions, I'm assuming that you have a nonroot user account set up,
159
160
and that it has "sudo" privileges,
160
161
so whenever we need to do something that requires root access, we use sudo,
161
- (or "become" in ansible terminology),
162
+ (or "become" in Ansible terminology),
162
163
and I'm explicit about that in the various instructions that follow.
163
164
164
165
My user is called "elspeth", but you can call yours whatever you like!
@@ -168,6 +169,7 @@ See the guide linked above if you need tips on creating a sudo user.
168
169
169
170
.General Server Debugging Tips
170
171
*******************************************************************************
172
+ // TODO: good advice but not quite sure it's phrased quite right for the new version of the chapter.
171
173
172
174
The most important lesson to remember from this chapter is,
173
175
as always but more than ever, to work incrementally,
@@ -237,32 +239,34 @@ rather than specifying a procedural series of steps to be followed one by one.
237
239
Take a look at the instructions here:
238
240
https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html
239
241
240
- ----
241
- pipx install --include-deps ansible
242
- pipx inject ansible docker
243
-
244
- # or
242
+ The simplest thing to do is to install them into the virtualenv
243
+ on our local machine:
245
244
246
- pip install ansible docker
245
+ [subs="specialcharacters,quotes"]
246
+ ----
247
+ $ *pip install ansible*
248
+ # we also need the Docker SDK for the ansible/docker integration to work:
249
+ $ *pip install docker*
247
250
----
248
251
249
252
// TODO: consider introducing an explicit requirements.dev.txt here,
250
253
// with -r requirements.txt and put ansible, docker, and selenium in there.
254
+ // or, maybe get that in place in the previous chapter, keep this one shorter.
251
255
252
256
253
257
==== A First Cut of an Ansible Playbook
254
258
255
259
Let's dip our toes into Ansible,
256
260
and see if we can get it to run a simple "hello world" container on our server.
257
261
258
- Here's what's called a "playbook" in ansible terminology.
262
+ Here's what's called a "playbook" in Ansible terminology.
259
263
It's in a format called YAML (Yet Another Markup Language),
260
264
which, if you've never come across before,
261
- you will soon develop a love-hatefootnote :[
265
+ you will soon develop a love-hate relationshipfootnote :[
262
266
The "love" part is that yaml is very easy to _read_ and scan through at a glance.
263
267
The "hate" part is that the actual syntax is surprisingly fiddly to get right:
264
268
the difference between lists and key/value maps is subtle and I can never quite remember it honestly.]
265
- relationship with .
269
+ for .
266
270
267
271
268
272
[role="sourcecode"]
@@ -292,20 +296,20 @@ relationship with.
292
296
----
293
297
====
294
298
295
- <1> An ansible playbook is a series of "tasks"
299
+ <1> An Ansible playbook is a series of "tasks"
296
300
(so in that sense it's still quite sequential and procedural),
297
301
but the individual tasks themselves are quite declarative.
298
302
Each one usually has a human-readable `name` attribute.
299
303
300
- <2> Each tasks uses an ansible "module" to do its work.
301
- The next few use the `builtin.apt` module which provides
302
- a wrapper around the `apt` Debian & Ubuntu package management tool.
304
+ <2> Each tasks uses an Ansible "module" to do its work.
305
+ This one uses the `builtin.apt` module which provides a wrapper
306
+ around the `apt` Debian & Ubuntu package management tool.
303
307
304
308
<3> Each module then provides a bunch of parameters which control how it works.
305
309
Here we specify the `name` of the package we want to install ("docker")
306
310
and tell it update its cache first, which is required on a fresh server.
307
311
308
- Most ansible modules have pretty good documentation,
312
+ Most Ansible modules have pretty good documentation,
309
313
check out the `builtin.apt` one for example.
310
314
I often skip to the
311
315
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html#examples[Examples section].
@@ -400,6 +404,14 @@ SSHing in to check things worked is a key server debugging skill!
400
404
It's something we want to practice on our staging server,
401
405
because ideally we'll want to avoid doing it on production machines.
402
406
407
+ Let's move on to trying to get our actual docker container running on the server.
408
+ As we go through, you'll see that we're going to work through very similar issues
409
+ to the ones we've already figured our way through in the last couple of chapters:
410
+
411
+ * Configuration
412
+ * Networking
413
+ * And the database.
414
+
403
415
404
416
=== Getting our image onto the server
405
417
@@ -421,7 +433,7 @@ so we're going to "simulate" this process by doing it manually.
421
433
422
434
It turns out you can "export" a container image to an archive format,
423
435
manually copy that to the server, and then re-import it.
424
- In ansible config, it looks like this:
436
+ In Ansible config, it looks like this:
425
437
426
438
[role="sourcecode"]
427
439
.infra/ansible-provision.yaml (ch11l002)
@@ -550,17 +562,19 @@ This means we don't have a dependency on having run `docker build` locally.
550
562
delegate_to: 127.0.0.1
551
563
552
564
- name: Export container image locally
565
+ [...]
553
566
----
554
567
====
555
568
556
- <1> Having this step also allows us to work around an issue
557
- with compatility between Apple's new ARM-based chips and
558
- our server's x86/amd64 architecture.
569
+ <1> I needed this `platform` attribute to work around an issue
570
+ with compatibility between Apple's new ARM-based chips and our server's
571
+ x86/amd64 architecture.
559
572
You could also use this `platform:` to cross-build docker images
560
573
for a rasbperry pi from a regular PC, or vice-versa.
574
+ It does no harm in any case.
561
575
562
576
563
- In any case, let's see if it works!
577
+ Now let's see if it works!
564
578
565
579
[subs="specialcharacters,quotes"]
566
580
----
@@ -596,25 +610,25 @@ Ah woops, we need to set those environment variables on the server too.
596
610
=== Using an env File to Store Our Environment Variables
597
611
598
612
When we run our container manually locally, we can pass in environment variables with the `-e` flag.
599
- But we don't want to hard-code secrets like SECRET_KEY into our ansible files
613
+ But we don't want to hard-code secrets like SECRET_KEY into our Ansible files
600
614
and commit them to our repo!
601
615
602
- Instead, we can use ansible to automate the creation of a secret key,
603
- and then save it to a file on the server, where it will be relatively secure
604
- (better than saving it to version contorl and pushing it to GitHub in any case!)
616
+ Instead, we can use Ansible to automate the creation of a secret key,
617
+ and then save it to a file on the server, where it _ill be _relatively_ secure
618
+ (better than saving it to version control and pushing it to GitHub in any case!)
605
619
606
- We can use a so-called "env file" to store environment variables,
607
- which are essentially a list of key-value pairs using shell syntax,
620
+ We can use a so-called "env file" to store environment variables.
621
+ Env files are essentially a list of key-value pairs using shell syntax,
608
622
a bit like you'd use with `export`.
609
623
610
624
One extra subtlety is that we want to vary the actual contents of the env file,
611
625
depending on where we're deploying to.
612
626
Each server should get its own unique secret key,
613
- adn we want different config for staging and prod, for example.
627
+ and we want different config for staging and prod, for example.
614
628
615
629
So, just as we inject variables into our html templates in Django,
616
630
we can use a templating language called "jinja2" to have variables in our env file.
617
- It's a common tool in ansible scripts, and the syntax is very similar to Django's.
631
+ It's a common tool in Ansible scripts, and the syntax is very similar to Django's.
618
632
619
633
Here's what our template for the env file will looks like:
620
634
@@ -632,7 +646,7 @@ DJANGO_ALLOWED_HOST="{{ host }}"
632
646
And here's how we use it in the provisioning script:
633
647
634
648
635
- [role="sourcecode"]
649
+ [role="sourcecode small-code "]
636
650
.infra/ansible-provision.yaml (ch11l004)
637
651
====
638
652
[source,yaml]
@@ -647,7 +661,7 @@ And here's how we use it in the provisioning script:
647
661
force: false # do not recreate file if it already exists. <2>
648
662
vars: # <3>
649
663
host: "{{ inventory_hostname }}" # <4>
650
- secret_key: "{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}"
664
+ secret_key: "{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}" # <5>
651
665
652
666
- name: Run container
653
667
community.docker.docker_container:
@@ -667,14 +681,19 @@ And here's how we use it in the provisioning script:
667
681
668
682
<3> The `vars` section defines the variables we'll inject into our template.
669
683
670
- <4> We actually use a built-in ansible variable called `inventory_hostname`.
684
+ <4> We actually use a built-in Ansible variable called `inventory_hostname`.
671
685
This variable woul actually be available in the template already,
672
686
but I'm renaming it for clarity.
673
687
688
+ <5> This `lookup('password')` thing I copy-pasted from Stackoverflow.
689
+ Come on there's no shame in that.
690
+
674
691
675
692
NOTE: Using an env file to store secrets is definitely better than committing
676
693
it to version control, but it's maybe not the state of the art either.
677
- TODO: mention other secret management tools. vault
694
+ You'll probably come across more advanced alternatives from various cloud providers,
695
+ or Hashicorp's "vault" tool.
696
+
678
697
679
698
680
699
.Idempotency and Declarative Configuration
@@ -685,8 +704,8 @@ meaning that, as much as possible, you specify the desired state that you want,
685
704
rather than specifying a series of steps to get there.
686
705
687
706
This concept goes along with the idea of "idempotency",
688
- which means that you get the same result when you run something once ,
689
- as when you run it multiple times .
707
+ which is wanting to get the same result when you run something for the first time ,
708
+ vs running it again on later occations .
690
709
691
710
An example is the `apt` module that we used to install docker.
692
711
It doesn't crash if docker is already installed, and in fact,
@@ -749,6 +768,7 @@ skipped=0 rescued=0 ignored=0
749
768
750
769
Looks good! What do our tests think?
751
770
771
+
752
772
==== More debugging
753
773
754
774
We run our tests as usual and run into a new problem:
@@ -901,6 +921,9 @@ Here's how
901
921
community.docker.docker_container_exec: # <3>
902
922
container: superlists
903
923
command: ./manage.py migrate
924
+
925
+ - name: Run container
926
+ [...]
904
927
----
905
928
====
906
929
@@ -955,11 +978,15 @@ Podman and Systemd into the mix, should things not go according to plan:
955
978
956
979
- You can get detailed info on the Container using
957
980
`docker inspect superlists`.
981
+ This is a good place to go check on environment variables,
982
+ port mappings, and exactly which image was running, for example.
958
983
959
984
- And you can inspect the image with
960
985
`docker image inspect superlists`.
961
- ((("debugging", "Docker")))
986
+ You might need this to check the exact image hash,
987
+ to make sure it's the same one you built locally.
962
988
989
+ ((("debugging", "Docker")))
963
990
964
991
*******************************************************************************
965
992
@@ -985,7 +1012,6 @@ Rewire the FT runner to be able to test against the local VM.
985
1012
Having a Vagrant config file is particularly helpful when working
986
1013
in a team--it helps new developers to spin up servers that look exactly
987
1014
like yours.((("", startref="ansible29")))
988
- ////
989
1015
990
1016
991
1017
@@ -1045,6 +1071,8 @@ $ *git log --graph --oneline --decorate*
1045
1071
[...]
1046
1072
----
1047
1073
1074
+ ////
1075
+
1048
1076
1049
1077
Anyway, you now have a live website! Tell all your friends! Tell your mum, if
1050
1078
no one else is interested! And, in the next chapter, it's back to coding
@@ -1057,26 +1085,26 @@ Further Reading
1057
1085
1058
1086
1059
1087
((("automated deployment", "additional resources")))
1060
- There's no such thing as the One True Way in deployment,
1061
- and I'm no grizzled expert in any case.
1088
+ There's no such thing as the One True Way in deployment;
1062
1089
I've tried to set you off on a reasonably sane path,
1063
1090
but there's plenty of things you could do differently,
1064
- and lots, lots more to learn besides.q
1091
+ and lots, lots more to learn besides.
1065
1092
Here are some resources I used for inspiration:
1066
1093
1067
1094
1068
1095
* http://12factor.net/[The 12-factor App] by the Heroku team
1069
1096
1070
1097
* http://hynek.me/talks/python-deployments[Solid Python Deployments for Everybody] by Hynek Schlawack
1071
1098
1072
- * The deployment chapter of <<twoscoops,Two Scoops of Django>> by Dan
1073
- Greenfeld and Audrey Roy
1099
+ * The deployment chapter of
1100
+ https://www.feldroy.com/books/two-scoops-of-django-3-x[Two Scoops of Django]
1101
+ by Dan Greenfeld and Audrey Roy
1074
1102
1075
1103
1076
1104
1077
1105
1078
1106
[role="pagebreak-before less_space"]
1079
- .Automated Deployments
1107
+ .Automated Deployment Recap
1080
1108
*******************************************************************************
1081
1109
1082
1110
TODO Maybe recap the key steps of any deployment:
@@ -1102,6 +1130,10 @@ Automating provisioning::
1102
1130
brand new servers.
1103
1131
This will involve interacting with the API of your hosting provider.
1104
1132
1133
+ ////
1134
+
1135
+ TODO: find a place for this
1136
+
1105
1137
Security::
1106
1138
A serious discussion of server security is beyond the scope of this book,
1107
1139
and I'd warn against running your own servers
@@ -1116,5 +1148,6 @@ Security::
1116
1148
wild place!
1117
1149
((("security issues and settings", "server security")))
1118
1150
((("Platform-As-A-Service (PaaS)")))
1151
+ ////
1119
1152
1120
1153
*******************************************************************************
0 commit comments