docker dynamic mapping running container port example details
- 2020-05-17 07:20:06
- OfStack
docker dynamic mapping operation of container port, recently made a project, for docker dynamic mapping operation of container port information is necessary to record, so as to be used in the future,
Docker comes with the EXPOSE command. You can easily map Container's internal port by writing dockerfile plus the -p parameter, but for container that is already running, if you want to open a new port to the public, you can only edit dockerfile and then re-build, which is a little inconvenient.
In fact, docker itself USES iptables for port mapping, so we can dynamically map the container port in operation through some simple operations.
You can see the specific port mapping by running the iptables command (IP opens ports 22 and 20280 for container of 192.168.42.41 in the example below)
[yaxin@ubox ~]$sudo iptables -nvxL
Chain INPUT (policy ACCEPT 262 packets, 529689 bytes)
pkts bytes target prot opt in out source destination
14355 789552 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:25
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
5479459 653248187 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
93990 314970368 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
4705395 2183219154 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 282 packets, 622495 bytes)
pkts bytes target prot opt in out source destination
Chain DOCKER (1 references)
pkts bytes target prot opt in out source destination
218 13193 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:22280
4868186 297463902 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:20280
78663 13128102 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:22
47 4321 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:28159
[yaxin@ubox ~]$sudo iptables -t nat -nvxL
Chain PREROUTING (policy ACCEPT 210199 packets, 14035875 bytes)
pkts bytes target prot opt in out source destination
1219483 82563968 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 197679 packets, 13316595 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 271553 packets, 16671466 bytes)
pkts bytes target prot opt in out source destination
1643 99251 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 271743 packets, 16682594 bytes)
pkts bytes target prot opt in out source destination
13468 811013 MASQUERADE all -- * !docker0 192.168.42.0/24 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:22280
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:20280
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:22
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:28159
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
22 1404 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22280 to:192.168.42.41:22280
288 17156 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:20280 to:192.168.42.41:20280
93 5952 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22222 to:192.168.42.41:22
8 512 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:28159 to:192.168.42.41:28159
All we have to do is configure the iptables rule for our own container situation.
The first is the table filter, and we need to configure its rollout and forwarding. By default, docker has put all FORWARD rules into the self-built chain DOCKER (chain), which is convenient for configuration and viewing.
Then configure the nat table to configure DNAT, which is the core configuration for port forwarding. In this table, you only need to configure POSTROUTING and DOCKER chains. I won't explain why this is so. If you want to know more about iptables, please google1.
Here's an example:
If I have an container named nginx(which I can query by running the docker ps command), I am now running the nginx program inside docker, listening to port 8888, and I want the external network to be accessible through port 8899 (note port 1)
Find IP assigned by docker for nginx
[yaxin@ubox ~]$sudo docker inspect -f '{{.NetworkSettings.IPAddress}}' nginx
192.168.42.43
Configure the FORWARD(DOCKER) chain for the filter table in iptables
[yaxin@ubox ~]$sudo iptables -A DOCKER ! -i docker0 -o docker0 -p tcp --dport 8888 -d 192.168.42.43 -j ACCEPT
Configure the PREROUTING(DOCKER) and POSTROUTING chains for the nat table in iptables
[yaxin@ubox ~]$sudo iptables -t nat -A POSTROUTING -p tcp --dport 8888 -s 192.168.42.43 -d 192.168.42.43 -j MASQUERADE
[yaxin@ubox ~]$sudo iptables -t nat -A DOCKER ! -i dokcer0 -p tcp --dport 8899 -j DNAT --to-destination 192.168.42.43:8888
Accessing curl http://IP:8899 via an external network will display the html page configured under nginx
Finally, the iptables rule
[yaxin@ubox ~]$sudo iptables -nvxL
Chain INPUT (policy ACCEPT 67893 packets, 212661547 bytes)
pkts bytes target prot opt in out source destination
14364 790008 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:25
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
5479682 653269356 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
94186 314986910 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
4705658 2183254076 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 71253 packets, 222512872 bytes)
pkts bytes target prot opt in out source destination
Chain DOCKER (1 references)
pkts bytes target prot opt in out source destination
218 13193 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:22280
4868186 297463902 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:20280
78663 13128102 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:22
47 4321 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.41 tcp dpt:28159
27 4627 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 192.168.42.43 tcp dpt:8888
[yaxin@ubox ~]$sudo iptables -t nat -nvxL
Chain PREROUTING (policy ACCEPT 232 packets, 16606 bytes)
pkts bytes target prot opt in out source destination
1220281 82620790 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 216 packets, 15671 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 317 packets, 19159 bytes)
pkts bytes target prot opt in out source destination
1644 99311 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 321 packets, 19367 bytes)
pkts bytes target prot opt in out source destination
13512 813656 MASQUERADE all -- * !docker0 192.168.42.0/24 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:22280
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:20280
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:22
0 0 MASQUERADE tcp -- * * 192.168.42.41 192.168.42.41 tcp dpt:28159
0 0 MASQUERADE tcp -- * * 192.168.42.43 192.168.42.43 tcp dpt:8888
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
22 1404 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22280 to:192.168.42.41:22280
288 17156 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:20280 to:192.168.42.41:20280
93 5952 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22222 to:192.168.42.41:22
8 512 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:28159 to:192.168.42.41:28159
4 208 DNAT tcp -- !dokcer0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8899 to:192.168.42.43:8888
Of course, using manual configuration is also cumbersome, so I wrote a script to automatically configure the port mapping, as described in the usage method script
#!/bin/bash
# filename: docker_expose.sh
if [ `id -u` -ne 0 ];then
echo "[EROOR] Please use root to run this script"
exit 23
fi
if [ $# -ne 3 ];then
echo "Usage: $0 <container_name> <add|del> [[<machine_ip>:]<machine_port>:]<container_port>[/<protocol_type>]"
exit 1
fi
IPV4_RE='(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])'
container_name=$1
action=$2
arguments=$3
# check action
if [ "$action"x != "add"x -a "$action"x != "del"x ];then
echo "[ERROR] Please use add or del parameter to add port map or delete port map"
exit 654
fi
if [ "$action"x == "add"x ];then
action="A"
else
action="D"
fi
# get container ip by container name
container_ip=`docker inspect -f '{{.NetworkSettings.IPAddress}}' $container_name 2> /dev/null`
if [ -z $container_ip ];then
echo "[ERROR] Get container's (${container_name}) IP error, please ensure you have this container"
exit 2
fi
# parse arguments
protocol_type=`echo $arguments | awk -F '/' '{print $2}'`
if [ -z $protocol_type ];then
protocol_type="tcp"
fi
# check protocol
if [ "$protocol_type"x != "tcp"x -a "$protocol_type"x != "udp"x ];then
echo "[ERROR] Only tcp or udp protocol is allowed"
exit 99
fi
machine_ip=''
machine_port=''
container_port=''
# split the left arguments
arguments=${arguments%/*}
machine_ip=`echo $arguments | awk -F ':' '{print $1}'`
machine_port=`echo $arguments | awk -F ':' '{print $2}'`
container_port=`echo $arguments | awk -F ':' '{print $3}'`
if [ -z $machine_port ];then
# arguments is: 234
container_port=$machine_ip
machine_port=$machine_ip
unset machine_ip
elif [ -z $container_port ];then
# arguments is: 234:456
container_port=$machine_ip
machine_port=$machine_port
unset machine_ip
fi
# check port number function
_check_port_number() {
local port_num=$1
if ! echo $port_num | egrep "^[0-9]+$" &> /dev/null;then
echo "[ERROR] Invalid port number $port_num"
exit 3
fi
if [ $port_num -gt 65535 -o $port_num -lt 1 ];then
echo "[ERROR] Port number $port_num is out of range(1-56635)"
exit 4
fi
}
# check port and ip address
_check_port_number $container_port
_check_port_number $machine_port
if [ ! -z $machine_ip ];then
if ! echo $machine_ip | egrep "^${IPV4_RE}$" &> /dev/null;then
echo "[ERROR] Invalid Ip Adress $machine_ip"
exit 5
fi
# check which interface bind the IP
for interface in `ifconfig -s | sed -n '2,$p' | awk '{print $1}'`;do
interface_ip=`ifconfig $interface | awk '/inet addr/{print substr($2,6)}'`
if [ "$interface_ip"x == "$machine_ip"x ];then
interface_name=$interface
break
fi
done
if [ -z $interface_name ];then
echo "[ERROR] Can not find interface bind with $machine_ip"
exit 98
fi
fi
# run iptables command
echo "[INFO] Now start to change rules to iptables"
echo "[INFO] Changing POSTROUTING chain of nat table"
iptables -t nat -${action} POSTROUTING -p ${protocol_type} --dport ${container_port} -s ${container_ip} -d ${container_ip} -j MASQUERADE
if [ -z $interface_name ];then
echo "[INFO] Changing DOCKER chain of filter table"
iptables -${action} DOCKER ! -i docker0 -o docker0 -p ${protocol_type} --dport ${container_port} -d ${container_ip} -j ACCEPT
echo "[INFO] Changing DOCKER chain of nat table"
iptables -t nat -${action} DOCKER ! -i docker0 -p ${protocol_type} --dport ${machine_port} -j DNAT --to-destination ${container_ip}:${container_port}
else
echo "[INFO] Changing DOCKER chain of filter table"
iptables -${action} DOCKER -i $interface_name -o docker0 -p ${protocol_type} --dport ${container_port} -d ${container_ip} -j ACCEPT
echo "[INFO] Changing DOCKER chain of nat table"
iptables -t nat -${action} DOCKER -i $interface_name -p ${protocol_type} --dport ${machine_port} -j DNAT --to-destination ${container_ip}:${container_port}
fi
Thank you for reading, I hope to help you, thank you for your support of this site!