Kubernetes completo da zero su Raspberry Pi

Kubernetes completo da zero su Raspberry Pi

Recentemente, una nota azienda ha annunciato che sta trasferendo la sua linea di laptop sull'architettura ARM. Sentendo questa notizia, ho ricordato che, mentre controllavo nuovamente i prezzi su EC2 in AWS, ho notato i Graviton con un prezzo molto interessante. Il trucco, ovviamente, era che si trattava di ARM. Allora non mi era nemmeno venuto in mente che ARM fosse qualcosa di piuttosto serio...

Per me, quest'architettura è stata sempre associata a mobile e altre cosette IoT. Server "veri" su ARM… è un po' insolito, persino strano... Tuttavia, una nuova idea si è installata nella mia mente, quindi uno dei fine settimana ho deciso di controllare cosa si può eseguire oggi su ARM. E per questo ho pensato di partire da qualcosa di familiare e vicino: un cluster Kubernetes. Non un qualsiasi "cluster", ma uno "serio", per farlo sembrare il più simile possibile a quello che sono abituato a vedere in produzione.

Secondo il mio piano, il cluster deve essere accessibile da internet, deve eseguire un'app web e avere almeno un sistema di monitoraggio. Per realizzare questa idea, saranno necessari un paio (o più) di Raspberry Pi non inferiori al modello 3B+. AWS potrebbe essere una base per gli esperimenti, ma ero particolarmente interessato ai „raspberry“ (che erano comunque fermi senza fare nulla). Quindi, installeremo un cluster Kubernetes su di essi con Ingress, Prometheus e Grafana.

Preparazione dei "raspberry"

Installazione del sistema operativo e SSH

Non mi sono troppo complicato nella scelta del sistema operativo da installare: ho semplicemente scelto l'ultima versione di Raspberry Pi OS Lite con sito ufficiale. Qui è disponibile la documentazione per l'installazione, tutte le operazioni devono essere eseguite su tutti i nodi del futuro cluster. Successivamente, dovranno essere eseguite le seguenti operazioni (anch'esse su tutti i nodi).

Collegando un monitor e una tastiera, è necessario configurare in anticipo la rete e SSH:

  1. Per il funzionamento del cluster, è necessario che il master abbia un indirizzo IP statico, mentre per i nodi di lavoro è a discrezione. Ho preferito indirizzi statici ovunque per facilità di configurazione.
  2. L'indirizzo statico può essere configurato nel sistema operativo (nel file /etc/dhcpcd.conf c'è un esempio appropriato) o fissando il lease nel server DHCP del router utilizzato (nel mio caso, il router domestico).
  3. Il server SSH viene semplicemente attivato in raspi-config (interfacing options → ssh).

Dopo di che, è possibile accedere tramite SSH (di default l'username è pi, e la password è raspberry o quella che è stata cambiata) e proseguire con le configurazioni.

Altre configurazioni

  1. Imposteremo il nome dell'host. Nel mio esempio useremo pi-control e pi-worker.
  2. Verifichiamo che il filesystem sia esteso su tutto il disco (df -h /). Se necessario, può essere esteso usando raspi-config.
  3. Cambiamo la password dell'utente predefinito in raspi-config.
  4. Disabilitiamo il file di swap (è una richiesta di Kubernetes; se ti interessano i dettagli su questo argomento, vedi issue #53533):
    dphys-swapfile swapoff
    systemctl disable dphys-swapfile
  5. Aggiorniamo i pacchetti alle versioni più recenti:
    apt-get update && apt-get dist-upgrade -y
  6. Installiamo Docker e pacchetti aggiuntivi:
    apt-get install -y docker docker.io apt-transport-https curl bridge-utils iptables-persistent

    Durante l'installazione iptables-persistent sarà necessario salvare le impostazioni di iptables per ipv4, e nel file /etc/iptables/rules.v4 aggiungere regole nella catena FORWARD, in questo modo:

    # Generated by xtables-save v1.8.2 on Sun Jul 19 00:27:43 2020
    *filter
    :INPUT ACCEPT [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    -A FORWARD -s 10.1.0.0/16  -j ACCEPT
    -A FORWARD -d 10.1.0.0/16  -j ACCEPT
    COMMIT
  7. Rimane solo da riavviarsi.

Ora è tutto pronto per l'installazione del cluster Kubernetes.

Installazione di Kubernetes

In questa fase ho volutamente rinviato tutte le mie e le nostre risorse aziendali per l'automazione dell'installazione e della configurazione del cluster K8s. Invece, utilizziamo la documentazione ufficiale con kubernetes.io (leggermente arricchita da commenti e abbreviazioni).

Aggiungiamo il repository Kubernetes:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update

Successivamente, nella documentazione viene proposto di installare CRI (container runtime interface). Poiché Docker è già installato, procediamo e installiamo i componenti principali:

sudo apt-get install -y kubelet kubeadm kubectl kubernetes-cni

Nella fase di installazione dei componenti principali ho subito aggiunto kubernetes-cni, che è necessario per il funzionamento del cluster. C'è un aspetto importante: il pacchetto kubernetes-cni per qualche motivo non crea la directory predefinita per le impostazioni degli interfacce CNI, quindi ho dovuto crearla manualmente:

mkdir -p /etc/cni/net.d

Per il funzionamento del backend di rete, di cui parleremo più avanti, è necessario installare un plugin per CNI. Ho scelto il plugin portmap che mi è familiare e comprensibile (puoi vedere l'elenco completo in documentazione):

curl -sL https://github.com/containernetworking/plugins/releases/download/v0.7.5/cni-plugins-arm-v0.7.5.tgz | tar zxvf - -C /opt/cni/bin/ ./portmap

Configurazione Kubernetes

Nodo con control plane

L'installazione stessa del cluster è abbastanza semplice. E per accelerare questo processo e verificare che le immagini Kubernetes siano disponibili, puoi eseguire in anticipo:

kubeadm config images pull

Ora eseguiamo l'installazione stessa: inizializziamo il control plane del cluster:

kubeadm init --pod-network-cidr=10.1.0.0/16 --service-cidr=10.2.0.0/16 --upload-certs

Si prega di notare che le sottoreti per i servizi e i pod non devono sovrapporsi tra loro e con le reti esistenti.

Alla fine ci verrà mostrato un messaggio dicendo che tutto è a posto e ci verrà anche indicato come collegare i nodi di lavoro al control plane:

Il tuo control-plane Kubernetes è stato inizializzato con successo! Per iniziare a utilizzare il tuo cluster, devi eseguire quanto segue come utente normale:
 mkdir -p $HOME/.kube
 sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
 sudo chown $(id -u):$(id -g) $HOME/.kube/config
Ora dovresti distribuire una rete pod nel cluster.
Esegui "kubectl apply -f [podnetwork].yaml" con una delle opzioni elencate su:
 https://kubernetes.io/docs/concepts/cluster-administration/addons/
Ora puoi unire un numero qualsiasi di nodi del control-plane eseguendo il seguente comando su ciascuno come root:
 kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 
   --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050 
   --contrl-plane --certificate-key 72a3c0a14c627d6d7fdade1f4c8d7a41b0fac31b1faf0d8fdf9678d74d7d2403
Si prega di notare che la certificate-key fornisce accesso a dati sensibili del cluster, mantienila segreta!
Come misura di sicurezza, gli uploaded-certs verranno eliminati in due ore; Se necessario, puoi utilizzare
"kubeadm init phase upload-certs --upload-certs" per ricaricare i certificati successivamente.
Poi puoi unire un numero qualsiasi di nodi di lavoro eseguendo quanto segue su ciascuno come root:
kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 
   --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050

Procediamo con le raccomandazioni per l'aggiunta del config per l'utente. E consiglio anche di aggiungere subito il completamento automatico per kubectl:

 kubectl completion bash > ~/.kube/completion.bash.inc
 printf "
 # Completamento della shell Kubectl
 source '$HOME/.kube/completion.bash.inc'
 " >> $HOME/.bash_profile
 source $HOME/.bash_profile

A questo punto è già possibile vedere il primo nodo nel cluster (anche se non è ancora pronto):

root@pi-control:~# kubectl get no
NAME         STATUS     ROLES    AGE   VERSION
pi-control   NotReady   master   29s   v1.18.6

Configurazione della rete

Successivamente, come detto nel messaggio dopo l'installazione, sarà necessario installare una rete nel cluster. La documentazione offre una scelta tra Calico, Cilium, contiv-vpp, Kube-router e Weave Net… Qui mi sono discostato dalle istruzioni ufficiali e ho scelto un'opzione a me più familiare: flannel in modalità host-gw (maggiore dettaglio sui backend disponibili è disponibile in documentazione del progetto).

Installarlo nel cluster è piuttosto semplice. Prima di tutto — scarichiamo i manifesti:

wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Poi cambiamo nelle impostazioni il tipo da vxlan in host-gw:

sed -i 's/vxlan/host-gw/' kube-flannel.yml

… e la sottorete dei pod — dal valore predefinito a quello specificato durante l'inizializzazione del cluster:

sed -i 's#10.244.0.0/16#10.1.0.0/16#' kube-flannel.yml

Dopo di che creiamo le risorse:

kubectl create -f kube-flannel.yml

Fatto! Dopo un po' il primo nodo K8s passerà allo stato Pronto:

NAME         STATUS   ROLES    AGE   VERSION
pi-control   Ready    master   2m    v1.18.6

Aggiunta del nodo di lavoro

Ora è possibile aggiungere un worker. Per fare ciò, dopo aver installato Kubernetes seguendo lo scenario descritto sopra, è sufficiente eseguire il comando ricevuto in precedenza:

kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 
    --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050

A questo punto possiamo considerare il cluster pronto:

root@pi-control:~# kubectl get no
NOME         STATO   RUOLO    ETÀ    VERSIONE
pi-control   Pronto    master   28m    v1.18.6
pi-worker    Pronto       2m8s   v1.18.6

Avevo solo due Raspberry Pi a disposizione, quindi non volevo dare uno di essi solo al control plane. Pertanto, ho rimosso il taint installato automaticamente dal nodo pi-control eseguendo:

root@pi-control:~# kubectl edit node pi-control

… e ho rimosso le righe:

 - effect: NoSchedule
   key: node-role.kubernetes.io/master

Riempimento del cluster con il minimo necessario

Innanzitutto avremo bisogno di Helm. Certo, è possibile fare tutto anche senza di esso, ma Helm consente di configurare alcuni componenti secondo le proprie esigenze senza modificare i file. E in effetti è semplicemente un file binario, che "non richiede nulla".

Quindi, accediamo a helm.sh nella sezione docs/installation ed eseguiamo il comando da lì:

curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

Dopo di che aggiungiamo il repository dei chart:

helm repo add stable https://kubernetes-charts.storage.googleapis.com/

Ora installeremo i componenti infrastrutturali secondo il progetto:

  • Ingress controller;
  • Prometheus;
  • Grafana;
  • cert-manager.

Ingress controller

Il primo componente — Ingress controller — si installa piuttosto facilmente ed è pronto per l'uso "out of the box". È sufficiente andare nella sezione bare-metal del sito ed eseguire il comando di installazione da lì:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/baremetal/deploy.yaml

Tuttavia, a questo punto il "raspberry" ha cominciato a faticare e a confrontarsi con l'IOPS del disco. Il problema è che insieme all'Ingress controller vengono installate molte risorse, vengono effettuate molte richieste all'API e, di conseguenza, molti dati vengono scritti in etcd. Insomma, o la scheda di memoria di classe 10 non è molto performante, o le schede SD non sono sufficienti per un carico del genere. Tuttavia, dopo circa 5 minuti tutto si è avviato.

È stato creato un namespace e in esso è apparso il controller e tutto ciò di cui ha bisogno:

root@pi-control:~# kubectl -n ingress-nginx get pod
NOME                                        PRONTO   STATO      RIAVVII   ETÀ
ingress-nginx-admission-create-2hwdx        0/1     Completato   0          31s
ingress-nginx-admission-patch-cp55c         0/1     Completato   0          31s
ingress-nginx-controller-7fd7d8df56-68qp5   1/1     In esecuzione     0          48s

Prometheus

I seguenti due componenti sono piuttosto semplici da installare tramite Helm dal chart repo.

Troviamo Prometheus, creiamo un namespace e installiamolo in esso:

helm search repo stable | grep prometheus
kubectl create ns monitoring
helm install prometheus --namespace monitoring stable/prometheus --set server.ingress.enabled=True --set server.ingress.hosts={"prometheus.home.pi"}

Per impostazione predefinita, Prometheus richiede 2 dischi: uno per i dati di Prometheus stesso e uno per i dati di AlertManager. Poiché non è stata creata una storage class nel cluster, i dischi non verranno richiesti e i pod non si avvieranno. Per le installazioni bare metal di Kubernetes di solito utilizziamo Ceph rbd, ma nel caso di Raspberry Pi è chiaramente eccessivo.

Pertanto, creiamo un semplice storage locale su hostpath. I manifesti PV (persistent volume) per prometheus-server e prometheus-alertmanager sono combinati nel file prometheus-pv.yaml in I repository Git con esempi per l'articolo. La directory per i PV deve essere creata previamente sul disco del nodo a cui vogliamo collegare Prometheus: nell'esempio è stato specificato nodeAffinity per hostname pi-worker e in esso sono state create le directory /data/localstorage/prometheus-server e /data/localstorage/prometheus-alertmanager.

Scarichiamo (cloniamo) il manifesto e lo aggiungiamo a Kubernetes:

kubectl create -f prometheus-pv.yaml

A questo punto ho affrontato per la prima volta un problema con l'architettura ARM. Kube-state-metrics, che viene installato per impostazione predefinita nel chart di Prometheus, si è rifiutato di avviarsi. Ha restituito un errore:

root@pi-control:~# kubectl -n monitoring logs prometheus-kube-state-metrics-c65b87574-l66d8
standard_init_linux.go:207: exec user process caused "exec format error"

Il fatto è che per kube-state-metrics viene utilizzato un'immagine del progetto CoreOS, che non viene compilata per ARM:

kubectl -n monitoring get deployments.apps prometheus-kube-state-metrics -o=jsonpath={.spec.template.spec.containers[].image}
quay.io/coreos/kube-state-metrics:v1.9.7

Ho dovuto cercare un po' e trovare, ad esempio, quest'immagine. Per utilizzarla, aggiorniamo il rilascio specificando quale immagine utilizzare per kube-state-metrics:

helm upgrade prometheus --namespace monitoring stable/prometheus --set server.ingress.enabled=True --set server.ingress.hosts={"prometheus.home.pi"} --set kube-state-metrics.image.repository=carlosedp/kube-state-metrics --set kube-state-metrics.image.tag=v1.9.6

Controlliamo che tutto sia avviato:

root@pi-control:~# kubectl -n monitoring get po
NOME                                             PRONTO   STATO              RIAVVÍ   ETÀ
prometheus-alertmanager-df65d99d4-6d27g          2/2     In esecuzione      0          5m56s
prometheus-kube-state-metrics-5dc5fd89c6-ztmqr   1/1     In esecuzione      0          5m56s
prometheus-node-exporter-49zll                   1/1     In esecuzione      0          5m51s
prometheus-node-exporter-vwl44                   1/1     In esecuzione      0          4m20s
prometheus-pushgateway-c547cfc87-k28qx           1/1     In esecuzione      0          5m56s
prometheus-server-85666fd794-z9qnc               2/2     In esecuzione      0          4m52s

Grafana e cert-manager

Per i grafici e i dashboard installiamo Grafana:

helm install grafana --namespace monitoring stable/grafana --set ingress.enabled=true --set ingress.hosts={"grafana.home.pi"}

Alla fine dell'output verrà mostrato come ottenere la password di accesso:

kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

Per ordinare i certificati installeremo cert-manager. Per la sua installazione ci rivolgeremo a documentazione, che offre i comandi pertinenti per Helm:

helm repo add jetstack https://charts.jetstack.io

helm install 
  cert-manager jetstack/cert-manager 
  --namespace cert-manager 
  --version v0.16.0 
  --set installCRDs=true

Per certificati self-signed per uso domestico, questo è più che sufficiente. Se invece è necessario ottenere lo stesso Let’s Encrypt, è necessario configurare anche il cluster issuer. I dettagli su questo possono essere trovati nel nostro articolo «Certificati SSL da Let’s Encrypt con cert-manager in Kubernetes».

Io ho scelto l'opzione da esempio nella documentazione, decidendo che la variante di staging di LE sarebbe stata sufficiente. Modifichiamo nell'esempio l'e-mail, salviamo nel file e aggiungiamo al cluster (cert-manager-cluster-issuer.yaml):

kubectl create -f cert-manager-cluster-issuer.yaml

Ora possiamo richiedere un certificato, ad esempio, per Grafana. Per questo sarà necessario un dominio e l'accesso al cluster dall'esterno. Ho un dominio e ho configurato il traffico inoltrando le porte 80 e 443 sul mio router domestico in base al servizio creato dell'ingress-controller:

kubectl -n ingress-nginx get svc
NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.2.206.61            80:31303/TCP,443:30498/TCP   23d

La porta 80 in questo caso è tradotta in 31303, e la 443 in 30498. (Le porte vengono generate casualmente, quindi le vostre saranno diverse.)

Ecco un esempio di certificato (cert-manager-grafana-certificate.yaml):

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: grafana
  namespace: monitoring
spec:
  dnsNames:
    - grafana.home.pi
  secretName: grafana-tls
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-staging

Aggiungiamolo al cluster:

kubectl create -f cert-manager-grafana-certificate.yaml

Dopo questo apparirà la risorsa Ingress, attraverso la quale avverrà la validazione da parte di Let’s Encrypt:

root@pi-control:~# kubectl -n monitoring get ing
NAME                        CLASS    HOSTS                        ADDRESS         PORTS   AGE
cm-acme-http-solver-rkf8l      grafana.home.pi      192.168.88.31   80      72s
grafana                        grafana.home.pi      192.168.88.31   80      6d17h
prometheus-server              prometheus.home.pi   192.168.88.31   80      8d

Dopo che la validazione sarà completata, vedremo che la risorsa certificate è pronta, e nel segreto sopra indicato grafana-tls si trovano il certificato e la chiave. Possiamo subito controllare chi ha emesso il certificato:

root@pi-control:~# kubectl -n monitoring get certificate
NAME      READY   SECRET        AGE
grafana   True    grafana-tls   13m

root@pi-control:~# kubectl -n monitoring get secrets grafana-tls -ojsonpath="{.data['tls.crt']}" | base64 -d | openssl x509 -issuer -noout
issuer=CN = Fake LE Intermediate X1

Torniamo a Grafana. Dobbiamo apportare alcune modifiche al suo rilascio Helm, modificando le impostazioni per TLS in base al certificato creato.

Per questo, scarichiamo il chart, lo modifichiamo e aggiorniamo dalla directory locale:

helm pull --untar stable/grafana

Modifichiamo il file grafana/values.yaml parametri TLS:

  tls:
    - secretName: grafana-tls
      hosts:
        - grafana.home.pi

Inoltre, possiamo subito configurare Prometheus installato come datasource:

datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
    - name: Prometheus
      type: prometheus
      url: http://prometheus-server:80
      access: proxy
      isDefault: true

Ora aggiorniamo il chart di Grafana dalla directory locale:

helm upgrade grafana --namespace monitoring ./grafana --set ingress.enabled=true --set ingress.hosts={"grafana.home.pi"}

Controlliamo che in Ingress grafana sia stato aggiunto il porto 443 e ci sia accesso tramite HTTPS:

root@pi-control:~# kubectl -n monitoring get ing grafana
NAME CLASS HOSTS ADDRESS PORTS AGE
grafana  grafana.home.pi 192.168.88.31 80, 443 63m

root@pi-control:~# curl -kI https://grafana.home.pi
HTTP/2 302
server: nginx/1.19.1
date: Tue, 28 Jul 2020 19:01:31 GMT
content-type: text/html; charset=utf-8
cache-control: no-cache
expires: -1
location: /login
pragma: no-cache
set-cookie: redirect_to=; Path=/; HttpOnly; SameSite=Lax
x-frame-options: deny
strict-transport-security: max-age=15724800; includeSubDomains

Per dimostrare Grafana in azione, puoi scaricare e aggiungere un dashboard per kube-state-metrics. Ecco come appare:

Kubernetes completo da zero su Raspberry Pi

Ti consiglio anche di aggiungere un dashboard per node exporter: mostrerà in dettaglio cosa succede con le "raspberries" (carico CPU, utilizzo della memoria, rete, disco, ecc.).

Dopo questo, considero che il cluster è pronto per accogliere e avviare applicazioni!

Nota sulla compilazione

Per la compilazione di applicazioni per architettura ARM ci sono almeno due opzioni. In primo luogo, è possibile compilare su un dispositivo ARM. Tuttavia, guardando l'utilizzo attuale di due Raspberry Pi, ho capito che non reggeranno nemmeno la compilazione. Quindi ho ordinato una nuova Raspberry Pi 4 (è più potente e ha 4 GB di RAM) — ho intenzione di compilare su di essa.

Il secondo opzione è la compilazione di un'immagine Docker multiarchitettura su una macchina più potente. Per questo c'è l'estensione docker buildx. Se l'applicazione è in un linguaggio compilato, sarà necessaria una cross-compilazione per ARM. Non descriverò tutte le impostazioni per questo percorso, poiché richiederebbe un articolo a parte. Utilizzando questo approccio, è possibile ottenere immagini "universali": Docker, eseguito su una macchina ARM, caricherà automaticamente l'immagine corrispondente all'architettura.

Conclusione

L'esperimento condotto ha superato tutte le mie aspettative: [almeno] un Kubernetes "vaniglia" con la base necessaria si comporta bene su ARM, e durante la sua configurazione sono emersi solo un paio di dettagli.

I Raspberry Pi 3B+ gestiscono carichi di lavoro sulla CPU, ma le loro schede SD sono un chiaro collo di bottiglia. I miei colleghi hanno suggerito che in alcune versioni esiste la possibilità di avviarsi da USB, dove è possibile collegare un SSD: allora la situazione probabilmente migliorerà.

Ecco un esempio di carico della CPU durante l'installazione di Grafana:

Kubernetes completo da zero su Raspberry Pi

Per esperimenti e "per provare", a mio avviso, un cluster Kubernetes sui "Raspberry" trasmette molto meglio le sensazioni di utilizzo rispetto a Minikube, perché tutti i componenti del cluster si installano e funzionano "in modo professionale".

C'è l'idea di aggiungere al cluster l'intero ciclo CI/CD, realizzato interamente su Raspberry Pi. Sarei anche felice se qualcuno condividesse la propria esperienza nella configurazione di K8s su AWS Graviton.

P.S. Sì, il "production" potrebbe essere più vicino di quanto pensassi:

Kubernetes completo da zero su Raspberry Pi

P.P.S.

Leggi anche nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster