skip to Main Content

Without docker, I could connect my fastapi app with mqtt broker. However with docker, I cannot connect my fastapi app with the docker mqtt broker. I get the error –

{"log":"    ', '.join(str(exc) for exc in exceptions)))n","stream":"stderr","time":"2022-02-21T04:42:21.137558005Z"}
{"log":"OSError: Multiple exceptions: [Errno 111] Connect call failed ('127.0.0.1', 1883), [Errno 99] Cannot assign requested addressn","stream":"stderr","time":"2022-02-21T04:42:21.137562299Z"}
{"log":"n","stream":"stderr","time":"2022-02-21T04:42:21.137566492Z"}
{"log":"ERROR:    Application startup failed. Exiting.n","stream":"stderr","time":"2022-02-21T04:42:21.137570669Z"}

Here is my main.app

from fastapi import FastAPI
from fastapi_mqtt import FastMQTT, MQTTConfig
from pydantic import BaseModel
from ipaddress import IPv4Address
import jsonpickle

app = FastAPI()


class Nmap(BaseModel):
    host: IPv4Address
    portRange: str

    class Config:
        schema_extra = {
            "example" : {
                "host": "10.0.2.15",
                 "portRange": "22-80",
                 "description": "Scan the port from 22 to 80 of the ip address 10.0.2.15"
            }
        }



mqtt_config = MQTTConfig()

mqtt = FastMQTT(config=mqtt_config)

mqtt.init_app(app)

@mqtt.on_connect()
def connect(client, flags, rc, properties):
    mqtt.client.subscribe("/mqtt/toModel/#") # subscribing mqtt topic wildcard- multi-level
    print("connected: ", client, flags, rc, properties)

@mqtt.on_message()
async def message(client, topic, payload, qos, properties):
    print("received message: ", topic, jsonpickle.decode(payload.decode()), qos, properties)
    return 0 


@mqtt.on_disconnect()
def disconnect(client, packet, exc=None):
    print("Disconnected")

@mqtt.on_subscribe()
def subscribe(client, mid, qos, properties):
    print("subscribed", client, mid, qos, properties)

@app.get("/")
async def func():
    mqtt.client.publish("/mqtt", "Hello from fastApi") 
    return {"result": True, "message": "Published"}

@app.post("/scan/{host}")
async def scan_host_port(nmap_details : Nmap):
    results = {"got_val" : nmap_details}
    print(type(nmap_details))
    mqtt.client.publish("/mqtt/fromModel/nmap", jsonpickle.encode(nmap_details)) 
    return results

Here is my app docker file –

FROM python:3.7

WORKDIR /code 


COPY ./requirements.txt /code/requirements.txt


RUN pip install -r /code/requirements.txt


COPY ./app /code/app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

For MQTT, I am using –

docker-compose.yml

version: "3"
services:
 mqtt:
      image: toke/mosquitto
      network_mode: bridge
      container_name: mqtt
      expose:
        - 1883
      ports:
        - 1883:1883
      restart: unless-stopped

I am sure it is something with my docker but I don’t know how to deploy it.My understanding is that docker is designed to run a single process. So one process for broker, another docker to simply run my fast api and another docker for nginx

2

Answers


  1. Chosen as BEST ANSWER
    version: '3.5'
    
    services:
     mqtt:
          image: toke/mosquitto
          container_name: mqtt
          expose:
            - 1883
          ports:
            - 1883:1883
          restart: unless-stopped
    
          networks:
            - my_network
    
     fast:
         container_name: fast
         expose:
            - 8000
         networks:
           - my_network
        
         ports:
            - 8000:8000
         restart: unless-stopped
         build:
          context: .
          dockerfile: Dockerfile
    
         depends_on:
            - mqtt
    
    networks:
      my_network:
        driver: bridge
    

  2. 127.0.0.1 always points to the TCP/IP stack the current code is bound to.

    Every Docker container has it’s own TCP/IP stack. So if you try to connect to 127.0.0.1 from the python container there will be no MQTT broker running on port 1883 of it’s own TCP/IP stack.

    You should have both containers in the same docker compose file using the same network (containers can be bound to multiple networks if needed) and you can then replace the IP address with three service name of the broker e.g. mqtt

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search