Lab 11: Docker
Task 1: Install Docker on Ubuntu
It is recommended that you do not run docker as the root user. If you currently cannot run $ docker
with your non-root account, then take the following steps:
If you need to add a user to the docker group that you’re not logged in as, declare that username explicitly using:
$ sudo usermod -aG docker <username>
Task 2: Pull images and run containers
- Locate the application you want to run on Docker hub. Let’s run the Docker
hello-world
application https://hub.docker.com/_/hello-world.$ docker run hello-world
Docker will first search locally for the hello-world
image. It will then download the image from Docker Hub if it is not found locally.Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:faa03e786c97f07ef34423fccceeec2398ec8a5759259f94d99078f264e9d7af
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
Docker Hub is the default repository for Docker installations.
- You can search for images available on the Docker Hub by using the
search
option. Let’s search for “nginx”:$ docker search nginx
- Let’s download the
nginx
image to our machine without running it. Use the pull
option:$ docker pull nginx
- To see all images downloaded to your machine run:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 76c69feac34e 2 weeks ago 142MB
hello-world latest feb5d9fea6a5 13 months ago 13.3kB
- Unlike the
hello-world
container that ran once and exited, some containers run endlessly and their logs are written to the terminal. We can run these containers in the background or in detached mode with the -d
flag. Let’s run nginx
in the background$ docker run -d nginx
- Run
$ docker ps
to view active containers.$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f2e1f7608e47 nginx "/docker-entrypoint.…" 6 seconds ago Up 5 seconds 80/tcp sweet_clarke
The hello-world
application we ran earlier does not run continuously. It simply prints the message and exits.
- To view all containers (active and inactive), run
$ docker ps -a
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f2e1f7608e47 nginx "/docker-entrypoint.…" 33 seconds ago Up 32 seconds 80/tcp sweet_clarke
6e4d35a225ec hello-world "/hello" About a minute ago Exited (0) About a minute ago gallant_edison
You can interact with a container by using the container ID or the unique container name. For example, the container ID and container name of the container created from the nginx
image is f2e1f7608e47
and sweet_clarke
respectively.
- To enter the shell of a running container and execute commands, run
$ docker exec -it <container-name> bash
:$ docker exec -it sweet_clarke bash
root@f2e1f7608e47:/#
- While in the container, view nginx default index file:
root@f2e1f7608e47:/# cat /usr/share/nginx/html/index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Navigate the shell to see its capabilities. Administrators can make changes to the Docker applications they deploy by entering the container shell.
- When done exploring the container shell, run
$ exit
to close the shell and return to your host machine.
- To stop a container, run
$ docker stop <cotainer-name>
. Let’s stop the sweet_clarke
container:$ docker stop sweet_clarke
- View a list of all containers again. The status should show that it exited.
$ docker ps -a
- You can restart a stopped container by running:
$ docker start <cotainer-name>
.
Note that $ docker run <image-name>
creates a new container, therefore it’s not necessary to use docker run
when the container is already created from the image, unless your intention is to create multiple containers.
- You can remove a container with
$ docker rm <container-name>
.$ docker rm sweet_clarke
$ docker rm 6e4d35a225ec # container ID for hello-world
- Let’s also remove the
hello-world
and the nginx
images from our local machine.
First, view available images:$ docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 13 months ago 13.3kB
nginx latest f2e1f7608e47 2 years ago 100.2MB
Use the rmi
option to remove the images:$ docker rmi hello-world nginx
Untagged: hello-world:latest
Untagged: hello-world@sha256:faa03e786c97f07ef34423fccceeec2398ec8a5759259f94d99078f264e9d7af
Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359
Untagged: nginx:latest
Untagged: nginx@sha256:943c25b4b66b332184d5ba6bb18234273551593016c0e0ae906bab111548239f
Deleted: sha256:76c69feac34e85768b284f84416c3546b240e8cb4f68acbbe5ad261a8b36f39f
Deleted: sha256:8b811a30cb94c227fb2ae61a2a1ec1e93381dbef06f9ea6b5c06df4f27651fed
Deleted: sha256:470cc6f5d954afeb2695504eff0eda9f8da0e1b3b8bde30e74d9c48dbcb99906
Deleted: sha256:1c277c746c47ba650267f58cc6e1ea430ce726065c79691ce04235d90209caff
Deleted: sha256:953e18d40076df9e65564241b34cfba85ab1a6c0634c67e92d6a1f633c5b97d9
Deleted: sha256:f0a780360f49b2b6afc28882ed2399799e6615862e7dc64451fb3688a33fe712
Deleted: sha256:a12586ed027fafddcddcc63b31671f406c25e43342479fc92a330e7e30d65f2e
Task 3: Create a custom Docker image
Let’s create a static page website and run it on a Python web server.
Note: There are more effective ways to set up a web server. We use the methods in this lab simply to explore the process of creating a Docker image.
- Create a directory for the Docker application and navigate to it:
$ mkdir ~/pythonweb && cd ~/pythonweb
- Create a
Dockerfile
in the directory:$ touch Dockerfile
- Add the following lines to
Dockerfile
:
- To pull the docker image for Ubuntu 22.04:
- To update the sources list and install python3:
RUN apt-get update && apt-get install -y python3 --no-install-recommends
The python web server listens on port 8000
by default. Network access to container services is disabled by default.
- To expose port
8000
to devices outside the container
- To start the python web server when the contaner is executed:
ENTRYPOINT ["python3", "-m", "http.server"]
The Dockerfile
should look like this:
FROM ubuntu:jammy
RUN apt-get update && apt-get install -y python3 --no-install-recommends
EXPOSE 8000
ENTRYPOINT ["python3", "-m", "http.server"]
RUN
is used to specify commands that should be run when building the image.
ENTRYPOINT
and CMD
define commands the container executes when it launches.
- Build the image:
docker build -t pythonweb:latest .
The .
at the end represents the location of the Dockerfile
. In this case it is in the CWD. Otherwise, we would have specified the directory where the Dockerfile
is located.
- Check your docker images and you should see something similar to the following:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pythonweb latest 60e7915677a4 About a minute ago 147MB
ubuntu jammy a8780b506fa4 10 days ago 77.8MB
- Run the image
pythonweb
and map port 80 on the host machine to port 8000 on the container. This will allow users interact with the web server in the container via the host machine.$ docker run -p 80:8000 -d pythonweb
- View the contents of the web server in your browser:
The Python web server works, but there is no index page, and it shows system files. Let’s proceed to create an index web page, add a working directory, and update the image with the new configuration.
- Create a directory to contain the web files.
$ mkdir ~/pythonweb/webfiles/
- Create
index.html
in ~/pythonweb/webfiles/index.html
:$ vi ~/pythonweb/webfiles/index.html
Add the following lines to the file:
<html>
<h1>SNA Lab</h1>
<p>SNA rocks :)</p>
</html>
- Modify the
Dockerfile
and add the following lines:
- To copy the contents of
webfiles
directory to the image:
COPY webfiles/ /usr/share/webfiles
- To change the container working directory to the
/usr/share/webfiles
directory
WORKDIR /usr/share/webfiles
The final Dockerfile
after the modification is shown below:
FROM ubuntu:jammy
RUN apt-get update && apt-get install -y python3 --no-install-recommends
COPY webfiles/ /usr/share/webfiles
WORKDIR /usr/share/webfiles
EXPOSE 8000
ENTRYPOINT ["python3", "-m", "http.server"]
- Check running containers, stop the container running
pythonweb
, and remove the container:$ docker ps -a
$ docker stop <container-name>
$ docker rm <container-name>
- Remove the
pythonweb
image.$ docker rmi pythonweb
- Build the image
docker build -t pythonweb:latest .
- Run the updated
pythonweb
image. This time, let’s specify a container name with the --name
flag. This is better than relying on the random names generated by Docker:$ docker run -p 80:8000 -d --name snaweb-container pythonweb
Run docker ps
to view the running container:$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ecfb00c4039f pythonweb "python3 -m http.ser…" 6 seconds ago Up 5 seconds 0.0.0.0:80->8000/tcp, :::80->8000/tcp snaweb-container
- Visit the page again and you should have results similar to the following:
Task 4: Docker inspect
and container logs
Docker inspect
is used to view low-level information on Docker objects.
- Inspect the
snaweb-container
container:$ docker inspect snaweb-container
The result is JSON data that contains some useful information if analyzed properly.
You can filter the results from inspect
without manually reading everything in the large volume of output it generates.
- Get the full container ID for
snaweb-container
:docker container inspect -f '{{.Id}}' snaweb-container
- View the containers main process:
$ docker container inspect -f '{{printf "%s " .Path}}{{range .Args}}{{printf "%s " .}}{{end}}' snaweb-container
- List all port bindings:
$ docker container inspect -f '{{range $target, $published := .NetworkSettings.Ports}}{{range $published}}{{printf "%s -> %s:%s\n" $target .HostIp .HostPort}}{{end}}{{end}}' snaweb-container
- View the network your container is connected to:
$ docker inspect -f "{{json .NetworkSettings.Networks }}" snaweb-container
The sample output shows that my container is connected to the bridge
network:{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"NetworkID":"ece6245e2975ad3a709dbf7d993a59454c703f09c68b892fd3922581ed967a8e","EndpointID":"40fd7af8985dba6f7f654bffef4df2de363db00b360f630ee19d2d78704df5a1","Gateway":"172.17.0.1","IPAddress":"172.17.0.2","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02","DriverOpts":null}}
- List the containers connected to a network along with their IP addresses:
Replace bridge
in the command with your network name.
$ docker network inspect -f '{{range .Containers}}{{printf "%s -> %s\n" .Name .IPv4Address}}{{end}}' bridge
Container logging helps developers keep track of patterns, troubleshoot issues, and fix bugs.
- View the container logs with
$ docker logs <container-name>
:$ docker logs snaweb-container
- Container logs are stored in the
/var/lib/docker/containers/ID/ID-json.log
.
- Use
inspect
to find the full log path for snaweb-container
:$ docker container inspect -f '{{.LogPath}}' snaweb-container
View the log file with tail:$ sudo tail /var/lib/docker/containers/ecfb00c4039fddb81a5185fd2287a908cefd140e8f1d832ab65a71759aac39ce/ecfb00c4039fddb81a5185fd2287a908cefd140e8f1d832ab65a71759aac39ce-json.log
{"log":"172.17.0.1 - - [13/Nov/2022 16:34:00] \"GET / HTTP/1.1\" 200 -\n","stream":"stderr","time":"2022-11-13T16:34:00.810777628Z"}
{"log":"172.17.0.1 - - [13/Nov/2022 16:48:49] \"GET / HTTP/1.1\" 304 -\n","stream":"stderr","time":"2022-11-13T16:48:49.9306387Z"}
{"log":"192.168.132.1 - - [13/Nov/2022 16:49:11] \"GET / HTTP/1.1\" 200 -\n","stream":"stderr","time":"2022-11-13T16:49:11.494390274Z"}
{"log":"192.168.132.1 - - [13/Nov/2022 16:49:11] code 404, message File not found\n","stream":"stderr","time":"2022-11-13T16:49:11.584257708Z"}
{"log":"192.168.132.1 - - [13/Nov/2022 16:49:11] \"GET /favicon.ico HTTP/1.1\" 404 -\n","stream":"stderr","time":"2022-11-13T16:49:11.584310308Z"}
Questions to answer
- Compare and contrast
ENTRYPOINT
and CMD
in Dockerfile. In what situation would you use each of them?
- List five security precautions you will take when building or deploying a Docker resource (image or container).
- Show a single line command that will remove all exited Docker containers. Do not use any text filtering editor. Show test results.
- Show how you can copy files to a running container without entering the container’s interactive shell.
- Create a dockerized web application running on nginx. The web index page
index.html
should be located on your host machine. The directory containing the index page should be mounted to the container and served from there.
This means that you should be able to modify the web index page on your host machine without interacting with the container.
Show all steps taken for the configuration including the test results.
- Setup rsyslog on your host machine as a central logging server. Create a Docker container and configure it to forward its log to your central logging server.
Show steps and test results.
Bonus
- Dockerize any open source application of your choice, and host it on Docker hub. Share link to the repository.
- Find and fix the problems in the following Dockerfile. There are some issues building the image and also running the container:
FROM alpine
RUN apt-get update && apt-get install -y python3 --no-install-recommends
RUN touch index.html
RUN echo "<html><h1>Testing web</h1></html>" >> index.html
CMD ["python", "-m", "http.server"]
Show all steps taken to fix it, and a working solution.
Lab 11: Docker
Task 1: Install Docker on Ubuntu
It is recommended that you do not run docker as the root user. If you currently cannot run
$ docker
with your non-root account, then take the following steps:$ groups
.Task 2: Pull images and run containers
hello-world
application https://hub.docker.com/_/hello-world. Docker will first search locally for thehello-world
image. It will then download the image from Docker Hub if it is not found locally.search
option. Let’s search for “nginx”:nginx
image to our machine without running it. Use thepull
option:hello-world
container that ran once and exited, some containers run endlessly and their logs are written to the terminal. We can run these containers in the background or in detached mode with the-d
flag. Let’s runnginx
in the background$ docker ps
to view active containers.$ docker ps -a
$ docker exec -it <container-name> bash
:$ exit
to close the shell and return to your host machine.$ docker stop <cotainer-name>
. Let’s stop thesweet_clarke
container:$ docker start <cotainer-name>
.$ docker rm <container-name>
.hello-world
and thenginx
images from our local machine.First, view available images: Use the
rmi
option to remove the images:Task 3: Create a custom Docker image
Let’s create a static page website and run it on a Python web server.
Dockerfile
in the directory:Dockerfile
:8000
to devices outside the containerDockerfile
should look like this:pythonweb
and map port 80 on the host machine to port 8000 on the container. This will allow users interact with the web server in the container via the host machine.The Python web server works, but there is no index page, and it shows system files. Let’s proceed to create an index web page, add a working directory, and update the image with the new configuration.
index.html
in~/pythonweb/webfiles/index.html
: Add the following lines to the file:Dockerfile
and add the following lines:webfiles
directory to the image:/usr/share/webfiles
directoryDockerfile
after the modification is shown below:pythonweb
, and remove the container:pythonweb
image.pythonweb
image. This time, let’s specify a container name with the--name
flag. This is better than relying on the random names generated by Docker: Rundocker ps
to view the running container:Task 4: Docker
inspect
and container logsDocker
inspect
is used to view low-level information on Docker objects.snaweb-container
container: The result is JSON data that contains some useful information if analyzed properly.You can filter the results from
inspect
without manually reading everything in the large volume of output it generates.snaweb-container
:bridge
network:Container logging helps developers keep track of patterns, troubleshoot issues, and fix bugs.
$ docker logs <container-name>
:/var/lib/docker/containers/ID/ID-json.log
.inspect
to find the full log path forsnaweb-container
: View the log file with tail:Questions to answer
ENTRYPOINT
andCMD
in Dockerfile. In what situation would you use each of them?index.html
should be located on your host machine. The directory containing the index page should be mounted to the container and served from there.Bonus