Lab 1 - Introduction To Containers
Lab 1 - Introduction To Containers
Table of Contents
Lab: Module 1 - Introduction to Containers .................................................................................................................................................2
Duration: 90 minutes ............................................................................................................................................................................................. 2
Prerequisite ............................................................................................................................................................................................................ 2
Exercise 1: Running Your First Container ............................................................................................................................................................... 2
Exercise 2: Working with Docker Command Line Interface (CLI)........................................................................................................................ 11
Exercise 3: Building Custom Container Images with Dockerfile: NodeJS, Nginx, and ASP.NET Core 2.x ............................................................ 16
Exercise 4: Interaction with a Running Container............................................................................................................................................... 28
Exercise 5: Tagging .............................................................................................................................................................................................. 34
Prerequisite
You must have labs virtual machines running on the Learn on Demand (LOD) website and register an account on the LOD website.
Tasks
1. Running WordPress Blog Engine Container
1. Go to the Lab on Demand (LOD) Linux Ubuntu Virtual Machine.
2. Click Start on the VM if you see this screen. Click on the Machines tab on the right to get the password for the VM. You can
click on the Machines tab at any time if you need to reference the password for the VM again.
3. To open a command line prompt, right click on the desktop and choose open terminal.
4. Run “sudo -i” to ensure all commands have elevated privileges. You will be prompted for your password, type it in,
then press enter. You should see root@super in your command line as the user.
5. Type “docker pull tutum/wordpress”. This will tell Docker client to connect to public Docker Registry and download
the latest version of the WordPress container image published by tutum (hence the format tutum/wordpress). The
container image has been pre-downloaded for you on the VM to save you a few minutes, but you will see each layer that
is cached show the text ‘Already exists’.
6. Run the command “docker images” and notice “tutum/wordpress” container image is now available locally for you to
use.
7. That’s it! You can now run the entire WordPress in a container. To do that run the command
“docker run -d -p 80:80 tutum/wordpress”. Pay close attention to the dash “-” symbol in front of “-p” and “-d” in
the command.
11. Let’s launch two more containers based on “tutum/wordpress” image. Execute following commands (one line at a time)
12. Run ‘docker ps’ to see all 3 running containers and their port numbers:
13. Now open a new browser window and navigate to URL (using DNS or IP as before) but with port “8080” append to it. You
can also try port “9090”. Notice that you now have three WordPress blog instances running inside separate containers
launched within few seconds. Contrast this to instead creating and running WordPress on virtual machine, which could
take significantly more time.
14. If you want to run a container with a name, you can specify the parameter like this: ‘docker run --name mycontainer1
-d -p 8081:80 tutum/wordpress’. Run this on port 8081 so that it does not conflict with one of the previously running
containers.
15. And, now if you run ‘docker ps’, you will see that the container has the name you assigned it using the --name parameter.
Tasks
1. Stopping Single Container
1. First list all the containers currently running by executing “docker ps” command. You should see list of all running
containers. Notice, the list contains multiple containers based on WordPress image that you run in previous exercise.
2. You can stop a running container by using “docker stop <<CONTAINER-ID>>” command. Where CONTAINER-ID is the
identifier of a running container. *Note: You can just use the first couple characters to identity the container ID, such
as “c27” for the screenshot below.
3. Now run the “docker ps” command and notice the listing show one less container running.
4. If you want see the Container ID of the stopped container, and you forgot the Container ID, you can run ‘docker ps -a’ to
see all containers, even ones that are stopped/exited.
2. Restart a Container
1. In previous task you issued a Docker command to stop a running container. You can also issue command to start the
container which was stopped. All you need is a container ID (same container ID you used earlier to stop a container in
previous section), you can also get this using ‘docker ps -a’.
2. To start a container run “docker start <<CONTAINER-ID>>”. Replace CONTAINER-ID with container identifier you use
in previous section to stop the container.
3. To make sure that container has started successfully run “docker ps” command. Notice that WordPress container is now
started.
3. Removing a Container
1. Stopping a container does not remove it and that’s the reason why you were able to start it again in the previous task.
To delete/remove a container and free the resources you need to issue a different command. Please note that this
command does not remove the underlying image but rather the specific container that was based on the image. To remove
the image and reclaim its resources, like disk space, you’ll will need to issue a different command which is covered under
the later section “Removing Container Image”.
2. To remove a container, run “docker rm –f <<CONTAINER-ID>>” command. Replace the CONTAINER-ID with the
container identifier you used in previous section. If you don’t have it handy, simply run “docker ps” and copy the container
ID from the listing.
The –f switch is used to force the remove operation. It’s needed if you are trying to remove a container that is not already
stopped.
4. Stopping All Containers
1. At times you may want to stop all of the running containers and avoid issuing command to stop one container at a time.
Run “docker stop $(docker ps -aq)” command to stop all running containers. Basically, you are issuing two
commands: First the docker ps with relevant switches to capture list of container IDs and then passing list of IDs to docker
stop command.
2. To remove a container image, you’ll need its IMAGE ID. Run command “docker images”. Note down the IMAGE ID
corresponding to the “tutum/wordpress” image that you will remove in next step.
3. Run the command “docker rmi <<IMAGE ID>> -f”. Replace the IMAGE ID with the image identifier you captured in
previous step. Notice the command to remove a container is “docker rm” and to remove an image is “docker rmi”, with an
‘i’ for the image. Don’t confuse these two commands! The -f is to force the removal, you cannot remove an image associated
with a stopped container unless you use the force parameter.
4. Now, run the command “docker images”. Notice that “tutum/wordpress” image is no longer available.
Exercise 3: Building Custom Container Images with Dockerfile: NodeJS, Nginx, and
ASP.NET Core 2.x
Dockerfile is essentially a plain text file with Docker commands in it to create a new image. You can think of it as a configuration file with
set of instructions needed to assemble a new image. In this exercise you will learn common commands that goes into Dockerfile by
creating custom images based on common technologies like NGINX and Node.JS.
Tasks
1. Building and Running Node.JS Application as Container
1. In this task you will create a new image based on the Node.js base image. You will start with a Dockerfile with
instructions to copy the files needed to host a custom Node.js application, install necessary software inside the image and
expose ports to allow the traffic. Later, you will learn how to build the image using Dockerfile and finally will run and test
it out.
The relevant files related to a node.js application along with the Dockerfile are available inside the directory
“labs/module1/nodejs”. You can get to that directory by using the “cd” command to navigate.
2. On the command prompt type “ls” and press Enter. Notice the available files include “server.js”, “package.json” and
“Dockerfile”.
3. Let’s examine the Dockerfile by typing the command “nano Dockerfile” and press Enter (the file is case sensitive, so
make sure the D in Dockerfile is capitalized). You can use any other text editor (for example vi etc. but instructions are
provided for nano text editor). Notice the structure of Dockerfile.
4. Move your cursor using the arrow keys to the line starting with “MAINTAINER Razi Rais” and change the text from that
to the following: LABEL maintainer="[email protected]”. Once finish making changes press “CTRL + X” and then
press “Y” when asked for confirmation to retain your changes. Finally, you will be asked for file name to write. For that
press Enter (without changing the name of the file). This will close the nano text editor.
5. You are now ready to build a new image based on the Dockerfile you just modified.
7. When it is complete, you will see a couple of npm warnings, these are expected.
8. Run the command “docker images” and notice the new container image appears with the name “mynodejs”. Also notice
the presence of parent image “node” that was also pulled from Docker Hub during the build operation.
9. Finally, lets create and run a new container based on “mynodejs” image. Run command “docker run -d -p
8080:8080 mynodejs”. (The “-d” parameter will run the container in the background, whereas the “-p” parameter
publishes the containers ports to the host). Here are binding the port of the container (port number on right-side of
colon) to the port of the host machine (port number on the left-side of the colon.)
10. To test the “mynodejs” application, go back to your Firefox browser and go to localhost:8080.
The relevant files including static html file “index.html” along with the Dockerfile are available inside the directory
“labs/module1/nginx”.
2. Type “ls” and press Enter. Notice the available files include “server.js” and “Dockerfile”.
3. Let’s examine the Dockerfile by typing the command “nano Dockerfile” and press Enter. You can use any other text
editor (for example, vi, etc.), but instructions are provided for nano text editor). Notice the structure of Dockerfile.
4. Move your cursor by using the arrow keys to the line starting with “MAINTAINER” and change the text from “Razi Rais” to
your name (or any other text of your liking). Once finish making changes press “CTRL + X” and then press “Y” when asked
for confirmation to retain your changes. Finally, you will be asked for file name to write. For that press Enter (without
changing the name of the file). This will close the nano text editor.
5. You are now ready to build a new container image based on the Dockerfile you just modified.
Run the command “docker build -t mynginx .”
Notice how the build command is reading instructions from the Docker file starting from the top and executing them one at a
time. The image will download much faster as this is a very small image.
6. If you want to see the layers of an image, you can do ‘docker history mynginx’ and see the one you just built. You can also
try running this command on other images you have on your VM too.
7. Run the command “docker images” and notice the new container image appears with the name “mynginx”. Also notice
the presence of parent image “nginx” that was pulled from Docker Hub during the build operation. Take a look at the sizes
of different images also—this will become important when you build your own custom images to reduce the size for both
security and performance.
8. Finally, create and run a new container based on “mynginx” image. Run command “docker run -d -p 80:80 mynginx”.
2. Normally you would need to run dotnet restore, build, and publish.. however, this is already included for you in the
Dockerfile.
3. Now that application is ready you will create a container image for it. You are provided with a Dockerfile. View the
content of Dockerfile by running a command “nano Dockerfile”. To exit the editor press “CTRL+X”. The Dockerfile
contents should match the screenshot below:
5. Launch the container running the app inside it by running the command
“docker run -d -p 8090:80 myaspcoreapp:2.0”
You are now running ASP.NET Core application inside the container listening at port 80 which is mapped to port 8090
on the host.
Tasks
1. Interaction with a Running Container
1. On the command line run “docker ps” to list all the currently running containers on your virtual machine.
Notice that multiple containers are running. To establish interactive session a with a running container you will need its
CONTAINER ID or NAME. Let’s establish an interactive session to a container based on “mynodejs” image. Please note
that your CONTAINER ID or NAME will probably be different. And, unless you specified a name, Docker came up with a
random adjective and noun and smushed them together to come up with its own clever name.
2. Run a command “docker exec -it <<CONTAINER ID OR NAME>> bash” on your mynodejs container. (docker exec is used
to run a command in a running container. The “it” parameter will invoke an interactive bash shell on the container.)
Notice that a new interactive session is now establish to a running container. Since “bash” is the program that was asked
to be executed you now have access to full bash shell inside the container.
3. You can run a command “ls” to view the listing of files and directories. Notice it has all the files copied by Dockerfile
command in previous section.
NOTE: For more information regarding running commands inside docker container please visit:
https://docs.docker.com/engine/reference/commandline/exec
2. Making Changes to a Running Container
While you are interacting and running commands inside a running container, you may also want to make
changes/updates to it. Later, you may also create a brand-new image out of these changes. In this task you will make
changes to “mynodejs” container image, test them out and finally create a new image (without the need of Dockerfile).
Please note that this approach of creating container images is generally used to quickly test various changes, but the
recommend best practice way to create container images is to use Dockerfile.
First, you will make updates to “server.js” file. You should have active session already established from previous section
(if not then please follow the instructions from previous section to create an active session now).
Before we can edit the “server.js” file we need to install a text editor. Basically, to keep the size of container down to
absolute minimum, the “nodejs” container image does not have any extra software installed on the container. This is a
common theme when building images and is also recommend practice.
1. Before installing any software run the command “apt-get update” (Note the dash between “apt” and “-get”.)
3. After “nano” is installed successfully, run the command “nano server.js” to open “server.js” file for editing.
4. Use the arrow keys to go to the line starting with “res.Send(…” and update the text from “Hello Node.js!!!” to
“Hello Node.js AGAIN!!!”. Your final changes should look like following:
5. Once you finish making changes press “CTRL + X” and then press “Y” when asked for confirmation to retain changes.
Finally, you will be asked for file name to write. For that press enter (without changing the name of the file). This will close
the Nano text editor.
6. To save the updates and exit the interactive bash session, run the command “exit”
7. The running container needs to be stopped first and then started again to reflect the changes. Run the command “docker
stop <<CONTAINER ID>>” to stop the container. Run the command “docker start <<CONTAINER ID>>” to start the
container.
8. Finally, to test the update you have made to the container go to Firefox and localhost:8080. Notice the output “Hello
Node.js AGAIN!!!”. This verifies that changes to the container were persisted.
command to get the list of running containers or “docker ps –a” to get list of all the containers that are stopped and
capture the CONTAINER ID of the container you have updated in previous section.
2. Now, view the list of all container images by running the command “docker images” and notice the availability of new
image with name “mynodejsv2”
You now have a container image with the changes you made and tested earlier and is ready to be used.
3. To test the new image run a command “docker run -d -p 8081:8080 mynodejsv2”. This will create a new container
based on the image “mynodejsv2”.
4. Finally, to test the container, go to localhost:8081 in Firefox. Notice the text “Hello Node.js AGAIN!!!” is returned from
the node.js application. This attest that changes were committed properly to new image and hence available to any
container created based on the that image.
Exercise 5: Tagging
In this exercise you will learn the role of tagging in container and how to tag new and existing container images using Docker commands.
Tasks
1. Tagging Existing Container Image
In this task you will tag “mynodejs” container image with “v1”. Recall from last task that currently this image has “latest”
tag associated with it. You can simply run “docker images” to verify that. When working with container images it becomes
important to provide consistent versioning information.
Tagging provides you with the ability to tag container images properly at the time of building a new image using the
“docker build –t imagename:tag .” command and then refer to image (for example inside Dockerfile with FROM statement)
using a format “image-name:tag”.
Basically, if you don’t provide a tag, Docker assumes that you meant “latest” and use it as a default tag for the image. It is
not good practice to make images without tagging them. You’d think you could assume latest = most recent image
version always? Wrong. That’s not true at all. Latest is just the tag which is applied to an image by default which
does not have a tag. If you push a new image with a tag which is neither empty nor ‘latest’, :latest will not be affected or
created. Latest is also easily overwritten by default if you forget to tag something again in the future. Careful!!!
When you run “docker images” notice the “TAG” column and pay attention to the fact that for all the custom images created
in the lab so far have tag value of “latest”.
To understand importance of tagging take a look at container image created in previous section “mynodejsv2”. The
“v2” at the very end was appended to provide an indicator that this is the second version of the image “mynodejs”. The
challenge with this scheme is that there is no inherent connection between the “mynodejs” and “mynodejsv2”. With
tagging, the same container image will take the format “mynodejs:v2”. This way you are telling everyone that “v2” is
different but it does have relation with “mynodejs” container image.
Please note that tags are just strings. So, any string including “v1”, “1.0”, “1.1”, “1.0-beta”, and “banana” all qualify as a
valid tag.
However, you should always want to follow consistent nomenclature when using tagging to reflect versioning. This is
critical because when you start developing and deploying containers into production you may want to roll back to
previous versions in a consistent manner. Not having a well-defined scheme for tagging will make it very difficult
particularly when it comes to troubleshooting containers.
NOTE: A good example of various tagging scheme chosen by Microsoft with dotnet core framework is available at:
https://hub.docker.com/r/microsoft/dotnet/tags
Notice how “latest” and “v1” both exist. V1 is technically newer, and latest just signifies the image that did not have a
version/tag before and can feel misleading. Also, note the Image ID for both are identical. The image and its content /
layers are all cached on your machine. The Image ID is content addressable, so the full content of it is hashed through a
hashing algorithm and it spits out an ID. If the content of any two (or more) images are the same, then the Image ID
will be the same, and only one copy of the actual layers are on your machine and pointed to by many different image
names/tags.
2. Navigate to the directory “labs/module1/nginx” that contains the “niginx” files along with Dockerfile. You can use the
command “cd labs/module1/nginx”
3. Build a new image by running the command “docker build -t nginxsample:v1 .” In this case you’re creating a
new image based on Dockerfile (covered in earlier exercise on NGINX).
4. If you run a “docker images” command, it will list the new container image with tag “v1”