运行 apache 的 Docker 容器总是暴露端口 80

Posted

技术标签:

【中文标题】运行 apache 的 Docker 容器总是暴露端口 80【英文标题】:Docker container running apache always exposing port 80 【发布时间】:2019-09-18 21:24:28 【问题描述】:

我有一个运行 Apache 的 Docker 映像,并且我已将 Apache(通过 httpd.conf)配置为侦听端口 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”实例时,除了我添加到 httpd.conf 中的“Listen 8080”实例之外,什么都没有显示。 p>

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 \"%Refereri\" \"%User-Agenti\"" combined
        LogFormat "%h %l %u %t \"%r\" %>s %b" common

        <IfModule logio_module>
                LogFormat "%h %l %u %t \"%r\" %>s %b \"%Refereri\" \"%User-Agenti\" %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 - 当然,我已经添加了它以及我的入口点脚本。如您所见,它现在非常基本,但我计划在以后添加它。 【参考方案1】:

DockerHub page 提供了有关如何执行此操作的提示。必须通过 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

【讨论】:

【参考方案2】:

httpd:2.4 映像的父 Dockerfile 公开端口 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/2210https://github.com/moby/moby/issues/3465

在我认为正确的答案中添加@BMitch 注释,因为容器可以在同一个网络中相互通信,而与暴露的端口无关 -

根据@BMitch -

EXPOSE 只是文档/元数据。它不会改变方式 容器相互通信。 docker ps 只是让你 知道图像创建者记录的端口可以发布但还没有 一直(因为地图没有主机端)。没有什么可做的 除非您有代码或用户坚持这样做,否则请在此处更改 文档与您的环境相匹配。为此,您必须 重建上游镜像。

【讨论】:

所以检查我的理解 - 你是说在 Docker 世界中,“公开”和“发布”一个端口被认为是两个不同的事情?即使我没有明确发布一个端口(使用-p 8080:8080-P),一个端口仍然可能会被暴露,即使它不会被监听并且因此无法使用? 没错。 Expose 是容器原生的,Publish 是外部世界的。 这对我来说是个大问题。我无法将httpd:2.4 的 Dockerfile 添加到我自己的文件中,因为我们的公司防火墙阻止了一些命令。而且我不能使用Listen 80,因为我无法使用 root 用户(我将添加到 Dockerfile 的其他内容需要特定用户)。我个人很乐意让端口 80 暴露,但由于我使用 Azure 应用服务来为我的应用程序提供服务,这不是问题 - 因为 80 和 8080 都暴露,所以应用服务只是发布两者中最低的一个,即端口 80(似乎没有办法覆盖)。 EXPOSE 只是文档/元数据。它不会改变容器之间的通信方式。 docker ps 只是让您知道图像创建者记录的端口可以发布但尚未发布(因为地图没有主机端)。除非您的代码或用户坚持此文档与您的环境相匹配,否则这里没有什么可更改的。为此,您必须重建上游映像。 是的,完全正确。无论暴露的端口如何,容器都可以在同一网络中相互通信。已将您的宝贵(一如既往:-))评论添加到答案中。

以上是关于运行 apache 的 Docker 容器总是暴露端口 80的主要内容,如果未能解决你的问题,请参考以下文章

Docker 容器部署Apache静态网站

无法通过容器的 ip 和通过 http 暴露的端口访问 Docker 容器中的 Web 应用程序

似乎无法将 docker 容器端口暴露给主机

Gitlab CI runner 无法暴露嵌套 Docker 容器的端口

Docker 容器无法连接到 Redis

Postgres 9.1 的 Docker 容器未将端口 5432 暴露给主机