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

13,187

Solution 1

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

Solution 2

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
Share:
13,187
JivanAmara
Author by

JivanAmara

Updated on July 26, 2022

Comments

  • JivanAmara
    JivanAmara over 1 year

    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
    
  • JivanAmara
    JivanAmara almost 7 years
    Sorry I wasn't clear, but no. Please see my edit to the question.
  • JivanAmara
    JivanAmara almost 7 years
    I'm trying to do opposite of that, the equivalent of -p containerport:hostport
  • arvymetal
    arvymetal almost 7 years
    Ah, your edit makes it clearer, and you're right. The host=network parameter I suggest would work to reach another container(or service on a port running on the Docker VM), but not on the host... I thought it would work with Docker for Mac, but a test proved that it doesn't. This is discussed here: forums.docker.com/t/should-docker-run-net-host-work/14215/26 Someone suggests, if using a VirtualBox based solution like Docker Toolbox, to complete with a reverse tunnel like ssh -t -R5432:localhost:5432 docker@$(docker-machine ip default) to connect VM on this port. Good luck!