README.md 13.2 KB
Newer Older
Nane Kratzke's avatar
Nane Kratzke committed
1
# Lab 09: Request-Response-basierte Interaktion mit gRPC
Nane Kratzke's avatar
Nane Kratzke committed
2

Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
3
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.
Nane Kratzke's avatar
Nane Kratzke committed
4
5
6
7
8
9
10
11
12

Dieses Lab bietet eine grundlegende Einführung in die Arbeit mit gRPC (auf Basis von Python) und umfasst:

- Definieren eines Dienstes in einer .proto-Datei.
- Server- und Client-Code mit Hilfe des Protokollpuffer-Compilers zu generieren
- Die Python gRPC-API für ein einfaches Client-Server-Setting zu nutzen.

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.

Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
13
14
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.

Nane Kratzke's avatar
Nane Kratzke committed
15
16
17
18
## Inhalt
- [Lab 09: Request-Response-basierte Interaktion mit gRPC](#lab-09-request-response-basierte-interaktion-mit-grpc)
  - [Inhalt](#inhalt)
  - [Vorbereitung](#vorbereitung)
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
19
20
21
22
23
24
25
  - [Ü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)
Nane Kratzke's avatar
Nane Kratzke committed
26
27
28
29
30
31
32

## Vorbereitung

Sie benötigen auf Ihren lokalen Entwicklungsrechner:

- [Python 3](https://www.python.org/downloads)
- [Docker](https://www.docker.com/get-started)
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
33
- [Lens](https://k8slens.io)
Nane Kratzke's avatar
Nane Kratzke committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

Führen Sie anschließend bitte folgende Schritte aus:

1. [Forken](https://git.mylab.th-luebeck.de/cloud-native/lab-rest/-/forks/new) Sie bitte dieses GitLab-Repository in Ihren GitLab-Namensraum.
2. Klonen Sie das in Schritt 1 geforkte Repository bitte anschließend auf ihrem lokalen Rechner (`git clone`).
3. Installieren Sie anschließend bitte lokal auf Ihrem Rechner die Kubernetes-IDE [Lens](https://k8slens.dev/).
4. Laden Sie sich Ihre `kubeconfig` Datei im [Moodle-Kurs](https://to-be-donex) herunter.
5. Starten Sie Lens und fügen Sie der IDE die kubeconfig Datei hinzu, um auf Ihren Cluster zugreifen zu können. Sie sollten dann Ihren Namespace in dem für Sie bereitgestellten K8S-Cluster sehen.
6. Erstellen Sie anschließend in Gitlab unter `Einstellungen -> Repository -> Bereitstellungstoken` für das in Schritt 1 geforkte Repository einen Bereitstellungstoken, um selbstgebaute Container Images deployen zu können.
    - **Name:** `Registry read access (deployment)`
    - **Username:** `image-registry` (bitte exakt so!)
    - **Scope:** `read-registry` (nicht mit read repository verwechseln!)
    - Klicken Sie anschließend auf Bereitstellungstoken erstellen und kopieren Sie sich dieses geheime Token in die Zwischenablage!
7. Hinterlegen Sie nun für Gitlab Build-Pipelines dieses geheime Token unter `Einstellungen -> CI/CD -> Variables (Aufklappen) -> ADD VARIABLE` als CI/CD-Variable.
    - **Key:** `CI_REGISTRY_TOKEN` (exakt so)
    - **Value:** Fügen Sie hier das geheime Token (Schritt 2) aus der Zwischenablage ein.
    - **Type:** `Variable` (nichts anderes)
    - **Flags:** Selektieren Sie `Mask Variable` damit das geheime Token in Log-Dateien maskiert wird.
8. Hinterlegen Sie in Ihrem geforkten GitLab-Repository nun die `kubeconfig`-Datei als CI-Environment-Variable mittels `Einstellungen -> CI/CI -> Variables (Aufklappen) -> ADD VARIABLE` (setzen Sie hierfür folgende Werte)
    - **Key:** `KUBECONFIG` (Exakt so eingeben)
    - **Value:** Inhalt der kubeconfig (z.B. mittels Copy-Paste aus Editor)
    - **Typ:** `File` (Auswählen, WICHTIG!!!)

Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
## Ü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).