@@ -189,9 +189,8 @@ At my registrar, the control screens looked a bit like <<registrar-control-scree
189
189
190
190
[[registrar-control-screens]]
191
191
.Domain setup
192
- image::images/twp2_0902 .png["Registrar control screens for two domains "]
192
+ image::images/gandi_add_dns_a_record .png["Registrar control screen for adding a DNS record "]
193
193
194
- //TODO: adjust illustration to show "superlists" not "book-example"
195
194
196
195
((("A-Records")))
197
196
In the DNS system, pointing a domain at a specific IP address is called an "A-Record".
@@ -263,42 +262,20 @@ relationship with.
263
262
264
263
tasks:
265
264
266
- - name: Install docker <1>
265
+ - name: Install docker #<1>
266
+ ansible.builtin.apt: #<2>
267
+ name: docker #<3>
268
+ state: latest
269
+ update_cache: true
267
270
become: true
268
- block:
269
- - name: Add Docker GPG apt Key
270
- ansible.builtin.apt_key: <2>
271
- url: https://download.docker.com/linux/ubuntu/gpg
272
- state: present
273
-
274
- - name: Add Docker Repository
275
- ansible.builtin.apt_repository:
276
- repo: deb https://download.docker.com/linux/ubuntu jammy stable
277
- state: present
278
-
279
- - name: Update apt and install docker-ce
280
- ansible.builtin.apt:
281
- name: docker-ce
282
- state: latest
283
- update_cache: true
284
-
285
- - name: Install Docker Module for Python
286
- ansible.builtin.pip:
287
- name: docker
288
-
289
- - name: Add our user to Docker allowed users
290
- ansible.builtin.user:
291
- name: elspeth
292
- groups: docker
293
- append: true
294
271
295
272
- name: Run test container
296
273
community.docker.docker_container:
297
274
name: testcontainer
298
275
state: started
299
276
image: busybox
300
277
command: echo hello world
301
- become: true
278
+ become: true
302
279
----
303
280
====
304
281
@@ -322,56 +299,55 @@ https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.h
322
299
323
300
324
301
[subs="specialcharacters,quotes"]
325
-
326
302
----
327
- $ *ansible-playbook --user=elspeth -i 192.168.56.10, infra/ansible-provision.yaml -vv*
303
+ $ *ansible-playbook --user=elspeth -i staging.ottg.co.uk, infra/ansible-provision.yaml -vv*
304
+ ansible-playbook [core 2.16.3]
305
+ config file = None
306
+ [...]
307
+ No config file found; using defaults
308
+ Skipping callback 'default', as we already have a stdout callback.
309
+ Skipping callback 'minimal', as we already have a stdout callback.
310
+ Skipping callback 'oneline', as we already have a stdout callback.
311
+
328
312
PLAYBOOK: ansible-provision.yaml **********************************************
329
313
1 plays in infra/ansible-provision.yaml
330
314
331
315
PLAY [all] ********************************************************************
332
316
333
317
TASK [Gathering Facts] ********************************************************
334
- task path: ...goat-book/infra/ansible-provision.yaml:2
335
- ok: [192.168.56.10]
336
-
337
- TASK [Add Docker GPG apt Key] *************************************************
338
- task path: ...goat-book/infra/ansible-provision.yaml:9
339
- changed: [192.168.56.10] => {"after": ["8D81803C0EBFCD88", "7EA0A9C3F273FCD8", "D94AA3F0EFE21092", "871920D1991BC93C"], "before": ["D94AA3F0EFE21092", "871920D1991BC93C"], "changed": true, "fp": "8D81803C0EBFCD88", "id": "8D81803C0EBFCD88", "key_id": "8D81803C0EBFCD88", "short_id": "0EBFCD88"}
340
-
341
- TASK [Add Docker Repository] **************************************************
342
- task path: ...goat-book/infra/ansible-provision.yaml:14
343
- changed: [192.168.56.10] => {"changed": true, "repo": "deb https://download.docker.com/linux/ubuntu jammy stable", "sources_added": ["/etc/apt/sources.list.d/download_docker_com_linux_ubuntu.list"], "sources_removed": [], "state": "present"}
344
-
345
- TASK [Update apt and install docker-ce] ***************************************
346
- task path: ...goat-book/infra/ansible-provision.yaml:19
347
- changed: [192.168.56.10] => {"cache_update_time": [...]
348
- changed: [192.168.56.10] => {"cache_update_time": 1706583891, "cache_updated":
349
- true, "changed": true, "stderr": "", "stderr_lines": [], "stdout": "Reading
350
- package lists...\nBuilding dependency tree...\nReading state
318
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:2
319
+ ok: [staging.ottg.co.uk]
320
+ PLAYBOOK: ansible-provision.yaml **********************************************
321
+ 1 plays in infra/ansible-provision.yaml
322
+
323
+ TASK [Install docker] *********************************************************
324
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:6
325
+ ok: [staging.ottg.co.uk] => {"cache_update_time": 1708981325, "cache_updated": true, "changed": false}
326
+
327
+
328
+ TASK [Install docker] *************************************************************************************************************
329
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:6
330
+ changed: [staging.ottg.co.uk] => {"cache_update_time": [...]
331
+ "cache_updated": true, "changed": true, "stderr": "", "stderr_lines": [],
332
+ "stdout": "Reading package lists...\nBuilding dependency tree...\nReading [...]
351
333
information...\nThe following additional packages will be installed:\n
352
- containerd.io docker-buildx-plugin docker-ce-cli docker-ce-rootless-extras\n
353
- [...]
354
- TASK [Add our user to Docker allowed users] ***********************************
355
- changed: [192.168.56.10] => {"append": true, "changed": true, "comment": "",
356
- "group": 1001, "groups": "docker", "home": "/home/elspeth", "move_home": false,
357
- "name": "elspeth", "shell": "/bin/bash", "state": "present", "uid": 1001}
334
+ wmdocker\nThe following NEW packages will be installed:\n docker wmdocker\n0
335
+
358
336
TASK [Run test container] *****************************************************
359
- task path: ...goat-book/infra/ansible-provision.yaml:31
360
- changed: [192.168.56.10] => {"changed": true, "container": {"AppArmorProfile":
361
- "docker-default", "Args": ["hello", "world"], "Config": {"AttachStderr": true,
362
- "AttachStdin": false, "AttachStdout": true, "Cmd": ["echo", "hello", "world"],
337
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:13
338
+ changed: [staging.ottg.co.uk] => {"changed": true, "container":
339
+ {"AppArmorProfile": "docker-default", "Args": ["hello", "world"], "Config":
363
340
[...]
364
341
365
- PLAY RECAP ***********************************************************
366
- 192.168.56.10 : ok=6 changed=6 unreachable=0 failed=0
342
+ PLAY RECAP ********************************************************************
343
+ staging.ottg.co.uk : ok=3 changed=2 unreachable=0 failed=0
367
344
skipped=0 rescued=0 ignored=0
368
345
----
369
346
370
- TODO: stop using local ip
371
347
372
348
373
349
////
374
-
350
+ old error message when trying to use elspeth user to run docker.
375
351
this goes wrong because groups don't work immediately:
376
352
377
353
TASK [Run test container] *****************************************************
@@ -382,30 +358,41 @@ PermissionError(13, 'Permission denied'))"}
382
358
waiting a few minutes fixes it
383
359
384
360
for now i'll just put become:true
385
-
386
-
387
361
////
388
362
389
363
390
364
=== SSHing Into the Server and Viewing Container Logs
391
365
392
- Now ssh into the server, check it worked
366
+ Time to get into some good old-fashioned sysadmin!
367
+ Let's SSH in to our server and see if we can see any evidence that our container has run.
368
+
369
+ We use `docker ps -a` to view all containers, including old/stopped ones,
370
+ and we can use `docker logs` to view the output from one of them:
393
371
394
- TODO: forget podman, just use docker.
395
372
396
373
[role="server-commands"]
397
374
[subs="specialcharacters,quotes"]
398
375
----
399
- elspeth@server:$ *podman ps -a*
400
- elspeth@server:$ *podman logs testcontainer*
401
- hello, world
376
+
377
+ Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-67-generic x86_64)
378
+ [...]
379
+
380
+ elspeth@server$ *docker ps -a*
381
+ CONTAINER ID IMAGE COMMAND CREATED STATUS
382
+ PORTS NAMES
383
+ 3a2e600fbe77 busybox "echo hello world" 2 days ago Exited (0) 10
384
+ minutes ago testcontainer
385
+
386
+ elspeth@server:$ *docker logs testcontainer*
387
+ hello world
402
388
----
403
389
404
390
TIP: Look out for that `elspeth@server`
405
391
in the command-line listings in this chapter.
406
392
It indicates commands that must be run on the server,
407
393
as opposed to commands you run on your own PC.
408
394
395
+
409
396
SSHing in to check things worked is a key server debugging skill!
410
397
It's something we want to practice on our staging server,
411
398
because ideally we'll want to avoid doing it on production machines.
@@ -419,12 +406,11 @@ and organisations will often run private ones,
419
406
hosted by cloud providers like AWS.
420
407
421
408
So your process of getting an image onto a server is usually
422
- * push the image from your machine to the registry
423
- * pull the image from the registry onto the server.
409
+ * Push the image from your machine to the registry
410
+ * Pull the image from the registry onto the server.
424
411
Usually this step is implicit,
425
- in that you just specifying the image name
426
- in the format registry-url/image-name:tag,
427
- and then `docker run` takes care of pulling down the image for you.
412
+ in that you just specifying the image name in the format registry-url/image-name:tag,
413
+ and then `docker run` takes care of pulling down the image for you.
428
414
429
415
But I don't want to ask you to create a DockerHub account,
430
416
or implicitly endorse any particular provider,
@@ -443,43 +429,132 @@ In ansible config, it looks like this:
443
429
- hosts: all
444
430
445
431
tasks:
446
- - name: Install podman
432
+ - name: Install docker
447
433
ansible.builtin.apt:
448
- name: podman
449
- update_cache: yes
434
+ name: docker
435
+ state: latest
450
436
become: true
451
437
452
- - name: Export container image locally
453
- containers.podman.podman_save:
454
- image: superlists
455
- dest: /tmp/superlists-img.oci
456
- format: oci-archive
457
- force: true
438
+ - name: Export container image locally #<1>
439
+ community.docker.docker_image:
440
+ name: superlists
441
+ archive_path: /tmp/superlists-img.tar
442
+ source: local
458
443
delegate_to: 127.0.0.1
459
444
460
- - name: Upload image to server
445
+ - name: Upload image to server #<2>
461
446
ansible.builtin.copy:
462
- src: /tmp/superlists-img.oci
463
- dest: /tmp/superlists-img.oci
447
+ src: /tmp/superlists-img.tar
448
+ dest: /tmp/superlists-img.tar
464
449
465
- - name: Import container image on server
466
- containers.podman.podman_load:
467
- input: /tmp/superlists-img.oci
450
+ - name: Import container image on server #<3>
451
+ community.docker.docker_image:
452
+ name: superlists
453
+ load_path: /tmp/superlists-img.tar
454
+ source: load
455
+ state: present
456
+ become: true
468
457
469
458
- name: Run container
470
- containers.podman.podman_container :
459
+ community.docker.docker_container :
471
460
name: superlists
472
461
image: superlists
473
462
state: started
474
463
recreate: true
475
464
----
476
465
====
477
466
467
+ <1> We export the docker image to a `.tar` file by using the `docker_image` module
468
+ with the `archive_path` set to temp file, and setting the `delegate_to` attribute
469
+ to say we're running that command on our local machine rather than the server.
478
470
479
- Let's see if that worked!
471
+ <2> We then use the `copy` module to upload the tarfile to the server
480
472
473
+ <3> And we use `docker_image` again but this time with `load_path` and `source: load`
474
+ to import the image back on the server
475
+
476
+ [subs="specialcharacters,quotes"]
477
+ ----
478
+ $ *ansible-playbook --user=elspeth -i staging.ottg.co.uk, infra/ansible-provision.yaml -vv*
479
+ [...]
480
+
481
+ PLAYBOOK: ansible-provision.yaml **********************************************
482
+ 1 plays in infra/ansible-provision.yaml
483
+
484
+ PLAY [all] ********************************************************************
485
+
486
+ TASK [Gathering Facts] ********************************************************
487
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:2
488
+ ok: [staging.ottg.co.uk]
489
+
490
+ TASK [Install docker] *********************************************************
491
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:5
492
+ ok: [staging.ottg.co.uk] => {"cache_update_time": 1708982855, "cache_updated": false, "changed": false}
493
+
494
+ TASK [Export container image locally] *****************************************
495
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:11
496
+ changed: [staging.ottg.co.uk -> 127.0.0.1] => {"actions": ["Archived image
497
+ superlists:latest to /tmp/superlists-img.tar, overwriting archive with image
498
+ 11ff3b83873f0fea93f8ed01bb4bf8b3a02afa15637ce45d71eca1fe98beab34 named
499
+ superlists:latest"], "changed": true, "image": {"Architecture": "amd64",
500
+ [...]
501
+
502
+ TASK [Upload image to server] *************************************************
503
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:18
504
+ changed: [staging.ottg.co.uk] => {"changed": true, "checksum":
505
+ "313602fc0c056c9255eec52e38283522745b612c", "dest": "/tmp/superlists-img.tar",
506
+ [...]
507
+
508
+ TASK [Import container image on server] ***************************************
509
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:23
510
+ changed: [staging.ottg.co.uk] => {"actions": ["Loaded image superlists:latest
511
+ from /tmp/superlists-img.tar"], "changed": true, "image": {"Architecture":
512
+ "amd64", "Author": "", "Comment": "buildkit.dockerfile.v0", "Config":
513
+ [...]
514
+
515
+ TASK [Run container] **********************************************************
516
+ task path: ...goat-book/superlists/infra/ansible-provision.yaml:32
517
+ changed: [staging.ottg.co.uk] => {"changed": true, "container":
518
+ {"AppArmorProfile": "docker-default", "Args": ["--bind", ":8888",
519
+ "superlists.wsgi:application"], "Config": {"AttachStderr": true, "AttachStdin":
520
+ false, "AttachStdout": true, "Cmd": ["gunicorn", "--bind", ":8888",
521
+ "superlists.wsgi:application"], "Domainname": "", "Entrypoint": null, "Env":
522
+ [...]
523
+ ----
524
+
525
+ TODO: sort out macos M1/arch issues, `docker build --platform linux/amd64` etc.
526
+
527
+ Looks ok! Let's see if that worked?
528
+
529
+ [subs="specialcharacters,quotes"]
530
+ ----
531
+
532
+ Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-67-generic x86_64)
533
+ [...]
534
+
535
+ elspeth@server$ *docker ps -a*
536
+ CONTAINER ID IMAGE COMMAND CREATED STATUS
537
+ PORTS NAMES
538
+ 3a2e600fbe77 busybox "echo hello world" 2 days ago Exited (0) 10
539
+ minutes ago testcontainer
540
+
541
+ elspeth@server:$ *docker logs testcontainer*
542
+ [2024-02-26 22:19:15 +0000] [1] [INFO] Starting gunicorn 21.2.0
543
+ [2024-02-26 22:19:15 +0000] [1] [INFO] Listening at: http://0.0.0.0:8888 (1)
544
+ [2024-02-26 22:19:15 +0000] [1] [INFO] Using worker: sync
545
+ [...]
546
+ File "/src/superlists/settings.py", line 22, in <module>
547
+ SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
548
+ ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
549
+ File "<frozen os>", line 685, in __getitem__
550
+ KeyError: 'DJANGO_SECRET_KEY'
551
+ [2024-02-26 22:19:15 +0000] [7] [INFO] Worker exiting (pid: 7)
552
+ [2024-02-26 22:19:15 +0000] [1] [ERROR] Worker (pid:7) exited with code 3
553
+ [2024-02-26 22:19:15 +0000] [1] [ERROR] Shutting down: Master
554
+ [2024-02-26 22:19:15 +0000] [1] [ERROR] Reason: Worker failed to boot.
555
+ ----
481
556
482
- Nope. TODO: debug, ssh into the server, show missing env vars .
557
+ Ah woops, we need to set those environment variables on the server too .
483
558
484
559
485
560
=== Using an env File to Store Our Environment Variables
0 commit comments