In this blog I'll explain 2 mechanisms by which you can expose library/httpd on different ports using host networking and how you can do the same using bridged networking. I'll describe several features of the different solutions and consequences for connectivity / host lookup options. At the end of the post I'll give some tips on how to test connectivity between containers.
Using Docker host networking
Image per port
You could create your own Dockerfile and use an ARG (argument) such as below:
FROM httpd:2.4
MAINTAINER Maarten Smeets <maarten.smeets@amis.nl>
LABEL nl.amis.smeetsm.httpd.name="Apache Httpd" nl.amis.smeetsm.httpd.version="2.4"
#COPY ./www/ /usr/local/apache2/htdocs/
ARG PORT
RUN sed -ri "s/^Listen 80/Listen $PORT/g" /usr/local/apache2/conf/httpd.conf
ENTRYPOINT ["httpd-foreground"]
You can build this like:
docker build --build-arg PORT=84 -t smeetsm/httpd:2.4 .
And run it like:
docker run -dit --network host --name my-running-app-01 smeetsm/httpd:2.4
This allows you to build a container for running on a specific port. Drawback of this is that you build the image specifically for running on a single port. If you want containers running on multiple ports, you'd need multiple images.
docker build --build-arg PORT=84 -t smeetsm/httpdport84:2.4 .
docker build --build-arg PORT=85 -t smeetsm/httpdport85:2.4 .
docker build --build-arg PORT=86 -t smeetsm/httpdport86:2.4 .
And run them like
docker run -dit --network host --name my-running-app-02 smeetsm/httpdport85:2.4
docker run -dit --network host --name my-running-app-03 smeetsm/httpdport86:2.4
You cannot run the same image on multiple ports. Thus you have to create an image per port and this might not be what you want. Also the containers you create this way are not easy to scale.
Single image running on different ports
A much cleaner solution would be to use the same base image supplied by Apache and supply the port with the run command. You can do this like:
docker run -dit --network host --name my-running-app-01 library/httpd:2.4 /bin/bash -c "sed -ri 's/^Listen 80/Listen 84/g' /usr/local/apache2/conf/httpd.conf && httpd-foreground"
docker run -dit --network host --name my-running-app-01 library/httpd:2.4 /bin/bash -c "sed -ri 's/^Listen 80/Listen 85/g' /usr/local/apache2/conf/httpd.conf && httpd-foreground"
docker run -dit --network host --name my-running-app-01 library/httpd:2.4 /bin/bash -c "sed -ri 's/^Listen 80/Listen 86/g' /usr/local/apache2/conf/httpd.conf && httpd-foreground"
The above commands start 3 containers on the specified ports (84,85,86) using the host network driver. This does have the limitation that the containers cannot communicate with each without going over the host interface. They all share the Docker host hostname since they use the Docker host network interface directly. Interesting to see is that they can use their own hostname to directly access different ports on the host.
For example, if I run my-running-app-01 on port 84 using hostname ubuntu-vm and I'm running my-running-app-02 using the same hostname (since the same network interface is used) running on port 85, I can access my-running-app-02 from my-running-app-01 by accessing ubuntu-vm or localhost(!) port 85. my-running-app-01 does not know if my-running-app-02 is running inside a Docker container or is directly hosted on the Docker host.
Bridged networking
Using bridged networking, which is often the default when using Docker, you can create named bridged networks. Hosts on these named bridged networks can find each other by their container name (automatic DNS).
Also bridged networks provide a layer between the host network and the container network. The containers can access other network resources by going through a NAT interface. You have to explicitly map ports if you want to access them from the host or the outside world. Using a bridged network, the software within the container can run on the same port and only the outside port can differ. You thus don't need to update configuration files when you want to run on different ports. In this case you use the same image for the creation of the different containers.
The below example uses the default bridge network. Containers can access each other by IP
docker run -dit --name my-running-app-01 -p 84:80 library/httpd:2.4
docker run -dit --name my-running-app-02 -p 85:80 library/httpd:2.4
docker run -dit --name my-running-app-03 -p 86:80 library/httpd:2.4
The below example uses a named bridge network. The containers can access each other by name.
docker network create --driver bridge my-net
docker run -dit --name my-running-app-01 -p 84:80 --network my-net library/httpd:2.4
docker run -dit --name my-running-app-02 -p 85:80 --network my-net library/httpd:2.4
docker run -dit --name my-running-app-03 -p 86:80 --network my-net library/httpd:2.4
Notes
In order to test network connectivity I used the following:
Networks and containers
In order to find out which networks were used:
docker network ls
NETWORK ID NAME DRIVER SCOPE
3457e6f0a394 bridge bridge local
43e8356475ab host host local
bffb13042787 my-net bridge local
fc4390096330 none null local
In order to find out which container was connected to which network and which IP it used I diid:
docker network inspect my-net
"Containers": {
"3398bb1f84504d1d5cb85a107420059dce3b617a91aef6663f526e0f7cd610b0": {
"Name": "my-running-app-02",
"EndpointID": "7f8191b81db6718b6f4c8091344e35a1b9641bb591025a6d5aa12699b631fbaf",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
},
"810f87402961d79538238d07a9fb70774621b5f6363878d83884fafc89e382ed": {
"Name": "my-running-app-01",
"EndpointID": "5a6c99d83d4d43fec8cb7b6812f1628620f39dd13abf4caa4e5bacbf36f2707a",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
}
The above is just a part of the output but does indicate containers and IP's
Check connectivity from within a container
Enter the container: docker exec -it my-running-app-01 /bin/bash
Since the container is Debian based, I can use Apt to install packages.
apt-get update && apt-get install -y telnet
telnet my-running-app-02 80
If I got a response like:
telnet my-running-app-02 80
Trying 172.18.0.3...
Connected to my-running-app-02.
Escape character is '^]'.
^C
A connection could be established.
If I got a response like
telnet my-running-app-02 81
Trying 172.18.0.3...
telnet: Unable to connect to remote host: Connection refused
I could not establish the connection. The above does indicate resolving my-running-app-02 to an IP worked.
If the host also couldn't be resolved, the exception would be like:
telnet whatever 80
telnet: could not resolve whatever/80: Name or service not known
No comments:
Post a Comment