The general idea for communication between microservices (whatever is HTTP, message or event-driven communication) is related to certain protocols and ports on which each microservice listens for incoming requests. Since Docker is the most popular choice for building microservices in the local development stage, I’m going to distinguish between the two known and often confusing terms EXPOSE
and publish
ports.
Expose ports
EXPOSE
is usually part of the Dockerfile and serves only as an information for the person or the team to know which port they should expect to open when building and running the container. Basically, a documentation in a way.
For instance, if we take the official Nginx Dockerfile, it ends with:
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx", "-g", "daemon off;"]
Step 1. Let’s try to run an Nginx container.
docker run --name webserver -d nginx
Step 2. Verify running container.
docker container ls
Output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03a415ae5c07 nginx "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 80/tcp webserver
Step 3. Test the connection from the host machine.
curl http://localhost
Expected output:
curl: (7) Failed to connect to localhost port 80: Connection refused
Step 4. Even if we add additional exposed port, the result will be the same.
docker run --name webserver-custom --expose 8888 -d nginx
Step 5. Verify the running container.
docker container ls
Expected output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5fbbbfdc1f40 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 80/tcp, 8888/tcp webserver-custom
Step 6. Test the connection from the host machine again, but this time use the other exposed port, in this case port 8888.
curl http://localhost:8888
Expected output:
curl: (7) Failed to connect to localhost port 8888: Connection refused
Publish ports
Publishing container ports is enabled by --publish
and -p
flags. Using these flags, we are mapping ports, so we could easily expose a container service to the host machine.
Step 1. Let’s do it from the command line first.
docker run --name webserver -d -p 80:80 nginx
Step 2. Verify running container.
docker container ls
Expected output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c1d9f7590490 nginx "/docker-entrypoint.…" 1 second ago Up Less than a second 0.0.0.0:80->80/tcp webserver
Step 3. Test the connection.
curl http://localhost
Expected output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Note(s): Regarding docker-compose, you could publish ports by adding the configuration option ports
. For instance:
version: '3.8'
services:
webserver:
image: nginx
ports:
- "80:80"
It’s worth to mention that, the Docker Compose stack creates its own default network where all services that are part of the stack are internally reachable.
Conclusion
TL;DR expose serves only as a documentation for the containers service listening port, while publish is actually exposing ports to external networks. Feel free to leave a comment below and if you find this tutorial useful, follow our official channel on Telegram.