{"id":21659333,"url":"https://github.com/drhelius/grpc-demo","last_synced_at":"2025-07-30T23:34:44.690Z","repository":{"id":56432328,"uuid":"255909624","full_name":"drhelius/grpc-demo","owner":"drhelius","description":"A demo to showcase technologies like Go, gRPC, Istio, Helm and Kubernetes Operators.","archived":false,"fork":false,"pushed_at":"2021-06-09T10:37:19.000Z","size":888,"stargazers_count":36,"open_issues_count":0,"forks_count":10,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-10T04:39:20.757Z","etag":null,"topics":["grpc","helm-chart","kubernetes-operators","service-mesh"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drhelius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-15T12:27:52.000Z","updated_at":"2024-02-17T00:08:48.000Z","dependencies_parsed_at":"2022-08-15T18:40:12.359Z","dependency_job_id":null,"html_url":"https://github.com/drhelius/grpc-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/drhelius/grpc-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drhelius%2Fgrpc-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drhelius%2Fgrpc-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drhelius%2Fgrpc-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drhelius%2Fgrpc-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drhelius","download_url":"https://codeload.github.com/drhelius/grpc-demo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drhelius%2Fgrpc-demo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267960839,"owners_count":24172508,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["grpc","helm-chart","kubernetes-operators","service-mesh"],"created_at":"2024-11-25T09:30:54.271Z","updated_at":"2025-07-30T23:34:44.658Z","avatar_url":"https://github.com/drhelius.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# Istio gRPC Golang Demo\n\n![Istio gRPC Golang Demo](images/kiali3.png)\n\nThis is a demo to showcase the features of some of the technologies that may be involved in modern microservice development and operation within a Kubernetes platform.\n\nThese technologies include [Go](https://golang.org/), [gRPC](https://grpc.io/), [Istio](https://istio.io/), [Helm](https://helm.sh/) and [Kubernetes Operators](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/).\n\nNote that this is just a demo and may not represent the real life. Technologies are mixed but they may not be used all at once. Hopefully, it could help you to understand basic concepts that may be the building blocks to create more complex gRPC microservices in a service mesh.\n\nFor deployment, three different mechanisms are provided. You can also choose whether you use Istio or not.\n\nAll the examples are provided for Red Hat OpenShift but could be applied to any Kubernetes distribution. If you want to run OpenShift on your laptop you may want to try [Red Hat CodeReady Containers](https://developers.redhat.com/products/codeready-containers/overview).\n\n## Index\n\n1. [Components](#1---components)\n    - [Microservices](#microservices)\n    - [Deployment Alternatives](#deployment-alternatives)\n    - [Istio](#istio)\n    - [Requirements](#requirements)\n2. [Architecture](#2---architecture)\n    - [Demo API](#demo-api)\n3. [gRPC services in Go](#3---grpc-services-in-go)\n    - [Protocol Buffers](#protocol-buffers)\n    - [Go Implementation](#go-implementation)\n    - [Testing the Services](#testing-the-services)\n4. [Istio Service Mesh in OpenShift](#4---istio-service-mesh-in-openshift)\n5. [Helm Charts](#5---helm-charts)\n    - [Package and distribute a Helm Chart](#package-and-distribute-a-helm-chart)\n    - [Deploy the demo using a Helm Chart (with Istio)](#deploy-the-demo-using-a-helm-chart-with-istio)\n    - [Deploy the demo using a Helm Chart (without Istio)](#deploy-the-demo-using-a-helm-chart-without-istio)\n6. [OpenShift Templates](#6---openshift-templates)\n    - [Deploy the demo using an OpenShift Template (with Istio)](#deploy-the-demo-using-an-openshift-template-with-istio)\n    - [Deploy the demo using an OpenShift Template (without Istio)](#deploy-the-demo-using-an-openshift-template-without-istio)\n7. [Kubernetes Operators](#7---kubernetes-operators)\n    - [Demo Operator](#demo-operator)\n    - [Deploy the demo using a Kubernetes Operator (without Istio)](#deploy-the-demo-using-a-kubernetes-operator-without-istio)   \n8. [Observability with Kiali](#8---observability-with-kiali)\n\n## 1 - Components\n\n### Microservices\n\n  - [Protobuf Repo](https://github.com/drhelius/grpc-demo-proto)\n  - [Account Service](https://github.com/drhelius/grpc-demo-account)\n  - [Order Service](https://github.com/drhelius/grpc-demo-order)\n  - [Product Service](https://github.com/drhelius/grpc-demo-product)\n  - [User Service](https://github.com/drhelius/grpc-demo-user)\n\n### Deployment Alternatives\n\n  - [Helm Charts](helm-charts)\n  - [OpenShift Templates](openshift-templates)\n  - [Kubernetes Operator](https://github.com/drhelius/grpc-demo-operator)\n\n### Istio\n\n  - [Service Mesh Control Plane](openshift-service-mesh)\n  \n### Requirements\n\nKubernetes | Istio | Helm | Go\n--- | --- | --- | ---\n1.13+ | 1.6+ | 3.2.0+ | 1.13+\n\n## 2 - Architecture\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/architecture.png\" alt=\"Demo Services\"/\u003e\n\u003c/p\u003e\n\nThis demo is composed of four microservices modeling how a customer buy products in an eCommerce:\n\n- [Account](https://github.com/drhelius/grpc-demo-account): Models a user account. This is a composite that aggregates data about the user personal information and product orders made.\n- [Order](https://github.com/drhelius/grpc-demo-order): This is a group of products ordered by the user in a single transaction.\n- [User](https://github.com/drhelius/grpc-demo-user): The user personal information.\n- [Product](https://github.com/drhelius/grpc-demo-product): A description of a product in the store including price an details.\n\nAll four microservices are written in Go using gRPC as the main communication framework. Additionally, an HTTP (REST) listener is also provided for each of them. \n\nIn addition to this four services, the *Order* service uses [`httpbin.org`](https://httpbin.org) for simulating service calls to external resources.\n\nThe demo can be setup using Istio or without using it.\n\nThree deployment methods are provided for demonstration purposes, you are not expected to use them all at once:\n\n- Helm Chart\n- OpenShift Template\n- Kubernetes Operator\n\nNote that, for simplicity, the operator is only provided for deploying the demo microservices without Istio.\n\nYou can have the services deployed both with Istio and without it at the same time but you may want to deploy them in different namespaces.\n\nAll three deployment methods will create the necessary Kubernetes resources to run all four microservices. These resources are `Deployments`, `Services` and `Routes`. If you are running the demo with Istio, they will also create `VirtualServices`, `DestinationRules`, `Gateways` and `ServiceEntries`.\n\n### Demo API\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/kiali2.png\" alt=\"Service Mesh architecture\" width=\"600\"/\u003e\n\u003c/p\u003e\n\n#### Account Service\n      \nThe *Account* service is the entry point. It aggregates data from *User* and *Order* services. When you query the *Account* service it calls *User* and *Order* services under the hood. It represents an user account with personal information and past orders.\n\n- Account\n  - Create\n    - Request:\n      - string id: The account ID\n      - User user: The user data\n      - Order Array orders: List of orders made\n    - Response:\n      - string id: The new created account ID\n  - Read\n    - Request:\n      - string id: The account ID\n    - Response:\n      - string id: The account ID\n      - User user: The user data\n      - Order Array orders: List of orders made\n     \n#### Order Service\n\nThe *Order* service simulates a collection of products purchased at the same time. When you query the *Order* service it calls *Product* service under the hood.\n \n- Order\n  - Create\n    - Request:\n      - string id: The order ID\n      - string name: A name for the order\n      - int date: The date when order was made\n      - Product Array: List of purchased products\n      - string ip: Public IP collected during the purchase, just for testing httpbin.org    \n    - Response:\n      - string id: The new created order ID\n  - Read\n    - Request:\n      - string id: The order ID\n    - Response:\n      - string id: The order ID\n      - string name: A name for the order\n      - int date: The date when order was made\n      - Product Array: List of purchased products\n      - string ip: Public IP collected during the purchase, just for testing httpbin.org\n     \n#### Product Service\n\nThe *Product* service is just a representation of a single product information.\n\n- Product\n  - Create\n    - Request:\n      - string id: The product ID\n      - string name: Product name\n      - string description: Product description\n      - int price: Product price\n    - Response:\n      - string id: The new created product ID\n  - Read\n    - Request:\n      - string id: The product ID\n    - Response:\n      - string id: The product ID\n      - string name: Product name\n      - string description: Product description\n      - int price: Product price\n\n#### User Service\n\nThe *User* service simulates personal information data.\n\n- User\n  - Create\n    - Request:\n      - string id: The user ID\n      - string name: User name\n      - string email: User email\n    - Response:\n      - string id: The new created user ID\n  - Read\n    - Request:\n      - string id: The user ID\n    - Response:\n      - string id: The user ID\n      - string name: User name\n      - string email: User email\n      \n## 3 - gRPC services in Go\n\ngRPC is a framework to connect services by using *Remote Procedure Calls*, this means that a client application can directly call a method on a server application on a different machine as if it were a local object.\n\nIt works across languages and platforms and it is increasingly being used in high performance environments. Its protocol can achieve bi-directional streaming with HTTP2 based transport.\n\ngRPC uses [Protocol Buffers](https://developers.google.com/protocol-buffers/docs/overview) (*protobuf* files) to define the structure for the data you want to transfer.\n\n### Protocol Buffers\n\nBefore diving into the code let examine the *protobuf* files that define the API used in this demo. There is a shared Git repository where all proto files are defined:\n\n- [grpc-demo-proto](https://github.com/drhelius/grpc-demo-proto)\n\nThis Git repo stores the proto files for all the services in the demo. Some services (*Account* and *Order*) work like composites. This mean they will aggregate information from other services (*User* and *Product*). In order to do that they share some common types. Sharing a repo with all the proto files let you share types between different proto files easily.\n\nIf you need to share data types, sharing a Git repo for all services or creating a repo for each service is a difficult and hairy decision. This demo will stick with a single repo approach for simplicity.\n\nThere is a directory for each service in the repo. In each directory there is a proto file describing the data types that will be used in the service. This is the *User* proto file:\n\n```proto\nsyntax = \"proto3\";\noption go_package = \"github.com/drhelius/grpc-demo-proto/user\";\n\npackage user;\n\nimport \"google/api/annotations.proto\";\n\nservice UserService {\n    rpc Create (CreateUserReq) returns (CreateUserResp) {\n        option (google.api.http) = {\n            post: \"/v1/user\"\n            body: \"user\"\n        };\n    }\n    rpc Read (ReadUserReq) returns (ReadUserResp) {\n        option (google.api.http) = {\n            get: \"/v1/user/{id}\"\n        };\n    }\n}\n\nmessage User {\n    string id = 1;\n    string name = 2;\n    string email = 3;\n}\n\nmessage CreateUserReq {\n    User user = 1;\n}\n\nmessage CreateUserResp {\n    string id = 1;\n}\n\nmessage ReadUserReq {\n    string id = 1;\n}\n\nmessage ReadUserResp {\n    User user = 1;\n}\n```\n\nIn this proto file a service called `UserService` is described. The service has two *methods*, `Create` and `Read`. Each *method* use *messages* to transfer data. \n\nThe `Read` *method* uses the `ReadUserReq` message as input and `ReadUserResp` as output. `ReadUserReq` is defined as a simple data structure with a single string that represents the User *id*. `ReadUserResp` is defined with a field called *user* of type `User`.\n\nThe type or *message* `User` is defined as a group of three strings, *id*, *name* and *email*.\n\nIn summary, the `Read` *method* expects a user ID and returns the user data.\n\nNote that this proto file is *importing* `google/api/annotations.proto` to annotate each *method* in the service with `option (google.api.http)`. This annotation let you [transcode HTTP to gRPC](https://cloud.google.com/endpoints/docs/grpc/transcoding) and vice versa, so that clients can access your gRPC API by using HTTP/JSON:\n\n```proto\n...\n        option (google.api.http) = {\n            post: \"/v1/user\"\n            body: \"user\"\n        };\n\n...\n\n        option (google.api.http) = {\n            get: \"/v1/user/{id}\"\n        };\n...\n```\n\nSo, to create a new User using HTTP you will `POST` the JSON data to `/v1/user`. For retrieving User data you will `GET` from `/v1/user/{id}`.\n\nThis is done by using a [gRPC Gateway](https://github.com/grpc-ecosystem/grpc-gateway). This gateway will pass all the messages to the gRPC server and transcode all inputs and outputs to HTTP/JSON.\n\nHTTP transcoding is not required in gRPC but it lets you mix gRPC with RESTful services. In this demo it lets you use simple `curl` commands for testing the services. Note that there is a [`grpcurl`](https://github.com/fullstorydev/grpcurl) tool too.\n\nThis is the *Account* proto file:\n\n```proto\nsyntax = \"proto3\";\noption go_package = \"github.com/drhelius/grpc-demo-proto/account\";\n\npackage account;\n\nimport \"google/api/annotations.proto\";\nimport \"user/user.proto\";\nimport \"order/order.proto\";\n\nservice AccountService {\n    rpc Create (CreateAccountReq) returns (CreateAccountResp) {\n        option (google.api.http) = {\n            post: \"/v1/account\"\n            body: \"account\"\n        };\n    }\n    rpc Read (ReadAccountReq) returns (ReadAccountResp) {\n        option (google.api.http) = {\n            get: \"/v1/account/{id}\"\n        };\n    }\n}\n\nmessage Account {\n    string id = 1;\n    user.User user = 2;\n    repeated order.Order orders = 3;\n}\n\nmessage CreateAccountReq {\n    Account account = 1;\n}\n\nmessage CreateAccountResp {\n    string id = 1;\n}\n\nmessage ReadAccountReq {\n    string id = 1;\n}\n\nmessage ReadAccountResp {\n    Account account = 1;\n}\n```\n\nThe *Account* proto file uses even more imports: `user/user.proto` and `order/order.proto`. Importing other proto files lets you use the *messages* defined in those files in your current proto file.\n\nThe *Account* service uses the messages from *User* and *Order* services because it aggregates information from both:\n\n```proto\nmessage Account {\n    string id = 1;\n    user.User user = 2;\n    repeated order.Order orders = 3;\n}\n```\n\nThe keyword `repeated` indicates that the `orders` field can be repeated any number of times (including zero).\n\nOnce you have defined your proto files you will use the `protoc` protocol buffer compiler to generate service interface code and stubs in your chosen language.\n\nIn the Git proto repository there is a [`build.sh`](https://github.com/drhelius/grpc-demo-proto/blob/master/build.sh) script to compile all the proto files:\n\n```shell\nGOOGLE_APIS=${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis\n\nfor proto in user product order account\ndo\n  echo \"Removing ${proto}/*.go\"\n  rm -f ${proto}/*.go\n  echo \"Generating ${proto}/${proto}.go\"\n  protoc -I . -I ${GOOGLE_APIS} --go_out=paths=source_relative,plugins=grpc:. ${proto}/${proto}.proto\n  echo \"Generating ${proto}/${proto}.gw.go\"\n  protoc -I . -I ${GOOGLE_APIS} --grpc-gateway_out=paths=source_relative:. ${proto}/${proto}.proto\ndone\n```\n\nBecause we are using the additional `google.api.http` API to transcode HTTP we need to tell `protoc` where to look for the [gRPC Gateway implementation](https://github.com/grpc-ecosystem/grpc-gateway).\n\nThe generated code is also committed to this repo so we can use it later as a Go dependency in the service implementation. There are two generated files, `${proto}.go` for normal client/server gRPC code and `${proto}.gw.go` for the HTTP gateway.\n\nIn order to use the `protoc` tool you need to install it runnning this commands before. Refer to the [official documentation](https://grpc.io/docs/languages/go/quickstart/) for more information:\n\n```shell\n$ export GO111MODULE=on  # Enable module mode\n$ go get github.com/golang/protobuf/protoc-gen-go\n$ export PATH=\"$PATH:$(go env GOPATH)/bin\"\n```\n\n### Go Implementation\n\nAll four services are implemented in the same way.\n\nIn `main.go` two goroutines are created for both serving gRPC and HTTP:\n\n```go\nfunc main() {\n\tvar wg sync.WaitGroup\n\n\twg.Add(1)\n\tgo grpc.Serve(\u0026wg, \"5000\")\n\n\twg.Add(1)\n\tgo http.Serve(\u0026wg, \"5000\", \"8080\")\n\n\twg.Wait()\n}\n```\n\ngRPC server is quite straightforward. It uses the code generated by `protoc` and imported from the proto files Git repository. The server is being created with an [OpenTracing](https://opentracing.io/) interceptor to enable *span* manipulation within the service:\n\n```go\nfunc Serve(wg *sync.WaitGroup, port string) {\n\tdefer wg.Done()\n\n\tlis, err := net.Listen(\"tcp\", \":\"+port)\n\n\tif err != nil {\n\t\tlog.Fatalf(\"[User] GRPC failed to listen: %v\", err)\n\t}\n\n\ts := grpc.NewServer(grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(\n\t\tgrpc_opentracing.UnaryServerInterceptor(grpc_opentracing.WithTracer(opentracing.GlobalTracer())),\n\t)))\n\n\tuser.RegisterUserServiceServer(s, \u0026impl.Server{})\n\n\tlog.Printf(\"[User] Serving GRPC on localhost:%s ...\", port)\n\n\tif err := s.Serve(lis); err != nil {\n\t\tlog.Fatalf(\"[User] GRPC failed to serve: %v\", err)\n\t}\n}\n```\n\nThe HTTP server is a little bit more complex because, in reality, it is a [gRPC Gateway](https://github.com/grpc-ecosystem/grpc-gateway) as commented before. It needs to know the port where gRPC is serving in order to connect to it, pass it all the messages and transcode all inputs and outputs. In addition, an *annotator* is being used to ensure that OpenTracing headers are propagated:\n\n```go\nfunc Serve(wg *sync.WaitGroup, grpc_port string, http_port string) {\n\tdefer wg.Done()\n\n\tctx := context.Background()\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tannotators := []annotator{injectHeadersIntoMetadata}\n\n\tmux := runtime.NewServeMux(runtime.WithMetadata(chainGrpcAnnotators(annotators...)))\n\topts := []grpc.DialOption{grpc.WithInsecure()}\n\terr := user.RegisterUserServiceHandlerFromEndpoint(ctx, mux, fmt.Sprintf(\":%s\", grpc_port), opts)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tlog.Printf(\"[User] Serving HTTP on localhost:%s ...\", http_port)\n\n\thttp.ListenAndServe(fmt.Sprintf(\":%s\", http_port), mux)\n}\n```\n\nYou can find the implementation of the server in the `impl` package, it implements the service interface (*methods* and *messages*) defined in the proto file:\n\n```go\ntype Server struct {\n\tuser.UnimplementedUserServiceServer\n}\n\nfunc (s *Server) Create(ctx context.Context, in *user.CreateUserReq) (*user.CreateUserResp, error) {\n\n\tlog.Printf(\"[User] Create Req: %v\", in.GetUser())\n\n\tr := \u0026user.CreateUserResp{Id: strconv.Itoa(randomdata.Number(1000000))}\n\n\tlog.Printf(\"[User] Create Res: %v\", r.GetId())\n\n\treturn r, nil\n}\n\nfunc (s *Server) Read(ctx context.Context, in *user.ReadUserReq) (*user.ReadUserResp, error) {\n\n\tlog.Printf(\"[User] Read Req: %v\", in.GetId())\n\n\tr := \u0026user.ReadUserResp{User: \u0026user.User{Id: in.GetId(), Name: randomdata.FullName(randomdata.RandomGender), Email: randomdata.Email()}}\n\n\tlog.Printf(\"[User] Read Res: %v\", r.GetUser())\n\n\treturn r, nil\n}\n```\n\nServices that calls other services, like the *Account* service, use gRPC clients:\n\n```go\nvar UserService user.UserServiceClient\n\nfunc init() {\n\tlog.Printf(\"[Account] Dialing to 'user:5000' ...\")\n\n\tkeepAliveParams := keepalive.ClientParameters{\n\t\tTime:                5 * time.Second,\n\t\tTimeout:             time.Second,\n\t\tPermitWithoutStream: true,\n\t}\n\n\tconn, err := grpc.Dial(\"user:5000\", grpc.WithInsecure(), grpc.WithBlock(), grpc.FailOnNonTempDialError(true), grpc.WithKeepaliveParams(keepAliveParams), grpc.WithStreamInterceptor(\n\t\tgrpc_opentracing.StreamClientInterceptor(\n\t\t\tgrpc_opentracing.WithTracer(opentracing.GlobalTracer()))))\n\tif err != nil {\n\t\tlog.Fatalf(\"[Account] Error dialing to User service: %v\", err)\n\t}\n\n\tUserService = user.NewUserServiceClient(conn)\n}\n```\n\n### Testing the Services\n\nYou can test each service individually but it is easier to test the *Account* service directly as this service will end up calling all the others.\n\nIf you are using Istio, the *Account* service will be exposed using an `IngressGateway` and an OpenShift `Route`. If not, only an OpenShift `Route` will be created.\n\nYou can invoke the *Account* service with the following command, given that `account-grpc-demo.mycluster.com` is the *fqdn* of your exposed `Route`. You can use any number for the account ID:\n\n`$ curl http://account-grpc-demo.mycluster.com/v1/account/01234`\n\nFor your reference, the *Account* service `Read` method response looks similar to this (in JSON):\n\n```json\n{\n    \"account\": {\n        \"id\": \"01234\",\n        \"user\": {\n            \"id\": \"261782\",\n            \"name\": \"Addison Davis\",\n            \"email\": \"avathompson150@test.com\"\n        },\n        \"orders\": [\n            {\n                \"id\": \"523773\",\n                \"name\": \"Goosebold\",\n                \"date\": \"319615\",\n                \"products\": [\n                    {\n                        \"id\": \"322704\",\n                        \"name\": \"Watchertwisty\",\n                        \"description\": \"She stared at him in astonishment, and as she read something of the significant hieroglyphic of his battered face, her lips whitened.\",\n                        \"price\": 164\n                    },\n                    {\n                        \"id\": \"897965\",\n                        \"name\": \"Slicerdot\",\n                        \"description\": \"I protest, even warmly, that neither he nor his sorrows were in my intention.\",\n                        \"price\": 399\n                    },\n                    {\n                        \"id\": \"575966\",\n                        \"name\": \"Ladybitter\",\n                        \"description\": \"The sun set; the dusk fell on the stream, and lights began to appear along the shore. The Chapman light–house, a three–legged thing erect on a mud–flat, shone strongly.\",\n                        \"price\": 226\n                    }\n                ],\n                \"ip\": \"0.0.0.0\"\n            },\n            {\n                \"id\": \"530053\",\n                \"name\": \"Shieldpatch\",\n                \"date\": \"744632\",\n                \"products\": [\n                    {\n                        \"id\": \"298342\",\n                        \"name\": \"Falconcoconut\",\n                        \"description\": \"And with that he went off to see my father, taking me with him by the arm.\",\n                        \"price\": 495\n                    }\n                ],\n                \"ip\": \"0.0.0.0\"\n            },\n            {\n                \"id\": \"842957\",\n                \"name\": \"Raptorthunder\",\n                \"date\": \"106101\",\n                \"products\": [\n                    {\n                        \"id\": \"67822\",\n                        \"name\": \"Pegasusrust\",\n                        \"description\": \"He completely abandoned the child of his marriage with Adelaida Ivanovna, not from malice, nor because of his matrimoni- al grievances, but simply because he forgot him.\",\n                        \"price\": 821\n                    },\n                    {\n                        \"id\": \"173082\",\n                        \"name\": \"Gemrain\",\n                        \"description\": \"Have I come to Utopia to hear this sort of thing?\",\n                        \"price\": 542\n                    }\n                ],\n                \"ip\": \"0.0.0.0\"\n            }\n        ]\n    }\n}\n```\n\nKeep reading for in-depth information about how to deploy the demo services.\n\n## 4 - Istio Service Mesh in OpenShift\n\nIn order to install OpenShift Service Mesh you should go through the steps explained in the [official docs](https://docs.openshift.com/container-platform/4.7/service_mesh/service_mesh_install/preparing-ossm-installation.html). The following is a simplified guide.\n\nIstio in OpenShift is installed by running a set of operators. Before installing the Red Hat Service Mesh operator you have to install the Elasticsearch, Jaeger and Kiali operators, in this order.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/operators.png\" alt=\"Red Hat Service Mesh Operators\"/\u003e\n\u003c/p\u003e\n\n### Install Elasticsearch Operator\n\n- Update Channel: 4.7\n- Installation Mode: All namespaces\n- Installed Namespace: openshift-operators\n- Approval Strategy: Automatic\n\n### Install Red Hat OpenShift Jaeger Operator\n\n- Update Channel: stable\n- Installation Mode: All namespaces\n- Installed Namespace: openshift-operators\n- Approval Strategy: Automatic\n\n### Install Kiali Operator (provided by Red Hat)\n\n- Update Channel: stable\n- Installation Mode: All namespaces\n- Installed Namespace: openshift-operators\n- Approval Strategy: Automatic\n\n### Install Red Hat OpenShift Service Mesh operator\n\n- Update Channel: stable\n- Installation Mode: All namespaces\n- Installed Namespace: openshift-operators\n- Approval Strategy: Automatic\n\n### Create istio-system project\n\nWith all four required operators installed in your cluster you are ready to deploy Istio.\n\nFirst create a namespace for the control plane, the name `istio-system` is recommended:\n\n`$ oc new-project istio-system`\n\n### Deploy Service Mesh control plane\n\nOnce the project is ready you can create the control plane.\n\nA Service Mesh Control Plane manifest is [provided in this repo](openshift-service-mesh/service-mesh-control-plane.yaml). Use it to bootstrap the installation of Istio in OpenShift:\n\n`$ kubectl apply -f openshift-service-mesh/service-mesh-control-plane.yaml -n istio-system`\n\nIstio operator will then create all the deployments that conform the control plane. After a few minutes it should look like this:\n\n```bash\n$ kubectl get deployments -n istio-system\nNAME                   READY   UP-TO-DATE   AVAILABLE   AGE\ngrafana                1/1     1            1           4m1s\nistio-egressgateway    1/1     1            1           4m6s\nistio-ingressgateway   1/1     1            1           4m8s\nistiod-basic           1/1     1            1           5m2s\njaeger                 1/1     1            1           4m9s\nkiali                  1/1     1            1           2m6s\nprometheus             1/1     1            1           4m29s\n```\n\n## 5 - Helm Charts\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/helm.png\" alt=\"Helm Release\"/\u003e\n\u003c/p\u003e\n\nHelm Charts are an easy and powerful tool for deploying your services.\n\nIn this demo there are two different charts for deploying the services both with Istio and without it:\n\n- [grpc-demo-services-istio](helm-charts/grpc-demo-services-istio/Chart.yaml)\n- [grpc-demo-services](helm-charts/grpc-demo-services/Chart.yaml)\n\nThese charts deploy all four services at once. This is convenient for this demo but in real life you may want to isolate each service lifecycle by installing them independently.\n\nA chart is a Helm package where you define [templates](helm-charts/grpc-demo-services-istio/templates) that will be used to create all the resource definitions to run whatever you wish in a Kubernetes cluster.\n\nThese templates can contain references to variables, functions, loops and conditionals that will be rendered when the chart is installed.\n\nThis example shows a `Gateway` template for the *Account* service. This `Gateway` is part of the Istio configuration in order to expose the service outside the mesh. Note that variables are being used to set up some values. The value of this variables will be provided when the chart is installed:\n\n```yaml\napiVersion: networking.istio.io/v1alpha3\nkind: Gateway\nmetadata:\n  labels:\n    app: account\n    app.kubernetes.io/name: account\n    app.kubernetes.io/component: service\n    app.kubernetes.io/part-of: {{ .Values.appName }}\n    group: {{ .Values.appName }}\n  name: account\nspec:\n  selector:\n    istio: ingressgateway\n  servers:\n  - hosts:\n    - {{ .Values.account.route }}\n    port:\n      name: http2\n      number: 80\n      protocol: HTTP2\n```\n\nYou can also provide [default values](helm-charts/grpc-demo-services-istio/values.yaml) for any variable.\n\nRefer to the [official docs](https://helm.sh/docs/topics/charts/) for more information on how to develop a Helm Chart.\n\n### Package and distribute a Helm Chart\n\nOnce you have developed a Helm Chart you can make a package and create a Helm repository to distribute it.\n\nHelm repositories are simple web servers that host tgz files. Each chart is distributed as a compressed tgz file. In addition to the charts you need an `index.yaml`. This index will contain the information of the charts in the Helm repo.\n\nIn this demo, the Helm repo is provided by using [GitHub Pages](https://pages.github.com/). GitHub let you use a directory in your Git repository to store web content. You can use this directory to store some charts and the `index.yaml` file.\n\nThis is the URL for the GitHub pages in this Git repo: \u003chttps://drhelius.github.io/grpc-demo/\u003e\n\nIf you visit this URL with your browser you will face a 404 as there isn't any web content at all. But Helm knows there is a Helm repository there because it can find the `index.yaml` file: \u003chttps://drhelius.github.io/grpc-demo/index.yaml\u003e\n\nUse this commands to compress and package the charts into a tgz file:\n\n```bash\n$ helm package helm-charts/grpc-demo-services\n$ helm package helm-charts/grpc-demo-services-istio\n```\n\nThe output will be a tgz file for each chart. Put these tgz files in the same directory:\n\n```bash\n$ mv grpc-demo-services-*.tgz docs/\n```\nIn this same directory you are going to generate the `index.yaml` file too.\n\nRun the following command to create the index. Specify the directory where the tgz files are located and the URL where you are expecting to publish the Helm repo. It will read the directory and generate an index file based on the charts found:\n\n`$ helm repo index docs --url https://drhelius.github.io/grpc-demo/`\n\nNow you can upload the whole directory to your desired web server. Your users can grab your charts by running:\n\n```bash\n$ helm repo add grpc-demo https://drhelius.github.io/grpc-demo/\n\"grpc-demo\" has been added to your repositories\n```\n\n### Deploy the demo using a Helm Chart (with Istio)\n\nCreate a project to deploy the demo services if you haven't done so:\n\n`$ oc new-project grpc-demo-istio`\n\nCreate the service mesh [Member Roll](https://docs.openshift.com/container-platform/4.7/service_mesh/service_mesh_install/installing-ossm.html#ossm-member-roll-create_installing-ossm) if you haven't done so. This will tell Istio to start monitoring the namespaces specified.\n\nA Service Mesh Member Roll manifest is [provided in this repo](openshift-service-mesh/service-mesh-member-roll.yaml). It includes the `grpc-demo-istio` namespace. If you are using a different name for the project you should change it accordingly.\n\nUse it to create the Member Roll:\n\n`$ kubectl apply -f openshift-service-mesh/service-mesh-member-roll.yaml -n istio-system`\n\nIn this example you are going to use the [provided Helm Chart](helm-charts/grpc-demo-services-istio/Chart.yaml) for deploying the services using Istio.\n\nMake sure you are working with the right namespace:\n\n`$ oc project grpc-demo-istio`\n\nAdd the chart repository to your helm client and name it `grpc-demo`:\n\n```bash\n\n$ helm repo add grpc-demo https://drhelius.github.io/grpc-demo/\n\"grpc-demo\" has been added to your repositories\n\n$ helm repo list\nNAME        URL\ngrpc-demo   https://drhelius.github.io/grpc-demo/\n```\n\nMake sure you get the latest list of charts:\n\n```bash\n$ helm repo update\nHang tight while we grab the latest from your chart repositories...\n...Successfully got an update from the \"grpc-demo\" chart repository\nUpdate Complete. ⎈ Happy Helming!⎈\n```\n\nThe chart you are going to install is called `grpc-demo-services-istio`. You can inspect the chart before installing it in your cluster:\n\n```bash\n$ helm show chart grpc-demo/grpc-demo-services-istio\napiVersion: v2\ndescription: A group of interconnected GRPC demo services written in Go that run on\n  OpenShift Service Mesh.\nhome: https://github.com/drhelius/grpc-demo\nicon: https://raw.githubusercontent.com/openshift/console/master/frontend/public/imgs/logos/golang.svg\nkeywords:\n- go\n- grpc\n- demo\n- service\n- istio\nmaintainers:\n- email: isanchez@redhat.com\n  name: Ignacio Sánchez\n  url: https://twitter.com/drhelius\nname: grpc-demo-services-istio\nsources:\n- https://github.com/drhelius/grpc-demo\nversion: 2.0.0\n```\n\nThe chart can be parameterized. These are the [default values](helm-charts/grpc-demo-services-istio/values.yaml) for all the parameters:\n\n```yaml\nappName: grpc-demo-istio\n\naccount:\n  image: quay.io/isanchez/grpc-demo-account\n  version: v1.0.0\n  replicas: 1\n  route: account-grpc-demo.mycluster.com\n\norder:\n  image: quay.io/isanchez/grpc-demo-order\n  version: v1.0.0\n  replicas: 1\n\nproduct:\n  image: quay.io/isanchez/grpc-demo-product\n  version: v1.0.0\n  replicas: 1\n\nuser:\n  image: quay.io/isanchez/grpc-demo-user\n  version: v1.0.0\n  replicas: 1\n\nlimits:\n  memory: \"200\"\n  cpu: \"0.5\"\n\nrequests:\n  memory: \"100\"\n  cpu: \"0.1\"\n```\n\nInstall the chart using a custom *Account* route. Note that you must provide a valid *fqdn* for the route that is going to expose the *Account* service HTTP listener using an [Ingress Gateway](https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/). This *fqdn* should make sense in your cluster so change `account-grpc-demo.mycluster.com` with a name valid in your cluster:\n\n`$ helm install --set account.route=account-grpc-demo.mycluster.com grpc-demo-istio grpc-demo/grpc-demo-services-istio`\n\nAfter a few minutes the services should be up an running:\n\n```bash\n$ kubectl get deployments\nNAME             READY   UP-TO-DATE   AVAILABLE   AGE\naccount-v1.0.0   1/1     1            1           3m1s\norder-v1.0.0     1/1     1            1           3m1s\nproduct-v1.0.0   1/1     1            1           3m1s\nuser-v1.0.0      1/1     1            1           3m1s\n```\n\nYou can test the services using HTTP by sending a GET request to the *Account* service (any account ID will do). For simplicity, the starting request will be HTTP but all subsequent requests between services will be GRPC:\n\n`$ curl http://account-grpc-demo.mycluster.com/v1/account/01234`\n\nYou can uninstall everything by running:\n\n`$ helm uninstall grpc-demo-istio`\n\n### Deploy the demo using a Helm Chart (without Istio)\n\nCreate a project to deploy the demo services if you haven't done so:\n\n`$ oc new-project grpc-demo`\n\nIn this example you are going to use the [provided Helm Chart](helm-charts/grpc-demo-services/Chart.yaml) for deploying the services without using Istio.\n\nMake sure you are working with the right namespace:\n\n`$ oc project grpc-demo`\n\nAdd the chart repository to your helm client and name it `grpc-demo`:\n\n```bash\n\n$ helm repo add grpc-demo https://drhelius.github.io/grpc-demo/\n\"grpc-demo\" has been added to your repositories\n\n$ helm repo list\nNAME        URL\ngrpc-demo   https://drhelius.github.io/grpc-demo/\n```\n\nMake sure you get the latest list of charts:\n\n```bash\n$ helm repo update\nHang tight while we grab the latest from your chart repositories...\n...Successfully got an update from the \"grpc-demo\" chart repository\nUpdate Complete. ⎈ Happy Helming!⎈\n```\n\nThe chart you are going to install is called `grpc-demo-services`. You can inspect the chart before installing it in your cluster:\n\n```bash\n$ helm show chart grpc-demo/grpc-demo-services\napiVersion: v2\ndescription: A group of interconnected GRPC demo services written in Go.\nhome: https://github.com/drhelius/grpc-demo\nicon: https://raw.githubusercontent.com/openshift/console/master/frontend/public/imgs/logos/golang.svg\nkeywords:\n- go\n- grpc\n- demo\n- service\nmaintainers:\n- email: isanchez@redhat.com\n  name: Ignacio Sánchez\n  url: https://twitter.com/drhelius\nname: grpc-demo-services\nsources:\n- https://github.com/drhelius/grpc-demo\nversion: 2.0.0\n```\n\nThe chart can be parameterized. These are the [default values](helm-charts/grpc-demo-services/values.yaml) for all the parameters:\n\n```yaml\nappName: grpc-demo\n\naccount:\n  image: quay.io/isanchez/grpc-demo-account\n  version: v1.0.0\n  replicas: 1\n\norder:\n  image: quay.io/isanchez/grpc-demo-order\n  version: v1.0.0\n  replicas: 1\n\nproduct:\n  image: quay.io/isanchez/grpc-demo-product\n  version: v1.0.0\n  replicas: 1\n\nuser:\n  image: quay.io/isanchez/grpc-demo-user\n  version: v1.0.0\n  replicas: 1\n\nlimits:\n  memory: \"200\"\n  cpu: \"0.5\"\n\nrequests:\n  memory: \"100\"\n  cpu: \"0.1\"\n```\n\nInstall the chart:\n\n`$ helm install grpc-demo grpc-demo/grpc-demo-services`\n\nAfter a few minutes the services should be up an running:\n\n```bash\n$ kubectl get deployments\nNAME             READY   UP-TO-DATE   AVAILABLE   AGE\naccount-v1.0.0   1/1     1            1           3m1s\norder-v1.0.0     1/1     1            1           3m1s\nproduct-v1.0.0   1/1     1            1           3m1s\nuser-v1.0.0      1/1     1            1           3m1s\n```\n\nAn HTTP route for every service is automatically generated:\n\n```bash\n$ kubectl get route\nNAME      HOST/PORT                                           PATH   SERVICES   PORT   TERMINATION   WILDCARD\naccount   account-grpc-demo.apps.mycluster.com                 account    http                 None\norder     order-grpc-demo.apps.mycluster.com                   order      http                 None\nproduct   product-grpc-demo.apps.mycluster.com                 product    http                 None\nuser      user-grpc-demo.apps.mycluster.com                    user       http                 None\n```\n\nYou can test the services using HTTP by sending a GET request to the *Account* service (any account ID will do). For simplicity, the starting request will be HTTP but all subsequent requests between services will be GRPC:\n\n`$ curl http://account-grpc-demo.apps.mycluster.com/v1/account/01234`\n\nYou can uninstall everything by running:\n\n`$ helm uninstall grpc-demo`\n\n## 6 - OpenShift Templates\n    \nOpenShift Templates are a simple tool to deploy services and apply parameterized changes in your cluster. They are not available in other Kubernetes distributions but they are very convenient for simple scenarios if you are using OpenShift.\n\nUnfortunately, these templates lack the dynamism (loops and conditionals) often used to achieve complex deployments. This usually makes Helm a better option. Refer to the [official docs](https://docs.openshift.com/container-platform/4.7/openshift_images/using-templates.html) for additional information.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/templates.png\" alt=\"Demo Templates\"/\u003e\n\u003c/p\u003e\n\nTwo templates are provided in this repo for deploying the demo services both with Istio and without it:\n\n- [grpc-demo-template-istio.yaml](openshift-templates/grpc-demo-template-istio.yaml)\n- [grpc-demo-template.yaml](openshift-templates/grpc-demo-template.yaml)\n\nThese templates deploy all four services at once. This is convenient for this demo but in real life you may want to isolate each service lifecycle by deploying them independently.\n\nThe templates define all the manifests needed in order to get the services deployed and running.\n\nParameters allow you to configure each service image, version, replicas and resources:\n\n```yaml\n...\n\nparameters:\n- description: Sets the Application name.\n  name: APP_NAME\n  displayName: Application name\n  value: grpc-demo\n- description: Sets the Account Service image.\n  name: ACCOUNT_IMAGE\n  displayName: Account Service image\n  value: quay.io/isanchez/grpc-demo-account\n- description: Sets the Account Service version.\n  name: ACCOUNT_VERSION\n  displayName: Account Service version\n  value: v1.0.0\n- description: Specifies how many instances of the Account Service to create in the cluster.\n  name: ACCOUNT_REPLICAS\n  displayName: Account Service replicas\n  value: \"1\"\n\n...\n```\n\nThe `APP_NAME` parameter is just an identifier to label all the manifests created by the templates and organize the view in the [OpenShift Developer Console](https://developers.redhat.com/blog/2019/10/16/openshift-developer-perspective/).\n\nThe `grpc-demo-template-istio.yaml` template expects an additional `ACCOUNT_ROUTE` parameter to expose the *Account* service using an [Ingress Gateway](https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/). Make sure to provide a valid *fqdn* for this route that makes sense in your cluster. The default value `account-grpc-demo.mycluster.com` is just a placeholder and will not work out of the box.\n\n### Deploy the demo using an OpenShift Template (with Istio)\n\nCreate a project to deploy the demo services if you haven't done so:\n\n`$ oc new-project grpc-demo-istio`\n\nCreate the service mesh [Member Roll](https://docs.openshift.com/container-platform/7/service_mesh/service_mesh_install/installing-ossm.html#ossm-member-roll-create_installing-ossm) if you haven't done so. This will tell Istio to start monitoring the namespaces specified.\n\nA Service Mesh Member Roll manifest is [provided in this repo](openshift-service-mesh/service-mesh-member-roll.yaml). It includes the `grpc-demo-istio` namespace. If you are using a different name for the project you should change it accordingly.\n\nUse it to create the Member Roll:\n\n`$ oc apply -f openshift-service-mesh/service-mesh-member-roll.yaml -n istio-system`\n\nIn this example you are going to use the [provided OpenShift template](openshift-templates/grpc-demo-template-istio.yaml) for deploying the services using Istio.\n\nMake sure you are working with the right namespace:\n\n`$ oc project grpc-demo-istio`\n\nAdd the template to your project:\n\n```bash\n$ oc apply -f openshift-templates/grpc-demo-template-istio.yaml\ntemplate.template.openshift.io/grpc-demo-istio created\n\n$ oc get template\nNAME              DESCRIPTION                                                                        PARAMETERS     OBJECTS\ngrpc-demo-istio   A group of interconnected GRPC demo services written in Go that run on OpenSh...   18 (all set)   22\n```\n\nThis template expects a parameter named `ACCOUNT_ROUTE` to expose the *Account* service using an [Ingress Gateway](https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/). Make sure to provide a valid *fqdn* for this route that makes sense in your cluster. The default value `account-grpc-demo.mycluster.com` is just a placeholder and will not work out of the box.\n\nNow, you can use the Developer Console in OpenShift to deploy all the services using this template. You can also use the cli:\n\n```bash\n$ oc process -f openshift-templates/grpc-demo-template-istio.yaml -p ACCOUNT_ROUTE=account-grpc-demo.mycluster.com | oc apply -f -\ndeployment.apps/account-v1.0.0 created\nservice/account created\nvirtualservice.networking.istio.io/account created\ndestinationrule.networking.istio.io/account created\ngateway.networking.istio.io/account created\nvirtualservice.networking.istio.io/account-gateway created\ndeployment.apps/order-v1.0.0 created\nservice/order created\nvirtualservice.networking.istio.io/order created\ndestinationrule.networking.istio.io/order created\ndeployment.apps/product-v1.0.0 created\nservice/product created\nvirtualservice.networking.istio.io/product created\ndestinationrule.networking.istio.io/product created\ndeployment.apps/user-v1.0.0 created\nservice/user created\nvirtualservice.networking.istio.io/user created\ndestinationrule.networking.istio.io/user created\nserviceentry.networking.istio.io/httpbin created\ngateway.networking.istio.io/httpbin created\ndestinationrule.networking.istio.io/httpbin created\nvirtualservice.networking.istio.io/httpbin created\n```\n\nAfter a few minutes the services should be up an running:\n\n```bash\n$ oc get deployments\nNAME             READY   UP-TO-DATE   AVAILABLE   AGE\naccount-v1.0.0   1/1     1            1           3m1s\norder-v1.0.0     1/1     1            1           3m1s\nproduct-v1.0.0   1/1     1            1           3m1s\nuser-v1.0.0      1/1     1            1           3m1s\n```\n\nYou can test the services using HTTP by sending a GET request to the *Account* service (any account ID will do). For simplicity, the starting request will be HTTP but all subsequent requests between services will be GRPC:\n\n`$ curl http://account-grpc-demo.mycluster.com/v1/account/01234`\n\nYou can uninstall everything by running:\n\n`$ oc process -f openshift-templates/grpc-demo-template-istio.yaml | oc delete -f -`\n\n### Deploy the demo using an OpenShift Template (without Istio)\n\nCreate a project to deploy the demo services if you haven't done so:\n\n`$ oc new-project grpc-demo`\n\nIn this example you are going to use the [provided OpenShift Template](openshift-templates/grpc-demo-template.yaml) for deploying the services without using Istio.\n\nMake sure you are working with the right namespace:\n\n`$ oc project grpc-demo`\n\nAdd the template to your project:\n\n```bash\n$ oc apply -f openshift-templates/grpc-demo-template.yaml\ntemplate.template.openshift.io/grpc-demo created\n\n$ oc get template\nNAME        DESCRIPTION                                                   PARAMETERS     OBJECTS\ngrpc-demo   A group of interconnected GRPC demo services written in Go.   17 (all set)   12\n```\n\nNow, you can use the Developer Console in OpenShift to deploy all the services using this template. You can also use the cli:\n\n```bash\n$ oc process -f openshift-templates/grpc-demo-template.yaml | oc apply -f -\ndeployment.apps/account-v1.0.0 created\nservice/account created\nroute.route.openshift.io/account created\ndeployment.apps/order-v1.0.0 created\nservice/order created\nroute.route.openshift.io/order created\ndeployment.apps/product-v1.0.0 created\nservice/product created\nroute.route.openshift.io/product created\ndeployment.apps/user-v1.0.0 created\nservice/user created\nroute.route.openshift.io/user created\n```\n\nAfter a few minutes the services should be up an running:\n\n```bash\n$ oc get deployments\nNAME             READY   UP-TO-DATE   AVAILABLE   AGE\naccount-v1.0.0   1/1     1            1           43s\norder-v1.0.0     1/1     1            1           42s\nproduct-v1.0.0   1/1     1            1           42s\nuser-v1.0.0      1/1     1            1           41s\n```\n\nAn HTTP route for every service is automatically created:\n\n```bash\n$ oc get route\nNAME      HOST/PORT                                           PATH   SERVICES   PORT   TERMINATION   WILDCARD\naccount   account-grpc-demo.apps.mycluster.com                 account    http                 None\norder     order-grpc-demo.apps.mycluster.com                   order      http                 None\nproduct   product-grpc-demo.apps.mycluster.com                 product    http                 None\nuser      user-grpc-demo.apps.mycluster.com                    user       http                 None\n```\n\nYou can test the services using HTTP by sending a GET request to the *Account* service (any account ID will do). For simplicity, the starting request will be HTTP but all subsequent requests between services will be GRPC:\n\n`$ curl http://account-grpc-demo.apps.mycluster.com/v1/account/01234`\n\nYou can uninstall everything by running:\n\n`$ oc process -f openshift-templates/grpc-demo-template-istio.yaml | oc delete -f -`\n\n## 7 - Kubernetes Operators\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/architecture_operator.png\" alt=\"Deploying with a Kubernetes Operator\"/\u003e\n\u003c/p\u003e\n\nIn this demo, a Kubernetes Operator is provided in order to deploy all four services at once:\n\n- [grpc-demo-operator](https://github.com/drhelius/grpc-demo-operator)\n\nThis is convenient for this demo as you will create and manage a simple CRD for deploying all together. In real life though, you may want to isolate each service lifecycle by deploying them independently. An Operator may not be the best solution for deploying services, this Operator is provided for demonstration purposes.\n\nThe operator in this demo can only deploy the services without using Istio. Creating Istio custom resources within a Go Operator is more complex and it has been omitted for simplicity. If you are interested, have a look at the [Istio client-go](https://github.com/istio/client-go) project.\n\nA nice way to create an Operator is by using the [Operator SDK](https://sdk.operatorframework.io/). It provides the tools to build, test and package Operators. In addition, it will create the scafolding needed to start writing your operator easily. Check out the [docs](https://sdk.operatorframework.io/docs/) and don't miss the awesome free eBook about [Kubernetes Operators](https://developers.redhat.com/books/kubernetes-operators).\n\nThere are three ways to create an Operator using the Operator SDK: Helm, Ansible and Go. The operator in this demo is written in Go. Given the three options, Go is the most powerful but also the most complex out of the three.\n\nRecommended reads before proceeding:\n\n- [Golang Based Operator Tutorial](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/)\n- ['Hello, World' tutorial with Kubernetes Operators](https://developers.redhat.com/blog/2020/08/21/hello-world-tutorial-with-kubernetes-operators/)\n- [With Kubernetes Operators comes great responsibility](https://www.redhat.com/en/blog/kubernetes-operators-comes-great-responsibility)\n- [Kubernetes Operators Best Practices](https://www.openshift.com/blog/kubernetes-operators-best-practices)\n- [5 tips for developing Kubernetes Operators with the new Operator SDK](https://developers.redhat.com/blog/2020/09/11/5-tips-for-developing-kubernetes-operators-with-the-new-operator-sdk/)\n\n### Demo Operator\n\n\u003e These are the steps followed to create the Operator provided in this demo. They are useful if you want to create an Operator from scratch. If you just want to deploy the demo using the Operator provided you can jump straight to [Deploy the demo using a Kubernetes Operator (without Istio)](#deploy-the-demo-using-a-kubernetes-operator-without-istio).\n\n- Install the Operator SDK following the [official docs](https://sdk.operatorframework.io/docs/installation/install-operator-sdk/).\n\n- Create a new project. Note that the example uses `example.com` to group the CRDs, you may use whatever you wish:\n\n```bash\n$ mkdir -p $HOME/projects/grpc-demo-operator\n$ cd $HOME/projects/grpc-demo-operator\n$ operator-sdk init --domain=example.com --repo=github.com/drhelius/grpc-demo-operator\n```\n\n- Create a new Custom Resource Definition (CRD) with version `v1` and Kind `DemoServices`. This kind is the name of your new custom CRD, so you can choose a different name if you wish:\n\n```bash\n$ operator-sdk create api --group grpcdemo --version v1 --kind DemoServices --resource=true --controller=true\n```\n\n- Now you can define the API. The Custom Resource (CR) in this demo defines the services you want to deploy and their resources. It looks like this:\n\n```yaml\napiVersion: grpcdemo.example.com/v1\nkind: DemoServices\nmetadata:\n  name: example-services\nspec:\n  services:\n    - name: account\n      image: quay.io/isanchez/grpc-demo-account\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n    - name: order\n      image: quay.io/isanchez/grpc-demo-order\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n    - name: product\n      image: quay.io/isanchez/grpc-demo-product\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n    - name: user\n      image: quay.io/isanchez/grpc-demo-user\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n```\n\nFor each service defined in this CR, the operator will create a `Deployment`, a `Service` and `Route`. This will make each microservice available in your cluster to be consumed.\n\n- The API for the CR is defined in the [`api/v1/demoservices_types.go`](https://github.com/drhelius/grpc-demo-operator/blob/master/api/v1/demoservices_types.go) file:\n\n```go\n// DemoServicesSpec defines the desired state of DemoServices\ntype DemoServicesSpec struct {\n\tServices []Service `json:\"services\"`\n}\n\n// Service defines the desired state of a Service\ntype Service struct {\n\tName     string    `json:\"name\"`\n\tImage    string    `json:\"image\"`\n\tVersion  string    `json:\"version\"`\n\tReplicas int32     `json:\"replicas\"`\n\tLimits   Resources `json:\"limits\"`\n\tRequests Resources `json:\"requests\"`\n}\n\n// Resources defines the desired resources for limits and requests\ntype Resources struct {\n\tCPU    string `json:\"cpu\"`\n\tMemory string `json:\"memory\"`\n}\n```\n\n- After modifying any `*_types.go` files always run the following command to update the generated code for that resource type:\n\n```bash\n$ make generate\n```\n\n- Depending on what you want to achieve you will watch a *primary* resource and some *secondary* ones. You can also add predicates to choose what will trigger the reconciler and what will not. This operator watches `DemoServices` as the primary resource. Additionaly it watches `Deployments`, `Services` and `Routes` as secondary resources:\n\n```go\npredCR := predicate.Funcs{\n\tUpdateFunc: func(e event.UpdateEvent) bool {\n\t\t// Ignore updates to CR status in which case metadata.Generation does not change\n\t\treturn e.MetaOld.GetGeneration() != e.MetaNew.GetGeneration()\n\t},\n}\n  \nerr = c.Watch(\u0026source.Kind{Type: \u0026grpcdemov1.DemoServices{}}, \u0026handler.EnqueueRequestForObject{}, predCR)\nif err != nil {\n\treturn err\n}\n\n...\n\nh := \u0026handler.EnqueueRequestForOwner{\n\tIsController: true,\n\tOwnerType:    \u0026grpcdemov1.DemoServices{},\n}\n\npredDeployment := predicate.Funcs{\n\tCreateFunc: func(e event.CreateEvent) bool {\n\t\treturn false\n\t},\n\tUpdateFunc: func(e event.UpdateEvent) bool {\n    // Ignore updates to CR status in which case metadata.Generation does not change\n\t\treturn e.MetaOld.GetGeneration() != e.MetaNew.GetGeneration()\n\t},\n}\n\nerr = c.Watch(\u0026source.Kind{Type: \u0026appsv1.Deployment{}}, h, predDeployment)\nif err != nil {\n\treturn err\n}\n```\n\n- You can then add the logic of the controller. The controller in this operator will trigger a *reconcile* when the primary watched resource changes. Then, it will keep the state  defined in it. Additionally, it will trigger when any of the secondary watched resources change, like `Deployments`, `Routes`, and `Services` to also check if they are in the desired state. Finally, it will delete any orphaned resource not owned by any microservice that may be removed from the `DemoService` CR:\n\n```go\nfunc (r *DemoServicesReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {\n\t//_ = context.Background()\n\n\treqLogger := r.Log.WithValues(\"req.Namespace\", req.Namespace, \"req.Name\", req.Name)\n\n\treqLogger.Info(\"Reconciling Services\")\n\n\tinstance := \u0026grpcdemov1.DemoServices{}\n\terr := r.Client.Get(context.TODO(), req.NamespacedName, instance)\n\tif err != nil {\n\t\tif errors.IsNotFound(err) {\n\t\t\treturn reconcile.Result{}, nil\n\t\t}\n\t\treturn reconcile.Result{}, err\n\t}\n\n\tfor _, srv := range instance.Spec.Services {\n\t\terr := r.manageDeployment(instance, srv, reqLogger)\n\t\tif err != nil {\n\t\t\treturn reconcile.Result{}, err\n\t\t}\n\n\t\terr = r.manageService(instance, srv, reqLogger)\n\t\tif err != nil {\n\t\t\treturn reconcile.Result{}, err\n\t\t}\n\n\t\terr = r.manageRoute(instance, srv, reqLogger)\n\t\tif err != nil {\n\t\t\treturn reconcile.Result{}, err\n\t\t}\n\t}\n\n\terr = r.deleteOrphanedDeployments(instance, reqLogger)\n\tif err != nil {\n\t\treturn reconcile.Result{}, err\n\t}\n\n\terr = r.deleteOrphanedServices(instance, reqLogger)\n\tif err != nil {\n\t\treturn reconcile.Result{}, err\n\t}\n\n\terr = r.deleteOrphanedRoutes(instance, reqLogger)\n\tif err != nil {\n\t\treturn reconcile.Result{}, err\n\t}\n\n\treturn ctrl.Result{}, nil\n}\n```\n\n- Build the operator and generate an image. Make sure you have access to the image repository in order to push it. Here is an example with [Quay](https://quay.io/):\n\n```shell\n$ make docker-build docker-push IMG=quay.io/isanchez/grpc-demo-operator:v0.0.1\n```\n\n- Before running the operator, the CRD must be registered with the Kubernetes apiserver. This will *install* the CRD in your cluster using `kubectl`:\n\n```shell\n$ make install\n```\n\n- This operator is expected to be run in the `grpc-demo` namespace. You can change it for all resources in `config/default/kustomization.yaml`:\n\n```shell\n$ cd config/default/ \u0026\u0026 kustomize edit set namespace \"grpc-demo\" \u0026\u0026 cd ../..\n```\n\n- This operator is a namespace-scoped operator. It will watch for CR changes within a namespace. You can provide the namespace to watch using the `WATCH_NAMESPACE` env var in the [operator Deployment manifest](https://github.com/drhelius/grpc-demo-operator/blob/master/config/manager/manager.yaml). In this demo the namespace to be watched is the same as the namespace where the operator is running:\n\n```yaml\nenv:\n- name: WATCH_NAMESPACE\n  valueFrom:\n    fieldRef:\n      fieldPath: metadata.namespace\n```\n\n- Run the following to deploy the operator. This will also install the RBAC manifests from [config/rbac](https://github.com/drhelius/grpc-demo-operator/tree/master/config/rbac).\n\n```shell\n$ make deploy IMG=quay.io/isanchez/grpc-demo-operator:v0.0.1\n```\n\n### Deploy the demo using a Kubernetes Operator (without Istio)\n\nFirst, clone the [provided Kubernetes Operator](https://github.com/drhelius/grpc-demo-operator) repository:\n\n```bash\n$ git clone https://github.com/drhelius/grpc-demo-operator.git\n$ cd grpc-demo-operator\n```\n\nCreate a project to deploy the demo services if you haven't done so:\n\n`$ oc new-project grpc-demo`\n\nMake sure you are working with the right namespace. The operator will run in the `grpc-demo` namespace by default:\n\n`$ oc project grpc-demo`\n\nThe repository you just cloned has a [Makefile](https://github.com/drhelius/grpc-demo-operator/blob/master/Makefile) to assist in some operations.\n\nRun this to build and deploy the operator, the CRDs and all required manifests like RBAC configuration:\n\n```bash\n$ make install\n$ make deploy IMG=quay.io/isanchez/grpc-demo-operator:v0.0.1\n```\n\nMake sure the operator is running fine:\n\n```bash\n$ kubectl get deployment grpc-demo-operator-controller-manager\nNAME                                    READY   UP-TO-DATE   AVAILABLE   AGE\ngrpc-demo-operator-controller-manager   1/1     1            1           2m56s\n```\n\nThe operator is watching custom resources with kind `demoservices.grpcdemo.example.com`.\n\nNow you can create your own [custom resource](https://github.com/drhelius/grpc-demo-operator/blob/master/config/samples/grpcdemo_v1_demoservices.yaml) to instruct the operator to create the demo services:\n\n```yaml\napiVersion: grpcdemo.example.com/v1\nkind: DemoServices\nmetadata:\n  name: example-services\nspec:\n  services:\n    - name: account\n      image: quay.io/isanchez/grpc-demo-account\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n    - name: order\n      image: quay.io/isanchez/grpc-demo-order\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n    - name: product\n      image: quay.io/isanchez/grpc-demo-product\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n    - name: user\n      image: quay.io/isanchez/grpc-demo-user\n      version: v1.0.0\n      replicas: 1\n      limits:\n        memory: 200Mi\n        cpu: \"0.5\"\n      requests:\n        memory: 100Mi\n        cpu: \"0.1\"\n```\n\nCreate the custom resource by using the provided example:\n\n```bash\n$ kubectl apply -f config/samples/grpcdemo_v1_demoservices.yaml\ndemoservices.grpcdemo.example.com/example-services created\n```\n\nAfter a few minutes the operator should have created all the required objects and the services should be up an running:\n\n```bash\n$ kubectl get deployment\nNAME                                    READY   UP-TO-DATE   AVAILABLE   AGE\naccount                                 1/1     1            1           2m14s\ngrpc-demo-operator-controller-manager   1/1     1            1           14m\norder                                   1/1     1            1           2m14s\nproduct                                 1/1     1            1           2m13s\nuser                                    1/1     1            1           2m13s\n```\n\nAn HTTP route for every service is automatically created:\n\n```bash\n$ kubectl get route\nNAME      HOST/PORT                                     PATH   SERVICES   PORT   TERMINATION   WILDCARD\naccount   account-grpc-demo.apps.mycluster.com                 account    http                 None\norder     order-grpc-demo.apps.mycluster.com                   order      http                 None\nproduct   product-grpc-demo.apps.mycluster.com                 product    http                 None\nuser      user-grpc-demo.apps.mycluster.com                    user       http                 None\n```\n\nYou can test the services using HTTP by sending a GET request to the *Account* service (any account ID will do). For simplicity, the starting request will be HTTP but all subsequent requests between services will be gRPC:\n\n`$ curl http://account-grpc-demo.apps.mycluster.com/v1/account/01234`\n\nYou can uninstall the services by deleting the custom resource and the operator will delete all of them for you:\n\n```bash\n$ kubectl delete demoservices.grpcdemo.example.com example-services\ndemoservices.grpcdemo.example.com \"example-services\" deleted\n```\n\n## 8 - Observability with Kiali\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/kiali2.png\" alt=\"Service Mesh architecture\" width=\"600\"/\u003e\n\u003c/p\u003e\n\nOnce you have Istio and the demo services up and running you can observe what is going on in your mesh with Kiali.\n\nFirst, you need the Kiali route to access the web console:\n\n```bash\n$ kubectl get routes -n istio-system\nNAME                            HOST/PORT                                                     PATH   SERVICES               PORT    TERMINATION          WILDCARD\ngrafana                         grafana-istio-system.apps.mycluster.com                              grafana                \u003call\u003e   reencrypt            None\ngrpc-demo-istio-account-mjs5x   account-grpc-demo-istio-system.apps.mycluster.com                    istio-ingressgateway   http2                        None\ngrpc-demo-istio-httpbin-6c9ww   httpbin.org                                                          istio-egressgateway    https   passthrough          None\nistio-ingressgateway            istio-ingressgateway-istio-system.apps.mycluster.com                 istio-ingressgateway   8080                         None\njaeger                          jaeger-istio-system.apps.mycluster.com                               jaeger-query           \u003call\u003e   reencrypt            None\nkiali                           kiali-istio-system.apps.mycluster.com                                kiali                  \u003call\u003e   reencrypt/Redirect   None\nprometheus                      prometheus-istio-system.apps.mycluster.com                           prometheus             \u003call\u003e   reencrypt            None\n```\n\nLogin into Kiali console and select the `grpc-demo-istio` namespace:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/kiali4.png\" alt=\"Kiali namespace selection\"/\u003e\n\u003c/p\u003e\n\nYou can choose between different types of graphs:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/kiali5.png\" alt=\"Kiali graph selection\"/\u003e\n\u003c/p\u003e\n\nAnd you can select what is displayed in the graphs:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/kiali6.png\" alt=\"Kiali display selection\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/kiali3.png\" alt=\"Service Mesh observability\"/\u003e\n\u003c/p\u003e\n\nThere is a link in Kiali to open the Jaeger UI. The services in this demo are propagating OpenTracing headers. Istio will then be able to correlate traces between different services and you can observe those traces in the Jaeger UI:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/jaeger.png\" alt=\"Jaeger tracing\"/\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrhelius%2Fgrpc-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrhelius%2Fgrpc-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrhelius%2Fgrpc-demo/lists"}