skip to Main Content

I have a flask app that uses rabbitmq where both are docker containers (along with other components, such as a celery workers). I want to use a common .env environment file for both dev and container use in my docker-compose.

Example .env

RABBITMQ_DEFAULT_HOST=localhost

Now, if I use this with with flask run it works fine as the container rabbitmq port is mapped to the host. If I run this inside the flask docker container, it fails because localhost of the flask container is not the same as the host. If I change localhost to my container name, rabbitmq.

RABBITMQ_DEFAULT_HOST=rabbitmq

It will resolve nice inside the flask container via docker to the dynamic ip of the rabbitmq container (local port map not even necessary), however, my flask run during development has no knowledge of this name / ip mapping and will fail.

Is there any easy way to handle this so it’s easily portable to other devs and just "works" when either outside using flask run or inside the container via docker-compose?

I’d also like to limit the port exposure if possible, such as 127.0.0.1:5672:5672.


Update

So far, this is the best I’ve come up with.. in the program, I use a socket to check if the name resolves, if not, then it looks to the env with a default to localhost.

import socket

def get_rabbitmq_host() -> str:
    try:
        return socket.gethostbyname("rabbitmq") # container name
    except socket.gaierror:
        return os.getenv("RABBITMQ_DEFAULT_HOST", "localhost")

Here is another method I tried that’s a lot faster (no dns timeout), but changes the order a bit.

def get_rabbitmq_host() -> str:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)
    result = sock.connect_ex(("127.0.0.1", 5672))
    sock.close()
    if result == 0:
        return "127.0.0.1"
    elif (
        os.getenv("RABBITMQ_DEFAULT_HOST") == "localhost"
        or os.getenv("RABBITMQ_DEFAULT_HOST") == "127.0.0.1"
    ):
        return "rabbitmq"
    else:
        return os.getenv("RABBITMQ_DEFAULT_HOST", "rabbitmq")

2

Answers


  1. Well no, not really. Or yes, depending on how you view it.

    Since now you find out that localhost does not mean the same in every context, nmaybe you should split up the variables, even though in some situations it maybe have the same value.

    So just something like

    rabbit_mq_internal_host=localhost
    rabbit_mq_external_host=rabbitmq #container name!
    

    Is there any easy way to handle this so it’s easily portable to other devs and just "works" when either outside using flask run or inside the container via docker-compose?

    Well: that is the point of the .env files. You have to different environments there, so make two different .env files. Or let everyone adjust the .env file according to her/his preferred way of running the app.

    I’d also like to limit the port exposure if possible, such as 127.0.0.1:5672:5672

    If you connect from container to container within a docker network, you do not need to publish the port at all. Only ports that have to be accessed from outside the network.

    Login or Signup to reply.
  2. I am not sure if I completely understood your situation. I am assuming that you are developing the application and have environment which you would like to have it separated in accordance to the environment for example localhost, development, test etc …

    With that assumption as above. I would suggest to have env’s in accordance to the environment like env_localhost, env_development where each key=value will be in accordance to the environment. Also, have an env.template file with empty key= so that if someone does not want a docker based runs then can setup that accordingly in a new file calling it the .env.

    Once the above is created now you can modify your docker build for the app the Dockerfile I mean where you can utilise the following snippet. The important part is the environment variable called SETUP and the rename of the environment to .env during the build process:

        # ... Other build commands follow
            WORKDIR /usr/src/backend
            COPY ./backend .
            ARG SETUP=development # This is important environment we will pass in future. Defaults to a value like development.
            COPY ./backend/env_${SETUP} .env # This is passed auto during docker-compose build I will tell that next.
       #  ... Other build commands follow
    

    After the modification of the Dockerfile, now you can perform docker-compose build according to the environment by passing a SETUP as env to the build as follows:

    docker-compose build --build-arg SETUP=localhost your_service_here
    

    Additionally, once this process is stable you can create a Makefile and have make build-local, make build-dev and so on.

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