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
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!