2

I'm running two Flask apps on two separate Docker containers together on localhost, with port mapping 3000:3000 and 5000:5000. Each of them has a route that accepts a POST request and do some functions. Here's the code for app 1:

@app.route('/preprocess', methods=['POST'])
def app_preprocess():
    req_data = request.get_json()

    bucket = req_data['bucket']
    input_file = req_data['input']
    upload_file = input_file + "_1"

    # do some functions

    to_send = {"bucket": bucket, "input": upload_file}
    to_send_string = json.dumps(to_send)

    requests.post("http://127.0.0.1:3000/postprocess", json=to_send_string)
    return "Sent request to 2"

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=5000)

And app 2:

@app.route('/postprocess', methods=['POST'])
def app_postprocess():
    req_data = request.get_json()

    bucket = req_data['bucket']
    input_file = req_data['input']
    upload_file = input_file + "_2"

    # do some functions

    return "Uploaded 2"

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=3000)

I used Postman to send the POST request to each app separately. Both app 1 and 2 does its job (the "do some functions" bit). App 2 would return "Uploaded 2" nicely.

App 1 stopped at the requests.post part, and returning an error:

requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=3000): Max retries exceeded
        with url: /postprocess (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at
        0x7f74d3fc6b10>: Failed to establish a new connection: [Errno 111] Connection refused')) // Werkzeug Debugger

Please help with fixing this, thank you. Also I'm a novice at Flask and HTTP routing so I might missed something.

EDIT: here's the docker-compose.yaml

version: '3'
services:

  uploader-1:
    build: ./uploader-1
    image: uploader-1
    container_name: uploader-1
    ports:
        - 5000:5000

  uploader-2:
    build: ./uploader-2
    image: uploader-2
    container_name: uploader-2
    ports:
        - 3000:3000
rok
  • 9,403
  • 17
  • 70
  • 126
Dan
  • 195
  • 3
  • 12
  • Are you using docker-compose to run your applications or just pure docker? – Iain Shelvington Jul 20 '20 at 03:50
  • I'm using docker-compose, let me edit my post with the yml – Dan Jul 20 '20 at 03:57
  • 1
    You should use the name of the other docker-compose service as the hostname instead of localhost - `requests.post("http://uploader-2:3000/postprocess", json=to_send_string)` – Iain Shelvington Jul 20 '20 at 03:59
  • So I tried that, and it no longer gives me an error; however app 2 is now not doing its job (the "do some functions" block). Is there a way for me to read uploader-2's output logs? – Dan Jul 20 '20 at 04:17
  • https://stackoverflow.com/questions/14814201/can-i-serve-multiple-clients-using-just-flask-app-run-as-standalone. you have walked straight into this limitation, thread is already busy processing route before `requests.post()` – Rob Raymond Jul 20 '20 at 04:27
  • @RobRaymond is there a way to remedy this? I've tried adding `threaded=True` to the parameters and it's still not working – Dan Jul 20 '20 at 04:38
  • Also to add, in both apps, they work just fine if I send a POST request directly with Postman – Dan Jul 20 '20 at 04:47
  • 2
    the standard way is to move your flask app to be managed by a WSGi server. for example Gunicorn. There's plenty of documentation on how to do this and containerise it. Your point on `requests` via `postman` modules depends on how they work internally with threads – Rob Raymond Jul 20 '20 at 04:57

3 Answers3

2

So when you create a docker instance, it creates a virtual network on your machine - so each docker instance thinks that 127.0.0.1 (or localhost) is itself, not your actual computer (what you think is 127.0.0.1). For this reason you should look into docker network, and either

a) reference the docker instance you want to connect to, or b) add a port (which is what you did) then reference the parent machines ip.

Look at the second answer here: How to get the IP address of the docker host from inside a docker container

Jmons
  • 1,766
  • 17
  • 28
-1

You have 2 separate containers where app runs on ports 5000 in the first and 3000 in the second.

You get requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=3000) when you send request from within first container where app runs on port 5000.

This is because there's no app running inside the first container on 3000.

Each container's localhost is different and it's different from docker host's localhost.

If you want to send request this way you need to allow containers to reuse host network stack by adding network: host to docker compose:

  uploader-2:
    build: ./uploader-2
    image: uploader-2
    container_name: uploader-2
    network: host
    ports:
        - 3000:3000

Another better option is to connect to hostnames which are container names.

From the docs:

By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.

So the code would look like:

requests.post("http://uploader-2:3000/postprocess", json=to_send_string)

Even, better option is send request to service names. It's possible to do so in docker compose or docker-swarm:

any service can reach any other service at that service’s name

rok
  • 9,403
  • 17
  • 70
  • 126
  • This is kind of a hack to get around the real problem: each container is its own `localhost` and the hard-coded URLs are wrong. [Networking in Compose](https://docs.docker.com/compose/networking/) describes the overall network setup and what host names are available. – David Maze Jul 20 '20 at 10:49
  • @DavidMaze my answer offered the first option as this is how the code would work without any changes. but, i agree it's better to use service names. edited my answer. – rok Jul 20 '20 at 20:15
-1

The error comes from the use of IP address 127.0.0.1

Docker introduces a virtual network, so each docker-isntance sees itself as 127 not the host machine

(thx @Jmons)


To launch your code locally, use your fixed IPv4 address.

For Windows users: open a command prompt (cmd) and run the ipconfig command, retrieve the IP@ which is at IPv4 address. . . . . . . . . . . . . .: it is generally of type 192.168.x.x

IPv4 address in command prompt

DDA
  • 11
  • 2
  • 2
    this is not correct- it s not that 127 is forbidden - its tha tdocker introduces a virtual network, so each docker-isntance sees itself as 127 *not* the host machine. – Jmons Jan 14 '21 at 16:51
  • Thx @Jmons, is it the same for 0.0.0.0 ? – DDA Jan 14 '21 at 17:08
  • 1
    Not quite - 0.0.0.0 is often used to mean *any* but its actually an invalid address. i.e. 0.0.0.0/0 means any in CIDR notation. When you're 'binding to an ip' often binding 0.0.0.0 means accept on any ip. (so if your machine has lots of ip,s or is listening in promiscuous mode, thats what that means). https://en.wikipedia.org/wiki/0.0.0.0 – Jmons Jan 14 '21 at 17:30