Как связаться с Docker-контейнером `localhost` из Mac? - PullRequest
0 голосов
/ 13 сентября 2018

Обратите внимание, что это отличается от Как предоставить сервис, работающий внутри контейнера Docker, привязанный к localhost , к которому можно обратиться несколькими способами в Docker для Linux, скажем, через --net host или даже -v привязать мой Linux-клиент и т. д. Моя проблема специфична для Docker для Mac, поэтому она не так проста.

У меня есть привязка TCP-сервера к localhost:5005 внутри Docker для Mac. (Из соображений безопасности я не должен связываться с 0.0.0.0:5005.)

У меня есть TCP-клиент, отправляющий запрос на этот сервер с моего Mac (не внутри контейнера Docker).

У меня вопрос, как мне заставить это работать?

В Linux Docker я просто использовал бы --net=host, чтобы сервер связывался с моим lo интерфейсом хоста, но похоже, что Docker для Mac работает на управляемой виртуальной машине, поэтому поведение сети host отличается.

Чтобы проиллюстрировать мою точку зрения:

на MacBook

Это просто не будет работать

[me@MacBook App]$ docker run -v `pwd`:/App -p 127.0.0.1:5005:5005 nitincypher/docker-ubuntu-python-pip /App/server.py
[me@MacBook App]$ ./client.py 
Client received data: 

В Linux

Для сравнения, в Linux было бы тривиально использовать сетевой режим host. Поскольку я использую интерфейс lo моего Linux в качестве интерфейса lo контейнера.

[me@Linux App]$ docker run -v `pwd`:/App --net=host nitincypher/docker-ubuntu-python-pip /App/server.py
Server Connection address: ('127.0.0.1', 52172)
Server received data: Hello, World!
[me@Linux App]$ ./client.py 
Client received data: Hello, World!

Код моего имитированного сервера

Требование: оно ДОЛЖНО связываться с localhost и ничем иным. Поэтому я не могу изменить его на 0.0.0.0.

#!/usr/bin/env python

import socket

TCP_IP = 'localhost'
TCP_PORT = 5005
BUFFER_SIZE = 20  # Normally 1024, but we want fast response

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)

conn, addr = s.accept()
print 'Server Connection address:', addr
while 1:
    data = conn.recv(BUFFER_SIZE)
    if not data: break
    print "Server received data:", data
    conn.send(data)  # echo
conn.close()

Код моего симулированного клиента

Требование: он ДОЛЖЕН запускаться на MacBook, поскольку реальный клиент написан на CPP и скомпилирован для запуска только на MacBook.

#!/usr/bin/env python

import socket


TCP_IP = 'localhost'
TCP_PORT = 5005
BUFFER_SIZE = 1024
MESSAGE = "Hello, World!"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()

print "Client received data:", data

1 Ответ

0 голосов
/ 14 сентября 2018

Вот рабочее решение. Основная идея заключается в использовании SSH-туннелирования для переадресации портов.

Идея высокого уровня

  • Сначала необходимо создать образ докера для поддержки доступа SSH, потому что
    1. ubuntu изображение не имеет sshd из коробки, а также
    2. вам нужно будет знать пароль root вашего работающего контейнера.
  • Затем вы раскручиваете свой контейнер так, как вы обычно делаете, за исключением того, что вы делаете это на основе созданного вами нового изображения.
  • Вы создаете сеанс SSH-туннелирования на своем MacBook, затем запускаете клиент на MacBook, как обычно.

Для справки, команда для SSH туннелирования может быть найдена здесь, процесс создания образа док-станции sshd объяснен здесь , а способ ssh в докер-контейнер объяснил здесь

Steps

  1. Создание файла Docker Dockerfile

    #Use whatever image you are using on Docker Linux , say "FROM ubuntu:16.04"
    FROM nitincypher/docker-ubuntu-python-pip
    
    RUN apt-get update && apt-get install -y openssh-server
    RUN mkdir /var/run/sshd
    RUN echo 'root:screencast' | chpasswd
    RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
    
    # SSH login fix. Otherwise user is kicked off after login
    RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
    
    ENV NOTVISIBLE "in users profile"
    RUN echo "export VISIBLE=now" >> /etc/profile
    
    EXPOSE 22
    CMD ["/usr/sbin/sshd", "-D"]
    
  2. Создание образа Docker из файла Dockerfile

    [me@MacBook App]$ docker build -t my_ssh_python .
    
  3. Раскрути свой серверный контейнер

    [me@MacBook App]$ docker run -d -P -v `pwd`:/App --name myserver my_ssh_python
    
  4. Запустите свой сервер внутри контейнера

    [me@MacBook App]$ docker exec myserver /App/server.py
    
  5. Создание туннеля SSH

    [me@MacBook App]$ ssh root@`hostname` -p `docker port myserver 22 | awk -F ":" '{print $2}'` -L 8000:localhost:8000 -N
    #Password is "screencast" as you built in Dockerfile
    

    Обратите внимание, что

    а. Вы должны использовать IP-адрес вашего MacBook вместо IP-адреса вашего док-контейнера.

    б. Вы будете использовать порт, на котором контейнер по умолчанию ssh порт 22 отображается на хосте

    с. При туннелировании -L 8000:localhost:8000 вы говорите, что перешлите что-нибудь с вашего MacBook 8000 (первые 8000) на контейнер Docker localhost в порту 8000

  6. Теперь вы можете использовать свой клиент локально

    [me@MacBook App]$ ./client.py 
    Client received data: Hello, World!
    

    А на стороне сервера вы можете увидеть

    Server Connection address: ('127.0.0.1', 55396)
    Server received data: Hello, World!
    
...