Necessity gives birth not only to invention, but can lead to updating and upgrading skills. This year, moving to developing on a machine too lightweight to run a virtual machine necessitated using Docker to create my local development environments, which I was only to happy to learn given its ubiquity.
Docker, for those who don’t know, provides a tool-set for packaging applications, or even whole application clusters, into deployable containers, including a standard set of dependencies and environments. Container technology represents an advance over virtual machines. In my use-case, containers have far less overhead than the virtual machine (VM) technology I used to use for local development. Instead of needing an entire VM, containers approach the problem more efficiently by leveraging the underlying system and resources.
Docker tends to be used a lot not just for development but for deployment. Since all that is necessary for the application to run is packaged in the container, this makes deploying applications across different host systems much less painful, and relatively hassle-free, at least in theory.
But there’s a big learning curve with Docker I feel. Representing as it does a slight paradigm shift in the way we develop and deploy, it requires a different mindset and adjustment to learn. Recently I switched machines again and decided to re-build everything I’d learned in docker this year from scratch, in relation to my main use case, which is simply running a local instance of the WordPress CMS, with which to develop custom themes and plugins. For this tool I use docker-compose, which I’ll get to later
But first I thought I’d take a step down the docker rabbit hole and revise the very basics, since there were some things I thought I might have missed in my haste to get a local WP environment running.
Normally in this situation I search the web for beginner tutorials and courses, and use my critical judgement to choose the best one for me. I ended up looking at two principle resources to get me up to speed with the very basics of Docker.
Docker in 2 hours
This video from freecodecamp.org provides a useful introduction, that helped me get to grips with some essential commands.
The course is somewhat hands on, with tasks and quizzes included via the interactive DevOps learning platform KodeKloud, “the True Learn-By-Doing platform!”. Great if you want that but not quite what I was looking for. I didn’t want the friction of signing up to a website to complete the course, but it’s quite possible to work through the examples in the course locally. The course introduces understanding such essential topics within docker as port mapping, data persistence using volume mapping, environment variables, before moving on to creating an image, including managing commands, networking and storage. The course also includes lectures on docker-compose (a way of configuring multiple containers at once), online cloud-based repositories of docker images (docker registry), a deeper look at the docker engine itself, and running docker orchestrations in production. The course consists of discrete lectures that can be worked through at the learner’s own pace.
Docker for beginners
The other resource I looked at, “docker for beginners“, makes a good stab at being “the one-stop shop for getting your hands dirty with Docker”, aiming to demystify the software and guide the learner through “hands-on experience with building and deploying” applications running in docker.
I found this resource, produced by Prakhar Srivastav, a software engineer working on development tools for google, more suitable for my needs – a straightforward linear path through Docker basics, using its own dedicated git repository (hosted on github).
Ptakhar begins with walking us through the basics of pulling a docker image and running it, but without the fine detail and useful commands given to us by freecodecamp’s video course. Referring to and switching between the two thus gave me a well-triangulated view of the software.
Prakhar continues with “baby steps” – pulling an image of a simple static html site that uses nginx as a dependency demonstrates how docker can provision a simple webserver for us.
Next Prakhar provides a slightly more complex application – a standalone Flask webapp, randomly displaying cat gifs, for us to wrap up in a docker container, to teach us how to make our own docker images from our apps. At this point in the tutorial however, I ran into a problem. Using the provided Dockerfile, it was easy to build the image. However when attempting to run the container, the code threw an error:
Traceback (most recent call last): File "/usr/src/app/./app.py", line 1, in from flask import Flask, render_template File "/usr/local/lib/python3.10/site-packages/flask/init.py", line 21, in from .app import Flask, Request, Response File "/usr/local/lib/python3.10/site-packages/flask/app.py", line 34, in from .sessions import SecureCookieSessionInterface File "/usr/local/lib/python3.10/site-packages/flask/sessions.py", line 14, in from collections import MutableMapping ImportError: cannot import name 'MutableMapping' from 'collections' (/usr/local/lib/python3.10/collections/init.py)
I like following slightly outdated tutorials, since when encountering a bug relating to the ever-changing rules of various dependencies, it’s satisfying to track down the issue and fix it. It’s a good way to learn the problem-solving skills needed. In this case, here’s the Dockerfile provided:
FROM python:3 # set a directory for the app WORKDIR /usr/src/app # copy all the files to the container COPY . . # install dependencies RUN pip install --no-cache-dir -r requirements.txt # define the port number the container should expose EXPOSE 5000 # run the command CMD ["python", "./app.py"]
Note the only line without a comment, the first, the FROM command, which instructs Docker to initialise a “new build stage and sets the Base Image for subsequent instructions” [docs]. As is well known, if a tag is not added to specify a version, docker will assume you want the latest version of the image. In this case, pulling the python:3 image pulls the latest version, 3.10.
My approach was to search the web for the last line of the error:
ImportError: cannot import name 'MutableMapping' from 'collections' (/usr/local/lib/python3.10/collections/__init__.py)
Which led me to a note on Stackoverflow that importing from collections has been deprecated in python since 3.3, and stops working in 3.10
I simply tried a solution that worked: altering the FROM command in the Dockerfile to specify python:3.9 (the latest version before the breaking change) instead fixes the problem, and indeed is the proposed solution to the bug on the docker-curriculum github repository. At the end of the thread though, a user proposes a different solution: the example app provided, built in the Python framework Flask, specifies using Flask v1.0; the github user JohnFowlerDXC suggests simply updating this to specify the latest version of Flask, which of course takes account of the breaking change to Python, and then Docker can continue assuming the latest version of Python. This strikes me as a much better solution since it updates Flask as well as ensure the app always uses the latest version of Python. Sure, further similar updates might need to take place down the line, in case of any further changes in Python that might require an update to Flask, so some might argue it’s better to fix a version that works, and indeed this would be leveraging one of Docker’s advantages, which is precisely this ability to specify precisely versioned and provisioned stable environments. However, in this case, updating Flask and paying the cost of keeping it updated seems to me preferable, just in order to keep up with the latest software.
So I completed the tutorial with this version of the sample code, continuing to use the latest version of python as per the author’s intentions, but updating the internal Python app to use the latest version of Flask. It all worked as expected.
Next time, I’ll walk through how I use docker and docker-compose daily to run containerised WordPress environments on my local machine.
Until then, this git repository contains a long list of docker resources to get your teeth into: