New to Voyager? Please start here.

Configure HTTP/2 and GRPC

You can configure HTTP/2 by configuring proto or, alpn under rules.http section (for frontend) or, backend section (for specific backend). If you want to use only HTTP/2.0, then you can specify it using proto: h2. However, if you like to use both HTTP/2.0 and HTTP/1.1 in a preferred order, then you need to specify the order using ALPN.

Please note the followings:

  • TLS needed to be configured for using ALPN.
  • A single rule/backend can’t use both ALPN and proto.
  • Multiple rules pointing the same frontend can’t use different ALPN or, proto configurations.

Example: gRPC Without TLS

First create demo namespace for this example.

$ kubectl create namespace demo
namespace/demo created

Deploy gRPC Test Server

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: grpc-server
  name: grpc-server
  namespace: demo
spec:
  selector:
    matchLabels:
      run: grpc-server
  template:
    metadata:
      labels:
        run: grpc-server
    spec:
      containers:
      - image: appscode/hello-grpc:0.1.0
        args:
        - run
        - --v=3
        name: grpc-server
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  labels:
    run: grpc-server
  name: grpc-server
  namespace: demo
spec:
  ports:
  - port: 3000
    targetPort: 8080
    name: h2c
  selector:
    run: grpc-server

Create Ingress

apiVersion: voyager.appscode.com/v1
kind: Ingress
metadata:
  name: test-ingress
  namespace: demo
spec:
  rules:
  - host: "*"
    http:
      port: 3001
      proto: h2
      paths:
      - path: /
        backend:
          service:
            name: grpc-server
            port:
              number: 3000
          proto: h2

Generated haproxy.cfg

# HAProxy configuration generated by https://github.com/appscode/voyager
# DO NOT EDIT!
global
  daemon
  stats socket /var/run/haproxy.sock level admin expose-fd listeners
  server-state-file global
  server-state-base /var/state/haproxy/
  # log using a syslog socket
  log /dev/log local0 info
  tune.ssl.default-dh-param 2048
  ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
  lua-load /etc/auth-request.lua
  hard-stop-after 30s
defaults
  log global
  # https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-option%20abortonclose
  # https://github.com/voyagermesh/voyager/pull/403
  option dontlognull
  option http-server-close
  option http-use-htx
  option logasap
  # Timeout values
  timeout client 50s
  timeout client-fin 50s
  timeout connect 5s
  timeout server 50s
  timeout tunnel 50s
  # Configure error files
  # default traffic mode is http
  # mode is overwritten in case of tcp services
  mode http
frontend http-0_0_0_0-3001
  bind *:3001  proto h2
  mode http
  option httplog
  option forwardfor
  acl is_proxy_https hdr(X-Forwarded-Proto) https
  acl is_proxy_https ssl_fc
  http-request set-var(req.scheme) str(https) if is_proxy_https
  http-request set-var(req.scheme) str(http) if ! is_proxy_https
  acl acl_: path_beg /
  use_backend grpc-server.demo:3000 if  acl_:
backend grpc-server.demo:3000
  server pod-grpc-server-6c7f686bbb-jhngb 172.17.0.6:8080        proto h2

Check Response

$ minikube service --url voyager-test-ingress -n demo
http://192.168.99.100:30652

Run gRPC client using docker.

$ docker run -it appscode/hello-grpc:0.1.0 client --address=192.168.99.100:30652 --name=Voyager
2019/02/05 08:01:18 192.168.99.100:30652
2019/02/05 08:01:18 intro:"hello, Voyager!"

Cleanup

$ kubectl delete ns demo
namespace "demo" deleted

Example: gRPC With TLS

First create demo namespace for this example.

$ kubectl create namespace demo
namespace/demo created

Create Secrets

Generate certs and create secret for backend server. Here we are using onessl. You can use openssl or any other tools to generate the certificates.

$ mkdir server-certs; cd server-certs
$ onessl create ca-cert
$ onessl create server-cert --domains ssl.appscode.test --ips 192.168.99.100,127.0.0.1
$ cat {server.crt,ca.crt} > bundle.crt
$ kubectl create secret tls server-secret -n demo --cert=bundle.crt --key=server.key

Generate certs and create secret for haproxy:

$ mkdir haproxy-certs; cd haproxy-certs
$ onessl create ca-cert
$ onessl create server-cert --domains ssl.appscode.test --ips 192.168.99.100,127.0.0.1
$ cat {server.crt,ca.crt} > bundle.crt
$ kubectl create secret tls haproxy-secret -n demo --cert=bundle.crt --key=server.key

Deploy gRPC Test Server

Deploy the gRPC server and mount the server-secret.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: grpc-server
  name: grpc-server
  namespace: demo
spec:
  selector:
    matchLabels:
      run: grpc-server
  template:
    metadata:
      labels:
        run: grpc-server
    spec:
      volumes:
      - name: secret-vol
        secret:
          secretName: server-secret
      containers:
      - image: appscode/hello-grpc:0.1.0
        args:
        - run
        - --tls-cert-file=/etc/certs/tls.crt
        - --tls-private-key-file=/etc/certs/tls.key
        - --v=3
        name: grpc-server
        imagePullPolicy: Always
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: secret-vol
          mountPath: "/etc/certs"
          readOnly: true
---
apiVersion: v1
kind: Service
metadata:
  labels:
    run: grpc-server
  name: grpc-server
  namespace: demo
  annotations:
    ingress.appscode.com/backend-tls: ssl ca-file /tmp/certs/tls.crt
spec:
  ports:
  - port: 3000
    targetPort: 8443
    name: h2
  selector:
    run: grpc-server

Here, ingress.appscode.com/backend-tls will be used by voyager to configure backend TLS.

Create Ingress

Create ingress and specify the spec.tls and spec.configVolumes.

apiVersion: voyager.appscode.com/v1
kind: Ingress
metadata:
  name: test-ingress
  namespace: demo
spec:
  tls:
  - secretName: haproxy-secret
    hosts:
    - "*"
  configVolumes:
  - name: server-certs-vol
    secret:
      secretName: server-secret
    mountPath: /tmp/certs
  rules:
  - host: "*"
    http:
      port: 3001
      alpn:
      - h2
      - http/1.1
      paths:
      - path: /
        backend:
          service:
            name: grpc-server
            port:
              number: 3000
          alpn:
          - h2
          - http/1.1

Generated haproxy.cfg

# HAProxy configuration generated by https://github.com/appscode/voyager
# DO NOT EDIT!
global
  daemon
  stats socket /var/run/haproxy.sock level admin expose-fd listeners
  server-state-file global
  server-state-base /var/state/haproxy/
  # log using a syslog socket
  log /dev/log local0 info
  tune.ssl.default-dh-param 2048
  ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
  lua-load /etc/auth-request.lua
  hard-stop-after 30s
defaults
  log global
  # https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-option%20abortonclose
  # https://github.com/voyagermesh/voyager/pull/403
  option dontlognull
  option http-server-close
  option http-use-htx
  option logasap
  # Timeout values
  timeout client 50s
  timeout client-fin 50s
  timeout connect 5s
  timeout server 50s
  timeout tunnel 50s
  # Configure error files
  # default traffic mode is http
  # mode is overwritten in case of tcp services
  mode http
frontend http-0_0_0_0-3001
  bind *:3001  ssl no-sslv3 no-tlsv10 no-tls-tickets crt /etc/ssl/private/haproxy/tls/  alpn h2,http/1.1 
  # Mark all cookies as secure
  rsprep ^Set-Cookie:\ (.*) Set-Cookie:\ \1;\ Secure
  # Add the HSTS header with a 6 month default max-age
  http-response set-header Strict-Transport-Security max-age=15768000
  mode http
  option httplog
  option forwardfor
  acl is_proxy_https hdr(X-Forwarded-Proto) https
  acl is_proxy_https ssl_fc
  http-request set-var(req.scheme) str(https) if is_proxy_https
  http-request set-var(req.scheme) str(http) if ! is_proxy_https
  acl acl_: path_beg /
  use_backend grpc-server.demo:3000 if  acl_:
backend grpc-server.demo:3000
  server pod-grpc-server-7578bf9696-rbq49 172.17.0.7:8443     ssl ca-file /tmp/certs/server.crt    alpn h2,http/1.1

Check Response

$ minikube service --url voyager-test-ingress -n demo
http://192.168.99.100:30446

Run gRPC client using docker and specify the haproxy certs.

$ docker run -v $(pwd)/haproxy-certs:/tmp/certs -it appscode/hello-grpc:0.1.0 client --address=192.168.99.100:30446 --crt=/tmp/certs/bundle.crt --name=Voyager
2019/02/05 12:11:13 192.168.99.100:30446
2019/02/05 12:11:13 intro:"hello, Voyager!"

Cleanup

$ kubectl delete ns demo
namespace "demo" deleted

Reference