Configuration of Angular2 application - nginx and docker - angularjs

I have a REST back-end service located on some server and front-end application made in angular.
I'm using Angular CLI to build application. Location of my back-end server is located in environment file.
Requirement for my app is that I'm providing two docker images. One with my back-end server (Java Spring Boot app) and the second one is static html build with ng build myApp command. Then i copy content od dist directory to proper directory on docker image as is shown here Nginx docker image.
Problem is, that back-end and front-end may work on different servers. Is there any way i can configure my front-end app that i can change back-end server location as per start of container?

I know this is an old question, but I faced the exact same problem and it took me a while to solve. May this be of help to those coming from search engines.
I found 2 solutions (I ended up choosing the second one). Both allow you to use environment variables in docker to configure your API URL.
Solution 1 ("client"-side): env.js asset + sed
The idea is to have your Angular client load an env.js file from your HTTP server. This env.js will contain the API URL, and will be modifiable by your container when it starts. This is what you discussed in the question comments.
Add an env.js in your angular app assets folder (src/assets for me with angular-cli):
var MY_APP_ENV = {
apiUrl: 'http://localhost:9400',
}
In your index.html, you will load your env:
<head>
<meta charset="utf-8">
<base href="/">
<script src="env.js"></script>
</head>
In your environment.ts, you can use the variable:
declare var MY_APP_ENV: any;
export const environment = {
production: false,
apiUrl: MY_APP_ENV.apiUrl
};
In your NGINX Dockerfile do:
FROM nginx:1.11-alpine
COPY tmp/dist /usr/share/nginx/html
COPY run.sh /run.sh
CMD ["sh", "/run.sh"]
The run.sh script is where the sed magic happens:
#!/bin/sh
# API
/bin/sed -i "s|http://localhost:9400|${MY_API_URL}|" /usr/share/nginx/html/env.js
nginx -g 'daemon off;'
In your angular services, use environment.apiUrl to connect to the API (you need to import environment, see Angular 2 docs).
Solution 2 (purely server side): nginx proxy config + envsubst
I wasn't happy with the previous solution because the API URL needed to be from the host point of view, it couldn't use another container hostname in my docker-compose setup.
So I thought: many people use NGINX as a proxy server, why not proxy /api to my other container this way.
Dockerfile:
FROM nginx:1.11-alpine
COPY tmp/dist /usr/share/nginx/html
COPY frontend.conf.template /etc/nginx/conf.d/frontend.conf.template
COPY run.sh /run.sh
CMD ["/bin/sh", "/run.sh"]
frontend.conf.template:
server {
listen 80;
server_name myserver;
# API Server
location /api/ {
proxy_pass ${MY_API_URL}/;
}
# Main
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri$args $uri$args/ /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
run.sh:
#!/bin/sh
# Substitute env vars
envsubst '$MY_API_URL' \
< /etc/nginx/conf.d/frontend.conf.template \
> /etc/nginx/conf.d/default.conf
# Start server
nginx -g 'daemon off;'
envsubt allows you to substitute environment variables in a string with a shell-like syntax.
Then use /api/xyz to connect to the API from the Angular app.
I think the second solution is much cleaner. The API URL can be the API docker container name in a docker-compose setup, which is nice. The client is not involved, it is transparent. However, it depends on NGINX.

Related

Unexpected Token Error in React When Hosting on a Subpath Using Nginx + Traefik

I have a Docker container running a production React app alongside Nginx for hosting its static files.
Dockerfile
FROM node:16.5.0-alpine AS builder
WORKDIR /app
COPY . .
ENV PUBLIC_URL /trade-journal
RUN npm ci --production
RUN npm run build
FROM nginx:1.23.1-alpine AS production
ENV NODE_ENV production
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
server {
listen 80;
location / {
# static file hosting location
root /usr/share/nginx/html/;
include /etc/nginx/mime.types;
try_files $uri $uri/ /index.html;
}
}
Above, you can see that I'm setting a PUBLIC_URL environment variable. This is because I'd like the app to be hosted on a subpath, like this: domain.com/trade-journal. I'm using Traefik to route to this subpath because in the future, I'd like to add more subpaths for other apps.
docker-compose.yml
reverse-proxy:
image: traefik:v2.8
command: --api.insecure=true --providers.docker
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
labels:
- traefik.enable=false
frontend:
build:
context: ./trade-journal/client
dockerfile: ./Dockerfile
image: "trade-journal-client"
labels:
- traefik.http.routers.frontend.rule=Host(`[MY-PUBLIC-IP-HERE]`) && PathPrefix(`/trade-journal`)
- traefik.http.services.frontend.loadbalancer.server.port=80
links:
- "backend:be"
I understand that React is designed to run on root by default, so I added:
<base href="%PUBLIC_URL%/"> in public/index.html, and
basename={process.env.PUBLIC_URL} to BrowserRouter
I've also alternatively tried setting the subpath with homepage in package.json, and writing the basename as just /trade-journal.
Either way, I get the same error in my browser on a blank page:
For whatever reason, it can't load http://domain/trade-journal/static/js/main.93967f03.js. Actually, I can't even view the code in that file, or any other JS or CSS files in that directory through the browser (even though I can when I bin/sh into the running container); I only ever get a blank index file.
I assume that the problem has to do with routing because when I remove any having to do with the subpath and host on /, the React app loads correctly.
I'm very new to hosting and deploying. How can I solve this problem?
It seems that I fixed it. I needed to use alias, and include the subpath in nginx as well. Now, I'm going to see if I can make this solution any more DRY (so I don't have to repeat the subpath so many times). If anyone has any suggestions, let me know.
location /trade-journal {
# static file hosting location
alias /usr/share/nginx/html/;
include /etc/nginx/mime.types;
try_files $uri $uri/ /trade-journal/index.html;
}

How to deploy a Django and React project on the same Ubuntu 18.04 server using gunicorn and nginx?

I have a Django project that I have already successfully deployed on my Ubuntu 18.04 server via gunicorn and nginx using this tutorial.
https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-18-04
The project uses Django Rest Framework and I'm able to access it's endpoints via a web browser. However, I would also like to deploy a separate react project on the same server, so that it can send http requests to the Django app and display data received from the REST API. How can I go about doing this?
Here is my current gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/my_project/coffeebrewer
ExecStart=/home/ubuntu/my_project/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/ubuntu/my_project/coffeebrewer/coffeebrewer.sock coffeebrewer.wsgi:application
[Install]
WantedBy=multi-user.target
And here are my current nginx configurations
server {
listen 80;
listen [::]:80;
server_name my_ipv6_address;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root root /home/ubuntu/my_project/coffeebrewer;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
I recommend that you try it locally first as a production mode by installing whitenoise django package and in your settings.py file add this line SECURE_CROSS_ORIGIN_OPENER_POLICY = None
From there, you can navigate forward

ReactJS + Nginx 404 Not Found

After coding my first website on React, I want to host it on a raspberry pi using nginx.
I have checked website is ok by npm start:
I then built it to create static files using npm run build to create the following files in ~/Documents/myWebsite1/build:
asset-manifest.json css favicon.ico files images index.html js manifest.json resumeData.json robots.txt static
After this I installed nginx, deleted default in both /etc/nginx/sites-available and /etc/nginx/sites-enabled then added the following file in /etc/nginx/sites-available:
server {
listen 80;
server_name localhost;
root ~/Documents/myWebsite1/build;
index index.html index.htm;
location / {
try_files $uri /index.html =404;
}}
nginx -t confirms syntax is ok.
nginx -T confirms only this server block is running.
When I go to my IP address, the page just reads 404 Not Found.
I have checked the logs using sudo tail -n 20 /var/log/nginx/error.log which return:
2022/01/10 18:01:25 [notice] 15929#15929: signal process started
Any ideas as to what I might be doing wrong?
Cheers,
Will
Nginx was trying to access the directory ~/Documents/myWebsite1/build but ~ is only a shell shortcut. Changing to an absolute path fixed it right away :)

Nginx, Docker and Docker Compose

I'm trying to set up a microservices architecture for a personal blog.
The idea is to have an NGINX container serving up a static gatsby site, and to redirect to other services. For example, I'd like to have a react app at /todos, and an api for that todo app at /todos_api.
My current folder structure is like this:
docker-compose.yml
gatsby_blog
(contains a build folder)
nginx
default.conf (this is my main nginx entry)
portfolio
todos
todo_client
nginx
default.conf (this is just for serving the react app)
todo_api
My docker-compose file looks like this:
version: "3"
services:
gatsby:
restart: always
build:
dockerfile: Dockerfile
context: ./gatsby_blog
ports:
- "80:80"
todoclient:
build:
dockerfile: Dockerfile
context: ./portfolio/todos/todo_client
My main Gatsby nginx file looks like this:
upstream todoclient {
server todoclient:3000;
}
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /todos {
rewrite /todos/(.*) /$1 break;
proxy_pass http://todoclient;
}
}
and my React nginx config is like this:
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
The issue I'm pretty certain is with my nginx configs. When I go to localhost I'm met with the gatsby app, but if I go to /todos I get an nginx error. I can see that the request is passed on to the todoclient container correctly, but the error returned is:
open() "/usr/share/nginx/html/todos" failed (2: No such file or directory
If anyone can see where I'm going wrong with the nginx configs I'd really appreciate it. I can post my Dockerfiles too if needed.
Thanks
EDIT
I've managed to get the proxy working now, but the issue is that the todos app cant find its static files. They're in the correct place in the container, and the container works in isolation, so the issue is to do with docker-compose and the nginx proxying.
Done it for angular. The nginx file should replace the file in /etc/nginx/nginx.conf. And place the todos folder - assuming that has the static pages - in /usr/share/nginx/html. You can try one service at a time
http {
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
include /etc/nginx/mime.types;
location /todos {
alias /usr/share/nginx/html/todos/;
absolute_redirect off;
rewrite ^(.+)/todos/+$ $1 permanent;
rewrite ^(.+)/todos/index.html$ $1 permanent;
try_files $uri$args $uri$args/ $uri/ /todos/index.html;
}
}
}

Using nginx to run dynamic content

I wanna use nginx to run my node.js application. I created a build of the application and inside my nginx.conf I set the root to point to the location of the build folder. This worked and my application ran successfully on nginx.
Now I'm wondering if I could serve dynamic content directly through nginx. Like how I would get the app running with npm start can I do something similar with nginx instead of using the build(static) files?
You need a reverse proxy.
In your application. Configure your server to run on an internal port. For example 3000.
Then configure nginx to proxy connections to your app. Here's a simple nginx configuration to do just that:
root /path/to/app/build;
# Handle static content
location ^~ /static {
try_files $uri $uri/ =404;
}
# Handle dynamic content
location / {
proxy_pass http://127.0.0.1:3000;
}
Or, if you prefer, you can invert the URL scheme to default to static files:
root /path/to/app/build;
# Handle dynamic content
location ^~ /api {
proxy_pass http://127.0.0.1:3000;
}
# Handle static content
location / {
try_files $uri $uri/ =404;
}
Why do something like this?
There are several reasons to use an nginx front-end instead of setting your server to serve directly on port 80.
Nginx can server static content much faster than Express.static or other node static server.
Nginx can act as a load-balancer when you want to scale your server.
Nginx has been battle-tested on the internet so most security issues has been fixed or is well known. In comparison, express or http.server are just libraries and you are the person responsible for your application's security.
Nginx is a bit faster at serving HTTPS compared to node. So you can develop a plain-old HTTP server in node and let nginx handle encryption.

Resources