Loading...
Willow Media

Opstarten nginx met ontbrekende (Docker) host

Om website die via Docker gehost worden, beschikbaar te maken voor het publiek, wordt veelal Nginx als webserver ertussen geplaatst. Waarom nginx? Op de website staat het volgende:

Because it can handle a high volume of connections, NGINX is commonly used as a reverse proxy and load balancer to manage incoming traffic and distribute it to slower upstream servers – anything from legacy database servers to microservices.

Nginx is relatief simpel te configureren en zou weinig negatieve effecten hebben op de doorvoer snelheid. Zoals de beschrijving aangeeft wordt hij ook veelal gebruikt als reverse proxy, en laat dat nou precies zijn wat we nodig hebben. Een reverse proxy is in dit geval een web server die tussen de gebruiker en een specifieke service zit. Zo 'praat' je niet rechtstreeks tegen de service. Hiernaast kunnen we ook zorgen dat Nginx het https deel op zich neemt, terwijl de service hier geen weet van heeft. Zo werd er tot een aantal Dotnet Core versie geleden, ook aangegeven dat voor productie websites het verstandig was om er een reverse proxy tussen te zetten (met https), aangezien toen Dotnet Core nog niet 'goed genoeg' was om zelf op te lossen.

Nog een reden om een reverse proxy te gebruiken, is het gebruik van virtual hosts. Dit betekend dat nginx meerdere websites kan 'hosten', met verschillende domeinnamen. Als we de website direct zouden hosten, zou deze poort 80 en/of 443 in gebruik nemen, zonder dat hier een 2de site gebruik van kan maken. De reverse proxy zorgt dat het juiste domeinnaam bij de juiste service terecht komt. Deze services hoeven overigens niet op dezelfde host te staan. Een good practice in het geval van services via docker, is dat deze services alleen intern op een host (of netwerk) te benaderen zijn, en niet rechtstreeks van buiten af.

Configuratie

In dit geval ga ik niet in op de volledige configuratie van nginx, die overigens ook via docker gehost wordt. Een kleine hint over de configuratie is dat er een intern docker netwerk gedefineerd is, waardoor nginx de hosts kan terug vinden. Hieronder een voorbeeld configuratie die zorgt dat verkeer voor www.willow-media.nl op poort 443 door wordt gestuurd naar willowmedia op poort 5000. 5000 is de standaard poort voor AspNet Core sites en willowmedia is de hostnaam van 1 van de docker instanties.

server {
        server_name   www.willow-media.nl;
        listen              443 ssl http2;
        listen              [::]:443 ssl http2;

        location / {
                proxy_pass http://willowmedia:5000;
                include /etc/nginx/proxy.conf;
        }

        # De rest van de configuratie is verwijderd
}

We lopen met deze configuratie meteen tegen een probleem aan. Als de docker container op het moment van (her)start van nginx niet beschikbaar is, zal nginx een foutmelding geven en niet starten. Hoewel dit meestal geen probleem is, is het vragen om problemen om dit niet op te lossen (laten we het maar even op Murphy's Law houden). Tijdens het starten van nginx wordt de hostnaam die bij de proxy_pass opgegeven is opgehaald bij de DNS. In dit geval is dat de DNS van docker. Dit gebeurd maar 1x, wat betekend dat als de host op dat moment niet te vinden is, nginx een foutmelding zal geven.

We kunnen dit probleem ondervangen door een variabele te gebruiken en deze te gebruiken voor proxy_pass. Echter was dit niet genoeg voor mijn situatie. Wat hiernaast ook nog kan voorkomen, is dat na het herstarten van een docker image, dat het (interne) IP nummer veranderd. Dit leid dan tot een "gateway error" vanuit nginx. Om dit op te lossen kan een resolver opgegeven worden, met een cache timeout. De resolver is in dit geval de DNS van docker, waarbij valid aangeeft hoe lang hij het IP nummer moet bewaren voordat hij op nieuw nagevraagd zal worden.

server {
        server_name  www.willow-media.nl;
        listen              443 ssl http2;
        listen              [::]:443 ssl http2;

        location / {
                # this is the internal Docker DNS, cache only for 30s
                resolver 127.0.0.11 valid=30s;
                set $upstream http://willowmedia:5000;
                proxy_pass $upstream;

                include /etc/nginx/proxy.conf;
        }

        # De rest van de configuratie is verwijderd
}

30 seconden lijkt in dit geval veel, maar aangezien het incidenteel voorkomt dat het IP veranderd, wil ik voorkomen dat de DNS heel vaak geraadpleegd wordt. Uit ervaring zal moeten blijken of deze lager moet. Voor de volledigheid kan er ook nog een error pagina toegevoegd worden aan de configuratie, die bij een 502 (gateway error) statuscode een tijdelijke pagina laat zien. Deze zou na 30 seconden automatisch kunnen herladen om te kijken of de host weer benaderbaar is.

  • Docker
  • Linux
  • Nginx
  • Proxy