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