It’s been a while, but thank you for checking in again! In one of our previous tutorials, we built a basic Docker container using nothing more than an Alpine Linux image and some container notes. As we’ve seen in other tutorials, we can extend this to run a myriad of other programs within the container on startup. But what good is a container if it can’t be shared? To do so, we need to learn a few new things: how to save a container, how to share a container, and how to load a container. Let’s get started, shall we?
As stated in the introduction, we’re going to keep this tutorial simple. The main flow will be something like this:
- Build a single Docker container on a light-weight Alpine Linux image
- Enable the Alpine image with Python (and pip)
- Create a simply Python file for the container to run on startup
- Build the container and export as an image
- Move the image to a different folder to emulate container sharing
- Unpack and run the “shared” container
Since we’ve seen many of these steps before, I’ll defer the explanations to previous Docker Demo tutorials. For brevity, the purpose of each step is added as a comment above each command in the
# Define base image of the container FROM alpine:3.14 AS build # Add notes to the container LABEL version = "1.0" LABEL description = "A more advanced Docker file!" # Define Python environment / install python & pip ENV PYTHONUNBUFFERED=1 RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python RUN python3 -m ensurepip RUN pip3 install --no-cache --upgrade pip setuptools # Copy our Python script from local to the container COPY ./hello.py . # Auto-run the Python script CMD ["python","hello.py"]
Similar to how we kept things simple with our
dockerfile, let’s go ahead and do the same thing with a very basic Python file. Here, the Python file only prints out a single line of text, “Greetings, friends!”. Keep in mind, the Python script file is expected to be in the same location as our Dockerfile!
Building the Docker container
So far, we’re still in familiar territory. Let’s go ahead and build our Docker container with commands we’ve already learned:
docker build --no-cache -t dockerfile:v0 . [+] Building 14.6s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/alpine:3.14 0.4s => CACHED [1/5] FROM docker.io/library/alpine:3.14@sha256:4c869a63e1b7c0722fed1e402a6466610327c3b83bdddb94bd94fb71da7f638a 0.0s => [internal] load build context 0.1s => => transferring context: 29B 0.0s => [2/5] RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python 2.7s => [3/5] RUN python3 -m ensurepip 5.0s => [4/5] RUN pip3 install --no-cache --upgrade pip setuptools 5.7s => [5/5] COPY ./hello.py . 0.1s => exporting to image 0.5s => => exporting layers 0.4s => => writing image sha256:c6a7cb4e5423410574e3859e344cfb0f8b1a3f485fe49211e2ee969aecad1e1f 0.0s => => naming to docker.io/library/dockerfile:v0
Now that our container has been built into an image (
dockerfile:v0), we can run it in interactive mode (
docker run -it dockerfile:v0):
docker run -it dockerfile:v0 Greetings, friends!
The output here is no surprise, but it confirms that everything we’ve done so far works perfectly, and that we’re using the correct image name.
Exporting a Docker container
With the old stuff out of the way, on to our new topic: exporting a Docker container. Similar to what we’ve seen in previous tutorials, Docker has a very nice and simple syntax for achieving the image export:
docker save. In fact, the documentation states that not only can we cherry pick particular tags we want to export, but we can also choose the name of the export file, as well! The syntax to do so follows this format:
docker save -o <output_file_name>.tar <image_tag>
Since this exports as to a
tar archive format, we can even peek inside the image using Windows Explorer; doing so, however, is not terribly revealing. We are presented with a list of alpha-numerically named folders, a
repositories file and a
manifest.json file…neither of which are immediately readable.
With this in mind, let’s go ahead and export our new container, tagged
dockerfile:v0 as a randomly named
tar archive file,
littleduck.tar. Although no output is shown, a new file (
littleduck.tar) is added to the same file directory as our
PS C:\Desktop\docker demo> docker save -o littleduck.tar dockerfile:v0 PS C:\Desktop\docker demo>
Sharing the Docker image
If we open a Windows Explorer to the folder where our
dockerfile resides, we see that the size of the exported Docker image is ~48 MB. The size is mainly due to not only the Docker dependencies installed within our container, but also the size of the Python installation, and, to a lesser degree, the size of the Alpine operating system we used as the base image for our container.
The smallish file size means it can be easily migrated to a server, e-mailed to a colleague, or placed on DockerHub. However, if DockerHub if your preferred method of sharing your Docker images, you’ll be much better off using
Git-like commands… but we’ll cover this in another tutorial. For now, let’s just say a friend has e-mailed us the file, and we’ve saved it in another folder. Since my files for this demo are located in
C:\Desktop\docker demos\, I’ll copy the new container to a separate folder,
Downloads folder, I can press
Shift and right click, selecting “Open Powershell Window here”. Within Powershell, I can run two commands — the first verifies the existence of our new Docker image archive, and the second confirms that Docker is callable via the
Docker environment variable.
PS C:\Downloads> ls Directory: C:\Downloads Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 11/23/2022 9:10 AM 50614894 littleduck.tar PS C:\Downloads> docker Usage: docker [OPTIONS] COMMAND A self-sufficient runtime for containers Options: --config string Location of client config files (default "C:\\Users\\aaron\\.docker")
Importing the Docker image
With the new Docker image verified within our “new” folder, we can use another straight-forward Docker command to load the file:
docker load. Since we are calling Powershell within the
Downloads folder, we can simply use the following syntax:
docker load -i <file_name>
-i flag stands for
--input, and our file name is simply the direct path of our new
.tar file. Within the
Downloads folder, then, we can execute the load command, followed by our normal
docker run -it <image_name> command:
C:\Downloads>docker load -i .\littleduck.tar Loaded image: dockerfile:v0 C:\Downloads>docker run -it dockerfile:v0 Greetings, friends!
Aha, success! The first output generated by the Windows terminal / Powershell shows the
.tar file was successfully unpacked, and our image,
dockerfile:v0 was loaded into the Docker environment. From here, we further validate the image’s operation via the output string, “Greetings, friends!”
In this tutorial, we’ve really come full-circle. Starting from scratch, we’ve now built a Docker container with Python functionality, and saved the compiled image into an external file. We’ve probed the image file in Windows Explorer, and we’ve moved the file to an arbitrary location on our computer — a simulation of sharing the file with others. From there, we called basic Docker commands to successfully load and import the image. Lastly, we ran the image and demonstrated its proper output.
So where do we go from here? Again, it’s your choice! Feel free to drop a suggestion in the comments below and let me know what topics you’d like to see covered. Until then, thanks again for learning with me — we’re all in this together! If you’re enjoying the content, please feel free to Like, comment, and subscribe — see you next time!
Get new content delivered directly to your inbox.