ReactPHP и докер: не удалось разрешить хосты из php в контейнер nginx

Работаю с докером и микросервисами. Представьте, что у нас есть 3 сервиса:

          user API
        /
gateway API
        \
          hotel API

Запросы проходят через шлюз к сервисам. У меня есть контейнер с php и nginx, где объемы одинаковы. Далее привожу список конфигов:

server {
    server_name gateway.api.loc;

    root /var/www/der-ibe/ibe-gateway-api/web;
    client_max_body_size 32m;

    location / {
        try_files $uri @rewriteapp;
    }

    location @rewriteapp {
        rewrite ^(.*)$ /app.php/$1 last;
    }

    location ~ ^/(app|app_dev|config)\.php(/|$) {
       ...
    }
}
server {
    server_name hotel.api.loc;
    root /var/www/der-ibe/ibe-hotel-api/web;
    client_max_body_size 32m;

    location / {
        try_files $uri @rewriteapp;
    }

    location @rewriteapp {
        rewrite ^(.*)$ /app.php/$1 last;
    }

    location ~ ^/(app|app_dev|config)\.php(/|$) {
        ...
    }
}

...

docker-compose.yml

version: "3.1" 
services:

    tphp:
        build: docker/php
        restart: always
        container_name: my_tphp
        extra_hosts:
            - gateway.api.loc:172.14.10.10
            - user.api.loc:172.14.10.10
            - hotel.api.loc:172.14.10.10
        networks:
            - test
        volumes:
            - ../user-api:/var/www/user-api
            - ../hotel-api:/var/www/hotel-api
            - ./:/var/www/gateway-api

    tnginx:
        build: docker/nginx
        restart: always
        container_name: my_tnginx
        ports:
            - 80:80
        networks:
            test:
                 ipv4_address: 172.14.10.10
        volumes:
            - ../user-api:/var/www/user-api
            - ../hotel-api:/var/www/hotel-api
            - ./:/var/www/gateway-api

networks:
    ibe:
      driver: bridge
      ipam:
        driver: default
        config:
          - subnet: 172.14.10.0/24

Если я использую простой cURL, я могу связаться с API отеля и пользователя и получить от них ответ, но если я попытаюсь сделать это с помощью reactPHP - я не смогу! Мой мир кода:

 public function asyncGetRequest(Route $route, $params, $token)
{
    $url = $this->buildURL($route, $params);

    $loop = React\EventLoop\Factory::create();

    $dns = '172.14.10.10';
    $sender = Clue\React\Buzz\Io\Sender::createFromLoopDns($loop, $dns);

    //$url = 'http://172.14.10.10:3000/api/quota/list';

    $client = new Clue\React\Buzz\Browser($loop, $sender);
    //$client = new Clue\React\Buzz\Browser($loop);

    $promise = $client->get($url,
        [
            'Authorization' => $token,
        ]
    );

    /** @var Response $response */
    $response = new Response();

    try {
        $response = Clue\React\Block\await($promise, $loop);
    } catch (ResponseException $e) {
        $this->logger->error($e->getMessage());
    }

    dump($response->getBody()->getContents()); // I work on symfony 3.2
    die;
}

Библиотека Clue — это просто оболочка для react. Итак, ошибка, которую я получаю: DNS query for hotel.api.loc timed out и последняя ошибка An error occurred in the underlying stream.

Вот у меня вопрос: что я делаю не так? Почему у меня есть доступ к контейнеру nginx с помощью curl, но нет доступа с помощью reactPHP? Даже если указать DNS!

Но если я использую голый ip с портом, я могу до него добраться. Я не профи в днс, просто базовые знания.

ОБНОВЛЕНИЕ

Я использую Linux Debian (в контейнере). Работайте на Mac.

Немного о логике: когда URI запроса gateway.api.loc/api/hotel/list внутри шлюза, мы отправляем запрос на hotel.api.loc на тот же URL (/api/hotel/list), принимаем ответ и возвращаем его.

Повторюсь, у меня есть доступ к доменам по cURL от, но не могу сделать это с помощью reactPHP и не понимаю почему...


person Nikita_kharkov_ua    schedule 05.09.2017    source источник
comment
Какую ОС вы используете? Пожалуйста, обновите это в своем вопросе   -  person Tarun Lalwani    schedule 06.09.2017
comment
Я нашел решение без использования DNS, просто прямой запрос. Я запрашиваю http://172.14.10.10/api/doc с заголовком 'Host' => 'user.api.loc', и он работает. Но если вы скажете мне, почему я не могу запросить напрямую user.api.loc/api/doc внутри gateway, я буду очень благодарен   -  person Nikita_kharkov_ua    schedule 06.09.2017
comment
Отлично, спасибо, что поделились   -  person Tarun Lalwani    schedule 06.09.2017


Ответы (2)


Ваша проблема в том, что нет разрешения DNS для gateway.api.loc или hotel.api.loc, они определены в вашей конфигурации Nginx для маршрутизации, но больше нигде для разрешения DNS. Вам нужно изменить композицию на ниже, чтобы они также разрешались в разрешении DNS докера.

version: "3.1" 
services:

    tphp:
        build: docker/php
        restart: always
        container_name: my_tphp
        extra_hosts:
            - gateway.api.ibe.lazy-ants.loc:172.14.10.10
            - user.api.ibe.lazy-ants.loc:172.14.10.10
            - hotel.api.ibe.lazy-ants.loc:172.14.10.10
        networks:
            - test
        volumes:
            - ../user-api:/var/www/user-api
            - ../hotel-api:/var/www/hotel-api
            - ./:/var/www/gateway-api

    tnginx:
        build: docker/nginx
        restart: always
        container_name: my_tnginx
        ports:
            - 80:80
        networks:
            test:
                 ipv4_address: 172.14.10.10
                 aliases:
                     - hotel.api.loc
                     - gateway.api.loc
        volumes:
            - ../user-api:/var/www/user-api
            - ../hotel-api:/var/www/hotel-api
            - ./:/var/www/gateway-api

networks:
    ibe:
      driver: bridge
      ipam:
        driver: default
        config:
          - subnet: 172.14.10.0/24

Это разрешение DNS будет работать только внутри компоновки, а не снаружи. Поэтому, если вы используете его снаружи, вам придется делать записи хоста.

Редактировать-1

Кажется, ваша проблема может быть только в коде. Вы пытаетесь использовать IP-адрес шлюза в качестве DNS, когда он не является DNS-сервером.

Изменять

$loop = React\EventLoop\Factory::create();

$dns = '172.14.10.10';
$sender = Clue\React\Buzz\Io\Sender::createFromLoopDns($loop, $dns);

//$url = 'http://172.14.10.10:3000/api/quota/list';

$client = new Clue\React\Buzz\Browser($loop, $sender);

to

$loop = React\EventLoop\Factory::create();

$dns = '172.14.10.10';
//$sender = Clue\React\Buzz\Io\Sender::createFromLoopDns($loop, $dns);
//$url = 'http://172.14.10.10:3000/api/quota/list';

$client = new Clue\React\Buzz\Browser($loop);

И посмотрите, поможет ли это

person Tarun Lalwani    schedule 05.09.2017
comment
Плохая новость: у меня не работает, и думаю не только у меня. Псевдонимы - это просто псевдоним для имени контейнера, а не для имени хоста. - person Nikita_kharkov_ua; 05.09.2017
comment
Псевдонимы предназначены для разрешения IP-адресов с использованием DNS, а не имени хоста. Пожалуйста, объясните мне, почему это не работает? Откуда вы запускаете код PHP и какую именно ошибку вы получаете? - person Tarun Lalwani; 05.09.2017
comment
Все как я показал в примере. Этот метод находится на контроле, метод из сервиса и вызывается из него. Ошибка: [1/4] TimeoutException: Timed out after 5 seconds +, [2/4] TimeoutException: DNS query for user.api.ibe.lazy-ants.net timed out [3/4] RuntimeException: DNS query for user.api.ibe.lazy-ants.net failed: too many retries [4/4] RuntimeException: An error occurred in the underlying streamредактирую docker-compose.yml, пожалуйста, исправьте и это; спасибо) - person Nikita_kharkov_ua; 05.09.2017
comment
Я забыл сказать, что когда я запрашиваю gateway.api.loc/api/user/list шлюз, он преобразуется, и запрос становится user.api.loc/api/user/list, и он запрашивается пользователем API с помощью reactPHP внутри проекта gateway. - person Nikita_kharkov_ua; 06.09.2017
comment
Теперь рассматриваемый докер-композитор прав. Пожалуйста, исправьте свой ответ. И я так хочу получить ответ! :) - person Nikita_kharkov_ua; 06.09.2017
comment
@Nikita_kharkov_ua, не понимаю, что вы имеете в виду. Реакция PHP, которую вы использовали, вероятно, была неправильной. Хотите понять, сработало ли для вас изменение кода или нет? - person Tarun Lalwani; 06.09.2017
comment
DNS Request did not return valid answer. - ошибка редактирования. - person Nikita_kharkov_ua; 06.09.2017

Проблема, которую вы видите, — это неполная реализация DNS в ReactPHP. Он не уважает /etc/resolv.conf и только недавно добавил поддержку /etc/hosts. Если вы не используете react/dns v0.4.11 или новее, тогда это вероятно, проблема, и вы должны попробовать обновить.

Я не уверен, как Docker разрешает DNS в наши дни для связанных контейнеров. Несколько лет назад это работало с подключенным /etc/hosts, который предоставляет имена хостов и соответствующие IP-адреса. Но я думаю, что в наши дни Docker использует внутренний DNS-сервер. Поскольку ReactPHP не выбирает это автоматически, вы должны явно установить его при создании преобразователя DNS.

Попробуйте установить 127.0.0.11 в качестве сервера имен, который является внутренним DNS Docker.

person kelunik    schedule 17.09.2017