14

Marrying Fn Functions to Dapr.io-leverage the power of the proxy

 3 years ago
source link: https://medium.com/oracledevs/marrying-fn-functions-to-dapr-io-leverage-the-power-of-the-proxy-45590d08333a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Marrying Fn Functions to Dapr.io-leverage the power of the proxy

Project Fn provides a framework for creating and running serverless functions. It is the foundation for the Functions service on Oracle Cloud Infrastructure. Dapr.io is an open source project that provides a powerful personal assistant for any application and a distributed application runtime that especially shines with microservices. Some aspects of Dapr.io do not readily apply to Fn style serverless functions — such as subscription to message topics — but others can be quite useful. Dapr provides a large collection of bindings to dozens of technologies and cloud services that make it quite easy for any application to interact with this broad spectrum of components.

With Dapr, an application has a sidecar — a companion process (running Dapr) that acts as a know-it-all proxy. With simple HTTP or gRPC calls, the application requests Dapr to make the technology and service specific calls on its behalf and share the result.

0*gnrpbt0W544Rt56W.png
Node application uses Dapr Sidecar to interact with a plethora of services and technologies

This application doesn’t get bloated with technology-specific plumbing, its developers don’t need to know APIs, and interaction details for specific technologies, and switching between technologies becomes much easier. Note: the diagram shows a Node application but it is important to realize that the interaction between the application and Dapr is via HTTP or gRPC and any application that can interact over HTTP can leverage Dapr.

The objective in this article is to use Dapr in Fn functions. These are serverless functions that are based on a container image. When the function is triggered, a container is started from the image and the application inside the container is executed and will handle the triggering event. When the response is produced, the container will continue to run (stay hot) for a little while in case there will be another request to handle. After several minutes of inactivity, the container will be stopped until the next function request arrives.

This article provides a simple application that demonstrates how Fn functions can be enhanced with the powers of Dapr.io. In this tutorial, we’ll:

  • install the Dapr runtime into the Function container
  • configure some Dapr components
  • run Daprd runtime as a “sidecar” (parallel process) alongside the Fn function
  • invoke the Dapr Sidecar from Fn function to trigger the components in order to interact with external services
0*BjS51vqKSoLoXUmi.png
Fn Function uses Dapr to interact with external components

The application is a simple one, though not meaningful regarding its functionality. I have picked Node as the function implementation language. This choice is arbitrary — most of what is done here should work in a similar fashion for other FDKs, such as Java, Python, and Go. The range of external services to interact with can also easily be expanded — have the function interact with Oracle Database, MySQL, SQL Server, AWS S3, Apache Kafka, Twitter, SMTP etc. All can easily be added — with a simple yaml file to configure the external service and a straightforward HTTP exchange from application to Dapr sidecar.

All sources discussed in this article are available in this GitHub Repo: https://github.com/lucasjellema/fn-dapr.

Note: If you haven’t already done so, you can sign up for an Oracle Cloud Free Tier account today.

Building the Function Container Image

The Fn container images can be built using the Fn command line interface. This process usually starts from the predefined Fn container images for each of the supported languages using a predefined Dockerfile to build the container. It is also possible to build a container using a custom Dockerfile (and even using a custom container image as starting point — using the so called hot wrap approach).

In this case, I use a custom Dockerfile. In order to have the Fn CLI recognize this Dockerfile, I have specified in the func.yaml file with meta data regarding the function that the runtime to be used for the function is docker.

0*pqlaa5hppKdeDoql.png
schema_version: 20180708name: daprized-functionversion: 0.0.1runtime: docker

The objective is to have the daprd executable installed into the Function container image and to have the executable started when the container starts — next to the application that handles function requests.

The build process consists of two stages: using a build container (development time only) in which resources are collected and prepared and the final container image intended for runtime. I have extended the Dockerfile to install the Dapr CLI and initialize Dapr with the slim install. The resulting daprd executable file is subsequently used when the runtime container image is assembled.

0*a5C_O9zC7m78AMXC.png
Two stage build process that produces the function runtime container image

The runtime container image is extended with the installation of the daprd executable into the function’s home directory, setting of the environment variable DAPR_HTTP_PORT on which the Dapr Sidecar will listen for instructions from its companion application and finally the CMD to run the start:dapr script through npm (from the package.json file).

This script contains the following command — to run both Dapr Sidecar and the Node Function application:

“start:dapr”: “/function/daprd -app-id daprized-function -dapr-http-port $DAPR_HTTP_PORT -components-path ./components/ & npm run start”

This instruction tells Dapr to run, listen to application instructions on port $DAPRP_HTTP_PORT, and read component definitions from all yaml files in directory /function/components. It also runs the start script in package.json through npm in a parallel process thread. This starts the Node application that implements the function. This application registers itself with the Fn FDK for handling incoming function triggers.

The Dockerfile in its entirety is shown here (see GitHub Repo)

FROM fnproject/node:dev as build-stageWORKDIR /functionCOPY package.json /function/RUN npm installRUN apk updateRUN apk add --no-cache bash# Install the Dapr CLIRUN wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash# Install the latest Dapr Runtime through the CLI, -s is for slim-initRUN dapr init -s; dapr --versionFROM fnproject/nodeWORKDIR /function# copy contents of build directory to /functionCOPY . /function/# copy contents of node-modules collected in build-stage to the function containerCOPY --from=build-stage /function/node_modules/ /function/node_modules/# copy the daprd main binary to the function containerCOPY --from=build-stage /root/.dapr/bin/daprd /function/# this port can be any available port; it needs to be known to both daprd and the function; the latter uses it to invoke the formerENV DAPR_HTTP_PORT=3500CMD [ "npm", "run", "start:dapr" ]

To build the container image for the function that will have the Dapr executable inside and will start with Dapr running alongside the function application, these are the steps using the Fn CLI:

fn build -v
0*9hL0xRwXtQC-vPYs.png

To deploy the function to the local Fn server (that was started with “fn start -log-level DEBUG” in order to have full visibility in order to learn what is happening inside the container):

fn deploy -create-app -app tutorial -local -no-bump
0*9JKm0uKLHA4vGvUu.png

And now the function can be invoked at its most simplest using:

fn invoke tutorial daprized-function
0*FKeZrDR_nWR2sGtC.png

The response is constructed using the greetings fetched through Dapr from a file on GitHub and the fruit information is retrieved by Dapr on behalf of the Function from a GraphQL API. The Function does not know about either GitHub (and HTTPS communication) or about the GraphQL API and its whereabouts. In both cases, it just calls upon its Dapr personal assistant to do the job of invoking the external services.

0*zWo8Q-qGOGyaPjdc.png

Leveraging Dapr from the Function

In order for the Function to make use of Dapr, it invokes Dapr on the port specified through $DAPR_HTTP_PORT. Dapr has a set of APIs for invoking bindings, interacting with state stores, working with Pub/Sub brokers etc. These APIs are called using simple HTTP calls.

The configuration of the external components for Dapr to interact with is defined in yaml files in the components subfolder under /function. In this example application, only two components are defined. One is of type bindings.http and simply sends HTTP or HTTPS requests to an external endpoint. The endpoint is configured in the yaml file — the application does not (need to) know where it is or if it may have changed at some point (which is great for mock testing among other things)

0*edfKfd98irBI6VWO.png
apiVersion: dapr.io/v1alpha1kind: Componentmetadata:name: greetingsnamespace: defaultspec:type: bindings.httpversion: v1metadata:- name: urlvalue: https://raw.githubusercontent.com

The application needs to know the name (“greetings”) of the component in order to have Dapr invoke it on its behalf.

The other component used in this example is a GraphQL API that returns Fruit information. It is configured in yaml:

0*KnDIlStz61z2AYBW.png
apiVersion: dapr.io/v1alpha1kind: Componentmetadata:name: fruits.graphqlspec:type: bindings.graphqlversion: v1metadata:- name: endpointvalue:  https://fruits-api.netlify.app/graphql

Again, all the application needs to know to invoke it is its name (“fruits.graphql”) and of course what kind of query it can send to this API.

Dapr has an SDK for Node that provides the most convenient method for interacting with the Dapr sidecar in a Node application. I tried to use it, but ran into an error that I could not easily resolve: “ReferenceError: globalThis is not defined — (/function/node_modules/dapr-client/actors/runtime/ActorRuntimeConfig.js:3:20).” I therefore decided to fall back to straightforward HTTP requests to the sidecar.

This is the generic function for making any call to the Dapr sidecar:

0*xKdlxaEGe8uadlLq.png
Generic Node function for calling binding on Dapr Sidecar

With the daprizedCall function at our disposal, it becomes easy to retrieve a greeting from one external component and fruit data from another:

0*JSkZULfrY8TvAsqz.png
Using the generic function for calling specific bindings

These calls have the name of the binding as input (as defined in the yaml files in the components subdirectory) as well as the data to provide to Dapr in order to specify the operation to perform and the payload to process.

For completeness sake, here is the main function in this Node application:

0*OHW6aUSSdMJyLEmL.png
Main function’s function

This is where Function invocations first end up from the Fn runtime framework.

Conclusion

Using a slightly amended build process for Fn Functions, it turns out to be quite possible to add Dapr to Function container images and run Dapr sidecar as a companion process to a Function application. This allows the function to leverage Dapr as a proxy for making calls to components in many different technologies and offered on different platforms and cloud infrastructures. This makes development (and testing) much easier. Of course there is a price to be paid: running the sidecar requires resources and installing the daprd application in the function container takes up close to 100 MB in storage. This might be a price worth paying when the value of Dapr is fully leveraged.

Resources

GitHub Repository with the sources for this article: https://github.com/lucasjellema/fn-dapr

Dapr.io Home page — https://dapr.io/ -

How to take control over building of the Function container — Creating a Function from a Docker Image https://fnproject.io/tutorials/ContainerAsFunction/

How to install Dapr runtime into a custom container — Running Dapr on Azure IoT Edge https://xaviergeerinck.com/post/2021/4/23/iot-dapr-iot-edge

Troubleshooting and Logging with Fn — https://fnproject.io/tutorials/Troubleshooting/

Dapr HTTP binding component — https://docs.dapr.io/reference/components-reference/supported-bindings/http/

Dapr GraphQL binding component https://docs.dapr.io/reference/components-reference/supported-bindings/graghql/

Fruits GraphQL API https://fruits-api.netlify.app/graphql

Dapr arguments and annotations for daprd, CLI, and Kubernetes https://docs.dapr.io/reference/arguments-annotations-overview/

Install packages in Alpine docker — https://stackoverflow.com/questions/48281323/install-packages-in-alpine-docker

Dapr support for Oracle Cloud Infrastructure Object Storage Service — https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-oci-objectstorage/

Fn Hot Wrap — using a custom Docker Container Image — https://fnproject.io/tutorials/docker/CustomLinuxContainer/


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK