How does a container resolve the IP of another service by the servicename?

Given a docker-compose setup like

services:
  pgadmin:
     image: dpage/pgadmin4:5
     networks:
        - mynetwork
  postgres:
     image: postgres:11
     networks:
        - mynetwork

I can connect “pgadmin” to “postgres” on its frontend via setting the postgres “hostname/address” to “postgres”, aka the service name, and not the IP.
Now I want to add another custom service made by myself, and I want it to connect to the same postgres server.
Unless I expose postgres over the internet I would need to specify the internal docker network IP. But I’d like to connect the same way pgadmin does, only specify the service name. How does this work?

For example, when I call docker network inspect mynetwork I see this entry of postgres service:

    "80139f845fc8ce06c9bc33a0b567878c4ee2c9d4f4949cef7197d2a87d3e54eb": {
        "Name": "mystack_postgres.1.s1duxbwwqd5nfjwpda6q4ekw6",
        "EndpointID": "72d45292c72c145438f80ab34428485802b499a960a4ac8b25b29643bd57dafd",
        "MacAddress": "<not shown>",
        "IPv4Address": "10.0.1.254/24",
        "IPv6Address": ""
    },

From inside the pgadmin container, I expected to find that IP, but no:

/pgadmin4 $ netstat | grep postgres
tcp        0      0 54a46b5c99a8:43766      10.0.1.214:postgresql   ESTABLISHED 
tcp        0      0 54a46b5c99a8:43768      10.0.1.214:postgresql   ESTABLISHED 
tcp        0      0 54a46b5c99a8:43764      10.0.1.214:postgresql   ESTABLISHED 

Do you have any starting points or explanations?
Thanks!

Thanks for your information.
In my python app, I have solved this issue this way:

        app.config['DB_HOST'] = get_docker_secret(name="postgres_host")
        if len(app.config['DB_HOST'].split(".")) == 4:
            # There is an IP -> no further action required
            pass
        else:
            # No IP found, but likely a service name -> get IP from service name
            import dns.resolver
            result = dns.resolver.resolve(db_host, 'A')
            if len(result) >= 1:
                val = result[0]
                print('Found postgres A Record: ', val.to_text())
                app.config['DB_HOST'] = val.to_text()

Its unclear what you try to achive. Is this other service also a container in the same network? If so, accessing the other containers in the same network by service name should work out of the box.

In Docker, User defined networks provide service discovering based on dns names matching the service name. If a network alias is defined, it will be registered as dns name as well.

If the other service is run outside docker, then you will need to publish ports and use the hostname/hostip and host port of the publised port mapping.

If you try to access a container by its internal ip, then you high likely try do use something in a way it is not ment to be used. Use the service name in container to container communication or publish the container port and access the published port instead. The ip address of a container is neither permanant nor reliable…

1 Like

What exactly is unclear?

My example uses the same network.

So yes, the containers are on the same docker network.

I try to explain it differently:
I have an app running on docker, which needs to connect to a database on the same docker network.
A database needs a IP. But pgadmin can know just the service name of a docker container (docker-compose). This is what I want, too.
Now when I just add the service name instead of the IP to my app, the database is not found. I take from this that the service discovery does not “just work”. With the code posted above, the app finds the IP by service name, and uses it. Works.
Not sure how I can explain this differently or better.

You say “In Docker, User defined networks provide service discovering based on dns names matching the service name. If a network alias is defined, it will be registered as dns name as well.”. This sounds exactly what I need. But as I explained it above, it does not work, or I did something wrong, which is seemingly not so obvious.

Greetings

This is a build in feature of user defined docker networks (every network defined in a compose file is a user defined network) with the bridge (=default for docker-compose)/overlay (=default for swarm deployments) driver. Service discovery is not available in macvlan/ipvlan networks or the default bridge network.

1 Like

Well, understood, but the question remains what I am doing wrong. How should I specify a database connection via service name? For example this one:
postgresql://scott:tiger@localhost/mydatabase
When I build that string with postgres it just does not work out of the box. Using docker internal network IP it does.
Currently my code looks like this:

dialect="psycopg2"
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+'+dialect+'://' \
     + quote(app.config['DB_USER'])  + ':' + quote(app.config['DB_PW']) \
     + '@' + app.config['DB_HOST'] + ':' + app.config['DB_PORT'] \
     + '/' + app.config['DB_NAME']

I assume you tried postgresql://scott:tiger@postgres/mydatabase?
I replaced localhost with your service name for the postgres database, which postgres as well.

Basically yes. Using correct user, password, dbname of course.

This should work… odd that it doesn’t.

Since name resolution works while querying the available dns servers within the container, I doubt that name resolution itself is the problem (otherwise you manual lookup wouldn’t work either).

You can always try to hook a network trouble shooting container into the same network as the container and check what dig, ping (or any other tool in the troubleshooting container) discover:

docker run -it --net container:<container_name> nicolaka/netshoot

With --net container:${container id or name} you basicly hook the netshoot container into the same network namespace as ${container id or name} and share its network interface. This allows to troubleshoot problems as if you are in the ${container id or name}, but have all the required tools in your hand to troubleshoot. I hope this makes sense :slight_smile: