Giter Club home page Giter Club logo

openfaas-gke's Introduction

OpenFaaS GKE

OpenFaaS

OpenFaaS is a serverless framework that runs on top of Kubernetes. It comes with a built-in UI and a handy CLI that takes you from scaffolding new functions to deploying them on your Kubernetes cluster. What's special about OpenFaaS is that you can package any executable as a function, and as long as it runs in a Docker container, it will work on OpenFaaS.

What follows is a step by step guide on running OpenFaaS with Kubernetes 1.8 on Google Cloud.

This setup is optimized for production use:

  • Kubernetes multi-zone cluster
  • OpenFaaS system API and UI are username/password protected
  • all OpenFaaS components have 1GB memory limits
  • the gateway read/write timeouts are set to one minute
  • asynchronous function calls with NATS streaming and three queue workers
  • optional GKE Ingress controller with Let's Encrypt TLS

Create a GCP project

Login to GCP and create a new project named openfaas. If you don't have a GCP account you can apply for a free trial. After creating the project, enable billing and wait for the API and related services to be enabled. Download and install the Google Cloud SDK from this page. After installing the SDK run gcloud init and then, set the default project to openfaas and the default zone to europe-west3-a.

Install kubectl using gcloud:

gcloud components install kubectl

Go to Google Cloud Platform -> API Manager -> Credentials -> Create Credentials -> Service account key and choose JSON as key type. Rename the file to account.json and put it in the project root. Add your SSH key under Compute Engine -> Metadata -> SSH Keys, also create a metadata entry named sshKeys with your public SSH key as value.

Create a Kubernetes cluster

Create a three node cluster with each node in a different zone:

k8s_version=$(gcloud container get-server-config --format=json | jq -r '.validNodeVersions[0]')

gcloud container clusters create demo \
    --cluster-version=${k8s_version} \
    --zone=europe-west3-a \
    --additional-zones=europe-west3-b,europe-west3-c \
    --num-nodes=1 \
    --machine-type=n1-standard-2 \
    --scopes=default,storage-rw

Increase the size of the default node pool to 6 nodes:

gcloud container clusters resize --size=2

You can delete the cluster at any time with:

gcloud container clusters delete demo -z=europe-west3-a

Set up credentials for kubectl:

gcloud container clusters get-credentials demo -z=europe-west3-a

Create a cluster admin user:

kubectl create clusterrolebinding "cluster-admin-$(whoami)" \
    --clusterrole=cluster-admin \
    --user="$(gcloud config get-value core/account)"

Grant admin privileges to kubernetes-dashboard (don't do this on a production environment):

kubectl create clusterrolebinding kube-system-cluster-admin \
    --clusterrole cluster-admin \
    --user system:serviceaccount:kube-system:default

You can access the kubernetes-dashboard at http://localhost:9099/ui using kubectl reverse proxy:

kubectl proxy --port=9099 &

Create a Weave Cloud Instance

Now that you have a Kubernetes cluster up and running you can start monitoring it with Weave Cloud. You'll need a Weave Could service token, if you don't have a Weave token go to Weave Cloud and sign up for a trial account.

Deploy the Weave Cloud agents:

kubectl apply -n kube-system -f \
"https://cloud.weave.works/k8s.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')&t=<WEAVE-TOKEN>"

Navigate to Weave Cloud Explore to inspect your K8S cluster:

nodes

Deploy OpenFaaS with basic authentication

Clone openfaas-gke repo:

git clone https://github.com/stefanprodan/openfaas-gke
cd openfaas-gke

Create the openfaas and openfaas-fn namespaces:

kubectl apply -f ./namespaces.yaml

Deploy OpenFaaS services in the openfaas namespace:

kubectl apply -f ./openfaas

This will create the pods, deployments and services for an OpenFaaS gateway, faas-netesd (K8S controller), Prometheus, Alert Manager, Nats and the Queue worker.

Before exposing OpenFaaS on the Internet, you'll need to setup authentication. First create a basic-auth secret with your username and password:

kubectl -n openfaas create secret generic basic-auth \
    --from-literal=user=admin \
    --from-literal=password=admin

Deploy Caddy as a reverse proxy for OpenFaaS gateway:

kubectl apply -f ./caddy

Wait for an external IP to be allocated and then use it to access the OpenFaaS gateway UI with your credentials at http://<EXTERNAL-IP>. You can get the external IP by running kubectl get svc.

get_gateway_ip() {
    kubectl -n openfaas describe service caddy-lb | grep Ingress | awk '{ print $NF }'
}

until [[ "$(get_gateway_ip)" ]]
 do sleep 1;
 echo -n ".";
done
echo "."
gateway_ip=$(get_gateway_ip)
echo "OpenFaaS Gateway IP: ${gateway_ip}"

Install the OpenFaaS CLI:

curl -sL cli.openfaas.com | sudo sh

Login with the CLI:

faas-cli login -u admin -p admin --gateway http://<EXTERNAL-IP>

If you want to avoid having your password in bash history, you could create a text file with it and use that along with the --password-stdin flag:

cat ~/faas_pass.txt | faas-cli login -u admin --password-stdin --gateway http://<EXTERNAL-IP>

You can logout at any time with:

faas-cli logout --gateway http://<EXTERNAL-IP>

Optionally you could expose the gateway using Google Cloud L7 HTTPS load balancer. A detailed guide on how to use Kubernetes Ingress controller with Let's Encrypt can be found here.

Deploy the OpenFaaS functions

Create a stack file named stack.yml containing two functions:

provider:
  name: faas
  gateway: http://<EXTERNAL-IP>

functions:
  nodeinfo:
    handler: node main.js
    image: functions/nodeinfo:burner
    labels:
      com.openfaas.scale.min: "2"
      com.openfaas.scale.max: "15"
  echo:
    handler: ./echo
    image: functions/faas-echo:latest

With the com.openfaas.scale.min label you can set the minimum number of running pods. With com.openfaas.scale.max you can set the maximum number of replicas for the autoscaler. By default OpenFaaS will keep a single pod running per function and under load it will scale up to a maximum of 20 pods.

Deploy nodeinfo and echo on OpenFaaS:

faas-cli deploy

The deploy command looks for a stack.yml file in the current directory and deploys all of the functions in the openfaas-fn namespace.

Invoke the functions:

echo -n "test" | faas-cli invoke echo --gateway=http://<EXTERNAL-IP>
echo -n "verbose" | faas-cli invoke nodeinfo --gateway=http://<EXTERNAL-IP>

Monitoring OpenFaaS

Let's run a load test with 10 function calls per second:

#install hey
go get -u github.com/rakyll/hey

#do 1K requests rate limited at 10 QPS
hey -n 1000 -q 10 -c 1 -m POST -d "verbose" http://<EXTERNAL-IP>/function/nodeinfo

In the Weave Cloud UI under Explore you'll see how OpenFaaS scales up the nodeinfo pods:

scaling

You can also monitor the scale up/down events with GCP Stackdrive Logs using this advanced filter:

resource.type="container"
resource.labels.cluster_name="demo"
resource.labels.namespace_id="openfaas"
logName:"gateway"
textPayload: "alerts"

Weave Cloud extends Prometheus by providing a distributed, multi-tenant, horizontally scalable version of Prometheus. It hosts the scraped Prometheus metrics for you, so that you don’t have to worry about storage or backups.

Weave Cloud comes with canned dashboards for Kubernetes that you can use to monitor a specific namespace:

k8s-metrics

You can also make your own dashboards based on OpenFaaS specific metrics:

openfaas-metrics

Create functions

With the OpenFaaS CLI you can chose between using a programming language template where you only need to provide a handler file, or a Docker template that you can build yourself. There are many supported languages like Go, JS (node), Python, C# and Ruby.

Let's create a function with Go that fetches the SSL/TLS certificate info for a given URL.

First create a directory for your functions under GOPATH:

mkdir -p $GOPATH/src/functions

Inside the functions dir use the CLI to create the certinfo function:

cd $GOPATH/src/functions
faas-cli new --lang go certinfo

Open handler.go in your favorite editor and add the certificate fetching code:

package function

import (
	"crypto/tls"
	"fmt"
	"net"
	"time"
)

func Handle(req []byte) string {
	address := string(req) + ":443"
	ipConn, err := net.DialTimeout("tcp", address, 2*time.Second)
	if err != nil {
		return fmt.Sprintf("Dial error: %v", err)
	}
	defer ipConn.Close()
	conn := tls.Client(ipConn, &tls.Config{
		InsecureSkipVerify: true,
	})
	if err = conn.Handshake(); err != nil {
		return fmt.Sprintf("Handshake error: %v", err)
	}
	defer conn.Close()
	addr := conn.RemoteAddr()
	host, port, err := net.SplitHostPort(addr.String())
	if err != nil {
		return fmt.Sprintf("Error: %v", err)
	}
	cert := conn.ConnectionState().PeerCertificates[0]

	return fmt.Sprintf("Host %v\nPort %v\nIssuer %v\nCommonName %v\nNotBefore %v\nNotAfter %v\nSANs %v\n",
		host, port, cert.Issuer.CommonName, cert.Subject.CommonName, cert.NotBefore, cert.NotAfter, cert.DNSNames)
}

Next to handler.go create handler_test.go with the following content:

package function

import (
	"regexp"
	"testing"
)

func TestHandleReturnsCorrectResponse(t *testing.T) {
	expected := "Google Internet Authority"
	resp := Handle([]byte("google.com"))

	r := regexp.MustCompile("(?m:" + expected + ")")
	if !r.MatchString(resp) {
		t.Fatalf("\nExpected: \n%v\nGot: \n%v", expected, resp)
	}
}

Modify the certinfo.yml file and add your Docker Hub username to the image name:

provider:
  name: faas
  gateway: http://localhost:8080

functions:
  certinfo:
    lang: go
    handler: ./certinfo
    image: stefanprodan/certinfo

Now let's build the function into a Docker image:

faas-cli build -f certinfo.yml

This will check your code for proper formatting with gofmt, run go build & test and pack your binary into an alpine image. If everything goes well, you'll have a local Docker image named username/certinfo:latest.

Push this image to Docker Hub. First create a public repository named certinfo, login to Docker Hub using docker CLI and then push the image:

docker login
faas-cli push -f certinfo.yml

Once the image is on Docker Hub you can deploy the function to your OpenFaaS GKE cluster:

faas-cli deploy -f certinfo.yml --gateway=http://<EXTERNAL-IP>

Invoke certinfo with:

$ echo -n "www.openfaas.com" | faas-cli invoke certinfo --gateway=<EXTERNAL-IP>

Host 147.75.74.69
Port 443
Issuer Let's Encrypt Authority X3
CommonName www.openfaas.com
NotBefore 2017-10-06 23:54:56 +0000 UTC
NotAfter 2018-01-04 23:54:56 +0000 UTC
SANs [www.openfaas.com]

Full source code of the certinfo function can be found on GitHub at stefanprodan/openfaas-certinfo. In the certinfo repo you can see how easy it is to do continuous deployments to Docker Hub with TravisCI for OpenFaaS functions.

Conclusions

Like most serverless frameworks, OpenFaaS empowers developers to build, ship and run code in the cloud. What OpenFaaS adds to this, is portability between dev and production environments with no vendor lock-in. OpenFaaS is a promising project and with over 64 contributors is a vibrant community. Even as a relatively young project, you can run it reliably in production backed by Kubernetes and Google Cloud. You can also use Weave Cloud to monitor and alert on any issues in your OpenFaaS project.

openfaas-gke's People

Contributors

johnmccabe avatar nenadilic84 avatar stefanprodan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

openfaas-gke's Issues

how to set timeouts

Original question
openfaas/faas#1016

OpenFaas timeout issue on GKE is 30 sec max. It doesn't seems like a OpenFaas settings but Kubernetes settings.

Please help to take a look this time and I am providing comprehensive troubleshooting steps and please update troubleshooting steps to include this as needed.

provider:
  name: faas
  gateway: https://endpoint

functions:
  duckling:     
    lang: dockerfile
    handler: ./function             
    image: gcr.io/[project_name]/duckling:latest 
    labels:
      com.openfaas.scale.min: 1
      com.openfaas.scale.max: 15
      com.openfaas.scale.factor: 50
    read_timeout: 300s  
    write_timeout: 300s  
    upstream_timeout: 300s 
    exec_timeout: 300s
    environment:
      read_timeout: 300s 
      write_timeout: 300s  
      upstream_timeout: 300s 
      exec_timeout: 300s
  

Expected Behaviour

Program running inside Kubenetes pods

ubuntu@duckling-85f846df8c-bn62l:~/function$ time echo "Today is Monday" | python3 index.py 
[{'dim': 'time', 'text': 'Monday', 'start': 9, 'end': 15, 'value': {'value': '2019-01-14T00:00:00.000Z', 'grain': 'day', 'others': [{'grain': 'day', 'value': '2019-01-07T00:00:00.000Z'}, {'grain': 'day', 'value': '2019-01-14T00:00:00.000Z'}, {'grain': 'day', 'value': '2019-01-21T00:00:00.000Z'}]}}, {'dim': 'time', 'text': 'Today', 'start': 0, 'end': 5, 'value': {'value': '2019-01-07T00:00:00.000Z', 'grain': 'day', 'others': [{'grain': 'day', 'value': '2019-01-07T00:00:00.000Z'}]}}]

real	2m50.517s [it takes 125 seconds]
user	1m0.818s
sys	0m1.127s

$ time curl -s https://[endpoint]/function/duckling -d "today meeting is at 11am"

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>

real	0m30.067s
user	0m0.014s
sys	0m0.000s
$ time curl -s https://[endpoint]/function/duckling -d "today meeting is at 11am"

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>

real	0m30.064s
user	0m0.007s
sys	0m0.008s

Kubernetes has a timeout of 30 seconds. I tried many place but still can't find where it is

$ kubectl logs -n openfaas-fn deploy/duckling
2019/01/07 07:28:39 Version: 0.9.14	SHA: a65df4795bc66147c41161c48bfd4c72f60c7434
2019/01/07 07:28:39 Read/write timeout: 5m0s, 5m0s. Port: 8080
2019/01/07 07:28:39 Writing lock-file to: /tmp/.lock
2019/01/07 07:28:55 Forking fprocess.
2019/01/07 07:30:11 Wrote 690 Bytes - Duration: 75.473247 seconds
2019/01/07 07:30:24 Forking fprocess.
2019/01/07 07:33:19 Wrote 890 Bytes - Duration: 175.281187 seconds
2019/01/07 07:36:38 Forking fprocess.
2019/01/07 07:37:41 Wrote 890 Bytes - Duration: 62.304900 seconds
2019/01/07 07:39:18 Forking fprocess.
2019/01/07 07:40:24 Wrote 890 Bytes - Duration: 66.362533 seconds
2019/01/07 07:54:00 Forking fprocess.
2019/01/07 07:55:04 Wrote 890 Bytes - Duration: 64.284393 seconds
2019/01/07 07:55:21 Forking fprocess.
2019/01/07 07:56:26 Wrote 890 Bytes - Duration: 64.773979 seconds
2019/01/07 07:59:21 Forking fprocess.
2019/01/07 08:00:22 Wrote 890 Bytes - Duration: 61.696822 seconds
2019/01/07 08:01:15 Forking fprocess.
2019/01/07 08:01:15 Wrote 25 Bytes - Duration: 0.073726 seconds [I changed my function here to just write output]
2019/01/07 08:01:40 Forking fprocess.
2019/01/07 08:02:44 Wrote 25 Bytes - Duration: 63.800074 seconds
2019/01/07 08:16:14 Forking fprocess.
2019/01/07 08:17:20 Wrote 25 Bytes - Duration: 66.350439 seconds
2019/01/07 08:35:14 Forking fprocess.
2019/01/07 08:36:15 Wrote 25 Bytes - Duration: 61.254206 seconds
2019/01/07 09:22:09 Forking fprocess.
2019/01/07 09:23:18 Wrote 25 Bytes - Duration: 69.571718 seconds
2019/01/07 09:25:40 Forking fprocess.

Current Behaviour

Possible Solution

Steps to Reproduce (for bugs)

  1. Creating a Kubernetes clusers
  2. Use helm to deploy OpenFaaS
  3. Create an nginx Ingress with HTTPS

Context

Your Environment

  • FaaS-CLI version ( Full output from: faas-cli version ):
$ faas-cli version
  ___                   _____           ____
 / _ \ _ __   ___ _ __ |  ___|_ _  __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) |  __/ | | |  _| (_| | (_| |___) |
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|____/
      |_|

CLI:
 commit:  b24c5763d9b61e0c04018a722f8f2f765498f18a
 version: 0.7.8
  • Docker version docker version (e.g. Docker 17.0.05 ):
$ docker version
Client:
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:49:01 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:16:44 2018
  OS/Arch:          linux/amd64
  Experimental:     false
$ docker version
Client:
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:49:01 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:16:44 2018
  OS/Arch:          linux/amd64
  Experimental:     false
  • Are you using Docker Swarm or Kubernetes (FaaS-netes)?

FaaS-Netes (Helm), nginx ingress

  • Operating System and version (e.g. Linux, Windows, MacOS):

Ubuntu 16.04

  • Link to your project or a code example to reproduce issue:

Any program run more than 30 seconds breaks.

  • Please also follow the [troubleshooting guide]

(https://github.com/openfaas/faas/blob/master/guide/troubleshooting.md) and paste in any other diagnostic information you have:

I have follow this guide more than 20 times.

$ kubectl get deploy -n openfaas  
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
alertmanager   1/1     1            1           82d
caddy-tls      1/1     1            1           82d
faas-netesd    1/1     1            1           82d
gateway        1/1     1            1           82d
kube-lego      1/1     1            1           82d
nats           1/1     1            1           82d
prometheus     1/1     1            1           82d
queue-worker   3/3     3            3           82d

Possible gateway timeout

$ kubectl get deploy/gateway -n openfaas -o yaml | grep timeout -A2
        - name: write_timeout
          value: 300s
        - name: read_timeout
          value: 300s
        - name: upstream_timeout
          value: 240s
        image: functions/gateway:0.7.8

Possible Ingress timeout

$ kubectl get ingress -n openfaas -o yaml | grep timeout
ingress.kubernetes.io/proxy-connect-timeout: 300s
ingress.kubernetes.io/proxy-read-timeout: 300s
ingress.kubernetes.io/proxy-send-timeout: 300s
ingress.kubernetes.io/upstream-fail-timeout: 300s
nginx.ingress.kubernetes.io/proxy-read-timeout: 600s
nginx.ingress.kubernetes.io/proxy-send-timeout: 600s

$ kubectl get deploy/caddy-tls -n openfaas -o yaml | grep -i timeout
    ingress.kubernetes.io/proxy-connect-timeout: 300s
    ingress.kubernetes.io/proxy-read-timeout: 300s
    ingress.kubernetes.io/proxy-send-timeout: 300s
    ingress.kubernetes.io/upstream-fail-timeout: 300s
    nginx.ingress.kubernetes.io/proxy-read-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-send-timeout: 600s
$ kubectl exec -n openfaas -it caddy-tls-95f8456b6-fgsw5 -- ash
/www # ps auxf
PID   USER     TIME   COMMAND
    1 root       0:02 caddy -agree --conf /Caddyfile
   16 root       0:00 ash
   20 root       0:00 ps auxf
/www #
/www # cat /Caddyfile 
:80 {
    status 200 /healthz
    basicauth /system {$ADMIN_USER} {$ADMIN_PASSWORD}
    basicauth /ui {$ADMIN_USER} {$ADMIN_PASSWORD}
    proxy / gateway:8080 {
            transparent
        }
    timeouts 300s ### added this!
    errors stderr
    tls off
}
/www # 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.