Invoking Functions by Name with a Kubernetes Ingress

On This Page

Overview

If you followed the Getting Started with Nuclio on Kubernetes or Getting Started with Nuclio on Google Kubernetes Engine (GKE) guide, you invoked functions using their HTTP interface with nuctl and the Nuclio dashboard. By default, each function deployed to Kubernetes declares a Kubernetes service that is responsible for routing requests to the functions' HTTP trigger port. To invoke the function externally, using nuctl, you probably exposed your function by using a NodePort, which is a unique cluster-wide port that's assigned to the function.

This means that if your function's HTTP trigger is configured with a NodePort, any underlying HTTP client can call http://<your cluster IP>:<some unique port> to reach it. You can try this out yourself: First, find out the NodePort assigned to your function, by using the nuctl get function command of the nuctl CLI or the kubectl get svc command of the Kubernetes CLI. Then, use curl to send an HTTP request to this port.

In addition to configuring a service, Nuclio can create a Kubernetes ingress for your function's HTTP trigger, with the path specified as <function name>/latest. However, without an ingress controller running on your cluster, this will have no effect. An Ingress controller will listen for changed ingresses and reconfigure some type of reverse proxy to route requests based on rules specified in the ingress resource.

Setting up an ingress controller

In this guide, you'll set up a Træfik controller, but any type of Kubernetes ingress controller should work. You can read Træfik's excellent documentation, but for the purposes of this guide you can simply run the following commands to set up the controller by using either of the following alternative methods:

  • Using kubectl to apply the resource YAML files. Note that this installs v1.7, and that the YAML files were removed from newer versions:

    kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/traefik-rbac.yaml
    kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/traefik-deployment.yaml
    
  • Using helm (provided helm is installed):

    helm install stable/traefik --name traefik --namespace kube-system
    

Verify that the controller is up by running the kubectl --namespace=kube-system get pods command, and then run the kubectl describe service --namespace=kube-system traefik-ingress-service command to get the ingress NodePort. Following is a sample output for NodePort 30019:

...
Port:                     web  80/TCP
TargetPort:               80/TCP
NodePort:                 web  30019/TCP
Endpoints:                172.17.0.8:80
Port:                     admin  8080/TCP
TargetPort:               8080/TCP
...
Note
You must ensure that all your requests are sent to the returned NodePort.

Run the following command to deploy the sample helloworld function; (the command assumes the use of Minikube):

nuctl deploy -p https://raw.githubusercontent.com/nuclio/nuclio/master/hack/examples/golang/helloworld/helloworld.go --registry $(minikube ip):5000 helloworld --run-registry localhost:5000

And now, invoke the function by its path. Replace <NodePort> with the NodePort of your ingress controller, and replace ${minikube ip) with your cluster IP if you are not using Minikube:

curl $(minikube ip):<NodePort>/helloworld/latest

For example, for NodePort 30019, run this command:

curl $(minikube ip):30019/helloworld/latest

Customizing function ingress

By default, functions initialize the HTTP trigger and register <function name>/latest. However, you might want to add paths for functions to organize them in namespaces/groups, or even choose through which domain your functions can be triggered. To do this, you can configure your HTTP trigger in the function's configuration. For example:

  ...
  triggers:
    http:
      maxWorkers: 4
      kind: "http"
      attributes:
        ingresses:
          i1:

            # this assumes that some.host.com points to <cluster ip>
            host: "some.host.com"
            paths:
            - "/first/path"
            - "/second/path"
          i2:
            paths:
            - "/wat"

If your helloworld function was configured in this way, and assuming that Træfik's NodePort is 30019, the function would be accessible through any of the following URLs:

  • <cluster ip>:30019/helloworld/latest
  • some.host.com:30019/helloworld/latest
  • some.host.com:30019/first/path
  • some.host.com:30019/second/path
  • <cluster ip>:30019/wat
  • some.host.com:30019/wat

Note that since the i1 configuration explicitly specifies some.host.com as the host for the paths, the function will not be accessible through the cluster IP; i.e., <cluster ip>:30019/first/path will return a 404 error.

Deploying an ingress example

To put this into practice, following is an example that deploys the ingress example. This is the function.yaml file for the example:

apiVersion: "nuclio.io/v1"
kind: "NuclioFunction"
spec:
  runtime: "golang"
  triggers:
    http:
      maxWorkers: 8
      kind: http
      attributes:
        ingresses:
          first:
            paths:
            - /first/path
            - /second/path
          second:
            host: my.host.com
            paths:
            - /first/from/host

And this is the definition of the Ingress handler function:

func Ingress(context *nuclio.Context, event nuclio.Event) (interface{}, error) {
	return "Handler called", nil
}

Deploy the function

Deploy the function with the nuctl CLI. If you did not use Minikube, replace $(minikube ip):5000 in the following command with your cluster IP:

nuctl deploy -p https://raw.githubusercontent.com/nuclio/nuclio/master/hack/examples/golang/ingress/ingress.go --registry $(minikube ip):5000 ingress --run-registry localhost:5000 --verbose

Behind the scenes, nuctl populates a function CR, which is picked up by the Nuclio controller. The controller iterates through all the triggers and looks for the required ingresses. For each ingress, the controller creates a Kubernetes Ingress object, which triggers the Træfik ingress controller to reconfigure the reverse proxy. Following are sample controller logs:

controller.functiondep (D) Adding ingress {"function": "helloworld", "host": "", "paths": ["/helloworld/latest"]}
controller.functiondep (D) Adding ingress {"function": "helloworld", "host": "my.host.com", "paths": ["/first/from/host"]}
controller.functiondep (D) Adding ingress {"function": "helloworld", "host": "", "paths": ["/first/path", "/second/path"]

Invoke the function with nuctl

Invoke the function with nuctl, which will use the configured NodePort:

nuctl invoke ingress

Following is a sample output for this command:

> Response headers:
Server = nuclio
Date = Thu, 02 Nov 2017 02:11:32 GMT
Content-Type = text/plain; charset=utf-8
Content-Length = 14

> Response body:
Handler called

Configure a custom host

Add my.host.com to your local /etc/hosts file so that it resolves to your cluster IP. The following command assumes the use of Minikube:

echo "$(minikube ip) my.host.com" | sudo tee -a /etc/hosts

Invoke the function with curl

Now, do some invocations with curl. The following examples assume the use of Minikube (except were your configured host is used) and NodePort 30019.

Note
The parenthesized "works" and error indications at the end of each line signify the expected outcome and are not part of the command.
curl $(minikube ip):30019/ingress/latest (works)
curl my.host.com:30019/ingress/latest (works)

curl $(minikube ip):30019/first/path (works)
curl my.host.com:30019/first/path (works)

curl my.host.com:30019/first/from/host (works)
curl $(minikube ip):30019/first/from/host (404 error)