Was ist ein Microservice?
Microservices sind eine zunehmend beliebte Architektur zum Erstellen umfangreicher Anwendungen. Anstatt eine einzelne, monolithische Codebasis zu verwenden, werden Anwendungen in eine Sammlung kleinerer Komponenten, sogenannte Microservices, zerlegt. Dieser Ansatz bietet mehrere Vorteile, darunter die Möglichkeit, einzelne Microservices zu skalieren, die Codebasis leichter verständlich und testbar zu halten und die Verwendung verschiedener Programmiersprachen, Datenbanken und anderer Tools für jeden Microservice zu ermöglichen.
Docker ist ein hervorragendes Tool zum Verwalten und Bereitstellen von Microservices. Jeder Microservice kann weiter in Prozesse unterteilt werden, die in separaten Docker-Containern ausgeführt werden, die mit Dockerfiles und Docker Compose-Konfigurationsdateien angegeben werden können. In Kombination mit einem Bereitstellungstool wie Kubernetes kann jeder Microservice dann einfach bereitgestellt, skaliert und von einem Entwicklerteam bearbeitet werden. Durch die Angabe einer Umgebung auf diese Weise lassen sich Microservices auch einfach zu einer größeren Anwendung verknüpfen.
Diese Anleitung zeigt, wie Sie einen Beispiel-Microservice mit Docker und Docker Compose erstellen und bereitstellen.
Bevor Sie beginnen
-
Wenn Sie dies noch nicht getan haben, erstellen Sie ein Linode-Konto und eine Compute-Instanz. Sehen Sie sich unsere Leitfäden Erste Schritte mit Linode und Erstellen einer Compute-Instanz an.
-
Folgen Sie unserem Leitfaden zum Einrichten und Sichern einer Compute-Instanz, um Ihr System zu aktualisieren. Möglicherweise möchten Sie auch die Zeitzone festlegen, Ihren Hostnamen konfigurieren, ein eingeschränktes Benutzerkonto erstellen und den SSH-Zugriff sichern.
Hinweis Dieses Handbuch wurde für Nicht-Root-Benutzer geschrieben. Befehlen, die erhöhte Berechtigungen erfordern, wird das Präfixsudo
vorangestellt . Wenn Sie mitsudo
nicht vertraut sind Befehl finden Sie in unserem Handbuch für Benutzer und Gruppen.
Installieren Sie Docker
Um Docker CE (Community Edition) zu installieren, befolgen Sie die Anweisungen in einer der folgenden Anleitungen:
-
Installieren und Verwenden von Docker unter Ubuntu und Debian
-
Installieren und Verwenden von Docker unter CentOS und Fedora
Vollständige Anweisungen zu noch mehr Linux-Distributionen finden Sie im Abschnitt „Install Docker Engine“ der offiziellen Docker-Dokumentation.
Installieren Sie Docker Compose
-
Laden Sie die neueste Version von Docker Compose herunter. Sehen Sie auf der Releases-Seite nach und ersetzen Sie
1.25.4
im folgenden Befehl mit der als Neueste Version gekennzeichneten Version :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
-
Dateiberechtigungen festlegen:
sudo chmod +x /usr/local/bin/docker-compose
Umgebung vorbereiten
In diesem Abschnitt werden Dockerfiles verwendet, um Docker-Images zu konfigurieren. Weitere Informationen zur Dockerfile-Syntax und Best Practices finden Sie in unserem How To Use Dockerfiles Guide und Docker’sDockerfile Best Practices Guide.
-
Erstellen Sie ein Verzeichnis für den Microservice:
mkdir flask-microservice
-
Erstellen Sie innerhalb des neuen Verzeichnisses eine Verzeichnisstruktur für die Microservice-Komponenten:
cd flask-microservice mkdir nginx postgres web
NGINX
-
Innerhalb des neuen
nginx
Unterverzeichnis eine Docker-Datei für das NGINX-Image erstellen:- Datei:nginx /Dockerfile
1 2
from nginx:alpine COPY nginx.conf /etc/nginx/nginx.conf
-
Erstellen Sie die
nginx.conf
referenziert in der Dockerfile:- Datei:/ 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
Das PostgreSQL-Image für diesen Microservice verwendet das offizielle postgresql
Image auf Docker Hub, daher ist kein Dockerfile erforderlich.
Im postgres
Unterverzeichnis, erstellen Sie eine init.sql
Datei:
- Datei: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 \.
Achtung In Zeile 22 von init.sql
, vergewissern Sie sich, dass Ihr Texteditor Tabulatoren nicht in Leerzeichen umwandelt. Ohne Tabulatoren zwischen den Einträgen in dieser Zeile funktioniert die App nicht.
Web
Das web
image enthält ein Beispiel für eine Flask-App. Fügen Sie die folgenden Dateien zum web
hinzu Verzeichnis zum Vorbereiten der App:
-
Erstellen Sie eine
.python-version
Datei, um die Verwendung von Python 3.6 anzugeben:echo "3.6.0" >> web/.python-version
-
Erstellen Sie ein Dockerfile für das
web
Bild:- Datei:web /Dockerfile
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"]
-
Erstellen Sie
web/linode.py
und fügen Sie das Beispiel-App-Skript hinzu:- Datei: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"
-
Fügen Sie eine
requirements.txt
hinzu Datei mit den erforderlichen Python-Abhängigkeiten:- Datei:web /requirements.txt
1 2 3 4
flask gunicorn psycopg2-binary redis
Docker Compose
Docker Compose wird verwendet, um die Verbindungen zwischen Containern und deren Konfigurationseinstellungen zu definieren.
Erstellen Sie eine docker-compose.yml
Datei im flask-microservice
Verzeichnis und fügen Sie Folgendes hinzu:
- Datei: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"
Microservice testen
-
Verwenden Sie Docker Compose, um alle Images zu erstellen, und starten Sie den Microservice:
cd flask-microservice/ && docker-compose up
Sie sollten sehen, dass alle Dienste in Ihrem Terminal gestartet werden.
-
Öffnen Sie ein neues Terminalfenster und stellen Sie eine Anfrage an die Beispielanwendung:
curl localhost
Hello Linode! This page has been viewed 1 time(s).
-
Seitenzugriffszähler zurücksetzen:
curl localhost/resetcounter
Successfully deleted redis and postgres counters
-
Kehren Sie zum Terminalfenster zurück, in dem Docker Compose gestartet wurde, um das standardmäßige Ausgangsprotokoll anzuzeigen:
flaskapp_1 | DEBUG in linode [/home/flask/app/web/linode.py:56]: flaskapp_1 | reset visitor count
Verwendung von Containern in der Produktion:Best Practices
Die im Beispiel-Microservice verwendeten Container sollen die folgenden Best Practices für die Verwendung von Containern in der Produktion veranschaulichen:
Container sollten sein:
-
Vergänglich :Es sollte einfach sein, Container mit minimaler Einrichtung und Konfiguration zu stoppen, zu zerstören, neu zu erstellen und neu bereitzustellen.
Der Microservice Flask ist dafür ein ideales Beispiel. Der gesamte Microservice kann mit Docker Compose hoch- oder heruntergefahren werden. Nachdem die Container ausgeführt wurden, ist keine zusätzliche Konfiguration erforderlich, wodurch die Anwendung einfach geändert werden kann.
-
Einwegartikel :Idealerweise sollte jeder einzelne Container innerhalb einer größeren Anwendung ausfallen können, ohne die Leistung der Anwendung zu beeinträchtigen. Verwenden eines
restart: on-failure
Option indocker-compose.yml
Datei sowie eine Anzahl von Replikaten ermöglichen einigen Containern im Beispiel-Microservice einen ordnungsgemäßen Ausfall, während die Webanwendung weiterhin bereitgestellt wird – ohne Beeinträchtigung für den Endbenutzer.Hinweis Die Anweisung zur Replikatzählung wird nur wirksam, wenn diese Konfiguration als Teil von aDocker Swarm bereitgestellt wird, was in diesem Handbuch nicht behandelt wird.
-
Schneller Einstieg :Das Vermeiden zusätzlicher Installationsschritte in der Docker-Datei, das Entfernen nicht benötigter Abhängigkeiten und das Erstellen eines Ziel-Images, das wiederverwendet werden kann, sind drei der wichtigsten Schritte beim Erstellen einer Webanwendung, die innerhalb von Docker eine schnelle Initialisierungszeit hat. Die Beispielanwendung verwendet kurze, prägnante, vorgefertigte Dockerfiles, um die Initialisierungszeit zu minimieren.
-
Schnell zum Stoppen :Bestätigen Sie, dass ein
docker kill --signal=SIGINT {APPNAME}
stoppt die Anwendung ordnungsgemäß. Zusammen mit einer Neustartbedingung und einer Replikatbedingung stellt dies sicher, dass Container, wenn sie ausfallen, effizient wieder online gebracht werden. -
Leicht :Verwenden Sie den kleinsten Basiscontainer, der alle Dienstprogramme bereitstellt, die zum Erstellen und Ausführen Ihrer Anwendung erforderlich sind. Viele Docker-Images basieren auf Alpine Linux, einer leichten und einfachen Linux-Distribution, die nur 5 MB in einem Docker-Image belegt. Die Verwendung einer kleinen Distribution spart Netzwerk- und Betriebsaufwand und erhöht die Containerleistung erheblich. Die Beispielanwendung verwendet gegebenenfalls Alpine-Images (NGINX, Redis und PostgreSQL) und ein Python-Slim-Basisimage für die Gunicorn/Flask-Anwendung.
-
Staatenlos :Da sie kurzlebig sind, sollten Container normalerweise keinen Zustand beibehalten. Der Status einer Anwendung sollte in einem separaten, persistenten Datenvolumen gespeichert werden, wie es beim PostgreSQL-Datenspeicher des Microservice der Fall ist. Der Schlüsselwertspeicher von Redis verwaltet zwar Daten in einem Container, aber diese Daten sind nicht anwendungskritisch; Der Redis-Speicher wird ordnungsgemäß auf die Datenbank zurückgesetzt, falls der Container nicht reagieren kann.
-
Tragbar :Alle Abhängigkeiten einer App, die für die Container-Laufzeit benötigt werden, sollten lokal verfügbar sein. Alle Abhängigkeiten und Startskripts des Beispiel-Microservices werden im Verzeichnis für jede Komponente gespeichert. Diese können in die Versionskontrolle eingecheckt werden, was es einfach macht, die Anwendung zu teilen und bereitzustellen.
-
Modular :Jeder Container sollte eine Verantwortlichkeit und einen Prozess haben. In diesem Microservice wird jeder der Hauptprozesse (NGINX, Python, Redis und PostgreSQL) in einem separaten Container bereitgestellt.
-
Protokollierung :Alle Container sollten sich bei
STDOUT
anmelden . Diese Einheitlichkeit macht es einfach, die Protokolle für alle Prozesse in einem einzigen Stream anzuzeigen. -
Belastbar :Die Beispielanwendung startet ihre Container neu, wenn sie aus irgendeinem Grund beendet werden. Dies trägt dazu bei, dass Ihre Docker-Anwendung auch während der Wartungszeiträume hochverfügbar und leistungsstark ist.
Weitere Informationen
Weitere Informationen zu diesem Thema finden Sie in den folgenden Ressourcen. Obwohl diese in der Hoffnung bereitgestellt werden, dass sie nützlich sind, beachten Sie bitte, dass wir nicht für die Genauigkeit oder Aktualität extern gehosteter Materialien garantieren können.
- Github-Repository für Beispiel-Microservice
- Verwenden von Containern zum Erstellen einer Microservices-Architektur