6 minutes read

This topic will focus on making specific settings to get the required environment inside your container. These instructions aren't difficult to understand. But still, they need to be studied carefully. Let's start with the USER instruction.

USER instruction

Operating systems provide us with the functionality to have multiple users with different privileges. Just like any regular computer operating system, Docker also has features for operating with users. In Docker, you can use the USER instruction to choose the user who will perform operations. Let's start by recalling which user performs operations by default.

FROM ubuntu:22.04

LABEL author=HyperUser

ENTRYPOINT ["whoami"]

Running a container on the Dockerfile above gives the result – root. So, this is the default user. Now, let's implement the USER instruction to switch to another user.

FROM ubuntu:22.04

LABEL author=HyperUser

USER bin

ENTRYPOINT ["whoami"]

This time the output will be bin. Meaning, the bin user will handle instructions like RUN, CMD, or ENTRYPOINT to execute commands.

Note that you can also specify the group coupled with the user. To do this you can use their IDs instead of names. The following Dockerfile will set the current user to bin and the current group to root:

FROM ubuntu:22.04

LABEL author=HyperUser

# USER UID:GID
USER 2:0

CMD ["id"]

When you run the container, the id command will print uid=2(bin) gid=0(root) groups=0(root).

Now, let's move on to the WORKDIR instruction.

WORKDIR instruction

Another important instruction that helps you configure the environment is the WORKDIR instruction. You might already know that the working directory is the / directory. When using Docker images, you may need to set up another working directory. This is where the WORKDIR instruction comes into play. It sets the working directory for instructions:

  • RUN

  • CMD

  • ENTRYPOINT

  • COPY

  • ADD

This instruction can be set more than once. However, each WORKDIR instruction path will be relative to the previous one. Let's look at the following Dockerfile:

FROM ubuntu:22.04

LABEL author=HyperUser

WORKDIR /etc
WORKDIR default

ENTRYPOINT ["pwd"]

For this case, the working directory will be /etc/default. Now, let's discuss an interesting question. What if you want to get back to one of the previous directories? In such cases, you can just set an absolute path of the desired directory:

FROM ubuntu:22.04

LABEL author=HyperUser

WORKDIR /etc
WORKDIR default
WORKDIR /etc

ENTRYPOINT ["pwd"]

If you run a container based on this Dockerfile., the output will be /etc.

EXPOSE instruction

Just like computers and other devices, containers can also use ports for communication. In practice, you'll have situations where you need to specify the port your container is listening to. The EXPOSE instruction tells Docker that the container listens to specific network ports. Let's run a simple container in the background in detached mode to check which port the container listens to.

FROM ubuntu:22.04

LABEL author=HyperUser

ENTRYPOINT ["/bin/bash"]

Let's build an ubuntu:v1 image and run the container in background mode with the following command:

$ docker run -d -it --name hs-ubuntu-v1 ubuntu:v1

Now, use docker ps to check the ports it uses:

$ docker ps
CONTAINER ID   IMAGE       COMMAND       CREATED          STATUS          PORTS    NAMES
20cfa11c34d0   ubuntu:v1   "/bin/bash"   35 minutes ago   Up 35 minutes            hs-ubuntu-v1

The PORTS column doesn't show any information because we didn't mention which port the container is listening to. This is when you'll use the EXPOSE instruction to explicitly specify the required ports. Let's add this instruction to the Dockerfile:

FROM ubuntu:22.04

LABEL author=HyperUser

EXPOSE 80

ENTRYPOINT ["/bin/bash"]

Using this instruction will expose the specified port for certain protocols during the container runtime. This time the PORTS column will display the exposed port:

$ docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Ports}}" --last=1
CONTAINER ID   IMAGE       NAMES           PORTS
f7665e89f11a   ubuntu:v2   hs-ubuntu-v2    80/tcp

As you can see, the protocol for the exposed port isn't specified but the output shows us TCP because it's the default configuration. You can also specify the protocol explicitly by writing EXPOSE 80/tcp or using another protocol for this purpose: EXPOSE 80/udp. Besides this, you have the option to expose both protocols at once:

FROM ubuntu:22.04

LABEL author=HyperUser

EXPOSE 80/tcp
EXPOSE 80/udp

ENTRYPOINT ["/bin/bash"]

Let's see what docker ps displays this time:

$ docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Ports}}" --last=1
CONTAINER ID   IMAGE       NAMES           PORTS
2f92df4d108a   ubuntu:v3   hs-ubuntu-v3    80/tcp, 80/udp

So, the container is listening to the 80 port for both TCP and UDP connection.

Docker provides the -p flag to publish ports when running containers. Don't confuse the EXPOSE instruction with the -p flag. They have different roles. With the EXPOSE instruction, you don't open the port for the external world. It just informs that the container expects to communicate via a specified port but you must publish the port to open it.

Conclusion

You have finished one more topic about Dockerfile instructions. You learned three instructions that let you configure the environment of the containers you will run. At this point, you are already familiar with most of the Docker instructions. So, let's practice what you have learned by doing some tasks.

29 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo