NĂ€ide sĂŒndmustest juhitud rakendusest, mis pĂ”hineb veebihookidel Mail.ru Cloud Solutions objektiteenuses S3

NĂ€ide sĂŒndmustest juhitud rakendusest, mis pĂ”hineb veebihookidel Mail.ru Cloud Solutions objektiteenuses S3
Rube Goldberg kohvimasin

SĂŒndmustel pĂ”hinev arhitektuur suurendab kasutatavate ressursside ökonoomsust, sest need aktiveeritakse ainult siis, kui need on vajalikud. On palju vĂ”imalusi, kuidas seda ellu viia ja mitte luua tĂ€iendavaid pilveolukordi töötaja-rakendustena. TĂ€na rÀÀgin mitte FaaS-st, vaid veebihĂŒĂŒdest. NĂ€itan nĂ€idisnĂ€idet sĂŒndmuste töötlemisest veebihĂŒĂŒdede abil objektide salvestamisel.

MĂ”ned sĂ”nad objektide salvestamisest ja veebihĂŒĂŒtest. Objektide salvestamine vĂ”imaldab salvestada igasuguseid andmeid pilves objektidena, mis on kergesti ligipÀÀsetavad S3 vĂ”i muu API kaudu (sĂ”ltuvalt rakendusest) HTTP/HTTPS kaudu. VeebihĂŒĂŒd (webhook) on kasutaja mÀÀratud vastavad vĂ€ljakutsed HTTP kaudu. Need kĂ€ivitatakse tavaliselt sĂŒndmuse korral, nĂ€iteks koodi saatmine reposse vĂ”i kommentaari avaldamine blogis. Kui sĂŒndmus toimub, saadab algne sait HTTP-pĂ€ringu URL-ile, mis on mÀÀratud veebihĂŒĂŒdeste jaoks. Selle tulemusena saab teha nii, et ĂŒhel saidil toimuvad sĂŒndmused kutsuvad esile tegevusi teisel saidil. Kui algne sait on objektide salvestamine, siis on sĂŒndmusteks selle sisu muutumine.wiki.

Lihtsate juhtumite nÀited, kus sellist automatiseerimist kasutada:

  1. Kopeerimine kÔigist objektidest teise pilvesalvestusse. Kopeerimine peab toimuma "realses ajas" iga faili lisamisel vÔi muutmisel.
  2. Graafiliste failide miniatuure kopeeritakse automaatselt, lisades fotodele veesilgeid ja teisi muudatusi.
  3. Teate saatmine uute dokumentide saabumisest (nĂ€iteks jaotatud raamatupidamisteenus laadib pilve aruanded ja rahapesu jĂ€lgimise teenus saab teateid uute aruannete kohta ning kontrollib ja analĂŒĂŒsib neid).
  4. Veidi keerulisemates juhtumites vĂ”ib nĂ€iteks tekkida Kubernetese pĂ€ringu vormimine, mis loob vajalike konteineritega alamÔÔga, edastab sellele ĂŒlesande parameetrid ja pĂ€rast töötlemist sulgeb konteineri.

NĂ€idisena teeme ĂŒlesande 1 variandi, kus Mail.ru Cloud Solutions (MCS) objektide salvestamise muudatused sĂŒnkroniseeritakse veebihĂŒĂŒdede kaudu AWS-i objektide salvestusse. Reaalsete koormuste korral tuleks ette nĂ€ha asĂŒnkroonne töö, registreerides veebihĂŒĂŒde jĂ€rjekorras, kuid Ă”ppeĂŒlesande jaoks teeme selle siiski ilma selleta.

Töö skeem

Koostöö protokoll on ĂŒksikasjalikult kirjeldatud S3 veebihookide juhendis MCS. Töö skeemis on jĂ€rgmised elemendid:

  • Avaldamisteenus, mis asub S3 salvestusruumi poolel ja avaldab HTTP pĂ€ringuid webnhook'i kĂ€ivitamisel.
  • Veebihookide vastuvĂ”tuseerver, mis kuulab avaldamisteenuse pĂ€ringuid HTTP kaudu ja viib ellu vastavaid toiminguid. Serveri vĂ”ib kirjutada mistahes keeles, meie nĂ€ites kirjutame serveri Go-s.

Veebihookide realiseerimise tunnusjoon S3 API-s on veebihookide vastuvÔtuserveri registreerimine avaldamisteenuses. EelkÔige peab veebihookide vastuvÔtuserver kinnitama tellimuse avaldamisteenuse teadetele (teistes veebihookide rakendustes ei ole tellimuse kinnitamine tavaliselt vajalik).

Seega peab veebihookide vastuvÔtuserver toetama kahte peamist toimingut:

  • vastama avaldamisteenuse pĂ€ringule registri kinnitamiseks,
  • töötlema saabuvat sĂŒndmust.

Veebihookide vastuvÔtuseerveri seadistamine

Veebihookide vastuvÔtuserveri kÀivitamiseks on vajalik Linuxi server. Selles artiklis kasutame nÀitena virtuaalset instantsi, mille kÀivitame MCS-is.

Paigaldame vajalikud tarkvarad ja kÀivitame veebihookide vastuvÔtuserveri.

ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install git
Pakettide loendite lugemine... Valmis
SÔltuvuste puu loomine
Oleku teabe lugemine... Valmis
JĂ€rgmised paketid paigaldati automaatselt ja ei ole enam vajalikud:
 bc dns-root-data dnsmasq-base ebtables landscape-common liblxc-common 
liblxc1 libuv1 lxcfs lxd lxd-client python3-attr python3-automat 
python3-click python3-constantly python3-hyperlink
 python3-incremental python3-pam python3-pyasn1-modules 
python3-service-identity python3-twisted python3-twisted-bin 
python3-zope.interface uidmap xdelta3
Kasutage 'sudo apt autoremove', et need eemaldada.
Soovitatud paketid:
 git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui 
gitk gitweb git-cvs git-mediawiki git-svn
JĂ€rgmised UUE paketid installitakse:
 git
0 uuendatud, 1 uus installitud, 0 eemaldatud ja 46 mitte uuendatud.
Vaja on 3915 kB arhiive.
PĂ€rast seda toimingut kasutatakse 32.3 MB lisadiskiruumi.
Saadi:1 http://MS1.clouds.archive.ubuntu.com/ubuntu bionic-updates/main 
amd64 git amd64 1:2.17.1-1ubuntu0.7 [3915 kB]
Laaditi 3915 kB 1s jooksul (5639 kB/s)
Valitakse varem valimata pakett git.
(Lugedes andmebaasi ... 53932 faili ja katalooge on praegu installitud.)
Valmistub unpackimiseks .../git_12.17.1-1ubuntu0.7_amd64.deb ...
Vabastamine git (1:2.17.1-1ubuntu0.7) ...
Git (1:2.17.1-1ubuntu0.7) seadistamine ...

Kloonime kausta veebihookide vastuvÔtuserveri jaoks:

ubuntu@ubuntu-basic-1-2-10gb:~$ git clone
https://github.com/RomanenkoDenys/s3-webhook.git
Kloonimine 's3-webhook'...
remote: Objektide loendamine: 48, valmis.
remote: Objektide lugemine: 100% (48/48), valmis.
remote: Objektide kompressimine: 100% (27/27), valmis.
remote: Kokku 114 (delta 20), taaskasutatud 45 (delta 18), pakitud 66
Saabunud objektid: 100% (114/114), 23.77 MiB | 20.25 MiB/s, valmis.
Delta lahendamine: 100% (49/49), valmis.

KĂ€ivitame serveri:

ubuntu@ubuntu-basic-1-2-10gb:~$ cd s3-webhook/
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80

Tellimuse teenusele

Saate registreerida oma veebihoki vastuvÔtva serveri API kaudu vÔi veebiliidese kaudu. Lihtsuse huvides registreerime lÀbi veebiliidese:

  1. Liigume konteinerite sektsiooni haldusportaalis.
  2. KĂŒlastame konteinerit, mille jaoks seadistame webhookid, ja vajutame hammasrattaikoonile:

NĂ€ide sĂŒndmustest juhitud rakendusest, mis pĂ”hineb veebihookidel Mail.ru Cloud Solutions objektiteenuses S3

LĂ€heme vahelehele Webhooks ja vajutame Lisa:

NĂ€ide sĂŒndmustest juhitud rakendusest, mis pĂ”hineb veebihookidel Mail.ru Cloud Solutions objektiteenuses S3
TÀidame vÀljad:

NĂ€ide sĂŒndmustest juhitud rakendusest, mis pĂ”hineb veebihookidel Mail.ru Cloud Solutions objektiteenuses S3

ID — veebihoki nimi.

SĂŒndmus — milliseid sĂŒndmusi edastada. Oleme seadnud edastama kĂ”iki sĂŒndmusi, mis toimuvad failidega (lisamine ja eemaldamine).

URL — veebihoki vastuvĂ”tva serveri aadress.

Filtri eesliide/jĂ€relkiri — filter, mis vĂ”imaldab genereerida veebihokkesid ainult objektidele, mille nimed vastavad teatud reeglitele. NĂ€iteks, et veebihokk aktiveeruks ainult .png failide puhul, tuleb Filtri jĂ€relkiri kirjutada «png».

Praegu toetatakse veebihoki vastuvÔtva serveriga suhtlemiseks ainult porte 80 ja 443.

Vajutame Lisa hook ja nÀeme jÀrgmist:

NĂ€ide sĂŒndmustest juhitud rakendusest, mis pĂ”hineb veebihookidel Mail.ru Cloud Solutions objektiteenuses S3
Hook on lisatud.

Veebihoki vastuvÔttev server nÀitab logides huki registreerimise protsessi:

ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80
2020/06/15 12:01:14 [POST] sisselogimise HTTP pÀring aadressilt 
95.163.216.92:42530
2020/06/15 12:01:14 Saadi ajatemperatuur: 2020-06-15T15:01:13+03:00 TeemaArn: 
mcs5259999770|myfiles-ash|s3:ObjectCreated:*,s3:ObjectRemoved:* Token: 
E2itMqAMUVVZc51pUhFWSp13DoxezvRxkUh5P7LEuk1dEe9y URL: 
http://89.208.199.220/webhook
2020/06/15 12:01:14 Genereeriti vastuse allkiri: 
3754ce36636f80dfd606c5254d64ecb2fd8d555c27962b70b4f759f32c76b66d

Registreerimine on lÔpule viidud. JÀrgmises osas vaatame lÀhemalt veebihoki vastuvÔtva serveri töö algoritmi.

Veebihoki vastuvÔtva serveri kirjeldus

Meie nÀites on server kirjutatud Go-s. Vaatame selle pÔhialuseid.

package main

// Generoi hmac_sha256_hex
func HmacSha256hex(message string, secret string) string {
}

// Generoi hmac_sha256
func HmacSha256(message string, secret string) string {
}

// Saada tellimuse kinnitus
func SubscriptionConfirmation(w http.ResponseWriter, req *http.Request, body []byte) {
}

// Saada tellimuse kinnitus
func GotRecords(w http.ResponseWriter, req *http.Request, body []byte) {
}

// Tervisliku seisundi kontroll
func Ping(w http.ResponseWriter, req *http.Request) {
    // logi pÀring
    log.Printf("[%s] sisselogimine HTTP Ping pÀring aadressilt %sn", req.Method, req.RemoteAddr)
    fmt.Fprintf(w, "Pongn")
}

//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
}

func main() {

    // get command line args
    bindPort := flag.Int("port", 80, "number between 1-65535")
    bindAddr := flag.String("address", "", "ip address in dot format")
    flag.StringVar(&actionScript, "script", "", "external script to execute")
    flag.Parse()

    http.HandleFunc("/ping", Ping)
    http.HandleFunc("/webhook", Webhook)

log.Fatal(http.ListenAndServe(*bindAddr+":"+strconv.Itoa(*bindPort), nil))
}

Vaatame pÔhifunktsioone:

  • Ping() — marsruut, mis vastab URL/ping, lihtsaim ellujÀÀmise proovi teostus.
  • Webhook() — pĂ”himasruut, URL/vĂ”rgukitsendi töötleja:
    • kinnitab registreerimist avaldamisteenuses (ĂŒleminek funktsioonile SubscriptionConfirmation),
    • töötleb saabuvaid vĂ”rgukitsendeid (funktsioon Gotrecords).
  • HmacSha256 ja HmacSha256hex funktsioonid — HMAC-SHA256 algoritmi ja HMAC-SHA256 rakendused, mis vĂ€ljastavad 16-kohaliste arvude stringi allkirja arvutamiseks.
  • main — peamine funktsioon, mis töötleb kĂ€surea parameetreid ja registreerib URL-i töötlejad.

KĂ€surea parameetrid, mida server aktsepteerib:

  • -port — port, millel server kuulab.
  • -address — IP-aadress, millel server kuulab.
  • -script — vĂ€line programm, mida kutsub iga saadetud veebkild.

Vaatame tÀhelepanelikumalt mÔningaid funktsioone:

//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {

    // Read body
    body, err := ioutil.ReadAll(req.Body)
    defer req.Body.Close()
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }

    // log request
    log.Printf("[%s] incoming HTTP request from %sn", req.Method, req.RemoteAddr)
    // check if we got subscription confirmation request
    if strings.Contains(string(body), 
""Type":"SubscriptionConfirmation"") {
        SubscriptionConfirmation(w, req, body)
    } else {
        GotRecords(w, req, body)
    }

}

See funktsioon mÀÀrab, mis saabus — registreerimise kinnitamise taotlus vĂ”i veebkild. Nagu tuleneb dokumentatsioon, registreerimise kinnitamiseks saadetakse jĂ€rgmine Json struktuur Post taotluses:

POST http://test.com HTTP/1.1
x-amz-sns-messages-type: SubscriptionConfirmation
content-type: application/json

{
    "Timestamp":"2019-12-26T19:29:12+03:00",
    "Type":"SubscriptionConfirmation",
    "Message":"Olete valinud tellida teema $topic. Tellimuse kinnitamiseks peate vastama arvutatud allkirjaga",
    "TopicArn":"mcs2883541269|bucketA|s3:ObjectCreated:Put",
    "SignatureVersion":1,
    "Token":«RPE5UuG94rGgBH6kHXN9FUPugFxj1hs2aUQc99btJp3E49tA»
}

Sellele taotlusele tuleb vastata:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Kus allkiri arvutatakse jÀrgmiselt:

signature = hmac_sha256(url, hmac_sha256(TopicArn, 
hmac_sha256(Timestamp, Token)))

Kui saabub veebkild, nÀeb Post-taotlus vÀlja jÀrgmine:

POST  HTTP/1.1
x-amz-sns-messages-type: SubscriptionConfirmation

{ "Records":
    [
        {
            "s3": {
                "object": {
                    "eTag":"aed563ecafb4bcc5654c597a421547b2",
                    "sequencer":1577453615,
                    "key":"some-file-to-bucket",
                    "size":100
                },
            "configurationId":"1",
            "bucket": {
                "name": "bucketA",
                "ownerIdentity": {
                    "principalId":"mcs2883541269"}
                },
                "s3SchemaVersion":"1.0"
            },
            "eventVersion":"1.0",
            "requestParameters":{
                "sourceIPAddress":"185.6.245.156"
            },
            "userIdentity": {
                "principalId":"2407013e-cbc1-415f-9102-16fb9bd6946b"
            },
            "eventName":"s3:ObjectCreated:Put",
            "awsRegion":"ru-msk",
            "eventSource":"aws:s3",
            "responseElements": {
                "x-amz-request-id":"VGJR5rtJ"
            }
        }
    ]
}

Seega peab sÔltuvalt taotlusest aru saama, kuidas andmeid töödelda. Valisin indikaatoriks kirje "Type":"SubscriptionConfirmation", kuna see on olemas tellimuse kinnitamise pÀringus ja ei ole veebihookis. SÔltuvalt sellest, kas see kirje on POST-pÀringus olemas vÔi mitte, liigub programmi edasine tÀitmine kas funktsiooni SubscriptionConfirmation, vÔi funktsiooni GotRecords.

Funktsiooni SubscriptionConfirmation ei hakka me ĂŒksikasjalikult kĂ€sitlema, see on rakendatud pĂ”himĂ”tete alusel, mis on esitatud dokumentatsioon. Selle funktsiooni lĂ€htekoodi saab uurida projekti git-repositooriumis.

Funktsioon GotRecords analĂŒĂŒsib saabuvat pĂ€ringut ja kutsub iga Record objekti jaoks vĂ€lja vĂ€lise skripti (mille nimi oli edastatud parameetris -script) jĂ€rgmiste parameetritega:

  • konteineri nimi
  • objekti vĂ”ti
  • tegevus:
    • copy — kui algses pĂ€ringus on EventName = ObjectCreated | PutObject | PutObjectCopy
    • delete — kui algses pĂ€ringus on EventName = ObjectRemoved | DeleteObject

Seega, kui saadetakse POST-pĂ€ringuga webhook, nagu on kirjeldatud ĂŒle, ja parameeter -script=script.sh, siis skript kutsutakse jĂ€rgmiselt:

script.sh  bucketA some-file-to-bucket copy

Oluline on mÔista, et antud veebihookide vastuvÔtu server ei ole lÔpetatud tootmislahendus, vaid lihtsustatud nÀide vÔimalikust rakendusest.

Töö nÀide

Teeme peamise konteineri failide sĂŒnkroniseerimise MCS-is varukonteinerisse AWS-is. Peamine konteiner kannab nime myfiles-ash, varukonteiner — myfiles-backup (konteineri konfiguratsioon AWS-is jÀÀb selle artikli raamidest vĂ€lja). Vastavalt sellele, kui fail paigutatakse peamisse konteinerisse, peab selle koopia ilmuma varukoopia konteinerisse, ja kui see kustutatakse peamisest, peab see kustutatakse varukonteinerist.

Töötame konteineritega awscli utiliidiga, millega on ĂŒhilduv nii MCS-i pilvesalvestus kui ka AWS-i pilvesalvestus.

ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install awscli
Pakettide loendi lugemine... Valmis
SÔltuvuste puu ehitamine
Oleku teabe lugemine... Valmis
PĂ€rast seda toimingut kasutab 34.4 MB lisadiskiruumi.
Pakkimine awscli (1.14.44-1ubuntu1) ...
Awscli seadistamine (1.14.44-1ubuntu1) ...

Konfigureerime juurdepÀÀsu MCS S3 API-le:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile mcs
AWS Access Key ID [None]: hdywEPtuuJTExxxxxxxxxxxxxx
AWS Secret Access Key [None]: hDz3SgxKwXoxxxxxxxxxxxxxxxxxx
Vaikimisi piirkonna nimi [None]:
Vaikimisi vÀljundi formaat [None]:

Konfigureerime juurdepÀÀsu AWS S3 API-le:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile aws
AWS Access Key ID [None]: AKIAJXXXXXXXXXXXX
AWS Secret Access Key [None]: dfuerphOLQwu0CreP5Z8l5fuXXXXXXXXXXXXXXXX
Vaikimisi piirkonna nimi [None]:
Vaikimisi vÀljundi formaat [None]:

Kontrollime juurdepÀÀse:

AWS-le:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile aws
2020-07-06 08:44:11 myfiles-backup

MCS-i puhul peab kĂ€sule lisama —endpoint-url:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile mcs --endpoint-url 
https://hb.bizmrg.com
2020-02-04 06:38:05 databasebackups-0cdaaa6402d4424e9676c75a720afa85
2020-05-27 10:08:33 myfiles-ash

JuurdepÀÀs on saadud.

NĂŒĂŒd kirjutame sissetuleva hook'i töötlemiseks skripti, mille nimetame s3_backup_mcs_aws.sh

#!/bin/bash
# Require aws cli
# if file added — copy it to backup bucket
# if file removed — remove it from backup bucket
# Variables
ENDPOINT_MCS="https://hb.bizmrg.com"
AWSCLI_MCS=`which aws`" --endpoint-url ${ENDPOINT_MCS} --profile mcs s3"
AWSCLI_AWS=`which aws`" --profile aws s3"
BACKUP_BUCKET="myfiles-backup"

SOURCE_BUCKET="${1}"
SOURCE_FILE="${2}"
ACTION="${3}"

SOURCE="s3://${SOURCE_BUCKET}/${SOURCE_FILE}"
TARGET="s3://${BACKUP_BUCKET}/${SOURCE_FILE}"
TEMP="/tmp/${SOURCE_BUCKET}/${SOURCE_FILE}"

case ${ACTION} in
    "copy")
    ${AWSCLI_MCS} cp "${SOURCE}" "${TEMP}"
    ${AWSCLI_AWS} cp "${TEMP}" "${TARGET}"
    rm ${TEMP}
    ;;

    "delete")
    ${AWSCLI_AWS} rm ${TARGET}
    ;;

    *)
    echo "Usage: ${0} sourcebucket sourcefile copy/delete"
    exit 1
    ;;
esac

KĂ€ivitame serveri:

ubuntu@ubuntu-basic-1-2-10gb:~\/s3-webhook$ sudo .\/s3-webhook -port 80 -\nskript scripts\/s3_backup_mcs_aws.sh

Kontrollime, kuidas see töötab. LÀbi MCS veebiliidese lisame faili test.txt konna myfiles-ash. Logides konsoolis nÀeme, et veebihookide serverisse on tehtud pÀring:

2020\/07\/06 09:43:08 [POST] sissetulev HTTP pÀring aadressilt \n95.163.216.92:56612\ndownload: s3:\/\/myfiles-ash\/test.txt kuni ..\/..\/..\/tmp\/myfiles-ash\/test.txt\nupload: ..\/..\/..\/tmp\/myfiles-ash\/test.txt kuni \ns3:\/\/myfiles-backup\/test.txt

Kontrollime konna myfiles-backup sisu AWS-is:

ubuntu@ubuntu-basic-1-2-10gb:~\/s3-webhook$ aws s3 --profile aws ls \nmyfiles-backup\n2020-07-06 09:43:10       1104 test.txt

NĂŒĂŒd eemaldame veebiliidese kaudu faili konnast myfiles-ash.

Serveri logid:

2020\/07\/06 09:44:46 [POST] sissetulev HTTP pÀring aadressilt \n95.163.216.92:58224\ndelete: s3:\/\/myfiles-backup\/test.txt

Konna sisu:

ubuntu@ubuntu-basic-1-2-10gb:~\/s3-webhook$ aws s3 --profile aws ls \nmyfiles-backup\nubuntu@ubuntu-basic-1-2-10gb:~$

Fail on eemaldatud, ĂŒlesanne lahendatud.

KokkuvÔte ja ToDo

Kogu selles artiklis kasutatud kood asub minu hoidlas. Seal asuvad ka skriptide nÀidised ja nÀidised allkirjade arvestamiseks veebihookide registreerimiseks.

See kood on vaid nĂ€ide, kuidas saab S3-veebihooke oma tegevuses kasutada. Nagu ma alguses ĂŒtlesin, kui plaanite sellise serveri tootmises kasutamist, on vajalik vĂ€hemalt kirjutada server ĂŒmber asĂŒnkroonselt: sissetulevad veebihookid tuleks registreerida jĂ€rjekorras (RabbitMQ vĂ”i NATS) ja sealt edastada ning töödelda töötajate rakendustega. Muutumatult mĂŒrarikka sissetuleku korral vĂ”ib serveri ressursside puudumisega kokku puutuda. JĂ€rjekordade olemasolu vĂ”imaldab jagada serverit ja töötajaid, samuti lahendada probleeme, mis on seotud ĂŒlesannete kordamisega tĂ”rgete korral. Samuti soovitatakse logimist muuta detailsemaks ja standardiseeritumaks.

Edu!

Veel lugeda teemal:

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster