I have an application running on http://localhost:8000 using a docker image that was made by me.
Now I want to use NGINX as reverse proxy listening on port 80, to redirect to the localhost:8000.
Here my nginx.conf file
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
proxy_pass http://localhost:8000;
}
}
}
Here my Dockerfile:
FROM nginx
RUN rm /etc/nginx/conf.d/default.conf
COPY index.html /usr/share/nginx/html
COPY nginx.conf /etc/nginx
CMD nginx
To build the image I use the command
docker build --no-cache -t mynginx .
To run it, I use
docker run -p 80:80 -d mynginx
Now, if I test from my local computer with curl localhost:8000 everything works, but if I try with curl localhost, I get a Bad Gateway error.
Moreover, I tried to serve static content and it works, but with the reverse proxy settings it does not work.
The reason you are getting a bad gateway is that from within your nginx container, localhost resolves to the container itself and not to the host system.
If you want to access your application container from your reverse proxy container, you need to put both containers into a network.
docker network create my-network
docker network connect --alias application my-network <application container id/name>
docker network connect --alias reverse-proxy my-network <reverse proxy container id/name>
--network can be an arbitrary name and --alias should be the hostnames you want to resolve to your containers.
In fact you do not need to provide an alias if you already assigned a (host)name (docker run --name ...) to your containers.
You can then change your proxy_pass directive to the (alias) name of your application container:
proxy_pass http://application:8000;
Also refer to the documentation: Container networking
curl localhost:8000 works => your application is running
curl localhost returns bad gateway means proxy_pass http://localhost:8000; does not work. This is because "localhost" is relative to its caller, in this case the nginx container, which does not have anything running on port 8000.
You need to point it to your application using proxy_pass http://your_app:8000; where "your_app" is the service name (docker-compose) or the container name or its network alias. Make sure they are in the same network.
Related
I am new to using docker and nginx together so i apologize in advance for my simple question which I am unable to get an answer to despite going through many resources on youtube.
I created an ubuntu server and ran the following command
sudo apt install nginx
Now I have a very simple flask application docker image(publicly available on docker hub and not developed by me) and I want to configure my nginx to work as a reverse proxy to my container running the said image.
My code for the reverse proxy in nginx configuration is as follows:
server{
listen 80;
location / {
proxy_pass "http://192.168.0.20:8000"
}
}
192.168.0.20 is my server ip and 8000 is the host port over which i am forwarding my docker container like
docker container run -p 8000:5000 <image>
But when I run http://192.168.0.20/ it opens default nginx index.html whereas I want it to forward to my app container to serve that static file because when i run http://192.168.0.20:8000/ it gives me desired output.
This might sound like a dumb question but i have been struggling to get a hang of nginx.
Thanks in advance for the help !!!
To reach the host from inside the container, you can't use 192.168.0.20, since that address isn't known inside the container. Depending on your host OS, you can use 172.17.0.1 (Linux) or host.docker.internal (Windows). If your OS is Linux, you should change your config to
server {
listen 80;
location / {
proxy_pass "http://172.17.0.1:8000/"
}
}
Rather than installing nginx yourself, you can use the nginx images that are available on docker hub. To get your config file into it, you copy it to /etc/nginx/conf.d/default.conf. Create a Dockerfile containing
FROM nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf
Build and run it with
docker build -t mynginx .
docker run -d -p 80:80 mynginx
Now you should be able to go to http://192.168.0.20/ and get a response from the Flask app.
I have my docker app running in the aws EC2 instance, and I am currently trying to map the app to the external IP address using Nginx. Here is a snap shot of the containers that I have running:
My test-app is a fairly simple app that displays a static html website. And I deployed it using the following command:
docker run -d --name=test-app test-app
The nginx-proxy has the following proxy.conf
server {
listen 80;
location / {
proxy_pass http://test-app;
}
}
Here is the Dockerfile for the nginx proxy:
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/*
COPY proxy.conf /etc/nginx/conf.d/
nginx-proxy is run using the following command:
docker run -d -p 80:80 --name=nginx-proxy nginx-proxy
However, the nginx container never runs, and here the error log I get
2020/03/27 15:55:19 [emerg] 1#1: host not found in upstream "test-app" in /etc/nginx/conf.d/proxy.conf:5
nginx: [emerg] host not found in upstream "test-app" in /etc/nginx/conf.d/proxy.conf:5
While your two containers are both running and you have properly exposed the ports required for the NGINX container. You have not exposed any ports for the test-app container. The NGINX container has no way of talking to it. Exposing ports directly with docker run would likely defeat the point of using a reverse proxy in your situation. So instead, what you should do in this situation is create a Network for both of your Docker containers and then add them to it. Then they will be able to communicate with one-another over a bridge. For example:
docker network create example
docker run -d --network=example --name=test-app test-app
docker run -d -p 80:80 --network=example --name=nginx-proxy nginx-proxy
Now that you have both of your pods on the same network, Docker will enable DNS-based service discovery between them by container name and you will be able to resolve them from each other. You can test connectivity like so: docker exec -it nginx-proxy ping test-app. Well, that is provided ping is installed in that Docker container.
I'm setting up some containers in my Ubuntu server. I've created two simple images this way:
Dockerfile: static-site
FROM nginx:alpine
COPY ./site/ /usr/share/nginx/html
Dockerfile: static-content
FROM nginx:alpine
COPY ./assets/ /usr/share/nginx/html
The Dockerfiles are in different location
Until here no problem at all. I've installed nginx-proxy and used the VIRTUAL_HOST to run them:
docker run -d -p 80 -e VIRTUAL_HOST=mysite.com static-site
docker run -d -p 80 -e VIRTUAL_HOST=static.mysite.com static-content
The result is that whatever address I put in the browser it always redirect me to mysite.com.
What am I doing wrong?
Also, I have a DNS record like this:
*.mysite.com 86400 A 0 xxx.xxx.xx.xx (the ip of mysite.com)
Could it be the problem?
you cant bind two containers to the same port ("80"). Most probably that the second container is dead (you can verify this by running docker ps). or it is running with automatically assigned ports
docker ps --format " {{.Image}} ==> {{.Ports}}"
nginx ==> 0.0.0.0:32769->80/tcp
nginx ==> 0.0.0.0:32768->80/tcp
To fix this issue you either use different ports for the containers and configure your DNS to be linked to a load balancer (so you can configure the destination port) or you switch to use single Nginx with multiple server definitions.
Dockerfile:
FROM nginx:alpine
COPY ./site/ /usr/share/nginx/site_html
COPY ./assets/ /usr/share/nginx/assets_html
COPY ./site.conf /etc/nginx/conf.d/default.conf
Nginx Config:
server {
listen 80;
server_name mysite.com;
root /usr/share/nginx/site_html;
}
server {
listen 80;
server_name static.mysite.com;
root /usr/share/nginx/static_html;
}
I am fairly new to this, I don't know if I am heading in the right direction or not. I have a custom nginx.conf that works fine, I am now trying to build a docker image with it so that I can run it as a container in kuberentes.
Here is my nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
stream {
#define the various upstreams
upstream app_1 {
server 192.168.122.206:5678;
}
upstream app_2 {
server 192.168.122.206:9000;
}
#map source port to upstream
map $remote_port $backend_svr {
1234 "app_1";
1235 "app_2";
}
#all udp traffic received on 8000, depending on source it will be redirected
server {
listen 8000 udp;
proxy_pass $backend_svr;
}
}
On my virtual machine I have /home/asvilla/docker-files/nginx-udp/Dockerfile which contains
FROM nginx
RUN chmod +w /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/nginx.conf
RUN cat /etc/nginx/nginx.conf
I build it using
docker build -t "custom_nginx:dockerfile" .
The nginx container should redirect udp traffic incoming on port 8000 to either port 5678 or port 9000 depending on the source of the udp packet.
I run with docker run 'image-id' but it doesn't function as expected. Running docker ps shows "PORTS 80/tcp "
and "COMMAND nginx-g daemon of ..."
Any pointers on what these mean. nginx by default binds tcp to port 80 but I have changed the nginx.conf, the cat command I run shows that the file is updated.
I am assuming that I have to expose the ports in the nginx.conf somehow. Any help much appreciated.
If your end goal is to run this in Kubernetes, your easiest path will be to put this config file into a ConfigMap and just configure your Deployment to run the standard nginx image. (In plain Docker, you can use docker run -v to inject the config file into the container at runtime to similar effect.)
It doesn't really matter what port nginx listens on inside the container. If the stock nginx container expects to listen on the standard HTTP port 80 (and it looks like its Dockerfile has an EXPOSE 80 directive) then you can embrace that and listen 80 in your nginx config (over TCP, not UDP). Then in your Kubernetes deployment you can specify that as a container port, and if you want to map it to something else, you can do that in the Service that wraps this. (In plain Docker, if you want host port 8000 to avoid conflicting with other things, docker run -p8000:80.)
In terms of best practices I'd discourage directly writing IP addresses into config files. If it's a persistent server outside your cluster, you can set up a DNS server in your network to resolve its hostname, or get a cloud service like Amazon's Route 53 to do it for you. If it's in Kubernetes, use the service's DNS name, backend.default.svc.cluster.local. Even if you really have only an IP address, creating an ExternalName service will help you if the service ever moves.
Assuming you have the config file in a ConfigMap, your Deployment would look very much like the sample Deployment in the Kubernetes documentation (it even runs an nginx:1.7.9 container publishing port 80).
You must publish the port at runtime like this:
docker run -p 8000:8000 image-id.
In your Dockerfile you need to add EXPOSE
FROM nginx
RUN chmod +w /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/nginx.conf
RUN cat /etc/nginx/nginx.conf
EXPOSE 8000
Then when you run it you execute: docker run -p 8000:8000 custom_nginx:dockerfile
I want to run Nginx in a docker container, it listens to port 80 and I want it to proxy_pass to port 8080 when the url starts with word api, and I have some web app listening port 8080. This has been working for me without docker, but with docker, I couldn't get it to work.
My nginx.conf is like:
location /{
# serve static page
}
location /api {
proxy_pass http://0.0.0.0:8080;
}
I run my nginx container with docker run -d -p 80:80 -p 8080: 8080 nginx
My problem is now I can no longer run my web app because it can't listen to port 8080 since that container is already listening to it.
docker run -d --net host nginx
Try it!
Nginx container will share the host network with IP and all ports
First, you need to create a network to place both containers:
docker network create nginx_network
Then, you should specify Docker's DNS server in nginx configuration:
location /api {
#Docker DNS
resolver 127.0.0.11;
#my_api - name of container with your API, see below
proxy_pass http://my_api:8080;
}
Finally, run your containers:
docker run --network="nginx_network" -d --name my_api your_api_container
docker run --network="nginx_network" -d -p 80:80 nginx
Note:
--name parameter's value for API's container must match domain name in Nginx config
it's enough to specify only 80 port for your nginx container
first run your API's container and then Nginx's container (see below)
both containers must be in the same network
This should work.
In case if you run nginx container first, then nginx will try to resolve domain name my_api on startup and fail, because container with this name doesn't exist yet. In this case there is a following workaround (not sure if it is good solution). Modify nginx config:
location /api {
#Docker DNS
resolver 127.0.0.11;
#hack to prevent nginx to resolve domain on start up
set $docker_host "my_api";
#my_api - name of container with your API, see below
proxy_pass http://$docker_host:8080;
}
You can (or rather should) only have one process per docker container which means you will have nginx running in one container and your application on another. The old way is to create links between containers like this:
$ docker run --name my-app -d myself/myapp
$ docker run --name proxy --link my-app:my-app -d nginx
This will add a line in /etc/hosts in the nginx container so it will be able to call the other container by it's name.
And then in nginx.conf file:
location /api {
proxy_pass http://my-app:8080;
}
However according to official Docker docs this method is deprecated and you should only use it's "absolutely needed". Instead you should use the docker networking. Theoretically if both containers are in the same network and the local DNS server is working (embedded in docker) they should be able to see each other without the --link parameter. Unfortunately it didn't work for me for some reason. Nginx didn't have the correct DNS configured in /etc/resolv.conf, but read the article and play around it, I'm sure it will work.