Skip to content

Instantly share code, notes, and snippets.

@haproxytechblog
Created January 16, 2019 18:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save haproxytechblog/ec6da14085bd1a88600cf247e6a63bf8 to your computer and use it in GitHub Desktop.
Save haproxytechblog/ec6da14085bd1a88600cf247e6a63bf8 to your computer and use it in GitHub Desktop.
HAProxy 1.9.2 Adds gRPC Support
frontend fe_mysite
bind :443 ssl crt /path/to/cert.pem alpn h2,http/1.1
default_backend be_servers
defaults
option http-use-htx
backend be_servers
balance roundrobin
server server1 192.168.3.10:3000 ssl verify none alpn h2,http/1.1 check maxconn 20
frontend fe_mysite
bind :443 ssl crt /path/to/cert.pem proto h2
default_backend be_servers
backend be_servers
balance roundrobin
server server1 192.168.3.10:3000 ssl verify none proto h2 check maxconn 20
syntax = "proto3";
option go_package = "codenamecreator";
message NameRequest {
string category = 1;
}
message NameResult {
string name = 1;
}
service CodenameCreator {
rpc GetCodename(NameRequest) returns (NameResult) {}
rpc KeepGettingCodenames(stream NameRequest) returns (stream NameResult) {}
}
FROM golang:alpine AS build
RUN apk add git protobuf
RUN go get -u google.golang.org/grpc
RUN go get -u github.com/golang/protobuf/protoc-gen-go
# Copy files to container
WORKDIR /go/src/app
COPY . .
# Build proto file
WORKDIR /go/src/app/codenamecreator
RUN protoc --go_out=plugins=grpc:. *.proto
type codenameServer struct{}
func (s *codenameServer) GetCodename(ctx context.Context, request *creator.NameRequest) (*creator.NameResult, error) {
generator := newCodenameGenerator()
codename := generator.generate(request.Category)
return &creator.NameResult{Name: codename}, nil
}
func (s *codenameServer) KeepGettingCodenames(stream creator.CodenameCreator_KeepGettingCodenamesServer) error {
// server implementation
}
address := ":3000"
crt := "server.crt"
key := "server.key"
lis, err := net.Listen("tcp", address)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
creds, err := credentials.NewServerTLSFromFile(crt, key)
if err != nil {
log.Fatalf("Failed to load TLS keys")
}
grpcServer := grpc.NewServer(grpc.Creds(creds))
address := os.Getenv("SERVER_ADDRESS") // haproxy URL
crt := os.Getenv("TLS_CERT") // haproxy.crt
creds, err := credentials.NewClientTLSFromFile(crt, "")
if err != nil {
log.Fatalf("Failed to load TLS certificate")
}
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))
bind :3001 proto h2
client := creator.NewCodenameCreatorClient(conn)
ctx := context.Background()
// simple, unary function call
result, err := client.GetCodename(ctx, &creator.NameRequest{Category: category})
// stream example, keeps connection open
fmt.Println("Generating codenames...")
stream, err := client.KeepGettingCodenames(ctx)
2019/01/15 14:19:13 Received: Mighty Warthog
2019/01/15 14:19:14 Received: Quizzical Dolphin
2019/01/15 14:19:15 Received: Gallant Giraffe
2019/01/15 14:19:16 Received: Curious Aardvark
2019/01/15 14:19:17 Received: Sleepy Badger
2019/01/15 14:19:18 Received: Nefarious Warthog
2019/01/15 14:39:36 ---Updating codename category to: Science---
global
log stdout local0
maxconn 50000
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options ssl-min-ver TLSv1.1
defaults
log global
maxconn 3000
mode http
timeout connect 10s
timeout client 30s
timeout server 30s
option httplog
option logasap
option http-use-htx
frontend fe_proxy
bind :3001 ssl crt /path/to/cert.pem alpn h2
default_backend be_servers
backend be_servers
balance roundrobin
server server1 server:3000 check maxconn 20 ssl alpn h2 ca-file /usr/local/etc/haproxy/pem/server.crt
<134>Jan 15 14:38:46 haproxy[8]: 172.28.0.4:34366 [15/Jan/2019:14:38:46.988] fe_proxy~ be_servers/server1 0/0/2/0/+2 200 +79 - - ---- 1/1/1/1/0 0/0 "POST /CodenameCreator/KeepGettingCodenames HTTP/2.0"
POST /CodenameCreator/KeepGettingCodenames HTTP/2.0
content-type: application/grpc
user-agent: grpc-go/1.18.0-dev
te: trailers
host: haproxy:3001
HTTP/2.0 200
content-type: application/grpc
frontend fe_proxy
bind :3001 ssl crt /path/to/cert.pem alpn h2
acl isgrpc req.hdr(content-type) -m str "application/grpc"
use_backend grp_servers if isgrpc
default_backend be_servers
frontend fe_proxy
bind :3001 ssl crt /path/to/cert.pem alpn h2
http-request deny unless { req.hdr(mysecretpassphrase) -m str "abc123" }
default_backend be_servers
client := creator.NewCodenameCreatorClient(conn)
ctx := context.Background()
// Add some metadata to the context
ctx = metadata.AppendToOutgoingContext(ctx, "mysecretpassphrase", "abc123")
capture request header mysecretpassphrase len 100
<134>Jan 15 15:48:44 haproxy[8]: 172.30.0.4:35052 [15/Jan/2019:15:48:44.775] fe_proxy~ be_servers/server1 0/0/1/0/+1 200 +79 - - ---- 1/1/1/1/0 0/0 {abc123} "POST /CodenameCreator/KeepGettingCodenames HTTP/2.0"
frontend fe_proxy
bind :3001 ssl crt /path/to/cert.pem alpn h2
acl is_codename_path path /CodenameCreator/KeepGettingCodenames
acl is_otherservice_path path /AnotherService/SomeFunction
use_backend be_codenameservers if is_codename_path
use_backend be_otherservers if is_otherservice_path
default_backend be_servers
@danf0rth
Copy link

Does any one experience low performance with HAProxy LB in front of gRPC. I get a lot of timeout errors when connect directly to HAProxy that balancing between two servers. Even when i connect directly to single node i get much better throughput then two nodes behind HAProxy. What am i doing wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment