How to Run Docker Containers

This entry is part 3 of 3 in the series Easy Containerization with Docker

Now let’s start running containers on our build system and, once the container is running, explore our container in different ways, then start and stop the containers as needed.

Launch an existing container

I’ve shown you how to find a container image from a Docker registry. Now you want to try to run an application with one of those container images you just downloaded.

Using the docker command with the run action you launch a command within a container. The container might have a command that’s already built into it and can just run when the container is launched, or you might want to identify some other command to run within that container that’s passed to the docker run on the command-line. The syntax of the docker run command is:

docker run [OPTS] image [COMMAND] [ARGS]

where image is the image ID or name of the image available locally or by pulling from a repository. This syntax simply runs the command identified inside the container along with any settings already in the container. You have the option of passing a command to the container so that command, instead of the default command within the container, is run. You can also pass options to add or change settings within the container.

Once the container launches the process inside the container only sees its own file system, its own process table and its own network interfaces. You need to add special arguments to the container to access directories by doing something called bind mounting of volume from the local system or making internal ports available to the local host, and you do that by doing what’s called mapping ports to other host ports. To see the options that are available to the docker run command type:

man docker-run

Currently this and other Docker subcommands specific man pages are not in the Ubuntu docker packaging but they are in Fedora and Red Hat Enterprise Linux. This slight shows some useful options that you can add when running the docker run command:

  • -v let’s you mount a directory from the host to it in the container. This is useful if you want to let a user of your container pass her own data to be processed within the container. For example, someone might mount a host directory that contains HTML pages in a place where the web server running inside the container can consume that data.
  • -t opens a pseudo terminal in a container
  • -i makes the container session interactive. You might use this option along with -t to use a container to run a bash shell interactively. This can be useful if you just want to poke around and see what’s inside the container, to see what files and other features that contains.
  • -m limits the amount of memory a container can use in bytes, kilobytes, megabytes or gigabytes.
  • -c increase the priority the container has to get access to the CPU.
  • –name=’name‘  set a specific container name.
  • -e  let’s you pass an environment variable that the process running in the container can see.
  • –entrypoint – overwrites the entry point set within the container when the image is made. The entry point instruction inside the container was made to define the intention of the container to run like you would executing a particular command identified by the particular entry point, so using this option essentially let’s you run a different command inside the container, other than the one that was designed by the intention of the container.
  • -h let you set the containers hostname.
  • -p  and -P are used to expose ports that are accessible from within the container to the host, so someone can reach services inside the container from ports outside of the host. The -p option can map a specific container port to a specific host port. The -P true publishes all ports from the container to ports on the host. How ports are exposed can be further defined using the -p options
  • -rm is very useful if you were running container that you don’t want to keep around to restart later or to save as an image. With this option the container is removed after exits. Using this option on temporary containers can save a lot of disk space.

Now that you’ve seen some options to use with docker run, I’ll show you a few examples of docker run command lines. The simple HTTP server Python module, that is included inside the default Fedora image is a simple web server that you can start within that container. It also gives us a way of illustrating docker run options for mounting volumes from the host, exposing ports in the container to the host and setting the container name and then running the command.

I’m going to create a directory to put data in for my web-server: mkdir /var/web_data; Next we’re going to do is create a little bit of data to go into that directory:

echo “The Web server is operational” > /var/web_data/tests.txt

Now I’m going to do a docker run command:

docker run -d -p 9090:9099 –name=”my_webserver” -w /opt -v /var/web_data:/opt fedora:latest /bin/python -m SimpleHTTPServer 9099

This says: “run this particular container in detach mode, use port number 9099 inside the container and map that to port 9090 on the host, set the name of this container to “my_webserver“, use the current working directory as /opt inside your container when the particular web server runs (-w /opt),  mount /var/web_data directory inside the container on the /opt directory, use the fedora:latest image, then run the actual python script that starts a simple HTTP server on the port 9099″.

This command creates the container and runs it. Now for me to check to see that it’s running I could actually run the curl command:

curl localhost:9090/test.text

If the curl command completes properly you should see the message “The Web server is operational“.

Journal messages that are generated within a container stay within that container by default. In Fedora 20 log messages generated by the system are directed to the systemd journal through the dev/log socket. By bind mounting the host dev/log socket to the containers dev/log socket, messages from the container will appear in the host journal. Here’s an example of a docker run command that mounts the dev/log socket from the host in the container.

docker run –rm -v /dev/log:/dev/log fedora:latest logger “Sending container message to the host”

Here we’re running the Fedora latest image and then we’re running the logger message. Now logger will send a message to the systemd journal that says “Sending container message to the host”. Now we can actually look in the journal on the host and look for the message we have sent:

journalctl | grep “Sending container message”

If I want to interact with the application within a container I can run it with the -i option, which stands for interactive, to keep standard input open and the –ti option to attach a pseudo terminal device to the container. This allows me to interact with any command that I run with the docker run command.

In this example I want a system that doesn’t have any Linux man pages on it, so I decide to try to read a man page that’s inside a Fedora container:

docker run –rm -i -t fedora man ssh

This command runs the man command and tries to print the SSH man page. The –rm means I don’t want to keep this container around so we remove it as soon as it’s done.

Now let’s see what is going on inside of a running container.

Check out a running container

Once a container is running, there are many ways to investigate how that container is configured and what it’s doing. The docker inspect command shows lots of information that is available about the running container. With no other options it will output all the information that has the standard output. In other words it will show on your screen. The output is in JSON file format. By adding the –format option you can query for specific piece of information and potentially use that information as input to other commands. For example:

  • the Pid option that lets you see the process ID of the running container.
  • the IPAddress option that lets you see the IP address assigned to the container by Docker.
  • the Binds options that lets you view which directories have been bind-mounted to your running container, that gives you access to those files from the host on your local container.

While docker inspect lets you look at information about the command from the outside, the nsenter command lets you look inside the running container. After you determine the process ID of the running container you can connect to it with nsenter. On the nsenter command-line you identify the namespaces you want to see and the command opens a shell to investigate those namespaces. Besides a process ID of a container, which you indicate with -t pid, you can add other options to view namespaces as well:

  • -m lets you see what is mounted within the container
  • -i lets you see the interprocess communications namespaces
  • -p lets you see the process namespaces
  • -n lets you can see information about the containers network interfaces
  • -u lets you to see the UTS namespace.

Now let’s try to use that in practice to determine the container ID or name of the running container and use inspect and nsenter to investigate it:

  1. We start with the docker ps command to see what running containers we have. And we have that simple HTTP container that I ran earlier so we can investigate that one.
  2. We type the docker inspect command and pass the container ID to it. And that will show us all the data associated with this container: the image name, how much memory is being used, any volumes etc.
  3. Now if you just want specific piece of information, you can use the format option. For example, docker inspect –format='{{.NetworkSetting.IPAddress}}’ my_webserver, that will return the specific IP address assigned to the container by Docker, it’s created on a little private network inside the host. Now having the IP address we could see if the container is alive using ping and what ports are available for this particular container with nmap. Other things we can inspect about our system or things like other network settings: for example we can use docker inspect –format='{{.NetworkSetting.Bridge}}’ my_webserver to see the network bridge, docker inspect –format='{{.HostConfig.Binds}}’ my_webserver to see what directories from the whole system are bound to the container system.
  4. To use the nsenter command I need first to get the process ID of my container. We can do that with: docker inspect –format='{{.State.Pid}}’ my_webserver.  Now to now we want to do is go inside this process we run: nsenter -m -u -in -i -p -t Pid, where Pid is the process ID returned by the inspect command run earlier. This will open up a shell inside of that particular container.
  5. Now let’s look around inside the container with ls /. You can see that the container has its own filesystem.  hostname show us that it also has a host name, this will be the name of the container if it is not explicitly set first when we launch the container. Next we can look with the mount command to show up any filesystems from the localhost that are mounted inside the container.

Once you’re done looking inside the container you can just tape exit and you’re back to your host system and you can start running other containers or looking inside of other containers as well.

At this point you should have a pretty good feel for the kind of things that are inside a container.

Manipulate containers (run, stop and start)

Just as you can see and change the state of services on a running Linux system you can likewise change the states of your containers.

To see what containers are currently running on your system you can use the docker ps command.  docker ps -l we’ll allow you to see the containers that are currently stopped but not yet removed.

To stop a container that is currently running type docker stop. If a container has been stopped or is simply exited without being deleted, you can start that container again by typing docker start.

Instead of docker stop you can also use docker restart that will stop the container and then start it again.

Instead of using docker stop you can use the docker kill command to send a kill signal to the container. You could also use docker kill to send other signals to a container.

Once you are done with a container you can remove it once it has been stopped with the docker rm command.

Now let’s try all these. First let’s run a container:

docker run –name=”my_man” -i -t fedora man ssh

This will run run ssh inside the container. Type quit. Now if you do docker ps you don’t see it here on the list of running containers, however if I type docker ps -l I see this is the list of containers that are not running. If I want to see all containers I can do docker ps -a. To see only the names of all the containers type docker ps -a -q. 

Now the start and stop options you can use by giving it either the name of a container or by giving the ID of a container. To stop my_webserver container just type docker stop my_webserver. To check if it is stopped just type docker ps and my_webserver should not be in the list of running containers. And to start the container: docker start my_webserver. 

And also because we had one container running before and stopped in the background we can rerun that my_man container, man page reader for the ssh command:

docker start -i -a my_man

Notice that I have to add some extra options to start. I have to say: run this interactively and attach to it.

If I’m done with my containers and want to remove them I can use the docker rm command:

docker rm my_man

This will succeed because it already exited. However the web server did not exited so when I try to remove it with docker rm my_webserver, it tells me there is an error so I have to stop the container before it lets me remove it. So just run the docker stop command and once it’s done stopping you should be able to run the rm command and that will remove the container from your system.


Post A Comment