Merge pull request #2307 from pcj/grpc-example
Documentation: grpc example
This commit is contained in:
commit
c80365dcfa
16 changed files with 698 additions and 1 deletions
2
Makefile
2
Makefile
|
@ -172,7 +172,7 @@ e2e-test:
|
||||||
.PHONY: cover
|
.PHONY: cover
|
||||||
cover:
|
cover:
|
||||||
@rm -rf coverage.txt
|
@rm -rf coverage.txt
|
||||||
@for d in `go list ./... | grep -v vendor | grep -v '/test/e2e'`; do \
|
@for d in `go list ./... | grep -v vendor | grep -v '/test/e2e' | grep -v 'images/grpc-fortune-teller'`; do \
|
||||||
t=$$(date +%s); \
|
t=$$(date +%s); \
|
||||||
go test -coverprofile=cover.out -covermode=atomic $$d || exit 1; \
|
go test -coverprofile=cover.out -covermode=atomic $$d || exit 1; \
|
||||||
echo "Coverage test $$d took $$(($$(date +%s)-t)) seconds"; \
|
echo "Coverage test $$d took $$(($$(date +%s)-t)) seconds"; \
|
||||||
|
|
105
docs/examples/grpc/README.md
Normal file
105
docs/examples/grpc/README.md
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
# gRPC
|
||||||
|
|
||||||
|
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.13.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/images/grpc-fortune-teller)
|
||||||
|
application provided here as an example.
|
||||||
|
|
||||||
|
### Step 1: kubernetes `Deployment`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ kubectl create -f app.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a standard kubernetes deployment object. It is running a grpc service
|
||||||
|
listening on port `50051`.
|
||||||
|
|
||||||
|
The sample application
|
||||||
|
[fortune-teller-app](https://github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller)
|
||||||
|
is a grpc server implemented in go. Here's the stripped-down implementation:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
grpcServer := grpc.NewServer()
|
||||||
|
fortune.RegisterFortuneTellerServer(grpcServer, &FortuneTeller{})
|
||||||
|
lis, _ := net.Listen("tcp", ":50051")
|
||||||
|
grpcServer.Serve(lis)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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").
|
||||||
|
|
||||||
|
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/secure-backends:
|
||||||
|
"true"`.
|
||||||
|
|
||||||
|
### Step 2: the kubernetes `Service`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ kubectl create -f svc.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we have a typical service. Nothing special, just routing traffic to the
|
||||||
|
backend application on port `50051`.
|
||||||
|
|
||||||
|
### Step 3: the kubernetes `Ingress`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ kubectl create -f ingress.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
A few things to note:
|
||||||
|
|
||||||
|
1. We've tagged the ingress with the annotation
|
||||||
|
`nginx.ingress.kubernetes.io/grpc-backend: "true"`. This is the magic
|
||||||
|
ingredient that sets up the appropriate nginx configuration to route http/2
|
||||||
|
traffic to our service.
|
||||||
|
1. We're terminating TLS at the ingress and have configured an SSL certificate
|
||||||
|
`fortune-teller.stack.build`. The ingress matches traffic arriving as
|
||||||
|
`https://fortune-teller.stack.build:443` and routes unencrypted messages to
|
||||||
|
our kubernetes service.
|
||||||
|
|
||||||
|
### Step 4: test the connection
|
||||||
|
|
||||||
|
Once we've applied our configuration to kubernetes, it's time to test that we
|
||||||
|
can actually talk to the backend. To do this, we'll use the
|
||||||
|
[grpcurl](https://github.com/fullstorydev/grpcurl) utility:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ grpcurl fortune-teller.stack.build:443 build.stack.fortune.FortuneTeller/Predict
|
||||||
|
{
|
||||||
|
"message": "Let us endeavor so to live that when we come to die even the undertaker will be sorry.\n\t\t-- Mark Twain, \"Pudd'nhead Wilson's Calendar\""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging Hints
|
||||||
|
|
||||||
|
1. Obviously, watch the logs on your app.
|
||||||
|
2. Watch the logs for the nginx-ingress-controller (increasing verbosity as
|
||||||
|
needed).
|
||||||
|
3. Double-check your address and ports.
|
||||||
|
4. Set the `GODEBUG=http2debug=2` environment variable to get detailed http/2
|
||||||
|
logging on the client and/or server.
|
||||||
|
5. Study RFC 7540 (http/2) <https://tools.ietf.org/html/rfc7540>.
|
||||||
|
|
||||||
|
> If you are developing public gRPC endpoints, check out
|
||||||
|
> https://proto.stack.build, a protocol buffer / gRPC build service that can use
|
||||||
|
> to help make it easier for your users to consume your API.
|
20
docs/examples/grpc/app.yaml
Normal file
20
docs/examples/grpc/app.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: fortune-teller-app
|
||||||
|
labels:
|
||||||
|
k8s-app: fortune-teller-app
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-app: fortune-teller-app
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: fortune-teller-app
|
||||||
|
image: quay.io/kubernetes-ingress-controller/grpc-fortune-teller:0.1
|
||||||
|
ports:
|
||||||
|
- containerPort: 50051
|
||||||
|
name: grpc
|
7
docs/examples/grpc/cert.yaml
Normal file
7
docs/examples/grpc/cert.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
apiVersion: "stable.k8s.psg.io/v1"
|
||||||
|
kind: "Certificate"
|
||||||
|
metadata:
|
||||||
|
name: fortune-teller.stack.build
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
domain: "fortune-teller.stack.build"
|
21
docs/examples/grpc/ingress.yaml
Normal file
21
docs/examples/grpc/ingress.yaml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "nginx"
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
nginx.ingress.kubernetes.io/grpc-backend: "true"
|
||||||
|
name: fortune-ingress
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: fortune-teller.stack.build
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: fortune-teller-service
|
||||||
|
servicePort: grpc
|
||||||
|
tls:
|
||||||
|
- secretName: fortune-teller.stack.build
|
||||||
|
hosts:
|
||||||
|
- fortune-teller.stack.build
|
12
docs/examples/grpc/svc.yaml
Normal file
12
docs/examples/grpc/svc.yaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: fortune-teller-service
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
k8s-app: fortune-teller-app
|
||||||
|
ports:
|
||||||
|
- port: 50051
|
||||||
|
targetPort: 50051
|
||||||
|
name: grpc
|
15
images/grpc-fortune-teller/BUILD.bazel
Normal file
15
images/grpc-fortune-teller/BUILD.bazel
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "gazelle", "go_prefix")
|
||||||
|
|
||||||
|
gazelle(
|
||||||
|
name = "gazelle",
|
||||||
|
args = [
|
||||||
|
"-build_file_name",
|
||||||
|
"BUILD,BUILD.bazel",
|
||||||
|
],
|
||||||
|
command = "fix",
|
||||||
|
prefix = "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_prefix(
|
||||||
|
prefix = "github.com/kubernetes/ingress-nginx/images/grpc-fortune-teller",
|
||||||
|
)
|
104
images/grpc-fortune-teller/Gopkg.lock
generated
Normal file
104
images/grpc-fortune-teller/Gopkg.lock
generated
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
packages = [
|
||||||
|
"proto",
|
||||||
|
"protoc-gen-go/descriptor",
|
||||||
|
"ptypes",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/duration",
|
||||||
|
"ptypes/timestamp"
|
||||||
|
]
|
||||||
|
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/vromero/gofortune"
|
||||||
|
packages = [
|
||||||
|
"lib",
|
||||||
|
"lib/fortune"
|
||||||
|
]
|
||||||
|
revision = "8a3c485287c0d3d3e4f8f0d9e0058f6cdd29740d"
|
||||||
|
version = "v0.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"context",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna",
|
||||||
|
"internal/timeseries",
|
||||||
|
"lex/httplex",
|
||||||
|
"trace"
|
||||||
|
]
|
||||||
|
revision = "b3c676e531a6dc479fa1b35ac961c13f5e2b4d2e"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "golang.org/x/text"
|
||||||
|
packages = [
|
||||||
|
"collate",
|
||||||
|
"collate/build",
|
||||||
|
"internal/colltab",
|
||||||
|
"internal/gen",
|
||||||
|
"internal/tag",
|
||||||
|
"internal/triegen",
|
||||||
|
"internal/ucd",
|
||||||
|
"language",
|
||||||
|
"secure/bidirule",
|
||||||
|
"transform",
|
||||||
|
"unicode/bidi",
|
||||||
|
"unicode/cldr",
|
||||||
|
"unicode/norm",
|
||||||
|
"unicode/rangetable"
|
||||||
|
]
|
||||||
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
packages = ["googleapis/rpc/status"]
|
||||||
|
revision = "35de2414665fc36f56b72d982c5af480d86de5ab"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"balancer",
|
||||||
|
"balancer/base",
|
||||||
|
"balancer/roundrobin",
|
||||||
|
"codes",
|
||||||
|
"connectivity",
|
||||||
|
"credentials",
|
||||||
|
"encoding",
|
||||||
|
"encoding/proto",
|
||||||
|
"grpclb/grpc_lb_v1/messages",
|
||||||
|
"grpclog",
|
||||||
|
"internal",
|
||||||
|
"keepalive",
|
||||||
|
"metadata",
|
||||||
|
"naming",
|
||||||
|
"peer",
|
||||||
|
"reflection",
|
||||||
|
"reflection/grpc_reflection_v1alpha",
|
||||||
|
"resolver",
|
||||||
|
"resolver/dns",
|
||||||
|
"resolver/passthrough",
|
||||||
|
"stats",
|
||||||
|
"status",
|
||||||
|
"tap",
|
||||||
|
"transport"
|
||||||
|
]
|
||||||
|
revision = "d89cded64628466c4ab532d1f0ba5c220459ebe8"
|
||||||
|
version = "v1.11.2"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "fcd36504ba120cfbbe161eac2ddd439c884fcef61ce318b5c0a544d27bc47043"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
30
images/grpc-fortune-teller/Gopkg.toml
Normal file
30
images/grpc-fortune-teller/Gopkg.toml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
59
images/grpc-fortune-teller/README.md
Normal file
59
images/grpc-fortune-teller/README.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
# grpc-fortune-teller
|
||||||
|
|
||||||
|
This is the grpc server application for the nginx-ingress grpc example. Bazel
|
||||||
|
0.12 is used for the building and container management.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Builds a statically compiled go binary cross-compiled for linux:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bazel build //app:fortune
|
||||||
|
Target //app:fortune up-to-date:
|
||||||
|
bazel-bin/app/linux_amd64_static_pure_stripped/fortune
|
||||||
|
```
|
||||||
|
|
||||||
|
> 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\""
|
||||||
|
}
|
||||||
|
```
|
62
images/grpc-fortune-teller/WORKSPACE
Normal file
62
images/grpc-fortune-teller/WORKSPACE
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
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.9")
|
||||||
|
|
||||||
|
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()
|
69
images/grpc-fortune-teller/app/BUILD.bazel
Normal file
69
images/grpc-fortune-teller/app/BUILD.bazel
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
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",
|
||||||
|
)
|
137
images/grpc-fortune-teller/app/main.go
Normal file
137
images/grpc-fortune-teller/app/main.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
images/grpc-fortune-teller/proto/fortune/BUILD.bazel
Normal file
24
images/grpc-fortune-teller/proto/fortune/BUILD.bazel
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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"],
|
||||||
|
)
|
18
images/grpc-fortune-teller/proto/fortune/doc.go
Normal file
18
images/grpc-fortune-teller/proto/fortune/doc.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
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 fortune acts as a placeholder f for generated *.pb.go files.
|
||||||
|
package fortune
|
14
images/grpc-fortune-teller/proto/fortune/fortune.proto
Normal file
14
images/grpc-fortune-teller/proto/fortune/fortune.proto
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package build.stack.fortune;
|
||||||
|
|
||||||
|
message PredictionRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message PredictionResponse {
|
||||||
|
string message = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service FortuneTeller {
|
||||||
|
rpc Predict(PredictionRequest) returns (PredictionResponse);
|
||||||
|
}
|
Loading…
Reference in a new issue