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 ~]$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

我们要做的就是根据自己container的情况配置iptables规则。

首先是filter这个表,我们要配置其放通转发,docker默认已经将所有的FORWARD规则放到了DOCKER这个自建链(chain)中了,这样方便配置,也方便查看。

然后再配置nat表为其配置DNAT,这个才是端口转发的核心配置。这个表中只需要配置POSTROUTING和DOCKER链即可,这里不讲为什么这么配置,如果想要深入了解iptables请google一下。

下面举个例子:

假如我有一个container,名字为nginx(通过运行docker ps命令即可查询),现在我在docker内部运行nginx程序,监听了8888端口,我希望外网可以通过8899端口(注意一下端口)访问

找到docker为nginx分配的IP

[yaxin@ubox ~]$sudo docker inspect -f '{{.NetworkSettings.IPAddress}}' nginx
192.168.42.43

配置iptables中filter表的FORWARD(DOCKER)链

[yaxin@ubox ~]$sudo iptables -A DOCKER ! -i docker0 -o docker0 -p tcp --dport 8888 -d 192.168.42.43 -j ACCEPT

配置iptables中nat表的PREROUTING(DOCKER)和POSTROUTING链

[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

通过外网访问curl http://IP:8899就会显示nginx下配置的html页面

最后iptables规则

[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

当然,使用手动配置也是比较麻烦的,所以我写了一个脚本来自动配置端口映射,使用方法脚本中有说明

#!/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

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Docker 多主机网络通信详细介绍

    最近做项目是关于Docker 的网络通信,需要多个主机进行链接通信,这里记录下,以后便于项目开发,大家需要的话也可以看下,少走些弯路. Docker多主机网络通信详解              Docker支持多主机网络通信功能,可以通过命令行建立多主机通信网络.本文使用Docker machine和Consul服务发现工具来讲解这一点. 前提是需要先安装Docker工具箱. 1.Docker Multi-Host Networking 作为一个示例,我们会在VirtualBox虚拟机上使用do

  • 详解Docker中VLAN网络模式的配置

    前言 Docker作为目前最火的轻量级容器技术,有很多令人称道的功能,如Docker的镜像管理.然而,Docker同样有着很多不完善的地方,网络 方面就是Docker比较薄弱的部分.因此,我们有必要深入了解Docker的网络知识,以满足更高的网络需求. Docker网络模式选择 目前已有不少文章介绍了Docker的网络模型,但是在实际应用中还是有不少坑和需要注意的点 在Docker应用到生产环境的时候,网络模型的选择主要有以下几种 1.原生Bridge NAT模式 2.Linux Bridge

  • Docker 容器操作退出后进入解决办法

    在我们对Docker容器操作的时候,有时候会误操作或者其他的原因无意间退出了正在操作的容器,也许你会担忧你在其中的一些操作未保存下来,无须担忧,本文中将会提供各种方法供你参考(我的建议使用最后一种).在本文,我们将讨论五种(4+1)连接Docker容器并与其进行交互的方法.例子中所有的代码都可以在GitHub中找到,你可以亲自对它们进行测试. 1.nsenter 安装 nsenter 工具在 util-Linux 包2.23版本后包含. 如果系统中 util-linux 包没有该命令,可以按照下

  • docker快速入门教程

    10分钟教会大家如何玩转Docker,这是 1.前言 进入云计算的时代,各大云提供商AWS,阿里云纷纷推出针对Docker的服务,现在Docker是十分火爆,那么Docker到底是什麽,让我们来体验一下. 2.Docker是什麽 Docker是一个开源的应用容器引擎,可以把应用以及依赖包放到一个可移植的容器中,然后发布到任何流行的 Linux 系统上,通过这种方式实现虚拟化. 提到虚拟化,大家应该十分熟悉了,有VMware,Xen,KVM等等很多.那么,Docker和VM有什么不同呢,我们用官网

  • docker中mysql初始化及启动失败问题解决方案

    最近做项目,遇到这样问题,docker 中的mysql 不能启动,经过上网查资料,终于解决了这个问题,这里记录下,也许还能帮助到大家, 在docker中有一个mysql服务,其数据文件是挂在在主机外面的文件,在docker中的root有访问该数据文件的权限,但是docker中mysql访问数据文件的时候提示权限不足,于是只有以root用户来启动mysql了. 数据初始化: mysql_install_db --user=root --explicit_defaults_for_timestamp

  • Docker 手动配置容器网络实例详解

    Docker 手动配置容器网络 docker容器的网络是net命名空间与虚拟设备的结合,容器在启动时会创建一对虚拟接口veth pair,这一对接口分别放到本地和容器中,在本地的veth会被分配类似vethxxxx的名称并被桥接到指定网桥的上(默认为docker0),可以通过brctl show命令查看网桥上挂载的接口,在容器中的veth会从网桥获取一个未使用地址,该veth的名称会被更改为eth0并配置默认路由到vethxxxx,docker允许在启动容器的时候通过--net参数指定不同的网络

  • Docker基础命令详解

    docker基本概念 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上. Docker是一个重新定义了程序开发测试.交付和部署过程的开放平台,Docker则可以称为构建一次,到处运行,这就是docker提出的"Build once,Run anywhere" 创建镜像 创建镜像的方法有三种: 基于已有的容器创建 基于本地模板导入 基于dockerfile 基于已有的容器创建 主要使用docker

  • Docker搭建前端Java的开发环境详解

    一.解决的痛点 1.免搭建后端开发环境. 2.开发环境改变只需要改变镜像就能同步更新. 3.不需要eclipse等IDE工具. 4.切换开发项目 二.解决思路 利用docker启动Ubuntu镜像,在容器中搭建好项目需要的开发环境,使用挂载卷将本地代码挂载到容器中,使用容器中的环境编译运行代码,宿主机通过 docker 暴漏出的端口访问容器中的服务,这样前端的开发机上就只需要部署docker就搞定了. 三.关于docker 了解docker 本文并不打算细讲docker的知识,相关的文章有很多,

  • Docker为网络bridge模式指定容器ip的方法

    前言 众所周知bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace.设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上.下面来看看Docker为网络bridge模式指定容器ip的方法. 实现方法 如果只是简单创建一个bridge模式的网络是无法给容器指定ip的 [root@vultrvpn conf.d]# docker network create --driver bridge wordpress_net ad1ff3d972

  • Docker与Golang的巧妙结合

    Docker与Golang的巧妙结合 [编者的话]这是一个展示在使用Go语言时如何让Docker更有用的提示与技巧的简辑.例如,如何使用不同版本的Go工具链来编译Go代码,如何交叉编译到不同的平台(并且测试结果!),或者如何制作真正小的容器镜像. 下面的文章假定你已经安装了Docker.不必是最新版本(这篇文章不会使用Docker任何花哨的功能). 没有go的Go ...意思是:"不用安装go就能使用Go" 如果你写Go代码,或者你对Go语言有一点点兴趣,你肯定要安装了Go编译器和Go

随机推荐