tavis nörttimaailmassa

EksisONE - artikkeleita ja ohjeita nörttimaailmasta

Nginx: SSL ja HTTP/ 2 + Varnish/Apache2/PHP-FPM

Varnish ei osaa hoitaa SSL-sertifikaatteja, eikä tule koskaan osaamaankaan reverse proxynä. Välimuistittaja tarvitsee jonkun eteensä muuntamaan nettiliikenteen tavalliseksi http-pyynnöksi ja jälkeensä jonkun antamaan sen sisällön, jota välimuistitetaan. Käytännössä tuo tarkoittaa useimmin webpalvelinta Varnishin molemmilla puolilla, joko samana ohjelmana tai erillisinä. On kolmaskin ratkaisu terminoimaan SSL ja se on käyttää ns. tyhmää proxyä, joka ei muuta tee kuin varmistaa pyydetyn https-liikenteen vaatiman SSL-sertifikaatin ja siirtää siitä eteenpäin tavallista http:tä. Olen kokeillut tuohon pariakin ratkaisua ja nyt sivustofarmin SSL-tarpeet hoitaa Nginx. Mielestäni jopa melkoisen mallikkaasti.

Kun otin Varnishin aikoinaan käyttöön, niin ensimmäisenä kokeilin ratkaisua, jossa Apache2:een rakennetaan oma virtual host ottamaan vastaan portista 443 tuleva https ja vastaava virtual host, mutta Varnishin takana, kuunteli sitten http-asiaa. Varnish hoiti maailman suuntaan http:n portin 80. Se toimi sinänsä hyvin ja kun oivalsin miten homma hoidetaan useammalla virtual hostilla, niin olin kohtuullisen tyytyväinen. Ainakin jonkun aikaa.

Taustaa

Apache2 > Varnish > Apache2 pino oli hivenen raskas. Toki olisin voinut ostaa dropletiin, joka on DigitalOceanin nimi virtuaalipalvelimelle, lisää prosessoreita ja RAM:ia, mutta olin sitä mieltä, että 4 CPU:ta 8 gigalla pitäisi riittää isompaan kuin yhdelle suomalaisittain vilkkaalle blogahtavalle koirasivustolle, muutamalla lähes kuolleelle wordpress-asennukselle, parille hitaasti myyvälle verkkokauppalle ja vähäkäyttöiselle Moodlelle. Älä ymmärrä väärin, eivät sivustot olleet lähelläkään kaatua resurssien vähyyden takia, mutta kuorma oli liian kova.

Kun yhtälöön lisättiin halu saada hyödynnettyä HTTP/2, niin keulilla oleva Apache2 saisi lähteä. Periaatteessa HTTP/2 on mahdollista toteuttaa Apachella ottamalla mod_http2 käyttöön, mutta se ei onnistu pakasta revittynä. Ongelmaksi tulee sellaiset kuin ALPN ja TLS (ihan sama asia kuin SSL, ovat muuttamassa nimeä), joka vahvistaa salauksen käyttöä. HTTP/2 ei moista vaadi, mutta selainvalmistajat vaativat.

Pariskunta ALPN/TLS on rakennettavissa, mutta se vaatii PHP-FPM:n ottamista käyttöön sekä yhden työkalun muuttamista käsin. Yhtään en tajua mistä on kyse, mutta Apachen ja PHP:n mukana oletuksena oleva kummajainen mpm_prefork kieltäytyy HTTP/2:sta ja tipauttaa yhteyden tuttuun HTTP/1.1:een taustalla sen kummallisemmin asiasta huutaen. Kyllä se ilmoittaa, mutta ilmoitusta täytyy tajuta vilkaista Apachen error.log tiedostosta.

Tein kaiken ohjeiden mukaan ja kaadoin jokaisen saittini. Lisäksi serveri sekosi päästään. Yritin palauttaa alkutilanteen, mutta sekään ei onnistunut. Minulla oli periaatteessa PHP-FPM poistettu, mutta silti jokainen virtual host vaati merkinnän sen käytöstä. Olin palauttanut sen takaisin paikalleen ja käyttöön mpm_prefork kummajaisen, jota systeemi ei osannut kuitenkaan käyttää. Googlen mukaan osa on saanut Apachen toimimaan myös HTTP/2:den kanssa, jopa Varnishin edessä. Minä en onnistunut. Googlen hakutulosten mukaan en ollut ainoa.

Tein sen minkä jokainen arvonsa tunteva web/system manager tekisi: tuore virtuaaliserveri käyttöön ja saitit sinne – se ei muuten onnistu ihan vartissa. Nyt otin käyttöön aikoinaan hylkäämäni stackin: Hitch > Varnish > Apache2.

Oli jo kokeillut Hitchiä terminoimassa SSL:n ja hoitamassa HTTP/2:den kuntoon. Se on kevyt, tehokas ja tekee toimiessaan juuri sen minkä pitääkin. Taatusti aivan erinomainen valinta, mutta ei minulle. Jostain syystä Hitch kämmäili Lets Encryptin sertifikaattien kanssa ja jos sen hoitamilla domaineilla yhden sertifikaatti ei ollut sitä  mitä Hitch halusi, niin se kaatoi itsensä kokonaan vieden mukanaan aivan kaikki sivustot. Virheilmoitukset olivat Windows-tasoa, eli täysin hyödyttömiä. Piti aina erikseen selvittää mistä Hitch oli ottanut nokkiinsa, ja se vei aikaa. Ilman Monitin tekemää valvontaa minulla olisi saattanut olla sivustot saavuttamattomissa luoja tietää kuinka kauan – kävijät eivät nimittäin kaatuneesta sivustosta ilmoittele, eivät edes tutut.

Olisin saattanut pystyä elämään tuon kanssa, mutta Hitch ei tullut toimeen Moodlen kanssa jostain todella oudosta syystä. Hitch on niin sanottu tyhmä proxi. Se ei tee sen enempää kuin hoitaa SSL:n, ei muuta headereita tai mitään muutakaan, vaan siirtää https:n jälkeen kaiken sellaisenaan Varnishille. Silti Moodle ei päästänyt kirjautumaan ja oireet olivat kuin cookiet eivät olisi kulkeneet. Sama systeemi samoilla Varnishin asetuksilla toimi kuitenkin Apachen ollessa keulilla ja tarjotessa HTTP/1.1:stä. Lisäksi Varnish oli asetettu antamaan Moodlelle return(pipe), joka tarkoittaa cachen läpijuoksua ilman, että yhtään mitään muutettaisiin. Yleensä neuvotaan käyttämään return(pass), mutta se jättää vain cacheamatta ja silti siivoaa cookiet, muokkaa headereita jne – joten jos sinulla on joskus Varnishin kanssa tarve läpäistä cache totaalisesti, niin tuo kannattaa pitää mielessä.

Minulla alkoi olla vaihtoehdot vähissä. Toki olisin voinut kokeilla Poundia ja paria muuta Hitchiä vastaavaa tai sen edeltäjää, mutta aloin olla kurkkuani myötä säätöjä, korjauksia ja valvottuja öitä. Otin siis käyttöön toisen mainstream webserverin: Nginx.

Lähtöasetelma

Päätin pitää backendinä Apachen. Toki se on hivenen raskaampi kuin Nginx, ainakin niin väitetään, mutta tunsin se jotenkin ja minulla oli jo olemassa sillä toimiva setup. En halunnut korjata ja muuttaa jotain, joka ei muutosta tarvinnut. Eräs totesi minulle, että Apache2 kannattaa heivata, koska Nginx on parempi tarjoamaan isoja staattisia tiedostoja. Anteeksi vain, mutta minulla on sisältösivustoja, ei tiedostonjakopalvelua. Lisäksi CDN hoitaa tiedostot Amazonin S3:n kautta ja Varnish tekee kaikkensa, että kukaan ei ylipäätään menisi Apachelle.

Oli toinenkin rajoite miksi Nginx ei ollut valinta taustalle. Minulla on muutama asiakkaan sivusto serverillä, enkä jaksa/halua tehdä virtual conf – tasolla erillisistä pyynnöistä muutoksia. He saavat siis käyttää .htaccess säätöjä, vaikka ne olenkin omilla sivuistoillani estänyt. Nginx ei tajua .htaccess tiedostoista mitään ja siksi niihin pääsy pitäisi varmuuden vuoksi erikseen jopa estää per virtual host – ylipäätään aika monet asiat on Ngixinssä tehtävä aina sivustokohtaisesti, eikä niitä saa tehtyä serverin tasolla globaalisti kaikkia koskeviksi.

Minulla siis

  • Nginx kuuntelee portteja 80 ja 443, ja kääntää http-pyynnöt https:ksi
  • Nginx vastaanottaa HTTP/2-liikenteen ja hoitaa SSL-sertfikaatit
  • Varnish kuuntelee Nginxiä portissa 8080 (nuo sisäiset portithan saa laittaa melkein miksi tahansa, kunhan käyttää vapaita) ja hoitaa välimuistit
  • Apache2 kuuntelee Varnishia portissa 81 ja tarjoaa niiden websivujen sisällön, joita ei saada cachesta

Usein tehdään niin, että Varnish kuuntelee porttia 80 ja sitten ohjaa http-pyynnöt takaisin edessä olevalle webserverille tai mikä 443 porttia kuunteleekaan, jonka jälkeen sama pyyntö palaa takaisin Varnishille http-pyyntönä. Turhaa mutkittelua vain siitä ilosta, että saadaan pitää Varnish auki maailmalle ja silti pakotettua asiakas https-protokollalle.

Nginx pystyy myös toimimaan reverse proxynä, joten siltä osin Varnish olisi turha. Mutta Varnish on tehokkaampi kuin Nginx koskaan, sekä ainakin minusta myös helpompi. Lisäksi jos jokin osa-alue kiukuttelee, niin se ei potkaise pallia muiden alta. Jos haluaa todella urakalla laittaa kaikki munat samaan koriin, niin antaa Nginxin hoitaa aivan kaiken – ja kun Nginx hajoaa, niin hajoaa aivan kaikki.

PHP-FPM

PHP-FPM on php:n laajennus, jota webserverit käyttävät. Siitä on hyötyä, kun kävijöitä tulee paljon. PHP-FPM osaa tehostaa toimintaansa kovemmassa kuormituksessa. Tavallisilla saiteilla, kun puhutaan maksimissaan tuhannesta kävijästä päivässä, PHP-FPM ei anna mitään varsinaista tehokasta aitoa etua, mutta sitä tarvitaan yhteen työhön: se auttaa HTTP/2 käytössä.

PHP-FPM on helppo asentaa:

apt install php-fpm

Tuo asentaa saman version PHP-FPM:n kuin mikä php:n versio on; tätä kirjoitettaessa minulla on php 7.3, joten PHP-FPM löytyy nimellä php7.3-fpm ja sen hakemisto on /etc/php/7.3/fpm. Hakemisto on oleellinen siksi, että siellä piilee php.ini ja se on seuraavaksi muokkauksessa – kaikki muutokset, jotka olet aikoinaan tehty php:lle tiedostoon php.ini, eivät enää vaikuta nettipuolella, koska PHP-FPM ottaa ylivallan. Ne on tehtävä uudestaan.  Ylipäätään oletusasetukset ovat mallia mikroskooppinen serveri, jossa ei käy kuin ylläpito joskus ja harvoin. Esimerkit ovat minulle sopivia, joten muokkaa itsellesi tarkoituksenmukaisemmiksi tai lähde liikkeelle ehdotetuista.

nano /etc/php/7.3/fpm/php.ini
  • max_execution_time = 300
  • max_input_time = 190
  • max_input_vars = 10000
  • post_max_size = 128M
  • upload_max_filesize = 64M
  • memory_limit = 512M

Tarvitaan vielä kaksi muutosta lisää.

Avaa PHP-FPM:n oma asetustiedosto:

nano /etc/php/7.3/fpm/php-fpm.conf

Lisää sinne tämä tai etsi vastaavat, aseta arvot ja poista kommentointi:


emergency_restart_threshold 10
emergency_restart_interval 1m
process_control_timeout 10s

Tuo tarkoittaa, että jos 10 lapsiprosessia on saavuttamattomissa minuutin, niin PHP-FPM käynnistää itsensä, mutta antaa järjissään oleville 10 sekuntia aikaa hoitaa hommansa loppuun.

nano /etc/php/7.3/fpm/pool.d/www.conf

Muuta nämä (minulla on 4 CPU/8 GB RAM):

  • pm.max_children = kaava on (koko muisti – muu käyttö) / keskimääräisen prosessin koko (minulla 32; laita sama, jos ei ole muuta ajatusta)
  • pm.start_servers =  CPU:t x 4 (minulla 16)
  • pm.min_spare_servers = CPU:t x 2 (minulla 8)
  • pm.max_spare_servers = sama kuin start_servers (minulla 16)
  • pm.process_idle_timeout = 10s (oletus)
  • pm.max_requests = 500 (oletus)

Käynnistä PHP-FPM uudestaan:

systemctl restart php7.3-fpm

Sallitaan PHP-FPM mm. rebootissa:

systemctl enable php7.3-fpm

Käynnistetään PHP-FPM:

systemctl start php7.3-fpm

PHP:ssä muutokset otetaan käyttöön käynnistämällä Apache2 uudestaan, mutta nyt joudutaan käynnistämään PHP-FPM uudestaan aina kun on tehty muutoksia:

systemctl restart php7.3-fpm
  • Kun sinulla on hieman joutoaikaa, niin tutustu paremmin PHP-FPM:n säätämiseen. Nyt tarjotut oletuarvot eivät välttämättä ole pitkän päälle paras mahdollinen vaihtoehto.

Nginx: asentaminen

Nginx asentuu tyypilliseen tapaan:

apt install nginx

Tehdään heti alkuun muutama muutos oletusarvoihin, joka helpottaa useamman serverin hallintaa muistitasolla ja muutenkin. Avataan nginx.conf:

nano /etc/nginx/nginx.conf
  • Alussa,  ennen lohkoa events on kohta: worker_processes auto; Se asettaa arvon samaksi kuin montako prosessorin ydintä sinulla on käytössä. Itselläni se on sama kuin 4. Useimmat ohjeet käskevät vaihtaa arvoksi ytimien määrän, mutta ohjeet koskevat vanhoja versioita, joissa oletusarvo oli 1. Nykyinen auto hoitaa arvon kohdalleen eikä sitä tarvitse muuttaa. Jos olet utelias, niin saat ytimien määrän selville komennolla:
nproc --all
  • Muuta events lohkon alussa olevan worker_connections 768; arvoksi 1024

    worker
    arvot määräävät kuinka monta yhteyttä Nginx hyväksyy sekunnissa. Kun yksi vierailija käyttää yleensä aina kaksi yhteyttä (se mitä kysymykseen ja vastaukseen tarvitaan, niin oletusarvo tarkoittaakin 384 kyselyä sekunnissa (joka on hieman eri asia kuin kävijöiden määrä). Tuo riittää suurimmalle osalle, joilla on harvoin noin paljon yhtä aikaisia vierailijoita. Mutta jos raja saavutetaan, niin kyselyt jätetään jonoon ja hoidetaan vasta kun jono menee eteenpäin. Se voi aiheuttaa pullonkaulan ja hidastaa.Yhteyksien määrä on processes x connections eli minulla nyt 4 x 1024 = 4096, joka siis periaatteessa on lähellä samaa kuim 2048 samanaikaista kävijää sekunnissa (se riittää aika pitkälle…). Osassa vinkkejä kehoitetaan tuplaamaan connections esimerkiksi arvoon 2048 jos tuntuu siltä, että ahdistaa. Vinkki on pätevä, jos sattuu omaamaan isoja tehokkaita prosessoreita. Suurimmalla osalla VPS-asiakkaita budjettihinnalla näin ei ole ja arvon nostaminen yli sen, mihin prosessorit kykenevät, on yksiselitteisesti tyhmää.Saat selvitettyä maksimiarvon tällä komennolla:
ulimit -n
  • Laita http lohkoon server_tokens off; tai poista sen kommentointi.
    Se estää kyselijöitä saamasta selville serverisi version; pieni varmuustoimenpide siis aukkojen ja haavoittuvuuksien metsästäjiä vastaan.
  • Etsi tämä rivi ja poista kommentointi eli risuaidan # merkki:
# server_names_hash_bucket_size 64;

Palomuuri

Nginx tarvitsee pääsyn ulkomaailmaan. Käytetään siihen palomuuria ja tarkkaan ottaen iptablesin liittymää ufw.

Jos sinulla on ollut jo Apache2 käytössä, niin periaatteessa portit 80 ja 443 ovat jo auki. Luultavasti ne on silloin aukaistu nimellä Apache Full. Jostain syystä, joka saattoi olla mitenkään liittymättä tuohon, niin minulla Nginx ei tykännyt ollenkaan tuosta asetuksesta. Jouduin ottamaan sen pois päältä ja kytkemään tilalle Nginx Full.

Näet käytettävät sovellukset näin:

ufw app list

Palomuurin nykytilanne selviää tästä:

ufw status numbered
  • vipu numbered ei ole pakollinen, mutta helpottaa, jos jotain täytyy sammuttaa

Jos sinulla näkyy, että Apache on asennettu tavalla tai toisella, niin saat sen otettua pois päältä:

ufw delete <numero>

Nginx sallitaan näin:

ufw allow "Nginx Full"

Voit tarkistaa ketkä kuuntelevat portteja, vaihda tilalle haluamasi portti:

netstat -tlnp | grep 443

Voit käyttää tätäkin:

fuser -v 443/tcp

Jos porttia kuuntelee väärä ohjelma, tai sinne yrittää joku muukin, niin helpoin tapa on katkaista kaikilta yhteys:

fuser -k 443/tcp

Sen jälkeen voit käynnistää esimerkiksi Nginxin. Jos samaa porttia oli kuunnellut Apache, niin varmista, että olet määritellyt /etc/apache2/ports.conf tiedostossa Apachelle pääsyn vain haluttuun porttiin.

Lets Encrypt

Tarvitset SSL-sertifikaatin ja siihen kannattaa käyttää Let’s Encryptiä. Toki voit ostaa maksullisenkin SSL-sertfikaatin, mutta ei se mitään aitoa lisäarvoa turvallisuuteen tuo.

Asennetaan ensin Let’s Enrypt:

apt install letsencrypt

Jos käytät sertifikaatin hankkimiseen --nginx vipua, niin asennetaan sekin:

apt install python-certbot-nginx

Ongelma on siinä, että jos teet Nginxissä  virtual hostin server-blokin portille 443 ilman sertifikaattia, niin Nginx kaatuu. Tarkoittaa sitä, että jokainen uusi domain on tehtävä ensin virtual hostin määrittelyllä, joka juttelee vain portin 80 kautta ja ilman Varnishia. Sen jälkeen muokkaat virtual hostin uudestaan Varnishin ymmärtämäksi. Apachessa pääsee helpommalla ja itse koen tämän jopa ärsyttäväksi.

Jos joutuu asettamaan usein uusia virtual hosteja, niin kannattanee kehittää jokin kiertotie – mikä sellainen olisi, niin ei hajuakaan (kommentoinnissa saa vinkata). Ja jos olet kuten minä, että uusia domaineja ei olla ostamassa ihan joka viikko, niin ehtii unohtamaan kaikki koukerot, että saa virtual hostin toimimaan.

Joudut siis tekemään yhden väliaikaisen virtual hostin Nginxiin. Se kannattaa nimetä lopullisella nimellä ja kun SSL-sertifikaatti on vihdoin haettu Let’s Encryptiltä, niin sitten muokkaa konffin lopulliseksi.

Tehdään virtual hostille hakemisto ja <domain> tarkoittaa tietysti sitä miten hakemiston nimeät:

mkdir -p /var/www/<domain>/public_html

Minulla epäonnistui pariin kertaan sertifikaatin haku tyhjälle hakemistolle, joten laitetaan sinne index.html tiedosto:

touch /var/www/<domain>/public_html/index.html

Jos haluat sinne jotain tekstiä varmistaaksesi, että oikea sivusto löytyy, niin avaa se ja lisää vaikka ’terve’ tai sivuston nimi, mutta pakko ei ole:

nano /etc/nginx/sites-available/<domain>/index.html

Tehdään virtual host:


server {
   listen 80;
   server_name <domain> www.<domain>;

   root /var/www/<domain>/public_html;
   index index.html;
}

Tarkista, että ei ole kirjoitusvirheitä:

nginx -t

Otetaan virtual host käyttöön:

ln -s /etc/nginx/sites-available/<domain> /etc/nginx/sites-enabled/

Ladataan Nginx uudestaan:

systemctl reload nginx

Varmista, että sivuston nimipalvelinmuutokset ovat voimassa:

curl -vL <domain>

Kun nimipalvelimet ovat päivittyneet, niin haetaan sertifikaatti:

certbot certonly --webroot --preferred-challenges http -d <domain> -d www.<domain>

Sinulta kysytään webrootia (Input the webroot for ..), Anna siihen sivuston hakemisto:

/var/www/<domain>/public_html

Sinulta kysytään www-alkuisen webrootia, joten vastaa 2 eli sama.

Voit toki asentaa SSL-sertifikaatin suoraankin, jolloin Lets Encrypt kirjoittaa tarvittavat tiedot conf-tiedostoon. Mutta jos olet koskaan aikaisemmin hakenut sertifikaatin jollekin domainille, niin siitähän saa kopioitua suoraan tarvittavat rivit, kunhan muistaa muuttaa domainin.

Tämä toimii joskus paremmin kuin certbot certonly, enkä tiedä miksi.

certbot --nginx -d <domain> -d www.<domain>

Ensimmäisellä kerralla siltä nimenomaiselta virtuaalipalvelimelta sertifikaattia hakiessasi joudut hyväksymään ehdot, antamaan sähköpostiosoitteesi ja halutessasi pääset liittymään EFF:n postituslistalle.

Jos sait virheilmoituksen, että certbot sai

  • error 404, niin luultavasti nimipalvelimet eivät ole päivittyneet
  • error 403, niin sinulla ei ole sivuston hakemistossa tiedostoa, jota Nginx/certbot suostuu lukemaan, kuten index.html
  • error 500/503, niin yrität saada sertifikaatitonta domainia menemään Varnishin kautta Apachelle, jossa ei ole domainille omaa virtual hostia

Kummassakin tapauksessa palaa takaisin ja korjaa Nginxin virtual host aiemmin olevan esimerkin mukaiseksi.

Joudut muokkaamaan virtual hostin lopulliseen muotoonsa ja silloin lisään porttia 443 kuuntelevaan server-blokkiin Let’s Encryptin tekemät SSL-sertifikaatin tiedot:


ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

Voit hakea sertifikaatin myös komennolla certbot --nginx -d <domain> -d www.<domain> ja saat sen. Ongelmaksi nousee sertifikaatin uusiminen. Se ei onnistu kuin portin 80 kautta, ja jos/kun 80 käännetään porttiin 443, niin certbot ei saa sertifikaattia uusittua. Silloin vaihtoehdoiksi jää portin 80 kuuntelu Varnishin puolella tai jokainen uusimiskerta käsin poistaa Nginxissä uudelleenohjauksen porttiin 443 ja palauttaa se kun sertifikaatti on uusittu. Molemmat ovat hyvin kaukana näppärän ketterästä ratkaisusta, joten käytetään vipua --webroot, jolloin porttiongelmaa ei ole.

Virtual host

Virtual hostit tehdään hakemistoon /etc/nginx/sites-available/ ja aktiiviset näkyvät myös hakemistossa /etc/nginx/sites-enabled/ aivan kuten Apachessakin. Periaatteessa Nginx käyttää samanlaista rakennetta kuin Apache2, mutta syntaksi on kuitenkin aika ajoin niin erilainen, että asioita ei voi siirtää noiden kahden välillä kopypeistaamalla.

On niissä muitakin eroja. Kuten että Nginx ei vaadi erillistä asettamista kuunnellakseen jotain porttia, kuten tehdään Apachessa tiedostossa /etc/apache2/ports.conf.  Jos Nginxissä portti on mainittu listen-komennolla jossain virtual hostissa, niin sitä kuunnellaan. Jos ei ole, niin sitä ei kuunnella. Vastaavia eroja on muitakin.

Jos tarvitset jotain säätöjä, niin googleta. Mutta jos tarve on saada Nginx Varnishin eteen hoitamaan SSL-terminointi portissa 443 ja lisäksi myös HTTP/2 hoidettua, niin käytä tätä esimerkkiä:

  • tarvitset map komennon, jos sinulla on 301 uudelleenohjauksia; jos ei ole, niin kommentoi
  • tarvitset hash_map_bucket_size suuremmalle asetukselle, jos sinulla on paljon uudelleenohajuksia malliin satoja; oletus on 64.
  • vaihda <ip-address> virtuaalipalvelimesi IP-osoitteeksi, mutta toimii ilmankin
  • example.tld on domainisi

Esimerkki tekee kaksi uudelleenohjausta:

  • kääntää kaikki http-pyynnöt muotoon https (http://www.example.tld > https://www.example.tld
  • kääntää kaikki ilman www-alkua olevat www:lle (http://example.com / https://example.tld > https://www.example.tld

Virtual host otetaan käyttöön tekemällä sen conf-tiedostosta symbolinen linkki hakemistosta sites-available hakemistoon sites-enabled. Apache2 tekee aivan samoin, mutta siihen on olemassa oma komentonsa a2ensite ja a2dissite. Nginxillä se tehdään linuxin komennoilla. Minusta Apachen tapa on helpompi omaksua ja muistaa, mutta minulta ei kysytä.

Kun haluat ottaa virtual hostin käyttöön:

ln -s /etc/nginx/sites-available/example.tld /etc/nginx/sites-enabled/
  • example.tld on tekemäsi tiedoston nimi; sen ei tarvitse olla domainin mukaan, mutta selvyyden takia useimmat niin nimeävät

Kun haluat ottaa virtual hostin pois linjoilta, niin poistat symbolisen linkin:

unlink /etc/nginx/sites-enabled/example.tld

Nginx asettaa asennuksessa oletushostin, aivan samoin kuin Apache2 tekee 000-default.conf kanssa. Se kannattaa ottaa pois päältä (tai sitten teet suoraan siihen ja jätät sen päälle, jos sinulla on vain yksi domain):

unlink /etc/nginx/sites-enabled/default

Aina kun teet muutoksia,  niin tarkista että edes syntaksi on oikein (ja tämä kannattaa opetella ulkoa ja käyttää aina muokkauksissa):

nginx -t

Normaalisti nyt ladattaisiin Nginx uudestaan, mutta ensiasennuksessa saa vielä odottaa.

HTTP/2

HTTP/2 käyttöönotto ei vaadi sen kummempia. Ei mitään modulien asentamista, jota Apache2 vaatii. Riittää, että sivustolla on SSL käytössä ja HTTP/2 merkitään virtual hostin server-blokkiin listen-riville:

Listen 443 ssl http2;

Perään reload:

systemctl reload nginx

Ja se oli siinä. Voit testata  HTTP/2 ja ALPN toimivuuden täällä.

Tässä on kuitenkin koukku. Nginx pystyy käyttämään vain yhtä HTTP-versiota samassa IP-osoitteessa ja portissa. Tuo tarkoittaa sitä, että jos asennat yhteenkin virtual hostiin porttiin 443 HTTP/2:den, niin HTTP/2 on käytössä aivan kaikissa sen IP-osoitteen virtual hosteissa portissa 443. Kääntäen, jos poistat HTTP/2:den käytöstä, niin se täytyy poistaa aivan kaikista, jotta se todellakin olisi poissa käytöstä.

Toki tuo helpottaa elämää siinä suhteessa, että riittää kun ottaa HTTP/2:den yhdessä virtual hostissa. Ei tarvitse editoida kaikkien muidenkin conf-tiedostoja. Mutta jos olet tilanteessa, että yksikin sivusto tarvitsisi jostain syystä vain HTTP/1.1 käyttöönsä ja muissa saisi olla HTTP/2, niin se ei onnistu. Se on joko kaikki tai ei kukaan. Tuon pystyy kiertämään purkkavirityksella laittamalla sen HTTP/1.1-sivuston kuuntelemaan jotain muuta porttia, kuten vaikka porttia 444 – mutta tuo on purkkaa.

Redirect 301 ja map

Sivuston ”tavalliset” uudelleenohjaukset, eli muuttuneet jutut, urlit jne, kannattaa tehdä heti ketjun alussa Nginxissä. Useinhan tavallisessa pinossa, jossa

  • Apache2/Nginx/Hitch/joku kuuntelee porttia 443
  • Varnish kuuntelee porttia 80

uudelleenohjaukset tehdään backendissä. Tuossa on pienen pieni teho-ongelma, koska silloin frontend kiertää per osoite:

  • kysyy ensin Varnishilta, löytyykö cachesta
  • kun ei löydy, niin ohjataan backendille
  • backend tekee uudelleenohjauksen ja lähettää takaisin Varnishille
  • Varnish antaa joko cachestä tai lähettää backendiin

Oikaistaan hieman mutkia ja tehdään uudelleenohjaus heti alussa, jolloin Varnish tekee vain yhden kerran työt ja backend saa vain kaksi ilmoitusta: ei löydy cachesta tai error 404.

Nginx saadaan laittamaan muistiin redirectit, kun käytetään asetusta map. Siinä tehdään yksinkertainen teksitiedosto, jossa jokaisella rivillä on ensin se url, joka uudelleenohjataan, ja toisena se url, johon uudelleenohjaus menee.

Esimerkiksi näin:

~/tätä/ei/ole/?$ /tänne/halutaan/

Regex auttaa elämää, jos osaa regexiä. Minä en osaa, mutta osaan kopypeistata. Ja kun olen Nginxin kanssa epävarma miten urlit tehdään, niin testaan toimiiko.

Jos/kun on tehnyt uudelleenohjauksia Apachella, niin kolme asiaa on eri tavalla:

  • Apache aloittaa ^-merkillä, Nginx aloittaa ~/ tai ~*/
    • ~ tarkoittaa, että kirjainkoko merkitsee
    • ~* tarkoittaa. että kirjainkoko ei merkitse
  • Nginx lopettaa aina puolipisteeseen ;
  • Apache merkitsee ohjauksen, Nginxillä se kerrotaan erikseen kyselyssä (Apache esim. [R=301,L], kun Nginx return 301)

Jos sinulla on Apachella lista uudelleenohjauksia, niin saat ne muokattua kuntoon kohtuullisen vähälle vaivalle käyttämällä esimerkiksi Notepad++ ohjelman korvaa-toimintaa

  • RewriteRule ^ korvataan ~/
  •  [R=301,L] (huomaa välilyönti alussa) korvataan ;
  • - [G] ja/tai - [R=410,L] korvataan /; – tuo tekee uudelleenohjauksen sivuston juureen, joka on kylläkin väärin, koska 410 on teknisesti 404. Koska halusin aikoinaan todellisenn uudelleenohjauksen error 410 Gone tapauksissa, joka ei onnistu Nginxillä, niin siirsin uudelleenohjauksen backedin Apachelle ja annoin Varnishin tehdä 410-redirectit.

Saatat saada ilmoituksen nginx -t komennolla, että sääntö ei kelpaa. Se johtuu siitä, että ohjattava osoite on silloin kahteen kertaan. Apache hyväksyy tuplat ja triplat käyttäen ensimmäistä osumaa, mutta Nginx on nirsompi: vain yksi kelpaa.

Nginxin tarkistus ja käynnistys

Kun olet valmis Nginxin kanssa, niin sallitaan sen käynnistys rebootissa:

systemctl enable nginx

Tarkista, että kaikki on kirjoitettu oikein (sanoinko jo, että tämä kannattaa opetella ulkoa ja kokeilla aina kun tekee muutoksia):

nginx -t

Jos virheitä ei ollut, niin ensimmäisellä kerralla käynnistetään:

systemctl start nginx

Jatkossa vain lataat uudestaan, kun olet tehnyt muutoksia virtual hosteihin:

systemctl reload nginx

Mutta jos olet muuttanut itse Nginxin asetuksia ja toimintaa, niin käynnistä uudelleen:

systemctl restart nginx

Varnish

Varnish asennetaan jo rutiinisti:

apt install varnish

Varnishin asettaminen vaatii tiedoston default.vcl muokkaamista ja sen saaminen kohdalleen voikin vaatia enemmän töitä ja ahkeraa Googlen käyttöä. Mutta jos sinulla on WordPress/Woocommerce sivusto ja toivottavasti vielä useampi domain, niin käy täältä katsomassa mallia.

Jos sinulla on vain yksi domain, niin yksinkertaistetusti kopioi tiedostoista all-common.vcl ja sopivan domainin .vcl-tiedostosta kaikki kohdat oikeisiin sub_ kohtiin tiedostoon default.vcl.

Koska Varnish ei juttele maailmalle, vaan Nginx siirtää proxynä kaiken Varnishille, niin Varnish ei saa jutella portille 80, vaan jollekin muulle vapaana olevalle localhostissa. Itse käytän porttia 8080. Portti on oltava sama kuin mikä Nginxin virtua hostissa asetettiin kohdassa proxy_pass.

Avataan Varnishin kovan ytimen asetukset:

systemctl edit --full varnish

Lisää sinne tämä:

ExecStart=/usr/sbin/varnishd -P /var/run/varnish.pid -j unix,user=vcache -F -a :8080 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,512m
  • kommentoi risuaidalla # vastaava jo olemassaoleva rivi
  • vaihda portti 8080, jos käytät jotain muuta
  • portti 6082 ei viittaa backnendiin, minulla Apache2, vaan Varnishin omaan liikenteeseen mm. varnishadm käyttöön.
  • 512m on se muistimäärä, joka Varnishille annetaan. 512 megaa riittää normaaliliikenteiselle sivustolle, mutta jos lämmität cachen eli laitat wget ohjelman hakemaan muistiin valmiiksi kaikki sivut, niin tuollainen 2000 postauksen WordPress täyttää sen heti. Itse käytän 5G eli viittä gigaa.

Kerrotaan muutos järjestelmälle:

systemctl daemon-reload

Osassa ohjeita käsketään muokata tiedostoa /lib/systemd/system/varnish.service (myös täällä) eikä minulle ole koskaan oikein selvinnyt, että koska käytetään kumpaa. Niissä on nimittäin samat tiedot, mutta niiden muokkaaminen ei muuta myös toista. Sen tiedän, että jos muokkaan tiedostoa varnish.service, niin seuraavalla uudelleenkäynnistyksellä käytössä onkin systemctl --edit varnish komennolla muokattu.

Tuosta tietenkin selviää pitämällä molemmat samanlaisina, mutta asiaa voi testata muuttamalla portti localhost:6082 toiseen vaikka portiksi 6081 (se on portti, jossa Varnish juttelee itsensä kanssa, kun käytetään varnishadm komentoa) ja kun kysyt systemctl status varnish niin näet kerrotun portin mukaan kumpaa käytetään. Mutta ilmeisesti systemctl --edit varnish muokkaa oikeaa silloin kun Varnishia käytetään enable komennon jälkeen, demonina tai jotain. Tai sitten ei.

Otetaan Varnish käyttöön:

systemctl enable varnish

Ensimmäisellä kerralla käynnistetään:

systemctl start varnish

Kun teet muutoksia vcl-tiedostoihin. niin uudelleenlataa – silloin cache säästyy:

systemctl reload varnish

Jos muutat Varnishin asetuksia, tai haluat tyhjentää välimuistin, niin käynnistä uudelleen:

systemctl restart varnish

Mutta kuten webservereilläkin, niin aina ennen uudelleenkäynnistystä tai -latausta kannattaa varmistaa, että ei ole tullut kirjoitusvirheitä tai puutu puolipisteitä. Jos käynnistät/uudelleenlataat Varnishin kirotusvirheillä, niin se kaatuu,

varnishd -C -f /etc/varnish/default.vcl

Apache2

Saat arvata yhden kerran miten Apache2 asennetaan…

apt install apache2

Apache2 on asennuksessa se, joka pitää sisällään kaikki verkkosivut – sitä kutsutaan myös backendiksi. Koska Apache2 juttelee vain ja ainoastaan Varnishin kanssa, niin sille sallitaan se portti, joka oli asetettu Varnishin default.vcl tiedoston alussa kohdassa backend default { .port = joka minulla on 81.

nano /etc/apache2/ports.conf

Tee siitä tällainen:


#Listen 80
Listen 127.0.0.1:81

<IfModule ssl_module>
# Listen 443
</IfModule>

<IfModule mod_gnutls.c>
# Listen 443
</IfModule>

  • kommentoi Listen 443 molemmissa kohdissa
  • kommentoi Listen 80
  • laita tilalle 127.0.0.1:81 eli Apache käyttää localhostin porttia 81

Virtual host

Koska sivusto majailee Apachessa, niin sille tarvitaan eräällä tavalla sisäinen virtual host. Se mikä tehtiin Nginxiin on se, joka näkyy maailmalle. Se mikä tehdään Apacheen, näkyy Varnishille. Koska Nginx on Varnishille proxy, niin se vain välittää pyydetyn domainin ja urlin Varnishille, joka kyselee moisen olemassaolosta Apachelta (jos ei löydä sitä välimuistista). Normaalitilanteessa siis Apache ja tietokantapalvelin vain pyörittelevät peukaloitaan ja pääsevät töihin vain harvoin.

Virtual host tehdään kuten porttiin 80 eli http-liikenteelle yleensä neuvotaan – sisäinen liikenne Varnishin ja Apachen välillä on aina http-liikennettä, ei koskaan SSL-salattua; sen ei tarvitse olla, koska pysytään serverin sisällä; joka toki kannattaa muistaa, jos backend sijaitseekin jossain muualla. Varnishin ja backendin välisen liikenteen saa SSL-salatuksi vain käyttämällä maksullista ja erittäin kallista Varnish Plus ratkaisua, mutta aidostihan tuo ei ole turvallisuusuhka ns. tavallisilla verkkosivuilla ja verkkokaupoilla.

Virtual hostin kohdalla on muistettava kaksi asiaa, loppu on tavanomaista Apachen virtual hostin säätöä:

  • alussa on oltava <VirtualHost 127.0.0.1:81> (portti on se mikä asetettiin)
  • VirtualHost osassa on oltava SetEnvIf X-Forwarded-Proto https HTTP=on

Lisäksi jos otit käyttöön PHP-FPM:n, niin sinulla on oltava tämä, että php ylipäätään toimisi (muista oikea versio):


<FilesMatch \.php$>
   SetHandler "proxy:unix:/var/run/php/php7.3-fpm.sock|fcgi://localhost/"
</FilesMatch>

Täältä löydät sivuston Katiska.info käytössä olevan virtual host tiedoston. Mukana on logien muuttaminen Varnishin vaatimaan muotoon, joka siirtää asiakkaan IP-osoitteen oikeaan paikkaan, sekä redirectit, joita vaativat WordPress (kategoria näkyy urlissa), WP Rocket (sivuvälimuisti) ja EWWW (kuvien optimointi). Voit käyttää sitä itsellesi sopivilta osin mallina.

Lopuksi

Nyt meillä on asennus, jossa

  • Nginx kuuntelee portteja 80 ja 443, sekä kääntää kaiken liikenteen porttiin 443 www-alkuisella urlilla
  • Varnish hoitaa välimuistin
  • Sivustot ovat Apachen hallussa

Jos unohdetaan Varnishin default.vcl, jonka säätäminen saattaa olla aloittelijalle välillä hermoja raastavaa, niin itselläni suurimmat ongelmakohdat olivat virtual hostien hakemistoihin /var/www/<domain>/public_html jääneet .htaccess ja .user.ini tiedostot, jotka estivät Nginxin ja sotkivat PHP-FPM:n toiminnan; minulla oli noiden takia yksi domain, joka antoi pelkästään error 500/503

Ylipäätään .htaccess kannattaisi ottaa pois käytöstä ja user.ini tiedostoa käytetään vain silloin kun ehdottomasti tiedetään mitä tehdään.

Domainin kanssa riidellessäni opin pari asiaa. Ensimmäinen on varmasti kokeneemmille selviö ja se on, että curl on paljon näppärämpi kuin latailla sivua selaimessa ja samalla pohtia, että tuleeko tulos sivustolta vai selaimen välimuistista – nimittäin Chrome ainakin laittaa mielellään omaan cacheensa jopa virhesivutkin. Toinen oli, että kirjoitusvirheille tulee sokeaksi – etsin muutaman päivän syytä siihen, miksi sain sivuston näkyviin localhostissa, mutta en muualta maailmasta – minulla oli Nginxin virtuaalikonfiin jäänyt proxyn portiksi 8080, kun se piti olla 80.

Kun haluat tietää headerit:

curl -I <url>

Jos sertfikaatit kiusaavat, niin tämä saattaa auttaa ymmärtämään mitä tapahtuu:

curl -vL <domain>
  • Jos kokeilet sitä, niin mielenkiintoinen osa löytyy ennen sivusisältöä

Nyt kun järjestelmä on pystyssä, niin toiminta on vakaampaa kuin muiden viritysten kanssa. Ja käytössä huoleton. Sekä nopea.