Getting Started with Docker Run

Posted on February 27, 2019 by KeithThompsonKeithThompson

Docker is a technology that quickly went from inception to being a backbone technology for many industries and companies. This rapid growth has meant almost everyone working in technology from development to DevOps needs to have a basic understanding of how to use Docker. The first step for anyone who wants to learn Docker should be to learn how to use docker run. This post will teach you the ins-and-outs of getting started with Docker using docker run.

The Docker Hello World

As when starting with almost any technology topic, we’re going to begin by running a “hello, world” program. This is such a common thing to do that Docker (the company) maintains a “hello-world” image. Let’s run our hello world and break down exactly what is happening:

Note: Make sure you can connect to Docker before running this command.

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

This is a pretty helpful message, partially explaining what happened. The main thing that isn’t explained in this message is that the container ran the executable that printed this output and then immediately stopped running. This is one of the fundamental ideas when working with containers: Each container only runs one process — and when the process stops, so does the container.

Breaking Down the docker run Command

In our hello world, we used the docker run command with a single positional argument (hello-world) that represents the name of the image we would like to use to create the container. This is the base for all docker run commands, and we’ll be looking at the main options we can pass to this command to make it more useful and achieve specific results.

Adding Interactivity

Containers are great for trying out new technologies without needing to install things to your local workstation. If you wanted to learn Python, but you don’t want to mess with your local Python installation, you could run a Python container in interactive mode, and it would function similar to what you would experience on a Linux server. Let’s create a container that has Python 3.7 pre-installed so we can take it for a spin. If we take a look at all of the available options for docker run (via docker run --help), we’ll see there is a -i option, so let’s use that and see what happens. Since we want a specific version of Python, we’ll add the 3.7 tag to our image name using a colon (:):

$ docker run -i --name python37 python:3.7
Unable to find image 'python:3.7' locally
3.7: Pulling from library/python
741437d97401: Pull complete
34d8874714d7: Pull complete
0a108aa26679: Pull complete
7f0334c36886: Pull complete
65c95cb8b3be: Pull complete
9107d7193263: Pull complete
dd6f212ec984: Pull complete
43288b101abf: Pull complete
f68aede0db03: Pull complete
Digest: sha256:fb877e7ea5c40de100534c6d6f515c486247bfd899cb2767f2de0e876a8127fa
Status: Downloaded newer image for python:3.7

We’re once again left with a prompt that appears to be hanging, but we’re actually in a Python REPL (Read, Evaluate, Print, Loop), so we can type Python code in and it will be run. This isn’t quite what we want, so let’s hit Ctrl-D to exit from this prompt and try again. This time, we’ll also add the -t flag to create a pseudo-TTY so we can see the prompt we’ve connected to.

We did name this container so it’s not randomly generated and it’s easy for us to remove. Let’s remove the python37 container before creating it again with the TTY:

$ docker rm python37
python37
$ docker run -it --name python37 python:3.7
Python 3.7.2 (default, Feb  6 2019, 12:04:03)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Since the default command for the python:3.7 image the python3 binary, we’re dropped into a REPL and this is exactly what we’d expect to see. It’s incredibly common to want to attach to a container to see what is going on within, and the -i and -t flags are necessary for that (commonly written as -it).

Backgrounding a Container

Running a container and having it immediately terminate is occasionally useful. We can use this approach to run a specific piece of work in a sandbox environment, but more often than not, we want to run a container for an extended period of time. When we want to run a server within a container, we need to consider two primary things:

  1. The process must be running in the foreground of the container. This means the image won’t daemonize the process.
  2. We need to daemonize the container using the -d flag.

Let’s use the nginx image from Docker Hub and run it in the background.

$ docker run -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
6ae821421a7d: Pull complete
da4474e5966c: Pull complete
eb2aec2b9c9f: Pull complete
Digest: sha256:dd2d0ac3fff2f007d99e033b64854be0941e19a2ad51f174d9240dda20d9f534
Status: Downloaded newer image for nginx:latest
14850ff85876c48ef2260f97313dadbd5ca32766ae3352416db60e82f4e772f2
$

After the new image is downloaded, we’ll get dropped back into our standard prompt because we daemonized the container. We can see it is still running by using the docker ps command:

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
14850ff85876        nginx               "nginx -g 'daemon of…"   About a minute ago   Up About a minute   80/tcp              vigorous_mccarthy
$

This output lets us know a few things:

  1. Our container’s ID (14850ff85876 in this case)
  2. The image used to create the container
  3. The command that is running within the container
  4. Ports that are exposed within the container
  5. The name of the container, which we can use as an alternative to the ID if we want to interact with this container

If we had run our command without the -d, we would have had our prompt appear to hang, when in reality we would be seeing the stdout logging from the NGINX process running in the foreground. As you can imagine, we don’t usually want to run server containers in the foreground unless we’re using the container for some development purpose.

Exposing Ports

As it stands right now, we can’t actually access our NGINX container’s content from the Docker host. To achieve this, we’ll need to map ports from our Docker host to ports within our container. The docker run command makes this easy by providing the --publish or -p option. We can use this option multiple times if there are multiple ports we need access to (such as 80 and 443 for HTTP and HTTPS). Let’s remove our NGINX container and recreate it exposing the ports:

Note: your container name will be different than mine, find it by running docker ps.

$ docker stop vigorous_mccarthy
vigorous_mccarthy
$ docker run -d -p 8080:80 -p 4443:443 nginx
6753df583a62c8c71db68a83c64acb71832a25572df8cdc1c63f7ad80cfb465a
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                         NAMES
6753df583a62        nginx               "nginx -g 'daemon of…"   7 seconds ago       Up 6 seconds        0.0.0.0:8080->80/tcp, 0.0.0.0:4443->443/tcp   xenodochial_mccarthy

For each port we’d like to map back to the Docker host, we can use another -p option where we’re mapping HOST_PORT:CONTAINER_PORT. Now we’re able to get more use out of running servers inside of Docker containers.

Setting Environment Variables

Finally, the last common thing we’ll want to do when working with docker run is set an environment variable within the container. Environment variables are commonly used to configure the services running within the containers, and we once again have a simple flag to allow us to set these values: the --env or -e flag. Just like the -p flag, we can use this option multiple times to set multiple environment variables, but we’ll also be setting them using an equal sign = instead of a colon. If we wanted to run a PostgreSQL database, we could use the official postgres image, which allows us to specify some environment variables before creating a container to configure the initial database that will be created. Let’s create a linuxacademy database for the la_test user using environment variables. This command will combine a lot of what we’ve learned and we’ll separate it out onto multiple lines to make it a little easier to read:

$ docker run -d 
  --name postgres 
  -p 5432:5432 
  -e POSTGRES_USER=la_test 
  -e POSTGRES_PASSWORD=secret_password 
  -e POSTGRES_DB=linuxacademy 
  postgres
3359c68bf647b471252de6196a26a5132fb0492f14fcb6d17a3238553ad11d13
$

Now we have a database container running what we configured using environment variables. Using a PostgreSQL client, you could connect to the linuxacademy database using the Docker host’s IP address, the user of la_test,  and the password of secret_password. If you happen to have psql installed, it would look something like this (substitute your Docker host’s IP address for 192.168.99.100):

$ psql postgres://la_test:secret_password@192.168.99.100:5432/linuxacademy
psql (11.1, server 10.4 (Debian 10.4-2.pgdg90+1))
Type "help" for help.

linuxacademy=# q
$

Deepening Your Docker Understanding

I hope this post has provided you with a better understanding of how you can utilize the docker run command to create containers that do what you expect them to do, but this is just the beginning. If you’d like to learn even more about Docker, you should check out this free eBook or these hands-on courses below:

Good luck, and happy learning!

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *