@@ -232,6 +232,9 @@ At my registrar, the control screens looked a bit like <<registrar-control-scree
232
232
.Domain setup
233
233
image::images/gandi_add_dns_a_record.png["Registrar control screen for adding a DNS record"]
234
234
235
+ // CSANAD: due to technical reasons, I cannot check on Digital Ocean whether this
236
+ // screenshot needs to be update. Please, someone else have a look!
237
+
235
238
236
239
((("A-Records")))
237
240
In the DNS system, pointing a domain at a specific IP address is called an "A-Record".
@@ -304,6 +307,8 @@ The "hate" part is that the actual syntax is surprisingly fiddly to get right:
304
307
the difference between lists and key/value maps is subtle and I can never quite remember it honestly.]
305
308
for.
306
309
310
+ // CSANAD: I would make it more obvious we created another directory for the
311
+ // ansible file.
307
312
308
313
[role="sourcecode"]
309
314
.infra/ansible-provision.yaml (ch11l001)
@@ -514,6 +519,8 @@ In Ansible config, it looks like this:
514
519
recreate: true
515
520
----
516
521
====
522
+ // CSANAD: I would add `update_cache: true` to the `Install docker` task back,
523
+ // it's a good practice to update the apt cache before installing.
517
524
518
525
<1> We export the docker image to a `.tar` file by using the `docker_image` module
519
526
with the `archive_path` set to temp file, and setting the `delegate_to` attribute
@@ -572,6 +579,7 @@ false, "AttachStdout": true, "Cmd": ["gunicorn", "--bind", ":8888",
572
579
"superlists.wsgi:application"], "Domainname": "", "Entrypoint": null, "Env":
573
580
[...]
574
581
----
582
+ // CSANAD: earlier we also added the `PLAY RECAP` line.
575
583
576
584
577
585
For completeness, let's also add a step to explicitly build the image locally.
@@ -675,9 +683,13 @@ Here's what our template for the env file will looks like:
675
683
----
676
684
DJANGO_DEBUG_FALSE=1
677
685
DJANGO_SECRET_KEY="{{ secret_key }}"
678
- DJANGO_ALLOWED_HOST ="{{ host }}"
686
+ DJANGO_ALLOWED_HOSTS ="{{ host }}"
679
687
----
680
688
====
689
+ // CSANAD: we named the allowed hosts' env variable in plural form, as it's
690
+ // named in settings.py but we pass one value, so it feels weird.
691
+ //
692
+ // I'd suggest DJANGO_ALLOWED_HOSTS="{{ hostname }}"
681
693
682
694
And here's how we use it in the provisioning script:
683
695
@@ -742,11 +754,18 @@ rather than specifying a series of steps to get there.
742
754
This concept goes along with the idea of "idempotency",
743
755
which is wanting to get the same result when you run something for the first time,
744
756
vs running it again on later occations.
757
+ // CSANAD: I would rephrase it a little:
758
+ // "which means doing something multiple times brings the same results as
759
+ // doing it for the first time."
745
760
746
761
An example is the `apt` module that we used to install docker.
747
762
It doesn't crash if docker is already installed, and in fact,
748
763
Ansible is smart enough to check first before trying to install anything.
749
764
765
+ // CSANAD: I think adding a counter-example, something that isn't idempotent
766
+ // would be helpful. E.g. adding a list item to our superlist, because
767
+ // it results in the list getting longer.
768
+
750
769
There is some subtlety here, for example, our templated env file
751
770
will only be writen once, so the step is idempotent in the sense
752
771
that it doesn't overwrite the file with a new random secret key every time you run it.
@@ -811,7 +830,7 @@ We run our tests as usual and run into a new problem:
811
830
812
831
[subs="specialcharacters,macros"]
813
832
----
814
- $ pass:quotes[*TEST_SERVER=staging.ottg.co.uk python manage.py test functional_tests*]
833
+ $ pass:quotes[*TEST_SERVER=staging.ottg.co.uk python src/ manage.py test functional_tests*]
815
834
[...]
816
835
selenium.common.exceptions.WebDriverException: Message: Reached error page:
817
836
about:neterror?e=connectionFailure&u=http%3A//staging.ottg.co.uk/[...]
@@ -828,6 +847,15 @@ $ pass:quotes[*curl -iv staging.ottg.co.uk*]
828
847
curl: (7) Failed to connect to staging.ottg.co.uk port 80 after 25 ms: Couldn't
829
848
connect to server
830
849
----
850
+ // CSANAD: my curl output looks a little different, saying "Connection refused"
851
+ //
852
+ // $ curl -iv 192.168.122.23
853
+ // * Trying 192.168.122.23:80...
854
+ // * connect to 192.168.122.23 port 80 failed: Connection refused
855
+ // * Failed to connect to 192.168.122.23 port 80 after 2 ms: Connection refused
856
+ // * Closing connection 0
857
+ // curl: (7) Failed to connect to 192.168.122.23 port 80 after 2 ms: Connection refused
858
+
831
859
832
860
Now let's ssh in and try `curl` from the server itself:
833
861
@@ -855,6 +883,10 @@ curl: (7) Failed to connect to localhost port 80 after 0 ms: Connection refused
855
883
----
856
884
857
885
Hmm, `curl` fails on the server too.
886
+ // CSANAD: Ackchually I'm not sure if it's supposed to work, since we set
887
+ // `inventory_hostname` for DJANGO_ALLOWED_HOSTS, so `localhost`
888
+ // would not get through.
889
+
858
890
But all this talk of `port 80`, both locally and on the server, might be giving us a clue.
859
891
Let's check `docker ps`:
860
892
@@ -869,7 +901,7 @@ minutes superlists
869
901
870
902
This might be ringing a bell now--we forgot the ports.
871
903
872
- We want to expose port 8888 inside the container as port 80 (the default web/http port)
904
+ We want to map port 8888 inside the container as port 80 (the default web/http port)
873
905
on the server:
874
906
875
907
[role="sourcecode"]
@@ -888,12 +920,24 @@ on the server:
888
920
----
889
921
====
890
922
923
+ // CSANAD: I would remind the reader we need to run ansible-playbook again.
924
+
891
925
That gets us to
892
926
893
927
----
894
928
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate
895
929
element: [id="id_list_table"]; [...]
896
930
----
931
+ // CSANAD: I have a different error:
932
+ // AssertionError: 'To-Do' not found in 'Bad Request(400)'
933
+ //
934
+ // >>> from django.conf import settings
935
+ // >>>
936
+ // >>>
937
+ // >>> settings.ALLOWED_HOSTS
938
+ // ['"192.168.122.23"']
939
+ // >>>
940
+ //
897
941
898
942
899
943
=== Mounting the database on the server and running migrations
@@ -909,6 +953,8 @@ django.db.utils.OperationalError: no such table: lists_list
909
953
----
910
954
911
955
956
+ // CSANAD: I think this `ansible-playbook` output was supposed to be shown after
957
+ // the changes in the `ansible-provision.yaml`
912
958
[subs="specialcharacters,quotes"]
913
959
----
914
960
$ *ansible-playbook --user=elspeth -i staging.ottg.co.uk, infra/ansible-provision.yaml -v*
@@ -929,6 +975,9 @@ skipped=0 rescued=0 ignored=0
929
975
930
976
931
977
Here's how
978
+ // CSANAD: I think a little more explanation is missing from here. Maybe just
979
+ // a sentence, but just showing the changes and the output feels a
980
+ // little incomplete.
932
981
933
982
[role="sourcecode"]
934
983
.infra/ansible-provision.yaml (ch11l006)
@@ -981,7 +1030,7 @@ Hooray
981
1030
[role="small-code"]
982
1031
[subs="specialcharacters,macros"]
983
1032
----
984
- $ pass:quotes[*TEST_SERVER=staging.ottg.co.uk python manage.py test functional_tests*]
1033
+ $ pass:quotes[*TEST_SERVER=staging.ottg.co.uk python src/ manage.py test functional_tests*]
985
1034
Found 3 test(s).
986
1035
[...]
987
1036
@@ -1008,9 +1057,11 @@ and reloads it automatically if it crashes.
1008
1057
1009
1058
A few more places to look and things to try, now that we've introduced
1010
1059
Podman and Systemd into the mix, should things not go according to plan:
1060
+ // CSANAD: we did not mention Podman or Systemd in this chapter
1011
1061
1012
1062
- You can check the Container logs using
1013
1063
`docker logs superlists`.
1064
+ // CSANAD: we already used this a lot, so this isn't "more debugging tip"
1014
1065
1015
1066
- You can get detailed info on the Container using
1016
1067
`docker inspect superlists`.
@@ -1155,10 +1206,13 @@ which are a fairly typical set of steps for deployment in general
1155
1206
2. Installing *system dependencies* - in our case, it was mainly Docker,
1156
1207
but inside the Docker image, we also had some system dependencies too,
1157
1208
like Python itself.
1209
+ // CSANAD: this is not true in the current edition as we are just using the
1210
+ // superlists image which is built upon the python:slim
1158
1211
1159
1212
3. Getting our *application code* (or "artifacts") onto the server.
1160
1213
In our case, since we're using Docker, the thing we needed to transfer was a Docker image.
1161
1214
We used a manual process, but typically you'd push and pull to an image repository.
1215
+ // CSANAD: we actually automated this step in this edition
1162
1216
1163
1217
4. Setting *environment variables and secrets*.
1164
1218
Depending on how you need to vary them,
@@ -1181,8 +1235,10 @@ which are a fairly typical set of steps for deployment in general
1181
1235
In our case, we stop the old container and start a new one.
1182
1236
In more advanced setups, you might be trying to achieve zero-downtime deploys,
1183
1237
and looking into techniques like red-green deployments.
1238
+ // CSANAD: we haven't mentioned the downtime so far
1184
1239
1185
1240
// TODO is there a better word than "switching across"?
1241
+ // CSANAD: I can only think of "releasing" or "deploying"
1186
1242
1187
1243
Every single aspect of deployment can and probably should be automated.
1188
1244
Here are a couple of general principles to think about
0 commit comments