Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I redirect a single port in a docker container to the container's host?

To make development easier for a project, I've put a couple of services it depends on in docker containers. This makes 'localhost' in the project's config mean something different when it is passed to one of the containers.

edit

To be clear, I'm trying to forward one of the container's ports to the host so when a process running in the container tries to access localhost:5432, it connects to the host's port 5432.

endedit

I'm currently using

HOST_IP=`ip route | grep default | awk '{ printf "%s",$3 }'`
cat /etc/hosts | sed "s/127.0.0.1/$HOST_IP/" > /tmp/etc_hosts
cp /tmp/etc_hosts /etc/hosts

to redirect anything targeting 'localhost' to the container's host. It works in this situation, but I'd prefer to find a way to do this only for the needed port as I expect it won't work in other situations.

Here's what I came up with to do that, but it's not working; when a connection in the container is to localhost:5432, it tries to connect to the container's 5432 instead of the host's:

# --- These are the things that should make redirecting port 5432 to the host machine
#     work, provided the container is run in privileged mode.
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.route_localnet=1
iptables -t nat -A PREROUTING -p tcp --dport 5432 -j DNAT --to 172.19.0.1:5432
iptables -A FORWARD -d 172.19.0.1 -p tcp --dport 5432 -j ACCEPT
iptables -t nat -A POSTROUTING -j MASQUERADE
like image 438
JivanAmara Avatar asked Jan 09 '17 19:01

JivanAmara


People also ask

How do I map a container port to a host port?

To make a port available to services outside of Docker, or to Docker containers which are not connected to the container's network, use the --publish or -p flag. This creates a firewall rule which maps a container port to a port on the Docker host to the outside world.

How do I run a Docker image on a specific port?

To publish a port for our container, we'll use the --publish flag ( -p for short) on the docker run command. The format of the --publish command is [host port]:[container port] . So, if we wanted to expose port 8000 inside the container to port 8080 outside the container, we would pass 8080:8000 to the --publish flag.


2 Answers

If I understand well, for development, you'd want localhost to resolve to a specific container, including when it's called from another container.

Host forwarding

Rewriting your hosts file is, as you mentioned it, not a good idea, since many services can experiment issues if you design localhost as being something different than, well... your local host.

But you can consider a few solutions.

Docker Toolbox

If running docker with Docker Toolbox, or by yourself on a virtual machine with Virtual Box, the intermediate virtual machine is visible, so localhost will represent it. You'll have to run the container, exposing this port, and then to set up a port forwarding in Virtualbox. If I use Wordpress as an example:

  1. docker run -p 80:80 --name website -d wordpress
  2. Virtual Box -> your docker VM (usually called default) -> Network -> Adapter 1 -> port forwarding -> create a mapping from host 8080 to guest 80

It will make Wordpress available at http://localhost:8080. Please note that under MacOS, the kernel restrains non-privileged port forwarding (ports under 1024).

This port forwarding can be created in command line, if you want to put it in a script:
VBoxManage modifyvm "default" --natpf1 "app,tcp,,8080,,80"

Docker for Windows/Docker for Mac

If running docker through Docker for Windows/Docker for Mac (or directly under Linux), rather than Docker Toolbox, you can run the container using the -p parameter, as specified by Scott's post, and your service will be available on localhost at this port (because the intermediate virtual machine is transparent, or no VM under Linux):

docker run -p 5432:5432 --name myapp -d myimage will make myapp available at localhost:5432.

socat (or iptables)

You can run socat on your host this way to forward communication on a specific port to your container:

socat TCP-LISTEN:5432,fork,reuseaddr,user=node,group=node,mode=777 TCP:172.19.0.1:5432 &

(where 172.19.0.1 is your container IP)

Container forwarding

--network

Your containers have their own hosts file, that you can see by issuing such a command:

docker run ubuntu cat /etc/hosts

You can add entries to hosts with the --add-host parameter:

docker run --add-host domain:1.2.3.4 --add-host domain2:5.6.7.8 ubuntu cat /etc/hosts

However this solution will be useless for localhost, because it won't remove the previous localhost associations. What you're looking for (and what is cleaner) is the parameter --network=host which allows the container to share the network interfaces of the host:

docker run --network=host ubuntu

This way, your container will be able to call the other containers services on localhost using their port.

The right way

Of course, the right way to achieve what you want would be to link your containers together and use their link names rather than localhost.

docker run -d  --name mariadb -e MYSQL_ROOT_PASSWORD=password mariadb 
docker run -d --name="wordpress" -p 8080:80 -e WORDPRESS_DB_PASSWORD=password --link mariadb:mysql wordpress

In this case, the Wordpress container will have a mysql entry in its hosts file, pointing to the mariadb container IP address. To see it, open a bash session in the Wordpress container and see by yourself.

docker exec -ti wordpress bash
#cat /etc/hosts
like image 146
arvymetal Avatar answered Sep 20 '22 18:09

arvymetal


Show us how you are launching your container

port mapping can happen in your docker run command : -p hostport:containerport

as in

docker run  -p 5432:5432 --name mycontainer -d myimage
like image 41
Scott Stensland Avatar answered Sep 17 '22 18:09

Scott Stensland