How to connect frontend to backend via docker-compose networks - docker

I'm trying to dockerize my angular + express application. I have a docker-compose file that creates the two containers, and I am able to hit the containers from my host machine(using my browser), but I'll just get a "ERR_NAME_NOT_RESOLVED" whenever I try to hit the backend from http requests made by my frontend.
I've looked up the issue, and it seems like most suggest that the service name and container port should be enough to hit the the other container when they're on the same network. I've tried to hit "http://express:8000/user?user=030f0e70-9a8f-11e9-b5d1-f5cb6c0f3616" which I think should work given what I've seen from other places, but regardless, I get the same error.
My docker-compose file looks like
version: '3' # specify docker-compose version
# Define the services/containers to be run
services:
angular: # name of the first service
build: ./ # specify the directory of the Dockerfile
ports:
- "4200:80" # specify port forewarding
links:
- "express"
depends_on:
- "express"
express: #name of the second service
build: # specify the directory of the Dockerfile
context: ./
dockerfile: dockerfile.be
ports:
- "8000:8000" #specify ports forewarding
expose:
- "8000"
Ideally, I'd like my frontend to be able to hit the other container with a set endpoint, so I could deploy the application with minimal changes. I'd appreciate any advice. I feel like I'm missing something really simple, but after a few hours of tinkering, I still haven't caught it.
Thanks!

In fact your traffic is as next:
User browser request page from angular container, then all pages will rendered to user's browser.
The front javascript code using angular HttpClient to fetch the data from express container.
At that time, although docker-compose setup a customized network for you which afford auto-dns to resolve angular & express, but this dns just works among containers, not effect for host.
But, your augular HttpClient which call http://express was happened on user's browser which not in container, so the auto-dns defintly not work for you to resolve the name express.
For you, if you just want to open browser from your docker host, you can use localhost, but if you also want the user from other machine to visit your web, you had to use the ip of your dockerhost.
Then, in angular HttpClient you need to use something like http://your_dockerhost_ip:8000 to visit express container.
If interested, you can visit this to see User-defined bridges provide automatic DNS resolution between containers.

A few things:
By using expose, you are making the container's published ports only available to linked/networked services. This is one reason why you are unable to access it locally.
Instead of hitting http://express:8000/ you should try to hit http://localhost:8000. The service is being published to your localhost system and is not being served by anything by default (e.g., IIS, NGINX).
Add a custom defined network in your compose file instead of using links. This is now the main way to network containers together:
version: '3' # specify docker-compose version
services:
angular: # name of the first service
build: ./ # specify the directory of the Dockerfile
ports:
- "4200:80" # maps port 4200 on localhost to 80 in container
network:
- mynetwork
depends_on:
- "express"
express: # name of the second service
build: # specify the directory of the Dockerfile
context: ./
dockerfile: dockerfile.be
ports:
- "8000:8000" # maps port 8000 on localhost to 8000 in container
networks:
- mynetwork
networks:
mynetwork:
2: https://docs.docker.com/compose/compose-file/#expose

**sample docker-compose.yaml**
version: '3.5'
services:
angular:
image: "angular-alpine:0.0.1"
container_name: angular
tty: true
stdin_open: true
networks:
app_net:
ipv4_address: 172.16.238.05
depends_on:
- express
express:
image: "express:0.0.1"
container_name: express
tty: true
stdin_open: true
networks:
app_net:
ipv4_address: 172.16.238.10
networks:
app_net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.16.238.0/24
>"1. Angular will run on 172.16.238.05:4200 and express will run on 172.16.238.10:some-port.
2. modify your config.ts or parameter.ts or anyfile where you configure express url into 172.16.238.10:some-port. now your angular will connect to express."

Related

Sending API-requests between two docker containers

I have running a DDEV-Environment for Magento2, locally on my Mac OSX (Ventura)
https://ddev.readthedocs.io/en/stable/users/quickstart/#magento-2
For testing purpose I included Nifi per docker-compose.yaml inside my ddev project .ddev/docker-compose.nifi.yaml
Below you can see the docker-compose, which is really minimal at this point. Nifi works like expected, because I can login etc, although it is not persistent yet, but thats a different problem
version: '3'
services:
nifi:
image: apache/nifi:latest
container_name: ddev-${DDEV_SITENAME}-nifi
ports:
# HTTP
- "8080:8080"
# HTTPS
- "8443:8443"
volumes:
# - ./nifi/database_repository:/opt/nifi/nifi-current/database_repository
# - ./nifi/flowfile_repository:/opt/nifi/nifi-current/flowfile_repository
# - ./nifi/content_repository:/opt/nifi/nifi-current/content_repository
# - ./nifi/provenance_repository:/opt/nifi/nifi-current/provenance_repository
# - ./nifi/state:/opt/nifi/nifi-current/state
# - ./nifi/logs:/opt/nifi/nifi-current/logs
# - ./nifi/conf/login-identity-providers.xml:/opt/nifi/nifi-current/conf/login-identity-providers.xml
- ".:/mnt/ddev_config"
All I want to do is sending a POST-requst from Nifi to my Magento2 module.
I tried several IPs now, which I got from docker inspect ddev-ddev-magento2-web but I always receive "Connection refused"
My output from docker network ls:
NETWORK ID NAME DRIVER SCOPE
95bea4031396 bridge bridge local
692b58ca294e ddev-ddev-magento2_default bridge local
46be47991abe ddev_default bridge local
7e19ae1626f1 host host local
f8f4f1aeef04 nifi_docker_default bridge local
dbdba30546d7 nifi_docker_mynetwork bridge local
ca12e667b773 none null local
My Magento2-Module is working properly, because sending requests from Postmanto it works fine
You don't want most of what you have. Please remove the ports statement, which you shouldn't need at all; if you need anything, you'll need an expose. But I doubt you need that in this case?
You'll want to look at the docs:
Additional services and add-ons
Additional services with docker-compose
Then create a .ddev/docker-compose.nifi.yaml with something like
services:
nifi:
image: apache/nifi:latest
container_name: ddev-${DDEV_SITENAME}-nifi
container_name: "ddev-${DDEV_SITENAME}-someservice"
labels:
com.ddev.site-name: ${DDEV_SITENAME}
com.ddev.approot: ${DDEV_APPROOT}
expose:
- "8080"
environment:
- VIRTUAL_HOST=$DDEV_HOSTNAME
- HTTP_EXPOSE=8080:8080
- HTTPS_EXPOSE=9999:8080
volumes:
- ".:/mnt/ddev_config"
The name of the "web" container from inside your nifi container will be "web", curl http://web:8080, assuming that you have nifi on port 8080.
I don't know what you're trying to accomplish, but this may get you started. Feel free to come over to the DDEV Discord channel for more interactive help.

Two docker containers cannot communicate

I have two docker containers. One container is a database and the other is a web application.
Web application calls the database through this link http://localhost:7200. However, the web application docker container cannot reach the database container.
I tried this docker-compose.yml, but does not work:
version: '3'
services:
web:
# will build ./docker/web/Dockerfile
build:
context: .
dockerfile: ./docker/web/Dockerfile
links:
- graph-db
depends_on:
- graph-db
ports:
- "8080:8080"
environment:
- WAIT_HOSTS=graph-db:7200
networks:
- backend
graph-db:
# will build ./docker/graph-db/Dockerfile
build:
./docker/graph-db
hostname: graph-db
ports:
- "7200:7200"
networks:
backend:
driver: "bridge"
So I have two containers:
web application: http://localhost:8080/reasoner and this container calls a database in http://localhost:7200 which resides in a different container.
However database container is not reachable by web container.
SOLUTION
version: '3'
services:
web:
# will build ./docker/web/Dockerfile
build:
context: .
dockerfile: ./docker/web/Dockerfile
depends_on:
- graph-db
ports:
- "8080:8080"
environment:
- WAIT_HOSTS=graph-db:7200
graph-db:
# will build ./docker/graph-db/Dockerfile
build:
./docker/graph-db
ports:
- "7200:7200"
and replace http://localhost:7200 in web app code with http://graph-db:7200
Do not use localhost to communicate between containers. Networking is one of the namespaces in docker, so localhost inside of a container only connects to that container, not to your external host, and not to another container. In this case, use the service name, graph-db, instead of localhost, in your app to connect to the db.
Your db host is graph-db, and that name that you should use in database configuration in your app. eg: http://graph-db:7200
From docker network documentation (bridge networks - the default network driver in Docker):
Imagine an application with a web front-end and a database back-end.
If you call your containers web and db, the web container can connect
to the db container at db, no matter which Docker host the application
stack is running on.

Conusume API from a client docker container to the server container

I have two different projects running on different docker containsers. Below the two YML files:
FILE webserver-api/docker-compose.yml
version: "3.1"
services:
webserver:
image: nginx:alpine
container_name: webserver-api
working_dir: /application
volumes:
- .:/application
- ./docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "8005:80"
FILE client-app/docker-compose.yml
version: '3'
services:
web:
container_name: client-app
build:
context: ./
dockerfile: deploy/web.docker
volumes:
- ./:/var/www
ports:
- "8010:80"
links:
- app
app: [...]
database: [...]
From the client-app I would like to call the webserver-api.
When I'm trying to consume the API from webserver-api I'm getting the message "cURL error connection refused" or timeout error.
For example
$response = file_get_contents('http:/localhost:8005/api/test');
I tried also to replace the localhost with the IP of the webserver-api container like this:
$response = file_get_contents('http://172.25.0.2:8005/api/test');
But still I get a timeout connection error.
Which is the correct URL of the server container to call form the client container? Or how to set the host URL?
Thanks a lot for the help and time.
You need create a network first. Then use this network for both your client and server docker compose. Otherwise the network is isolated.
Another approach is expose the port of server to localhost and connect to localhost from client side.
As per the docker-compose documentation
By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.
So ideally if your service are interdependent you should put them in a single compose file. In that case you could have accessed your service directly by name and container port
http://webserver/api/test
But since they are in separate compose file, you can access the service via host mapped port
$response = file_get_contents('http://localhost:8005/api/test');
it should also work.
To debug you can check
If port binding to 8005 is happening on your host.
The endpoint specified is correct and accessible from host.
Finally I figured it out.
By default docker creates a network called (in my case) webserver-api_default where webserver-api is the name of the folder that contains the YML file [projectname]_default.
On the client-app/docker-compose.yml of the client I had to specify which network to join:
version: '3'
networks:
default:
external:
name: webserver-api_default
web:
container_name: client-app
build:
context: ./
dockerfile: deploy/web.docker
volumes:
- ./:/var/www
ports:
- "8010:80"
links:
- app
app: [...]
database: [...]
And from the client container I have to make the call to the URL:
$response = file_get_contents('http://webserver-api:8005/api/test');
Where webserver-api is the name of the server container and not the name of the network.
https://docs.docker.com/compose/networking/

Docker web app can't communicate with API app

I have 2 .net core apps running in docker (one is a web api, the other is a web app consuming the web api):
I can't seem to communicate with the api via the web app, but I can access the api by going directly to it in my browser at http://localhost:44389
I have an environment variable in my web app that has that same info, but it can't get to it.
If I were to specify the deployed version of my API on azure, it's able to communicate with that address. Seems like the problem is the containers talking to each other.
I read that creating a bridge should fix that problem but it doesn't seem to. What am I doing wrong?
Here is my docker compose file:
version: '3.4'
services:
rc.api:
image: ${DOCKER_REGISTRY}rcapi
build:
context: .
dockerfile: rc.Api/Dockerfile
ports:
- "44389:80"
rc.web:
image: ${DOCKER_REGISTRY}rcweb
build:
context: .
dockerfile: rc.Web/Dockerfile
environment:
- api_endpoint=http://localhost:44389
depends_on:
- rc.api
networks:
my-net:
driver: bridge
docker-compose automatically creates a network between your containers. As your containers are in the same network you would be able to connect between containers using aliases. docker-compose creates an alias with the container name and the container IP. So in your case docker-compose should look like
version: '3.4'
services:
rc.api:
image: ${DOCKER_REGISTRY}rcapi
build:
context: .
dockerfile: rc.Api/Dockerfile
ports:
- "44389:80"
rc.web:
image: ${DOCKER_REGISTRY}rcweb
build:
context: .
dockerfile: rc.Web/Dockerfile
environment:
- api_endpoint=http://rc.api
depends_on:
- rc.api
networks:
my-net:
driver: bridge
As in rc.api opens port 80 in its container, therefore rc.web can access to 80 port with http://rc.api:80 or http://rc.api (without port since HTTP port is default 80)
You need to call http://rc.api because you have two containers and the API containers localhost is different from the web apps container localhost.
The convention is each service can be resolved by its name specified in the docker-compose.yml.
Thus you can call the API on internal Port 80 instead of exposing it on a particular port.

Docker-compose doesn't resolve DNS to correct service

I have two services, web and helloworld. The following is my docker-compose YAML file:
version: "3"
services:
helloworld:
build: ./hello
volumes:
- ./hello:/usr/src/app
ports:
- 5001:80
web:
build: ./web
volumes:
- ./web:/usr/share/nginx/html
ports:
- 5000:80
depends_on:
- helloworld
Inside the index.html in web, I made a button that opens http://helloworld when clicked on. However, my button ends up going to helloworld.com instead of the correct service. Both services work fine when I do localhost:5001 and localhost:5000. Am I missing something?
Docker's embedded DNS for service discovery is for container-to-container networking. For connections from outside of docker (e.g. from your browser) you need to publish the port (e.g. 5000 and 5001 in your file) and connect to that published port.
To use the container-to-container networking, you would need the DNS lookup to happen inside of the web container and the connection to go from web to helloworld, instead of from your browser to the container.
Edit: from your comment, you may find a reverse proxy helpful. Traefik and nginx-proxy are two examples out there. You can configure these to forward to containers by hostname or by a virtual path, and in your situation, I think path based routing would be easier. The resulting compose file would look something like:
version: "3"
services:
traefik:
image: traefik
command: --docker --docker.watch
volumes:
- /var/lib/docker.sock:/var/lib/docker.sock
ports:
- 8080:80
helloworld:
build: ./hello
volumes:
- ./hello:/usr/src/app
labels:
- traefik.frontend.rule=PathPrefixStrip:/helloworld
- traefik.port=80
web:
build: ./web
volumes:
- ./web:/usr/share/nginx/html
labels:
- traefik.frontend.rule=PathPrefixStrip:/
- traefik.port=80
The above is all untested off the top of my head configuration, but should get you in the right direction. With the PathPrefixStrip rule, you can make a link in web to "/helloworld" which will go to the other container. And since the link doesn't have a hostname or port, it will go to the same traefik hostname/port you are already using.

Resources