Cos'è un microservizio?
I microservizi sono un'architettura sempre più popolare per la creazione di applicazioni su larga scala. Anziché utilizzare un'unica base di codice monolitica, le applicazioni vengono suddivise in una raccolta di componenti più piccoli chiamati microservizi. Questo approccio offre numerosi vantaggi, tra cui la possibilità di scalare singoli microservizi, semplificare la comprensione e il test della base di codice e consentire l'uso di linguaggi di programmazione, database e altri strumenti diversi per ciascun microservizio.
Docker è uno strumento eccellente per la gestione e la distribuzione di microservizi. Ciascun microservizio può essere ulteriormente suddiviso in processi in esecuzione in contenitori Docker separati, che possono essere specificati con Dockerfiles e file di configurazione Docker Compose. In combinazione con uno strumento di provisioning come Kubernetes, ogni microservizio può quindi essere facilmente distribuito, ridimensionato e collaborato da un team di sviluppatori. Specificare un ambiente in questo modo semplifica anche il collegamento di microservizi insieme per formare un'applicazione più ampia.
Questa guida mostra come creare e distribuire un microservizio di esempio utilizzando Docker e Docker Compose.
Prima di iniziare
-
Se non l'hai già fatto, crea un account Linode e un'istanza di calcolo. Consulta le nostre guide Introduzione a Linode e Creazione di un'istanza di calcolo.
-
Segui la nostra guida alla configurazione e alla protezione di un'istanza di calcolo per aggiornare il tuo sistema. Potresti anche voler impostare il fuso orario, configurare il tuo nome host, creare un account utente limitato e rafforzare l'accesso SSH.
Nota Questa guida è scritta per un utente non root. I comandi che richiedono privilegi elevati sono preceduti dasudo
. Se non hai familiarità consudo
comando, puoi consultare la nostra guida Utenti e Gruppi.
Installa Docker
Per installare Docker CE (Community Edition), segui le istruzioni all'interno di una delle guide seguenti:
-
Installazione e utilizzo di Docker su Ubuntu e Debian
-
Installazione e utilizzo di Docker su CentOS e Fedora
Per istruzioni complete su ancora più distribuzioni Linux, fai riferimento alla sezione Install Docker Engine della documentazione ufficiale di Docker.
Installa Docker Compose
-
Scarica l'ultima versione di Docker Compose. Controlla la pagina dei rilasci e sostituisci
1.25.4
nel comando seguente con la versione contrassegnata come Ultima versione :sudo curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
-
Imposta i permessi dei file:
sudo chmod +x /usr/local/bin/docker-compose
Prepara l'ambiente
Questa sezione utilizza Dockerfiles per configurare le immagini Docker. Per ulteriori informazioni sulla sintassi e sulle best practice di Dockerfile, consulta la nostra guida all'uso di Dockerfiles e la guida alle best practice di Dockerfile di Docker.
-
Crea una directory per il microservizio:
mkdir flask-microservice
-
Crea una struttura di directory per i componenti del microservizio all'interno della nuova directory:
cd flask-microservice mkdir nginx postgres web
NGINX
-
All'interno del nuovo
nginx
sottodirectory, crea un Dockerfile per l'immagine NGINX:- File:nginx /File Docker
1 2
from nginx:alpine COPY nginx.conf /etc/nginx/nginx.conf
-
Crea il
nginx.conf
referenziato nel Dockerfile:- File:/ nginx/nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
user nginx; worker_processes 1; error_log /dev/stdout info; error_log off; pid /var/run/nginx.pid; events { worker_connections 1024; use epoll; multi_accept on; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /dev/stdout main; access_log off; keepalive_timeout 65; keepalive_requests 100000; tcp_nopush on; tcp_nodelay on; server { listen 80; proxy_pass_header Server; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # app comes from /etc/hosts, Docker added it for us! proxy_pass http://flaskapp:8000/; } } }
PostgreSQL
L'immagine PostgreSQL per questo microservizio utilizzerà il postgresql
ufficiale immagine su Docker Hub, quindi non è necessario alcun Dockerfile.
Nel postgres
sottodirectory, crea una init.sql
file:
- File:postgres /init.sql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; SET client_min_messages = warning; SET row_security = off; CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; SET search_path = public, pg_catalog; SET default_tablespace = ''; SET default_with_oids = false; CREATE TABLE visitors ( site_id integer, site_name text, visitor_count integer ); ALTER TABLE visitors OWNER TO postgres; COPY visitors (site_id, site_name, visitor_count) FROM stdin; 1 linodeexample.com 0 \.
Attenzione Nella riga 22 di init.sql
, assicurati che il tuo editor di testo non converta le schede in spazi. L'app non funzionerà senza schede tra le voci in questa riga.
Web
Il web
l'immagine conterrà un'app Flask di esempio. Aggiungi i seguenti file al web
directory per preparare l'app:
-
Crea un
.python-version
per specificare l'uso di Python 3.6:echo "3.6.0" >> web/.python-version
-
Crea un Dockerfile per il
web
immagine:- File:web /File Docker
1 2 3 4 5 6 7 8 9 10
from python:3.6.2-slim RUN groupadd flaskgroup && useradd -m -g flaskgroup -s /bin/bash flask RUN echo "flask ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers RUN mkdir -p /home/flask/app/web WORKDIR /home/flask/app/web COPY requirements.txt /home/flask/app/web RUN pip install --no-cache-dir -r requirements.txt RUN chown -R flask:flaskgroup /home/flask USER flask ENTRYPOINT ["/usr/local/bin/gunicorn", "--bind", ":8000", "linode:app", "--reload", "--workers", "16"]
-
Crea
web/linode.py
e aggiungi lo script dell'app di esempio:- File:web /linode.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
from flask import Flask import logging import psycopg2 import redis import sys app = Flask(__name__) cache = redis.StrictRedis(host='redis', port=6379) # Configure Logging app.logger.addHandler(logging.StreamHandler(sys.stdout)) app.logger.setLevel(logging.DEBUG) def PgFetch(query, method): # Connect to an existing database conn = psycopg2.connect("host='postgres' dbname='linode' user='postgres' password='linode123'") # Open a cursor to perform database operations cur = conn.cursor() # Query the database and obtain data as Python objects dbquery = cur.execute(query) if method == 'GET': result = cur.fetchone() else: result = "" # Make the changes to the database persistent conn.commit() # Close communication with the database cur.close() conn.close() return result @app.route('/') def hello_world(): if cache.exists('visitor_count'): cache.incr('visitor_count') count = (cache.get('visitor_count')).decode('utf-8') update = PgFetch("UPDATE visitors set visitor_count = " + count + " where site_id = 1;", "POST") else: cache_refresh = PgFetch("SELECT visitor_count FROM visitors where site_id = 1;", "GET") count = int(cache_refresh[0]) cache.set('visitor_count', count) cache.incr('visitor_count') count = (cache.get('visitor_count')).decode('utf-8') return 'Hello Linode! This page has been viewed %s time(s).' % count @app.route('/resetcounter') def resetcounter(): cache.delete('visitor_count') PgFetch("UPDATE visitors set visitor_count = 0 where site_id = 1;", "POST") app.logger.debug("reset visitor count") return "Successfully deleted redis and postgres counters"
-
Aggiungi un
requirements.txt
file con le dipendenze Python richieste:- File:web /requisiti.txt
1 2 3 4
flask gunicorn psycopg2-binary redis
Composizione Docker
Docker Compose verrà utilizzato per definire le connessioni tra i container e le relative impostazioni di configurazione.
Crea un docker-compose.yml
nel flask-microservice
directory e aggiungi quanto segue:
- File:docker -compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
version: '3' services: # Define the Flask web application flaskapp: # Build the Dockerfile that is in the web directory build: ./web # Always restart the container regardless of the exit status; try and restart the container indefinitely restart: always # Expose port 8000 to other containers (not to the host of the machine) expose: - "8000" # Mount the web directory within the container at /home/flask/app/web volumes: - ./web:/home/flask/app/web # Don't create this container until the redis and postgres containers (below) have been created depends_on: - redis - postgres # Link the redis and postgres containers together so they can talk to one another links: - redis - postgres # Pass environment variables to the flask container (this debug level lets you see more useful information) environment: FLASK_DEBUG: 1 # Deploy with three replicas in the case one of the containers fails (only in Docker Swarm) deploy: mode: replicated replicas: 3 # Define the redis Docker container redis: # use the redis:alpine image: https://hub.docker.com/_/redis/ image: redis:alpine restart: always deploy: mode: replicated replicas: 3 # Define the redis NGINX forward proxy container nginx: # build the nginx Dockerfile: http://bit.ly/2kuYaIv build: nginx/ restart: always # Expose port 80 to the host machine ports: - "80:80" deploy: mode: replicated replicas: 3 # The Flask application needs to be available for NGINX to make successful proxy requests depends_on: - flaskapp # Define the postgres database postgres: restart: always # Use the postgres alpine image: https://hub.docker.com/_/postgres/ image: postgres:alpine # Mount an initialization script and the persistent postgresql data volume volumes: - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql - ./postgres/data:/var/lib/postgresql/data # Pass postgres environment variables environment: POSTGRES_PASSWORD: linode123 POSTGRES_DB: linode # Expose port 5432 to other Docker containers expose: - "5432"
Testa il microservizio
-
Usa Docker Compose per creare tutte le immagini e avviare il microservizio:
cd flask-microservice/ && docker-compose up
Dovresti vedere tutti i servizi avviati nel tuo terminale.
-
Apri una nuova finestra del terminale e fai una richiesta all'applicazione di esempio:
curl localhost
Hello Linode! This page has been viewed 1 time(s).
-
Reimposta il contatore delle visite alla pagina:
curl localhost/resetcounter
Successfully deleted redis and postgres counters
-
Torna alla finestra del terminale in cui Docker Compose è stato avviato per visualizzare il registro di uscita standard:
flaskapp_1 | DEBUG in linode [/home/flask/app/web/linode.py:56]: flaskapp_1 | reset visitor count
Utilizzo di container in produzione:best practice
I contenitori utilizzati nel microservizio di esempio hanno lo scopo di dimostrare le seguenti best practice per l'utilizzo dei contenitori in produzione:
I contenitori dovrebbero essere:
-
Effimero :dovrebbe essere facile fermare, distruggere, ricostruire e ridistribuire i container con un'installazione e una configurazione minime.
Il microservizio Flask ne è un esempio ideale. L'intero microservizio può essere attivato o disattivato utilizzando Docker Compose. Non è necessaria alcuna configurazione aggiuntiva dopo l'esecuzione dei contenitori, il che semplifica la modifica dell'applicazione.
-
Monouso :Idealmente, qualsiasi singolo contenitore all'interno di un'applicazione più grande dovrebbe essere in grado di non riuscire senza influire sulle prestazioni dell'applicazione. Utilizzando un
restart: on-failure
opzione nelladocker-compose.yml
file, oltre ad avere un conteggio delle repliche, consente ad alcuni contenitori nel microservizio di esempio di non riuscire correttamente pur continuando a servire l'applicazione Web, senza alcun degrado per l'utente finale.Nota La direttiva sul conteggio delle repliche sarà efficace solo quando questa configurazione viene distribuita come parte di aDocker Swarm, che non è trattato in questa guida.
-
Inizio rapido :evitare passaggi di installazione aggiuntivi nel file Docker, rimuovere le dipendenze non necessarie e creare un'immagine di destinazione che può essere riutilizzata sono tre dei passaggi più importanti nella creazione di un'applicazione Web con tempi di inizializzazione rapidi all'interno di Docker. L'applicazione di esempio utilizza Dockerfile brevi, concisi e predefiniti per ridurre al minimo il tempo di inizializzazione.
-
Veloce a fermarsi :Convalida che un
docker kill --signal=SIGINT {APPNAME}
interrompe l'applicazione con grazia. Questo, insieme a una condizione di riavvio e una condizione di replica, garantirà che quando i container si guastano, verranno riportati online in modo efficiente. -
Leggero :utilizza il contenitore di base più piccolo che fornisce tutte le utilità necessarie per creare ed eseguire l'applicazione. Molte immagini Docker sono basate su Alpine Linux, una distribuzione Linux leggera e semplice che occupa solo 5 MB in un'immagine Docker. L'utilizzo di una piccola distribuzione consente di risparmiare rete e sovraccarico operativo e aumenta notevolmente le prestazioni del container. L'applicazione di esempio utilizza immagini alpine ove applicabile (NGINX, Redis e PostgreSQL) e un'immagine di base python-slim per l'applicazione Gunicorn / Flask.
-
Apolidi :Poiché sono effimeri, i contenitori in genere non dovrebbero mantenere lo stato. Lo stato di un'applicazione deve essere archiviato in un volume di dati separato e persistente, come nel caso dell'archivio dati PostgreSQL del microservizio. L'archivio valori-chiave Redis conserva i dati all'interno di un contenitore, ma questi dati non sono critici per l'applicazione; l'archivio Redis eseguirà il failback normale al database se il contenitore non è in grado di rispondere.
-
Portatile :tutte le dipendenze di un'app necessarie per il runtime del contenitore devono essere disponibili localmente. Tutte le dipendenze e gli script di avvio del microservizio di esempio vengono archiviati nella directory di ciascun componente. Questi possono essere archiviati nel controllo della versione, semplificando la condivisione e la distribuzione dell'applicazione.
-
Modulare :Ogni container dovrebbe avere una responsabilità e un processo. In questo microservizio, ciascuno dei processi principali (NGINX, Python, Redis e PostgreSQL) viene distribuito in un contenitore separato.
-
Registrazione :Tutti i contenitori devono accedere a
STDOUT
. Questa uniformità semplifica la visualizzazione dei registri di tutti i processi in un unico flusso. -
Resiliente :l'applicazione di esempio riavvia i suoi contenitori se vengono chiusi per qualsiasi motivo. Questo aiuta a fornire alla tua applicazione Dockerizzata disponibilità e prestazioni elevate, anche durante i periodi di manutenzione.
Maggiori informazioni
Si consiglia di consultare le seguenti risorse per ulteriori informazioni su questo argomento. Sebbene questi siano forniti nella speranza che possano essere utili, tieni presente che non possiamo garantire l'accuratezza o la tempestività dei materiali ospitati esternamente.
- Repository Github per microservizi di esempio
- Utilizzo dei container per creare un'architettura di microservizi