简体   繁体   English

Docker:暴露应用程序在容器内使用的多个端口,这些端口是未预先确定的

[英]Docker: exposing multiple ports which applications use inside a container which are not pre-determined

server.pl server.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 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

First, i would start server.pl 首先,我将启动server.pl

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

Then i would put the port number on client.pl 然后我将端口号放在client.pl上

perl test_socket_client.pl

then, on server.pl shell, i see 然后,在server.pl shell上,我明白了

received hi from X

So, it's working as intended. 所以,它按预期工作。 Now when I put the server.pl inside a container via 现在,当我将server.pl放入容器中时

docker run ubuntu perl server.pl
put started on port 38170 (1), SOMAXCONN=128

And then, I would write the port number in client.pl and run it, but server.pl does not get the message 然后,我会在client.pl中编写端口号并运行它,但server.pl没有收到消息

My understanding is that container port isnt expose to host's via EXPOSE 我的理解是容器端口不会通过EXPOSE暴露给主机

Now, even if the problem could be fixed with EXPOSE, server.pl connects on an unassigned port ie the pornumber can change every time it runs LocalPort => 0, server.pl is run inside the container. 现在,即使可以使用EXPOSE修复问题,server.pl也会在未分配的端口上连接,即每次运行LocalPort => 0,色情内容都会更改LocalPort => 0, server.pl在容器内运行。 My understanding is you have to expose the port at container runtime but at this time, you dont know which port server.pl will run at. 我的理解是您必须在容器运行时公开端口,但此时,您不知道将运行哪个端口server.pl。 And I would like it to be this way, no designated port, as multiple instances of server.pl could be run in a container (so need to be able to use different ports). 我希望它是这样的,没有指定的端口,因为server.pl的多个实例可以在容器中运行(因此需要能够使用不同的端口)。 Is there a strategy to accommodate this? 是否有适应这种策略?

can you expose port ranges, perhaps, 30000 and upwards when starting container? 你可以在启动容器时暴露端口范围,可能是30000及以上吗? [I've read some other stack overflow questions on exposing port ranges but it seems to have some performance issues as a real process is forked per port (?) Docker expose all ports or range of ports from 7000 to 8000 The ideal solution would be somehow only exposing ports being used by the app which resides in a container at runtime. [我已经阅读了一些关于暴露端口范围的其他堆栈溢出问题,但它似乎有一些性能问题,因为实际进程是按端口分叉的(?) Docker将所有端口或端口范围从7000暴露到8000理想的解决方案是某种程度上只暴露应用程序在运行时驻留在容器中的端口。 Maybe that is accomplished by an orchestrator? 也许这是由一个协调者完成的?

Option 1 - Docker swarm & overlay network 选项1 - Docker群和覆盖网络

Containers (to be more precise: services) within the overlay network of a Docker swarm expose all ports per default. Docker swarm的覆盖网络中的容器(更准确地说:服务)默认显示所有端口。 The overlay network acts more or less as private network in this case. 在这种情况下,覆盖网络或多或少地作为专用网络。 Hence, your client application would only be able to connect to server container/service, if it is also part of the swarm. 因此,如果客户端应用程序也是群集的一部分,则它只能连接到服务器容器/服务。
However, if this is not the case, you could still solve this situation by using the service update --publish-add API. 但是,如果不是这种情况,您仍然可以使用service update --publish-add API来解决这种情况。 This command provides the possibility to change port exposure at runtime (which is more or less what you asked for), for further information have a look at the Publish ports on an overlay network section: 此命令提供了在运行时更改端口暴露的可能性(这或多或少是您要求的),有关详细信息,请查看覆盖网络上发布端口部分:

Swarm services connected to the same overlay network effectively expose all ports to each other. 连接到同一覆盖网络的群集服务有效地将所有端口相互暴露。 For a port to be accessible outside of the service, that port must be published using the -p or --publish flag on docker service create or docker service update. 对于可在服务外部访问的端口,必须使用docker service create或docker service update上的-p或--publish标志发布该端口。 Both the legacy colon-separated syntax and the newer comma-separated value syntax are supported. 支持遗留冒号分隔语法和较新的逗号分隔值语法。 The longer syntax is preferred because it is somewhat self-documenting. 较长的语法是首选,因为它有点自我记录。

This will be the best option if you want to use the Docker API to solve your problem and it will also perfectly deal with multi-host scenarios. 如果您想使用Docker API解决问题,这将是最佳选择,它也将完美地处理多主机方案。 But for the advanced part (where your client is not part of the swarm and you want to use the service update ), you will have to wrap your container into a service. 但是对于高级部分(您的客户端不是swarm的一部分而您想要使用service update ),您必须将容器包装到服务中。
Note: The --publish-add was not really thought through, as the docker service update works by restarting the container and therefore your application would very likely switch to another port, this could be an option for a slightly different use case. 注意:-- --publish-add并未真正考虑过,因为--publish-add docker service update通过重新启动容器来工作,因此您的应用程序很可能会切换到另一个端口,这可能是一个稍微不同的用例的选项。

Option 2 - --net=host 选项2 - --net = host

If all your server containers should run on one host anyway, you could easily use the --net=host feature of Docker and you don't have to explicitly expose any port. 如果所有服务器容器都应该在一台主机上运行,​​那么您可以轻松使用Docker的--net=host功能,而不必显式公开任何端口。

Example: 例:

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

Option 3 - port proxying 选项3 - 端口代理

You could also use applications like socat or ssh to manually map the port of the server application to a predefined port for every container. 您还可以使用socatssh等应用程序手动将服务器应用程序的端口映射到每个容器的预定义端口。 See for example this answer from Exposing a port on a live Docker container . 例如,在实时Docker容器上公开端口,请参阅此答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM