README.md 15.5 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

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

Nane Kratzke's avatar
Nane Kratzke committed
7
- Definieren eines Dienstes in einer `.proto`-Datei.
Nane Kratzke's avatar
Nane Kratzke committed
8
9
10
11
12
- 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
  - [Ü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)
Nane Kratzke's avatar
Nane Kratzke committed
21
  - [Übung 03: Performancevergleich HTTP (REST) vs. gRPC (auf localhost)](#übung-03-performancevergleich-http-rest-vs-grpc-auf-localhost)
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
22
23
24
25
  - [Ü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
Nane Kratzke committed
33
- [Lens](https://k8slens.dev)
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
## Ü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`

Nane Kratzke's avatar
Nane Kratzke committed
147
## Übung 03: Performancevergleich HTTP (REST) vs. gRPC (auf localhost)
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
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

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
Nane Kratzke's avatar
Nane Kratzke committed
179
180
181
182
   gRPC: 11.01ms REST: 25.45ms
   gRPC: 11.16ms REST: 26.45ms
   gRPC: 11.81ms REST: 27.41ms
   [...]
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
183
   ```
Nane Kratzke's avatar
Nane Kratzke committed
184
4. Beenden Sie die Messung mittels `CTRL-C` (Linux/Windows) oder `CMD-C` (Mac OS).
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
185
186
187
188
189
190
191
192
193
194

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)

Nane Kratzke's avatar
Nane Kratzke committed
195
196
197
198
199
200
201
202
Nachdem wir in Übung 03, die REST und gRPC lokal auf einem Host verglichen haben, wollen wir dies nun in der eher üblichen "Lebensumgebung", d.h. in einem Kubernetes Cluster machen.

Hierzu sind im Verzeichnis `deploy` entsprechende Deployments und Services sowie in der `.gitlab-ci.yml` eine entsprechende Deployment Pipeline vorbereitet.

- `deploy/grpc-dep.yaml` und `deploy/grpc-svc.yaml` deployed insgesamt 5 Pods mit gRPC-API (in einem Cluster von fünf Knoten) und fasst diese zu einem gRPC-Service zusammen.
- `deploy/rest-dep.yaml` und `deploy/rest-svc.yaml` deployed insgesamt 5 Pods mit REST-API (in einem Cluster von fünf Knoten) und fasst diese zu einem REST-Service zusammen.
- `deploy/monitor-dep.yaml` deployed einen Pod mit der `compare.py`-Funktionalität, der den REST- und gRPC-Service analog zu Übung 3 in Kubernetes abfragt und die Latenzen ermittelt und loggt.

Nane Kratzke's avatar
Nane Kratzke committed
203
204
Durch dieses Setting ist sichergestellt, dass der Monitor Pod sowohl Pods auf seinem eigenen Node als auch auf anderen Nodes abfragt, um sowohl Intra- als auch Inter-Node Calls abzudecken.

Nane Kratzke's avatar
Monitor    
Nane Kratzke committed
205
206
Stoßen Sie also bitte jetzt die Deployment Pipeline an und verfolgen Sie in Lens wie die REST- und gRPC-Pods deployed werden. Wenn dies erfolgt ist triggern Sie dann bitte in der Gitlab Pipeline den manuellen `monitor`-Job der `deploy`-Stage.

Nane Kratzke's avatar
Nane Kratzke committed
207
208
Verfolgen Sie in Lens das Log (`Pods -> Monitor Pod (auswählen) -> Log`) des `monitor`-Pods. Sie sollten dann ein Log wie dieses hier in etwa sehen:

Nane Kratzke's avatar
Nane Kratzke committed
209
```Bash
Nane Kratzke's avatar
Nane Kratzke committed
210
211
212
213
214
215
216
217
218
gRPC: 00.66ms REST: 04.70ms
gRPC: 00.63ms REST: 04.59ms
gRPC: 00.61ms REST: 04.41ms
gRPC: 00.60ms REST: 04.39ms
gRPC: 00.59ms REST: 04.40ms
gRPC: 00.60ms REST: 04.42ms
gRPC: 00.61ms REST: 04.47ms
[...]
```
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
219

Nane Kratzke's avatar
Nane Kratzke committed
220
Vermutlich sind die Latenzen anders als auf ihrem lokalen System (je nachdem was für ein System Sie haben). Allerdings sollten auch im Cluster die gRPC-Latenzen gegenüber den REST-Latenzen deutlich niedriger sein. Vermutlich ist dies sogar deutlich ausgeprägter.
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
221
222
223
224
225
226

## 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.
Nane Kratzke's avatar
Nane Kratzke committed
227
- Wie schätzen Sie vor dem Hintergrund der Zahlen die Leistungsfähigkeit des Ihnen zur Verfügung gestellten Clusters im Vergleich zu Ihrem eigenen System ein?
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

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.
Nane Kratzke's avatar
Nane Kratzke committed
262
- gRPC ist ein auf HTTP/2-basiertendes Binärprotokoll im Gegensatz zum HTTP/1-basierten Textprotokoll. gRPC ist daher bis zu einer Größenordnung schneller als übliche HTTP-basierte Request-Response-Verfahren.
Nane Kratzke's avatar
Deploy    
Nane Kratzke committed
263
264
265
- 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.
Nane Kratzke's avatar
Minors    
Nane Kratzke committed
266
267
- gRPC wird daher in Cloud-nativen Systemen meist im "inneren" von Systemen eingesetzt, die durch ein hohes Volumen von schnell zu berechnenden und zu verteilenden Events charakterisiert sind (z.B. Messaging).
- Für Messaging gibt es aber noch Spezial-Lösungen. Mit diesen werden wir uns im kommenden Lab beschäftigen.