Upload files to "/"

This commit is contained in:
2024-06-12 19:59:01 -04:00
commit ad1e9ffbc5
+129
View File
@@ -0,0 +1,129 @@
<!DOCTYPE html>
<h2 id="setting-up-nginx-as-reverse-proxy-to-deploy-multiple-services-on-the-same-server-using-docker">Setting up Nginx as reverse proxy to deploy multiple services on the same server using Docker </h2><p>Let me show you how to go about configuring the above mentioned setup.</p><p>With these steps, you can install multiple web-based application containers running under Nginx with each standalone container corresponding to its own respective domain or subdomain.</p><p>First, let's see what you need in order to follow this tutorial.</p><h3 id="prerequisites">Prerequisites</h3><p>You'll be needing the following knowledge to get started with this tutorial easily. Althogh, you can get by without them as well.</p><ul><li>A Linux system/server. You can easily deploy a Linux server in minutes using <a href="https://www.linode.com/?r=19db9d1ce8c1c91023c7afef87a28ce8c8c067bd">Linode cloud service</a>.</li><li>Familiarity with Linux commands and terminal.</li><li>Basic knowledge of Docker.</li><li>You should have Docker and Docker Compose installed on your Linux server. Please read our guide on <a href="https://linuxhandbook.com/install-docker-centos/">installing Docker</a> and <a href="https://linuxhandbook.com/install-docker-compose-centos/">Docker Compose on CentOS</a>.</li><li>You should also own a domain (so that you can set up services on sub-domains).</li></ul><p>I have used domain.com as an example domain name in the tutorial. Please make sure you change it according to your own domains or subdomains.</p><p>Other than the above, please also make sure of the following things:</p><p><strong>Change your domains DNS records</strong></p><p>In your domain name providers A/AAAA or CNAME record panel, make sure that both the domain and subdomains (including www) point to your servers IP address.</p><p>This is an example for your reference:</p><table>
<thead>
<tr>
<th>Hostname</th>
<th>IP Address</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td>domain.com</td>
<td>172.105.50.178</td>
<td>Default</td>
</tr>
<tr>
<td>*</td>
<td>172.105.50.178</td>
<td>Default</td>
</tr>
<tr>
<td>sub0.domain.com</td>
<td>172.105.50.178</td>
<td>Default</td>
</tr>
<tr>
<td>sub1.domain.com</td>
<td>172.105.50.178</td>
<td>Default</td>
</tr>
</tbody>
</table>
<p><strong>Swap space</strong></p><p>To make sure all your container apps are at ease and never run out of memory after you deploy them, you must have the necessary swap space on your system.</p><p>You can always <a href="https://linuxhandbook.com/increase-swap-ubuntu/">adjust swap</a> according to the available RAM on your system. You can decide the swap space based on the bundle of app containers on the single server and estimating their cumulative RAM usage. </p><h3 id="step-1-set-up-nginx-reverse-proxy-container">Step 1: Set up Nginx reverse proxy container</h3><p>Start with setting up your nginx reverse proxy. Create a directory named "reverse-proxy" and switch to it:</p><pre><code>mkdir reverse-proxy &amp;&amp; cd reverse-proxy
</code></pre><p>Create a file named <code>docker-compose.yml</code>, open it in your favourite terminal-based text editor like <a href="https://linuxhandbook.com/basic-vim-commands/">Vim</a> or <a href="https://www.nano-editor.org/">Nano</a>. </p><p>For the nginx reverse proxy, I'll be using <a href="https://hub.docker.com/r/jwilder/nginx-proxy">jwilder/nginx-proxy</a> image. Copy and paste the following in the docker-compose.yml file:</p><pre><code>version: "3.7"
services:
reverse-proxy:
image: "jwilder/nginx-proxy:latest"
container_name: "reverse-proxy"
volumes:
- "html:/usr/share/nginx/html"
- "dhparam:/etc/nginx/dhparam"
- "vhost:/etc/nginx/vhost.d"
- "certs:/etc/nginx/certs"
- "/run/docker.sock:/tmp/docker.sock:ro"
restart: "always"
networks:
- "net"
ports:
- "80:80"
- "443:443"
</code></pre><p>Now let's go through the important parts of the compose file:</p><ul><li>You have declared four volumes, html, dhparam, vhost and certs. They're persistent data that you'd definitely want to keep even after the container's been down. The <code>html</code> &amp; <code>vhost</code> volumes will be very important in the next <a href="https://letsencrypt.org/">Let's Encrypt</a> container deployment. They're designed to work together.</li><li>The docker socker is mounted read-only inside the container. This one's necessary for the reverse proxy container to generate nginx's configuration files, detect other containers with a specific environment variable.</li><li><a href="https://linuxhandbook.com/docker-restart-policy/">Docker restart policy</a> is set to <code>always</code>. Other options include <code>on-failure</code> and <code>unless-stopped</code>. In this case, always seemed more appropriate.</li><li>The ports 80 and 443 are bound to the host for http and https respectively.</li><li>Finally, it uses a different network, not the default bridge network.</li></ul><blockquote>Using a user defined network is very important. This will help in isolating all the containers that are to be proxied, along with enabling the reverse proxy container to forward the clients to their desired/intended containers and also let the containers communicate with each other (Which is not possible with the default bridge network unless <code>icc</code> is set to <code>true</code> for the daemon).</blockquote><p>Keep in mind that YML is very finicky about tabs and indention.</p><h3 id="step-2-set-up-a-container-for-automatic-ssl-certificate-generation">Step 2: Set up a container for automatic SSL certificate generation</h3><p>For this, you can using <a href="https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion">jrcs/letsencrypt-nginx-proxy-companion</a> container image. </p><p>On the same <code>docker-compose.yml</code> file that you used before, add the following lines:</p><pre><code> letsencrypt:
image: "jrcs/letsencrypt-nginx-proxy-companion:latest"
container_name: "letsencrypt-helper"
volumes:
- "html:/usr/share/nginx/html"
- "dhparam:/etc/nginx/dhparam"
- "vhost:/etc/nginx/vhost.d"
- "certs:/etc/nginx/certs"
- "/run/docker.sock:/var/run/docker.sock:ro"
environment:
NGINX_PROXY_CONTAINER: "reverse-proxy"
DEFAULT_EMAIL: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="06737563744662696b676f682865696b">[email&#160;protected]</a>"
restart: "always"
depends_on:
- "reverse-proxy"
networks:
- "net"
</code></pre><p>In this service definition:</p><ul><li>You're using the same exact volumes as you used for the reverse-proxy container. The <code>html</code> and <code>vhost</code> volumes sharing are necessary for the <a href="https://letsencrypt.org/docs/challenge-types/">ACME Challenge of letsencrypt</a> to be successful. This container will generate the certificates inside <code>/etc/nginx/certs</code>, in the container. This is why you are sharing this volume with your reverse proxy container. The <code>dhparam</code> volume will contain the dhparam file. The socket is mounted to detect other containers with a specific environment variable.</li><li>Here you have defined two environment variables. The <code>NGINX_PROXY_CONTAINER</code> variable points to the reverse proxy container. Set it to the name of the container. The <code>DEFAULT_EMAIL</code> is the email that'll be used while generating the certificates for each domain/subdomain.</li><li>The <code>depends_on</code> option is set so that this service waits for the reverse proxy to start first, then and only then, this'll start.</li><li>Finally, this container also shares the same network. This is necessary for the two containers to communicate.</li></ul><h3 id="step-3-finalize-the-docker-compose-file">Step 3: Finalize the docker compose file</h3><p>Once the service definitions are done, complete the docker-compose file with the following lines:</p><pre><code>volumes:
certs:
html:
vhost:
dhparam:
networks:
net:
external: true
</code></pre><p>The network <code>net</code> is set to external because the proxied containers will also have to use this network. And if we leave the network to get created by <code>docker-comspose</code>, the network name will depend on the current directory. This will create a weirdly named network.</p><p>Other than that, other containers will have to set that network to be external anyway, otherwise those compose files will also have to reside in this same directory, none of which is ideal. </p><p>Therefore, create the network using</p><pre><code> docker network create net</code></pre><p>The following is the whole content of the <code>docker-compose.yml</code> file.</p><pre><code>version: "3.7"
services:
reverse-proxy:
image: "jwilder/nginx-proxy:latest"
container_name: "reverse-proxy"
volumes:
- "html:/usr/share/nginx/html"
- "dhparam:/etc/nginx/dhparam"
- "vhost:/etc/nginx/vhost.d"
- "certs:/etc/nginx/certs"
- "/run/docker.sock:/tmp/docker.sock:ro"
restart: "always"
networks:
- "net"
ports:
- "80:80"
- "443:443"
letsencrypt:
image: "jrcs/letsencrypt-nginx-proxy-companion:latest"
container_name: "letsencrypt-helper"
volumes:
- "html:/usr/share/nginx/html"
- "dhparam:/etc/nginx/dhparam"
- "vhost:/etc/nginx/vhost.d"
- "certs:/etc/nginx/certs"
- "/run/docker.sock:/var/run/docker.sock:ro"
environment:
NGINX_PROXY_CONTAINER: "reverse-proxy"
DEFAULT_EMAIL: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="fe8b8d9b8cbe9a91939f9790d09d9193">[email&#160;protected]</a>"
restart: "always"
depends_on:
- "reverse-proxy"
networks:
- "net"
volumes:
certs:
html:
vhost:
dhparam:
networks:
net:
external: true
</code></pre><p>Finally, you can deploy these two containers (Ngnix and Let's Encrypt) using the following command:</p><pre><code>docker-compose up -d
</code></pre><h3 id="step-4-verify-that-ngnix-reverse-proxy-is-working">Step 4: Verify that Ngnix reverse proxy is working</h3><p>The container that'll serve the frontend will need to define two environment variables.</p><p><code>VIRTUAL_HOST</code>: for generating the reverse proxy config</p><p><code>LETSENCRYPT_HOST</code>: for generating the necessary certificates</p><p>Make sure that you have correct values for these two variables. You can run nginx-dummy image with reverse proxy like this:</p><pre><code>docker run --rm --name nginx-dummy -e VIRTUAL_HOST=sub.domain.com -e LETSENCRYPT_HOST=sub.domain.com -e VIRTUAL_PORT=80 --network net -d nginx:latest</code></pre><p>Now if you go to your sub-domain used in the previous command, you should see a message from Ngnix server.</p><figure class="kg-card kg-image-card"><img src="https://linuxhandbook.com/content/images/2020/09/ngnix-reverse-proxy.png" class="kg-image" alt="Ngnix reverse proxy" loading="lazy" width="783" height="279" srcset="https://linuxhandbook.com/content/images/size/w600/2020/09/ngnix-reverse-proxy.png 600w, https://linuxhandbook.com/content/images/2020/09/ngnix-reverse-proxy.png 783w" sizes="(min-width: 720px) 720px"></figure><p>Once you have successfully tested it, you can <a href="https://linuxhandbook.com/docker-stop-container/">stop the running docker container</a>:</p><pre><code>docker stop nginx-dummy</code></pre><p>You may also stop the Ngnix reverse proxy if you are not going to use it:</p><pre><code>docker-compose down</code></pre><h3 id="step-5-run-other-service-containers-with-reverse-proxy">Step 5: Run other service containers with reverse proxy</h3><p>The process of setting up other containers so that they can be proxied is VERY simple.</p><p>I'll show it with two instances of <a href="https://nextcloud.com/">Nextcloud</a> deployment in a moment. Let me first tell you what you are doing here.</p><h4 id="do-not-bind-to-any-port">Do not bind to any port</h4><p>The container can leave out the port that serves the frontend. The reverse proxy container will automatically detect that.</p><h4 id="-optional-define-virtual_port">(OPTIONAL) Define VIRTUAL_PORT</h4><p>If the reverse proxy container fails to detect the port, you can define another environment variable named <code>VIRTUAL_PORT</code> with the port serving the frontend or whichever service you want to get proxied, like "80" or "7765".</p><h4 id="set-let-s-encrypt-email-specific-to-a-container">Set Let's Encrypt email specific to a container</h4><p>You can override the <code>DEFAULT_EMAIL</code> variable and set a specific email address for a specific container/web service's domain/subdomain certificate(s), by setting the email id to the environment variable <code>LETSENCRYPT_EMAIL</code>. This works on a per-container basis.</p><p>Now that you know all those stuff, let me show you the command that deploys a Nextcloud instance that'll be proxied using the nginx proxy container, and will have TLS(SSL/HTTPS) enabled.</p><blockquote>This is NOT AN IDEAL deployment. The following command is used for demonstrative purpose only.</blockquote><pre><code>docker run --name nextcloud --network net -e VIRTUAL_HOST="sub0.domain.com" -e LETSENCRYPT_HOST="sub0.domain.com" -d nextcloud:19.0.2
</code></pre><p>In the example, you used the same network as the reverse proxy containers, defined the two environment variables, with the appropriate subdomains (Set yours accordingly). After a couple of minutes, you should see Nextcloud running on sub0.domain.com. Open it in a browser to verify.</p><p>You can deploy another Nextcloud instance just like this one, on a different subdomain, like the following:</p><pre><code>docker run --name anothernextcloud --network net -e VIRTUAL_HOST="sub1.domain.com" -e LETSENCRYPT_HOST="sub1.domain.com" -d nextcloud:19.0.2
</code></pre><p>Now you should see a different Nextcloud instance running on a different subdomain on the same server. </p><p>With this method, you can deploy different web apps on the same server served under different subdomains, which is pretty handy.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://linuxhandbook.com/update-docker-container-zero-downtime/">