Dragos Stoica
Dragos Stoica

Reputation: 1935

Unable to communicate between docker containers on localhost

First, I am new to docker containers, I don't know much about how they are running, but I have this setup:

The API is reachable at https://localhost:44384/api/weatherforecast The containerized angular app is reachable at https://localhost:4200

If I am running the angular app without docker, after fixing the CORS issues with a proxy, I am able to connect to https://localhost:44384/api/weatherforecast. However, when I am running the dockerized version I am receiving the following error:

Failed to load resource: the server responded with a status of 504 (Gateway Timeout)

In the VS Console I see this error:

clearance_1  | [HPM] Error occurred while trying to proxy request /api/weatherforecast from loccoalhost:4200 to https://localhost:44384 (ECONNREFUSED) (https://nodejs.org/api/errors.html#errors_common_system_errors)

It seems to be a connectivity issue, so I dig a bit on the internet and I tried to bring these two containers under the same network.

Here are the steps: 1. Create a bridge "test" network

docker network create test
  1. Connect both containers to the newly created network:

docker network connect test [container id 1]

docker network connect test [container id 2]

  1. If I inspect the network everything seems to be fine, but the api is still not callable on localhost

Other potential useful stuff:

docker-compose.yml :

version: "3.7"
services:
  clearance:
     build:
     # network: host
      context: .
      dockerfile: DockerFileLocal
     ports:
       - 4200:4200
     volumes:
       - /app/node_modules
       - .:/app

proxy.conf.json

{
   "/api/*": {
      "target": "https://localhost:44384",
      "secure": false,
      "logLevel": "debug",
      "changeOrigin": true
   }
}

What am I missing?

Upvotes: 5

Views: 11436

Answers (2)

Z4-tier
Z4-tier

Reputation: 7978

Add a section to your compose file under each service that declares static IP's for each container:

services:
  webserver:
    build:
      dockerfile: Dockerfile
      context: .
    depends_on:
      - some_other_container
    ports:
      - "443:443"
    environment:
      - MY_VAR='my_val'
########### ADD THIS PART ##############
    networks:
        dev_net:
          ipv4_address: "192.168.0.50"

and add a section at the same level as services for the network:

networks:
  dev_net:
    name: my_dev_net
    driver: overlay
    ipam:
      config:
          - subnet: "192.168.0.0/24"

Then make sure that the hosted applications are configured to use those IP's to communicate. If IP's are not manually assigned, docker will pretty much act like a DHCP server and give whatever IP's it decides to assign.


To clarify: I think part of the problem you are having is due to Docker networking itself: on Mac and Windows, Docker can't really provide a fully routable virtual network where the IP's assigned to containers are accessible directly from the outside. No matter what solution you go with, it's going to have to work around that limitation. The only way to make a container accessible from the outside is to directly bind it to ports on the host (using docker run -p [source_port]:[host_port])

Here is a demo that should clarify my suggestion. This will:

  • Build two containers using a single docker-compose.yml file
  • Container #1 will use openssl to serve a simple text file on port 8080
  • Port 8080 on Container #1 will be bound to the same port on the host
  • Container #2 will use curl to fetch the file from Container #1 using the private network IP address
  • The same curl command is run from the host, but using localhost instead of the private IP (since that is not accessible from outside docker)
  • Both containers are assigned static IP addresses on the Docker private network (which is defined in the compose file)

The purpose of this is to present a minimal example that: allows two Docker containers to communicate; over the docker private network; using private static IP addresses; while also binding to a port on the host system; allowing connections inbound to the container from outside the docker private network.


docker-compose.yml:

version: "3.7"
services:
  c1:
    build:
      dockerfile: Dockerfile
      context: ./c1
    ports:
      - "8080:8080"
    networks:
        dev_net:
          ipv4_address: "192.168.0.50"
  c2:
    build:
      dockerfile: Dockerfile
      context: ./c2
    depends_on:
      - c1
    networks:
      dev_net:
        ipv4_address: "192.168.0.51"
networks:
  dev_net:
    name: test_dev_net
    driver: overlay
    external: false
    ipam:
      config:
          - subnet: "192.168.0.0/24"


Container #1 Dockerfile:

FROM alpine:3.7
RUN apk update && apk upgrade && apk add openssl
COPY  run.sh /run.sh
CMD ["/run.sh"]


Container #1 run.sh:

#!/bin/sh

echo "HELLO, WORLD!" >> test_file.txt

openssl req -x509 -newkey rsa:2048 -keyout key.pem \
    -out cert.pem -days 365 -nodes -subj /C=\/ST=\/L=\/O=\/OU=\/CN=\/

openssl s_server -key key.pem -cert cert.pem -accept 8080 -HTTP &

sleep 25


Container #2 Dockerfile:

FROM alpine:3.7
RUN apk update && apk upgrade && apk add curl;
COPY  run.sh /run.sh
CMD ["/run.sh"]


Container #2 run.sh:

#!/bin/sh

c1_url="https://192.168.0.50:8080/test_file.txt"

for _ in $(seq 0 2); do
  curl -k -g  ${c1_url}
  sleep 5
done


build / run / test:

$ docker-compose up & \
  sleep 10 && \
  curl -k -g "https://localhost:8080/test_file.txt"
[3] 63380

Starting docker_c1_1 ... done
Starting docker_c2_1 ... done
Attaching to docker_c1_1, docker_c2_1
c1_1  | Generating a RSA private key
c1_1  | .....................................+++++
c1_1  | writing new private key to 'keys/key.pem'
c1_1  | No value provided for Subject Attribute C, skipped
c1_1  | No value provided for Subject Attribute ST, skipped
c1_1  | No value provided for Subject Attribute L, skipped
c1_1  | No value provided for Subject Attribute O, skipped
c1_1  | No value provided for Subject Attribute OU, skipped
c1_1  | No value provided for Subject Attribute CN, skipped
c2_1  |   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
c2_1  |                                  Dload  Upload   Total   Spent    Left  Speed
100    42    0    42    0     0   3230      0 --:--:-- --:--:-- --:--:--  3230
c2_1  | HELLO, WORLD!   <<<<------ Container #2 curl output
c2_1  | HELLO, WORLD!
c2_1  | HELLO, WORLD!
HELLO, WORLD!           <<<<------ Host curl output
docker_c2_1 exited with code 0
c1_1  | DONE. Exiting...
docker_c1_1 exited with code 0
[3]   Done                    docker-compose up
$

So this solution will work to allow docker-compose to build containers which use static IP addressing on the private docker network and which are also exposed via port binding on the host.

Upvotes: 1

Reqven
Reqven

Reputation: 1778

I had a similar issue with an angular app and an API both running in separate Docker containers.
Both apps were individually working fine with the following setup :

Angular running at http://localhost:4200
API running at http://localhost:8080

Problem

The Angular app couldn't reach the API.
The following code gave me network related errors all the time.

this.http.get('http://localhost:8080').subscribe(console.log);

Solution

Links
Link to containers in another service. Either specify both the service name and a link alias (SERVICE:ALIAS), or just the service name. Containers for the linked service are reachable at a hostname identical to the alias, or the service name if no alias was specified.

When a container needs to reach another container via the network, we need to create a link.
I ended up creating a link to the api service in the angular service definition in the docker-compose.yml

version: "2"
services:
  api:
    build:
      context: .
      dockerfile: ./api/Dockerfile
    volumes:
      - ./api:/usr/src/app
    ports:
      - "8080:8080"

  angular:
    build:
      context: .
      dockerfile: ./angular/Dockerfile
    volumes:
      - ./angular:/usr/src/app
    ports:
      - "4200:4200"
    links:
      - api

Then I fixed the network error by replacing localhost with api in the Angular app.

this.http.get('http://api:8080').subscribe(console.log);

You don't need a proxy. However, you might have to tweak your config to make it work.

Upvotes: 2

Related Questions