简体   繁体   中英

How to reach docker container `localhost` from Mac?

Note this is different from How to expose a service running inside a docker container, bound to localhost , which can be addressed in multiple ways in Docker for Linux, say through --net host or even -v to bind my Linux-flavor client in etc. My problem is specific for Docker for Mac, so it's not as straightforward.

I have a TCP server binding to localhost:5005 running inside Docker for Mac. (For security reason, I must not bind to 0.0.0.0:5005 .)

I have a TCP client sending request to this server from my Mac (not inside the docker container).

My question is, how do I make it work?

In Linux Docker, I would simply use --net=host so the server binds to my host lo interface, but it seems that Docker for Mac runs on a managed VM, so the host network behavior is different behavior.

To illustrate my point:

On MacBook

It simply would not work

[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: 

On Linux

In comparison, it would be trivial to do on Linux by using host network mode. Since I'm using my Linux's lo interface as my container lo interface.

[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!

My Simulated Server Code

Requirement: It MUST bind to localhost , and nothing else. So I cannot change it to 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()

My Simulated Client Code

Requirement: It MUST be ran on MacBook, since the real client is written in CPP and compiled to run only on 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

Here's a working solution. The basic idea is to use SSH tunneling to do the port forwarding.

High Level Idea

  • You first need to build a docker image to support SSH access, because
    1. ubuntu image doesn't have a sshd out of box, and also
    2. you will need to know the password of root of your running container.
  • Then you will spin up your container as what you would normally do except that you are doing that based on the new image you created.
  • You create a SSH tunneling session from your MacBook, then you run your client on MacBook as you would normally do.

For reference, the command for SSH tunneling can be found here, the process of creating a sshd docker image is explained here , and how to ssh into docker container is explained here

Steps

  1. Create a Docker file 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. Create a Docker Image from the Dockerfile

     [me@MacBook App]$ docker build -t my_ssh_python . 
  3. Spin up your server container

     [me@MacBook App]$ docker run -d -P -v `pwd`:/App --name myserver my_ssh_python 
  4. Start your server inside the container

     [me@MacBook App]$ docker exec myserver /App/server.py 
  5. Create a SSH tunnel

     [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 

    Note that

    a. You have to use the IP address of your MacBook instead of your docker container's IP address.

    b. You will use the port where the default container ssh port 22 is mapped to on host

    c. In tunneling -L 8000:localhost:8000 , you are saying forward anything from your MacBook 8000 (the first 8000) to Docker container's localhost at port 8000

  6. Now you can use you client locally

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

    And on server side, you can see

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM