diff --git a/docs/examples/grpc/README.md b/docs/examples/grpc/README.md index 60e696bfd..8835b5110 100644 --- a/docs/examples/grpc/README.md +++ b/docs/examples/grpc/README.md @@ -1,93 +1,152 @@ # gRPC -This example demonstrates how to route traffic to a gRPC service through the -nginx controller. +This example demonstrates how to route traffic to a gRPC service through the nginx controller. ## Prerequisites 1. You have a kubernetes cluster running. -2. You have a domain name such as `example.com` that is configured to route - traffic to the ingress controller. Replace references to - `fortune-teller.stack.build` (the domain name used in this example) to your - own domain name (you're also responsible for provisioning an SSL certificate - for the ingress). -3. You have the nginx-ingress controller installed in typical fashion (must be - at least - [quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0](https://quay.io/kubernetes-ingress-controller/nginx-ingress-controller) - for grpc support. -4. You have a backend application running a gRPC server and listening for TCP - traffic. If you prefer, you can use the - [fortune-teller](https://github.com/kubernetes/ingress-nginx/tree/master/images/grpc-fortune-teller) - application provided here as an example. +2. You have a domain name such as `example.com` that is configured to route traffic to the ingress controller. +3. You have the nginx-ingress controller installed as per docs, with gRPC support. +4. You have a backend application running a gRPC server and listening for TCP traffic. If you want, you can use as an example. +5. You're also responsible for provisioning an SSL certificate for the ingress. So you need to have a valid SSL certificate, deployed as a Kubernetes secret of type tls, in the same namespace as the gRPC application. -### Step 1: kubernetes `Deployment` +### Step 1: Create a Kubernetes `Deployment` for gRPC app -```sh -$ kubectl create -f app.yaml -``` +- Make sure your gRPC application pod is running and listening for connections. For example you can try a kubectl command like this below: + ``` + $ kubectl get po -A -o wide | grep go-grpc-greeter-server + ``` +- If you have a gRPC app deployed in your cluster, then skip further notes in this Step 1, and continue from Step 2 below. -This is a standard kubernetes deployment object. It is running a grpc service -listening on port `50051`. +- As an example gRPC application, we can use this app . -The sample application -[fortune-teller-app](https://github.com/kubernetes/ingress-nginx/tree/master/images/grpc-fortune-teller) -is a grpc server implemented in go. Here's the stripped-down implementation: +- To create a container image for this app, you can use [this Dockerfile](../../../images/go-grpc-greeter-server/rootfs/Dockerfile). -```go -func main() { - grpcServer := grpc.NewServer() - fortune.RegisterFortuneTellerServer(grpcServer, &FortuneTeller{}) - lis, _ := net.Listen("tcp", ":50051") - grpcServer.Serve(lis) -} -``` +- If you use the Dockerfile mentioned above, to create a image, then given below is an example of a Kubernetes manifest, to create a deployment resource, that uses that image. If needed, then edit this manifest to suit your needs. Assuming the name of this yaml file is `deployment.go-grpc-greeter-server.yaml` ; -The takeaway is that we are not doing any TLS configuration on the server (as we -are terminating TLS at the ingress level, grpc traffic will travel unencrypted -inside the cluster and arrive "insecure"). + ``` + cat </go-grpc-greeter-server # Edit this for your reponame + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + name: go-grpc-greeter-server + ports: + - containerPort: 50051 + EOF + ``` -For your own application you may or may not want to do this. If you prefer to -forward encrypted traffic to your POD and terminate TLS at the gRPC server -itself, add the ingress annotation `nginx.ingress.kubernetes.io/backend-protocol: "GRPCS"`. +### Step 2: Create the Kubernetes `Service` for the gRPC app -### Step 2: the kubernetes `Service` +- You can use the following example manifest to create a service of type ClusterIP. Edit the name/namespace/label/port to match your deployment/pod ; + ``` + cat < To build for your host system, comment out the `goos` and `goarch` attributes -> in the `go_binary` rule. - -## Run - -Builds a minimal docker image that wraps the go_binary, loads it into your local -docker image repository, and runs it: - -```sh -$ bazel run //app:image -Loaded image ID: sha256:aa597c897c873116fcbfccafecf5ab0f6f4178a05e4a00c8e79de91ac0d2e9e7 -Tagging aa597c897c873116fcbfccafecf5ab0f6f4178a05e4a00c8e79de91ac0d2e9e7 as bazel/app:image -2018/05/01 02:13:43 Restored /tmp/fortune-teller/usr/share/games/fortunes/fortunes.dat -2018/05/01 02:13:43 Restored /tmp/fortune-teller/usr/share/games/fortunes/literature -2018/05/01 02:13:43 Restored /tmp/fortune-teller/usr/share/games/fortunes/literature.dat -2018/05/01 02:13:43 Restored /tmp/fortune-teller/usr/share/games/fortunes/riddles -2018/05/01 02:13:43 Restored /tmp/fortune-teller/usr/share/games/fortunes/riddles.dat -2018/05/01 02:13:43 Restored /tmp/fortune-teller/usr/share/games/fortunes/fortunes -2018/05/01 02:13:43 Assets restored to /tmp/fortune-teller -2018/05/01 02:13:43 Listening for gRPC requests at 50051 -``` - -Or run it via docker: - -```sh -$ docker run bazel/app:image -``` - -Build image and push to the container registry specified in the `container_push` -rule: - -```sh -$ bazel run //app:push -``` - -## Invoke - -```sh -$ grpcurl -plaintext localhost:50051 build.stack.fortune.FortuneTeller/Predict -{ - "message": "Whenever the literary German dives into a sentence, that is the last\nyou are going to see of him until he emerges on the other side of his\nAtlantic with his verb in his mouth.\n\t\t-- Mark Twain \"A Connecticut Yankee in King Arthur's Court\"" -} -``` diff --git a/images/grpc-fortune-teller/WORKSPACE b/images/grpc-fortune-teller/WORKSPACE deleted file mode 100644 index a6960d054..000000000 --- a/images/grpc-fortune-teller/WORKSPACE +++ /dev/null @@ -1,62 +0,0 @@ -workspace(name = "com_github_kubernetes_ingress_nginx_images_grpc_fortune_teller") - -##################################################################### -# RULES_GO -##################################################################### - -git_repository( - name = "io_bazel_rules_go", - remote = "https://github.com/bazelbuild/rules_go.git", - commit = "161c91485b007c6bf51c0e81808cf4ee2ded299d", -) - -http_archive( - name = "com_github_scele_rules_go_dep", - urls = ["https://github.com/scele/rules_go_dep/archive/49a5e4ca9f6a16c9b4c930a51ce3a537498bb4e1.tar.gz"], - strip_prefix = "rules_go_dep-49a5e4ca9f6a16c9b4c930a51ce3a537498bb4e1", - sha256 = "f170d3d6f55e216f1493f975cde6c489d7070da2a8a41fd4de9812d96f4fb38b", -) - -load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains") -load("@com_github_scele_rules_go_dep//dep:dep.bzl", "dep_import") - -go_register_toolchains(go_version = "1.10.1") - -go_rules_dependencies() - -dep_import( - name = "godeps", - prefix = "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller", - gopkg_lock = "//:Gopkg.lock", -) - -load("@godeps//:Gopkg.bzl", "go_deps") - -go_deps() - -############################################################# -# RULES_DOCKER -############################################################# - -RULES_DOCKER_VERSION = "553d5506bb7325185950f91533b967da8f5bc536" - -http_archive( - name = "io_bazel_rules_docker", - url = "https://github.com/bazelbuild/rules_docker/archive/%s.zip" % RULES_DOCKER_VERSION, - strip_prefix = "rules_docker-" + RULES_DOCKER_VERSION, - sha256 = "e0b3d966f2a5c0fe921b6294df7c823afa63b4c439f0a7f3b9da3ed6534bab83", -) - -load( - "@io_bazel_rules_docker//container:container.bzl", - container_repositories = "repositories", -) - -container_repositories() - -load( - "@io_bazel_rules_docker//go:image.bzl", - go_image_repositories = "repositories", -) - -go_image_repositories() diff --git a/images/grpc-fortune-teller/app/BUILD.bazel b/images/grpc-fortune-teller/app/BUILD.bazel deleted file mode 100644 index 4fffd481e..000000000 --- a/images/grpc-fortune-teller/app/BUILD.bazel +++ /dev/null @@ -1,69 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_embed_data") -load("@io_bazel_rules_docker//go:image.bzl", "go_image") -load("@io_bazel_rules_docker//container:push.bzl", "container_push") - -# Concatenates the fortune databases to a single bundle. -# May need to adjust paths for your system (built on ubuntu 16.04). -# $ apt-get install fortune -genrule( - name = "tar", - outs = ["fortune.tar"], - cmd = " && ".join([ - "OUT=$$(pwd)/$@", - "tar -cvf $$OUT /usr/share/games/fortunes", - ]), -) - -# Generates a .go source file with the tarball content in -# the fortuneFiles variable. -go_embed_data( - name = "fortune_assets", - srcs = [ - ":tar", - ], - package = "main", - unpack = True, - var = "fortuneFiles", -) - -go_library( - name = "go_default_library", - srcs = [ - "main.go", - ":fortune_assets", # keep - ], - importpath = "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller/app", - visibility = ["//visibility:private"], - deps = [ - "//proto/fortune:go_default_library", - "@com_github_vromero_gofortune//lib/fortune:go_default_library", - "@org_golang_google_grpc//:go_default_library", - "@org_golang_google_grpc//codes:go_default_library", - "@org_golang_google_grpc//reflection:go_default_library", - ], -) - -go_binary( - name = "fortune", - embed = [":go_default_library"], - goarch = "amd64", - goos = "linux", - pure = "on", - static = "on", - visibility = ["//visibility:public"], -) - -go_image( - name = "image", - binary = ":fortune", - visibility = ["//visibility:public"], -) - -container_push( - name = "push", - format = "Docker", - image = ":image", - registry = "quay.io", - repository = "kubernetes-ingress-controller/grpc-fortune-teller", - tag = "0.1", -) diff --git a/images/grpc-fortune-teller/app/main.go b/images/grpc-fortune-teller/app/main.go deleted file mode 100644 index d09799cbe..000000000 --- a/images/grpc-fortune-teller/app/main.go +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "context" - "fmt" - "io/ioutil" - "log" - "net" - "os" - "path" - - proto "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller/proto/fortune" - "github.com/vromero/gofortune/lib/fortune" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/reflection" -) - -const ( - grpcPort = 50051 -) - -func main() { - - baseDir := "/tmp/fortune-teller" - mustMkdirAll(baseDir) - - opts := []grpc.ServerOption{ - grpc.MaxConcurrentStreams(200), - } - - grpcServer := grpc.NewServer(opts...) - - fortuneTeller := &FortuneTeller{ - fs: createFortuneFilesystemNodeDescriptor(baseDir), - } - proto.RegisterFortuneTellerServer(grpcServer, fortuneTeller) - - reflection.Register(grpcServer) - - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", grpcPort)) - if err != nil { - log.Fatalf("Error while starting grpc server: %v\n", err) - } - - log.Printf("Listening for gRPC requests at %d\n", grpcPort) - grpcServer.Serve(lis) -} - -// FortuneTeller - struct that will implement the grpc service interface. -type FortuneTeller struct { - fs *fortune.FileSystemNodeDescriptor -} - -// Predict - implementation for the grpc unary request method. -func (f *FortuneTeller) Predict(ctx context.Context, r *proto.PredictionRequest) (*proto.PredictionResponse, error) { - _, data, err := fortune.GetRandomFortune(*f.fs) - if err != nil { - return nil, grpc.Errorf(codes.Internal, "Unable to render fortune: %v", err) - } - return &proto.PredictionResponse{ - Message: data, - }, nil -} - -func createFortuneFilesystemNodeDescriptor(baseDir string) *fortune.FileSystemNodeDescriptor { - - // Restore the packed fortune data - fortuneDir := path.Join(baseDir, "usr/share/games/fortunes") - - mustRestore(baseDir, fortuneFiles, nil) - - // init gofortune fs - fs, err := fortune.LoadPaths([]fortune.ProbabilityPath{ - {Path: fortuneDir}, - }) - if err != nil { - log.Fatalf("Unable to load fortune paths: %v", err) - } - - fortune.SetProbabilities(&fs, true) // consider all equal probabilities - return &fs -} - -// mustRestore - Restore assets. -func mustRestore(baseDir string, assets map[string][]byte, mappings map[string]string) { - // unpack variable is provided by the go_embed data and is a - // map[string][]byte such as {"/usr/share/games/fortune/literature.dat": - // bytes... } - for basename, bytes := range assets { - if mappings != nil { - replacement := mappings[basename] - if replacement != "" { - basename = replacement - } - } - filename := path.Join(baseDir, basename) - dirname := path.Dir(filename) - //log.Printf("file %s, dir %s, rel %d, abs %s, absdir: %s", file, dir, rel, abs, absdir) - if err := os.MkdirAll(dirname, os.ModePerm); err != nil { - log.Fatalf("Failed to create asset dir %s: %v", dirname, err) - } - - if err := ioutil.WriteFile(filename, bytes, os.ModePerm); err != nil { - log.Fatalf("Failed to write asset %s: %v", filename, err) - } - log.Printf("Restored %s", filename) - } - - log.Printf("Assets restored to %s", baseDir) -} - -// mustMkdirAll - make all dirs and panic if fail -func mustMkdirAll(dirs ...string) { - for _, dir := range dirs { - err := os.MkdirAll(dir, os.ModePerm) - if err != nil { - panic(fmt.Sprintf("Failed mkdir %s: %v", dir, err)) - } - } -} diff --git a/images/grpc-fortune-teller/go.mod b/images/grpc-fortune-teller/go.mod deleted file mode 100644 index e69de29bb..000000000 diff --git a/images/grpc-fortune-teller/proto/fortune/BUILD.bazel b/images/grpc-fortune-teller/proto/fortune/BUILD.bazel deleted file mode 100644 index b3008b826..000000000 --- a/images/grpc-fortune-teller/proto/fortune/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") - -go_library( - name = "go_default_library", - srcs = ["doc.go"], - embed = [":build_stack_fortune_go_proto"], # keep - importpath = "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller/proto/fortune", - visibility = ["//visibility:public"], -) - -proto_library( - name = "build_stack_fortune_proto", - srcs = ["fortune.proto"], - visibility = ["//visibility:public"], -) - -go_proto_library( - name = "build_stack_fortune_go_proto", - compilers = ["@io_bazel_rules_go//proto:go_grpc"], - importpath = "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller/proto/fortune", - proto = ":build_stack_fortune_proto", - visibility = ["//visibility:public"], -) diff --git a/images/grpc-fortune-teller/proto/fortune/fortune.proto b/images/grpc-fortune-teller/proto/fortune/fortune.proto deleted file mode 100644 index d71b4acb1..000000000 --- a/images/grpc-fortune-teller/proto/fortune/fortune.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -package build.stack.fortune; - -message PredictionRequest { -} - -message PredictionResponse { - string message = 1; -} - -service FortuneTeller { - rpc Predict(PredictionRequest) returns (PredictionResponse); -} \ No newline at end of file