How to NGINX Reverse Proxy with Docker Compose
Take a look at how to use NGINX reverse proxy with Docker Compose to expose multiple services without changing ports.
Join the DZone community and get the full member experience.
Join For FreeWhile developing a web application, a common method of calling the application from a local machine is through http://localhost:x
ports, which essentially means that we are required to expose several ports to access different modules of the application. In the article below, we will go through the method of using Reverse Proxy to call an application and the benefits of using it.
Why Do We Need Reverse Proxy?
The most obvious reason for using Reverse Proxy is to avoid changing ports every time you try to access different modules of the application through the same URL. Through Reverse Proxy we can reach frontend, backend, or other services without changing port through a single domain. Another important reason for using Reverse Proxy is to mask services behind a proxy and avoid dealing with CORS issues.
xxxxxxxxxx
# Without Reverse Proxy
# Domain Name: http://mydomain.com
# Mysql wordpress: http://mydomain.com:10088
# Angular app: http://mydomain.com:7787
# Backend: https://mydomain:9876
xxxxxxxxxx
# With Reverse Proxy
# Domain Name: http://mydomain.com
# Mysql wordpress: http://mydomain.com/db
# Angular app: http://mydomain.com/ang
# Backend: https://mydomain/wp
Prerequisites
- Docker Compose on Linux
- iptables should be enabled. Note that Docker uses iptables to access incoming connections.
Building docker-compose.yml
For reference for this article, let us create a Wordpress-MySQL server with NGINX in one service. Start by creating the Docker container, along with defining ports, base image, container name, and service names.
xxxxxxxxxx
version: '2'
services:
whilefly_wp:
container_name: production_wp
image: nginx:latest
volumes:
- "/home/xx/Desktop/cust/xx/html:/var/www/html"
- "/home/xx/Desktop/cust/xx/Docker/logs:/logs"
- "/home/xx/Desktop/cust/xx/Docker/database:/docker-entrypoint-initdb.d"
- "xx_db_data:/var/lib/mysql"
user: root
restart: always
ports:
- "8081:80"
environment:
MYSQL_ROOT_PASSWORD: 'xxxxx'
MYSQL_DATABASE: 'production_db'
MYSQL_USER: 'production_admin'
MYSQL_PASSWORD: 'xxxxxx'
nginx:
image: nginx:latest
container_name: production_nginx
volumes:
- /home/xx/Desktop/cust/xx/Docker/nginx/reverse_proxy.conf:/etc/nginx/conf.d/default.conf
- /home/xx/Desktop/cust/xx/Docker/nginx/cert/star_xx_com.pem:/etc/nginx/cert/star_xx_com.pem
- /home/xx/Desktop/cust/xx/Docker/nginx/cert/star_xx_com.key:/etc/nginx/cert/star_xx_com.key
- /home/xx/Desktop/cust/xx/Docker/nginx/cert/star_xx_com.crt:/etc/nginx/cert/star_xx_com.crt
ports:
- 8080:8080
- 443:443
links:
- whilefly_wp
volumes:
xx_db_data:
Let's explain the setup in steps.
services
command defines the service you will build in Docker.
xxxxxxxxxx
version: '2'
services:
running_wp:
nginx:
2. As shown below, the running_wp
service uses nginx:latest
as the base image. You can also use your own custom image for MySQL and Wordpress. Next is to assign a container name, for instance currently production_wp
is used below. Copy the local files under volume section to the given directory while building this docker image. It is important to assign Ports which are to be used for accessibility within or outside of the application. As shown below, we have mapped 8081
host to 80
port for container.
xxxxxxxxxx
version: '2'
services:
running_wp:
container_name: production_wp
image: boraozkan/nginx:latest
volumes:
- "/home/xx/Desktop/cust/xx/html:/var/www/html"
- "/home/xx/Desktop/cust/xx/Docker/logs:/logs"
- "/home/xx/Desktop/cust/xx/Docker/database:/docker-entrypoint-initdb.d"
- "xx_db_data:/var/lib/mysql"
user: root
restart: always
ports:
- "8081:80"
environment:
MYSQL_ROOT_PASSWORD: 'xxxxx'
MYSQL_DATABASE: 'production_db'
MYSQL_USER: 'production_admin'
MYSQL_PASSWORD: 'xxxxxx'
3. Assign necessary access credentials with the environment
command.
xxxxxxxxxx
environment:
MYSQL_ROOT_PASSWORD: 'xxxxx'
MYSQL_DATABASE: 'production_db'
MYSQL_USER: 'production_admin'
MYSQL_PASSWORD: 'xxxxxx'
4. Assign the base image and container name. The base image will create NGINX on the first run. You can also add encrypted SSL files through https://letsencrypt.org/; it is free.
xxxxxxxxxx
version: '2'
nginx:
image: nginx:latest
container_name: production_nginx
volumes:
- /home/xx/Desktop/cust/xx/Docker/nginx/reverse_proxy.conf:/etc/nginx/conf.d/default.conf
- /home/xx/Desktop/cust/xx/Docker/nginx/cert/star_xx_com.pem:/etc/nginx/cert/star_xx_com.pem
- /home/xx/Desktop/cust/xx/Docker/nginx/cert/star_xx_com.key:/etc/nginx/cert/star_xx_com.key
- /home/xx/Desktop/cust/xx/Docker/nginx/cert/star_xx_com.crt:/etc/nginx/cert/star_xx_com.crt
ports:
- 8080:8080
- 443:443
links:
- running_wp
volumes:
xx_db_data:
5. Add NGINX configuration file under conf.d
path - this is the default directory for virtual host descriptions.
xxxxxxxxxx
/home/xx/Desktop/cust/xx/Docker/nginx/reverse_proxy.conf:/etc/nginx/conf.d/default.conf
6. As shown below there are two parts of this config file. First one shows the server side defining which port will be listened by the NGINX Container (8080
and 443
). Secondly, the forward traffic would be directed to port 8081
— this is the port of production container for reaching.
xxxxxxxxxx
# re-route everything to production_wp
server {
listen 8080;
server_name production_wp;
error_page 497 http://$host:80$request_uri;
ssl on;
ssl_certificate /etc/nginx/cert/star_xx_com.crt;
ssl_certificate_key /etc/nginx/cert/star_xx_com.key;
location /wp {
proxy_pass http://localhost:8081/wp;
rewrite /wp(.*) /origin-d$1
proxy_pass http://localhost:8081;
proxy_redirect / /wp;
sub_filter 'href="/' 'href="/wp'
}
}
# wordpress container via production_wp
server {
listen 443;
server_name production_wp;
error_page 497 http://$host:80$request_uri;
7. Enable secure HTTP with the ssl on
command.
xxxxxxxxxx
ssl on;
ssl_certificate /etc/nginx/cert/star_xx_com.crt;
ssl_certificate_key /etc/nginx/cert/star_xx_com.key;
8. To prefix headers for proxy connections, we can use the proxy_set_header
directive which helps in redefining or appending fields to the request header passed through the proxied server.
xxxxxxxxxx
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
9. For Reverse Proxy as mentioned in the beginning, we will give a path for the Wordpress container in NGINX configuration. Let us say it is using http://localhost/wp to http://localhost:8081, which is an asymmetric path.
xxxxxxxxxx
location /wp {proxy_pass http://localhost:8081/wp;rewrite /wp(.*) /origin-d$1proxy_pass http://localhost:8081;proxy_redirect / /wp;sub_filter 'href="/' 'href="/wp' }
10. Now we can start our containers with the command below. Remember to be in same path with docker-compose.yaml
while starting containers.
xxxxxxxxxx
docker-compose up -d
11. The docker-compose up
command is a shorthand form of docker-compose build
and docker-compose run
.
xxxxxxxxxx
#docker-compose up -d
Pulling nginx (nginx:latest)...
2.1: Pulling from nginx:latest
b8f262c62ec6: Pulling fs layer
a98660e7def6: Pulling fs layer
4d75689ceb37: Pulling fs layer
639eb0368afa: Waiting
99e337926e9c: Waiting
431d44b3ce98: Waiting
beb665ea0e0e: Pulling fs layer
c98a22d85c62: Waiting
bf70d116f1d7: Waiting
97f2d71621e0: Waiting
ea02a46a87c8: Waiting
78fff17c3a50: Waiting
12. When complete, we should have two containers deployed, one of which we cannot access directly:
xxxxxxxxxx
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c327bb01a85 nginx:latest "nginx -g 'daemon of…" 8 minutes ago Up 8 minutes 0.0.0.0:8080->8080/tcp, 0.0.0.0:443->443/tcp production_nginx
3823ce1f25d8 boraozkan/nginx:latest "/usr/bin/supervisord" 8 minutes ago Up 8 minutes 3306/tcp, 0.0.0.0:8081->80/tcp production_wp
13. We can check our applications (one with NGINX and the other one with Apache).
Navigate to http://localhost:8080, and this will hit NGINX Reverse Proxy which will in turn will load the NGINX web application:
14. Also check with navigating to http://localhost:8081 or http://localhost/wp, through the Nginx Reverse Proxy asymmetric path and the Apache web application will be loaded:
Troubleshooting
Once the services are up, try to connect your web application to the localhost link. If it is not answered, check your iptables table for correctness.
By default Docker containers can make connections to the outside world, but the outside world cannot connect to containers. Each outgoing connection will appear to originate from one of the host machine’s own IP addresses thanks to an iptables
masquerading rule on the host machine that the Docker server creates when it starts:
xxxxxxxxxx
$ sudo iptables -t nat -L -n
...
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
Conclusion
This article is aimed to show how you can use Nginx with docker-compose
easily. It also shows setting of Reverse Proxy configuration on containers. Using this will give you additional flexibility during deployment of a web application.
I hope you liked this post! More to come soon.
Until then, here are some more Kubernetes and Docker best practices for managing and deploying containers.
Published at DZone with permission of Sudip Sengupta. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments