Commit eaf2e2b7 authored by Nane Kratzke's avatar Nane Kratzke
Browse files

Deploy

parent 2a8f9388
Pipeline #29603 failed with stages
in 50 seconds
# Lab 09: Request-Response-basierte Interaktion mit gRPC
gRPC (gRPC Remote Procedure Calls) ist ein Protokoll zum Aufruf von Funktionen in verteilten Computersystemen. Es basiert auf HTTP/2 und [Protocol Buffers](https://developers.google.com/protocol-buffers). [gRPC](https://grpc.io) wird von der [Cloud Native Computing Foundation](https://www.cncf.io) als ["incubating project"](https://www.cncf.io/projects) bewertet.
gRPC (gRPC Remote Procedure Calls) ist ein Protokoll zum Aufruf von entfernten Funktionen (Remote Procedures) in verteilten Computersystemen. Es basiert auf HTTP/2 und [Protocol Buffers](https://developers.google.com/protocol-buffers). [gRPC](https://grpc.io) wird von der [Cloud Native Computing Foundation](https://www.cncf.io) als ["incubating project"](https://www.cncf.io/projects) geführt.
Dieses Lab bietet eine grundlegende Einführung in die Arbeit mit gRPC (auf Basis von Python) und umfasst:
......@@ -10,11 +10,19 @@ Dieses Lab bietet eine grundlegende Einführung in die Arbeit mit gRPC (auf Basi
Es basiert auf dem offiziellen [gRPC Tutorial](https://grpc.io/docs/languages/python). Diese Lab ist allerdings so vorbereitet, dass Sie automatisch in Kubernetes deployen können.
Insbesondere werden wir uns den Performance Unterschied zu REST-basierten APIs ansehen. Beide Ansätze fallen nämlich in den Ansatz des Request-Response Kommunikationsmusters.
## Inhalt
- [Lab 09: Request-Response-basierte Interaktion mit gRPC](#lab-09-request-response-basierte-interaktion-mit-grpc)
- [Inhalt](#inhalt)
- [Vorbereitung](#vorbereitung)
- [Übung 01:](#übung-01)
- [Übung 01: Entwickeln eines gRPC-Clients](#übung-01-entwickeln-eines-grpc-clients)
- [Übung 02: Entwickeln eines REST-Servers (zum Vergleich)](#übung-02-entwickeln-eines-rest-servers-zum-vergleich)
- [Übung 03: Performancevergleich HTTP (REST) vs. gRPC (auf einem Host)](#übung-03-performancevergleich-http-rest-vs-grpc-auf-einem-host)
- [Übung 04: Performancevergleich HTTP (REST) vs. gRPC (in einem Cluster)](#übung-04-performancevergleich-http-rest-vs-grpc-in-einem-cluster)
- [Verständnis- und Transferfragen](#verständnis--und-transferfragen)
- [Links](#links)
- [Was sollten Sie mitnehmen](#was-sollten-sie-mitnehmen)
## Vorbereitung
......@@ -22,6 +30,7 @@ Sie benötigen auf Ihren lokalen Entwicklungsrechner:
- [Python 3](https://www.python.org/downloads)
- [Docker](https://www.docker.com/get-started)
- [Lens](https://k8slens.io)
Führen Sie anschließend bitte folgende Schritte aus:
......@@ -45,4 +54,186 @@ Führen Sie anschließend bitte folgende Schritte aus:
- **Value:** Inhalt der kubeconfig (z.B. mittels Copy-Paste aus Editor)
- **Typ:** `File` (Auswählen, WICHTIG!!!)
## Übung 01:
## Übung 01: Entwickeln eines gRPC-Clients
Zur Entwicklung einer gRPC-basierten Kommunikation zwischen Client und Server ist eine Schnittstellen-Definition erforderlich. In gRPC wird dies in `.proto` Dateien vorgenommen. Aus diesen `.proto`-Dateien werden Stubs für die Client- und die Server-Seite vorgenommen.
- Installieren Sie sich bitte die notwendigen Python Pakete auf Ihrem lokalen Entwicklungssystem mittels `python3 -m pip install -r grpc/Requirements.txt`
- Lesen Sie sich dann ein wenig in das offizielle [gRPC Tutorial](https://grpc.io/docs/languages/python) ein.
- Schauen Sie anschließend die `grpc/helloworld.proto` Datei an und versuchen Sie diese nachzuvollziehen.
Für die folgenden Schritte wechseln Sie bitte ins `grpc`-Verzeichnis (`cd grpc`).
- Generieren Sie dann die Stubs für die Client- und Server-Seite.
```Bash
python3 -m grpc_tools.protoc --proto_path=. --python_out=. --grpc_python_out=. helloworld.proto
```
Dies sollte im `grpc`-Verzeichnis die Stubs `helloworld_pb2_grpc.py` und `helloworld_pb2.py` für Client und Server erzeugt haben. Schauen Sie gerne einmal hinein, diese Dateien dürfen aber nicht manuell angepasst werden. Spätestens beim Container-Build würde manuelle Anpassungen an diesen Stubs wieder überschrieben werden.
- Versuch Sie nun die `grpc/greeter_server.py` vor dem Hintergrund des gRPC Tutorials nachzuvollziehen. Sie sehen dort, wie die Stubs zu importieren sind, und wie die gRPC-API als von `Servicer`-Klassen abzuleitende Klasse zu implentieren ist. Diese `Servicer`-Klassen wurden aus der `.proto` im vorherigen Schritt generiert.
- Versuchen Sie ferner die `grpc/greeter_client.py` nachzuvollziehen. Sie sehen hier wie der Stub zu beziehen ist. Über dieses Proxy-Objekt läuft die Kommunikation mit dem entfernten gRPC-Server.
Starten Sie nun den gRPC-Server lokal bei sich auf dem Rechner.
```Bash
python3 grpc/greeter_server.py
```
In einer weiteren Shell starten Sie bitte den gRPC-Client.
```Bash
python3 grpc/greeter_client.py
```
Dieser Client setzt 100 entfernte Aufrufe ab. In der Konsole des Servers sollten Sie folgende Logs sehen:
```
[...]
ipv6:[::1]:50554 | 2021-03-03T14:17:20.518175 | Request name: "you"
ipv6:[::1]:50554 | 2021-03-03T14:17:20.520777 | Request name: "you"
ipv6:[::1]:50554 | 2021-03-03T14:17:20.529476 | Request name: "you"
ipv6:[::1]:50554 | 2021-03-03T14:17:20.531520 | Request name: "you"
ipv6:[::1]:50554 | 2021-03-03T14:17:20.537797 | Request name: "you"
[...]
```
Gratulation! Sie haben erfolgreich Ihren (ersten?) gRPC-Server entwickelt!
Prüfen Sie bitte noch, ob Sie erfolgreich einen entsprechenden Container bauen können.
```
docker build -t greeter_grpc grpc
```
## Übung 02: Entwickeln eines REST-Servers (zum Vergleich)
Für einen Performance Vergleich bauen wir uns einen REST-Server auf Basis von Flask (analog zum vorherigen Lab).
Dieser Server soll auf Port 5000 arbeiten und
- unter der Route `/` die Zeichenkette `"Hello!"` liefern.
- Unter der Route `/hello/<name>` soll die Zeichenkette `"Hello, <name>!"` geliefert werden. `<name>` ist als Platzhalter für Routen wie `/hello/Max` oder `hello/Anna` zu verstehen.
Entwickeln Sie diesen REST-Server in der Datei `rest/greeter_rest.py` und lassen diesen wie folgt laufen.
```Bash
> python3 rest/greeter_rest.py
* Serving Flask app "greeter_rest" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
[...]
```
Prüfen Sie z.B. mit diesen URLs, ob dies funktioniert:
- [http://localhost:5000](http://localhost:5000) => `Hello!`
- [http://localhost:5000/hello/Otto](http://localhost:5000/hello/Otto) => `Hello, Otto!`
Prüfen Sie bitte auch, ob Sie den vorbereiteten Container bauen können.
```Bash
docker build -t greeter_rest rest
```
Gratulation! Sie haben schon wieder einen REST-API gebaut.
> __Hinweis:__
> Wenn Sie gar nicht weiter kommen, hilft vielleicht folgende Abkürzung.
>
> `> cp cheat/greeter_rest-ue2.py rest/greeter_rest.py`
## Übung 03: Performancevergleich HTTP (REST) vs. gRPC (auf einem Host)
Entwickeln Sie nun auf Basis des Clients aus Übung 01 einen Client, der einmal den gRPC-Server und dann den REST-Server abfragt und dies 100-mal wiederholt. Dabei sollen die Antwortzeiten für gRPC-Calls und REST-Calls gesammelt und hinsichtlich ihres Medianwertes ausgewertet werden.
Folgende Pakete sind ggf. hilfreich!
```Python
from statistic import median
# median() bestimmt den Median einer List von Werten
import time
# time.time() liefert aktuelle Timestamp in ms
import requests
# HTTP Get request in Python
# response = requests.get("http://localhost:5000/hello/you")
```
Entwickeln Sie diesen Client bitte in der Datei `grpc/compare.py`.
1. Bauen Sie den REST- und den gRPC Container
```Bash
> docker build -t greeter_rest rest
> docker build -t greeter_grpc grpc
```
2. Starten Sie beide Container in zwei unterschiedlichen Shells
```Bash
Shell 1:> docker run -p 5000:5000 greeter_rest
Shell 2:> docker run -p 5555:5555 greeter_grpc
```
3. Starten Sie dann in einer dritten Shell Ihr Benchmarking
```Bash
> python3 grpc/compare.py
gRPC: 6.12ms
REST: 15.28ms
```
Führen Sie diese Messungen gerne ein paar mal hintereinander auf Ihrem System aus.
> __Hinweis:__
> Wenn Sie gar nicht weiter kommen, hilft vielleicht folgende Abkürzung.
>
> `> cp cheat/compare-ue3.py grpc/compare.py`
## Übung 04: Performancevergleich HTTP (REST) vs. gRPC (in einem Cluster)
Lokal
## Verständnis- und Transferfragen
- Welche Art der API-Entwicklung erscheint Ihnen "zugänglicher" (REST, gRPC)?
- Wie schätzen Sie den Grad der Kopplung bei REST und gRPC ein und warum?
- Erklären Sie die gemessenen Performance-Unterschiede zwischen Übung 03 und Übung 04. Benennen Sie ein paar Komponenten, die dafür verantwortlich sein könnten.
Zur Beantwortung der folgenden Fragen, füllen Sie bitte die folgende Tabelle aus.
- Sie haben einen Use Case mit wenigen langlaufenden Requests. Für welchen Ansatz (REST, gRPC) entscheiden Sie sich?
- Sie haben einen Use Case mit vielen langlaufenden Requests. Für welchen Ansatz (REST, gRPC) entscheiden Sie sich?
- Sie haben einen Use Case mit wenigen kurzen Requests. Für welchen Ansatz (REST, gRPC) entscheiden Sie sich?
- Sie haben einen Use Case mit vielen kurzen Requests. Für welchen Ansatz (REST, gRPC) entscheiden Sie sich?
. | schneller Req. | langsamer Req.
-------------- | -------------- | ---------------
**viele Req.** | gRPC o. REST? | gRPC o. REST?
**wenig Req.** | gRPC o. REST? | gRPC o. REST?
Beantworten Sie nun die obigen Fragen erneut. Berücksichtigen Sie aber ergänzend die folgenden Randbedingungen des Cloud-native Computings.
- Gewichten Sie in Fällen wo sowohl gRPC als auch REST in Frage kommen könnten, das Kriterium der losen Kopplung höher als das der Performance.
- Beachten Sie, dass Sie höhere Requestzahlen auch durch horizontale Skalierung in den Griff bekommen können.
. | schneller Req. | langsamer Req.
-------------- | -------------- | ---------------
**viele Req.** | gRPC o. REST? | gRPC o. REST?
**wenig Req.** | gRPC o. REST? | gRPC o. REST?
## Links
- [gRPC](https://www.grpc.io)
- [gRPC + Python](https://www.grpc.io/docs/languages/python/quickstart)
- [Case study Salesforce](https://www.cncf.io/case-studies/salesforce) (Streaming services und Push notification services)
- [Case study Mux](https://www.cncf.io/case-studies/mux) (Video streaming)
- [Weitere gRPC Fallstudien](https://www.cncf.io/case-studies/?_sft_lf-project=grpc) (von CNCF.io)
## Was sollten Sie mitnehmen
- gRPC basiert auf HTTP/2 und fügt sich daher nahtlos in das Service Computing ein, da alle mit HTTP kompatiblen Komponenten (Proxies, etc.) auf Applikationsschicht des ISO-Netzwerk-Stacks dann auch mit gRPC funktionieren. Z.B. können gRPC-Dienste über Nginx-Ingress-Controller in Kubernetes exponiert werden.
- gRPC ist ein auf HTTP/2-basiertes Binärprotokoll im Gegensatz zum HTTP/1-basierten Textprotokoll und ist daher bis zu einer Größenordnung schneller als HTTP-basierte Request-Response-Verfahren.
- gRPC erhöht allerdings die Kopplung zwischen Diensten (durch Stubs und Service Definition Files `.proto`)
- Muss man `.proto` Dateien in gRPC Dateien anfassen, muss man sowohl Consuming wie auch Providing Service updaten. Mit automatisierten Deployment Pipelines kann man diesen Aufwand zwar "verstecken", allerdings erhöht dies die Abhängigkeiten zwischen Diensten.
- Vor dem Hintergrund vieler/weniger und schneller/langsamer Requests ist in Cloud-nativen Systemen der Einsatz von gRPC daher fast nur noch in 25% dieses Problemraums ratsam (bei vielen schnellen Requests). In allen anderen Fällen, verliert der Performance Vorteil von gRPC erheblich an Wert bzw. kann durch horizontale Skalierung kompensiert werden.
- gRPC wird daher in Cloud-nativen Systemen meist im "inneren" von Systemen eingesetzt, die ein hohes Volumen von schnell zu berechnenden und zu verteilenden Events zu verarbeiten haben (z.B. Messaging).
import flask
import os, time, requests, statistics
import requests, time, os
from statistics import median
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
grpc_svc = os.environ.get("GRPC_SVC", "localhost")
grpc_port = os.environ.get("GRPC_PORT", "5555")
rest_svc = os.environ.get("REST_SVC", "localhost")
rest_port = os.environ.get("REST_PORT", "5000")
channel = grpc.insecure_channel(f"{ grpc_svc }:{ grpc_port }")
stub = helloworld_pb2_grpc.GreeterStub(channel)
app = flask.Flask(__name__)
grpc_s = []
rest_s = []
@app.route('/', methods=['GET'])
def monitor():
grpc_s = []
rest_s = []
n = int(flask.request.args.get('n', '10'))
for i in range(0, n):
while True:
for i in range(0, 100):
start = time.time()
try:
x = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
except:
print(f"Error gRPC call { grpc_svc }:{ grpc_port }")
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
grpc_s.append(time.time() - start)
start = time.time()
try:
y = requests.get(f"http://{ rest_svc }:{ rest_port }/hello/you")
except:
print(f"Error GET http://{ rest_svc }:{ rest_port }/hello/you")
x = requests.get(f"http://{ rest_svc }:{ rest_port }/hello/you")
rest_s.append(time.time() - start)
return ", ".join([
f"gRPC: {statistics.median(grpc_s) * 1000:.2f}ms",
f"REST: {statistics.median(rest_s) * 1000:.2f}ms",
f"n = { n }"
])
print(f"gRPC service => { grpc_svc }:{ grpc_port }")
print(f"REST service => { rest_svc }:{ rest_port }")
app.run(host="0.0.0.0", port=8080)
print(f"gRPC: { median(grpc_s) * 1000 :05.2f}ms REST: { median(rest_s) * 1000 :05.2f}ms")
......@@ -16,11 +16,9 @@ spec:
containers:
- name: monitor
image: {{ CI_REGISTRY_IMAGE }}/grpc:latest
command: ["python3", "-u", "greeter_monitor.py"]
command: ["python3", "-u", "compare.py"]
env:
- name: GRPC_SVC
value: grpc-svc
- name: REST_SVC
value: rest-svc
ports:
- containerPort: 8080
apiVersion: v1
kind: Service
metadata:
name: monitor-svc
spec:
selector:
app: monitor
ports:
- port: 8080
targetPort: 8080
......@@ -2,4 +2,5 @@ grpcio
grpcio-tools
googleapis-common-protos
flask
requests
\ No newline at end of file
requests
tqdm
\ No newline at end of file
import requests, time, os
from statistics import median
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
grpc_svc = os.environ.get("GRPC_SVC", "localhost")
grpc_port = os.environ.get("GRPC_PORT", "5555")
rest_svc = os.environ.get("REST_SVC", "localhost")
rest_port = os.environ.get("REST_PORT", "5000")
channel = grpc.insecure_channel(f"{ grpc_svc }:{ grpc_port }")
stub = helloworld_pb2_grpc.GreeterStub(channel)
grpc_s = []
rest_s = []
while True:
for i in range(0, 100):
start = time.time()
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
grpc_s.append(time.time() - start)
start = time.time()
x = requests.get(f"http://{ rest_svc }:{ rest_port }/hello/you")
rest_s.append(time.time() - start)
print(f"gRPC: { median(grpc_s) * 1000 :05.2f}ms REST: { median(rest_s) * 1000 :05.2f}ms")
import requests
import time
from statistics import mean
from tqdm import tqdm
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
from tqdm import tqdm
channel = grpc.insecure_channel('localhost:5555')
stub = helloworld_pb2_grpc.GreeterStub(channel)
......@@ -14,13 +11,6 @@ grpc_s = []
rest_s = []
for i in tqdm(range(0, 100)):
start = time.time()
# This is a remote procedure call!
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
grpc_s.append(time.time() - start)
start = time.time()
x = requests.get("http://localhost:5000/hello/you")
rest_s.append(time.time() - start)
print(f"gRPC: { mean(grpc_s) * 1000 }ms")
print(f"REST: { mean(rest_s) * 1000 }ms")
\ No newline at end of file
# print(response)
\ No newline at end of file
from concurrent import futures
import grpc
from concurrent import futures
from datetime import datetime
import helloworld_pb2
......@@ -10,11 +9,11 @@ import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
# print(f"{ context.peer() } | { datetime.now().isoformat() } | Request { request } ".strip())
print(f"{ context.peer() } | { datetime.now().isoformat() } | Request { request } ".strip())
return helloworld_pb2.HelloReply(message=f"Hello, { request.name }!")
# Server setup and start
server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
port = 5555
print(f"listening on port { port }")
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment