Come configurare la mappatura delle porte Docker per utilizzare Nginx come proxy upstream?


87

Aggiorna II

Ora è il 16 luglio 2015 e le cose sono cambiate di nuovo. Ho scoperto questo contenitore automagico da Jason Wilder : https://github.com/jwilder/nginx-proxye risolve questo problema in circa il tempo necessario al docker runcontenitore. Questa è ora la soluzione che sto usando per risolvere questo problema.

Aggiornare

Ora è luglio 2015 e le cose sono cambiate drasticamente per quanto riguarda i container Docker di rete. Ora ci sono molte offerte diverse che risolvono questo problema (in vari modi).

Dovresti usare questo post per acquisire una comprensione di base docker --linkdell'approccio alla scoperta dei servizi, che è tanto basilare quanto sembra, funziona molto bene e in realtà richiede meno balli fantasiosi rispetto alla maggior parte delle altre soluzioni. È limitato in quanto è abbastanza difficile collegare in rete i contenitori su host separati in un dato cluster e i contenitori non possono essere riavviati una volta collegati in rete, ma offre un modo rapido e relativamente semplice per collegare in rete i contenitori sullo stesso host. È un buon modo per avere un'idea di ciò che il software che probabilmente utilizzerai per risolvere questo problema sta effettivamente facendo sotto il cofano.

Inoltre, probabilmente vorrai dare un'occhiata anche al nascente Docker network, Hashicorp's consul, Weaveworks weave, Jeff Lindsay's progrium/consul&gliderlabs/registrator e Google's Kubernetes.

C'è anche il CoreOS offerte che utilizzano etcd, fleete flannel.

E se vuoi davvero organizzare una festa, puoi avviare un cluster per eseguire Mesosphere, o Deis, o Flynn.

Se sei nuovo nel networking (come me), allora dovresti tirare fuori gli occhiali da lettura, far apparire "Paint The Sky With Stars - The Best of Enya" sul Wi-Hi-Fi e farti una birra - sarà un po 'prima di capire veramente esattamente cosa stai cercando di fare. Suggerimento: Si sta cercando di attuare una Service Discovery Layernel vostro Cluster Control Plane. È un modo molto carino per trascorrere un sabato sera.

È molto divertente, ma vorrei aver dedicato del tempo per istruirmi meglio sul networking in generale prima di immergermi subito. Alla fine ho trovato un paio di post dei benevoli dei del Tutorial sull'oceano digitale: Introduction to Networking Terminologye Understanding ... Networking. Suggerisco di leggerli un paio di volte prima di immergerti.

Divertiti!



Post originale

Non riesco a capire la mappatura delle porte per i Dockercontenitori. Nello specifico come passare le richieste da Nginx a un altro contenitore, in ascolto su un'altra porta, sullo stesso server.

Ho un Dockerfile per un contenitore Nginx in questo modo:

FROM ubuntu:14.04
MAINTAINER Me <me@myapp.com>

RUN apt-get update && apt-get install -y htop git nginx

ADD sites-enabled/api.myapp.com /etc/nginx/sites-enabled/api.myapp.com
ADD sites-enabled/app.myapp.com /etc/nginx/sites-enabled/app.myapp.com
ADD nginx.conf /etc/nginx/nginx.conf

RUN echo "daemon off;" >> /etc/nginx/nginx.conf

EXPOSE 80 443

CMD ["service", "nginx", "start"]



E poi il api.myapp.comfile di configurazione appare così:

upstream api_upstream{

    server 0.0.0.0:3333;

}


server {

    listen 80;
    server_name api.myapp.com;
    return 301 https://api.myapp.com/$request_uri;

}


server {

    listen 443;
    server_name api.mypp.com;

    location / {

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_pass http://api_upstream;

    }

}

E poi un altro anche per app.myapp.com.

E poi corro:

sudo docker run -p 80:80 -p 443:443 -d --name Nginx myusername/nginx


E tutto funziona bene, ma le richieste non vengono trasmesse agli altri contenitori / porte. E quando eseguo ssh nel contenitore Nginx e ispeziono i log non vedo errori.

Qualsiasi aiuto?


1
Si prega di inserire il materiale per la risposta nella risposta, non nel corpo della domanda.
jscs

Risposte:


57

La risposta di @ T0xicCode è corretta, ma ho pensato di espandere i dettagli poiché in realtà mi ci sono volute circa 20 ore per ottenere finalmente l'implementazione di una soluzione funzionante.

Se stai cercando di eseguire Nginx nel proprio contenitore e usarlo come proxy inverso per bilanciare il carico di più applicazioni sulla stessa istanza del server, i passaggi che devi seguire sono i seguenti:

Collega i tuoi contenitori

Quando si utilizzano i docker runcontenitori, in genere inserendo uno script di shell User Data, è possibile dichiarare collegamenti a qualsiasi altro contenitore in esecuzione . Ciò significa che è necessario avviare i contenitori in ordine e solo questi ultimi possono collegarsi ai primi. Così:

#!/bin/bash
sudo docker run -p 3000:3000 --name API mydockerhub/api
sudo docker run -p 3001:3001 --link API:API --name App mydockerhub/app
sudo docker run -p 80:80 -p 443:443 --link API:API --link App:App --name Nginx mydockerhub/nginx

Quindi, in questo esempio, il APIcontenitore non è collegato a nessun altro, ma il Appcontenitore è collegato APIed Nginxè collegato a entrambi APIe App.

Il risultato di ciò sono le modifiche alle envvariabili e ai /etc/hostsfile che risiedono nei contenitori APIe App. I risultati sono così:

/ etc / hosts

L'esecuzione cat /etc/hostsall'interno del tuo Nginxcontainer produrrà quanto segue:

172.17.0.5  0fd9a40ab5ec
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3  App
172.17.0.2  API



ENV Vars

L'esecuzione envall'interno del tuo Nginxcontainer produrrà quanto segue:

API_PORT=tcp://172.17.0.2:3000
API_PORT_3000_TCP_PROTO=tcp
API_PORT_3000_TCP_PORT=3000
API_PORT_3000_TCP_ADDR=172.17.0.2

APP_PORT=tcp://172.17.0.3:3001
APP_PORT_3001_TCP_PROTO=tcp
APP_PORT_3001_TCP_PORT=3001
APP_PORT_3001_TCP_ADDR=172.17.0.3

Ho troncato molte delle variabili effettive, ma i valori sopra riportati sono i valori chiave di cui hai bisogno per proxy del traffico ai tuoi contenitori.

Per ottenere una shell per eseguire i comandi precedenti all'interno di un contenitore in esecuzione, utilizzare quanto segue:

sudo docker exec -i -t Nginx bash

Puoi vedere che ora hai sia /etc/hostsvoci di file envche variabili che contengono l'indirizzo IP locale per uno dei contenitori che erano collegati. Per quanto ne so, questo è tutto ciò che accade quando esegui contenitori con opzioni di collegamento dichiarate. Ma ora puoi utilizzare queste informazioni per configurare nginxall'interno del tuo Nginxcontenitore.



Configurazione di Nginx

È qui che diventa un po 'complicato e ci sono un paio di opzioni. Puoi scegliere di configurare i tuoi siti in modo che puntino a una voce nel /etc/hostsfile che dockerhai creato, oppure puoi utilizzare le ENVvariabili ed eseguire una sostituzione di stringa (io ho usato sed) sul tuo nginx.confe su qualsiasi altro file di configurazione che potrebbe essere nella tua /etc/nginx/sites-enabledcartella per inserire l'IP valori.



OPZIONE A: Configura Nginx usando ENV Vars

Questa è l'opzione che ho scelto perché non sono riuscito a far funzionare l' /etc/hostsopzione file. Proverò abbastanza presto l'opzione B e aggiornerò questo post con eventuali risultati.

La differenza fondamentale tra questa opzione e l'utilizzo /etc/hostsdell'opzione file è il modo in cui scrivi il tuo Dockerfileper utilizzare uno script di shell come CMDargomento, che a sua volta gestisce la sostituzione della stringa per copiare i valori IP dai ENVtuoi file di configurazione.

Ecco il set di file di configurazione con cui sono finito:

Dockerfile

FROM ubuntu:14.04
MAINTAINER Your Name <you@myapp.com>

RUN apt-get update && apt-get install -y nano htop git nginx

ADD nginx.conf /etc/nginx/nginx.conf
ADD api.myapp.conf /etc/nginx/sites-enabled/api.myapp.conf
ADD app.myapp.conf /etc/nginx/sites-enabled/app.myapp.conf
ADD Nginx-Startup.sh /etc/nginx/Nginx-Startup.sh

EXPOSE 80 443

CMD ["/bin/bash","/etc/nginx/Nginx-Startup.sh"]

nginx.conf

daemon off;
user www-data;
pid /var/run/nginx.pid;
worker_processes 1;


events {
    worker_connections 1024;
}


http {

    # Basic Settings

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 33;
    types_hash_max_size 2048;

    server_tokens off;
    server_names_hash_bucket_size 64;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;


    # Logging Settings
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;


    # Gzip Settings

gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 3;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/xml text/css application/x-javascript application/json;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";

    # Virtual Host Configs  
    include /etc/nginx/sites-enabled/*;

    # Error Page Config
    #error_page 403 404 500 502 /srv/Splash;


}

NOTA: è importante includerlo daemon off;nel nginx.conffile per assicurarsi che il contenitore non venga chiuso immediatamente dopo l'avvio.

api.myapp.conf

upstream api_upstream{
    server APP_IP:3000;
}

server {
    listen 80;
    server_name api.myapp.com;
    return 301 https://api.myapp.com/$request_uri;
}

server {
    listen 443;
    server_name api.myapp.com;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_pass http://api_upstream;
    }

}

Nginx-Startup.sh

#!/bin/bash
sed -i 's/APP_IP/'"$API_PORT_3000_TCP_ADDR"'/g' /etc/nginx/sites-enabled/api.myapp.com
sed -i 's/APP_IP/'"$APP_PORT_3001_TCP_ADDR"'/g' /etc/nginx/sites-enabled/app.myapp.com

service nginx start

Lascio a te il compito di fare i compiti sulla maggior parte dei contenuti di nginx.confe api.myapp.conf.

La magia accade Nginx-Startup.shquando usiamo sedper sostituire la stringa sul APP_IPsegnaposto che abbiamo scritto nel upstreamblocco dei nostri file api.myapp.confe app.myapp.conf.

Questa domanda di ask.ubuntu.com lo spiega molto bene: trova e sostituisci il testo all'interno di un file usando i comandi

GOTCHA Su OSX, sedgestisce le opzioni in modo diverso, -ispecificatamente il flag. Su Ubuntu, il -iflag gestirà la sostituzione "sul posto"; aprirà il file, cambierà il testo e quindi "salverà" lo stesso file. Su OSX, il -iflag richiede l'estensione del file che desideri abbia il file risultante. Se stai lavorando con un file che non ha estensione, devi inserire "" come valore per il -iflag.

GOTCHA Per usare ENV vars all'interno della regex che sedusa per trovare la stringa che vuoi sostituire, devi racchiudere la var tra virgolette doppie. Quindi la sintassi corretta, anche se dall'aspetto traballante, è come sopra.

Quindi docker ha avviato il nostro contenitore e attivato lo Nginx-Startup.shscript da eseguire, che ha utilizzato sedper modificare il valore APP_IPnella ENVvariabile corrispondente che abbiamo fornito nel sedcomando. Ora abbiamo i file di configurazione all'interno della nostra /etc/nginx/sites-enableddirectory che hanno gli indirizzi IP delle ENVvariabili che docker ha impostato all'avvio del contenitore. All'interno del tuo api.myapp.conffile vedrai che il upstreamblocco è cambiato in questo:

upstream api_upstream{
    server 172.0.0.2:3000;
}

L'indirizzo IP che vedi potrebbe essere diverso, ma ho notato che di solito è 172.0.0.x.

Ora dovresti avere tutto il routing in modo appropriato.

GOTCHA Non è possibile riavviare / rieseguire alcun container dopo aver eseguito l'avvio iniziale dell'istanza. Docker fornisce a ciascun container un nuovo IP all'avvio e non sembra riutilizzare nessuno di quelli utilizzati in precedenza. Quindi api.myapp.comotterrai 172.0.0.2 la prima volta, ma poi 172.0.0.4 la volta successiva. Ma Nginxavrà già impostato il primo IP nei suoi file di configurazione, o nel suo /etc/hostsfile, quindi non sarà in grado di determinare il nuovo IP per api.myapp.com. La soluzione a questo è probabile che utilizzi CoreOSe il suo etcdservizio che, nella mia comprensione limitata, agisce come uno condiviso ENVper tutte le macchine registrate nello stesso CoreOScluster. Questo è il prossimo giocattolo con cui giocherò.



OPZIONE B: Usa /etc/hostsvoci di file

Questo dovrebbe essere il modo più semplice e veloce per farlo, ma non sono riuscito a farlo funzionare. Apparentemente hai appena inserito il valore della /etc/hostsvoce nei tuoi file api.myapp.confe app.myapp.conf, ma non sono riuscito a far funzionare questo metodo.

AGGIORNAMENTO: Vedi la risposta di @Wes Tod per le istruzioni su come far funzionare questo metodo.

Ecco il tentativo che ho fatto in api.myapp.conf:

upstream api_upstream{
    server API:3000;
}

Considerando che c'è una voce nel mio /etc/hostsfile in questo modo: 172.0.0.2 APIho pensato che avrebbe semplicemente inserito il valore, ma non sembra esserlo.

Ho anche avuto un paio di problemi accessori con il mio Elastic Load Balancerapprovvigionamento da tutte le AZ, quindi questo potrebbe essere stato il problema quando ho provato questo percorso. Invece ho dovuto imparare a gestire la sostituzione delle stringhe in Linux, quindi è stato divertente. Ci proverò tra un po 'e vedrò come va.


2
Un altro problema nell'utilizzo dei collegamenti è che se riavvii il contenitore API, molto probabilmente otterrà un nuovo ip. Ciò non si riflette nel file nginx containers / etc / hosts che continuerà a utilizzare il vecchio ip e quindi dovrà essere riavviato.
judoole

13

Ho provato a utilizzare il popolare proxy inverso Jason Wilder che magicamente funziona per tutti, e ho imparato che non funziona per tutti (cioè: me). E sono nuovo di zecca per NGINX, e non mi è piaciuto il fatto di non aver capito le tecnologie che stavo cercando di usare.

Volevo aggiungere i miei 2 centesimi, perché la discussione sopra sui linkingcontenitori insieme ora è datata poiché è una funzionalità deprecata. Quindi ecco una spiegazione su come farlo usando networks. Questa risposta è un esempio completo della configurazione di nginx come proxy inverso per un sito Web a pagine statiche utilizzando Docker Composee la configurazione di nginx.

TL; DR;

Aggiungi i servizi che devono dialogare tra loro su una rete predefinita. Per una discussione dettagliata sulle reti Docker, ho imparato alcune cose qui: https://technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the-bad-and- il brutto/

Definisci la rete

Prima di tutto, abbiamo bisogno di una rete su cui possano dialogare tutti i tuoi servizi di backend. Ho chiamato il mio webma può essere quello che vuoi.

docker network create web

Crea l'app

Faremo solo una semplice app per il sito web. Il sito web è una semplice pagina index.html servita da un contenitore nginx. Il contenuto è un volume montato sull'host in una cartellacontent

DockerFile:

FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf

default.conf

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /var/www/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

docker-compose.yml

version: "2"

networks:
  mynetwork:
    external:
      name: web

services:
  nginx:
    container_name: sample-site
    build: .
    expose:
      - "80"
    volumes:
      - "./content/:/var/www/html/"
    networks:
      default: {}
      mynetwork:
        aliases:
          - sample-site

Nota che non abbiamo più bisogno della mappatura delle porte qui. Esponiamo semplicemente la porta 80. Questo è utile per evitare collisioni di porte.

Esegui l'app

Avvia questo sito Web con

docker-compose up -d

Alcuni controlli divertenti riguardanti le mappature dns per il tuo contenitore:

docker exec -it sample-site bash
ping sample-site

Questo ping dovrebbe funzionare, all'interno del tuo contenitore.

Crea il proxy

Proxy inverso Nginx:

Dockerfile

FROM nginx

RUN rm /etc/nginx/conf.d/*

Reimpostiamo tutta la configurazione dell'host virtuale, poiché la personalizzeremo.

docker-compose.yml

version: "2"

networks:
  mynetwork:
    external:
      name: web


services:
  nginx:
    container_name: nginx-proxy
    build: .
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./conf.d/:/etc/nginx/conf.d/:ro
      - ./sites/:/var/www/
    networks:
      default: {}
      mynetwork:
        aliases:
          - nginx-proxy

Esegui il proxy

Avvia il proxy utilizzando il nostro fidato

docker-compose up -d

Supponendo che non ci siano problemi, hai due contenitori in esecuzione che possono parlare tra loro usando i loro nomi. Proviamolo.

docker exec -it nginx-proxy bash
ping sample-site
ping nginx-proxy

Configura host virtuale

Ultimo dettaglio è impostare il file di hosting virtuale in modo che il proxy possa dirigere il traffico in base a come si desidera impostare la corrispondenza:

sample-site.conf per la nostra configurazione di hosting virtuale:

  server {
    listen 80;
    listen [::]:80;

    server_name my.domain.com;

    location / {
      proxy_pass http://sample-site;
    }

  }

In base a come è stato impostato il proxy, avrai bisogno di questo file memorizzato nella tua conf.dcartella locale che abbiamo montato tramite la volumesdichiarazione nel docker-composefile.

Ultimo ma non meno importante, dì a nginx di ricaricare la sua configurazione.

docker exec nginx-proxy service nginx reload

Questa sequenza di passaggi è il culmine di ore di mal di testa martellanti mentre lottavo con il sempre doloroso errore 502 Bad Gateway e apprendevo nginx per la prima volta, poiché la maggior parte della mia esperienza era con Apache.

Questa risposta è per dimostrare come eliminare l'errore 502 Bad Gateway che risulta dal fatto che i contenitori non sono in grado di parlare tra loro.

Spero che questa risposta salvi qualcuno là fuori ore di dolore, dal momento che far parlare i contenitori tra loro è stato davvero difficile da capire per qualche motivo, nonostante fosse quello che mi aspettavo fosse un ovvio caso d'uso. Ma poi di nuovo, io stupido. E per favore fatemi sapere come posso migliorare questo approccio.


Ah! L'ole 502 Gateway Error, un classico famigerato di questi tempi. Grazie @gdbj per aver sprecato tempo per far avanzare la conversazione e fornire una soluzione così dettagliata.
AJB

Volevo solo dire grazie per aver dedicato del tempo per questo, mi ha risparmiato un bel po 'di seccature. Grazie.
Entità singola

10

Utilizzando i collegamenti docker , è possibile collegare il contenitore a monte al contenitore nginx. Una caratteristica aggiunta è che docker gestisce il file host, il che significa che sarai in grado di fare riferimento al contenitore collegato utilizzando un nome piuttosto che l'ip potenzialmente casuale.


7

L '"Opzione B" di AJB può essere fatta funzionare utilizzando l'immagine di base di Ubuntu e configurando nginx da soli. (Non ha funzionato quando ho usato l'immagine Nginx da Docker Hub.)

Ecco il file Docker che ho usato:

FROM ubuntu
RUN apt-get update && apt-get install -y nginx
RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log
RUN rm -rf /etc/nginx/sites-enabled/default
EXPOSE 80 443
COPY conf/mysite.com /etc/nginx/sites-enabled/mysite.com
CMD ["nginx", "-g", "daemon off;"]

La mia configurazione nginx (aka: conf / mysite.com):

server {
    listen 80 default;
    server_name mysite.com;

    location / {
        proxy_pass http://website;
    }
}

upstream website {
    server website:3000;
}

E infine, come avvio i miei contenitori:

$ docker run -dP --name website website
$ docker run -dP --name nginx --link website:website nginx

Questo mi ha reso operativo e il mio nginx ha puntato a monte verso il secondo container docker che ha esposto la porta 3000.


Grazie per l'assistenza Wes! Ci proverò quando torno dalle vacanze.
AJB

Ho avuto alcuni problemi come questo con le immagini ufficiali. Ho trovato molto meglio basare la casella di Ubuntu e poi tirare direttamente le linee dai file Docker. Non che sia necessario, ma ahimè ...
Wes Todd

1
Questo è fantastico, ma quello che non capisco è come la configurazione di nginx conosce il valore di "sito web". Magia Nginx o Magia Docker o qualcos'altro?
Kyle Chadha

1
La riga upstream website {definisce il valore del sito Web per nginx. Questo è ciò che poi usi nel tuo file proxy_pass. La parte docker di questo utilizza solo lo stesso nome per coerenza, ma non ha nulla a che fare con l'installazione di nginx. Per renderlo un po 'più chiaro il passaggio del proxy dovrebbe essere:upstream website { server localhost:3000; }
Wes Todd

2
Cordiali saluti, sto usando l'ultima immagine ufficiale nginx (1.9.2) e sembra che funzioni per me. Quindi forse hanno risolto il problema.
Pejvan

6

La risposta di @ gdbj è un'ottima spiegazione e la risposta più aggiornata. Tuttavia, ecco un approccio più semplice.

Quindi, se vuoi reindirizzare tutto il traffico dall'ascolto di nginx 80a un altro contenitore che espone8080 , la configurazione minima può essere di:

nginx.conf:

server {
    listen 80;

    location / {
        proxy_pass http://client:8080; # this one here
        proxy_redirect off;
    }

}

docker-compose.yml

version: "2"
services:
  entrypoint:
    image: some-image-with-nginx
    ports:
      - "80:80"
    links:
      - client  # will use this one here

  client:
    image: some-image-with-api
    ports:
      - "8080:8080"

Documenti Docker


All'inizio pensavo avresti avuto problemi con la collisione delle porte.
gdbj

@gdbj Il mio problema era la risoluzione URL / IP tra i contenitori. Immagino che abbiamo avuto lo stesso. Nel tuo caso usi reti, che funziona anche bene, nel mio caso mi
limito a

Fantastico, è tutto ciò di cui avevo bisogno! Grazie per la risposta.
Floran Gmehlin

2

Ho appena trovato un articolo di Anand Mani Sankar che mostra un modo semplice di utilizzare il proxy upstream di nginx con docker composer.

Fondamentalmente è necessario configurare il collegamento dell'istanza e le porte nel file docker-compose e aggiornare di conseguenza a monte su nginx.conf.


1
L'articolo utilizza linksche sono deprecati. Usa le reti adesso: docs.docker.com/engine/userguide/networking
gdbj

La sostituzione magica della configurazione di nginx funzionerà anche in questo caso?
Dormouse
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.