Table of Contents
Open Table of Contents
A Star Wars Docker Tutorial
Objectives
Upon completion of this session, you should understand how to:
- Run and interact with docker image
- Author a Dockerfile, build it, and run it
- Identify at least one use case where containerization is useful
Context
You are working as a consultant for 1-900-STAR-WARZ, a paid call-in telephone service for researching Star Wars facts. This a niche service where the customer base consists of new and veteran Star Wars fanatics who are usually either:
- boomers, who do not know how to internet
- conspiracy theorist who do not want to internet
Effectively, they provide a VPN-by-telephone service.
Current Need
Provide a command line solution for looking up a Star Wars character and their homeworld.
Restrictions
- Employees of 1-900-STAR-WARZ are allowed internet access but only from a linux terminal. They are not allowed to use web browsers or other GUI applications because they require too much RAM and mouse clicking.
- Customer would like to be able to maintain this solution on their own.
- Is only familiar with shell scripting (no python or nodejs please)
- Prefers Ubuntu or other debian variant of linux
- Wants to minimize disk utilization
- Prefers httpie over curl
- Does not want to install any additional dependencies on employee computers other than Docker
Exercise
The following exercise requires a working container Docker installation. Podman will work just fine as well. It is also assumed you know your way around a linux-based terminal.
Run a Docker Image in a Container
How to find an appropriate docker image?
- dockerhub: https://hub.docker.com/
- Amazon ECR Public: https://gallery.ecr.aws/
- Microsoft Artifact Registry: https://mcr.microsoft.com/
To review:
- image publisher
- image size
- image contents
- security concerns
For this exercise, we’ll use debian:bullseye-slim
😎 PROTIP: You will learn more through muscle memory by typing the following commands and NOT copy/pasting.
Hello docker
Enter the following in a terminal:
docker run debian:bullseye-slim echo Hello docker
What just happened??
- A docker image was downloaded from DockerHub (the default)
- A container was started with this image as a container and executed
the provided command of
echo Hello docker
Try running the same command again. Does it take less time? Docker caches downloaded images
Now try the following caommands:
docker run -it --rm debian:bullseye-slim
echo Hello docker
exit
What just happened??
- We created and ran a docker image with an interactive terminal (-i and -t params)
- Did you notice the change in shell prompt after line 1?
- You are now in an isolated Linux container
- The default command on the Docker file is bash. As a result, you get a bash terminal/shell. Reference.
exit
closes the shell, which detaches from the container.- The docker run argument,
--rm
, automatically removes the container on exit and frees up that disk space - An image can be run as many container instances on the same system.
--rm
removes the container, not the image. Try the following command to confirm the image is still on your computer:
docker image ls
Experimenting with a Solution
We will use swapi.dev to perform the character lookup.
// example request
GET <https://swapi.dev/api/people/?search=luke>
// example response (truncated to relevant data)
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"name": "Luke Skywalker",
"homeworld": "https://swapi.dev/api/planets/1/"
}
]
}
Back to the Container!
Let’s see if we can request and parse only the relevant data in the container
# get a bash shell in a container
docker run -it --rm debian:bullseye-slim
# install httpie and jq
apt update && apt install httpie jq -y
# try the http request
# NOTE: `--print b` will ensure that only the response body is emitted
http --print b <https://swapi.dev/api/people/?search=luke>
# now, let's limit the output to what we need
http --print b <https://swapi.dev/api/people/?search=luke> | jq '.results[] | { name: .name, homeworld: .homeworld }'
Build a Docker file
Ideally, the customer should not have to run all of the above commands to perform a look up. And the customer does not want to install httpie and jq on all computers. Instead we can bundle the solution into our own docker image based on the debiean:bullseye-slim image. How do we simplify the solution to just the desired input and output?
To begin, create a directory for the solution and some files.
mkdir ~/star-warz
cd ~/star-warz
touch Dockerfile
touch run.sh
Define the Dockerfile: Iteration 1
Edit the files as follows
Dockerfile
# This is the base image for our image
FROM debian:bullseye-slim
# Install the required dependencies
# The arguments -y and --no-install-recommends will avoid any unecessary prompts
RUN apt update && apt install httpie jq -y --no-install-recommends
# Copy a script into the docker image, and make it executable
COPY run.sh .
RUN chmod +x run.sh
# Define the entrypoint for docker run
ENTRYPOINT [ "./run.sh" ]
run.sh
# !/bin/bash
http --print b <https://swapi.dev/api/people/?search=luke> | jq '.results[] | { name: .name, homeworld: .homeworld }'
How do we run it?
# build the image
# -t specifies a tag for the image, otherwise, the image is only identifiable by its hash
docker build . -t star-warz
# run it
# WARNING: here -t does not mean "tag", it is setting up a terminal
docker run --rm -t star-warz
What just happened?
- We built a new docker image with a tag of
star-warz
. Try runningdocker image ls
and confirming its existence - We ran the image with a terminal and exited.
- We should have seen the search results for luke
How do we make it re-usable?
luke is hard-coded in run.sh
To enable the solution to accept a search term from the command line requires a small change.
Edit run.sh
as follows
# !/bin/bash
http --print b <https://swapi.dev/api/people/?search=$1> | jq '.results[] | { name: .name, homeworld: .homeworld }'
Build and run again
docker build . -t star-warz && docker run --rm -t star-warz
Run more searches
docker run --rm -t star-warz luke
docker run --rm -t star-warz anakin
Additional Exercises
- Currently, the response for homeworld is a URL. The customer would like to see the homeworld name instead.
- How could you further simplify the solution?