首页 > 解决方案 > docker-compose 一个反向代理后面的应用程序,它也可以访问外部服务

问题描述

在本地服务器上,我启动了几个 gunicorn 实例,每个实例在唯一端口上运行不同的 Web 应用程序,作为内部网络其余部分的服务。现在我想将每个 gunicorn 实例放在它自己的 nginx 反向代理服务器后面,以实现更好的负载平衡。

我决定尝试将每个(nginx、gunicorn)对部署在自己的 Docker 容器中,只将其唯一的端口暴露给外界。我知道使用端口转发很容易 - 例如“5555:80”。但是,每个应用程序还可以访问独立于 Docker 在同一主机上运行的外部服务,例如数据库。

通过反复试验,我发现只有以“docker run --network=host ...”的方式运行Docker 容器才能访问外部服务(例如MySQL 或MongoDB)。这具有让容器共享主机网络的效果,但它也暴露了 gunicorn 打开的任何端口,这意味着它为客户端留下了绕过反向代理的方法。虽然这一切都在安全网络上的本地服务器上运行,但它似乎不是一个好的安全实践,因为它使后端容易受到拒绝服务攻击。

所以我想我想要两全其美 - 我希望每个(代理,gunicorn)对通过只有他们使用的专用网络相互交谈,同时我将一个端口(例如“5555”)暴露给网络,并且在 gunicorn 下运行的 web 应用程序仍然可以访问同一主机上的其他服务。

我的 nginx.conf 看起来像这样:

http {
    upstream app {
        server network1_app;
    }

    server {
        location / {
            proxy_pass  http://app;
        }
    }
}

我的 docker-compose.yml 可能看起来像:

version: "3"
services:

  proxy:
    image: nginx:alpine
    networks:
      - host
      - network1
    volumes:
      ./nginx.conf:/etc/nginx/nginx.conf:ro

  app:
    build: ./app
    networks:
      - network1
    ports: "5555:80" # Do I have to associate this with the "host" network somehow?

  networks:
    network1:

然后部署这个

docker stack deploy network1 -c docker-compose.yml

我是在正确的道路上,还是我让它太复杂了?完全不使用 Docker 会更直接吗?相反,我可以为此创建命名套接字。

如果可以的话,我喜欢使用 docker-compose,因为它封装了一些细节并使管理更容易(如果它有效的话)。它还为安全漏洞留下了更少的表面。

标签: dockernginxdocker-composegunicornnginx-reverse-proxy

解决方案


在阅读了这个线程并进行了一些试验和错误之后,我发现问题在于主机 iptables 将请求丢弃到除了端口 22 (ssh) 之外的几乎任何东西。

例如:

$ docker run -it --rm --add-host host.docker.internal:xxx.xxx.xxx.xxx busybox telnet host.docker.internal 27017

超时。

$ docker run -it --rm --add-host host.docker.internal:xxx.xxx.xxx.xxx busybox telnet host.docker.internal 22
Connected to host.docker.internal

我可以通过 iptables 中的一个简单规则来克服这个问题:

-A INPUT -i docker0 -j ACCEPT

然后在我的 docker-compose.yaml 中添加一个部分:

extra-hosts:
  - "host.docker.internal:xxx.xxx.xxx.xxx"

我暂时推迟了,因为我的项目负责人让我等待。


推荐阅读