Now I'm trying to deploy my MERN app on VPS.
I'd like to connect the client to the frontend and let the frontend communicate with the backend inside of the server. Because I think it's unnecessary to expose the backend on the public port since it'll be api server only for this site.
I've tried to mimic those files for Nginx settings and docker settings in so many ways and still doesn't work, and now I'm very frustrated. Because I really can't figure out the reason it doesn't let me connect to the backend docker.
1. On the top, 2 folders; frontend / backend
├── Backend
│ ├── Dockerfile
│
├── Frontend
│ │
│ └── Dockerfile
│ │
│ └── frontend.conf
│
└── docker-compose.yml
2. Frontend - 2 staged Dockerfile : stage 1 - react build, stage 2 - nginx with the build (and add .conf file to nginx)
/Frontend/Dockerfile
#
# Stage 1: React production build
#
FROM node:16.18.0 as frontend
WORKDIR /frontend
COPY package*.json ./
RUN yarn
COPY . .
ENV NODE_ENV=production
RUN yarn build
# CMD ["yarn","start:prod"]
EXPOSE 5500
#
# Stage 2: Nginx as a proxy & static file server
#
FROM nginx
WORKDIR /usr/share/nginx/html
RUN apt-get update && apt-get install -y certbot python3-certbot-nginx
RUN rm -rf /usr/share/nginx/html/*
COPY --from=frontend /peacemomo_frontend/build .
COPY --from=frontend /frontend/frontend.conf /etc/nginx/conf.d/
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
frontend.conf
upstream server {
server host.docker.internal:5050;
}
server {
listen 80 default_server;
listen [::]:80;
# listen 443 default_server;
# listen [::]:443 default_server;
# actual domain comes here - all set and able to open the static files.
server_name example.com www.example.com;
server_tokens off;
gzip on;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css application/javascript image/svg+xml;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header Authorization "";
proxy_hide_header Authorization;
}
}
**
3. Backend - simple Dockerfile from node
**
# FROM node:15.4.0
FROM node:16.18.0
WORKDIR /backend
COPY package.json ./
RUN yarn
RUN yarn remove sharp
RUN yarn add sharp
COPY . .
EXPOSE 5050
CMD ["yarn", "run", "start:prod"]
4. docker-compose.yml at the top to create docker containers with a network "nw_test".
docker-compose.yml
version: "3.9"
services:
frontend:
image: frontend
container_name: frontend
build:
context: ./frontend
dockerfile: Dockerfile
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- 80:80
- 443:443
restart: unless-stopped
env_file:
- ./frontend/.env.production
volumes:
- ./frontend:/frontend
- /frontend/node_modules
depends_on:
- backend
backend:
image: backend
container_name: backend
build:
context: ./backend
dockerfile: Dockerfile
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "5050"
restart: unless-stopped
env_file:
- ./backend/config/.env.production
volumes:
- ./backend:/backend
- /backend/node_modules
networks:
default:
name: nw_test
**Result:
After docker compose up, approaching to the domain,
index.html of frontend works.
Fail to connect to backend.
: the client request www.example.com/api/... => it attempts to connect upstream xxx.xxx.xx.x:5050 (it's the container local ip address) but failes with 504
**
[logs in the server]
[error] 6#6: *20 upstream timed out (110: Connection timed out) while connecting to upstream, client: ..., server: example.com, request: "GET /api/users/auth HTTP/1.1", upstream: "http://172.17.0.1:5050/api/users/auth", host: "example.com", referrer: "http://example.com/"
client |
"GET /api/users/auth HTTP/1.1" 504 562 "http://example.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" "-"
I am new in web-servers. I am setting up a server (example.de) serving two different dockerized applications. The exposed ports 3000 (app1) and 5000 (app2) are mapped in server ports 9000 and 9001 respectively. Requesting the urls example.de:9000 and example.de:9001, the content is properly load. Now, I want to use a NGINX container as reverse proxy to redirect the urls example.de/app1, and example.de/app2.
"include /etc/nginx/mime.types;" is set by default in the nginx.conf file.
The default.conf is
server {
listen 80;
listen [::]:80;
server_name example.de;
location /app1{
proxy_pass http://app1:3000;
}
location /app2{
proxy_pass http://app2:5000;
}
}
The working docker-compose file looks like:
version: '3'
services:
app1:
build:
context: app1
container_name: app1
restart: "<RESTART_POLICY>"
volumes:
- vol_app1:/data
app2:
build:
context: app2
container_name: app2
restart: "<RESTART_POLICY>"
volumes:
- vol_app2:/data
nginx-proxy:
container_name: nginx
build:
context: nginx/
ports:
- "80:80"
depends_on:
- app1
- app2
links:
- app1
- app2
volumes:
- vol_nginx:/etc/nginx/conf.d
volumes:
vol_app1:
driver_opts:
type: none
device: /vol/app1
o: bind
vol_app2:
driver_opts:
type: none
device: /vol/app2
o: bind
vol_nginx:
driver_opts:
type: none
device: /vol/nginx
o: bind
The NGINX Dockerfile is:
FROM nginx:latest
RUN rm /etc/nginx/conf.d/default.conf
f the redirection is set like the default.conf, when I access the url http://example.de/app1, the browser request for /app1 not for the entrypoint / (xxx.x.xx.xx - - [09/Aug/2019:06:31:57 +0000] "GET /app1 HTTP/1.1" 404 77 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0" "-").
UPDATE
All containers are in the same network.
Docker 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.
UPDATE II
If I do not redirect, it works by adding upstream blocks to the default.conf:
upstream app1{
server app1:3000;
}
upstream app2{
server app2:5000;
}
server {
listen 80;
listen [::]:80;
server_name example.de;
location / {
proxy_pass http://app1;
}
}
Nevertheless, the path with /app1 and /app2 does not redirect to the docker container.
I'm trying to use nginx as a load balancer / proxy server which points to a series to tomcat servers. This is my current nginx configuration.
server {
listen 80;
server_name _;
rewrite ^ https://$http_host$request_uri? permanent;
}
server {
listen 443;
resolver 127.0.0.11 valid=5s;
ssl on;
ssl_certificate /etc/nginx/certs/default.crt; # path to your cacert.pem
ssl_certificate_key /etc/nginx/certs/default.key; # path to your privkey.pem
ssl_verify_client off;
server_name localhost;
fastcgi_param HTTPS on;
fastcgi_param HTTP_SCHEME https;
charset utf-8;
client_max_body_size 200M;
set $app https://app:8443;
set $auth https://auth:8443/authentication/;
set $discovery https://discovery:8443/discovery/;
location / {
proxy_pass $app;
}
location /authentication {
proxy_pass $auth;
}
location /discovery {
proxy_pass $discovery;
proxy_set_header Host $http_host;
proxy_set_header X_FORWARDED_PROTO https;
}
}
This is dockerized if it makes any difference, but provisioning fails to resolve correctly while docs is working fine. The only difference between docs and provisioning is that 'docs' is serving pure html files via tomcat. (The tomcat7 standard /docs/) while provisioning is actually a java servlet (JaxRS/spring etc ).
If I hit the image directly it works as expected, while if I try to hit the same endpoint via the nginx it fails to resolve.
My docker-compose configuration for reference.
version: '2'
services:
db:
image: db:nodata
expose:
- 5433
zk:
image: zookeeper
ports:
- 2181:2181
discovery:
image: services_discovery:latest
env_file: docker_environment
expose:
- 8443
ports:
- 8443:8443
links:
- db
- zk
app:
image: tomcat-jsse-ssl:7-jdk8
volumes:
- ./app/www/:/usr/local/tomcat7/webapps/ROOT/
expose:
- 8443
ports:
- 8444:8443
auth:
image: tomcat-jsse-ssl:7-jdk8
volumes:
- ./authentication/www/authentication/:/usr/local/tomcat7/webapps/authentication/
expose:
- 8443
proxy:
build: ./proxy/
depends_on:
- 'auth'
- 'app'
- 'discovery'
ports:
- 443:443
restart: always
With the images running I can resolve the following URLs just fine.
https://localhost:8443/discovery/ready
https://localhost:8444/
ie. both containers are running fine:
https://localhost/ loaded via nginx works fine.
https://localhost/authentication/ loaded via nginx works fine.
https://localhost/discovery/ready ==> 404.
Server Logs:
proxy_1 | 172.20.0.1 - - [24/Apr/2017:00:04:28 +0000] "GET /discovery/ready HTTP/1.1" 404 400 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
proxy_1 | 172.20.0.1 - - [24/Apr/2017:00:04:43 +0000] "GET /discovery/api/swagger.json HTTP/1.1" 404 400 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
proxy_1 | 172.20.0.1 - - [24/Apr/2017:00:04:57 +0000] "GET /discovery/ready HTTP/1.1" 404 400 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
Discovery Tomcat Access Log (when access directly)
172.20.0.1 - - [23/Apr/2017:00:02:38 +0000] "GET /discovery/ready HTTP/1.1" 200 70
172.20.0.2 - - [24/Apr/2017:00:04:28 +0000] "GET /discovery/ HTTP/1.0" 404 949
172.20.0.2 - - [24/Apr/2017:00:04:43 +0000] "GET /discovery/ HTTP/1.0" 404 949
172.20.0.2 - - [24/Apr/2017:00:04:57 +0000] "GET /discovery/ HTTP/1.0" 404 949
The first entry is when I hit the server directly via https://localhost:8443/discovery/ready everything else
is when nginx sends the request to the server. For some reason it's not translating the request correctly.
Any thoughts/suggestions would be appreciated?
Note: I simplified my example/config for the purposes of this question and any references to "provisioning" are now "discovery".
UPDATE: I figured out why it's breaking for the 'servlet'. It's actually breaking constantly. It's stripping away all of the URL except the base.
for example.
https://localhost/authentication?q=dummy
becomes
172.20.0.3 - - [24/Apr/2017:03:22:06 +0000] "GET /authentication/ HTTP/1.0" 200 28
note that the query parameters are stripped away.
The nginx documentation says that you are responsable to rebuild the url:
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
So, you can try to capture the rest of the URI using regex and send it on the proxy_pass section:
location ~* ^/discovery/(.*) {
proxy_pass $discovery$1$is_args$args;
.... other configs....
}
I've a service listening to 8080 port. This one is not a container.
Then, I've created a nginx container using official image:
docker run --name nginx -d -v /root/nginx/conf:/etc/nginx/conf.d -p 443:443 -p 80:80 nginx
After all:
# netstat -tupln | grep 443
tcp6 0 0 :::443 :::* LISTEN 3482/docker-proxy
# netstat -tupln | grep 80
tcp6 0 0 :::80 :::* LISTEN 3489/docker-proxy
tcp6 0 0 :::8080 :::* LISTEN 1009/java
Nginx configuration is:
upstream eighty {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name eighty.domain.com;
location / {
proxy_pass http://eighty;
}
}
I've checked I'm able to connect with with this server with # curl http://127.0.0.1:8080
<html><head><meta http-equiv='refresh'
content='1;url=/login?from=%2F'/><script>window.location.replace('/login?from=%2F');</script></head><body
style='background-color:white; color:white;'>
...
It seems running well, however, when I'm trying to access using my browser, nginx tells bt a 502 bad gateway response.
I'm figuring out it can be a problem related with the visibility between a open by a non-containerized process and a container. Can I container stablish connection to a port open by other non-container process?
EDIT
Logs where upstream { server 127.0.0.1:8080; }:
2016/07/13 09:06:53 [error] 5#5: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 62.57.217.25, server: eighty.domain.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8080/", host: "eighty.domain.com"
62.57.217.25 - - [13/Jul/2016:09:06:53 +0000] "GET / HTTP/1.1" 502 173 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0" "-"
Logs where upstream { server 0.0.0.0:8080; }:
62.57.217.25 - - [13/Jul/2016:09:00:30 +0000] "GET / HTTP/1.1" 502 173 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0" "-" 2016/07/13 09:00:30 [error] 5#5: *1 connect() failed (111: Connection refused) while connecting to upstream, client:
62.57.217.25, server: eighty.domain.com, request: "GET / HTTP/1.1", upstream: "http://0.0.0.0:8080/", host: "eighty.domain.com" 2016/07/13 09:00:32 [error] 5#5: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 62.57.217.25, server: eighty.domain.com, request: "GET / HTTP/1.1", upstream: "http://0.0.0.0:8080/", host: "eighty.domain.com"
62.57.217.25 - - [13/Jul/2016:09:00:32 +0000] "GET / HTTP/1.1" 502 173 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0" "-"
Any ideas?
The Problem
Localhost is a bit tricky when it comes to containers. Within a docker container, localhost points to the container itself.
This means, with an upstream like this:
upstream foo{
server 127.0.0.1:8080;
}
or
upstream foo{
server 0.0.0.0:8080;
}
you are telling nginx to pass your request to the local host.
But in the context of a docker-container, localhost (and the corresponding ip addresses) are pointing to the container itself:
by addressing 127.0.0.1 you will never reach your host machine, if your container is not on the host network.
Solutions
Host Networking
You can choose to run nginx on the same network as your host:
docker run --name nginx -d -v /root/nginx/conf:/etc/nginx/conf.d --net=host nginx
Note that you do not need to expose any ports in this case.
This works though you lose the benefit of docker networking. If you have multiple containers that should communicate through the docker network, this approach can be a problem. If you just want to deploy nginx with docker and do not want to use any advanced docker network features, this approach is fine.
Access the hosts remote IP Address
Another approach is to reconfigure your nginx upstream directive to directly connect to your host machine by adding its remote IP address:
upstream foo{
//insert your hosts ip here
server 192.168.99.100:8080;
}
The container will now go through the network stack and resolve your host correctly:
You can also use your DNS name if you have one. Make sure docker knows about your DNS server.
For me helped this line of code proxy_set_header Host $http_host;
server {
listen 80;
server_name localhost;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_pass http://myserver;
}
Just to complete other answers, I'm using mac for development and using host.docker.internal directly on upstream worked for me and no need to pass the host remote IP address. Here is config of the proxy nginx:
events { worker_connections 1024; }
http {
upstream app1 {
server host.docker.internal:81;
}
upstream app1 {
server host.docker.internal:82;
}
server {
listen 80;
server_name app1.com;
location / {
proxy_pass http://app1;
}
}
server {
listen 80;
server_name app2.com;
location / {
proxy_pass http://app2;
}
}
}
As you can see, I used different ports for different apps behind the nginx proxy. I used port 81 for the app1 and port 82 for the app2 and both app1 and app2 have their own nginx containers:
For app1:
docker run --name nginx -d -p 81:80 nginx
For app2:
docker run --name nginx -d -p 82:80 nginx
Also, please refer to this link for more details:
docker doc for mac
What you can do is configure proxy_pass that from container perspective the adress will be pointing to your real host.
To get host address from container perspective you can do as following on Windows with docker 18.03 (or more recent):
Run bash on container from host where image name is nginx (works on Alpine Linux distribution):
docker run -it nginx /bin/ash
Then run inside container
/ # nslookup host.docker.internal
Name: host.docker.internal
Address 1: 192.168.65.2
192.168.65.2 is the host's IP - not the bridge IP like in spinus accepted answer.
I am using here host.docker.internal:
The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker for Windows.
Then you can change nginx config to:
proxy_pass http://192.168.65.2:{your_app_port};
and it should work fine.
Remember to provide the same port as your local application runs with.
# the upstream component nginx needs to connect to
upstream django {
# server unix:///path/to/your/mysite/mysite.sock; # for a file socket
server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}
location / {
uwsgi_pass django;
include /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
}
complete reference: https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html
nginx.sh
ip=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -n 1)
docker run --name nginx --add-host="host:${ip}" -p 80:80 -d nginx
nginx.conf
location / {
...
proxy_pass http://host:8080/;
}
It‘s works for me
I had this issue and it turned out to be an issue with the docker container not starting up due to a permissions issue.
In my case running
docker-compose ps
showed that the container had not started and exited with status 1. Turns out the permissions had been lost in migrating to a new machine. Adjusting the permissions to a know staff user on the parent directory fixed the problem for me and I was then able to start docker service where as previously I was getting
nginx_1_c18a7f6f7d6d | chown: /var/www/html: Operation not permitted
I've attempted to migrate my stack to use version 2 docker-compose.yml and have run into a problem with network hostnames not being resolved by nginx.
My stack involves an nginx reverse proxy (on debian:wheezy) that serves secure content via several other software components of which I won't go into detail (see config below).
In the version 1 yaml, I used environment variables from docker links alongside with LUA script to insert them into the nginx.conf (using nginx-extras). This worked perfectly as a reverse proxy in front of the docker containers.
In the version 2 yaml I am using the hostnames as generated by docker networking. I am able to successfully ping these hostnames from within the container, however nginx is unable to resolve them.
2016/05/04 01:23:44 [error] 5#0: *3 no resolver defined to resolve ui, client: 10.0.2.2, server: , request: "GET / HTTP/1.1", host: "localhost"
Here is my current config:
docker-compose.yml:
version: '2'
services:
# back-end
api:
build: .
depends_on:
- db
- redis
- worker
environment:
RAILS_ENV: development
ports:
- "3000:3000"
volumes:
- ./:/mmaps
- /var/log/mmaps/api:/mmaps/log
volumes_from:
- apidata
command: sh -c 'rm -rf /mmaps/tmp/pids/server.pid; rails server thin -b 0.0.0.0 -p 3000'
# background process workers
worker:
build: .
environment:
RAILS_ENV: development
QUEUE: "*"
TERM_CHILD: "1"
volumes:
- ./:/mmaps
- /var/log/mmaps/worker:/mmaps/log
volumes_from:
- apidata
command: rake resque:work
# front-end
ui:
image: magiandev/mmaps-ui:develop
depends_on:
- api
ports:
- "8080:80"
volumes:
- /var/log/mmaps/ui:/var/log/nginx
# database
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: pewpewpew
volumes_from:
- mysqldata
volumes:
- /var/log/mmaps/db:/var/log/mysql
# key store
redis:
image: redis:2.8.13
user: root
command: ["redis-server", "--appendonly yes"]
volumes_from:
- redisdata
volumes:
- /var/log/mmaps/redis:/var/log/redis
# websocket server
monitor:
image: magiandev/mmaps-monitor:develop
depends_on:
- api
environment:
NODE_ENV: development
ports:
- "8888:8888"
# media server
media:
image: nginx:1.7.1
volumes_from:
- apidata
ports:
- "3080:80"
volumes:
- ./docker/media/nginx.conf:/etc/nginx/nginx.conf:ro
- /srv/mmaps/public:/usr/local/nginx/html:ro
- /var/log/mmaps/mediapool:/usr/local/nginx/logs
# reverse proxy
proxy:
build: docker/proxy
ports:
- "80:80"
- "443:443"
volumes:
- /var/log/mmaps/proxy:/var/log/nginx
apidata:
image: busybox:ubuntu-14.04
volumes:
- /srv/mmaps/public:/mmaps/public
command: echo api data
mysqldata:
image: busybox:ubuntu-14.04
volumes:
- /srv/mmaps/db:/var/lib/mysql
command: echo mysql data
redisdata:
image: busybox:ubuntu-14.04
volumes:
- /srv/mmaps/redis:/data
command: echo redis data
# master data
# convenience container for backups
data:
image: busybox:ubuntu-14.04
volumes_from:
- apidata
- mysqldata
- redisdata
command: echo mmaps data
nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
# permanent redirect to https
server {
listen 80;
rewrite ^ https://$host$request_uri? permanent;
}
server {
listen 443 ssl;
ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
proxy_pass http://ui:80$request_uri;
}
location /monitor/ {
proxy_pass http://monitor:8888$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /api/ {
client_max_body_size 0;
proxy_pass http://api:3000$request_uri;
}
location /files/ {
client_max_body_size 0;
proxy_pass http://media:80$request_uri;
}
location /mediapool/ {
proxy_pass http://media:80$request_uri;
add_header X-Upstream $upstream_addr;
if ($request_uri ~ "^.*\/(.*\..*)\?download=true.*$"){
set $fname $1;
add_header Content-Disposition 'attachment; filename="$fname"';
}
proxy_pass_request_headers on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www;
}
}
}
# stay in the foreground so Docker has a process to track
daemon off;
After some reading I have tried to use 'dnsmasq' and set resolver 127.0.0.1 within the nginx.conf but I cannot get this to work:
2016/05/04 01:54:26 [error] 6#0: recv() failed (111: Connection refused) while resolving, resolver: 127.0.0.1:53
Is there a better way to configure nginx to proxy pass to my containers that works with V2?
You can rename your containers and resolving by names.