Nginx with docker not proxy my ruby app correctly
I have two Docker containers (built from custom configured nginx and Ruby images) and when I start the containers and make requests, they seem to be proxy requests to the correct locations (but one of the services that are proxied doesn't handle the request perfectly correctly).
i.e. when I try to proxy to my Ruby container, I either get the error "Sinatra does not recognize this error" OR "301 redirects"?
Note: the code can also be found here https://github.com/Integralist/Docker-Examples/tree/master/Nginx
Below is the Dockerfile for nginx:
FROM ubuntu
# install nginx
RUN apt-get update && apt-get install -y nginx
RUN rm -rf /etc/nginx/sites-enabled/default
# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
Below is the Dockerfile for the Ruby application:
FROM ruby:2.1-onbuild
CMD ["ruby", "app.rb"]
Note:
Someone asked how myapp.rb
and other dependencies are loaded (since minedocker run
didn't mount them and the Dockerfile doesn't seem to add them). If you look at the ruby "onbuild" tagged version of the image, you can seeCOPY
all these files for us https://github.com/docker-library/ruby/2.0/onbuild/Dockerfile
A Ruby application looks like this:
require "sinatra"
set :bind, "0.0.0.0"
get "/" do
"Hello World"
end
And the file nginx.conf
looks like this:
user nobody nogroup;
worker_processes auto; # auto-detect number of logical CPU cores
events {
worker_connections 512; # set the max number of simultaneous connections (per worker process)
}
http {
upstream app {
server app:4567; # app is automatically defined inside /etc/hosts by Docker
}
server {
listen *:80; # Listen for incoming connections from any interface on port 80
server_name ""; # Don't worry if "Host" HTTP Header is empty or not set
root /usr/share/nginx/html; # serve static files from here
location /app/ { # catch any requests that start with /app/
proxy_pass http://app; # proxy requests onto our app server (i.e. a different container)
}
}
}
I start a Ruby container like this:
docker run --name ruby-app -p 4567:4567 -d my-ruby-app
I start nginx container like this:
docker run --name nginx-container \
-v $(pwd)/html:/usr/share/nginx/html:ro \
-v $(pwd)/docker-nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
--link ruby-app:app \
-P -d my-nginx
If I run it curl http://$(boot2docker ip):32785/app/
, I get back to the error "Sinatra is not know this ditty"; and if i run curl http://$(boot2docker ip):32785/app
will i return the message 301 Moved Permanently
?
I'm sure I'm missing something very obvious (maybe how Sinatra is set up? How do I need to configure the route /app
? Or should I use the directive alias
in nginx.conf
)
Any help was appreciated.
source to share
So it seems that the answer is subtlety about how nginx layout blocks work ...
If you don't put a slash / at the end of the ascending name, you will see that nginx passes the request as / app /, not just /
By placing / after the upstream name, it acts more like a directive alias
.
So this works ...
location /app/ {
proxy_pass http://app/; # this is what I want
}
But that won't work ...
location /app/ {
proxy_pass http://app; # this ISN'T what I want
}
source to share
So, I think there are only a few fundamental problems with what you are configuring and what you are testing.
First, you say to nginx: "When you get / app /, proxy into a container with sinatra":
location /app/ { # catch any requests that start with /app/
proxy_pass http://app; # proxy requests onto our app server (i.e. a different container)
}
So this will be passed to Sinatra with the request URI being /app/
.
As you, your sinatra app didn't figure out this route, why are you getting "Sinatra doesn't know this melody".
The reason you get a 301 when you try to use it without a trailing slash is because sinatra will automatically redirect anything without a trailing slash.
As Dirk says, the reason you get "Hello world" on hit is:
curl http://$(boot2docker ip):4567/
This is because this route is defined in your sinatra itineraries if you click:
curl http://$(boot2docker ip):4567
then you should get 301 redirects to /.
To fix the problem, you need to:
- Change your sinatra itinerary to "/ app".
- Change your nginx location to / instead of / app /.
Option 2 will be the most flexible as any request to the nginx container will map directly to the sinatra container, as you added another route that you will need to update for the nginx config to add this proxying route to the sinatra container.
source to share