Nginx + Docker. 504 Gateway Timeout - docker

I've been struggling for a bit on this and I'm starting to get depressed...
I'm running a droplet on DigitalOcean with Ubuntu 18.10.
I've used Docker to serve my different service (nuxt, socket.io, nginx, nodejs)
And I've used Nginx to revese proxy those services (at first i'd just want to render the nuxt app)
When executing
docker-compose up --build -d
All processes are running and are accessible on the server but inaccessible from my local machine (I get a 504 Gateway Timeout).
There is one time where I got it working but when adding some location rule, it broke again.
So here's my config for nginx :
server {
listen 8080;
server_name 104.248.201.255;
location / {
proxy_pass http://doodlehelha-nuxt:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
And here's the nginx service in my docker-compose.yml
nginx:
image: nginx:1.15
container_name: doodlehelha-nginx
ports:
- '8080:8080'
- '443:443'
expose:
- '8080'
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- nuxt
- api
- socket
This is a very simple configuration and that's why I don't know what's wrong...
If someone more experienced can point it out, I'd be very grateful.

Related

Does accessing a remote server IP address via the browser default resolve to port 80?

I have a containerized app that uses nginx as a reverse proxy. If I map nginx ports as 1337:80 I am only able to reach my website at <MY_INSTANCE_IP>:1337. If I instead map nginx ports as 80:80 I am able to reach my website at <MY_INSTANCE_IP>. Changing the ports in my docker-compose file worked but I'd like to know why.
My docker-compose config:
version: '3.7'
services:
web:
build:
context: .
dockerfile: ./compose/production/flask/Dockerfile
image: flask_web
command: /start
volumes:
- .:/app
expose:
- 5000
env_file:
- .env/.prod
environment:
- FLASK_APP=app
nginx:
build: ./compose/production/nginx
ports:
- 80:80
depends_on:
- web
My nginx config:
upstream flask-app {
server web:5000;
}
server {
listen 80;
server_name <MY_INSTANCE_IP>;
location / {
proxy_pass http://flask-app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
# client_max_body_size 20M;
}
}
So, you have nginx set to listen on port 80 (default http). When you set the port for your nginx service in docker-compose the first number is the port that docker will "publish" the service on the host and the second number, after the colon (:), is the port the server is listening on "inside" the container. See:
https://docs.docker.com/config/containers/container-networking/#published-ports for more detail.

How to use nginx on Docker for Windows as reverse proxy for localhost?

I am using Docker for Windows and want to set up nginx as reverse proxy. All is working fine but if I want to define a proxy to my localhost I always get a 502 or 504 error. I thought setting an extra_host would solve my problem but didn't. Is there any other IP that I can try to set as host or is something else wrong?
docker-compose.yml:
version: '3'
volumes:
etc:
driver: local
services:
nginx:
container_name: nginx
image: nginx:latest
volumes:
- ./etc:/etc/nginx
ports:
- 8088:80
extra_hosts:
- localhost:127.0.0.1
nginx.conf:
user nginx;
worker_processes 1;
events {
}
http {
server {
listen 80;
server_name localhost;
location /auth {
proxy_pass http://localhost:8082/auth;
}
location /graphql {
proxy_pass http://localhost:8080/graphql;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_http_version 1.1;
}
location ^~ / {
proxy_pass http://localhost:8082/auth;
}
location /sso/login {
proxy_pass http://localhost:8082/auth;
}
}
}
PS: all referred paths are docker-containers e.g. /auth is a keycloak authentification server
I solved this problem myself. If you open the docker settings (right-click on docker icon) then you have the following network settings.
Per default the DNS server is set to automatic -> change this to fixed 8.8.8.8
Then you can access your containers with 10.0.75.2 instead localhost.
Last but not least add this address as extra_host to your docker-compose file and fire it up.
version: '3'
volumes:
etc:
driver: local
services:
nginx:
container_name: nginx
image: nginx:latest
volumes:
- ./etc:/etc/nginx
ports:
- 8088:80
extra_hosts:
- localhost:10.0.75.2
Looking at the documentation, with docker for mac we can use host.docker.internal to resolve the internal IP used by the host
location /api {
proxy_pass http://host.docker.internal:8080;
}

How to dockerize nginx and another server in separate docker-compose files?

I have a reactjs app which I want to server through nginx. The app makes API requests a node.js Express server. I decided to dockerize nginx together with static website files (from reactjs app) in one docker-compose file. I created another docker-compose file for Express server. The containers are currently on my laptop and are using localhost. I haven't been able to get them to work for a long time and there's unfortunately not much information about this on the internet. (the website and Express server work when not inside Docker).
First of all I created a new docker network:
docker network create --driver bridge appstore-net
This is my docker-compose file for website and nginx:
version: '3.5'
services:
appstore-front:
container_name: appstore-front-production
build:
context: .
dockerfile: Dockerfile-prod
ports:
- '80:80'
networks:
- appstore-net
external_links:
- appstore-bl-server-production
networks:
appstore-net:
external: true
This is my docker-compose file for Express server:
version: '3'
services:
appstore-bl-server:
container_name: appstore-bl-server-production
build:
dockerfile: Dockerfile-prod
context: .
volumes:
- ".:/usr/src/app"
ports:
- "3000:3000"
networks:
- appstore-net
networks:
appstore-net:
external: true
This is my nginx configuration:
server {
listen 80;
listen [::]:80;
#Docker DNS
resolver 127.0.0.11;
server_name localhost;
access_log /var/log/nginx/appstore.access.log;
error_log /var/log/nginx/appstore.error.log;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
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;
#hack to prevent nginx to resolve container's host on start up
set $docker_host "appstore-bl-server";
proxy_pass http://$docker_host:3000;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
As you can see I'm using docker DNS resolver as well as creating "passing proxy" to URL consisting of Express container name.
How can I make it work?
EDIT: I found 2 issues that could've been the reason:
1) external_links needs to refer to container name, not container service.
2) docker_host variable in nginx needs to refer to service name, not container name.
With these corrections the setup works (corrected the values in the OP as well).

How to configure nginx with multiple docker-compose.yml files?

If I want to setup nginx with my docker containers, one option is to setup the nginx instance in my docker-compose.yml, and link the nginx container to all application containers.
The drawback of this approach, however, is that the docker-compose.yml becomes server-level, since only one nginx container can expose ports 80/443 to the internet.
I'm interested in being able to define several docker-compose.yml files on the same server, but still easily expose the public-facing containers in each compose file via a single server-specific nginx container.
I feel this should be pretty easy, but I haven't been able to find a good resource or example for this.
First, you need to create a network for nginx and the proxied containers:
docker network create nginx_network
Next, configure the nginx container in a compose file like this:
services:
nginx:
image: your_nginx_image
ports:
- "80:80"
- "443:443"
networks:
- nginx_network
networks:
nginx_network:
external: true
After that you can run proxied containers:
services:
webapp1:
image: ...
container_name: mywebapp1
networks:
- nginx_network # proxy and app must be in same network
- webapp1_db_network # you can use additional networks for some stuff
database:
image: ...
networks:
- webapp1_db_network
networks:
nginx_network:
external: true
webapp1_db_network: ~ # this network won't be accessible from outside
Also, to make this work you need to configure your nginx properly:
server {
listen 80;
server_name your_app.example.com;
# Docker DNS
resolver 127.0.0.11;
location / {
# hack to prevent nginx to resolve container's host on start up
set $docker_host "mywebapp1";
proxy_pass http://$docker_host:8080;
}
}
You need to tell nginx to use Docker's DNS, so it will be able to access containers by their names.
But note that if you run the nginx container before the others, then nginx will try to resolve another container's host and fail, because the other containers are not running yet. You can use a hack with placing the host into a variable. With this hack, nginx won't try to resolve host until receiving a request.
With this combination you can have nginx always up, while starting and stopping proxied applications independently.
Update:
If you want a more dynamic solution, you can modify the nginx config as follows:
server {
listen 80;
resolver 127.0.0.11;
# define server_name with regexp which will read subdomain into variable
server_name ~^(?<webapp>.+)\.example\.com;
location / {
# use variable from regexp to pass request to desired container
proxy_pass http://$webapp:8080;
}
}
With this configuration, a request to webapp1.example.com will be passed to container "webapp1", webapp2.example.com to "webapp2" etc. You only need to add DNS records and run app containers with right name.
The accepted answer is great, but since I am in the trenches with this right now I'm going to expand upon it with my debugging steps in hopes it helps someone (myself in the future?)
Docker-compose projects often use nginx as reverse-proxy to route http traffic to the other docker services. nginx was a service in my projectfolder/docker-compose.yml which was connected to two docker networks.
One was the default network created when I used docker-compose up on projectfolder/docker-compose.yml (It is named projectfolder_default and services connect to it by default UNLESS you have a networks property for your service with another network, then make sure you add - default to the list). When I ran docker network ls I saw projectfolder_default in the list and when I ran docker network inspect projectfolder_default I saw the nginx container, so everything was good.
The other was a network called my_custom_network that I set up myself. I had a startup script that created it if it did not exist using https://stackoverflow.com/a/53052379/13815107 I needed it in order to talk to the web service in otherproject/docker-compose.yml. I had correctly added my_custom_network to:
nginx service's networks list of projectfolder/docker-compose.yml
bottom of projectfolder/docker-compose.yml
web service's networks in otherproject/docker-compose.yml
bottom of otherproject/docker-compose.yml
The network showed up and had the right containers using docker network ls and docker network inspect my_custom_network
However, I assumed that proxy_pass to http://web in my server.conf would map to the docker service web.projectfolder_default. I was mistaken. To test this I opened shell on the nginx container (docker exec -it nginx sh). When I used ping web (may need to apt-get update, apt-get install iputils-ping) it succeeded, but it printed a url with my_custom_network which is how I figured out the mistake.
Update: I tried using http://web.my_custom_network in server.conf.template and it routed great, but my webapp (Django-based) choked on underscores in the url. I renamed web to web2 in otherproject/docker-compose.yml and then used something like docker stop otherproject_web and docker rm otherproject_web to get rid of the bad one.
projectfolder/docker-compose.yml
services:
# http://web did NOT map to this service!! Use http://web.main_default or change the names
web:
...
nginx:
...
links:
- web
networks:
- default
- my_custom_network
...
networks:
- my_custom_network
external: true
otherproject/docker-compose.yml
services:
# http://web connected to this service instead. You could use http://web.my_custom_network to call it out instead
web:
...
networks:
- default
- my_custom_network
...
networks:
- my_custom_network
external: true
projectfolder/.../nginx/server.conf.template (next to Dockerfile)
...
server {
...
location /auth {
internal;
# This routed to wrong 'web'
proxy_pass http://web:9001;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location / {
alias /data/dist/;
}
location /robots.txt {
alias /robots.txt;
}
# Project Folder backend
location ~ ^/(api|login|logout)/ {
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_connect_timeout 300s;
proxy_read_timeout 300s;
# This routed to wrong 'web'
proxy_pass http://web:9001;
}
# Other project UI
location /other-project {
alias /data/other-project-client/dist/;
}
# Other project Django server
location ~ ^/other-project/(rest)/ {
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_connect_timeout 300s;
proxy_read_timeout 300s;
# nginx will route, but won't work some frameworks like django (it doesn't like underscores)
# I would rename this web2 in yml and use http://web2
proxy_pass http://web.my_custom_network:8000;
}
}

Subdomains, Nginx-proxy and Docker-compose

I'm looking for a way to configure Nginx to access hosted services through a subdomain of my server. Those services and Nginx are instantiated with Docker-compose.
In short, when typing jenkins.192.168.1.2, I should access to Jenkins hosted on 192.168.1.2 redirected with Nginx proxy.
A quick look of what I currently have. It doesn't work without a top domain name, so it works fine on play-with-docker.com, but not locally with for example 192.168.1.2.
server {
server_name jenkins.REVERSE_PROXY_DOMAIN_NAME;
location / {
proxy_pass http://jenkins:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
To have a look of what I want: https://github.com/Ivaprag/devtools-compose
My overall goal is to access remote docker containers without modifying clients' DNS service.
Unfortunately nginx doesn't support sub-domains on IP addresses like that.
You would either have to modify the clients hosts file (which you said you didn't want to do)...
Or you can just set your nginx to redirect like so:
location /jenkins {
proxy_pass http://jenkins:8080;
...
}
location /other-container {
proxy_pass http://other-container:8080;
}
which would allow you to access jenkins at 192.168.1.2/jenkins
Or you can try and serve your different containers through different ports. E.g:
server {
listen 8081;
location / {
proxy_pass http://jenkins:8080;
...
}
}
server {
listen 8082;
location / {
proxy_pass http://other-container:8080;
...
}
}
And then access jenkins from 192.168.1.2:8081/
If you are already using docker-compose I recommend using the jwilder nginx-proxy container.
https://github.com/jwilder/nginx-proxy
This allows you to add unlimited number of web service containers to the backend of the defined nginx proxy, for example:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- "/etc/nginx/vhost.d"
- "/usr/share/nginx/html"
- "/var/run/docker.sock:/tmp/docker.sock:ro"
- "nginx_certs:/etc/nginx/certs:rw"
nginx:
build:
context: ./docker/nginx/
dockerfile: Dockerfile
volumes_from:
- data
environment:
VIRTUAL_HOST: www.host1.com
nginx_2:
build:
context: ./docker/nginx_2/
dockerfile: Dockerfile
volumes_from:
- data
environment:
VIRTUAL_HOST: www.host2.com
apache_1:
build:
context: ./docker/apache_1/
dockerfile: Dockerfile
volumes_from:
- data
environment:
VIRTUAL_HOST: www.host3.com
The nginx-proxy mount the host docker sock file in order to get information about the other containers running, if any of them have the env variable VIRTUAL_HOST set then it will add it to its configuration.
I was trying to configure subdomains in nginx (host), for two virtualhosts in one LXC container.
The way it worked for me:
For apache (in the container), I created two virtual hosts: one in port 80 and the other one in port 90.
For enabling port 90 in apache2 (container), it was necessary to add the line "Listen 90" below "Listen 80" in /etc/apache2/ports.conf
For NGINX (host machine), configured two DOMAINS, both in port 80 creating independent .conf files in /etc/nginx/sites-available. Created symbolic link for each file to /etc/nginx/sites-enabled.
In the first NGINX myfirstdomain.conf file, redirect to http://my.contai.ner.ip:80.
In the second NGINX myseconddomain.conf file, redirect to http://my.contai.ner.ip:90
That was it for me !

Resources