【问题标题】:Docker: exposing multiple ports which applications use inside a container which are not pre-determinedDocker:暴露应用程序在容器内使用的多个端口,这些端口不是预先确定的
【发布时间】:2018-07-24 03:40:12
【问题描述】:

服务器.pl

sub sock_initialize {
    my $sock  = q{};
    my $port  = q{};

    # Get a port for our server.
    $sock = IO::Socket::INET->new(
        Listen    => SOMAXCONN,    # listen queue depth
        LocalPort => 0,
        Reuse     => 1
    );

    die "Unable to bind a port: $!" if !$sock;

    $port      = $sock->sockport();
    my $ip = "";
    my $uid = (getpwuid( $> ))[2];
    my $queue = join(":", $ip, $port, $$, $uid);

    print sprintf("put started on port $port ($$), SOMAXCONN=%d\n", SOMAXCONN);
    return $sock;
} ## end sub sock_initialize

my $listen_sock = sock_initialize();
while (1) {
    #my $xsock = Accept();
    my $xsock;
    while (1) {
        $! = 0;
        # Accept can block.  Need to use nonblocking poll (Stevens)
        $xsock = $listen_sock->accept;    # ACCEPT
        last if defined $xsock;
        next if $! == EINTR;
        die "accept error: $!";
        if ( defined $xsock ) {
            $xsock->blocking(0);    # mark executor socket nonblocking
            $xsock->sockopt( SO_KEEPALIVE() => 1 ) or die "sockopt: $!";
        }


    #my $rbufp = $conn->readbufref;
    #my $rdstatus = Read( $sock, $rbufp );
    my $buff = "";

    while (1) {
        $! = 0;
        # Accept can block.  Need to use nonblocking poll (Stevens)
        $xsock = $listen_sock->accept;    # ACCEPT
        last if defined $xsock;
        next if $! == EINTR;
        die "accept error: $!";
        if ( defined $xsock ) {
            $xsock->blocking(0);    # mark executor socket nonblocking
            $xsock->sockopt( SO_KEEPALIVE() => 1 ) or die "sockopt: $!";
        }
    }

    #my $rbufp = $conn->readbufref;
    #my $rdstatus = Read( $sock, $rbufp );
    my $buff = "";
    while (1) {
        my $nbytes = sysread $xsock, $buff, 32768, length($buff);    # SYSCALL

        if ( !defined $nbytes ) {                            # read error
            next if $! == EINTR;
            last if $! == EWOULDBLOCK;                       # normal
            return;
        }
        last if $nbytes == 0;                            # EOF
    }
    print "received $buff\n";
    last;
}

client.pl

my $host = "localhost";
my $port = 37402; # get port number from server.pl


my $s = IO::Socket::INET->new (PeerAddr => $host,
                                        PeerPort => $port,
                                        Type     => SOCK_STREAM,
                                        Proto    => 'tcp',
                                        Timeout  => 1);
if ($s) {
    $s->blocking (0) ;
}

my   $nbytes = syswrite $s, "hi from X";   # SYSCALL

首先,我将启动 server.pl

$perl test_socket_server.pl 
$put started on port 37402 (16974), SOMAXCONN=128

然后我会把端口号放在client.pl上

perl test_socket_client.pl

然后,在 server.pl shell 上,我看到了

received hi from X

所以,它按预期工作。 现在,当我通过

将 server.pl 放入容器中时
docker run ubuntu perl server.pl
put started on port 38170 (1), SOMAXCONN=128

然后,我会在 client.pl 中写入端口号并运行它,但是 server.pl 没有收到消息

我的理解是容器端口不会通过 EXPOSE 暴露给主机

现在,即使问题可以通过 EXPOSE 解决,server.pl 也会连接到未分配的端口,即每次运行时色情号码都会发生变化LocalPort => 0, server.pl 在容器内运行。我的理解是您必须在容器运行时公开端口,但此时您不知道 server.pl 将在哪个端口运行。我希望它是这种方式,没有指定端口,因为 server.pl 的多个实例可以在一个容器中运行(因此需要能够使用不同的端口)。有什么策略可以解决这个问题吗?

你可以在启动容器时公开端口范围,也许是 30000 及以上? [我已经阅读了一些关于暴露端口范围的其他堆栈溢出问题,但它似乎存在一些性能问题,因为每个端口都分叉了一个真正的进程(?)Docker expose all ports or range of ports from 7000 to 8000 理想的解决方案是以某种方式只暴露应用程序正在使用的端口它在运行时驻留在容器中。也许这是由协调器完成的?

【问题讨论】:

  • 您能否链接处理使用端口 ragens 时的性能问题的帖子?为什么你要提供服务器和客户端的源代码?很难直截了当,而且我认为无论如何您都不愿意更改代码以使用固定端口,因此即使nc -l -p $((RANDOM%9999)) 也可以证明您要实现的目标,但我会省略这些细节.
  • 重提你的问题:你没有提到使用网络选项 host 的参数,比如:--net=host 所以你不需要首先绑定/公开端口,而是端口获取需要时直接绑定
  • @Murmel 这是一个被容器化的遗留代码,并且希望在不更改应用程序的情况下在容器中按原样工作。代码是版本化的,如果我们进行基础设施更改(容器化),我们必须将修复/功能应用于所有先前版本,即 --net=host
  • 好吧,我不太确定你是否明白 --net=host 选项是 docker API 的一部分(并且不建议作为附加参数添加到你的应用程序中),所以我将此点添加到我的答案的可能选项列表中。但可能是,我错过了你的观点,为什么这不是你的选择。

标签: perl docker


【解决方案1】:

选项 1 - Docker swarm 和覆盖网络

Docker swarm 的overlay network 中的容器(更准确地说:服务)默认公开所有端口。在这种情况下,覆盖网络或多或少地充当专用网络。因此,您的 client 应用程序只能连接到 server 容器/服务,前提是它也是 swarm 的一部分。
但是,如果这并非如此,您仍然可以使用service update --publish-add API 来解决这种情况。此命令提供了在运行时更改端口暴露的可能性(这或多或少是您要求的),有关更多信息,请查看Publish ports on an overlay network 部分:

连接到同一个覆盖网络的 Swarm 服务有效地将所有端口相互公开。要在服务外部访问端口,必须使用 docker service create 或 docker service update 上的 -p 或 --publish 标志发布该端口。支持旧的冒号分隔语法和新的逗号分隔值语法。较长的语法是首选,因为它有点自我记录。

如果您想使用 Docker API 来解决您的问题,这将是最佳选择,它也将完美地处理多主机场景。但是对于高级部分(您的客户端不属于 swarm 并且您想使用 service update),您必须将容器包装到服务中。
注意:--publish-add 并没有经过深思熟虑,因为 docker service update 通过重新启动容器来工作,因此您的应用程序很可能会切换到另一个端口,这可能是一个稍微不同的用例的选项。

选项 2 - --net=host

如果您的所有 服务器 容器无论如何都应该在一台主机上运行,​​您可以轻松使用 Docker 的--net=host 功能,而不必显式公开任何端口。

例子:

docker run --net=host ubuntu perl server.pl

选项 3 - 端口代理

您还可以使用socatssh 等应用程序手动将服务器应用程序的端口映射到每个容器的预定义端口。例如,参见 this answerExposing a port on a live Docker container

【讨论】:

  • 感谢您的回答。我认为我需要使用的是 macvlan 网络,而不是覆盖网络,因此 client.pl 不需要在容器中并且能够连接到它
猜你喜欢
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-29
  • 2018-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多