首页 文章

在实时Docker容器上公开端口

提问于
浏览
335

我正在尝试创建一个类似于全功能虚拟机的Docker容器 . 我知道我可以使用Dockerfile中的EXPOSE指令来公开一个端口,我可以使用 -p 标志和 docker run 分配端口,但是一旦容器实际运行,是否有命令打开/映射其他端口?

例如,假设我有一个运行sshd的Docker容器 . 其他人使用容器ssh并安装httpd . 有没有办法在容器上公开端口80并将其映射到主机上的端口8080,以便人们可以访问容器中运行的Web服务器,而无需重新启动它?

14 回答

  • 125

    这是另一个想法 . 使用SSH进行端口转发;当您的Docker主机是VM时,这也可以在OS X(可能还有Windows)中运行 .

    docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>
    
  • 1

    有一个方便的HAProxy包装 .

    docker run -it -p LOCALPORT:PROXYPORT --rm --link TARGET_CONTAINER:EZNAME -e "BACKEND_HOST=EZNAME" -e "BACKEND_PORT=PROXYPORT" demandbase/docker-tcp-proxy
    

    这会为目标容器创建一个HAProxy . 十分简单 .

  • 0

    这就是我要做的事情:

    • 提交实时容器 .

    • 使用新映像再次运行容器,端口打开(我建议安装共享卷并打开ssh端口)

    sudo docker ps 
    sudo docker commit <containerid> <foo/live>
    sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash
    
  • 0

    虽然您无法公开现有容器的新端口,但您可以在同一个Docker网络中启动一个新容器,并将其转发到原始容器 .

    # docker run \
      --rm \
      -p $PORT:1234 \
      verb/socat \
        TCP-LISTEN:1234,fork \
        TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT
    

    工作示例

    启动一个侦听端口80的Web服务,但是 not 暴露其内部端口80(oops!):

    # docker run -ti mkodockx/docker-pastebin   # Forgot to expose PORT 80!
    

    找到它的Docker网络IP:

    # docker inspect 63256f72142a | grep IPAddress
                        "IPAddress": "172.17.0.2",
    

    在端口8080暴露的情况下启动 verb/socat ,并使其将TCP流量转发到该IP的端口80:

    # docker run --rm -p 8080:1234 verb/socat TCP-LISTEN:1234,fork TCP-CONNECT:172.17.0.2:80
    

    您现在可以在http://localhost:8080/上访问pastebin,并且您的请求转到 socat:1234 ,后者将其转发到 pastebin:80 ,并且响应以相反的方式传递相同的路径 .

  • 286

    我不得不处理同样的问题,并且能够在不停止任何正在运行的容器的情况下解决它 . 使用Docker 1.9.1,这是截至2016年2月的最新解决方案 . 无论如何,这个答案是@ ricardo-branco答案的详细版本,但对新用户更为深入 .

    在我的场景中,我想暂时连接到在容器中运行的MySQL,并且由于其他应用程序容器已链接到它,因此停止,重新配置和重新运行数据库容器是不可能的 .

    因为我'd like to access the MySQL database externally (from Sequel Pro via SSH tunneling), I' m将在主机上使用端口 33306 . (不是 3306 ,以防万一有外部MySQL实例在运行 . )

    大约一个小时的调整iptables证明没有结果,即使:

    一步一步,这就是我做的:

    mkdir db-expose-33306
    cd db-expose-33306
    vim Dockerfile
    

    编辑 dockerfile ,将其置于:

    # Exposes port 3306 on linked "db" container, to be accessible at host:33306
    FROM ubuntu:latest # (Recommended to use the same base as the DB container)
    
    RUN apt-get update && \
        apt-get -y install socat && \
        apt-get clean
    
    USER nobody
    EXPOSE 33306
    
    CMD socat -dddd TCP-LISTEN:33306,reuseaddr,fork TCP:db:3306
    

    然后构建图像:

    docker build -t your-namespace/db-expose-33306 .
    

    然后运行它,链接到正在运行的容器 . (使用 -d 而不是 -rm 将其保留在后台,直到明确停止并删除 . 我只希望它在这种情况下暂时运行 . )

    docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306  your-namespace/db-expose-33306
    
  • 1

    IPtables黑客行为不起作用,至少在Docker 1.4.1上是这样 .

    最好的方法是使用公开端口运行另一个容器并使用socat继电器 . 这就是我用SQLPlus(临时)连接数据库所做的事情:

    docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus
    

    Dockerfile:

    FROM debian:7
    
    RUN apt-get update && \
        apt-get -y install socat && \
        apt-get clean
    
    USER nobody
    
    CMD socat -dddd TCP-LISTEN:1521,reuseaddr,fork TCP:db:1521
    
  • 0

    您可以使用像Weave Net这样的覆盖网络,它将为每个容器分配一个唯一的IP地址,并隐式地将所有端口暴露给网络的每个容器部分 .

    编织还提供host network integration . 默认情况下禁用它,但是,如果您还要从主机访问容器IP地址(及其所有端口),则只需运行 weave expose 即可运行 .

    完全披露:我在Weaveworks工作 .

  • 31

    首先阅读Ricardo的回复 . 这对我有用 .

    但是,如果正在使用docker-compose启动正在运行的容器,则存在这样的情况 . 这是因为docker-compose(我正在运行docker 1.17)创建了一个新网络 . 解决这种情况的方法是

    docker network ls

    然后追加以下 docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name

  • 7

    您无法通过Docker执行此操作,但可以从主机访问容器的未公开端口 .

    如果你有一个容器,其端口8000上运行的东西,你可以运行

    wget http://container_ip:8000
    

    要获取容器的IP地址,请运行以下命令:

    docker ps
    
    docker inspect container_name | grep IPAddress
    

    在内部,Docker会在你运行图像时弹出来调用iptables,所以可能会有一些变化 .

    在localhosts端口8001上公开容器的端口8000:

    iptables -t nat -A  DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000
    

    解决这个问题的一种方法是使用您想要的端口映射设置另一个容器,并比较 iptables-save 命令的输出(但是,我必须删除一些强制流量通过docker代理的其他选项) .

    NOTE: this is subverting docker, so should be done with the awareness that it may well create blue smoke

    要么

    另一种选择是查看(新的?post 0.6.6?) - P选项 - 它将使用随机主机端口,然后连接它们 .

    要么

    使用0.6.5,您可以使用LINKs功能调出一个与现有容器对话的新容器,还有一些中继到该容器的-p标志? (我没用过LINKs)

    要么

    与docker 0.11?您可以使用 docker run --net host .. 将容器直接连接到主机的网络接口(即,net不是名称间隔的),因此在容器中打开的 all 端口将被暴露 .

  • 2

    实时端口映射是不可能的,但是有多种方法可以为Docker容器提供虚拟机所具有的真实接口 .

    Macvlan接口

    Docker现在包含Macvlan network driver . 这会将Docker网络连接到"real world"接口,并允许您将该网络地址直接分配给容器(如虚拟机桥接模式) .

    docker network create \
        -d macvlan \
        --subnet=172.16.86.0/24 \
        --gateway=172.16.86.1  \
        -o parent=eth0 pub_net
    

    pipework 也可以map a real interface进入容器或旧版本的Docker中的setup a sub interface .

    路由IP

    如果您可以控制网络,则可以将您的Docker主机用于容器中 .

    然后将该网络分配给容器并设置Docker主机以通过docker网络路由数据包 .

    共享主机接口

    --net host 选项允许将主机接口共享到容器中,但由于共享特性,这可能不是在一个主机上运行多个容器的良好设置 .

  • 2

    要添加到accepted answer iptables 解决方案,我必须在主机上再运行两个命令才能将其打开到外部世界 .

    HOST> iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.17.0.2:443
    HOST> iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source 172.17.0.2 --destination 172.17.0.2 --dport https
    HOST> iptables -A DOCKER -j ACCEPT -p tcp --destination 172.17.0.2 --dport https
    

    注意:我正在打开端口https(443),我的docker内部IP是 172.17.0.2

    注意2:这些规则和时间只会持续到容器重新启动

  • 24

    以下是一些解决方案:

    https://forums.docker.com/t/how-to-expose-port-on-running-container/3252/12

    运行容器时映射端口的解决方案 . docker run -d --net = host myvnc,它将自动公开并将端口映射到您的主机

  • 27

    如果某人没有回答 - 请检查您的目标容器是否已在docker network中运行:

    docker inspect my-target-container | grep NetworkMode
            "NetworkMode": "my-network-name",
    

    如果是,则应在同一网络中运行代理容器 .

    docker inspect my-target-container | grep -A2 Aliases
                    "Aliases": [
                        "my-alias",
                        "23ea4ea42e34a"
    

    最终命令:

    docker run --net my-network-name --name my-new-proxy \
    -d -p 8080:1234 alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:my-alias:80
    
  • 4

    您可以使用SSH创建隧道并在主机中公开容器 .

    您可以通过两种方式执行此操作,从容器到主机以及从主机到容器 . 但是你需要一个像OpenSSH这样的SSH工具(一个是客户端,另一个是服务器) .

    例如,在容器中,您可以这样做

    $ yum install -y openssh openssh-server.x86_64
    service sshd restart
    Stopping sshd:                                             [FAILED]
    Generating SSH2 RSA host key:                              [  OK  ]
    Generating SSH1 RSA host key:                              [  OK  ]
    Generating SSH2 DSA host key:                              [  OK  ]
    Starting sshd:                                             [  OK  ]
    $ passwd # You need to set a root password..
    

    您可以从此行(在容器中)找到容器IP地址:

    $ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*/\1/g'
    172.17.0.2
    

    然后在主机中,您可以这样做:

    sudo ssh -NfL 80:0.0.0.0:80 root@172.17.0.2
    

相关问题