In a previous article, we talked about Docker images but we could only use a small section to talk about Docker containers. Now, let’s go deeper.
Docker Containers
A Docker container is created from a Docker image. If a Docker image is a recipe, think of the Docker container as the dish that is prepared
A container image is a lightweight, stand-alone, executable package of a piece of software that includes everything needed to run it: code, runtime, system tools, system libraries, settings.
Containers isolate software from its surroundings, for example differences between development and staging environments and help reduce conflicts between teams running different software on the same infrastructure.
So to actually take advantage of a Docker image, we have to run it. When we run a Docker image, we create a Docker container.
Running a Docker Image
The basic way of running a Docker image is through the docker run
command.
Docker runs processes in isolated containers. A container is a process which runs on a host. The host may be local or remote. When an operator executes docker run, the container process that runs is isolated in that it has its own file system, its own networking, and its own isolated process tree separate from the host.
There is a full documentation that details all the options that you can add to the docker run
command. We will just run through the ones that we use regularly.
In this article, we will use the AdonisJs image we created in the previous article. The stephenafamo/adonisjs
image.
Specifying the Docker Image
The docker run
command needs at least one argument, and that is the image you want to run. For example.
docker run stephenafamo/adonisjs
We can also specify the tag of the image we want to run. Like this.
docker run stephenafamo/adonisjs:1.0.0
To view the running containers, we use the docker ps
command. If we want to see all containers, we use;
docker ps -a
NOTE : To see all information regarding a container, we inspect it by running
docker inspect container_name
Naming containers
When we list the running Docker containers, the last column is the name of the container. By default, Docker assigns a random string as the name of the container.
We can specify the name of the container using the --name
flag. Like this;
docker run --name adonis stephenafamo/adonisjs:1.0.0
Now when we run docker ps
we can see our container properly named.
Foreground or background
You would have noticed by now that when we ran the adonis container, it began executing in the terminal, you probably had to stop the process, or open a new terminal in order to check the containers. This is because the container was running in the foreground.
We can make our container to run in the background in a detached mode by adding the -d
or --detach
flag. Like this;
docker run --name adonis -d stephenafamo/adonisjs:1.0.0
If you do not need a container to persist, that is if you want Docker to automatically clean up the container and remove the file system when the container exits, you should run the container with the --rm
flag.
docker run --name adonis --rm stephenafamo/adonisjs:1.0.0
Constraining resources
When you are running multiple Docker containers, you may decide to limit the amount of resources available to a specific container. By default, it will attempt to use the entire resources of the host. The full list is available here, let’s just look at the ones used frequently.
Memory
We can specify the amount of available memory by using the -m
or --memory
flag.
docker run --name adonis -d -m 4g stephenafamo/adonisjs:1.0.0
Memory and Swap
We can specify the amount of available memory with swap by using the --memory-swap
flag.
docker run --name adonis -d -m 4g --memory-swap 8g stephenafamo/adonisjs:1.0.0
NOTE: You should always set the Memory limit when using Memoryswap limit
CPUS
We can limit the number of cpus a container can use with the --cpus
flag.
docker run --name adonis -d --cpus 2 stephenafamo/adonisjs:1.0.0
CPU Shares
Instead of manually keeping track of the number of cpus each container is using, we can use relative weights to different containers and allow Docker to limit the processing power appropriately.
We do this using the -c
or --cpu-shares
flag.
docker run --name adonis -d --cpu-shares 2 stephenafamo/adonisjs:1.0.0
Ports
In the creation of a Docker image, some ports are usually exposed. This is done with the EXPOSE
directive in Dockerfile.
We can expose additional ports using the --expose
flag of the docker run
command.
docker run --name adonis -d \
--expose 80 --expose 443 \
stephenafamo/adonisjs:1.0.0
We can then bind some ports on our host machine to ports in our docker container by “publishing” these ports.
If we add the -P
(upper case) or the --publish-all
flag to our docker run
command, all the exposed ports will be published and assigned random ports on the host machine from a range of ports.
docker run --name adonis -d \
--expose 80 --expose 443 \
-P \
stephenafamo/adonisjs:1.0.0
We can specify which ports we want to publish using the p
(lower case) or the --publish
flag.
docker run --name adonis -d \
--expose 80 --expose 443 -p 80 \
stephenafamo/adonisjs:1.0.0
We can further specify which port on the host should be mapped like this;
docker run --name adonis -d \
--expose 80 --expose 443 \
-p 32277:80 -p 23458:443 \
stephenafamo/adonisjs:1.0.0
NOTE : If we specify a port in the docker container using the -p
flag, we do not need to expose it because it will be automatically exposed if it was not previously exposed. So we can do something like this:
docker run --name adonis -d \
-p 32277:80 -p 23458:443 \
stephenafamo/adonisjs:1.0.0
Environmental Variables
A lot of Docker images make use of environmental variables to determine how the Docker container will behave when it is run. Also, we may need to specify evironmental variables for the application we intend to run within the container.
For example, the stephenafamo/adonisjs:1.0.0
images uses an environmental variable to add any flags to the adonis install
command which it runs when the container is started.
We can easily set the environmental variable of the container when it is created using the -e
or --env
flag.
docker run --name adonis \
-d \
-e "adonisFlags=--slim --yarn" \
stephenafamo/adonisjs:1.0.0
For the sake of security or collaboration, we may perfer to keep the environmental variables in a separate place. In that case we can use the --env-file
flag to specify a file which contains the environmental variables.
docker run --name adonis \
-d \
--env-file /path/to/env/file \
stephenafamo/adonisjs:1.0.0
NOTE : We can use the
-e
and--env-file
flags multiple times to specify multiple enviromental variables or env files.Volumes
Docker volumes are invaluable when we need to persist data. Since all the data in a Docker container is destroyed whenever the container is removed, we need to use volumes in a lot of situations.
We will talk about Docker volumes in detail later. We can create volumes, attach them to one or multiple containers, and the data in the volume will persist even if the container is removed.
For the sake of simplicity in this article, we will simply mount a directory in our Docker container.
Mounting a directory is a good way to persist data because the files in that directory will remain even if the container is deleted.
We can mount a directory using the -v
or --volume
flag.
docker run --name adonis \
-d \
-v /path/to/mount/directory:/path/to/destination/in/container \
stephenafamo/adonisjs:1.0.0
Restart
Several reasons can cause our Docker container to exit. We can set what happens when the container exits using the --restart
flag. The options are “no”, “always”, “unless-stopped” or “on-failure”.
docker run --name adonis \
-d \
--restart always \
stephenafamo/adonisjs:1.0.0
Networking
We can modify the network setting of a container using various flags. This is especially useful when you need to customise how your containers talk to each other. The right networking configuration allows us to do some really powerful things such as this. The full list of networking flags is available here.
Networking in Docker is another area we will treat in more detail in a later article.
Conclusion
We have gone through how we can run a Docker container and some of the many ways we can customize the behaviour of our container.
I hope that this makes you even more comfortable with using Docker. I believe that it is a wonderful tool.
As always, I appreciate comments, suggestions and corrections.
The post Understanding Docker Containers appeared first on Stephen AfamO's Blog.
Top comments (0)