Docker Swarm 在 Nginx 中获取真实 IP(客户端主机)

我有一个包含 nginx 和 PHP 的堆栈,可以在 Docker Swarm 集群上运行.

I have a stack with nginx and PHP to run on Docker Swarm Cluster.

在我的 PHP 应用程序中,我需要从访问我的 web 应用程序的客户端主机获取包含真实 IP 的 remote_addr ($_SERVER['REMOTE_ADDR']).

In a moment in my PHP application, I need to get the remote_addr ($_SERVER['REMOTE_ADDR']) which contains the real IP from the client host accessing my webapp.

但问题是docker swarm集群通知nginx的IP.它显示了一个像 10.255.0.2 这样的内部 IP,但真正的 IP 是来自客户端主机的外部 IP(如 192.168.101.151).

But the problem is that the IP informed for nginx by docker swarm cluster. It's showed an Internal IP like 10.255.0.2, but the real IP it's the external IP from the client Host (like 192.168.101.151).

我该如何解决?

我的 docker-compose 文件:

My docker-compose file:

version: '3'

services:
  php:
    image: php:5.6
    volumes:
      - /var/www/:/var/www/
      - ./data/log/php:/var/log/php5
    networks:
      - backend
    deploy:
      replicas: 1
  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - /var/www/:/var/www/
      - ./data/log/nginx:/var/log/nginx
    networks:
      - backend
networks:
  backend:

我的 default.conf (vhost.conf) 文件:

My default.conf (vhost.conf) file:

server {
    listen          80;
    root            /var/www;
    index           index.html index.htm index.php;

    access_log  /var/log/nginx/access.log  main;
    error_log   /var/log/nginx/error.log error;

    location / {
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;

        try_files   $uri $uri/ /index.php;
    }

    location = /50x.html {
        root   /var/www;
    }

    # set expiration of assets to MAX for caching
    location ~* .(js|css|gif|png|jp?g|pdf|xml|oga|ogg|m4a|ogv|mp4|m4v|webm|svg|svgz|eot|ttf|otf|woff|ico|webp|appcache|manifest|htc|crx|oex|xpi|safariextz|vcf)(?[0-9]+)?$ {
            expires max;
            log_not_found off;
    }

    location ~ .php$ {
        try_files                   $uri =404;
        fastcgi_index               index.php;
        fastcgi_split_path_info     ^(.+.php)(/.+)$;
        fastcgi_pass                php:9000;
        include                     fastcgi_params;
        fastcgi_param               SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param               PATH_INFO       $fastcgi_path_info;
        fastcgi_read_timeout        300;
    }
}

我的 nginx 配置文件:

My nginx config file:

user  nginx;
worker_processes    3;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    keepalive_timeout   15;
    client_body_buffer_size     100K;
    client_header_buffer_size   1k;
    client_max_body_size        8m;
    large_client_header_buffers 2 1k;

    gzip             on;
    gzip_comp_level  2;
    gzip_min_length  1000;
    gzip_proxied     expired no-cache no-store private auth;
    gzip_types       text/plain application/x-javascript text/xml text/css application/xml;

    log_format  main  '$remote_addr - $remote_user [$time_local]  "$request_filename" "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    include /etc/nginx/conf.d/*.conf;
}

推荐答案

对于那些不想阅读所有 github 线程的人 ( https://github.com/moby/moby/issues/25526 ),对我有用的答案是将配置更改为:

for those don't want to read all the github thread ( https://github.com/moby/moby/issues/25526 ), the answer that was good for me was to change the config to this :

version: '3.7'
services:
  nginx:
    ports:
      - mode: host
        protocol: tcp
        published: 80
        target: 80
      - mode: host
        protocol: tcp
        published: 443
        target: 81

这仍然可以让内部覆盖网络工作,但使用 iptables 的一些技巧将这些端口直接转发到容器,以便容器内的服务看到数据包的正确源 IP 地址.

This still lets the internal overlay network work, but uses some tricks with iptables to forward those ports directly to the container, so the service inside the container see the correct source IP address of the packets.

iptables 中没有允许在多个容器之间平衡端口的功能,因此您只能将一个端口分配给一个容器(其中包括一个容器的多个副本).

There is no facility in iptables to allow balancing of ports between multiple containers, so you can only assign one port to one container (which includes multiple replicas of a container).

相关文章