Общение между docker-container внутри php-приложений

410
05 августа 2017, 05:11

Есть веб-сервисы внутри докер-контейнеров, назовем их api1 и api2.

Внутри api1 определены следующие маршруты:

Route::get('/test', function () {
return view('welcome');
});
$router->group(['prefix' => '/v1/user'], function ($router) {
    $router->post('/auth', 'Api\SignInController@auth');
    $router->post('/request-password-reset', 'Api\SignInController@request_password_reset');
    $router->post('/set-password', 'Api\SignInController@set_password');
    $router->post('/set-password/by-token', 'Api\SignInController@set_password_by_token');
});

$router->group(['prefix' => '/v1/session'], function ($router) {
    $router->post('/check-session', 'Api\SessionController@checkSession');
    $router->post('/expire', 'Api\SessionController@expire');
});

Из системы http-запросы он принимает исправно. А вот когда возникла необходимость из одного сервиса обращаться в другой (использую guzzle) возникли проблемa:

curl запрос не находит адрес контейнера, который доступен извне, при этом, если запрос идет на имя контейнера - он доходит, но возвращает какой-то бред.

Итак, мой docker-compose.yml:

version: "3.0"
services:
app-api-signin:
   container_name: app-api-signin
   build: docker/common/php
   working_dir: /app
   volumes:
     - ./api_signin:/app
   expose:
     - 9000
   links:
     - mysql
     - memcached
 nginx-api-signin:
   container_name: nginx-api-signin
   image: nginx:latest
   ports:
     - "127.0.0.2:8081:80"
   volumes:
     - ./api_signin:/app
     - ./docker/common/api-signin/nginx/vhost.conf:/etc/nginx/conf.d/vhost.conf
   links:
     - app-api-signin
app-api-cabinet:
   container_name: app-api-cabinet
   build: docker/common/php
   working_dir: /app
   volumes:
     - ./api_cabinet:/app
   expose:
     - 9000
   links:
     - mysql
     - memcached
     - nginx-api-signin
nginx-api-cabinet:
   container_name: nginx-api-cabinet
   image: nginx:latest
   ports:
     - "127.0.0.2:8083:80"
   volumes:
     - ./api_cabinet:/app
     - ./docker/common/api-cabinet/nginx/vhost.conf:/etc/nginx/conf.d/vhost.conf
   links:
     - app-api-cabinet
 mysql:
   container_name: mysql
   image: mysql:5.7
   ports:
     - "3306:3306"
   volumes:
     - ./db-data:/var/lib/mysql:rw
 memcached:
   container_name: memcached
   image: memcached:latest
   ports:
     - "11211:11211"

Что я пробовал делать:

1) Слать запросы на адрес nginx:

$client = new Client([
   'base_uri' => 'http://127.0.0.2:8081'
]);
$g_request = new \GuzzleHttp\Psr7\Request('POST', '/api/v1/session/check-session');
$response = $client->send($g_request);
dd($response->getBody());

Ответ:

ConnectException
cURL error 7: Failed to connect to 127.0.0.2 port 8081: Connection refused

2) Слать запрос, используя вместо ip-адреса имя docker-container:

$client = new Client([
   'base_uri' => 'http://nginx-api-signin'
]);
$g_request = new \GuzzleHttp\Psr7\Request('POST', '/api/v1/session/check-session');
$response = $client->send($g_request);
dd($response->getBody());

Ответ:

ClientException
Client error: `POST http://nginx-api-signin/api/v1/session/check-session` resulted in a `404 Not Found` response:
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr> (truncated...)

3) Слать запрос просто на докер-контейнер меняя метод запроса на get:

$client = new Client([
   'base_uri' => 'http://nginx-api-signin/'
]);
$g_request = new \GuzzleHttp\Psr7\Request('GET', '/');
$response = $client->send($g_request);
dd($response->getBody());

Ответ (не является ошибкой):

Stream {#176
  -stream: stream resource @12
  wrapper_type: "PHP"
  stream_type: "TEMP"
  mode: "w+b"
  unread_bytes: 0
  seekable: true
  uri: "php://temp"
  options: []
  }
  -size: null
  -seekable: true
  -readable: true
  -writable: true
  -uri: "php://temp"
  -customMetadata: []
 }

Из этого всего видно, что доступ к docker-containerу получаю в последнем случае, но как мне обратиться к своему api, используя имя docker-container'a?

Answer 1
Как происходит сетевое общение между контейнерами?

По умолчанию Docker использует bridge-сетку для каждого контейнера, выдающую ему адрес из внутреннего диапазона (например, 172.17.0.2) и позволяющую обращаться к ресурсам внешней сети. В случае с Docker Сompose такая сеть создается для каждой композиции контейнеров:

etki@kepler:/tmp > docker-compose up -d
Creating network "tmp_default" with the default driver
Creating tmp_nginx_1
etki@kepler:/tmp > docker network inspect tmp_default
[
  {
    "Name": "tmp_default",
    ...
    "Driver": "bridge",
    ...
  }
]

Это значит следующее:

  • К каждому контейнеру можно обратиться по его айпи-адресу
  • Каждый контейнер может обратиться к внешним (по отношению к машине) ресурсам, и его пакеты будут проброшены через основную сеть.
  • Обращения к 127.0.0.0/8 обрабатываются loopback-сетью, т.е. не выходят из контейнера в принципе

Кроме того, в docker встроен DNS, которым пользуется Docker Compose. Внутри созданной сетки имена контейнеров будут резолвиться в их адреса:

etki@kepler:/tmp > docker exec tmp_nginx_1 getent hosts nginx
192.168.0.2     nginx

Таким образом, для обращения к контейнерам спеки nginx-api-signin действительно правильней всего указывать nginx-api-signin в качестве хоста, потому что такой запрос уйдет на внутренний ДНС и зарезолвится в реальные айпишники контейнеров. Nginx, в свою очередь, должен использовать app-api-signin и app-api-cabinet в качестве адресов FCGI-обработчиков.

Что в этом случае может быть неправильно в примере?

Насколько видно, до nginx получается достучаться, однако он отдает 404 и 405. Это означает просто то, что nginx неправильно сконфигурирован и не воспринимает приходящие запросы как приходящие на нужный сервер; скорее всего, у него внутри нет хоста nginx-api-signin, а default_server занимается обычной раздачей статики. Должно быть достаточно создать этот хост, чтобы запросы к нему начали отрабатывать корректно.

В случае каких-либо проблем можно включить дебаг-лог nginx, указав формат debug для error_log и изменив команду контейнера на [nginx-debug, -g, 'daemon off;']

READ ALSO
Убрать переносы в атрибуте xml при помощи php

Убрать переносы в атрибуте xml при помощи php

Есть xml файл выгруженный в строку вида

318
преобразование base64 в файл

преобразование base64 в файл

С клиента на сервер приходит json формы, в одном из полей которой храниться картинка в виде base64Как эту картинку средствами php5 сохранить на сервер...

284
Вывод checked елементов в таблицу на PHP

Вывод checked елементов в таблицу на PHP

Здравствуйте,у меня есть json файл который передается с формы на серверКак мне вывести все checked элементы в таблицу на PHP для отправки на эмейл

191
В чем может быть проблема c foreach?

В чем может быть проблема c foreach?

Почему такой результат???? Должен же быть:

189