Samler logger med Loki

Samler logger med Loki

Vi i Badoo overvĂ„ker kontinuerlig nye teknologier og vurderer om det er verdt Ă„ bruke dem i systemet vĂ„rt. Vi Ăžnsker Ă„ dele en av disse studiene med samfunnet. Den er dedikert til Loki – et system for aggregasjon av logger.

Loki er en lĂžsning for lagring og visning av logger, samt at denne stakken tilbyr et fleksibelt system for analyse av dem og sending av data til Prometheus. I mai kom det en ny oppdatering som utviklerne aktivt promoterer. Vi ble interessert i hva Loki kan gjĂžre, hvilke muligheter den gir, og i hvilken grad den kan vĂŠre et alternativ til ELK – stakken vi bruker nĂ„.

Hva er Loki

Grafana Loki er et sett med komponenter for et fullverdig system for arbeid med logger. I motsetning til andre lignende systemer er Loki basert pĂ„ ideen om kun Ă„ indeksere metadataene til loggene – labels (som i Prometheus), mens selve loggene komprimeres i separate chunks.

Hjemmeside, GitHub

FÞr vi gÄr videre til beskrivelsen av hva som kan gjÞres med Loki, vil jeg forklare hva som menes med «ideen om Ä indeksere kun metadata». La oss sammenligne tilnÊrmingen til Loki og tilnÊrmingen til indeksering i tradisjonelle lÞsninger, som Elasticsearch, ved hjelp av et eksempel pÄ en linje fra nginx-loggen:

172.19.0.4 - - [01/Jun/2020:12:05:03 +0000] "GET /purchase?user_id=75146478&item_id=34234 HTTP/1.1" 500 8102 "-" "Stub_Bot/3.0" "0.001"

Tradisjonelle systemer parser hele linjen, inkludert felt med mange unike verdier som user_id og item_id, og lagrer alt i store indekser. Fordelen med denne tilnÊrmingen er at man kan utfÞre komplekse forespÞrsel raskt, siden nesten alle dataene er i indeksen. Men det koster Ä ha en stor indeks, som krever mer minne. Resultatet er at den fulltekstsÞkende indeksen for logger er sammenlignbar i stÞrrelse med selve loggene. For Ä sÞke raskt i den, mÄ indeksen lastes inn i minnet. Jo flere logger, desto raskere vokser indeksen og desto mer minne bruker den.

Loki-tilnÊrmingen krever at vi kun henter nÞdvendige data fra strengen, hvor antallet av verdier er lite. Slik fÄr vi en liten indeks og kan sÞke etter data ved Ä filtrere dem etter tid og indekserte felt, deretter skanne det som er igjen med regulÊre uttrykk eller substring-sÞk. Prosessen kan virke litt treg, men Loki deler opp forespÞrselen i flere deler og utfÞrer dem parallelt, noe som gjÞr at et stort volum av data kan behandles pÄ kort tid. Antallet shards og parallelle forespÞrsel i dem kan konfigureres; dermed er mengden data som kan behandles per tidsenhet lineÊrt avhengig av antallet ressurser som tilbys.

Denne avveiningen mellom en stor rask indeks og en liten indeks med parallell full skanning lar Loki kontrollere systemkostnadene. Den kan fleksibelt tilpasses og utvides i henhold til behov.

Loki-stakken bestÄr av tre komponenter: Promtail, Loki, Grafana. Promtail samler logger, behandler dem og sender dem til Loki. Loki lagrer dem. Og Grafana kan spÞrre om data fra Loki og vise dem. Generelt kan Loki brukes ikke bare til lagring av logger og sÞk i dem. Hele stakken gir store muligheter for behandling og analyse av innkommende data, ved Ä bruke Prometheus-metoden.
Beskrivelse av installasjonsprosessen kan finnes her.

Jeg mener, at det er en fornÞjelse at arbejde med logs i R80. Vi har en meget smart sÞgestreng, der giver os mulighed for at "skÊre" bÄde pÄ vilkÄrlig tekst, blade og andre indeksérbare parametre som kilde, destination, handling osv.

SĂžking i logger kan utfĂžres i det spesielle grensesnittet i Grafana – Explorer. For forespĂžrslene brukes LogQL-sprĂ„ket, som ligner veldig pĂ„ PromQL som brukes i Prometheus. I prinsippet kan det betraktes som en distribuert grep.

SĂžkegrensesnittet ser slik ut:

Samler logger med Loki

Selve forespÞrselen bestÄr av to deler: selector og filter. Selector er sÞk i indekserte metadata (etiketter) som er tildelt logger, mens filter er sÞkestrengen eller regexen som brukes til Ä filtrere postene som er bestemt av selectoren. I eksempelet som er gitt: I klammeparentesene er selectoren, alt etter er filteret.

{image_name="nginx.promtail.test"} |= "index"

PÄ grunn av hvordan Loki fungerer, kan man ikke gjÞre forespÞrsel uten selector, men etikettene kan gjÞres sÄ generelle som Þnsket.

Selector er key-value verdier i klammeparenteser. Man kan kombinere selectorer og angi forskjellige sĂžkekriterier ved Ă„ bruke operatorene =, != eller regulĂŠre uttrykk:

{instance=~"kafka-[23]",name!="kafka-dev"} 
// Vil finne logger med etiketten instance som har verdiene kafka-2, kafka-3 og ekskludere dev 

En filter er en tekst eller regex som vil filtrere all data hentet av selektoren.

Det er mulighet for Ä hente ad-hoc-diagrammer basert pÄ innsamlede data i metrics-modus. For eksempel kan man finne frekvensen av oppfÞringen i nginx-loggene som inneholder strengen index:

Samler logger med Loki

Fullstendig beskrivelse av mulighetene kan finnes i dokumentasjonen LogQL.

Loggparsing

Det finnes flere metoder for Ă„ samle logger:

  • Ved hjelp av Promtail, en standardkomponent i stabelen for loggsamling.
  • Direkte fra Docker-containere ved hjelp av Loki Docker Logging Driver.
  • Bruke Fluentd eller Fluent Bit, som kan sende data til Loki. I motsetning til Promtail har de ferdige parser for nesten alle typer logger og takler til og med multiline-logger.

Vanligvis brukes Promtail til parsing. Det gjĂžr tre ting:

  • Finner datakilder.
  • Knytter etiketter til dem.
  • Sender data til Loki.

Per nÄ kan Promtail lese logger fra lokale filer og fra systemd journal. Det mÄ installeres pÄ hver maskin hvor loggene skal samles.

Det er integrering med Kubernetes: Promtail finner automatisk tilstanden til klyngen via Kubernetes REST API og samler logger fra noden, tjenesten eller podden, samtidig som det knytter etiketter basert pÄ metadata fra Kubernetes (podnavn, filnavn osv.).

Det er ogsÄ mulig Ä knytte etiketter basert pÄ data fra loggen ved hjelp av Pipeline. Pipen til Promtail kan bestÄ av fire typer stadium. Mer detaljert - i den offisielle dokumentasjonen, jeg vil ogsÄ nevne noen nyanser.

  1. Parsing stages. Dette er RegEx og JSON-stadiet. PĂ„ dette stadiet henter vi data fra loggene til det sĂ„kalte extracted map. Vi kan hente fra JSON ved bare Ă„ kopiere feltene vi trenger til extracted map, eller ved hjelp av regulĂŠre uttrykk (RegEx), der de “mapper” named groups. Extracted map representerer et key-value lager, hvor key er navnet pĂ„ feltet og value er verdien fra loggene.
  2. Transform stages. Dette stadiet har to alternativer: transform, hvor vi definerer regler for transformasjon, og source - datakilden for transformasjon fra extracted map. Hvis det ikke finnes et felt i extracted map, vil det bli opprettet. Dermed kan vi lage etiketter som ikke er basert pĂ„ extracted map. PĂ„ dette stadiet kan vi manipulere dataene i extracted map ved Ă„ bruke en ganske kraftig Golang Template. I tillegg mĂ„ vi huske at extracted map lastes helt inn under parsing, noe som gir muligheten til for eksempel Ă„ sjekke verdien i den: “{{if .tag}tag value exists{end}}”. Malen stĂžtter betingelser, lĂžkker og enkelte strengefunksjoner som Replace og Trim.
  3. Handlingsfaser. PĂ„ dette stadiet kan vi gjĂžre noe med det utdragne:
    • Opprett en etikett fra extracted data som vil bli indeksert av Loki.
    • Endre eller sette tidspunktet for hendelsen i loggen.
    • Endre dataene (loggtekst) som skal sendes til Loki.
    • Opprett metrikker.
  4. Filtreringsfaser. Match-fasen, hvor vi kan sende unĂždvendige poster til /dev/null, eller viderebehandle dem.

Jeg viser et eksempel pÄ behandling av vanlige nginx-logger, og hvordan vi kan parse logger ved hjelp av Promtail.

For testen tar vi en modifisert nginx jwilder/nginx-proxy:alpine som nginx-proxy og en liten demon som kan forespĂžrre seg selv via HTTP. Demonen har flere endepunkter som den kan gi svar med forskjellig stĂžrrelse, ulike HTTP-status og oppholdstid.

Vi vil samle logger fra Docker-beholdere som kan finnes pÄ stien /var/lib/docker/containers//-json.log

I docker-compose.yml konfigurerer vi Promtail og angir banen til konfigurasjonen:

promtail:
  image: grafana/promtail:1.4.1
 // ...
 volumes:
   - /var/lib/docker/containers:/var/lib/docker/containers:ro
   - promtail-data:/var/lib/promtail/positions
   - ${PWD}/promtail/docker.yml:/etc/promtail/promtail.yml
 command:
   - '-config.file=/etc/promtail/promtail.yml'
 // ...

Vi legger til i promtail.yml banen til loggene (i konfigurasjonen finnes det et alternativ "docker" som gjÞr det samme med en enkelt linje, men det ville ikke vÊrt sÄ tydelig):

scrape_configs:
 - job_name: containers

   static_configs:
       labels:
         job: containerlogs
         __path__: /var/lib/docker/containers/*/*log  # for linux only

NĂ„r en slik konfigurasjon er aktivert, vil logger fra alle beholdere bli sendt til Loki. For Ă„ unngĂ„ dette, endrer vi innstillingene for testnginx i docker-compose.yml – vi legger til loggingsfeltet tag:

proxy:
 image: nginx.test.v3
//

 logging:
   driver: "json-file"
   options:
     tag: "{{.ImageName}}|{{.Name}}"

Vi retter opp promtail.yml og konfigurerer pipeline. Logger av denne typen kommer inn:

{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /api/index HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.096"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.66740443Z"}
{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /200 HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.000"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.702925272Z"}

Pipeline-steg:

 - json:
     expressions:
       stream: stream
       attrs: attrs
       tag: attrs.tag

Vi henter innholdet fra JSON-feltet stream, attrs, attrs.tag (hvis de finnes) og plasserer dem i en extracted map.

 - regex:
     expression: ^(?P([^|]+))|(?P([^|]+))$
     source: "tag"

Hvis feltet tag ble lagt i extracted map, bruker vi regex for Ä hente ut navnene pÄ bildet og containeren.

 - labels:
     image_name:
     container_name:

Vi tilordner etiketter. Hvis det finnes nĂžkler image_name og container_name i extracted data, vil deres verdier bli tildelt de tilhĂžrende etikettene.

 - match:
     selector: '{job="docker",container_name="",image_name=""}'
     action: drop

Vi kaster bort alle logger hvor de angitte etikettene image_name og container_name ikke ble funnet.

  - match:
     selector: '{image_name="nginx.promtail.test"}'
     stages:
       - json:
           expressions:
             row: log

For alle logger der image_name er lik nginx.promtail.test, henter vi feltet log fra den opprinnelige loggen og plasserer det i extracted map med nĂžkkelen row.

  - regex:
         # suppress forego colors
         expression: .+nginx.+|.+[0m(?P[a-z_.-]+) +(?P.+)
         source: logrow

Vi renser inngangsstrengen med regex og henter ut nginx virtual host og nginx logg-strengen.

     - regex:
         source: nginxlog
         expression: ^(?P[w.]+) - (?P[^ ]*) [(?P[^ ]+).*] "(?P[^ ]*) (?P[^ ]*) (?P[^ ]*)" (?P[d]+) (?P[d]+) "(?P[^"]*)" "(?P[^"]*)"( "(?P[d.]+)")?

Vi parser nginx-loggen med regex.

    - regex:
           source: request_url
           expression: ^.+.(?Pjpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$
     - regex:
           source: request_url
           expression: ^/photo/(?P[^/?.]+).*$
       - regex:
           source: request_url
           expression: ^/api/(?P[^/?.]+).*$

Vi analyserer request_url. Ved hjelp av regex bestemmer vi forespĂžrselens hensikt: statiske filer, bilder, API og plasserer den tilhĂžrende nĂžkkelen i extracted map.

       - template:
           source: request_type
           template: "{{if .photo}}photo{{else if .static_type}}static{{else if .api_request}}api{{else}}other{{end}}"

Ved hjelp av betingede operatorer i Template sjekker vi de tilordnede feltene i extracted map og setter de nĂždvendige verdiene for feltet request_type: photo, static, API. Setter other hvis det ikke var mulig. NĂ„ inneholder request_type forespĂžrselstypen.

       - labels:
           api_request:
           virtual_host:
           request_type:
           status:

Vi tilordner etiketter til api_request, virtual_host, request_type og status (HTTP status) basert pÄ det som er lagt i extracted map.

       - output:
           source: nginx_log_row

Vi endrer output. NĂ„ sendes den rensede nginx-loggen fra extracted map til Loki.

Samler logger med Loki

Etter Ä ha startet den gitte konfigurasjonen kan man se at hver post har fÄtt tildelt etiketter basert pÄ dataene fra loggen.

Det er viktig Ă„ merke seg at utvinning av etiketter med hĂžy kardinalitet kan betydelig bremse Loki. Det vil si at man ikke bĂžr plassere for eksempel user_id i indeksen. Les mer om dette i artikkelen “Hvordan etiketter i Loki kan gjĂžre loggforespĂžrselene raskere og enklere”. Men det betyr ikke at man ikke kan sĂžke etter user_id uten indekser. Man mĂ„ bruke filtre under sĂžket (for Ă„ «greppe» dataene), mens indeksen her fungerer som en identifikator for strĂžmmen.

Visualisering av logger

Samler logger med Loki

Loki kan brukes som datakilde for grafikker i Grafana ved hjelp av LogQL. FĂžlgende funksjoner stĂžttes:

  • rate — antall oppfĂžringer per sekund;
  • count over time — antall oppfĂžringer i et gitt tidsintervall.

Det finnes ogsÄ aggregasjonsfunksjoner som Sum, Avg og andre. Man kan lage ganske komplekse grafer, for eksempel graf over antall HTTP-feil:

Samler logger med Loki

Den standard data source Loki er noe begrenset i funksjonalitet sammenlignet med data source Prometheus (for eksempel kan man ikke endre legenden), men Loki kan kobles til som en kilde av typen Prometheus. Jeg er ikke sikker pĂ„ om dette er dokumentert atferd, men ifĂžlge utviklernes svar “Hvordan konfigurere Loki som Prometheus-datakilde? · Issue #1222 · grafana/loki”, er dette for eksempel helt lovlig, og Loki er fullstendig kompatibel med PromQL.

Vi legger til Loki som datakilde av typen Prometheus og skriver inn URL /loki:

Samler logger med Loki

Og man kan lage grafer som om vi jobbet med metrikker fra Prometheus:

Samler logger med Loki

Jeg tror at forskjellen i funksjonalitet er midlertidig, og utviklerne vil rette dette i fremtiden.

Samler logger med Loki

Metrikker

I Loki er det mulig Ä utvinne numeriske metrikker fra logger og sende dem til Prometheus. For eksempel, i nginx-loggen finner vi antall bytes pÄ svaret, samt, med en bestemt modifikasjon av standard loggformat, tiden i sekunder som ble brukt pÄ svaret. Disse dataene kan utvinnes og sendes til Prometheus.

Vi legger til en ny seksjon i promtail.yml:

- match:
   selector: '{request_type="api"}'
   stages:
     - metrics:
         http_nginx_response_time:
           type: Histogram
           description: "svartid ms"
           source: response_time
           config:
             buckets: [0.010,0.050,0.100,0.200,0.500,1.0]
- match:
   selector: '{request_type=~"static|photo"}'
   stages:
     - metrics:
         http_nginx_response_bytes_sum:
           type: Counter
           description: "total response bytes"
           source: bytes_out
           config:
             action: add
         http_nginx_response_bytes_count:
           type: Counter
           description: "telle response bytes"
           source: bytes_out
           config:
             action: inc

Alternativet gjĂžr at du kan definere og oppdatere metrikker basert pĂ„ data fra extracted map. Disse metrikene sendes ikke til Loki – de vises i Promtail /metrics-endepunktet. Prometheus mĂ„ konfigureres slik at den kan motta dataene som er samlet pĂ„ dette stadiet. I det gitte eksemplet for request_type="api" samler vi en histogrammetrisk. Med denne typen metrikker er det enkelt Ă„ fĂ„ percentiler. For statisk og bilder samler vi summen av byte og antallet linjer der vi fikk byte for Ă„ beregne gjennomsnittet.

Les mer om metrikker her.

Åpner porten pĂ„ Promtail:

promtail:
     image: grafana/promtail:1.4.1
     container_name: monitoring.promtail
     expose:
       - 9080
     ports:
       - "9080:9080"

Forsikrer oss om at metrikker med prefikset promtail_custom har dukket opp:

Samler logger med Loki

Konfigurerer Prometheus. Legger til jobben promtail:

- job_name: 'promtail'
 scrape_interval: 10s
 static_configs:
   - targets: ['promtail:9080']

Og tegner et diagram:

Samler logger med Loki

Slik kan man for eksempel finne de fire tregeste forespÞrslene. OgsÄ for disse metrikene kan man sette opp overvÄking.

Skalering

Loki kan vÊre i bÄde enkeltmodus (single binary mode) og i sharding-modus (horizontally-scalable mode). I det andre tilfellet kan den lagre data i skyen, og chunkene og indeksen lagres separat. I versjon 1.5 ble det implementert muligheten for lagring pÄ ett sted, men den anbefales ikke brukt i produksjon ennÄ.

Samler logger med Loki

Chunkene kan lagres i S3-kompatibel lagring, og for lagring av indekser kan man bruke horisontalt skalerbare databaser: Cassandra, BigTable eller DynamoDB. Andre deler av Loki – Distributors (for skriving) og Querier (for forespĂžrsel) – er stateless og kan ogsĂ„ skaleres horisontalt.

PĂ„ DevOpsDays Vancouver 2019 sa en av deltakerne, Callum Styan, at med Loki har prosjektet hans petabyte av logger med en indeks pĂ„ mindre enn 1% av totalstĂžrrelsen: “How Loki Correlates Metrics and Logs — And Saves You Money”.

Sammenligning av Loki og ELK

IndeksstĂžrrelse

For testing av den resulterende indeksstÞrrelsen tok jeg logger fra nginx-containeren, for hvilken Pipeline ble konfigurert som nevnt over. Filen med logger inneholdt 406 624 linjer med en total stÞrrelse pÄ 109 MB. Loggene ble generert over en time, omtrent 100 oppfÞringer i sekundet.

Eksempel pÄ to linjer fra loggen:

Samler logger med Loki

Ved indeksering i ELK ga dette en indeksstÞrrelse pÄ 30,3 MB:

Samler logger med Loki

I tilfelle av Loki ga dette omtrent 128 KB med indekser og omtrent 3,8 MB med data i blokker. Det er verdt Ä merke seg at loggen ble kunstig generert og ikke skilte seg ut med stor variasjon av data. Enkel gzip pÄ den opprinnelige Docker JSON-loggen med data ga en komprimering pÄ 95,4 %, og med tanke pÄ at det kun ble sendt renset nginx-logg til Loki, er komprimeringen ned til 4 MB forstÄelig. Det totale antallet unike verdier for etikettene i Loki var 35, noe som forklarer den lille stÞrrelsen pÄ indeksen. For ELK ble loggen ogsÄ renset. Dermed komprimerte Loki de opprinnelige dataene med 96 %, mens ELK gjorde det med 70 %.

Minneforbruk

Samler logger med Loki

Sammenlignet med hele Prometheus-stakken og ELK, sÄ "spiser" Loki flere ganger mindre. Det er klart at en tjeneste skrevet i Go bruker mindre enn en tjeneste skrevet i Java, og sammenligningen mellom JVM Heap-stÞrrelsen til Elasticsearch og den tildelte minnen for Loki er feilaktig, men det er likevel verdt Ä merke seg at Loki bruker betydelig mindre minne. Dets fordel nÄr det gjelder prosessorkraft er ikke sÄ Äpenbar, men finnes fortsatt.

Hastighet

Loki "fortÊrer" logger raskere. Hastigheten avhenger av mange faktorer - hva slags logger det er, hvor intrikate vi parser dem, nettverket, disken, osv. - men den er definitivt hÞyere enn for ELK (i min test omtrent det dobbelte). Dette forklares med at Loki legger mye mindre data i indeksen, og dermed bruker mindre tid pÄ indeksering. NÄr det gjelder sÞkehastighet er situasjonen motsatt: Loki sakker betydelig bak pÄ data som overstiger noen gigabyte, mens hastigheten for ELK ikke avhenger av datastÞrrelsen.

Jeg mener, at det er en fornÞjelse at arbejde med logs i R80. Vi har en meget smart sÞgestreng, der giver os mulighed for at "skÊre" bÄde pÄ vilkÄrlig tekst, blade og andre indeksérbare parametre som kilde, destination, handling osv.

Loki er betydelig bak ELK nÄr det gjelder muligheter for Ä sÞke i logger. Grep med regulÊre uttrykk er kraftig, men det kan ikke mÄle seg med en mer sofistikert database. Mangelen pÄ range-spÞrsmÄl, aggregasjon kun etter etiketter, samt umuligheten av Ä sÞke uten etiketter - alt dette begrenser oss i sÞket etter informasjon av interesse i Loki. Dette betyr ikke at man ikke kan finne noe med Loki, men det definerer arbeidsflyten for logger, hvor man fÞrst finner problemer pÄ Grafana-diagrammer, og deretter sÞker etter hva som skjedde i loggene basert pÄ disse etikettene.

Grensesnittet

For det fĂžrste ser det bra ut (beklager, jeg kunne ikke la vĂŠre). Grafana har et vakkert grensesnitt, men Kibana er langt mer funksjonell.

Fordeler og ulemper med Loki

Blant fordelene er at Loki integreres med Prometheus, dermed fÄr vi metrikker og varsling ut av boksen. Det er praktisk for innsamling og lagring av logger fra Kubernetes Pods, da det arver service discovery fra Prometheus og automatisk legger pÄ etiketter.

Ulempene er svak dokumentasjon. Noen ting, som spesifikasjoner og funksjoner i Promtail, oppdaget jeg bare under arbeid med koden, godt at det er open-source. En annen ulempe er dÄrlige parsing-muligheter. For eksempel kan ikke Loki parse multiline-logger. En annen svakhet er at Loki er en relativt ung teknologi (release 1.0 kom i november 2019).

Konklusjon

Loki er en 100% interessant teknologi som er egnet for smÄ og mellomstore prosjekter, og lar deg lÞse mange oppgaver knyttet til loggaggregasjon, loggsÞk, overvÄkning og logganalyse.

Vi bruker ikke Loki i Badoo, siden vi har en ELK-stack som fungerer for oss og som over mange Är har fÄtt en rekke tilpassede lÞsninger. For oss er problemet loggsÞk. Med nesten 100 GB logger per dag, er det viktig for oss Ä kunne finne alt og litt mer, og gjÞre det raskt. For Ä lage grafer og overvÄking bruker vi andre lÞsninger som er tilpasset vÄre behov og integrert med hverandre. Loki-stack har merkbare fordeler, men den vil ikke gi oss mer enn det vi har, og fordelene vil helt sikkert ikke oppveie kostnadene ved migrasjonen.

Og selv om det etter undersÞkelsen ble klart at vi ikke kan bruke Loki, hÄper vi at dette innlegget vil hjelpe deg i valget.

Repository med koden som ble brukt i artikkelen, finnes here.

Kilde: habr.com

KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster