I'm building a web application with Angular.js 2, Elasticsearch, and Docker.
I'm trying to access Elasticsearch via its REST API from my Angular app using the linked url "http://search:9200/_cat/health", but I'm getting a GET http://search:9200/_cat/health net::ERR_NAME_NOT_RESOLVED error.
The weird thing is that when I do curl -XGET "http://search:9200/_cat/health" from inside the web app's container everything is fine and I'm getting a correct response.
Here is my respective docker-compose.yml file
version: '2'
services:
search:
image: elasticsearch:latest
command: elasticsearch -Des.network.host=0.0.0.0
ports:
- "9200:9200"
- "9300:9300"
web:
build: .
command: npm start
volumes:
- .:/visfx
ports:
- "3000:3000"
links:
- search
also here's the call to the elasticsearch service
#Injectable()
export class ElasticsearchService {
private _esUrl = 'http://search:9200';
constructor(private http: Http) { }
getHealthString() {
let healthUrl = '/_cat/health';
return this.http.get(this._esUrl + healthUrl)
.map(res => <string>res.text())
.do(data => console.log(data))
.catch(this.handleError);
}
...
}
If that javascript is running on the client side you'll have to use a real DNS name which routes to the docker host as docker's names are only resolved using docker's internal resolver.
Related
I have a React and .NET 6 project, each in separate containers and brought up together on the same Docker Virtual Network via Docker Compose. Both start and run fine but the React frontend is unable to communicate to the backend.
For whatever reason, I can access the weather api on my backend using my browser (doesn't work on the frontend) but not any other endpoint. https://localhost:5001/api/weatherforecast returns the generated weather data (default tutorial weather api).
But when I go to other endpoints like https://localhost:5001/api/user?userId=1, I get the following: Error: The SSL connection could not be established, see inner exception.
I'm not sure if it is an issue with the setupProxy.js or something wrong with the self-signed cert on the .NET backend.
docker-compose.yml
version: "3.8"
networks:
api-network:
services:
server:
build:
context: ./server-testing/server
args:
PROXY: ${PROXY}
container_name: 'server'
ports:
- '5000:5000'
- '5001:5001'
networks:
- api-network
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_Kestrel__Certificates__Default__Password=${PW}
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
volumes:
- ~/.aspnet/https:/https:ro
client:
build:
context: ./client
args:
PROXY: ${PROXY}
container_name: 'client'
depends_on:
- server
ports:
- '3000:3000'
networks:
- api-network
stdin_open: true # Keep STDIN open even if not attached
tty: true # Allocate a pseudo-TTY
Frontend Setup
The build is served by an nginx layer to serve a production React build.
In my package.json, I have "start": "set HTTPS=true&&react-scripts start" instead of "start": "react-scripts start".
// setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
const context = [
"/api"
];
module.exports = function (app) {
const appProxy = createProxyMiddleware(context, {
target: 'https://server:5001/',
secure: false,
changeOrigin: true
});
app.use(appProxy);
};
Backend Setup
Self-signed certs generated with dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p { password here }
Three endpoints:
/weatherforecast
/user/
/cart/
Endpoints are accessed as https://localhost:5001/api/endpoint?params
If you need any more information, please let me know. Thanks!
Currently I have two container inside AWS EC2 instance.One of them is React app. It uses nginx. This app should send request to golang web service which is inside another container. They are both in same EC2 instance. When i open browser and go to EC2 instance's public IP address i am able to see my React app is working. When i try to send request to golang web service it says that "(failed)net::ERR_CONNECTION_REFUSED". I am able to use curl request inside EC2 and receive response. How can i do the same on React Request.
Here is the my axios post
axios.post('http://localhost:9234/todos', { "name": todo, "completed": false },).then((res) => {
console.log(res)
if (res.data.todo.name === todo) {
setTodos([...todos, todo]);
}
}).catch((err) => { console.log(err) });
Here is my request with curl
curl -d '{"id":9,"name":"baeldung"}' -H 'Content-Type: application/json' http://localhost:9234/todos
Thanks for helps
from your details it seems likely that one or more of the following is true:
you do not have port 9234 forwarded to the container
you do not have port 9234 open in the EC2 instance's security group
Furthermore, localhost as #Jaroslaw points out is from the persective of the browser. That localhost should also be the IP of the ec2 instance or dns that resolves to that IP.
To be clear, the react webapp doesn't run on the ec2 instance. Its static assets such as DOM elements and Javascript get served to the browser and it runs there.
As #Daniel said javascript gets served to the browser and it runs there. So when your browser requesting the address localhost it actually means your computer's localhost. To access the golang server you need to forward the 9234 port from the docker container.
services:
web:
ports:
- "9234:9234"
And then also you need to open the 9234 port in firewall of your ec2 instance then you can access your go server using the public address of your ec2 from your browser.
axios.post('http://{{public_address_of_Ec2}}:9234/todos', { "name": todo, "completed": false },).then((res) => {
console.log(res)
if (res.data.todo.name === todo) {
setTodos([...todos, todo]);
}
}).catch((err) => { console.log(err) });
But this is not recommended to expose the ports to access your server. You may use nginx to listen on your port 80 and then load balance the requests to your go server. Here is a yaml you can add in your docker-compose to use nigx:
nginx:
image: nginx:latest
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web #your web app service name
ports:
- "80:80"
networks:
- "web.network" #your existing network name
And the nginx conf should be:
user nginx;
# can handle 1000 concurrent connections
events {
worker_connections 1000;
}
# forwards http requests
http {
# http server
server {
# listens the requests coming on port 80
listen 80;
access_log off;
# / means all the requests have to be forwarded to api service
location / {
# resolves the IP of api using Docker internal DNS
proxy_pass http://web:9234;
}
}
}
I have searched for hours trying to find a decent solution to my problem and I can't seem to find a solution. Basically, I broke up my Flask API into two separate files using blueprints, one that serves as the main Flask application and the other to handle all the authentication routes. I am running into CORS issues... Before I separated them, it worked fine with the flask-cors library. However, when I broke it into separate files using blueprints, it didn't work. I did a bunch of research and there seems be a couple ways to do it, but the best way seems to be solving the problem on the server side (at least in theory because I haven't found a solution to it yet).
app.py
# Third-party libraries
from flask import Flask
from flask_cors import CORS
# Internal imports
from login import login_page
# Flask app setup
app = Flask(__name__)
app.register_blueprint(login_page)
CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
if __name__ == "__main__":
app.run(host="localhost", debug=True)
login.py
# Third-party libraries
from flask import Flask, redirect, request, url_for, Blueprint, render_template, abort
from flask_login import (
LoginManager,
current_user,
login_required,
login_user,
logout_user
)
from oauthlib.oauth2 import WebApplicationClient
import requests
from flask_cors import CORS
login_page = Blueprint('login_page', __name__)
CORS(login_page)
# Login route
#login_page.route('/login', methods=['GET'])
def login():
# Find out what URL to hit for Google login
google_provider_config = get_google_provider_config()
authorization_endpoint = google_provider_config["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri = request.base_url + "/callback",
scope = ["openid", "email", "profile"]
)
return redirect(request_uri)
From the documentation:
https://flask-cors.readthedocs.io/en/latest/api.html#using-cors-with-blueprints
Which seems to be what I am doing... But it's not working.
Edit:
React code:
authenticate = async () => {
console.log('authenticate called')
const response = await users.get('/login');
}
I added a test route to see if the way I was handling blue prints was incorrect with the flask-cors library as follows:
#login_page.route('/test')
def test():
print('test')
return 'test'
Sure enough, I was able to see the text print on the console. So this leaves me to believe it is something else preventing me from accessing Google's authentication servers.
Flask application is running on localhost:5000 and React application is running on localhost:3000. The string that gets passed into the redirect method in login is as follows:
https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=None&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Flogin%2Fcallback&scope=openid+email+profile
However, this was working prior to working with React.
Chrome console error:
Access to XMLHttpRequest at '...' (redirected from 'http://localhost:3000/login') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have also added a proxy to my package.json file as follows:
"proxy": "http://localhost:5000"
Which I got from: https://blog.miguelgrinberg.com/post/how-to-create-a-react--flask-project/page/3
Update:
I have decided to go with the approach of solving this problem using Nginx since it seems to that flask-cors library is not suitable for this problem as no solution seems to be possible.
Nginx.conf
user www-data;
worker_processes 1;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
upstream react-client {
server react-client;
}
upstream flask-backend {
server flask-backend;
}
server {
listen 80;
server_name localhost;
location / {
# From docker-compose, react-client port 80 is exposed
proxy_pass http://react-client;
}
# location /login/callback
# location /logout
}
server {
listen 3000;
server_name localhost;
location /login {
proxy_pass http://flask-backend;
}
}
}
docker-compose.yml:
version: '3'
services:
reverse-proxy:
image: nginx:1.17.10
container_name: reverse-proxy
depends_on:
- flask-backend
- react-client
volumes:
- ./reverse-proxy/nginx.conf:/etc/nginx/nginx.conf
ports:
- 80:80
react-client:
image: react-client
container_name: react-client
build:
context: ./client
depends_on:
- flask-backend
ports:
- 3000:3000
restart: on-failure
flask-backend:
image: flask-backend
container_name: flask-backend
build:
context: ./api
ports:
- 5000:5000
restart: on-failure
With the Nginx configuration and Docker files, I am able to go to http://localhost:80 which forwards me to the react client. This time, I am not getting the CORS errors, but I do get this message in my inspector, Failed to load resource: The network connection was lost.
My understanding is this:
http://localhost:80 -> http://localhost:3000, then when a button is pressed, it prompts the new route
http://localhost:3000/login -> http://localhost:5000/login
I have two server blocks because the web application is running in port 3000 and Nginx server on port 80.
Instead of CORS(login_page) try CORS(login_page, resources=r'/login')
Edit: To explain why this might work.
The error you're getting indicates that the /login route doesn't have CORS permitted. You're instantiating Flask-CORS correctly so I suspect if you tried delineating the specific route you want CORS to apply to you would fix the issue.
The docs make it seem like CORS(login_page) should automatically open up all routes to CORS but because that's not working trying the above answer might provide better results.
The Error
When deploying to Azure Web Apps with Multi-container support, I receive an "Invalid Host Header" message from https://mysite.azurewebsites.com
Local Setup
This runs fine.
I have two Docker containers: client a React app and server an Express app hosting my API. I am using a proxy to host my API on server.
In client's package.json I have defined:
"proxy": "http://localhost:3001"
I use the following docker compose file to build locally.
version: '2.1'
services:
server:
build: ./server
expose:
- ${APP_SERVER_PORT}
environment:
API_HOST: ${API_HOST}
APP_SERVER_PORT: ${APP_SERVER_PORT}
ports:
- ${APP_SERVER_PORT}:${APP_SERVER_PORT}
volumes:
- ./server/src:/app/project-server/src
command: npm start
client:
build: ./client
environment:
- REACT_APP_PORT=${REACT_APP_PORT}
expose:
- ${REACT_APP_PORT}
ports:
- ${REACT_APP_PORT}:${REACT_APP_PORT}
volumes:
- ./client/src:/app/project-client/src
- ./client/public:/app/project-client/public
links:
- server
command: npm start
Everything runs fine.
On Azure
When deploying to Azure I have the following. client and server images have been stored in Azure Container Registry. They appear to load just fine from the logs.
In my App Service > Container Settings I am loading the images from Azure Container Registry (ACR) and I'm using the following configuration (Docker compose) file.
version: '2.1'
services:
client:
image: <clientimage>.azurecr.io/clientimage:v1
build: ./client
expose:
- 3000
ports:
- 3000:3000
command: npm start
server:
image: <serverimage>.azurecr.io/<serverimage>:v1
build: ./server
expose:
- 3001
ports:
- 3001:3001
command: npm start
I have also defined in Application Settings:
WEBSITES_PORT to be 3000.
This results in the error on my site "Invalid Host Header"
Things I've tried
• Serving the app from the static folder in server. This works in that it serves the app, but it messes up my authentication. I need to be able to serve the static portion from client's App.js and have that talk to my Express API for database calls and authentication.
• In my docker-compose file binding the front end to:
ports:
- 3000:80
• A few other port combinations but no luck.
Also, I think this has something to do with the proxy in client's package.json based on this repo
Any help would be greatly appreciated!
Update
It is the proxy setting.
This somewhat solves it. By removing "proxy": "http://localhost:3001" I am able to load the website, but the suggested answer in the problem does not work for me. i.e. I am now unable to access my API.
Never used azure before and I also don't use a proxy (due to its random connection issues), but if your application is basically running express, you can utilize cors. (As a side note, it's more common to run your express server on 5000 than 3001.)
I first set up an env/config.js folder and file like so:
module.exports = {
development: {
database: 'mongodb://localhost/boilerplate-dev-db',
port: 5000,
portal: 'http://localhost:3000',
},
production: {
database: 'mongodb://localhost/boilerplate-prod-db',
port: 5000,
portal: 'http://example.com',
},
staging: {
database: 'mongodb://localhost/boilerplate-staging-db',
port: 5000,
portal: 'http://localhost:3000',
}
};
Then, depending on the environment, I can implement cors where I'm defining express middleware:
const cors = require('cors');
const config = require('./path/to/env/config.js');
const env = process.env.NODE_ENV;
app.use(
cors({
credentials: true,
origin: config[env].portal,
}),
);
Please note the portal and the AJAX requests MUST have matching host names. For example, if my application is hosted on http://example.com, my front-end API requests must be making requests to http://example.com/api/ (not http://localhost:3000/api/ -- click here to see how I implement it for my website), and the portal env must match the host name http://example.com. This set up is flexible and necessary when running multiple environments.
Or if you're using the create-react-app, then simply eject your app and implement a proxy inside the webpack production configuration.
Or migrate your application to my fullstack boilerplate, which implements the cors example above.
So, I ended up having to move off of containers and serve the React app up in more of a typical MERN architecture with the Express server hosting the React app from the static build folder. I set up some routes with PassportJS to handle my authentication.
Not my preferred solution, I would have preferred to use containers, but this works. Hope this points someone out there in the right direction!
I have an Angularjs frontend and a Spring-Boot Rest API in backend.
I have create two Docker
DockerFile Front:
FROM httpd:2.4
COPY ./public-html/ /usr/local/apache2/htdocs/
DockerFile Back:
FROM tomcat:8.0
EXPOSE 8080
COPY rest-api.war /usr/local/tomcat/webapps/rest-api.war
I have a Docker-Compose file, i have define Alias
Docker-Compose:
rest:
image: restapi
container_name: restapi
ports:
- "8080:8080"
frontend:
image: frontend
container_name: frontend
ports:
- "80:80"
i redifine the baseURL in my AngularJs controller
app.controller('MainCtrl', function ($scope, $location, $http, MainService) {
var that = this;
var baseUrl = 'http://rest:8080';
when i lauchn my app in the console i have this
Error:
GET http://rest:8080/category net::ERR_NAME_NOT_RESOLVED
the hosts files in the other containers is not updated automatically
What is wrong ?
****** UPDATE ******
I have create a network
$docker network create my-network
I have redefine my docker-compose file
Container connected to that network reach other containers.
So i have the same error.
When i see in Kitematic my Backend have an ip like this:
And when i see in the hosts files the ip is not the same.
When i modify my controller with ip of Kitematic all works but when i use Alias is not working
So you are trying to use the linked alias inside your browser (Angular) application? Docker only exposes these aliases to the containers. Your local development system, being outside of the docker network, will not have these additions to the host file and therefore not be able to resolve hosts to IPs.
Any application running inside the containers, like a Node.js backend, will be able to use these aliases. Browsers can't.
If you create a new network, any container connected to that network can reach other containers by their name or service name.
create network
$docker network create your-network
docker-compose.yml
rest:
image: restapi
container_name: restapi
ports:
- "8080:8080"
net: your-network
frontend:
image: frontend
container_name: frontend
ports:
- "80:80"
net: your-network
Note if you use docker-compose file version 2.0. composer will create the network for you.
You can try and link the two containers in the configuration file.
rest:
image: restapi
container_name: restapi
ports:
- "8080:8080"
frontend:
image: frontend
container_name: frontend
ports:
- "80:80"
links:
- rest