Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Date with gRPC

A Date with gRPC

Pain of gRPC on Kubernetes
YWC Programmer Meetup #6

Transcript

  1. A Date with gRPC YWC Programmer Meetup 2019

  2. Me • Manatsawin Hanmongkolchai • Architect at Wongnai - We

    build the platform for Wongnai of tomorrow • Owner at TipMe - Donate platform for streamers
  3. Today's Agenda • RPC vs REST • Brief introduction to

    gRPC • gRPC on Kubernetes • … ??
  4. RPC?

  5. REST

  6. import requests requests.post("http://sealerd/documents/1/seal", json={ "key": 1, })

  7. What now?

  8. XML? JSON?

  9. { "success": true }

  10. { "response": { "success": true }, "status": 200 }

  11. Error handling

  12. try: req = requests.post("http://sealerd/documents/1/seal") except requests.HTTPError: print("Server error") return body

    = req.json() if "error" in body: print(body["error"]) return print("Seal success!!") Why this is not exception?
  13. try: req = requests.post("http://sealerd/documents/1/seal") except requests.HTTPError: print("Server error") return body

    = req.json() if "error" in body: print(body["error"]) return print("Seal success!!") How could I know there is error field? What if load balancer errors?
  14. It's not hard to solve It just more boilerplate to

    write (Or you could make it a library)
  15. Then why don't you just use gRPC?

  16. gRPC 101

  17. Step 1: Write a service declaration

  18. syntax = "proto3"; package tipme.sealerd; service Sealerd { rpc Seal

    (SealRequest) returns (SealResponse); } message SealRequest { string path = 1; } message SealResponse { } Free API Docs, with types!
  19. Step 2: Generate

  20. python -m grpc_tools.protoc -I=. \ --python_out=. \ --grpc_python_out=. sealerd.proto

  21. # -*- coding: utf-8 -*- # Generated by the protocol

    buffer compiler. DO NOT EDIT! # source: sealerd/sealerd.proto import sys _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='sealerd/sealerd.proto', package='tipme.sealerd', syntax='proto3', serialized_options=None, serialized_pb=_b('\n\x15sealerd/sealerd.proto\x12\rtipme.sealerd\"\x1b\n\x0bSealRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x0e\n\x0cSealResponse2J\n\x07Sealerd\x12?\n\x04Seal\x12\x1a.tipme.sealerd.SealRequest\x1a\x1b.tipme.sealerd.SealResponseb\x06proto3') ) _SEALREQUEST = _descriptor.Descriptor( name='SealRequest', full_name='tipme.sealerd.SealRequest', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='path', full_name='tipme.sealerd.SealRequest.path', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], nested_types=[], enum_types=[ ], serialized_options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=40, serialized_end=67,
  22. Step 3: Run

  23. import grpc from proto_gen.sealerd.sealerd_pb2 import SealRequest from proto_gen.sealerd.sealerd_pb2_grpc import SealerdStub

    channel = grpc.insecure_channel("sealerd:4000") client = SealerdStub(channel) result = client.Seal(SealRequest(path=path)) print(result)
  24. What you get • Exception on errors • Typed request

    & response • More efficient serialization • (Optional) Server to server verification & encryption • Connection reuse • Load balancing
  25. Sounds great?

  26. gRPC, in real world, in Kubernetes

  27. How Kubernetes do load balancing

  28. Kubernetes service balance connections, not calls

  29. What we think

  30. What we actually get with gRPC

  31. What we actually get with gRPC

  32. It's even worse: server shutdown

  33. Client Server Client Client Server Client Server Connection Reset Service

    IP
  34. None
  35. None
  36. Solution

  37. 1. gRPC client side load balance

  38. None
  39. It's Go only

  40. 2. Read Kubernetes Blog

  41. None
  42. (Yes, I used their image earlier)

  43. 2. Use service mesh (Maybe it'll be ready in 2020….)

  44. 3. Disable connection pool? It'll be slower, but it won't

    break my infra
  45. None
  46. None
  47. None
  48. No disable! Only set connection lifetime

  49. 4. Use REST

  50. 5. Join guild of gRPC haters

  51. None
  52. None
  53. import requests requests.post("http://sealerd/twirp/tipme.Sealerd/Seal", json={ "document": 1, "key": 1, })

  54. import requests requests.post("http://sealerd/twirp/tipme.Sealerd/Seal", json={ "key": 1, }) but… • Client

    library • Still standardized request/response/error • Send either Protobuf or JSON = easy to debug • HTTP 1.1 + Bring your own server = can integrate into existing stack • No streaming/bidirectional support (yet)
  55. Porting

  56. Have fun! And pray for service mesh