GNU/Linux >> Linux Esercitazione >  >> Panels >> Docker

Come distribuire microservizi con Docker

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

  1. 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.

  2. 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 da sudo . Se non hai familiarità con sudo 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

  1. 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
    
  2. 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.

  1. Crea una directory per il microservizio:

     mkdir flask-microservice
    
  2. Crea una struttura di directory per i componenti del microservizio all'interno della nuova directory:

     cd flask-microservice
     mkdir nginx postgres web
    

NGINX

  1. 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
  2. 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:

  1. Crea un .python-version per specificare l'uso di Python 3.6:

     echo "3.6.0" >> web/.python-version
    
  2. 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"]
  3. 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"
  4. 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

  1. 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.

  2. 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).
  3. Reimposta il contatore delle visite alla pagina:

    curl localhost/resetcounter
    
    Successfully deleted redis and postgres counters
  4. 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:

  1. 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.

  2. 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 nella docker-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.
  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. Registrazione :Tutti i contenitori devono accedere a STDOUT . Questa uniformità semplifica la visualizzazione dei registri di tutti i processi in un unico flusso.

  10. 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

Docker
  1. Come distribuire un server DNS dinamico con Docker su Debian 10

  2. Come distribuire Modsecurity con Nginx su Ubuntu 20.04 LTS

  3. Come installare Jenkins con Docker

  4. Come distribuire app con Rancher

  5. Come distribuire un container nginx con Docker su Linode

Come distribuire Pi-Hole su Debian 11

Come distribuire Docker Compose Stack su Kubernetes con Kompose

Come distribuire CouchDB come cluster con Docker

Come distribuire Rocket Chat con Nginx su Ubuntu 18.04

Come distribuire un'applicazione PHP con Nginx e MySQL utilizzando Docker e Docker Compose

Come distribuire l'applicazione Laravel con Nginx su Ubuntu?