docker 动态映射运行的container端口实例详解
docker动态映射运行的container端口,最近做项目,对于docker动态映射运行的container端口的资料有必要记录下,以便以后在用到,
Docker自带了EXPOSE命令,可以通过编写dockerfile加-p参数方便的映射Container内部端口,但是对于已经运行的container,如果你想对外开放一个新的端口,只能编辑dockerfile然后重新build,有点不太方便。
其实docker本身使用了iptables来做端口映射的,所以我们可以通过一些简单的操作来实现动态映射运行中的container端口。
通过运行iptables命令可以看到具体的端口映射(下面的实例中IP为192.168.42.41的container开放了22和20280等端口)
[yaxin@ubox~]$sudoiptables-nvxL ChainINPUT(policyACCEPT262packets,529689bytes) pktsbytestargetprotoptinoutsourcedestination 14355789552DROPtcp--**0.0.0.0/00.0.0.0/0tcpdpt:25 ChainFORWARD(policyACCEPT0packets,0bytes) pktsbytestargetprotoptinoutsourcedestination 5479459653248187DOCKERall--*docker00.0.0.0/00.0.0.0/0 93990314970368ACCEPTall--*docker00.0.0.0/00.0.0.0/0ctstateRELATED,ESTABLISHED 47053952183219154ACCEPTall--docker0!docker00.0.0.0/00.0.0.0/0 00ACCEPTall--docker0docker00.0.0.0/00.0.0.0/0 ChainOUTPUT(policyACCEPT282packets,622495bytes) pktsbytestargetprotoptinoutsourcedestination ChainDOCKER(1references) pktsbytestargetprotoptinoutsourcedestination 21813193ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:22280 4868186297463902ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:20280 7866313128102ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:22 474321ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:28159 [yaxin@ubox~]$sudoiptables-tnat-nvxL ChainPREROUTING(policyACCEPT210199packets,14035875bytes) pktsbytestargetprotoptinoutsourcedestination 121948382563968DOCKERall--**0.0.0.0/00.0.0.0/0ADDRTYPEmatchdst-typeLOCAL ChainINPUT(policyACCEPT197679packets,13316595bytes) pktsbytestargetprotoptinoutsourcedestination ChainOUTPUT(policyACCEPT271553packets,16671466bytes) pktsbytestargetprotoptinoutsourcedestination 164399251DOCKERall--**0.0.0.0/0!127.0.0.0/8ADDRTYPEmatchdst-typeLOCAL ChainPOSTROUTING(policyACCEPT271743packets,16682594bytes) pktsbytestargetprotoptinoutsourcedestination 13468811013MASQUERADEall--*!docker0192.168.42.0/240.0.0.0/0 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:22280 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:20280 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:22 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:28159 ChainDOCKER(2references) pktsbytestargetprotoptinoutsourcedestination 221404DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:22280to:192.168.42.41:22280 28817156DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:20280to:192.168.42.41:20280 935952DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:22222to:192.168.42.41:22 8512DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:28159to:192.168.42.41:28159
我们要做的就是根据自己container的情况配置iptables规则。
首先是filter这个表,我们要配置其放通转发,docker默认已经将所有的FORWARD规则放到了DOCKER这个自建链(chain)中了,这样方便配置,也方便查看。
然后再配置nat表为其配置DNAT,这个才是端口转发的核心配置。这个表中只需要配置POSTROUTING和DOCKER链即可,这里不讲为什么这么配置,如果想要深入了解iptables请google一下。
下面举个例子:
假如我有一个container,名字为nginx(通过运行dockerps命令即可查询),现在我在docker内部运行nginx程序,监听了8888端口,我希望外网可以通过8899端口(注意一下端口)访问
找到docker为nginx分配的IP
[yaxin@ubox~]$sudodockerinspect-f'{{.NetworkSettings.IPAddress}}'nginx
192.168.42.43
配置iptables中filter表的FORWARD(DOCKER)链
[yaxin@ubox~]$sudoiptables-ADOCKER!-idocker0-odocker0-ptcp--dport8888-d192.168.42.43-jACCEPT
配置iptables中nat表的PREROUTING(DOCKER)和POSTROUTING链
[yaxin@ubox~]$sudoiptables-tnat-APOSTROUTING-ptcp--dport8888-s192.168.42.43-d192.168.42.43-jMASQUERADE [yaxin@ubox~]$sudoiptables-tnat-ADOCKER!-idokcer0-ptcp--dport8899-jDNAT--to-destination192.168.42.43:8888
通过外网访问curlhttp://IP:8899就会显示nginx下配置的html页面
最后iptables规则
[yaxin@ubox~]$sudoiptables-nvxL ChainINPUT(policyACCEPT67893packets,212661547bytes) pktsbytestargetprotoptinoutsourcedestination 14364790008DROPtcp--**0.0.0.0/00.0.0.0/0tcpdpt:25 ChainFORWARD(policyACCEPT0packets,0bytes) pktsbytestargetprotoptinoutsourcedestination 5479682653269356DOCKERall--*docker00.0.0.0/00.0.0.0/0 94186314986910ACCEPTall--*docker00.0.0.0/00.0.0.0/0ctstateRELATED,ESTABLISHED 47056582183254076ACCEPTall--docker0!docker00.0.0.0/00.0.0.0/0 00ACCEPTall--docker0docker00.0.0.0/00.0.0.0/0 ChainOUTPUT(policyACCEPT71253packets,222512872bytes) pktsbytestargetprotoptinoutsourcedestination ChainDOCKER(1references) pktsbytestargetprotoptinoutsourcedestination 21813193ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:22280 4868186297463902ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:20280 7866313128102ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:22 474321ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.41tcpdpt:28159 274627ACCEPTtcp--!docker0docker00.0.0.0/0192.168.42.43tcpdpt:8888 [yaxin@ubox~]$sudoiptables-tnat-nvxL ChainPREROUTING(policyACCEPT232packets,16606bytes) pktsbytestargetprotoptinoutsourcedestination 122028182620790DOCKERall--**0.0.0.0/00.0.0.0/0ADDRTYPEmatchdst-typeLOCAL ChainINPUT(policyACCEPT216packets,15671bytes) pktsbytestargetprotoptinoutsourcedestination ChainOUTPUT(policyACCEPT317packets,19159bytes) pktsbytestargetprotoptinoutsourcedestination 164499311DOCKERall--**0.0.0.0/0!127.0.0.0/8ADDRTYPEmatchdst-typeLOCAL ChainPOSTROUTING(policyACCEPT321packets,19367bytes) pktsbytestargetprotoptinoutsourcedestination 13512813656MASQUERADEall--*!docker0192.168.42.0/240.0.0.0/0 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:22280 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:20280 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:22 00MASQUERADEtcp--**192.168.42.41192.168.42.41tcpdpt:28159 00MASQUERADEtcp--**192.168.42.43192.168.42.43tcpdpt:8888 ChainDOCKER(2references) pktsbytestargetprotoptinoutsourcedestination 221404DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:22280to:192.168.42.41:22280 28817156DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:20280to:192.168.42.41:20280 935952DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:22222to:192.168.42.41:22 8512DNATtcp--!docker0*0.0.0.0/00.0.0.0/0tcpdpt:28159to:192.168.42.41:28159 4208DNATtcp--!dokcer0*0.0.0.0/00.0.0.0/0tcpdpt:8899to:192.168.42.43:8888
当然,使用手动配置也是比较麻烦的,所以我写了一个脚本来自动配置端口映射,使用方法脚本中有说明
#!/bin/bash
#filename:docker_expose.sh
if[`id-u`-ne0];then
echo"[EROOR]Pleaseuseroottorunthisscript"
exit23
fi
if[$#-ne3];then
echo"Usage:$0<container_name><add|del>[[<machine_ip>:]<machine_port>:]<container_port>[/<protocol_type>]"
exit1
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
#checkaction
if["$action"x!="add"x-a"$action"x!="del"x];then
echo"[ERROR]Pleaseuseaddordelparametertoaddportmapordeleteportmap"
exit654
fi
if["$action"x=="add"x];then
action="A"
else
action="D"
fi
#getcontaineripbycontainername
container_ip=`dockerinspect-f'{{.NetworkSettings.IPAddress}}'$container_name2>/dev/null`
if[-z$container_ip];then
echo"[ERROR]Getcontainer's(${container_name})IPerror,pleaseensureyouhavethiscontainer"
exit2
fi
#parsearguments
protocol_type=`echo$arguments|awk-F'/''{print$2}'`
if[-z$protocol_type];then
protocol_type="tcp"
fi
#checkprotocol
if["$protocol_type"x!="tcp"x-a"$protocol_type"x!="udp"x];then
echo"[ERROR]Onlytcporudpprotocolisallowed"
exit99
fi
machine_ip=''
machine_port=''
container_port=''
#splittheleftarguments
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
#argumentsis:234
container_port=$machine_ip
machine_port=$machine_ip
unsetmachine_ip
elif[-z$container_port];then
#argumentsis:234:456
container_port=$machine_ip
machine_port=$machine_port
unsetmachine_ip
fi
#checkportnumberfunction
_check_port_number(){
localport_num=$1
if!echo$port_num|egrep"^[0-9]+$"&>/dev/null;then
echo"[ERROR]Invalidportnumber$port_num"
exit3
fi
if[$port_num-gt65535-o$port_num-lt1];then
echo"[ERROR]Portnumber$port_numisoutofrange(1-56635)"
exit4
fi
}
#checkportandipaddress
_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]InvalidIpAdress$machine_ip"
exit5
fi
#checkwhichinterfacebindtheIP
forinterfacein`ifconfig-s|sed-n'2,$p'|awk'{print$1}'`;do
interface_ip=`ifconfig$interface|awk'/inetaddr/{printsubstr($2,6)}'`
if["$interface_ip"x=="$machine_ip"x];then
interface_name=$interface
break
fi
done
if[-z$interface_name];then
echo"[ERROR]Cannotfindinterfacebindwith$machine_ip"
exit98
fi
fi
#runiptablescommand
echo"[INFO]Nowstarttochangerulestoiptables"
echo"[INFO]ChangingPOSTROUTINGchainofnattable"
iptables-tnat-${action}POSTROUTING-p${protocol_type}--dport${container_port}-s${container_ip}-d${container_ip}-jMASQUERADE
if[-z$interface_name];then
echo"[INFO]ChangingDOCKERchainoffiltertable"
iptables-${action}DOCKER!-idocker0-odocker0-p${protocol_type}--dport${container_port}-d${container_ip}-jACCEPT
echo"[INFO]ChangingDOCKERchainofnattable"
iptables-tnat-${action}DOCKER!-idocker0-p${protocol_type}--dport${machine_port}-jDNAT--to-destination${container_ip}:${container_port}
else
echo"[INFO]ChangingDOCKERchainoffiltertable"
iptables-${action}DOCKER-i$interface_name-odocker0-p${protocol_type}--dport${container_port}-d${container_ip}-jACCEPT
echo"[INFO]ChangingDOCKERchainofnattable"
iptables-tnat-${action}DOCKER-i$interface_name-p${protocol_type}--dport${machine_port}-jDNAT--to-destination${container_ip}:${container_port}
fi
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!