Proxy is very useful in a development environment when you need to expose several ports to access different modules of the application. You can use Reverse Proxy to access different modules of the application through the same URL. It will also help you to access the backend, frontend, and other services using a single domain name.
In this post, we will set up two websites inside two Docker containers, then set up an Nginx reverse proxy to access both websites.
Prerequisites
- A fresh Ubuntu 20.04 server on the Atlantic.Net Cloud Platform
- A root password configured on your server
For the purposes of this tutorial, we will use the following directory structure:
├── proxy │ ├── backend-not-found.html │ ├── default.conf │ ├── docker-compose.yml │ ├── Dockerfile │ ├── includes │ │ ├── proxy.conf │ │ └── ssl.conf │ └── ssl │ ├── website1.crt │ ├── website1.key │ ├── website2.crt │ └── website2.key ├── website1 │ ├── docker-compose.yml │ └── index.html └── website2 ├── docker-compose.yml └── index.html
Step 1 – Set Up a Host File
First, you will need to edit the /etc/hosts file and bind both website domains with the IP address:
nano /etc/hosts
Add the following lines:
your-server-ip website1.test your-server-ip website2.test
Save and close the file when you are finished.
Step 2 – Install Docker and Docker Compose
First, you will need to install some required dependencies on your server. You can install them with the following command:
apt-get install git apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y
After installing all of them, add the Docker repository with the following command:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
Once the repository is added, install Docker and Docker Compose with the following command:
apt-get install docker-ce docker-compose -y
After installing both packages, you can proceed to the next step.
Step 3 – Create a First Website Container
First, create a directory to host your first website container.
mkdir website1
Next, change the directory to website1 and create a docker-compose.yml file:
cd website1 nano docker-compose.yml
Add the following lines:
version: '2' services: app: image: nginx volumes: - .:/usr/share/nginx/html ports: - "80"
Save and close the file when you are finished.
Next, create an Index page for your website.
nano index.html
Add the following lines:
<!DOCTYPE html> <html> <head> <title>First Website</title> </head> <body> <h1>Hello! This is my first website.</h1> </body> </html>
Save and close the file then launch your website1 container with the following command:
docker-compose build docker-compose up -d
This will download an Nginx image, launch a container, and expose it on port 80.
Creating network "website1_default" with the default driver Pulling app (nginx:)... latest: Pulling from library/nginx 69692152171a: Already exists 30afc0b18f67: Pull complete 596b1d696923: Pull complete febe5bd23e98: Pull complete 8283eee92e2f: Pull complete 351ad75a6cfa: Pull complete Digest: sha256:6d75c99af15565a301e48297fa2d121e15d80ad526f8369c526324f0f7ccb750 Status: Downloaded newer image for nginx:latest Creating website1_app_1 ... done
Step 4 – Create a Second Website Container
First, create a directory to host your second website container.
cd mkdir website2
Next, change the directory to website2 and create a docker-compose.yml file:
cd website2 nano docker-compose.yml
Add the following lines:
version: '2' services: app: image: nginx volumes: - .:/usr/share/nginx/html ports: - "80"
Save and close the file, then create an Index page for the second website.
nano index.html
Add the following lines:
<!DOCTYPE html> <html> <head> <title>Second Website</title> </head> <body> <h1>Hello! This is my second website.</h1> </body> </html>
Save and close the file, then launch your website2 container with the following command:
docker-compose build docker-compose up -d
You should see the following output:
Creating network "website2_default" with the default driver Creating website2_app_1 ... done
You can now verify both website containers with the following command:
docker ps
You should get the following output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 01e83ba3a3dd nginx "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:49154->80/tcp, :::49154->80/tcp website2_app_1 75207f53411c nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:49153->80/tcp, :::49153->80/tcp website1_app_1
Step 5 – Set up Reverse Proxy Container
First, create a new proxy directory with the following command:
cd mkdir proxy
Next, change the directory to proxy and create a Dockerfile for a new custom image:
cd proxy nano Dockerfile
Add the following lines:
FROM nginx COPY ./default.conf /etc/nginx/conf.d/default.conf COPY ./backend-not-found.html /var/www/html/backend-not-found.html COPY ./includes/ /etc/nginx/includes/ COPY ./ssl/ /etc/ssl/certs/nginx/
Save and close the file, then create a backend index file for a not found response.
nano backend-not-found.html
Add the following lines:
<html> <head><title>Proxy Backend Not Found</title></head> <body> <h2>Proxy Backend Not Found</h2> </body> </html>
Save and close the file, then create an Nginx default configuration file:
nano default.conf
Add the following lines:
server { listen 80; listen 443 ssl http2; server_name website1.test; # Path for SSL config/key/certificate ssl_certificate /etc/ssl/certs/nginx/website1.crt; ssl_certificate_key /etc/ssl/certs/nginx/website1.key; include /etc/nginx/includes/ssl.conf; location / { include /etc/nginx/includes/proxy.conf; proxy_pass http://website1_app_1; } access_log off; error_log /var/log/nginx/error.log error; } # web service2 config. server { listen 80; listen 443 ssl http2; server_name website2.test; # Path for SSL config/key/certificate ssl_certificate /etc/ssl/certs/nginx/website2.crt; ssl_certificate_key /etc/ssl/certs/nginx/website2.key; include /etc/nginx/includes/ssl.conf; location / { include /etc/nginx/includes/proxy.conf; proxy_pass http://website2_app_1; } access_log off; error_log /var/log/nginx/error.log error; } # Default server { listen 80 default_server; server_name _; root /var/www/html; charset UTF-8; error_page 404 /backend-not-found.html; location = /backend-not-found.html { allow all; } location / { return 404; } access_log off; log_not_found off; error_log /var/log/nginx/error.log error; }
Save and close the file when you are finished.
Next, create a docker-compose.yml file to launch a proxy container:
nano docker-compose.yml
Add the following lines:
version: '2' services: proxy: build: ./ networks: - website1 - website2 ports: - 80:80 - 443:443 networks: website1: external: name: website1_default website2: external: name: website2_default
Save and close the file when you are finished.
Next, you will need to generate an SSL certificate and key for both websites.
First, create an SSL directory within your proxy directory:
mkdir ssl
Next, change the directory to ssl and generate ssl certificates and keys with the following command:
cd ssl openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout website1.key -out website1.crt openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout website2.key -out website2.crt
Next, go back to your proxy directory and create an includes directory:
cd .. mkdir includes
Next, change the directory to includes and create a proxy configuration file:
cd includes nano proxy.conf
Add the following lines:
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_buffering off; proxy_request_buffering off; proxy_http_version 1.1; proxy_intercept_errors on;
Save and close the file, then create an ssl.conf file:
nano ssl.conf
Add the following lines:
ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHAECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; ssl_prefer_server_ciphers on;
Save and close the file when you are finished.
Next, go back to the proxy directory and build the proxy container with the following command:
cd .. docker-compose build
You should see the following output:
Building proxy Step 1/5 : FROM nginx ---> d1a364dc548d Step 2/5 : COPY ./default.conf /etc/nginx/conf.d/default.conf ---> d70a72d6011f Step 3/5 : COPY ./backend-not-found.html /var/www/html/backend-not-found.html ---> 1cb5af66233f Step 4/5 : COPY ./includes/ /etc/nginx/includes/ ---> 7c18f2753b1e Step 5/5 : COPY ./ssl/ /etc/ssl/certs/nginx/ ---> ebb0447a1317 Successfully built ebb0447a1317 Successfully tagged proxy_proxy:latest
Next, launch the proxy container with the following command:
docker-compose up -d
Output:
Creating proxy_proxy_1 ... done
You can now verify your all containers with the following command:
docker ps -a
You should get the following output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c413da7e85ba proxy_proxy "/docker-entrypoint.…" 16 seconds ago Up 15 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp proxy_proxy_1 01e83ba3a3dd nginx "/docker-entrypoint.…" 52 minutes ago Up 52 minutes 0.0.0.0:49154->80/tcp, :::49154->80/tcp website2_app_1 75207f53411c nginx "/docker-entrypoint.…" 53 minutes ago Up 53 minutes 0.0.0.0:49153->80/tcp, :::49153->80/tcp website1_app_1
Step 6 – Check the Nginx Reverse Proxy
At this point, both the website and proxy container are started successfully. Now, it’s time to test it.
First, your first website using the following command:
curl website1.test
If everything is fine, you should see your first website page:
<!DOCTYPE html> <html> <head> <title>First Website</title> </head> <body> <h1>Hello! This is my first website.</h1> </body> </html>
Now, test your second website with the following command:
curl website2.test
If everything is fine, you should see your second website page:
<!DOCTYPE html> <html> <head> <title>Second Website</title> </head> <body> <h1>Hello! This is my second website.</h1> </body> </html>
Conclusion
In the above guide, you learned how to set up an Nginx reverse proxy container for two websites within a Docker container. This should help you to deploy an application in the development environment; try it on your dedicated server from Atlantic.Net.