Search code examples
dockercurldocker-composemicroservicesnginx-reverse-proxy

docker-compose microservice inter-container api communication with nginx proxy


I am trying to build a docker-compose file that will mimic my production environment with its various microservices. I am using a custom bridge network with an nginx proxy that routes port 80 and 443 requests to the correct service containers. The docker-compose file and the nginx conf files together specify the port mappings that allow the proxy container to route traffic for each DNS entry to its matching container.

Consequently, I can use my container names as DNS entries to access each container service from my host browser. I can also exec into each container and ping other containers by that same DNS hostname. However, I cannot successfully curl from one container to another by the container name alone.

It seems that I need to append the proxy port mapping to each inter-service API call when operating within the Docker environment. In my production environment each service has its own environment and can respond on ports 80 and 443. The code written for each service therefore ignores port specifications and simply calls each service by its DNS hostname. I would rather not have to append port id mappings to each API call throughout the various code bases in order for my services to talk to each other in the Docker environment.

Is there a tool or configuration setting that will allow my microservice containers to successfully call each other in Docker without the need of a proxy port map?

version: '3'

services:
  #---------------------
  # nginx proxy service 
  #---------------------
  nginx_proxy:
    image: nginx:alpine
    networks:
      - test_network
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./site1/site1.test.conf:/etc/nginx/conf.d/site1.test.conf"
      - "./site2/site2.test.conf:/etc/nginx/conf.d/site2.test.conf"
    container_name: nginx_proxy
  #------------
  # site1.test 
  #------------
  site1.test:
    build: alpine:latest
    networks:
      - test_network
    ports:
      - "9001:9000"
    environment:
      - "VIRTUAL_HOST=site1.test"
    volumes:
      - "./site1:/site1"
    container_name: site1.test
  #------------
  # site2.test 
  #------------
  site2.test:
    build: alpine:latest
    networks:
      - test_network
    ports:
      - "9002:9000"
    environment:
      - "VIRTUAL_HOST=site2.test"
    volumes:
      - "./site2:/site2"
    container_name: site2.test

# networks
networks:
  test_network:

Solution

  • http://hostname/ always means http://hostname:80/ (that is, TCP port 80 is the default port for HTTP URLs). So if you want one container to be able to reach the other as http://othercontainer/, the other container needs to be running an HTTP daemon of some sort on port 80 (which probably means it needs to at least be started as root within its container).

    If your nginx proxy routes to all of the containers successfully, it's not wrong to just route all inter-container traffic through it (in a previous technology generation we would have called this a service bus). There's not a trivial way to do this in Docker, but you might be able to configure it as a standard HTTP proxy.

    I would suggest making all of the outbound service URLs configurable in any case, probably as environment variables. You can imagine wanting to run multiple services together in a development environment (in which case the service URL might be http://localhost:9002), or in a pure-Docker environment like what you show (http://otherservice:9000), or in a hybrid multi-host Docker setup (http://other.host.example.com:9002), or in Kubernetes (http://otherservice.default.svc.cluster.local:9000).