Running .NET on Google Cloud Functions

salmaan rashid
Jul 29, 2017 · 4 min read

This is a sample application running a .NET Cloud Function as an HTTP Trigger. While GCF (Google Cloud Functions) currently only supports NodeJS, there are some techniques you can use to launch different language runtimes and delegate control of the socket to that runtime. This article and code linked from it at the bottom describes one technique to run .NET on GCF.

Disclaimer: This is not an official Google product. It is not and will not be maintained by Google, and is not part of Google Cloud Functions project. There is no guarantee of any kind, including that it will work or continue to work, or that it will supported in any way.

The basic flow of this application initially starts GCF as node application, then delegates control of the listen socket to your target runtime (.NET in this case).

On application startup:

  1. Initialize NodeJS GCF entrypoint (index.js on port :8080)
  2. NodeJS launches (gyp add-on cpp binary (execer.cc)
  3. execer binary scans NodeJS’s socket file descriptors
  4. execer binary launches .NET binary (bin/mainapp) and passes the listener socket to .NET
  5. .NET application “takes control” of the listening socket.
  6. .NET application now receives user-requests directly as a GCF function

Wait…so how are you able to run .NET on GCF without coreCLR installed?

Well, while the full coreCLR isn’t installed, all that is needed to run a simple .NET binary *is* uploaded during the deployment.

In this case, during build time, the default Dockerfile acquires all the shared_objects (.so) files for .NET, compiles the sample application into a binary for linux, then uploads it.

So, when you stage the build files for deployment, the following folders get generated (all of this is done for you via make!!)

  • ~/bin/ contains the the .NET binary to execute and supporting .dll
  • ~/lib contains all the .so files required to run coreCLR
  • ~/node_modules contains the NodeJS files to launch the intermediate execer.cc add-on module

You can find the full git repo here:


The easiest way to try this out is with Google Cloud Shell as it already has docker 17+, node and dotnet2.0+

  1. Enable Cloud Functions API on your project.
  2. Open up Cloud Shell
  3. Get the repo
git clone https://github.com/salrashid123/cloud-functions-dotnet.git 

4. Build and stage the files:

cd cloud-functions-dotnetdocker build -t docker_tmp .docker cp `docker create docker_tmp`:/user_code/lib .
docker cp `docker create docker_tmp`:/user_code/bin .
docker cp `docker create docker_tmp`:/user_code/node_modules .

5. Deploy

Remember to specify the staging GCS bucket.

gcloud beta functions deploy gcfdotnet --stage-bucket your_staging_bucket --trigger-http

6. Verify

Deployment by invoking the endpoint URL (this may take sometime to initialize first time)

curl -v https://us-central1-your_project.cloudfunctions.net/gcfdotnet

Thats it! The following section describes how this works and how to build if you have dotnet locally

This sample uses Docker multi-stage builds, compiles nodeJS modules, then dotnet, and finally acquires the requisite linux shared_objects to run on Linux. Once all those files are ready within the container, the last set of steps ‘copies’ them out of the container so you can deploy the function directly.

Note, if you build the full Default dockerfile, you can execute the container since the build step includes this by default:

docker run -p 8080:8080 -t docker_tmp

Revision: 1/28/18: Update install instructions for multi-stage builds

Writing a cloud function

You need to have dotnet 2.0 available:

Edit Startup.cs file to add on your HTTP Trigger function here.:

At this point, you can build and deploy with docker as shown above.

Building dotnet bin/ folder locally

If you have dotnet2.0 installed locally, you can build and run it locally to to test…so on your laptop, run

dotnet restore -r ubuntu.14.04-x64
dotnet publish -c Release -r ubuntu.14.04-x64 -o bin/

then set the permissions before deploying and executing:

chmod u+x bin/mainapp
bin/mainapp

The entrypoint for your cloud function must be ‘/execute’ (meaning that this endpoint is what is called on the http-trigger

http://localhost:8080/execute

Note that if you wish to develop locally and have dotnet installed, you can modify the default Docker file to not build the dotnet binary:

but build the donet locally for deployment:

dotnet restore -r ubuntu.14.04-x64
dotnet publish -c Release -r ubuntu.14.04-x64 -o bin/
chmod u+x bin/mainapp

Appendix

Required libraries for dotnet

Essentially this is a copy of the required minimum set needed for dotnet to run on the OS wihtout extras. For more information on this, see distroless builds

How this application works

The following snippets and links describes how control transfer for the socket file descriptors takes place from

node -> cpp -> .NET

Acquire the file descriptors here by modifying the following to pass in LD_LIBRARY_PATH environment variables:

change to:

Then in .NET, use the following Kestrel Options to listen on a handler.

Program.cs

Google Cloud Platform - Community

A collection of technical articles published or curated by Google Cloud Platform Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

salmaan rashid

Written by

Google Cloud Platform - Community

A collection of technical articles published or curated by Google Cloud Platform Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.