Docker容器运行Apache始终暴露端口80。

9

我有一个运行Apache的Docker镜像,并且我已经通过httpd.conf配置了Apache监听端口8080

Listen 8080

当我构建图像并运行时,我能够成功通过端口 8080 连接到我的网站,所以在这一点上一切都很顺利。
docker build -t my/apache:8080 .
docker run --name "MyWebsite" -p 8080:8080 -v ~/dir:/mnt/dir -d -t my/apache:8080

然而,当我使用docker ps命令列出我的运行容器时,我发现端口80也被暴露了,原因不明。

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                            NAMES
23c4e1f0ea66        my/apache:8080      "/docker-entrypoint.…"   12 minutes ago      Up 12 minutes       80/tcp, 0.0.0.0:8080->8080/tcp   MyWebsite

当我登录正在运行的容器并搜索“Listen 80”的实例时,除了我添加到的“Listen 8080”实例外,没有任何实例显示。
docker exec -it 23c4e1f0ea66 /bin/bash
grep -ri "Listen 80"

我的Dockerfile只包含一个EXPOSE声明- EXPOSE 8080。然而,我不认为这实际上会暴露端口,并且更多地是作为一种记录当运行利用该镜像的容器时应该暴露哪个端口的方式。
如何找出何时暴露端口80,重要的是,如何阻止它被暴露?

Dockerfile

FROM httpd:2.4

COPY httpd.conf /usr/local/apache2/conf/
COPY docker-entrypoint.sh /

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["apache"]

### Apache (proxies to MapProxy).
EXPOSE 8080

入口脚本

#!/bin/bash
set -e

if [ "$1" = 'apache' ]; then
        echo "Starting Apache"
        httpd-foreground
fi

exec "$@"

HTTP配置

ServerRoot "/usr/local/apache2"

Listen 8080

LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
<IfModule !mpm_prefork_module>
</IfModule>
<IfModule mpm_prefork_module>
</IfModule>
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so

<IfModule unixd_module>
        User daemon
        Group daemon
</IfModule>

ServerAdmin applicationdelivery@landmark.co.uk
ServerName mapproxy.gcs.lmkcloud.net:8080
DocumentRoot "/usr/local/apache2/htdocs"
ErrorLog /proc/self/fd/2

LogLevel warn

<IfModule log_config_module>
        LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
        LogFormat "%h %l %u %t \"%r\" %>s %b" common

        <IfModule logio_module>
                LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
        </IfModule>

        CustomLog /proc/self/fd/1 common

</IfModule>

<IfModule mime_module>
        TypesConfig conf/mime.types
        AddType application/x-compress .Z
        AddType application/x-gzip .gz .tgz
</IfModule>

<IfModule ssl_module>
        SSLRandomSeed startup builtin
        SSLRandomSeed connect builtin
</IfModule>

ProxyPreserveHost On
ProxyPass / http://example.com:8001/ retry=1 acquire=3000 timeout=20 Keepalive=On
ProxyPassReverse / http://example.com:8001/

请问您能否添加Dockerfile文件? - vivekyad4v
@vivekyad4v - 当然,我已经添加了它,还有我的入口脚本。正如你所看到的,现在它非常基础,但我计划以后继续完善它。 - David Gard
2个回答

11

父Dockerfile为httpd:2.4镜像暴露了端口80 -
https://github.com/docker-library/httpd/blob/75e85910d1d9954ea0709960c61517376fc9b254/2.4/Dockerfile

Dockerfile中的EXPOSE语句最终会在docker ps中输出。但是,它只对容器网络公开,并不允许通过定义的端口与同一网络外的容器或主机进行通信。要允许这种情况发生,您需要发布端口。


例子 -

docker run -dit --expose 8008 httpd:2.4

输出 -

CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                  PORTS                              NAMES
d628b537aded        httpd:2.4                 "httpd-foreground"       3 seconds ago       Up 2 seconds            80/tcp, 8008/tcp                   objective_dewdney

这将暴露容器端口。参数--expose等同于在Dockerfile中使用EXPOSE声明。


现在让我们尝试发布端口 -

docker run -dit -p 8009 httpd:2.4

输出 -

CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                  PORTS                              NAMES
2c8c93a78e97        httpd:2.4                 "httpd-foreground"       2 seconds ago       Up 2 seconds            80/tcp, 0.0.0.0:32768->8009/tcp    keen_swirles

看到0.0.0.0:32768,现在它已经使用一个随机的短暂端口 (例如32768) 发布到主机。您也可以将其发布到特定的主机端口。


例子 -

docker run -dit -p 8009:8009 httpd:2.4

输出 -

CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                  PORTS                              NAMES
1023df9822e5        httpd:2.4                 "httpd-foreground"       2 seconds ago       Up 2 seconds            80/tcp, 0.0.0.0:8009->8009/tcp     fervent_almeida

简而言之,目前无法从父Dockerfile中取消暴露端口80的方式。您当然可以暴露更多端口。
这是一个开放问题 - https://github.com/moby/moby/issues/2210 https://github.com/moby/moby/issues/3465 将@BMitch的评论添加到答案中,我认为他的评论非常准确,因为容器可以在同一网络中相互通信,而不管暴露了哪个端口 -
根据@BMitch的说法 -
EXPOSE仅是文档/元数据。它不会改变容器之间的通信方式。docker ps只是让您知道映像创建者记录的端口可以发布,但尚未(因为没有主机端的映射)。除非您有坚持此文档与环境匹配的代码或用户,否则不需要更改任何内容。对于这个问题,您必须重建上游映像。

那么,为了检查我的理解 - 您是说在Docker世界中,“暴露(exposing)”和“发布(publishing)”端口被认为是两个不同的事情?即使我没有显式地发布端口(使用-p 8080:8080-P),一个端口仍然可能会被暴露,尽管它不会处于监听状态,因此无法使用? - David Gard
没错。Expose 是容器本身的,而 publish 则是为了外部世界。 - vivekyad4v
这对我来说是一个巨大的问题。我无法复制 httpd:2.4 的 Dockerfile 并将其添加到我的文件中,因为我们公司的防火墙会阻止其中一些命令。而且我不能使用 Listen 80,因为我无法使用 root 用户(Dockerfile 中需要添加其他内容,需要特定用户)。我个人愿意将端口 80 暴露出来,但由于我正在使用 Azure 应用服务来提供我的应用程序,所以这不是问题——因为 80 和 8080 端口都已经暴露了,应用服务只会发布其中较低的端口,即端口 80(似乎没有办法覆盖)。 - David Gard
2
“EXPOSE”仅仅是文档/元数据,它不会改变容器之间的通信方式。“docker ps”只是告诉你一个端口可以被发布,但是由于没有映射的主机端,它还没有被发布。除非您有代码或用户坚持这份文档与您的环境相匹配,否则这里没有任何需要更改的地方。为此,您必须重新构建上游镜像。 - BMitch
是的,没错。容器可以在同一网络中相互通信,而不受暴露端口的影响。我已经将您宝贵的(像往常一样 :-)) 评论添加到答案中了。 - vivekyad4v
将其标记为答案,因为它完全解释了正在发生的事情。不幸的是,由于父Docker镜像中暴露了端口80,我将无法按计划使用Azure Web App。 - David Gard

4

DockerHub 页面 上有一个提示,说明如何完成这个操作。必须获取并通过 Dockerfile 将备用配置文件添加到容器中。

首先要获取配置文件的副本:

docker run --rm httpd:2.4 cat /usr/local/apache2/conf/httpd.conf > my-httpd.conf

然后编辑 my-httpd.conf 文件并修改端口:

Listen 8080

最后,在Dockerfile中添加指令将其复制:
COPY ./my-httpd.conf /usr/local/apache2/conf/httpd.conf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接