Как получить доступ к внутреннему серверу в контейнере докера?

Итак, я пытаюсь докеризовать приложение Flask, которое выполняет следующие действия:

  1. Внутренне запускает сервер Bokeh по адресу 127.0.0.1:5006 контейнера.
  2. Запускает приложение Flask по адресу 0.0.0.0:5000, чтобы оно было доступно извне контейнера.
  3. Когда соединение установлено, Flask пытается получить данные с сервера Bokeh (экземпляр которого создается вместе с контейнером). Но при работе в качестве контейнера он никогда не попадает на собственный локальный хост контейнера (даже не задав для него --net = host).

Единственный способ, которым мне удалось заставить его работать, - это также создать сервер Bokeh в 0.0.0.0 и передать IP-адрес хоста контейнера (я в Windows) на адрес, с которого Flask пытается его получить ( см. поле url в autoload_server). Но, очевидно, это не сработает для других машин.

Вот соответствующие фрагменты кода:

server = Server({'/datavisualization': bokeh_app}, io_loop=io_loop, address="127.0.0.1",
                 allow_websocket_origin=["*"], host=["*"])

@app.route('/')
def bokeh_server():
    # Fetch Running Bokeh Server
    bokeh_embed = autoload_server(model=None,
                                  app_path="/datavisualization",
                                  url="http://127.0.0.1:5006")

    html = render_template('index.html', bokeh_embed=Markup(bokeh_embed))

    return html

if __name__ == '__main__':
    from tornado.httpserver import HTTPServer
    from tornado.wsgi import WSGIContainer
    from bokeh.util.browser import view
    # Serve the Flask app
    http_server = HTTPServer(WSGIContainer(app))
    http_server.listen(5000, address='0.0.0.0')
    io_loop.add_callback(view, "http://0.0.0.0:5000/")
    io_loop.start()

Может быть, кто-нибудь с большим опытом работы с Docker может помочь мне понять, что здесь происходит, пожалуйста? Я предполагал, что с точки зрения контейнера все, что опубликовано на localhost, будет видно другим службам, опубликованным из того же контейнера.

заранее спасибо


person Gabriel    schedule 07.01.2018    source источник
comment
Вы говорите, что Flask пытается получить данные с сервера Bokeh. Можете ли вы уточнить, действительно ли код Flask устанавливает соединение или Flask просто генерирует HTML / JavaScript, который содержит информацию о сервере Bokeh, чтобы браузер установил соединение? Если это на самом деле последнее, то это имеет смысл, браузер не сможет получить доступ к 127.0.0.1:5006.   -  person Andy Shinn    schedule 07.01.2018
comment
Я думаю, что вы абсолютно правы, на самом деле он будет генерировать HTML / JS для браузера, чтобы установить соединение с сервером.   -  person Gabriel    schedule 07.01.2018


Ответы (2)


Если бы вы запускали эту установку на машине Linux, то --net=host позволил бы вашему контейнеру взаимодействовать с петлевым интерфейсом хоста; этот флаг просто указывает, что контейнер не должен использовать собственное сетевое пространство имен, а вместо этого просто работает с доступом ко всем сетевым адаптерам хоста.

Однако --net=host работает иначе, если вы используете Docker в Windows или MacOS - это потому, что, пока клиент докера работает на вашем компьютере с Windows, контейнеры фактически работают на демоне докера в отдельной виртуальной машине. Если вы запустите --net=host и укажете свой сервер Flask на localhost, вы фактически попадете на локальный хост из виртуальной машины докера, а не на локальный хост вашего компьютера Windows, на котором работает Bokeh.

Сеть в Docker может быть сложной, когда вам нужно иметь дело с несколькими контейнерами, которые общаются друг с другом. Проблема, с которой вы имеете дело, заключается в том, как обнаружить службы в вашем приложении. Есть несколько способов решить эту проблему:

Самым простым, вероятно, является вставка IP-адреса вашего сервера Bokeh в ваше приложение Flask в качестве конфигурации; обычно это делается с использованием переменных среды, которые могут быть переданы при запуске контейнера докеров с флагом -e BOKEH_IP=1.2.3.4. Вы бы настроили другой IP на производстве.

Вы также можете поместить свой сервер Bokeh в контейнер, а затем использовать что-то вроде Docker Compose, Docker Swarm или Kubernetes для управления взаимодействием между вашими сервисами. Docker Compose работает на одной машине, Docker Swarm удобен для простого планирования контейнеров на нескольких машинах, а Kubernetes - более сложный и полнофункциональный вариант для оркестровки большого количества взаимодействующих сервисов.

person Symmetric    schedule 07.01.2018
comment
Передача IP-адреса виртуальной машины Docker и использование --net=host действительно было самым простым решением. В будущем я рассмотрю решение, основанное на двух контейнерах, как вы упомянули. Спасибо - person Gabriel; 07.01.2018

Я собираюсь ответить на вопрос о том, что, как я подозреваю, происходит на основе моего комментария.

В моем очень кратком обзоре Боке и server_document (из https://docs.bokeh.org/en/latest/docs/user_guide/embed.html#bokeh-applications), похоже, что эта функция действительно генерирует некоторый JavaScript. На самом деле он не подключается к серверу Bokeh из самого Python или Flask. Соединение идет из браузера.

Учитывая эту информацию, прослушивание http://127.0.0.1:5006 не позволит браузеру подключиться. Даже если контейнер запущен в режиме --net=host в Windows или Mac, контейнер будет прослушивать сеть виртуальной машины Docker, а не фактический хост, что все равно приведет к сбою подключения.

Сервер Bokeh должен будет прослушивать 0.0.0.0, чтобы любой IP-адрес, проходящий через Docker NAT, мог подключиться. Кроме того, вам нужно указать браузеру, как попасть в этот контейнер. Это означает использование IP-адреса виртуальной машины или IP-адреса вашего компьютера, на котором открыт порт контейнера Docker.

В производственной среде вы, вероятно, запустите порты сервера Flask и Bokeh за балансировщиком нагрузки или другим обратным прокси-сервером, который затем может предоставить одно IP / DNS-имя для подключения к ним. Затем вы могли бы использовать это DNS-имя или IP-адрес в качестве url параметра.

person Andy Shinn    schedule 07.01.2018