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:
RUNCMDENTRYPOINTCOPYADD
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:v1Now, 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-v1The 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/tcpAs 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/udpSo, 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.