1

The scenario is like this:

There is an application running inside container in a Linux machine. This application has several services running inside, each service has it's own port number. Port number is quite random.

Now I want to access to this application in a Mac laptop. This Mac laptop is able to ping the Linux machine ip.

One way I'm currently doing is mapping all the ports from docker container to Linux machine, then I'm able to access from Mac using Linux machine IP.

But this method is not scalable with more services joining. I'm wondering if anyone has seen same problem and had a better way to handle this? We do not run Kubernetes on these Linux machines because they are not servers. These machines are for personal development.

Thank you!

Rong Shen
  • 63
  • 6

1 Answers1

1

Docker does not have the functionality to map a port after container creation, or to modify an existing port mapping.

The usual solution is to configure your application to use a set port or range of ports. If that's not possible then there are a few options to work around the issue.

Host network

Use docker run --network=host. The containers share the hosts network stack so are available on the hosts IP. Note as this give's the container access to the hosts network so could interfere with host services or expose more of your host to the container than normal.

Routable, User defined network.

Create a user defined network for your containers and assign it an IP range that the network can route to the Docker host. Services listening on ports in containers are then directly addressable.

docker network create \
    --subnet=10.1.3.0/24 \
    -o com.docker.network.bridge.enable_ip_masquerade=false \
    routable

A route for the new docker network will need be added to your network gateway(s) so they can route the traffic via your Docker host. On Linux this would be something like:

ip route add 10.1.3.0/24 via $DOCKER_HOST_IP

Then you should be able transfer data as normal

docker run --net=routable --rm -it alpine ping $DOCKER_HOST_GATEWAY_IP

Macvlan bridge

Docker has a macvlan network driver that allows you to map a host interface into a container, kind of like a bridged interface in a VM. The container can then have an interface on the same network as the host.

docker network create -d macvlan \
    --subnet=10.1.2.0/24 \
    -o macvlan_mode=bridge \
    -o parent=enp3s0 macvlan
docker run --net=macvlan --ip=10.1.2.128 --rm -it alpine ping 10.1.2.1

Note that you can't communicate with the docker hosts IP address over this bridge. You can add a macvlan sub interface to the host and move the hosts IP address onto it to allow traffic.

Some VM's and virtual networks get finicky about having additional MAC addresses they don't know about generating data. AWS EC2, for example will reject the containers traffic.

Container iptables

It's possible to create iptables NAT rules in a containers namespace. To do this inside a container the container needs the NET_ADMIN capability. Some form of script could lookup the applications ports once it's started and forward traffic with a DNAT rule from your static externally mapped ports to the dynamic application ports.

docker run -p 5000:5000 --cap-add=NET_ADMIN debian:9
# port=$(ss -lntpH | awk '/"app-bin"/ { split($4,a,":"); print a[length(a)]; exit}')
# iptables -t nat -A -p tcp -m tcp --dport 5000 -j DNAT --to-destination 127.0.0.1:$port

You could similarly add the iptables rules from the docker host for the containers network namespace if you don't want to add the NET_ADMIN capability to the container. The host needs a little help to use container name spaces

pid=$(docker inspect -f '{{.State.Pid}}' ${container_id})
mkdir -p /var/run/netns/
ln -sfT /proc/$pid/ns/net /var/run/netns/$container_id
ip netns exec "${container_id}" iptables -t nat -vnL
Matt
  • 68,711
  • 7
  • 155
  • 158
  • thanks a lot for the detailed explanation on every options. We will go with Routable, User defined network option. Thanks! – Rong Shen Dec 13 '17 at 21:32