Docker is a program that allows the operating system to run processes in an isolated environment based on specially created images. And, while the technologies underlying Docker existed earlier, it was Docker that changed the way we now build project infrastructure, assemble and launch services.
(Some details were purposefully left out of the article to avoid unnecessary information).
Contents
Installation
To start with Docker, you must first install the Docker Engine. Download links for all popular platforms are provided at https://docs.docker.com/engine/install/. Select yours and install Docker.
There's one thing to remember when installing Docker on Mac and Linux. By default, Docker runs through a non-networked UNIX socket. For security reasons, the socket is blocked for users who are not members of the docker group. Although the installer automatically adds the current user to this group, Docker will not work straight away. In reality, changing the group by the user has no effect till the user logs in again. This is a feature of the core. Enter the id command to see which groups your user belongs to.
You can check if the installation was successful with the docker info command:
It provides quite a lot of information about Docker configurations as well as work statistics.
Launch
At this stage, the execution commands are given with no explanation. Later, more information on how they're formed and what include will be given.
Let's start with the easiest one:
This command will begin downloading the nginx image on the initial call, so you will have to wait a few moments. After the image is downloaded, bash will run, and you will find yourself in a container.
Go through the file system, and look at the /etc/nginx. As you can see, its content does not match what you have on your system. This file system was generated by the nginx image. Everything you do within it will not affect your main file system. With the exit command, you can get back to your native system.
Now, let's call the cat command, which is already in another container but is also launched from the nginx image:
The command is executed almost instantly since the image has already been uploaded. Unlike before, when a bash script and an interactive session were launched in the container, the cat /etc/nginx/nginx.conf command for the nginx image will display the contents of the specified file (taken from the running container's file system) and return control to where you were before. You won’t end up inside the container.
The last launch option will be:
This command does not restore control, because Nginx starts running. Enter localhost:8080 into your browser. You'll see that the Welcome to nginx! page has loaded!. If you return to the terminal where the container was launched, you'll see a log of requests to localhost:8080. You may stop nginx by pressing Ctrl + C.
Although each launch was different and resulted in various outcomes, the overall pattern of their work is consistent. Docker downloads the image automatically (the first argument after docker run) and, if necessary, runs the container with the specified command.
An image is an independent file system. For now, we use ready-made images, but we will eventually learn how to create them ourselves.
A container is a running process of the operating system in an isolated environment with an image file system.
A container is, once again, just a usual operating system process. The main difference is that Docker starts the process in an isolated environment due to the kernel’s capabilities (details in the end). The container sees its own process list, network, file system, and so forth. Unless otherwise specified, it cannot interact with your main operating system or anything stored or launched there.
Try running the docker run -it ubuntu bash command and typing ps auxf inside the running container. The output will be as follows:
There are only two processes, and the process ID of Bash is 1. To ensure that the /home directory is empty, you may use the ls /home command. Also, check out that the root user is the default one inside the container.
What's all this for?
Docker is a general-purpose tool for delivering applications to machines (local computers or remote servers) and running them in an isolated environment.
Remember the process when you had to build programs from the source. It’s likely to consist of the following steps:
- Install all of the operating system dependencies (finding them is quite a task)
- Download the archive, unpack it
- Start configuration make configure
- Start compilation make compile
- Install make install
As you can see, the process is not always straightforward or quick, and in certain cases, it may even be impossible because of cryptic issues (not to mention the corruption of the operating system).
Docker simplifies this method by allowing you to run a single command with a near-perfect success rate. Look at a fictional example in which the Tunnel program is installed on a local computer in the /usr/local/bin directory. It uses the tunnel image:
This command moves the program's executable file from the tunnel image in the /usr/local/bin directory on the main system. The docker run command launches a container from the tunnel image. The program is compiled locally and eventually ends up in the /usr/local/bin directory of the main file system. You may now run the program by typing tunnel in the terminal.
What if the program we are installing has dependencies? The secret is that the container was launched from a fully equipped image. It includes all necessary dependencies and ensures approximately 100% operability regardless of the status of the main OS.
It is not always necessary to copy a program from a container to your main system. It is enough to launch the container only when you need it. Assume we decided to create a static website using Jekyll. Jekyll is a popular static website generator written in Ruby, and even this guide is generated using it and Docker as well.
Earlier you have had to install at least Ruby and Jekyll itself as a gem on your main system (gem is the name of packages in Ruby). Furthermore, as with all such things, Jekyll only works with particular versions of Ruby, which leads to its own configuration issues.
Running Jekyll with Docker is reduced to just a single command executed in the blog directory (you can examine our guides repository):
In the same way, a huge number of different software products are being launched at the moment. The further away, the more this approach takes over the world. Now, we dip into the origin of the name Docker.
As you know, ships are the leading way to distribute goods around the world. Previously, the cost of transportation was high, because each cargo had its own shape and type of material.
Loading a bag of fish or a car onto a ship are different tasks requiring different processes and tools. There were problems with loading methods that required a variety of cranes and tools. And, given the ship's fragility, securely loading cargo on board was a challenging procedure.
But at some point, everything changed. The following picture says more than a thousand words:
Containers have equalized all types of cargo and standardized loading and unloading tools around the world, which in turn led to a simplification of processes, acceleration, and, therefore, lower costs.
The same thing happened in software development. Docker has become a universal software delivery tool, regardless of its structure, dependencies, or installation method. All that is required for programs distributed via Docker is that they are contained within the image and do not interact with the main system or other containers. The importance of this fact cannot be overestimated. Now, updating software versions does not involve either the system itself or other programs. Nothing can break anymore. All you need to do is download a new image of the program you want to update. In other words, Docker removed the dependency hell problem and made the infrastructure immutable.
Given the existence of numerous configuration tools (chef, puppet, ansible) prior to the Docker era, server management was somewhat daunting. The main cause of all problems was the mutable state. Programs are installed, updated, and deleted. This happens at different times and in somewhat different ways on different servers. Updating the version of a language like PHP, Ruby, or Python, for example, could be quite an adventure with a total loss of workability. It's much easier to put a new server next to it and switch to it. In theory, Docker allows you to make such a switch. Because each running container exists in its own environment, it is best to forget about the old one and replace it with a new one. Moreover, the rollback in such a system is pretty superficial: all you need is to stop the new container and rebuild the old one using the previous image.
Application in a container
Let’s now discuss how an application corresponds to a container. There are two possible approaches:
- The entire application is a container, inside which a process tree unfolds: an application, a web server, a database, and so on
- Every running container is an atomic service. In other words, each container, whether a web server or an application, is a single program
In fact, only the second approach provides access to all of Docker's advantages. Firstly, services are usually split over numerous servers and moved between them (for example, when a server crashes). Secondly, updating one service shouldn't lead to stopping the others.
The first approach is extremely rare, yet sometimes required. Hexlet, for example, works in two modes. The website and its services follow the second model, in which each service is distinct, but for practical exercises performed in a browser, we follow the principle of "one user — one container.” Depending on the exercise, anything can be placed inside the container. At least, the Hexlet IDE itself always starts there, and it in turn generates terminals (processes). A database is launched in the same container in our database course, and a web server is launched in a web-related course. This method creates the illusion of working on a genuine machine and makes it simpler to maintain work with exercises. Just to be clear, you probably won't require our use case, which is highly unique.
Another important aspect when working with containers relates to the state. For example, if the database is launched in a container, then its data should never be stored within the container. A container can be easily destroyed because it is an operating system process, and its existence is never permanent. Docker includes tools for storing and accessing data from the main file system. We'll get to them later.
Working with images
Docker is more than just a program. This is an entire ecosystem with many projects and services. The main service you will have to work with is Registry, an image storage.
Generally, it works the same way as the package repository of any package manager. You can view its contents on the website https://hub.docker.com/, by clicking Explore.
When we run the run docker run <image name> command, Docker checks for the presence of the specified image on the local machine and downloads it if necessary. The list of images already downloaded to your computer can be displayed with the docker images command:
Let's figure out how the image name is formed, and what it includes.
The second column in the output above is called TAG. The docker run nginx command is, in fact, a shortcut for the docker run nginx:latest which was executed. That is, we do not just download the nginx image, but its particular version. The latest is the default tag. It's easy to guess that it means the latest version of the image.
It is essential to understand that this is just an agreement, not a rule. A specific image may or may not have the latest tag, but it will not contain the most recent changes just because no one publishes them. ВPopular images, on the other hand, follow the agreement. As it is clear from the context, tags in Docker are changeable; that is, no one guarantees that downloading an image with the same tag on different computers at different times will result in the same thing. This approach may seem strange and unreliable because there are no guarantees, but in practice, there are certain agreements that all popular images follow. The latest tag always contains the most recent version and is constantly updated, but semantic versioning is also actively used. Let’s have a look at https://hub.docker.com/_/nginx
1.13.8, mainline, 1, 1.13, latest
1.13.8-perl, mainline-perl, 1-perl, 1.13-perl, perl
1.13.8-alpine, mainline-alpine, 1-alpine, 1.13-alpine, alpine
1.13.8-alpine-perl, mainline-alpine-perl, 1-alpine-perl, 1.13-alpine-perl, alpine-perl
1.12.2, stable, 1.12
1.12.2-perl, stable-perl, 1.12-perl
1.12.2-alpine, stable-alpine, 1.12-alpine
1.12.2-alpine-perl, stable-alpine-perl, 1.12-alpine-perl
Tags with the complete semantic version (x.x.x) are always immutable, even if they contain something else, such as 1.12.2-alphine. This version is safe to use in the production environment. When the path version changes, tags such as 1.12 are updated. That is, there may be version 1.12.2 inside the image, followed by 1.12.8 in the future. The same applies to versions that merely specify the major version, for example, 1. Only in this case, the update is not only on the patch but also on the minor version.
As you may recall, the docker run command downloads the image if it is not available locally, but this check has nothing to do with updating the content. In short, if the nginx:latest has been updated, docker run will not download it; instead, it will use the latest, that is already downloaded. Another command for guaranteed image updates is docker pull. It always checks whether the image has been updated for a certain tag.
Along with tags, the image name can include a prefix, such as etsy/chef. This prefix is the service account name that is used to create images for the Registry. The majority of the images have a prefix, but there’s still a small set, literally a hundred images, with no prefix at all. These images are special since they are supported by Docker itself. Therefore, if there is no prefix in the image name, this is an official image. You can check their list: https://github.com/docker-library/official-images/tree/master/library
Images can be deleted with the docker rmi <imagename> command.
If Docker has at least one container from the removed image, then it will not allow to delete it for obvious reasons. If you still want to delete both the image and all containers associated with it, use the -f flag.
Container management
The picture describes the life cycle (finite state machine) of the container. The circles depict the states, the console commands are highlighted in bold, and the squares show what is actually being executed.
Follow the path of the docker run command. This single command performs two actions in Docker: creating a container and starting it. There are more complex execution scenarios, but in this section, we will consider only the basics.
Let’s run nginx so that it works in the background. To do this, we will add the -d flag after the word run:
After executing the command, Docker return control and outputs the container ID. Make sure that nginx is working by opening localhost:8080 in browser. Unlike the previous launch, our nginx works in the background, which means its output (logs) is not visible. You can view it using the docker logs command, which requires the container ID:
You can also open a log pager using tail -f. To do this, run docker logs -f 431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e. The log will now be updated every time you refresh the page in the browser. You can exit this mode by pressing Ctrl + C, but the container will not stop.
Now we will output information about the running containers with the docker ps command:
Columns description:
- CONTAINER_ID — container identifier. Just like Git, Docker uses an abbreviated hash entry
- IMAGE — the name of the image that is used to create this container. If no tag is specified, then the latest is implied latest is implied
- COMMAND — a command that was executed at the start of the container
- CREATED — container creation time
- STATUS — current state
- PORTS — port forwarding
- NAMES — unique names. In addition to the identifier, Docker allows you to specify a name. It's much easier to handle the container this way. If the name is not provided when creating the container, Docker will generate a random one. Like the one, Nginx has in the output above
(The docker stats command gives information about running containers’ resource consumption).
Let's try to stop the container now. Run the following command:
If you type docker ps, you won’t see this container, it has been stoped.
The docker ps command outputs only running containers. However, there may be some stopped ones as well. Moreover, they can stop both upon successful completion and in case of errors. Try typing docker run ubuntu ls, followed by docker run ubuntu bash -c "unknown". These commands do not initiate a long-running process. Instead, the first one terminates immediately after execution, and the second with an error, because such a command does not exist.
The docker ps -a command will now output all containers. The first three lines of output will be:
Here are our two most recent launches. If you look at the STATUS column, you can see that both containers are in the Exited state. That is, the running command inside them was executed, and they stopped. The only difference is that one completed successfully (0), and the other did not (127). Even after it has been stopped, the container can be restarted:
Only this time you won't see the output. Use the docker logs determined_thatchar command to see it.
Interaction between the components of a system
Running an isolated container that exists entirely within itself is useless. In general, the container must interact with the outside world, accept incoming requests to a specific port, execute requests to other services, read shared files, and write to them. When you create a container, you can configure all of these features.
Interactive mode
As shown, the simplest way to use Docker is to create a container and execute some commands inside it:
Docker returns control after executing the command, and we are no longer inside the container. If we try to run bash in the same way, we will not get what we expected:
The thing is that bash starts an interactive session within the container. To interact with it, keep the STDIN stream open and the TTY running (pseudo-terminal). Therefore, to start interactive sessions, make sure to include the -i and -t options. They are typically added together as -it. So, the correct way to start bash is docker run -it ubuntu bash.
Ports
If you run Nginx usint the docker run nginxcommand, it will be unable to accept any request, even though it listens to port 80 inside the container (let me remind you that each container exists in its own network by default). But if you run it like this: docker run -p 8080:80 nginx, then nginx will start responding on port 8080.
The -p flag specifies how and which ports should be exposed to the outside. The format 8080:80 stands for “expose port 8080 inside the container to port 80 outside the container”. Furthermore, port 8080 s by default listened to on 0.0.0.0, that is, on all available interfaces. As a result, the container launched this way is accessible not only via localhost:8080 but also from outside the machine (if access is not denied somehow else). If you only need to do a loopback, the command is docker run -p 127.0.0.1:8080:80 nginx.
Docker allows you to forward an unlimited number of ports. In the case of Nginx, for example, it is frequently necessary to use both ports 80 and 443 for HTTPS. You can do it this way: docker run -p 80:80 -p 443:443 nginx. You can read about the other ways to forward ports in the official documentation.
Volumes
Accessing the main file system is another common task. When launching nginx container, for example, you may provide the configuration that is stored on the main fs (file system). Docker will forward it in the internal fs for Nginx to read and use.
The -voption is used for forwarding. Here's how to start a bash session from an Ubuntu image by connecting the command history from the main file system: docker run -it -v ~/.bash_history:/root/.bash_history ubuntu bash. The history will be displayed if you press the up arrow in the open bash-shell. You can forward both files and directories. Any changes made inside the volume have an effect both inside and outside the container, and all operations are available by default. The number of files and directories that can be forwarded is unlimited, just like the number of ports.
There are some key rules to remember when working with Volumes:
- The path to the file in the external system must be absolute
- If the inner path (what follows :) does not exist, Docker will create all the necessary directories and files. If it does, Docker will replace the old one with the one that was forwarded
Docker provides various other options for building and using Volumes, in addition to forwarding a part of the fs outside. Read the official documentation for further details.
Environment variables
The application in a container is often configured via environment variables following 12factors. There are two ways of installing them:
- The -e flag is used as follows: docker run -it -e "HOME=/tmp" ubuntu bash
- The --env-file option inserts a special file containing environment variable definitions into the
Preparing your own docker image
Creating and publishing your own image is as simple as using it. The whole process is divided into three steps:
- Create Dockerfile in the project's root. The process of creating an image should be described inside
- Build the image using the docker build command
- Publish the image to Registry with the docker push command
Let’s consider the image creation process by the example of packaging the eslint linter (do not forget to repeat it by yourself). As a result, we will have an image that can be used as follows:
So, simply run the container from this image by attaching the directory with the js files to the internal directory /app as Volume.
1. The following is the final directory structure with the files from which the image will be constructed:
eslint-docker/
Dockerfile
eslintrc.yml
The eslintrc.yml file contains the linter configuration. It is read automatically if it’s in the home directory and named .eslintrc.yml. That is, this file should be placed in the image's /root directory under this name.
2. Dockerfile
# Dockerfile
FROM node:9.3
WORKDIR /usr/src
RUN npm install -g eslint babel-eslint
RUN npm install -g eslint-config-airbnb-base eslint-plugin-import
COPY eslintrc.yml /root/.eslintrc.yml
CMD ["eslint", "/app"]
Dockerfile has quite a simple format. An instruction (directive) and its description are stated on each line.
FROM
The Dockerfile FROM instruction is required to set the base image to inherit from. It is important to note here that the images are based on each other and together form a big tree.
The busybox image can be found at the root of this tree. Because Docker provides ready-to-use images for each ecosystem and stack, it is not directly used in applied tasks.
RUN
The main Dockerfile instruction. In fact, we will state the sh command that will be run within the environment here. When creating an image, it is typically specified in the FROM instruction. Since by default everything is executed from the root user, there is no need to sudo (and most likely it is not in the base image). Also, keep in mind that building an image is not an interactive process. When using a command that potentially requires something from the user, it is necessary to hide its output. In the case of package managers, for example, they do the following: apt-get install -y curl. The -y flag indicates that installation should proceed without additional questions.
A Docker image is technically a set of so-called layers rather than a single file. Each RUN call creates a new layer, which can be viewed as a collection of files generated and updated (including deletion) by the RUN command. By caching unchanged layers, you can significantly improve system performance. On the other hand, Docker reuses layers in different images if they are identical, reducing both download speed and disk space occupied. The subject of layers cache is quite important when extensively using Docker. To get the most of it, you must first grasp how it works and how to precisely define the RUN instructions.
COPY
The COPY instruction, as the name implies, copies a file or directory from the main fs to the image. But the transferred files should be in the same directory as the Dockerfile. We generally use this instruction to pack an application in an image.
WORKDIR
Sets the working directory. All subsequent instructions will be assumed to be executed from it. By default, the WORKDIR instruction acts like a cd command. Besides, when we launch the container, it also starts from the working directory. For example, by running bash, you will end up inside it.
CMD
The same instruction that defines the docker run default action. It is only used if the container was launched without a command; otherwise, it is ignored.
3. Building
To create a docker image, use the docker build command. Use -t flag to provide the image name, including the account name and tag. As usual, if you do not specify the tag, then the latest will be set.
After running this command, the current image will be listed in the docker images. You can even start using it, there is no need to publish it to Registry. Let me remind you that the docker run command does not search for an updated version of the image if an image with the same name and tag already exist locally.
4. Publishing
To complete the publishing, you must meet two conditions:
- Register on Docker Cloud and create a repository for the image there
- Log in to the CLI interface using the docker login command.
Docker Compose
Docker Compose is a tool that allows you to develop a project locally using Docker. It is similar to Vagrant in its purpose.
Docker Compose also helps to manage a set of containers, each of which is a separate project service. Management includes building, dependency-aware launch, and configuration. Docker Compose configuration is specified in the docker-compose.yml file at the project roo. It looks as follows:
In action
Setting up project machines with Docker usually comes down to installing Docker. Then you just have to deploy. The basic process is:
- Download a new image
- Stop the old container
- Build a new one
This process does not depend on the technology stack. You can deploy (as well as configure machines) using Ansible.
Another option, suitable for more complex projects, is to use special orchestration tools such as Kubernetes. It generally requires extensive preparation, including an understanding of how distributed systems operate.
Docker: what’s under the hood?
Docker's isolation is provided by the Cgroups and Namespaces kernel features. They enable you to run an operating system process not just in an isolated environment, but also with limited use of hardware resources (RAM or CPU).
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="en" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="2GOyXT9e7PlISgMsTUkk6-f4yl21X28ZDK2f-aGp4DeLpd9rHjXXGS9A_MT_1xMq61JEBnlBdRWuKFXq7HU_7A";gon.locale="en";gon.language="en";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLScNwxM8rjQRRWqW5G6dn6-0NvLUblFemy7EvA9VsJ7Ov5wXqA";
//]]>
</script>
<meta charset="utf-8">
<title>Why Do I Need Docker, and How Do I Use It? | Hexlet Guides</title>
<meta name="description" content="How do you build a project infrastructure, and how do you bundle and run services? We'll discuss isolating processes in the operating system without relying on heavy virtualization.">
<link rel="canonical" href="https://hexlet.io/blog/posts/why-do-i-need-docker-and-how-do-i-use-it">
<meta property="og:title" content="Why Do I Need Docker, and How Do I Use It? | Hexlet Guides">
<meta property="og:description" content="How do you build a project infrastructure, and how do you bundle and run services? We'll discuss isolating processes in the operating system without relying on heavy virtualization.">
<meta property="og:image" content="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOlsxNDU2LDcyOF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--b2ecf6121248a24883cda19469732b47b57f4e80/docker.png">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="uxl2ravx0AjUB-x54jlxu4EPCjd0wOxgQ4-G1uu_MUPo3xubiprr6LMNE5FQp0Z6jaWEbLje9mzhCkzFpmPumA" />
<script src="/vite/assets/inertia-CgrHVkgd.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-C1cfMHAs.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-0bhwJkNI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-BXKrsWjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-Du9ljYPK.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-V3hfk_CP.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/RootLayout-CUZzAr0T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DbDKujDz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-B-jV56Ol.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-DOJkeu70.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-ClTF9s_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-mvvEXZQ8.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DATLpQdV.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-BhY0AP_c.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-P1s4q94S.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-BsSGGK2I.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-Bfba02I7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-CYyKzrjQ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-SJZekO2j.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-bo78L81P.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-BhqaZ6vG.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-DlXMvSuQ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CFtMU8gd.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BhDYOUva.css" />
<script src="/vite/assets/application-ZyVHkzwO.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-BokUl44d.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-mvvEXZQ8.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-gQnwoPhY.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-CB1F2-VC.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-BhDYOUva.css" media="screen" />
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript>
<iframe height="0" src="https://www.googletagmanager.com/ns.html?id=GTM-WK88TH" style="display:none;visibility:hidden" width="0"></iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Hexlet logo" height="24" src="https://hexlet.io/vite/assets/logo_en_light-FS-yL6XB.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Menu" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
All courses
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Everything</div>
<div class="text-muted">8</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Popular categories</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_backend-development">Backend Development
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_frontend-development">Frontend Development
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Testing
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Popular courses</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Frontend Developer
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
About Hexlet
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">About company
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Blog
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.results">Results</span>
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.career">Career</span>
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Testimonials
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.support">Support</span>
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.referal_program">Referal Program</span>
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.certificates">Certificates</span>
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.vacancies">Vacancies</span>
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.teams">Teams</span>
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.college">College</span>
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button"><span class="translation_missing" title="translation missing: en.layouts.header.private_school">Private School</span>
</span></li>
</ul>
</li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Switch theme" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Sign up</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://hexlet.io/session/new" role="button"><span>Sign in</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOlsxNDU2LDcyOF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--b2ecf6121248a24883cda19469732b47b57f4e80/docker.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/blog/posts/show","props":{"errors":{},"locale":"en","language":"en","httpsHost":"https://hexlet.io","host":"hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":null,"formAuthToken":"aaOBKxX_pmu_Wre_9sumajIRfhxttB5KGMfNEB0_dpU6ZewdNJSdi9hQSFdEVZGrPrvwR6GqBEa6QgcDUOOpTg","post":{"model_name":"BlogPost","category":{"id":9,"name":"Code","slug":"code","state":"published","created_at":"2017-01-19T10:57:24.051Z"},"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"tags":[],"id":2813,"title":"Why Do I Need Docker, and How Do I Use It?","slug":"why-do-i-need-docker-and-how-do-i-use-it","state":"published","summary":"Docker is a program that allows the operating system to run processes in an isolated environment based on specially created images. And, while the technologies underlying Docker existed earlier, it was Docker that changed the way we now build project infrastructure, assemble and launch services.","votes_count":0,"created_at":"2024-11-30T19:06:29.321Z","published_at":"2024-12-02T09:10:56.131Z","body":"**Docker is a program that allows the operating system to run processes in an isolated environment based on specially created images. And, while the technologies underlying Docker existed earlier, it was Docker that changed the way we now build project infrastructure, assemble and launch services.**\r\n\r\n*(Some details were purposefully left out of the article to avoid unnecessary information).*\r\n\r\n## Contents\n\n## Installation\r\n\r\nTo start with Docker, you must first install the Docker Engine. Download links for all popular platforms are provided at https://docs.docker.com/engine/install/. Select yours and install Docker.\r\n\r\nThere's one thing to remember when installing Docker on Mac and Linux. By default, Docker runs through a non-networked UNIX socket. For security reasons, the socket is blocked for users who are not members of the docker group. Although the installer automatically adds the current user to this group, Docker will not work straight away. In reality, changing the group by the user has no effect till the user logs in again. This is a feature of the core. Enter the `id` command to see which groups your user belongs to.\r\n\r\nYou can check if the installation was successful with the `docker info` command:\r\n\r\n```shell\r\n$ docker info\r\nContainers: 22\r\n Running: 2\r\n Paused: 0\r\n Stopped: 20\r\nImages: 72\r\nServer Version: 17.12.0-ce\r\nStorage Driver: overlay2\r\n Backing Filesystem: extfs\r\n Supports d_type: true\r\n Native Overlay Diff: true\r\nLogging Driver: json-file\r\nCgroup Driver: cgroupfs\r\n...\r\n```\r\n\r\nIt provides quite a lot of information about Docker configurations as well as work statistics.\r\n\r\n## Launch\r\n\r\nAt this stage, the execution commands are given with no explanation. Later, more information on how they're formed and what include will be given.\r\n\r\nLet's start with the easiest one:\r\n\r\n```shell\r\n$ docker run -it nginx bash\r\nroot@a6c26812d23b:/#\r\n```\r\n\r\nThis command will begin downloading the _nginx_ image on the initial call, so you will have to wait a few moments. After the image is downloaded, _bash_ will run, and you will find yourself **in a container**.\r\n\r\nGo through the file system, and look at the */etc/nginx*. As you can see, its content does not match what you have on your system. This file system was generated by the _nginx_ image. Everything you do within it will not affect your main file system. With the `exit` command, you can get back to your native system.\r\n\r\nNow, let's call the `cat` command, which is already in another container but is also launched from the _nginx_ image:\r\n\r\n```shell\r\n$ docker run nginx cat /etc/nginx/nginx.conf\r\n\r\nuser nginx;\r\nworker_processes 1;\r\n\r\nerror_log /var/log/nginx/error.log warn;\r\npid /var/run/nginx.pid;\r\n...\r\n$\r\n```\r\n\r\nThe command is executed almost instantly since the image has already been uploaded. Unlike before, when a bash script and an interactive session were launched in the container, the `cat /etc/nginx/nginx.conf` command for the _nginx_ image will display the contents of the specified file (taken from the running container's file system) and return control to where you were before. You won’t end up inside the container.\r\n\r\nThe last launch option will be:\r\n\r\n```shell\r\n# Note that no command is specified after the image name\r\n# This approach works if the launch command is written in the image\r\n$ docker run -p 8080:80 nginx\r\n```\r\n\r\nThis command does not restore control, because Nginx starts running. Enter `localhost:8080` into your browser. You'll see that the _Welcome to nginx!_ page has loaded!. If you return to the terminal where the container was launched, you'll see a log of requests to `localhost:8080`. You may stop nginx by pressing <kbd>Ctrl + C</kbd>.\r\n\r\nAlthough each launch was different and resulted in various outcomes, the overall pattern of their work is consistent. Docker downloads the image automatically (the first argument after `docker run`) and, if necessary, runs the container with the specified command.\r\n\r\n**An image is an independent file system**. For now, we use ready-made images, but we will eventually learn how to create them ourselves.\r\n\r\n**A container is a running process of the operating system in an isolated environment** with an image file system.\r\n\r\nA container is, once again, just a usual operating system process. The main difference is that Docker starts the process in an isolated environment due to the kernel’s capabilities (details in the end). The container sees its own process list, network, file system, and so forth. Unless otherwise specified, it cannot interact with your main operating system or anything stored or launched there.\r\n\r\nTry running the `docker run -it ubuntu bash` command and typing `ps auxf` inside the running container. The output will be as follows:\r\n\r\n```shell\r\nUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\r\nroot 1 0.1 0.1 18240 3300 pts/0 Ss 15:39 0:00 /bin/bash\r\nroot 12 0.0 0.1 34424 2808 pts/0 R+ 15:40 0:00 ps aux\r\n```\r\n\r\nThere are only two processes, and the process ID of Bash is 1. To ensure that the */home* directory is empty, you may use the `ls /home` command. Also, check out that the `root` user is the default one inside the container.\r\n\r\n## What's all this for?\r\n\r\n**Docker is a general-purpose tool for delivering applications to machines (local computers or remote servers) and running them in an isolated environment.**\r\n\r\nRemember the process when you had to build programs from the source. It’s likely to consist of the following steps:\r\n\r\n* Install all of the operating system dependencies (finding them is quite a task)\r\n* Download the archive, unpack it\r\n* Start configuration `make configure`\r\n* Start compilation `make compile`\r\n* Install `make install`\r\n\r\nAs you can see, the process is not always straightforward or quick, and in certain cases, it may even be impossible because of cryptic issues (not to mention the corruption of the operating system).\r\n\r\nDocker simplifies this method by allowing you to run a single command with a near-perfect success rate. Look at a fictional example in which the Tunnel program is installed on a local computer in the */usr/local/bin* directory. It uses the _tunnel_ image:\r\n\r\n```shell\r\ndocker run -v /usr/local/bin:/out tunnel\r\n```\r\n\r\nThis command moves the program's executable file from the _tunnel_ image in the */usr/local/bin* directory on the main system. The `docker run` command launches a container from the _tunnel_ image. The program is compiled locally and eventually ends up in the */usr/local/bin* directory of the main file system. You may now run the program by typing `tunnel` in the terminal.\r\n\r\nWhat if the program we are installing has dependencies? The secret is that the container was launched from a fully equipped image. It includes all necessary dependencies and ensures approximately 100% operability regardless of the status of the main OS.\r\n\r\nIt is not always necessary to copy a program from a container to your main system. It is enough to launch the container only when you need it. Assume we decided to create a static website using Jekyll. Jekyll is a popular static website generator written in Ruby, and even this guide is generated using it and Docker as well.\r\n\r\nEarlier you have had to install at least Ruby and Jekyll itself as a gem on your main system (gem is the name of packages in Ruby). Furthermore, as with all such things, Jekyll only works with particular versions of Ruby, which leads to its own configuration issues.\r\n\r\nRunning Jekyll with Docker is reduced to just a single command executed in the blog directory (you can examine our guides [repository](https://github.com/hexletguides/hexletguides.github.io)):\r\n\r\n```shell\r\ndocker run --rm --volume=\"$PWD:/srv/jekyll\" -it jekyll/jekyll jekyll server\r\n```\r\n\r\nIn the same way, a huge number of different software products are being launched at the moment. The further away, the more this approach takes over the world. Now, we dip into the origin of the name Docker.\r\n\r\n\r\n\r\nAs you know, ships are the leading way to distribute goods around the world. Previously, the cost of transportation was high, because each cargo had its own shape and type of material.\r\n\r\n\r\n\r\nLoading a bag of fish or a car onto a ship are different tasks requiring different processes and tools. There were problems with loading methods that required a variety of cranes and tools. And, given the ship's fragility, securely loading cargo on board was a challenging procedure.\r\n\r\nBut at some point, everything changed. The following picture says more than a thousand words:\r\n\r\n\r\n\r\nContainers have equalized all types of cargo and standardized loading and unloading tools around the world, which in turn led to a simplification of processes, acceleration, and, therefore, lower costs.\r\n\r\nThe same thing happened in software development. Docker has become a universal software delivery tool, regardless of its structure, dependencies, or installation method. All that is required for programs distributed via Docker is that they are contained within the image and do not interact with the main system or other containers. The importance of this fact cannot be overestimated. Now, updating software versions does not involve either the system itself or other programs. Nothing can break anymore. All you need to do is download a new image of the program you want to update. In other words, Docker removed the [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell) problem and made the infrastructure [immutable](https://martinfowler.com/bliki/ImmutableServer.html).\r\n\r\nGiven the existence of numerous configuration tools (chef, puppet, ansible) prior to the Docker era, server management was somewhat daunting. The main cause of all problems was the mutable state. Programs are installed, updated, and deleted. This happens at different times and in somewhat different ways on different servers. Updating the version of a language like PHP, Ruby, or Python, for example, could be quite an adventure with a total loss of workability. It's much easier to put a new server next to it and switch to it. In theory, Docker allows you to make such a switch. Because each running container exists in its own environment, it is best to forget about the old one and replace it with a new one. Moreover, the rollback in such a system is pretty superficial: all you need is to stop the new container and rebuild the old one using the previous image.\r\n\r\n## Application in a container\r\n\r\nLet’s now discuss how an application corresponds to a container. There are two possible approaches:\r\n\r\n1. The entire application is a container, inside which a process tree unfolds: an application, a web server, a database, and so on\r\n2. Every running container is an atomic service. In other words, each container, whether a web server or an application, is a single program\r\n\r\nIn fact, only the second approach provides access to all of Docker's advantages. Firstly, services are usually split over numerous servers and moved between them (for example, when a server crashes). Secondly, updating one service shouldn't lead to stopping the others.\r\n\r\nThe first approach is extremely rare, yet sometimes required. Hexlet, for example, works in two modes. The website and its services follow the second model, in which each service is distinct, but for practical exercises performed in a browser, we follow the principle of \"one user — one container.” Depending on the exercise, anything can be placed inside the container. At least, the Hexlet IDE itself always starts there, and it in turn generates terminals (processes). A database is launched in the same container in our database course, and a web server is launched in a web-related course. This method creates the illusion of working on a genuine machine and makes it simpler to maintain work with exercises. Just to be clear, you probably won't require our use case, which is highly unique.\r\n\r\nAnother important aspect when working with containers relates to the state. For example, if the database is launched in a container, then its data should never be stored within the container. A container can be easily destroyed because it is an operating system process, and its existence is never permanent. Docker includes tools for storing and accessing data from the main file system. We'll get to them later.\r\n\r\n## Working with images\r\n\r\nDocker is more than just a program. This is an entire ecosystem with many projects and services. The main service you will have to work with is Registry, an image storage.\r\n\r\nGenerally, it works the same way as the package repository of any package manager. You can view its contents on the website [https://hub.docker.com/](https://hub.docker.com/), by clicking Explore.\r\n\r\nWhen we run the _run_ `docker run <image name>` command, Docker checks for the presence of the specified image on the local machine and downloads it if necessary. The list of images already downloaded to your computer can be displayed with the `docker images` command:\r\n\r\n```shell\r\n$ docker images\r\nREPOSITORY TAG IMAGE ID CREATED SIZE\r\nworkshopdevops_web latest cfd7771b4b3a 2 days ago 817MB\r\nhexletbasics_app latest 8e34a5f631ea 2 days ago 1.3GB\r\nmokevnin/rails latest 96487c602a9b 2 days ago 743MB\r\nubuntu latest 2a4cca5ac898 3 days ago 111MB\r\nRuby 2.4 713da53688a6 3 weeks ago 687MB\r\nRuby 2.5 4c7885e3f2bb 3 weeks ago 881MB\r\nnginx latest 3f8a4339aadd 3 weeks ago 108MB\r\nelixir latest 93617745963c 4 weeks ago 889MB\r\npostgres latest ec61d13c8566 5 weeks ago 287MB\r\n```\r\n\r\nLet's figure out how the image name is formed, and what it includes.\r\n\r\nThe second column in the output above is called TAG. The `docker run nginx` command is, in fact, a shortcut for the `docker run nginx:latest` which was executed. That is, we do not just download the _nginx_ image, but its particular version. The latest is the default tag. It's easy to guess that it means the latest version of the image.\r\n\r\nIt is essential to understand that this is just an agreement, not a rule. A specific image may or may not have the _latest_ tag, but it will not contain the most recent changes just because no one publishes them. ВPopular images, on the other hand, follow the agreement. As it is clear from the context, tags in Docker are changeable; that is, no one guarantees that downloading an image with the same tag on different computers at different times will result in the same thing. This approach may seem strange and unreliable because there are no guarantees, but in practice, there are certain agreements that all popular images follow. The latest tag always contains the most recent version and is constantly updated, but [semantic versioning](https://semver.org/) is also actively used. Let’s have a look at https://hub.docker.com/_/nginx\r\n\r\n 1.13.8, mainline, 1, 1.13, latest\r\n 1.13.8-perl, mainline-perl, 1-perl, 1.13-perl, perl\r\n 1.13.8-alpine, mainline-alpine, 1-alpine, 1.13-alpine, alpine\r\n 1.13.8-alpine-perl, mainline-alpine-perl, 1-alpine-perl, 1.13-alpine-perl, alpine-perl\r\n 1.12.2, stable, 1.12\r\n 1.12.2-perl, stable-perl, 1.12-perl\r\n 1.12.2-alpine, stable-alpine, 1.12-alpine\r\n 1.12.2-alpine-perl, stable-alpine-perl, 1.12-alpine-perl\r\n\r\nTags with the complete semantic version (x.x.x) are always immutable, even if they contain something else, such as _1.12.2-alphine_. This version is safe to use in the production environment. When the path version changes, tags such as _1.12_ are updated. That is, there may be version _1.12.2_ inside the image, followed by _1.12.8_ in the future. The same applies to versions that merely specify the major version, for example, _1_. Only in this case, the update is not only on the patch but also on the minor version.\r\n\r\nAs you may recall, the `docker run` command downloads the image if it is not available locally, but this check has nothing to do with updating the content. In short, if the _nginx:latest_ has been updated, `docker run` will not download it; instead, it will use the _latest_, that is already downloaded. Another command for guaranteed image updates is `docker pull`. It always checks whether the image has been updated for a certain tag.\r\n\r\nAlong with tags, the image name can include a prefix, such as `etsy/chef`. This prefix is the service account name that is used to create images for the Registry. The majority of the images have a prefix, but there’s still a small set, literally a hundred images, with no prefix at all. These images are special since they are supported by Docker itself. Therefore, if there is no prefix in the image name, this is an official image. You can check their list: https://github.com/docker-library/official-images/tree/master/library\r\n\r\nImages can be deleted with the `docker rmi <imagename>` command.\r\n\r\n```shell\r\n$ docker rmi Ruby:2.4\r\nUntagged: Ruby:2.4\r\nUntagged: Ruby@sha256:d973c59b89f3c5c9bb330e3350ef8c529753ba9004dcd1bfbcaa4e9c0acb0c82\r\n```\r\n\r\nIf Docker has at least one container from the removed image, then it will not allow to delete it for obvious reasons. If you still want to delete both the image and all containers associated with it, use the `-f` flag.\r\n\r\n## Container management\r\n\r\n\r\n\r\nThe picture describes the life cycle (finite state machine) of the container. The circles depict the states, the console commands are highlighted in bold, and the squares show what is actually being executed.\r\n\r\nFollow the path of the `docker run` command. This single command performs two actions in Docker: creating a container and starting it. There are more complex execution scenarios, but in this section, we will consider only the basics.\r\n\r\nLet’s run nginx so that it works in the background. To do this, we will add the `-d` flag after the word _run_:\r\n\r\n```shell\r\n$ docker run -d -p 8080:80 nginx\r\n431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e\r\n```\r\n\r\nAfter executing the command, Docker return control and outputs the container ID. Make sure that nginx is working by opening `localhost:8080` in browser. Unlike the previous launch, our nginx works in the background, which means its output (logs) is not visible. You can view it using the `docker logs` command, which requires the container ID:\r\n\r\n```shell\r\n$ docker logs 431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e\r\n\r\n172.17.0.1 - - [19/Jan/2018:07:38:55 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\" \"-\"\r\n```\r\n\r\nYou can also open a log pager using `tail -f`. To do this, run `docker logs -f 431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e`. The log will now be updated every time you refresh the page in the browser. You can exit this mode by pressing <kbd>Ctrl + C</kbd>, but the container will not stop.\r\n\r\nNow we will output information about the running containers with the `docker ps` command:\r\n\r\n```shell\r\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\r\n431a3b3fc24b nginx \"nginx -g 'daemon of…\" 2 minutes ago Up 2 minutes 80/tcp wizardly_rosalind\r\n```\r\n\r\nColumns description:\r\n\r\n* CONTAINER_ID — container identifier. Just like Git, Docker uses an abbreviated hash entry\r\n* IMAGE — the name of the image that is used to create this container. If no tag is specified, then the latest is implied _latest_ is implied\r\n* COMMAND — a command that was executed at the start of the container\r\n* CREATED — container creation time\r\n* STATUS — current state\r\n* PORTS — port forwarding\r\n* NAMES — unique names. In addition to the identifier, Docker allows you to specify a name. It's much easier to handle the container this way. If the name is not provided when creating the container, Docker will generate a random one. Like the one, Nginx has in the output above\r\n\r\n(*The `docker stats` command gives information about running containers’ resource consumption).*\r\n\r\nLet's try to stop the container now. Run the following command:\r\n\r\n```shell\r\n# Instead of CONTAINER_ID, you can specify a name\r\n$ docker kill 431a3b3fc24b # docker kill wizardly_rosalind\r\n431a3b3fc24b\r\n```\r\n\r\nIf you type `docker ps`, you won’t see this container, it has been stoped.\r\n\r\nThe `docker ps` command outputs only running containers. However, there may be some stopped ones as well. Moreover, they can stop both upon successful completion and in case of errors. Try typing `docker run ubuntu ls`, followed by `docker run ubuntu bash -c \"unknown\"`. These commands do not initiate a long-running process. Instead, the first one terminates immediately after execution, and the second with an error, because such a command does not exist.\r\n\r\nThe `docker ps -a` command will now output all containers. The first three lines of output will be:\r\n\r\n```shell\r\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\r\n85fb81250406 ubuntu \"bash -c unkown\" Less than a second ago Exited (127) 3 seconds ago loving_bose\r\nc379040bce42 ubuntu \"ls\" Less than a second ago Exited (0) 9 seconds ago determined_thatchar\r\n```\r\n\r\nHere are our two most recent launches. If you look at the STATUS column, you can see that both containers are in the Exited state. That is, the running command inside them was executed, and they stopped. The only difference is that one completed successfully (0), and the other did not (127). Even after it has been stopped, the container can be restarted:\r\n\r\n```shell\r\ndocker start determined_thatchar # In your case, there will be another name\r\n\r\n```\r\n\r\nOnly this time you won't see the output. Use the `docker logs determined_thatchar` command to see it.\r\n\r\n## Interaction between the components of a system\r\n\r\nRunning an isolated container that exists entirely within itself is useless. In general, the container must interact with the outside world, accept incoming requests to a specific port, execute requests to other services, read shared files, and write to them. When you create a container, you can configure all of these features.\r\n\r\n### Interactive mode\r\n\r\nAs shown, the simplest way to use Docker is to create a container and execute some commands inside it:\r\n\r\n```shell\r\n$ docker run ubuntu ls /usr\r\nbin\r\ngames\r\ninclude\r\nlib\r\nlocal\r\nsbin\r\nshare\r\nsrc\r\n$\r\n```\r\n\r\nDocker returns control after executing the command, and we are no longer inside the container. If we try to run bash in the same way, we will not get what we expected:\r\n\r\n```shell\r\n$ docker run ubuntu bash\r\n$\r\n```\r\n\r\nThe thing is that `bash` starts an interactive session within the container. To interact with it, keep the STDIN stream open and the TTY running (pseudo-terminal). Therefore, to start interactive sessions, make sure to include the `-i` and `-t` options. They are typically added together as `-it`. So, the correct way to start bash is `docker run -it ubuntu bash`.\r\n\r\n### Ports\r\n\r\nIf you run Nginx usint the `docker run nginx`command, it will be unable to accept any request, even though it listens to port _80_ inside the container (let me remind you that each container exists in its own network by default). But if you run it like this: `docker run -p 8080:80 nginx`, then nginx will start responding on port _8080_.\r\n\r\nThe `-p` flag specifies how and which ports should be exposed to the outside. The format `8080:80` stands for “expose port _8080_ inside the container to port _80_ outside the container”. Furthermore, port _8080_ s by default listened to on `0.0.0.0`, that is, on all available interfaces. As a result, the container launched this way is accessible not only via `localhost:8080` but also from outside the machine (if access is not denied somehow else). If you only need to do a _loopback_, the command is `docker run -p 127.0.0.1:8080:80 nginx`.\r\n\r\nDocker allows you to forward an unlimited number of ports. In the case of Nginx, for example, it is frequently necessary to use both ports `80` and `443` for HTTPS. You can do it this way: `docker run -p 80:80 -p 443:443 nginx`. You can read about the other ways to forward ports in the official documentation.\r\n\r\n### Volumes\r\n\r\nAccessing the main file system is another common task. When launching nginx container, for example, you may provide the configuration that is stored on the main fs (file system). Docker will forward it in the internal fs for Nginx to read and use.\r\n\r\nThe `-v`option is used for forwarding. Here's how to start a bash session from an Ubuntu image by connecting the command history from the main file system: `docker run -it -v ~/.bash_history:/root/.bash_history ubuntu bash`. The history will be displayed if you press the up arrow in the open bash-shell. You can forward both files and directories. Any changes made inside the volume have an effect both inside and outside the container, and all operations are available by default. The number of files and directories that can be forwarded is unlimited, just like the number of ports.\r\n\r\nThere are some key rules to remember when working with Volumes:\r\n\r\n* The path to the file in the external system must be absolute\r\n* If the inner path (what follows `:`) does not exist, Docker will create all the necessary directories and files. If it does, Docker will replace the old one with the one that was forwarded\r\n\r\nDocker provides various other options for building and using Volumes, in addition to forwarding a part of the fs outside. Read the official documentation for further details.\r\n\r\n### Environment variables\r\n\r\nThe application in a container is often configured via environment variables following [12factors](https://12factor.net). There are two ways of installing them:\r\n\r\n* The `-e` flag is used as follows: `docker run -it -e \"HOME=/tmp\" ubuntu bash`\r\n* The `--env-file` option inserts a special file containing environment variable definitions into the\r\n\r\n## Preparing your own docker image\r\n\r\nCreating and publishing your own image is as simple as using it. The whole process is divided into three steps:\r\n\r\n* Create `Dockerfile` in the project's root. The process of creating an image should be described inside\r\n* Build the image using the `docker build` command\r\n* Publish the image to Registry with the `docker push` command\r\n\r\nLet’s consider the image creation process by the example of packaging the `eslint` linter (do not forget to repeat it by yourself). As a result, we will have an image that can be used as follows:\r\n\r\n```shell\r\n$ docker run -it -v /path/to/js/files:/app my_account_name/eslint\r\n\r\n/app/index.js\r\n 3:6 error Parsing error: Unexpected token\r\n\r\n 1 | import path from 'path';\r\n 2 |\r\n> 3 | path(;)\r\n | ^\r\n 4 |\r\n\r\n✖ 1 problem (1 error, 0 warnings)\r\n```\r\n\r\nSo, simply run the container from this image by attaching the directory with the js files to the internal directory */app* as Volume.\r\n\r\n### 1. The following is the final directory structure with the files from which the image will be constructed:\r\n\r\n```\r\neslint-docker/\r\n Dockerfile\r\n eslintrc.yml\r\n```\r\n\r\nThe *eslintrc.yml* file contains the linter configuration. It is read automatically if it’s in the home directory and named *.eslintrc.yml*. That is, this file should be placed in the image's */root* directory under this name.\r\n\r\n### 2. Dockerfile\r\n\r\n```\r\n# Dockerfile\r\nFROM node:9.3\r\n\r\nWORKDIR /usr/src\r\n\r\nRUN npm install -g eslint babel-eslint\r\nRUN npm install -g eslint-config-airbnb-base eslint-plugin-import\r\n\r\nCOPY eslintrc.yml /root/.eslintrc.yml\r\n\r\nCMD [\"eslint\", \"/app\"]\r\n```\r\n\r\nDockerfile has quite a simple format. An instruction (directive) and its description are stated on each line.\r\n\r\n#### FROM\r\n\r\nThe Dockerfile FROM instruction is required to set the base image to inherit from. It is important to note here that the images are based on each other and together form a big tree.\r\n\r\nThe _busybox_ image can be found at the root of this tree. Because Docker provides ready-to-use images for each ecosystem and stack, it is not directly used in applied tasks.\r\n\r\n#### RUN\r\n\r\nThe main Dockerfile instruction. In fact, we will state the _sh_ command that will be run within the environment here. When creating an image, it is typically specified in the FROM instruction. Since by default everything is executed from the _root_ user, there is no need to _sudo_ (and most likely it is not in the base image). Also, keep in mind that building an image is not an interactive process. When using a command that potentially requires something from the user, it is necessary to hide its output. In the case of package managers, for example, they do the following: `apt-get install -y curl`. The `-y` flag indicates that installation should proceed without additional questions.\r\n\r\nA Docker image is technically a set of so-called layers rather than a single file. Each RUN call creates a new layer, which can be viewed as a collection of files generated and updated (including deletion) by the RUN command. By caching unchanged layers, you can significantly improve system performance. On the other hand, Docker reuses layers in different images if they are identical, reducing both download speed and disk space occupied. The subject of layers cache is quite important when extensively using Docker. To get the most of it, you must first grasp how it works and how to precisely define the `RUN` instructions.\r\n\r\n#### COPY\r\n\r\nThe COPY instruction, as the name implies, copies a file or directory from the main fs to the image. But the transferred files should be in the same directory as the Dockerfile. We generally use this instruction to pack an application in an image.\r\n\r\n#### WORKDIR\r\n\r\nSets the working directory. All subsequent instructions will be assumed to be executed from it. By default, the `WORKDIR` instruction acts like a `cd` command. Besides, when we launch the container, it also starts from the working directory. For example, by running bash, you will end up inside it.\r\n\r\n#### CMD\r\n\r\nThe same instruction that defines the `docker run` default action. It is only used if the container was launched without a command; otherwise, it is ignored.\r\n\r\n### 3. Building\r\n\r\nTo create a docker image, use the `docker build` command. Use `-t` flag to provide the image name, including the account name and tag. As usual, if you do not specify the tag, then the _latest_ will be set.\r\n\r\n```shell\r\n$ docker build -t my_account_name/eslint .\r\n```\r\n\r\nAfter running this command, the current image will be listed in the `docker images`. You can even start using it, there is no need to publish it to Registry. Let me remind you that the `docker run` command does not search for an updated version of the image if an image with the same name and tag already exist locally.\r\n\r\n### 4. Publishing\r\n\r\n```shell\r\n$ docker push my_account_name/eslint\r\n```\r\n\r\nTo complete the publishing, you must meet two conditions:\r\n\r\n* Register on Docker Cloud and create a repository for the image there\r\n* Log in to the CLI interface using the `docker login` command.\r\n\r\n## Docker Compose\r\n\r\nDocker Compose is a tool that allows you to develop a project locally using Docker. It is similar to Vagrant in its purpose.\r\n\r\nDocker Compose also helps to manage a set of containers, each of which is a separate project service. Management includes building, dependency-aware launch, and configuration. Docker Compose configuration is specified in the `docker-compose.yml` file at the project roo. It looks as follows:\r\n\r\n```yaml\r\n# https://github.com/hexlet-basics/hexlet_basics\r\n\r\nversion: '3.3'\r\n\r\nservices:\r\n db:\r\n image: postgres\r\n app:\r\n build:\r\n context: services/app\r\n dockerfile: Dockerfile\r\n command: mix phx.server\r\n ports:\r\n - \"${PORT}:${PORT}\"\r\n env_file: '.env'\r\n volumes:\r\n - \"./services/app:/app:cached\"\r\n - \"~/.bash_history:/root/.bash_history:cached\"\r\n - \".bashrc:/root/.bashrc:cached\"\r\n - \"/var/tmp:/var/tmp:cached\"\r\n - \"/tmp:/tmp:cached\"\r\n depends_on:\r\n - db\r\n```\r\n\r\n## In action\r\n\r\nSetting up project machines with Docker usually comes down to installing Docker. Then you just have to deploy. The basic process is:\r\n\r\n1. Download a new image\r\n2. Stop the old container\r\n3. Build a new one\r\n\r\nThis process does not depend on the technology stack. You can deploy (as well as configure machines) using [Ansible](https://docs.ansible.com/ansible/2.5/modules/docker_container_module.html).\r\n\r\nAnother option, suitable for more complex projects, is to use special orchestration tools such as [Kubernetes](https://kubernetes.io/). It generally requires extensive preparation, including an understanding of how distributed systems operate.\r\n\r\n## Docker: what’s under the hood?\r\n\r\nDocker's isolation is provided by the [Cgroups](https://en.wikipedia.org/wiki/Cgroups) and [Namespaces](https://en.wikipedia.org/wiki/Linux_namespaces) kernel features. They enable you to run an operating system process not just in an isolated environment, but also with limited use of hardware resources (RAM or CPU).","reading_time":19,"url":"https://en.hexlet.io/blog/posts/why-do-i-need-docker-and-how-do-i-use-it","cover_thumb_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbMTAwLDUwXSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--f0d4342fcdbdffa15c37fb02bfb423ac88d5c0c9/docker.png","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/docker.png","cover_main_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOlsxNDU2LDcyOF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--b2ecf6121248a24883cda19469732b47b57f4e80/docker.png","related_stacks_count":5},"relatedPosts":[{"model_name":"BlogPost","id":2812,"title":"What is Deployment?","slug":"what-is-deployment","summary":"Deployment is the process of web service “deployment”, e.g. of a website, in the operating environment. An operating environment is a place where a website is started and available for requests. This can be ready-made hosting as well as your own server infrastructure.","created_at":"2024-11-30T18:46:23.446Z","published_at":"2024-11-30T18:48:09.956Z","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzAzLCJwdXIiOiJibG9iX2lkIn19--1de52a265d13d63e0c809a60839e404b1fc74fa4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/deploy.png"},{"model_name":"BlogPost","id":1965,"title":"Code Complete: Sneaky One-Liners","slug":"code-complete-sneaky-one-liners","summary":"Programmers like compact code. If it's done correctly, then it should be easy to read and shouldn't contain any parts that make you think about it more than necessary.","created_at":"2022-08-12T08:23:23.195Z","published_at":"2022-08-12T09:24:48.986Z","cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp"},{"model_name":"BlogPost","id":1966,"title":"Code Complete: Using a Library or Writing Your Own Code","slug":"code-complete-using-a-library-or-writing-your-own-code","summary":"Is it worth using libraries for a few simple functions? Wouldn't writing them yourself be easier? These are questions regularly asked by novice developers and by almost anyone working on Hexlet projects. Let's take a closer look.","created_at":"2022-08-12T08:55:56.757Z","published_at":"2022-08-12T09:21:40.170Z","cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp"}],"category":{"id":9,"name":"Code","slug":"code","state":"published","created_at":"2017-01-19T10:57:24.051Z"},"mainStackCategory":{"id":21,"name":"Frontend Development","slug":"frontend-development","short_name":"Frontend","order":10,"state":"published","category_slug":"courses_frontend-development"},"categories":[{"id":16,"name":"Success stories","slug":"success","state":"published","created_at":"2022-03-25T13:12:03.087Z"},{"id":17,"name":"Student diary","slug":"student-diary","state":"published","created_at":"2022-03-25T13:12:41.336Z"},{"id":9,"name":"Code","slug":"code","state":"published","created_at":"2017-01-19T10:57:24.051Z"},{"id":8,"name":"Motivation","slug":"motivation","state":"published","created_at":"2017-01-19T10:56:28.412Z"}],"relatedLandings":[{"stack":{"id":66,"slug":"python","title":"Python Developer","audience":"for_beginners","start_type":"anytime","pricing_model":"subscription","priority":"low","kind":"profession","state":"unpublished","stack_state":"not_finished","order":null,"duration_in_months":10},"id":119,"slug":"python","title":"Python Developer","subtitle":"","subtitle_for_lists":null,"locale":"en","current":true,"duration_in_months_text":"10 months","stack_slug":"python","price_text":"from $49","duration_text":"10 months","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzUzMSwicHVyIjoiYmxvYl9pZCJ9fQ==--e7c47f31588724cdfecb95811018cb3a9412a51d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/python2.png"},{"stack":{"id":138,"slug":"http-api","title":"HTTP API","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":null,"duration_in_months":1},"id":235,"slug":"http-api","title":"HTTP API","subtitle":"API skills for development, testing, and integration to boost chances of moving into backend development","subtitle_for_lists":"","locale":"en","current":true,"duration_in_months_text":"1 month","stack_slug":"http-api","price_text":"from $49","duration_text":"1 month","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTIzNTYsInB1ciI6ImJsb2JfaWQifX0=--2d6024687d0f6c574953b46308b68acdb82ac5b1/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Application%20programming%20interface-bro.png"},{"stack":{"id":15,"slug":"frontend","title":"Frontend Developer","audience":"for_beginners","start_type":"anytime","pricing_model":"purchase","priority":"low","kind":"profession","state":"published","stack_state":"finished","order":1,"duration_in_months":10},"id":22,"slug":"frontend","title":"Frontend Developer","subtitle":"","subtitle_for_lists":"","locale":"en","current":true,"duration_in_months_text":"10 months","stack_slug":"frontend","price_text":"from $49","duration_text":"10 months","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTIzNTMsInB1ciI6ImJsb2JfaWQifX0=--f1948fb0c5a45a6d76671a1a32b8cf6f0b53ee57/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Software%20code%20testing-cuate.png"},{"stack":{"id":143,"slug":"cli-basics","title":"Command line basics","audience":"for_beginners","start_type":"anytime","pricing_model":"free","priority":"low","kind":"track","state":"published","stack_state":"finished","order":null,"duration_in_months":1},"id":240,"slug":"cli-basics","title":"Command line basics","subtitle":"","subtitle_for_lists":"","locale":"en","current":true,"duration_in_months_text":"1 month","stack_slug":"cli-basics","price_text":"Free","duration_text":"1 month","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTIzMzIsInB1ciI6ImJsb2JfaWQifX0=--187366d2e2ebf33e9a4a23f8115a40205a9e3551/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-bro.png"},{"stack":{"id":141,"slug":"web-development-free","title":"Fundamentals of Web Development","audience":"for_beginners","start_type":"anytime","pricing_model":"free","priority":"high","kind":"track","state":"published","stack_state":"finished","order":null,"duration_in_months":1},"id":238,"slug":"web-development-free","title":"Fundamentals of Web Development","subtitle":"","subtitle_for_lists":"","locale":"en","current":true,"duration_in_months_text":"1 month","stack_slug":"web-development-free","price_text":"Free","duration_text":"1 month","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTIzMzgsInB1ciI6ImJsb2JfaWQifX0=--f9e618ee974487da182c63e571fbaa8d1c186a80/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-bro.png"}]},"url":"/blog/posts/why-do-i-need-docker-and-how-do-i-use-it","version":"1656487db0d1dd5f33634fe1070e57e55135cbeb","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","author":"Kirill Mokevnin","name":"Why Do I Need Docker, and How Do I Use It?","datePublished":"2024-12-02T09:10:56.131Z","headline":"Docker is a program that allows the operating system to run processes in an isolated environment based on specially created images. And, while the technologies underlying Docker existed earlier, it was Docker that changed the way we now build project infrastructure, assemble and launch services.","image":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOlsxNDU2LDcyOF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--b2ecf6121248a24883cda19469732b47b57f4e80/docker.png","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0}]}</script><div style="--container-size:var(--container-size-lg);margin-top:var(--mantine-spacing-xl);height:100%" class="m_7485cace mantine-Container-root" data-size="lg" data-strategy="block"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"position":1,"@type":"ListItem","item":{"@id":"/blog","name":"Hexlet's blog"}},{"position":2,"@type":"ListItem","item":{"@id":"/blog/categories/code","name":"Code"}},{"position":3,"@type":"ListItem","item":{"@id":"/blog/posts/why-do-i-need-docker-and-how-do-i-use-it","name":"Why Do I Need Docker, and How Do I Use It?"}}]}</script><div style="margin-bottom:var(--mantine-spacing-xs)" class="m_8b3717df mantine-Breadcrumbs-root"><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/"><div style="color:inherit" class="m_4451eb3a mantine-Center-root"><svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-home-link "><path d="M20.085 11.085l-8.085 -8.085l-9 9h2v7a2 2 0 0 0 2 2h4.5"></path><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 1.807 1.143"></path><path d="M20 21a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M20 16a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M15 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M21 16l-5 3l5 2"></path></svg></div></a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/blog">Hexlet's blog</a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/blog/categories/code">Code</a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><p style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:var(--mantine-color-dimmed)" class="mantine-focus-auto m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root" data-size="sm">Why Do I Need Docker, and How Do I Use It?</p></div><style data-mantine-styles="inline">.__m__-_R_eub_{margin-bottom:var(--mantine-spacing-xs);}@media(min-width: 36em){.__m__-_R_eub_{margin-bottom:var(--mantine-spacing-xs);}}</style><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root __m__-_R_eub_"><style data-mantine-styles="inline">.__m__-_R_deub_{width:100%;}@media(min-width: 36em){.__m__-_R_deub_{width:70%;}}@media(min-width: 75em){.__m__-_R_deub_{width:75%;}}</style><div class="__m__-_R_deub_"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size)" class="m_8a5d1357 mantine-Title-root" data-order="1">Why Do I Need Docker, and How Do I Use It?</h1></div></div></div><style data-mantine-styles="inline">.__m__-_R_2iub_{--grid-gutter:var(--mantine-spacing-xl);}</style><div class="m_410352e9 mantine-Grid-root __m__-_R_2iub_"><div class="m_dee7bd2f mantine-Grid-inner"><style data-mantine-styles="inline">.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:100%;--col-max-width:100%;}@media(min-width: 48em){.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}@media(min-width: 62em){.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:66.66666666666667%;--col-max-width:66.66666666666667%;}}</style><div class="m_96bdd299 mantine-Grid-col __m__-_R_dmiub_"><div style="--stack-gap:var(--mantine-spacing-md);--stack-align:stretch;--stack-justify:flex-start;margin-bottom:var(--mantine-spacing-xl)" class="m_6d731127 mantine-Stack-root"><div class=""><div style="--group-gap:calc(0.625rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-sm);color:var(--mantine-color-gray-text)" class="m_4081bf90 mantine-Group-root"><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-inline-end:var(--mantine-spacing-lg)" class="m_4081bf90 mantine-Group-root">December 2, 2024</div><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-clock "><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"></path><path d="M12 7v5l3 3"></path></svg></div>19 minutes</div><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-thumb-up "><path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path></svg></div></div></div><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img style="--image-radius:var(--mantine-radius-md);--image-object-fit:cover;width:100%;height:100%" class="m_9e117634 mantine-Image-root" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mjk5LCJwdXIiOiJibG9iX2lkIn19--9b84aa0230fd40d408ada7222ee97f6398692470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOlsxNDU2LDcyOF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--b2ecf6121248a24883cda19469732b47b57f4e80/docker.png" alt="Why Do I Need Docker, and How Do I Use It?"/></div></div><div style="margin-bottom:var(--mantine-spacing-xl)" class="m_d08caa0 mantine-Typography-root"><p><strong>Docker is a program that allows the operating system to run processes in an isolated environment based on specially created images. And, while the technologies underlying Docker existed earlier, it was Docker that changed the way we now build project infrastructure, assemble and launch services.</strong></p>
<p><em>(Some details were purposefully left out of the article to avoid unnecessary information).</em></p>
<h2 id="heading-2-1">Contents</h2>
<ul>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-2">Installation</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-3">Launch</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-4">What's all this for?</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-5">Application in a container</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-6">Working with images</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-7">Container management</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-8">Interaction between the components of a system</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-13">Preparing your own docker image</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-23">Docker Compose</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-24">In action</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-25">Docker: what’s under the hood?</a></li>
</ul>
<h2 id="heading-2-2">Installation</h2>
<p>To start with Docker, you must first install the Docker Engine. Download links for all popular platforms are provided at <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://docs.docker.com/engine/install/" rel="noopener noreferrer" target="_blank">https://docs.docker.com/engine/install/</a>. Select yours and install Docker.</p>
<p>There's one thing to remember when installing Docker on Mac and Linux. By default, Docker runs through a non-networked UNIX socket. For security reasons, the socket is blocked for users who are not members of the docker group. Although the installer automatically adds the current user to this group, Docker will not work straight away. In reality, changing the group by the user has no effect till the user logs in again. This is a feature of the core. Enter the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">id</code> command to see which groups your user belongs to.</p>
<p>You can check if the installation was successful with the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker info</code> command:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker info
Containers: 22
Running: 2
Paused: 0
Stopped: 20
Images: 72
Server Version: 17.12.0-ce
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
...</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>It provides quite a lot of information about Docker configurations as well as work statistics.</p>
<h2 id="heading-2-3">Launch</h2>
<p>At this stage, the execution commands are given with no explanation. Later, more information on how they're formed and what include will be given.</p>
<p>Let's start with the easiest one:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker run -it nginx bash
root@a6c26812d23b:/#</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>This command will begin downloading the <em>nginx</em> image on the initial call, so you will have to wait a few moments. After the image is downloaded, <em>bash</em> will run, and you will find yourself <strong>in a container</strong>.</p>
<p>Go through the file system, and look at the <em>/etc/nginx</em>. As you can see, its content does not match what you have on your system. This file system was generated by the <em>nginx</em> image. Everything you do within it will not affect your main file system. With the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">exit</code> command, you can get back to your native system.</p>
<p>Now, let's call the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">cat</code> command, which is already in another container but is also launched from the <em>nginx</em> image:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker run nginx cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
...
$</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>The command is executed almost instantly since the image has already been uploaded. Unlike before, when a bash script and an interactive session were launched in the container, the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">cat /etc/nginx/nginx.conf</code> command for the <em>nginx</em> image will display the contents of the specified file (taken from the running container's file system) and return control to where you were before. You won’t end up inside the container.</p>
<p>The last launch option will be:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"># Note that no command is specified after the image name
# This approach works if the launch command is written in the image
$ docker run -p 8080:80 nginx</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>This command does not restore control, because Nginx starts running. Enter <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">localhost:8080</code> into your browser. You'll see that the <em>Welcome to nginx!</em> page has loaded!. If you return to the terminal where the container was launched, you'll see a log of requests to <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">localhost:8080</code>. You may stop nginx by pressing <kbd>Ctrl + C</kbd>.</p>
<p>Although each launch was different and resulted in various outcomes, the overall pattern of their work is consistent. Docker downloads the image automatically (the first argument after <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code>) and, if necessary, runs the container with the specified command.</p>
<p><strong>An image is an independent file system</strong>. For now, we use ready-made images, but we will eventually learn how to create them ourselves.</p>
<p><strong>A container is a running process of the operating system in an isolated environment</strong> with an image file system.</p>
<p>A container is, once again, just a usual operating system process. The main difference is that Docker starts the process in an isolated environment due to the kernel’s capabilities (details in the end). The container sees its own process list, network, file system, and so forth. Unless otherwise specified, it cannot interact with your main operating system or anything stored or launched there.</p>
<p>Try running the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -it ubuntu bash</code> command and typing <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ps auxf</code> inside the running container. The output will be as follows:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.1 18240 3300 pts/0 Ss 15:39 0:00 /bin/bash
root 12 0.0 0.1 34424 2808 pts/0 R+ 15:40 0:00 ps aux</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>There are only two processes, and the process ID of Bash is 1. To ensure that the <em>/home</em> directory is empty, you may use the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ls /home</code> command. Also, check out that the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">root</code> user is the default one inside the container.</p>
<h2 id="heading-2-4">What's all this for?</h2>
<p><strong>Docker is a general-purpose tool for delivering applications to machines (local computers or remote servers) and running them in an isolated environment.</strong></p>
<p>Remember the process when you had to build programs from the source. It’s likely to consist of the following steps:</p>
<ul>
<li>Install all of the operating system dependencies (finding them is quite a task)</li>
<li>Download the archive, unpack it</li>
<li>Start configuration <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make configure</code></li>
<li>Start compilation <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make compile</code></li>
<li>Install <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">make install</code></li>
</ul>
<p>As you can see, the process is not always straightforward or quick, and in certain cases, it may even be impossible because of cryptic issues (not to mention the corruption of the operating system).</p>
<p>Docker simplifies this method by allowing you to run a single command with a near-perfect success rate. Look at a fictional example in which the Tunnel program is installed on a local computer in the <em>/usr/local/bin</em> directory. It uses the <em>tunnel</em> image:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">docker run -v /usr/local/bin:/out tunnel</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>This command moves the program's executable file from the <em>tunnel</em> image in the <em>/usr/local/bin</em> directory on the main system. The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code> command launches a container from the <em>tunnel</em> image. The program is compiled locally and eventually ends up in the <em>/usr/local/bin</em> directory of the main file system. You may now run the program by typing <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">tunnel</code> in the terminal.</p>
<p>What if the program we are installing has dependencies? The secret is that the container was launched from a fully equipped image. It includes all necessary dependencies and ensures approximately 100% operability regardless of the status of the main OS.</p>
<p>It is not always necessary to copy a program from a container to your main system. It is enough to launch the container only when you need it. Assume we decided to create a static website using Jekyll. Jekyll is a popular static website generator written in Ruby, and even this guide is generated using it and Docker as well.</p>
<p>Earlier you have had to install at least Ruby and Jekyll itself as a gem on your main system (gem is the name of packages in Ruby). Furthermore, as with all such things, Jekyll only works with particular versions of Ruby, which leads to its own configuration issues.</p>
<p>Running Jekyll with Docker is reduced to just a single command executed in the blog directory (you can examine our guides <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/hexletguides/hexletguides.github.io" rel="noopener noreferrer" target="_blank">repository</a>):</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">docker run --rm --volume="$PWD:/srv/jekyll" -it jekyll/jekyll jekyll server</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>In the same way, a huge number of different software products are being launched at the moment. The further away, the more this approach takes over the world. Now, we dip into the origin of the name Docker.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://imgur.com/0rmuluv.png" alt="docker logo" loading="lazy"/></p>
<p>As you know, ships are the leading way to distribute goods around the world. Previously, the cost of transportation was high, because each cargo had its own shape and type of material.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://imgur.com/QCtxxni.png" alt="loading cargo onto a ship" loading="lazy"/></p>
<p>Loading a bag of fish or a car onto a ship are different tasks requiring different processes and tools. There were problems with loading methods that required a variety of cranes and tools. And, given the ship's fragility, securely loading cargo on board was a challenging procedure.</p>
<p>But at some point, everything changed. The following picture says more than a thousand words:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://imgur.com/aFZOWsy.png" alt="shipping container terminal" loading="lazy"/></p>
<p>Containers have equalized all types of cargo and standardized loading and unloading tools around the world, which in turn led to a simplification of processes, acceleration, and, therefore, lower costs.</p>
<p>The same thing happened in software development. Docker has become a universal software delivery tool, regardless of its structure, dependencies, or installation method. All that is required for programs distributed via Docker is that they are contained within the image and do not interact with the main system or other containers. The importance of this fact cannot be overestimated. Now, updating software versions does not involve either the system itself or other programs. Nothing can break anymore. All you need to do is download a new image of the program you want to update. In other words, Docker removed the <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Dependency_hell" rel="noopener noreferrer" target="_blank">dependency hell</a> problem and made the infrastructure <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://martinfowler.com/bliki/ImmutableServer.html" rel="noopener noreferrer" target="_blank">immutable</a>.</p>
<p>Given the existence of numerous configuration tools (chef, puppet, ansible) prior to the Docker era, server management was somewhat daunting. The main cause of all problems was the mutable state. Programs are installed, updated, and deleted. This happens at different times and in somewhat different ways on different servers. Updating the version of a language like PHP, Ruby, or Python, for example, could be quite an adventure with a total loss of workability. It's much easier to put a new server next to it and switch to it. In theory, Docker allows you to make such a switch. Because each running container exists in its own environment, it is best to forget about the old one and replace it with a new one. Moreover, the rollback in such a system is pretty superficial: all you need is to stop the new container and rebuild the old one using the previous image.</p>
<h2 id="heading-2-5">Application in a container</h2>
<p>Let’s now discuss how an application corresponds to a container. There are two possible approaches:</p>
<ol>
<li>The entire application is a container, inside which a process tree unfolds: an application, a web server, a database, and so on</li>
<li>Every running container is an atomic service. In other words, each container, whether a web server or an application, is a single program</li>
</ol>
<p>In fact, only the second approach provides access to all of Docker's advantages. Firstly, services are usually split over numerous servers and moved between them (for example, when a server crashes). Secondly, updating one service shouldn't lead to stopping the others.</p>
<p>The first approach is extremely rare, yet sometimes required. Hexlet, for example, works in two modes. The website and its services follow the second model, in which each service is distinct, but for practical exercises performed in a browser, we follow the principle of "one user — one container.” Depending on the exercise, anything can be placed inside the container. At least, the Hexlet IDE itself always starts there, and it in turn generates terminals (processes). A database is launched in the same container in our database course, and a web server is launched in a web-related course. This method creates the illusion of working on a genuine machine and makes it simpler to maintain work with exercises. Just to be clear, you probably won't require our use case, which is highly unique.</p>
<p>Another important aspect when working with containers relates to the state. For example, if the database is launched in a container, then its data should never be stored within the container. A container can be easily destroyed because it is an operating system process, and its existence is never permanent. Docker includes tools for storing and accessing data from the main file system. We'll get to them later.</p>
<h2 id="heading-2-6">Working with images</h2>
<p>Docker is more than just a program. This is an entire ecosystem with many projects and services. The main service you will have to work with is Registry, an image storage.</p>
<p>Generally, it works the same way as the package repository of any package manager. You can view its contents on the website <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://hub.docker.com/" rel="noopener noreferrer" target="_blank">https://hub.docker.com/</a>, by clicking Explore.</p>
<p>When we run the <em>run</em> <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run <image name></code> command, Docker checks for the presence of the specified image on the local machine and downloads it if necessary. The list of images already downloaded to your computer can be displayed with the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker images</code> command:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
workshopdevops_web latest cfd7771b4b3a 2 days ago 817MB
hexletbasics_app latest 8e34a5f631ea 2 days ago 1.3GB
mokevnin/rails latest 96487c602a9b 2 days ago 743MB
ubuntu latest 2a4cca5ac898 3 days ago 111MB
Ruby 2.4 713da53688a6 3 weeks ago 687MB
Ruby 2.5 4c7885e3f2bb 3 weeks ago 881MB
nginx latest 3f8a4339aadd 3 weeks ago 108MB
elixir latest 93617745963c 4 weeks ago 889MB
postgres latest ec61d13c8566 5 weeks ago 287MB</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Let's figure out how the image name is formed, and what it includes.</p>
<p>The second column in the output above is called TAG. The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run nginx</code> command is, in fact, a shortcut for the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run nginx:latest</code> which was executed. That is, we do not just download the <em>nginx</em> image, but its particular version. The latest is the default tag. It's easy to guess that it means the latest version of the image.</p>
<p>It is essential to understand that this is just an agreement, not a rule. A specific image may or may not have the <em>latest</em> tag, but it will not contain the most recent changes just because no one publishes them. ВPopular images, on the other hand, follow the agreement. As it is clear from the context, tags in Docker are changeable; that is, no one guarantees that downloading an image with the same tag on different computers at different times will result in the same thing. This approach may seem strange and unreliable because there are no guarantees, but in practice, there are certain agreements that all popular images follow. The latest tag always contains the most recent version and is constantly updated, but <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://semver.org/" rel="noopener noreferrer" target="_blank">semantic versioning</a> is also actively used. Let’s have a look at <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://hub.docker.com/_/nginx" rel="noopener noreferrer" target="_blank">https://hub.docker.com/_/nginx</a></p>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">1.13.8, mainline, 1, 1.13, latest
1.13.8-perl, mainline-perl, 1-perl, 1.13-perl, perl
1.13.8-alpine, mainline-alpine, 1-alpine, 1.13-alpine, alpine
1.13.8-alpine-perl, mainline-alpine-perl, 1-alpine-perl, 1.13-alpine-perl, alpine-perl
1.12.2, stable, 1.12
1.12.2-perl, stable-perl, 1.12-perl
1.12.2-alpine, stable-alpine, 1.12-alpine
1.12.2-alpine-perl, stable-alpine-perl, 1.12-alpine-perl</code>
<p>Tags with the complete semantic version (x.x.x) are always immutable, even if they contain something else, such as <em>1.12.2-alphine</em>. This version is safe to use in the production environment. When the path version changes, tags such as <em>1.12</em> are updated. That is, there may be version <em>1.12.2</em> inside the image, followed by <em>1.12.8</em> in the future. The same applies to versions that merely specify the major version, for example, <em>1</em>. Only in this case, the update is not only on the patch but also on the minor version.</p>
<p>As you may recall, the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code> command downloads the image if it is not available locally, but this check has nothing to do with updating the content. In short, if the <em>nginx:latest</em> has been updated, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code> will not download it; instead, it will use the <em>latest</em>, that is already downloaded. Another command for guaranteed image updates is <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker pull</code>. It always checks whether the image has been updated for a certain tag.</p>
<p>Along with tags, the image name can include a prefix, such as <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">etsy/chef</code>. This prefix is the service account name that is used to create images for the Registry. The majority of the images have a prefix, but there’s still a small set, literally a hundred images, with no prefix at all. These images are special since they are supported by Docker itself. Therefore, if there is no prefix in the image name, this is an official image. You can check their list: <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/docker-library/official-images/tree/master/library" rel="noopener noreferrer" target="_blank">https://github.com/docker-library/official-images/tree/master/library</a></p>
<p>Images can be deleted with the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker rmi <imagename></code> command.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker rmi Ruby:2.4
Untagged: Ruby:2.4
Untagged: Ruby@sha256:d973c59b89f3c5c9bb330e3350ef8c529753ba9004dcd1bfbcaa4e9c0acb0c82</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>If Docker has at least one container from the removed image, then it will not allow to delete it for obvious reasons. If you still want to delete both the image and all containers associated with it, use the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-f</code> flag.</p>
<h2 id="heading-2-7">Container management</h2>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://imgur.com/HDtynCF.png" alt="Docker Container LifeCycle" loading="lazy"/></p>
<p>The picture describes the life cycle (finite state machine) of the container. The circles depict the states, the console commands are highlighted in bold, and the squares show what is actually being executed.</p>
<p>Follow the path of the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code> command. This single command performs two actions in Docker: creating a container and starting it. There are more complex execution scenarios, but in this section, we will consider only the basics.</p>
<p>Let’s run nginx so that it works in the background. To do this, we will add the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-d</code> flag after the word <em>run</em>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker run -d -p 8080:80 nginx
431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>After executing the command, Docker return control and outputs the container ID. Make sure that nginx is working by opening <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">localhost:8080</code> in browser. Unlike the previous launch, our nginx works in the background, which means its output (logs) is not visible. You can view it using the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker logs</code> command, which requires the container ID:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker logs 431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e
172.17.0.1 - - [19/Jan/2018:07:38:55 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" "-"</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>You can also open a log pager using <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">tail -f</code>. To do this, run <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker logs -f 431a3b3fc24bf8440efe2bca5bbb837944d5ae5c3b23b9b33a5575cb3566444e</code>. The log will now be updated every time you refresh the page in the browser. You can exit this mode by pressing <kbd>Ctrl + C</kbd>, but the container will not stop.</p>
<p>Now we will output information about the running containers with the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker ps</code> command:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
431a3b3fc24b nginx "nginx -g 'daemon of…" 2 minutes ago Up 2 minutes 80/tcp wizardly_rosalind</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Columns description:</p>
<ul>
<li>CONTAINER_ID — container identifier. Just like Git, Docker uses an abbreviated hash entry</li>
<li>IMAGE — the name of the image that is used to create this container. If no tag is specified, then the latest is implied <em>latest</em> is implied</li>
<li>COMMAND — a command that was executed at the start of the container</li>
<li>CREATED — container creation time</li>
<li>STATUS — current state</li>
<li>PORTS — port forwarding</li>
<li>NAMES — unique names. In addition to the identifier, Docker allows you to specify a name. It's much easier to handle the container this way. If the name is not provided when creating the container, Docker will generate a random one. Like the one, Nginx has in the output above</li>
</ul>
<p>(<em>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker stats</code> command gives information about running containers’ resource consumption).</em></p>
<p>Let's try to stop the container now. Run the following command:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"># Instead of CONTAINER_ID, you can specify a name
$ docker kill 431a3b3fc24b # docker kill wizardly_rosalind
431a3b3fc24b</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>If you type <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker ps</code>, you won’t see this container, it has been stoped.</p>
<p>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker ps</code> command outputs only running containers. However, there may be some stopped ones as well. Moreover, they can stop both upon successful completion and in case of errors. Try typing <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run ubuntu ls</code>, followed by <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run ubuntu bash -c "unknown"</code>. These commands do not initiate a long-running process. Instead, the first one terminates immediately after execution, and the second with an error, because such a command does not exist.</p>
<p>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker ps -a</code> command will now output all containers. The first three lines of output will be:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
85fb81250406 ubuntu "bash -c unkown" Less than a second ago Exited (127) 3 seconds ago loving_bose
c379040bce42 ubuntu "ls" Less than a second ago Exited (0) 9 seconds ago determined_thatchar</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Here are our two most recent launches. If you look at the STATUS column, you can see that both containers are in the Exited state. That is, the running command inside them was executed, and they stopped. The only difference is that one completed successfully (0), and the other did not (127). Even after it has been stopped, the container can be restarted:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">docker start determined_thatchar # In your case, there will be another name</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Only this time you won't see the output. Use the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker logs determined_thatchar</code> command to see it.</p>
<h2 id="heading-2-8">Interaction between the components of a system</h2>
<p>Running an isolated container that exists entirely within itself is useless. In general, the container must interact with the outside world, accept incoming requests to a specific port, execute requests to other services, read shared files, and write to them. When you create a container, you can configure all of these features.</p>
<h3 id="heading-3-9">Interactive mode</h3>
<p>As shown, the simplest way to use Docker is to create a container and execute some commands inside it:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker run ubuntu ls /usr
bin
games
include
lib
local
sbin
share
src
$</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Docker returns control after executing the command, and we are no longer inside the container. If we try to run bash in the same way, we will not get what we expected:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker run ubuntu bash
$</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>The thing is that <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">bash</code> starts an interactive session within the container. To interact with it, keep the STDIN stream open and the TTY running (pseudo-terminal). Therefore, to start interactive sessions, make sure to include the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-i</code> and <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-t</code> options. They are typically added together as <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-it</code>. So, the correct way to start bash is <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -it ubuntu bash</code>.</p>
<h3 id="heading-3-10">Ports</h3>
<p>If you run Nginx usint the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run nginx</code>command, it will be unable to accept any request, even though it listens to port <em>80</em> inside the container (let me remind you that each container exists in its own network by default). But if you run it like this: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -p 8080:80 nginx</code>, then nginx will start responding on port <em>8080</em>.</p>
<p>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-p</code> flag specifies how and which ports should be exposed to the outside. The format <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">8080:80</code> stands for “expose port <em>8080</em> inside the container to port <em>80</em> outside the container”. Furthermore, port <em>8080</em> s by default listened to on <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">0.0.0.0</code>, that is, on all available interfaces. As a result, the container launched this way is accessible not only via <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">localhost:8080</code> but also from outside the machine (if access is not denied somehow else). If you only need to do a <em>loopback</em>, the command is <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -p 127.0.0.1:8080:80 nginx</code>.</p>
<p>Docker allows you to forward an unlimited number of ports. In the case of Nginx, for example, it is frequently necessary to use both ports <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">80</code> and <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">443</code> for HTTPS. You can do it this way: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -p 80:80 -p 443:443 nginx</code>. You can read about the other ways to forward ports in the official documentation.</p>
<h3 id="heading-3-11">Volumes</h3>
<p>Accessing the main file system is another common task. When launching nginx container, for example, you may provide the configuration that is stored on the main fs (file system). Docker will forward it in the internal fs for Nginx to read and use.</p>
<p>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-v</code>option is used for forwarding. Here's how to start a bash session from an Ubuntu image by connecting the command history from the main file system: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -it -v ~/.bash_history:/root/.bash_history ubuntu bash</code>. The history will be displayed if you press the up arrow in the open bash-shell. You can forward both files and directories. Any changes made inside the volume have an effect both inside and outside the container, and all operations are available by default. The number of files and directories that can be forwarded is unlimited, just like the number of ports.</p>
<p>There are some key rules to remember when working with Volumes:</p>
<ul>
<li>The path to the file in the external system must be absolute</li>
<li>If the inner path (what follows <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">:</code>) does not exist, Docker will create all the necessary directories and files. If it does, Docker will replace the old one with the one that was forwarded</li>
</ul>
<p>Docker provides various other options for building and using Volumes, in addition to forwarding a part of the fs outside. Read the official documentation for further details.</p>
<h3 id="heading-3-12">Environment variables</h3>
<p>The application in a container is often configured via environment variables following <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://12factor.net" rel="noopener noreferrer" target="_blank">12factors</a>. There are two ways of installing them:</p>
<ul>
<li>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-e</code> flag is used as follows: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run -it -e "HOME=/tmp" ubuntu bash</code></li>
<li>The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">--env-file</code> option inserts a special file containing environment variable definitions into the</li>
</ul>
<h2 id="heading-2-13">Preparing your own docker image</h2>
<p>Creating and publishing your own image is as simple as using it. The whole process is divided into three steps:</p>
<ul>
<li>Create <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">Dockerfile</code> in the project's root. The process of creating an image should be described inside</li>
<li>Build the image using the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker build</code> command</li>
<li>Publish the image to Registry with the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker push</code> command</li>
</ul>
<p>Let’s consider the image creation process by the example of packaging the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">eslint</code> linter (do not forget to repeat it by yourself). As a result, we will have an image that can be used as follows:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker run -it -v /path/to/js/files:/app my_account_name/eslint
/app/index.js
3:6 error Parsing error: Unexpected token
1 | import path from 'path';
2 |
> 3 | path(;)
| ^
4 |
✖ 1 problem (1 error, 0 warnings)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>So, simply run the container from this image by attaching the directory with the js files to the internal directory <em>/app</em> as Volume.</p>
<h3 id="heading-3-14">1. The following is the final directory structure with the files from which the image will be constructed:</h3>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">eslint-docker/
Dockerfile
eslintrc.yml</code>
<p>The <em>eslintrc.yml</em> file contains the linter configuration. It is read automatically if it’s in the home directory and named <em>.eslintrc.yml</em>. That is, this file should be placed in the image's <em>/root</em> directory under this name.</p>
<h3 id="heading-3-15">2. Dockerfile</h3>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight"># Dockerfile
FROM node:9.3
WORKDIR /usr/src
RUN npm install -g eslint babel-eslint
RUN npm install -g eslint-config-airbnb-base eslint-plugin-import
COPY eslintrc.yml /root/.eslintrc.yml
CMD ["eslint", "/app"]</code>
<p>Dockerfile has quite a simple format. An instruction (directive) and its description are stated on each line.</p>
<h4 id="heading-4-16">FROM</h4>
<p>The Dockerfile FROM instruction is required to set the base image to inherit from. It is important to note here that the images are based on each other and together form a big tree.</p>
<p>The <em>busybox</em> image can be found at the root of this tree. Because Docker provides ready-to-use images for each ecosystem and stack, it is not directly used in applied tasks.</p>
<h4 id="heading-4-17">RUN</h4>
<p>The main Dockerfile instruction. In fact, we will state the <em>sh</em> command that will be run within the environment here. When creating an image, it is typically specified in the FROM instruction. Since by default everything is executed from the <em>root</em> user, there is no need to <em>sudo</em> (and most likely it is not in the base image). Also, keep in mind that building an image is not an interactive process. When using a command that potentially requires something from the user, it is necessary to hide its output. In the case of package managers, for example, they do the following: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">apt-get install -y curl</code>. The <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-y</code> flag indicates that installation should proceed without additional questions.</p>
<p>A Docker image is technically a set of so-called layers rather than a single file. Each RUN call creates a new layer, which can be viewed as a collection of files generated and updated (including deletion) by the RUN command. By caching unchanged layers, you can significantly improve system performance. On the other hand, Docker reuses layers in different images if they are identical, reducing both download speed and disk space occupied. The subject of layers cache is quite important when extensively using Docker. To get the most of it, you must first grasp how it works and how to precisely define the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">RUN</code> instructions.</p>
<h4 id="heading-4-18">COPY</h4>
<p>The COPY instruction, as the name implies, copies a file or directory from the main fs to the image. But the transferred files should be in the same directory as the Dockerfile. We generally use this instruction to pack an application in an image.</p>
<h4 id="heading-4-19">WORKDIR</h4>
<p>Sets the working directory. All subsequent instructions will be assumed to be executed from it. By default, the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">WORKDIR</code> instruction acts like a <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">cd</code> command. Besides, when we launch the container, it also starts from the working directory. For example, by running bash, you will end up inside it.</p>
<h4 id="heading-4-20">CMD</h4>
<p>The same instruction that defines the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code> default action. It is only used if the container was launched without a command; otherwise, it is ignored.</p>
<h3 id="heading-3-21">3. Building</h3>
<p>To create a docker image, use the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker build</code> command. Use <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">-t</code> flag to provide the image name, including the account name and tag. As usual, if you do not specify the tag, then the <em>latest</em> will be set.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker build -t my_account_name/eslint .</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>After running this command, the current image will be listed in the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker images</code>. You can even start using it, there is no need to publish it to Registry. Let me remind you that the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker run</code> command does not search for an updated version of the image if an image with the same name and tag already exist locally.</p>
<h3 id="heading-3-22">4. Publishing</h3>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$ docker push my_account_name/eslint</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>To complete the publishing, you must meet two conditions:</p>
<ul>
<li>Register on Docker Cloud and create a repository for the image there</li>
<li>Log in to the CLI interface using the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker login</code> command.</li>
</ul>
<h2 id="heading-2-23">Docker Compose</h2>
<p>Docker Compose is a tool that allows you to develop a project locally using Docker. It is similar to Vagrant in its purpose.</p>
<p>Docker Compose also helps to manage a set of containers, each of which is a separate project service. Management includes building, dependency-aware launch, and configuration. Docker Compose configuration is specified in the <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">docker-compose.yml</code> file at the project roo. It looks as follows:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"># https://github.com/hexlet-basics/hexlet_basics
version: '3.3'
services:
db:
image: postgres
app:
build:
context: services/app
dockerfile: Dockerfile
command: mix phx.server
ports:
- "${PORT}:${PORT}"
env_file: '.env'
volumes:
- "./services/app:/app:cached"
- "~/.bash_history:/root/.bash_history:cached"
- ".bashrc:/root/.bashrc:cached"
- "/var/tmp:/var/tmp:cached"
- "/tmp:/tmp:cached"
depends_on:
- db</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<h2 id="heading-2-24">In action</h2>
<p>Setting up project machines with Docker usually comes down to installing Docker. Then you just have to deploy. The basic process is:</p>
<ol>
<li>Download a new image</li>
<li>Stop the old container</li>
<li>Build a new one</li>
</ol>
<p>This process does not depend on the technology stack. You can deploy (as well as configure machines) using <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://docs.ansible.com/ansible/2.5/modules/docker_container_module.html" rel="noopener noreferrer" target="_blank">Ansible</a>.</p>
<p>Another option, suitable for more complex projects, is to use special orchestration tools such as <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://kubernetes.io/" rel="noopener noreferrer" target="_blank">Kubernetes</a>. It generally requires extensive preparation, including an understanding of how distributed systems operate.</p>
<h2 id="heading-2-25">Docker: what’s under the hood?</h2>
<p>Docker's isolation is provided by the <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Cgroups" rel="noopener noreferrer" target="_blank">Cgroups</a> and <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Linux_namespaces" rel="noopener noreferrer" target="_blank">Namespaces</a> kernel features. They enable you to run an operating system process not just in an isolated environment, but also with limited use of hardware resources (RAM or CPU).</p></div><div class=""><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-lg)" class="m_4081bf90 mantine-Group-root"><div class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:var(--mantine-spacing-xs);color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-user "><path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"></path><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"></path></svg></div><p style="margin-inline-end:var(--mantine-spacing-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Kirill Mokevnin</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">a year ago</p></div><div style="align-items:center" class="m_8bffd616 mantine-Flex-root __m__-_R_5dirddmiub_"><button style="display:inline-flex" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" type="button"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:var(--mantine-spacing-xs);color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-thumb-up "><path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path></svg></div></button><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0</p></div></div></div><div style="background-color:var(--mantine-color-indigo-light);border:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding:var(--mantine-spacing-xl)" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Read more:</p><ul style="margin-inline-start:var(--mantine-spacing-lg)" class="m_abbac491 mantine-List-root"><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/what-is-deployment">What is Deployment?</a></span></div></li><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/code-complete-sneaky-one-liners">Code Complete: Sneaky One-Liners</a></span></div></li><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/code-complete-using-a-library-or-writing-your-own-code">Code Complete: Using a Library or Writing Your Own Code</a></span></div></li></ul></div><div style="margin-block:var(--mantine-spacing-xl)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div></div><div></div></div><style data-mantine-styles="inline">.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:100%;--col-max-width:100%;}@media(min-width: 48em){.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}@media(min-width: 62em){.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:33.333333333333336%;--col-max-width:33.333333333333336%;}}</style><div class="m_96bdd299 mantine-Grid-col __m__-_R_lmiub_ mantine-visible-from-md"><div style="background-color:var(--mantine-color-indigo-light);border:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-bottom:var(--mantine-spacing-xl);padding:var(--mantine-spacing-xl);width:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div style="margin-bottom:var(--mantine-spacing-md)" class="m_4451eb3a mantine-Center-root" data-inline="true"><p style="font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Categories</p></div><ul class="m_abbac491 mantine-List-root"><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Success stories">Success stories</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Student diary">Student diary</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Code">Code</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Motivation">Motivation</button></span></div></li></ul></div><div style="justify-content:end;margin-top:0rem;position:sticky;top:calc(5rem * var(--mantine-scale))" class="m_8bffd616 mantine-Flex-root __m__-_R_5dlmiub_"><div tabindex="0" style="cursor:pointer"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses_frontend-development?promo_name=program_category&promo_position=blog_post&promo_creative=card&promo_type=card"><div style="background-color:var(--mantine-color-default);border:calc(0.0625rem * var(--mantine-scale)) solid var(--mantine-color-default-border);padding-inline:var(--mantine-spacing-xl);padding-top:var(--mantine-spacing-xl);padding-bottom:var(--mantine-spacing-xs);width:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div class="m_4451eb3a mantine-Center-root" data-inline="true"><p style="font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Frontend Development</p></div><img class="m_9e117634 mantine-Image-root" src="/vite/assets/development-BVihs_d5.png"/><p style="margin-bottom:var(--mantine-spacing-xs);text-align:right" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Explore →</p></div></a></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">About Hexlet</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">About us</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io/category/4316" data-target="_blank" role="button">Help</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Site Map</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Learn</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend-development">Backend
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_frontend-development">Frontend
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_python">Python
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Testing
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5"><span class="translation_missing" title="translation missing: en.layouts.footer_content.popular_courses_for_beginners">Popular Courses For Beginners</span></div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Frontend Developer</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5"><span class="translation_missing" title="translation missing: en.layouts.footer_content.popular_courses_for_advanced">Popular Courses For Advanced</span></div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/web-development-free">Fundamentals of Web Development</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-development">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/css-animation">CSS Animation</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/http-api">HTTP API</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/layout-designer-positioning">Position CSS</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/cli-basics">Command line basics</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/git-basics-free">Git fundamentals</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Facebook" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.facebook.com/hexlethq"><span class="bi bi-facebook"></span>
</a></li>
<li class="me-3">
<a aria-label="Instagram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.instagram.com/hello_hexlet/"><span class="bi bi-instagram"></span>
</a></li>
<li>
<a aria-label="Twitter" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://twitter.com/Hexlet_IO"><span class="bi bi-twitter-x"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:%2B7%20717%20272%2076%2070">+7 717 272 76 70</a>
<span class="d-block opacity-50 small">free call</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Legal</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer"><span class="translation_missing" title="translation missing: en.layouts.footer_content.offer">Offer</span></a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts"><span class="translation_missing" title="translation missing: en.layouts.footer_content.contacts">Contacts</span></a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>TOO "Hexlet"</div>
<div>The Republic of Kazakhstan, Almaty</div>
<div>Auezova St., 14A</div>
<div>BIN 230340043714
</div>
</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-CIOaBlj-.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-0bhwJkNI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-V3hfk_CP.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DejNWqwz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-B-jV56Ol.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-DOJkeu70.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-DZwEN4Zo.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-ClTF9s_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-mvvEXZQ8.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-CJocDKTE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-DH3_MBnL.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-Cj65YiRw.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-Csl7vw8x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-DeAQqnBE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-CN66HKVH.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-DQgTDFJJ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-Bfba02I7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-BcxwEZ7X.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/prop-types-DGBR76ns.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-CYyKzrjQ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-SJZekO2j.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-bo78L81P.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-BhqaZ6vG.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-DlXMvSuQ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CFtMU8gd.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>