Absortio

Email → Summary → Bookmark → Email

The Definitive Guide To Docker in 2023

Extracto

This is the most comprehensive guide on Docker online.In this docker tutorial, you will learn docker from scratch to an advanced level starting from the concept of containerization to Docker Compose in Docker. You will learn how to dockerize a JavaScript application and how to use dockerize a full-stack application using Docker Compose.

Contenido

What is Docker Image?

A Docker image is a read-only multi-layered self-contained template with instructions to create a container. An image is a template used to create a container. Images can be created based on another image with some personalized customization for specific use cases.

The container image must contain everything needed to run an application - all dependencies, configurations, scripts, binaries, etc. The image also contains other configurations for the container, such as environment variables, a default command to run, and other metadata.

The Open Container Initiative (OCI) defined a standard specification for creating container images which means that images created for Docker will also work with Podman. Unlike in the early days when container engines had different image formats.

There are many images already published on the Docker registry or other registries that are publicly available for use based on the OCI standards.

You can choose from the list or create your own image from scratch as we will demonstrate further in this lesson.

Creating a Docker Image

Docker images are created using a Dockerfile, to build your own image, you create a `Dockerfile` with a simple syntax for defining the steps needed to create the image and run it. Here is an example of a simple `Dockerfile` to create an image:

FROM node:16.17.0-alpine

# Set working directory
WORKDIR /usr/src/app

# Install dependencies
COPY package*.json ./
RUN npm ci --production

# Copy app source code
COPY . .

# Build app
RUN npm run build --production

COPY ./.env ./build

# Expose port, you can expose any port your app uses here
EXPOSE 3333

# Start app
CMD ["node", "./build/server.js"]

The Dockerfile is self-explanatory due to the comments included in the file. Let’s go deeper into Docker commands used in the `Dockerfile`.

  1. FROM: specifies which image is being used to build this new Docker image

  2. RUN: used to run a command while building the Docker image

  3. WORKDIR: It creates a new folder inside the Docker image

  4. COPY: used to copy the source codes and other files into a folder specified inside the Docker image.

  5. EXPOSE: used to expose the port number of the Docker image to the outside (client) machine.

  6. CMD: used to set the default command to be executed when a container is run. It is usually used in conjunction with the ENTRYPOINT command to provide a default application to be run when the container is started.

This simple `Dockerfile` creates an image from a base image in this case `node:16.17.0-alpine` and set the working directory inside the container to `/usr/src/app` using the `WORKDIR` command, inside the `app` directory is where all the files the is copied using the command `COPY package*.json ./` will be stored.

Next, the image executes the `npm ci --production` using the `RUN` command inside the container, Next, we copy all the files from our local project directory to the container working directory.

Lastly, we execute the `run build` command, copy other files, expose the container port to the outside world, and start our server.

This was only a demonstration to show you how to use `Dockerfile` to create an image. Every command in the Dockerfile above has specific functionality and there are many more commands you can use to create your container images. You can explore all the commands in the official documentation.

Now that you have created a Dockerfile, it's of no use if you don’t build it to an image. Therefore, let’s look at some of the Docker Client commands for images:

Image Commands

Here are some of the popular commands you can use to manage images:

Build the Docker Image

Once you've created a Dockerfile, you can build the image using the `docker build` command where `image-name` is the user-defined name of the image.

docker build -t image-name .

Run the Docker Image

There are several ways to run your image using the Docker Run command. Below, we're going to explore a few ways to run your Docker image:

Run the Docker Image:

docker run image-name

You can run the Docker Image with specific parameters. For example, to run the Docker Image with parameters such as port with `-p`, the name with `--name`, and interactive mode with `-it`, use the following command:

docker run --name container-name -it -p 3333:3333 image-name

Below is a table showing all the commands you can use to explore and manage Docker container images.

docker image build

Build an image from a Dockerfile. It’s the same with `docker build` command

docker image history

Show the history of an image

docker image import

Import the contents from a tarball to create a filesystem image

docker image inspect

Display detailed information on one or more images

docker image load

Load an image from a tar archive or STDIN

docker image ls

List images

docker image prune

Remove unused images

docker image pull

Download an image from a registry

docker image push

Upload an image to a registry

docker image rm

Remove one or more images

docker image save

Save one or more images to a tar archive (streamed to STDOUT by default)

docker image tag

Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

Now that you have learned how to create your own image for your project, you can also create an image the same way and upload it to the Docker registry for others or your team members to use. Let’s explore what the Docker registry entails.

What is a Docker Registries?

An image registry serves as a centralized repository where you can upload your own images and download images created by others. Docker Hub, the default public registry for Docker, and Quay by Red Hat, another highly popular image registry.

You can go to Docker Hub and scan through to see the most popular image or some of the images for your favorite tools. When you are at the home page, click on the Explore menu to see all the images as shown below:

Docker registry

What are Docker Containers?

The concept of containerization gave birth to containers. Therefore, the concept of containers is very fundamental to the world of containerization.

A container is a runnable instance of an image. A docker container can be created, started, stopped, moved, or deleted using the Docker CLI Client or any other accepted clients. You can combine multiple containers to become one or attach one or more networks, storage, or create a new image based on the current state of the container.

If you know virtual machines, then you may consider containers to be equivalent to modern virtual machines.

Docker containers are completely isolated environments from the host machine as well as other containers. They are built to be a more lightweight, standalone, executable package of software that includes everything needed to run an application which is the codes, runtimes, system tools, system libraries, or settings.

Docker container.pngLet me work you through the diagram a little deeper to understand Docker containers from the ground up.

Docker containers are lightweight because they share the machine’s OS system kernel and therefore do not require an OS per application, driving higher server efficiencies and reducing server and licensing costs.

As you can see in the diagram above, each container relies on the Docker layer which in turn uses the resources from the machine’s OS.

This is one of the differences between virtual machines and Docker containers, and also a great benefit of using Docker containers because a virtual machine relies on individual Guest OS for each application as shown in the image below:

virtual machinesNow that we understand Docker containers, let’s look at how to manipulate and manage a Docker container in the next lesson.

How to Run a Container

When we learned how to run Docker images, we used the docker run command to achieve it. The command is also used to create and start a container by specifying the image name and some optional options as shown below:

docker run image-name

This command will create and start a container if the image name is specified correctly and it exists anywhere in your local machine or Docker registry.

However, Docker has updated this command and made it easy to understand thereby improving developer experiences. The structure of the new command syntax looks like this:

docker <object> <command> <options>

Where:

  • <object> is the type of Docker object you'll be manipulating. This can be a containerimagenetwork, or volume object.

  • <command> indicates the task to be carried out by the daemon, that is the run command.

  • <options> can be any valid parameter that can override the default behavior of the command, like the -publish option for port mapping.

Now the complete syntax to start and run a container from an image will look like this:

docker container run image-name

Let’s replace the image-name with something more practical, let’s run the nginx container as an example. To run a container using this image, execute the following command on your terminal:

docker container run --publish 8080:80 nginx

This will result in an output such as:

Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
3ae0c06b4d3a: Pull complete 
efe5035ea617: Pull complete 
a9b1bd25c37b: Pull complete 
f853dda6947e: Pull complete 
38f44e054f7b: Pull complete 
ed88a19ddb46: Pull complete 
495e6abbed48: Pull complete 
Digest: sha256:08bc36ad52474e528cc1ea3426b5e3f4bad8a130318e3140d6cfe29c8892c7ef
Status: Downloaded newer image for nginx:latest

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up

2023/07/06 12:51:31 [notice] 1#1: using the "epoll" event method
2023/07/06 12:51:31 [notice] 1#1: nginx/1.25.1
2023/07/06 12:51:31 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) 
2023/07/06 12:51:31 [notice] 1#1: OS: Linux 5.15.49-linuxkit
2023/07/06 12:51:31 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023/07/06 12:51:31 [notice] 1#1: start worker processes
2023/07/06 12:51:31 [notice] 1#1: start worker process 29
2023/07/06 12:51:31 [notice] 1#1: start worker process 30
2023/07/06 12:51:31 [notice] 1#1: start worker process 31
2023/07/06 12:51:31 [notice] 1#1: start worker process 32
2023/07/06 12:51:31 [notice] 1#1: start worker process 33

Also, notice that we have a new option added to the command, the --publish option. Let’s talk about that in the next section and other popular options you can use with your Docker container command.

How to Publish a Port

As you already know, Docker containers are isolated environments. Your host system (local machine) does not know anything that’s going on inside the container environment unless you expose it.

Therefore, one way to expose the running application inside your container is by exposing the port to your host system.

Let’s say, for example, you started an Express application inside your container on port 3000, your host machine will not know that port 3000 is running your Express application unless you explicitly expose it when you want to create the container using the command below:

docker container run --publish 3000:3000 my-express-app

When you write --publish 3000:3000, it meant any request sent to port 3000 of your host system will be forwarded to port 3000 inside the container‌.

The process is called port mapping, you can map your container port to a different and available port on your local machine as shown in this example:

docker container run --publish 4000:3000 my-express-app

In this case, if you visit localhost:3000, it will not work because your container port is mapped to 4000 on your local machine and it’s expecting traffic from localhost:4000.

Also, you can use a shorthand version of the command option as shown below which means the same thing:

docker container run -p 4000:3000 my-express-app

How to Use Detached Mode

Next, you can run your container commands in detached mode meaning that your command will run in the background and your terminal will be wide open for new commands.

Here’s a command and the option to do so:

docker container run  --detach  --publish 4000:3000 my-express-app

or the shorthand version:

docker container run -d -p 4000:3000 my-express-app

How to List Containers

The next command is the container ls command, which allows you to list all available containers in your local machine that are currently running.

If you have any container in your local machine running, it should show you a list of them as shown below:

docker container ls

# CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                  NAMES
# 9f21cb888058        my-express-app  "/docker-entrypoint.…"   5 seconds ago       Up 5 seconds        0.0.0.0:8080->80/tcp   upbeat_burn

To list all your containers including all states such as running, stopped, etc. Use the command shown in the example below:

docker container ls --all

 // or the short-hand version

docker container ls -a

CONTAINER ID   IMAGE            COMMAND                  CREATED            STATUS                     PORTS                  NAMES
9f21cb888058   my-express-app  "/docker-entrypoint.…"   5 seconds ago       Up 5 seconds               0.0.0.0:8080->80/tcp   upbeat_burn
ae9192b8e32d   node            "docker-entrypoint.s…"   9 minutes ago       Exited (0) 6 minutes ago                          upbeat_blackburn

A container named upbeat_burn is running. It was created 5 seconds ago and the status is Up 5 seconds, which indicates that the container has been running fine since its creation.

The CONTAINER ID is 9f21cb888058 which is the first 12 characters of the full container ID. The full container ID is 9f21cb88805810797c4b847dbd330d9c732ffddba14fb435470567a7a3f46cdc which is 64 characters long. This full container ID was printed as the output of the docker container run command in the previous section.

How to Name or Rename a Container

Every container has two identifiers by default which are:

  • CONTAINER ID - a random 64-character-long string.

  • NAME - a combination of two random words, joined with an underscore.

You can rename a container to your user-defined name which will be easy to refer to than using the two randomly generated identifiers.

You can rename a container using the --name option or -n for short. To run another container using the my-express-app image with the name my-express-app-container you can execute the following command:

docker container run --detach --publish 4000:3000 --name my-express-app-container my-express-app

A new container with the name my-express-app-container will be started.

You can even rename old containers using the container rename command. The syntax for the command is as follows:

docker container rename <container identifier> <new name>

Here's an example:

docker container rename my-express-app my-express-app-container-2

The command doesn't yield any output but you can verify that the changes have taken place using the container ls command. The rename command works for containers both in the running state and the stopped state.

How to Stop or Kill a Running Container

To stop a running container is easy. You can use the ctrl + c command to stop a running container on your terminal.

However, if you’re container is running in a detached mode, you need to use the following command to stop it.

Below is a generic syntax for the command is as follows:

docker container stop <container identifier>

Where the container identifier can either be the id or the name of the container. You can get the identifier with the docker container ls command. Next, here’s an example to stop a running container.

docker container stop my-express-app-container

# my-express-app-container

If you use the name as an identifier, you'll get the name thrown back to you as output. The stop command shuts down a container gracefully by sending a SIGTERM signal. If the container doesn't stop within a certain period, a SIGKILL signal is sent which shuts down the container immediately.

In cases where you want to send a SIGKILL signal instead of a SIGTERM signal, you may use the container kill command instead. The container kill command follows the same syntax as the stop command.

docker container kill my-express-app-container

How to Restart a Container

Restarting a container can happen in two ways:

  • Restarting the container from a failed, stopped, or killed state.

  • Rebooting a container from a running state.

You can start a container from a failed, stopped, or killed state using the command below:

docker container start my-express-app-container

Next, you can reboot a running container using the following command:

docker container restart my-express-app-container

The main difference between the two commands is that the container restart command attempts to stop the target container and then starts it back up again, whereas the start command just starts an already stopped container.

In the case of a stopped container, both commands are exactly the same. But in the case of a running container, you must use the container restart command.

How to Create a Container Without Running

Sometimes, you just want to create a container without running it. You can achieve this using the following command:

docker container create --publish 4000:3000 my-express-app-container

The STATUS of the container is Created at the moment, and, given that it's not running, it won't be listed without the use of the --all option in the docker container ls command.

The create command will create a new container and store it inside your local machine without running it. So that you can use the docker container start command to start the container next time without creating it again.

How to Remove Containers

Sometimes, you want to remain unused containers since all containers including stopped and killed containers still remain in your local system. Removing unused containers can save your system memory.

The following command is used to remove any container by passing in the identifier of the container you want to delete.

Here’s the syntax:

docker container rm <container identifier>

You can use the docker container ls -all command to get the identifier.

docker container rm 6cf52771dde1

# 6cf52771dde1

This command will delete the container whose identifier is specified as seen above.

In addition, you can use the --rm option to indicate a one-time container meaning that you want the container to be deleted immediately after they are stopped. You can use the --rm option in both start and run commands. Let’s look at this example with the run command.

docker container run --rm --detach --publish 5000:3000 --name my-express-app-container-one-time my-express-app-container

The my-express-app-container-one-time container will be deleted immediately after it is stopped or killed.

How to Run a Container in Interactive Mode

Some images come with different lightweight versions of operating systems such as Linux and different distributions such as Ubuntu, Fedora, Kali, etc. If you use any of these images that come with an operating system. You can interact with the inside of the operating system when running the container.

For instance, Programming languages such as pythonphpgo, or run-times like node and deno all have their official images.

These images do not just run some pre-configured program. They are instead configured to run a shell by default. In the case of the operating system images, it can be something like sh or bash and in the case of the programming languages or runtimes, it is usually their default language shell.

For example, if you are using the Ubuntu image, you can interact with the Ubuntu shell to install programs, navigate to folders or create a new file. In the same way, if you’re using a Node.js image, you might want to interact with the default language shell.

To achieve this, Docker provides the interactive --interactive or -it option which allows you to interact with the shell of your image when starting or running a container.

Here’s an example with an Ubuntu image:

docker container run --rm -it ubuntu

Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
5af00eab9784: Pull complete 
Digest: sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
Status: Downloaded newer image for ubuntu:latest

[email protected]:/# cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="<https://www.ubuntu.com/>"
SUPPORT_URL="<https://help.ubuntu.com/>"
BUG_REPORT_URL="<https://bugs.launchpad.net/ubuntu/>"
PRIVACY_POLICY_URL="<https://www.ubuntu.com/legal/terms-and-policies/privacy-policy>"
UBUNTU_CODENAME=jammy
[email protected]:/#

The -it option sets the stage for you to interact with any interactive program inside a container. This option is actually two separate options mashed together.

  • The i or -interactive option connects you to the input stream of the container so that you can send inputs to bash.

  • The t or -tty option makes sure that you get some good formatting and a native terminal-like experience by allocating a pseudo-tty.

You need to use the -it option whenever you want to run a container in interactive mode. Another example can be running the node image as follows:

docker container run -it node

Unable to find image 'node:latest' locally
latest: Pulling from library/node
42cbebf8bc11: Pull complete 
9a0518ec5756: Pull complete 
356172c718ac: Pull complete 
dddcd3ceb998: Pull complete 
abe47058ac42: Pull complete 
08ff2ee7b183: Pull complete 
70b6353bf75e: Pull complete 
2742b73156b1: Pull complete 
Digest: sha256:57391181388cd89ede79f371e09373824051eb0f708165dcd0965f18b3682f35
Status: Downloaded newer image for node:latest

Welcome to Node.js v20.3.1.
Type ".help" for more information.
> 5 + 5
10
>

How to Execute Commands Inside a Container

You can execute a command inside your container, assuming you want to execute a command that is not available in Windows operating while you’re using Windows at the moment.

You can easily spin up a container that uses a Linux operating system and execute the command immediately. You can even make it a one-time container as discussed above just to help you achieve a specific task in Linux.

For example, assume that you want to encode a string using the base64 program. This is something that's available in almost any Linux or Unix-based operating system (but not on Windows).

In this situation, you can quickly spin up a container using images like busybox and let it do the job.

The generic syntax for encoding a string using base64 is as follows:

echo -n solomon-secret | base64

# c29sb21vbi1zZWNyZXQ=

And the generic syntax for passing a command to a container that is not running is as follows:

docker container run <image name> <command>

To perform the base64 encoding using the busybox image, you can execute the following command:

docker container run --rm busybox sh -c "echo -n solomon-secret | base64

# c29sb21vbi1zZWNyZXQ=

What happens here is that, in a container run command, whatever you pass after the image name gets passed to the default entry point of the image to be executed.

Fuente: Mastering Backend