How to share a global dict between Cloud Run instances in python? - python

I'm building a flask server in python with Cloud Run, for a chatbot to call.
Sometimes if user wants to do something with the chatbot, the bot need ask the user to login to a 3rd party server before doing the things.
I have two routes:
Route 1 is "/login", it returns a simple iframe which will open a login page in a 3rd party server, generate a "session_id", and save some info I already get to a global variable dict called "runtimes" with the "session_id" as key, so that I can use it later when visitor successfully logged in.
Route 2 is "/callback/<session_id>". After user successfully login to its account, the 3rd party server will call this route with a token in url parameters. Then I will use the "session_id" to read the saved info from "runtimes", and do later things.
It works well in my local machine. But in Google Cloud Run, because it support multiple instances, sometimes it will trigger a new instance when server calls "callback", so it cannot get the "runtime" because they are in different instances.
I know that I can save the runtimes dict to a database to solve this problem, but it looks too overkill...Just not seem right.
Is there any easy way that I can make the "runtimes" be shared between instances?

The solution here is to use a central point of storage: database, memorystore, firestore,... something out of Cloud Run itself.
You can also try the Cloud Run execution runtime v2 that allow you to mount a network disk, such as Cloud Storage or Filestore. You can imagine to store the session data in a file which has the name of the session ID.
Note: On Cloud Run side, something is cooking, but it's not 100% safe, it will be a best effort. A database backup will be required even with that new feature

Related

How to get parameters from API Gateway in Cloud Functions

I have a API Gateway that calls a Cloud Function..
I want to create a config file to 3 routes:
..myurl/user
..myurl/client
..myurl/order
My problem is that I would like to use the same Cloud Function wrote in Python to deal with each scenario since it's just a function to get the data and write in BigQuery, in the end my need is just to know if the data is from user, client or order to switch correctly the insert target load.
Today I am using 3 different cloud functions for each path in the API spec.

Cloud Run: endpoint that runs a function as background job

I am trying to deploy a rest api in cloud run where one endpoint launches an async job. The job is defined inside a function in the code.
It seems one way to do it is to use Cloud Task, but this would mean to make a self-call to another endpoint of the deployed api. Specifically, to create an auxiliary endpoint that contains the job code (e.g. /run-my-function) and another one to set the queue to cloud task that launches the /run-my-function?
Is this the right way to do it or I have misunderstand something? In case it's the right way how to specify the url of the /run-my-function endpoint without explicitly hard-code the cloud run deployed uRL name?
The code for the endpoint that launches the endpoint with the run-my-function code would be:
from google.cloud import tasks_v2
client = tasks_v2.CloudTasksClient()
project = 'myproject'
queue = 'myqueue'
location = 'mylocation'
url = 'https://cloudrunservice-abcdefg-ca.b.run.app/run-my-function'
service_account_email = '12345#cloudbuild.gserviceaccount.com'
parent = client.queue_path(project, location, queue)
task = {
"http_request": {
"http_method": tasks_v2.HttpMethod.POST,
'url': url,
"oidc_token": {"service_account_email": service_account_email},
}
}
response = client.create_task(parent=parent, task=task)
However, this requires to hard-code the service name https://cloudrunservice-abcdefg-ca.b.run.app and to define an auxiliary endpoint /run-my-function that can be called via http
In your code you are able to get the Cloud Run URL without hardcoding it or setting it in an environment variable.
You can have a look to a previous article that I wrote, in the gracefull termison part. I provide a working code in Go, not so difficult to re-implement in Python.
Here the principle:
Get the Region and the project Number from the Metadata server. Keep in mind that Cloud Run has specific metadata like the region
Get the K_SERVICE env var (it's a standard Cloud Run env var)
Perform a call to the Cloud Run Rest API to get the service detail and customize the request with the data got previously
Extract the status.url JSON entry from the response.
Now you have it!
Let me know if you have difficulties to achieve that. I'm not good at Python, but I will be able to write that piece of code!

Trigger a function when message received in Azure IoT Hub

First I would say I'm new to Azure.
Most of my cloud experience comes from AWS.
I'm using IoT Hub with a connected device that sends a message every 1 min.
So far what I did what according to this guide from the Microsoft team:
https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-live-data-visualization-in-web-apps
Now, I wanted to create something like Lambda function in AWS, and from what I understand in Azure they called it Azure Functions. I wanted to create a function that gets triggered every time a new message from my device has been received, do something (let's say add 1) and then send it back (so I can pull the 'new' data to my backend).
So far what I did was to create a new "Azure Function" (which I guess it's like a container to functions?)
And then I try to create a new function by click 'Add new' and click on the 'IoT Hub (Event Hub)' template. But when I get to my code and try to test it I get a 404 response. Do I need to create something else? Do I need to create a new 'event' in my IoT Hub? Do I need to create a new 'Event Hub'?
Thanks!
P.s
I try to google it but must of the answers were with the old portal or in C#, I'm using Node and Python.
This scenario is covered in this sample. The sample is in JavaScript. It writes the messages to a database, but you can change this part if you want.
To answer some of your other questions:
IoTHub comes with a built-in Event Hub, so no need to create anything else! Your Azure Function will use an Event Hub trigger to subscribe to events coming from IoT Hub. By default, every event that a device sends to IoT Hub will end up on that endpoint, so to 'create' a new event, use the device SDK (on a device or on your machine) to send a message to IoT Hub.
You mentioned 'sending it back', but in most cases you don't have to respond to IoT Hub messages. You can for instance store the message in a database and build a web application that reads from that database. You could also get real-time updates in your web application, but that's outside the scope of your question.
I have tried to answer your queries below:-
I wanted to create a function that gets triggered every time a new
message from my device has been received, do something (let's say add 1)
and then send it back (so I can pull the 'new' data to my backend).
If you mean sending the data back to IoTHub, that doesn't seem logical to me as the manipulated data is something not sent by device. I would rather treat my Azure function as the backend and save/send the data in some persistent store or a message broker where it can be accessed by other consumer(s).
So far what I did was to create a new "Azure Function" (which I guess
it's like a container to functions?) And then I try to create a new
function by click 'Add new' and click on the 'IoT Hub (Event Hub)'
template. But when I get to my code and try to test it I get a 404
response. Do I need to create something else?
There are couple of ways by which you can create the Azure function with Built-in endpoints that is compatible with Event Hub as the trigger. Check below image. Relevant information about Built-in endpoints can be found here.
Do I need to create a new 'event' in my IoT Hub?
Not sure exactly what you mean by this. The way the flow work is
Send telemetry messages from device. NodeJS example can be found here.
You need to add message routing for messages arriving at the IoTHub should be received in Built-in endpoints. Check image below for telemetry message routing to Built-in endpoint.
Similarly you can route device twin change events, lifecycle events to Built-in endpoint.
Do I need to create a new 'Event Hub'?
Not required, as the Built-in endpoint is Event Hub compatible. Check documentation here. Unless, you have a specific need as per your business use case a custom Event hub endpoint is not required.
But when I get to my code and try to test it I get a 404 response.
Now, we need to trigger the azure function whenever a new event/message is received on the Built-in Endpoint. You can do this by couple of ways.
Azure portal
Command line
VS code
The main point to be noted above is your azure function binding[trigger] is set correctly in the function.json file. Here is how the trigger looks like.
MyEventHub and myEventHubReadConnectionAppSetting value should be picked from Application settings. Check image below.
I suggest you to go through this page for in depth understanding of how the Event hub trigger works with Azure function.
Once you have all the above steps done, you can open your Azure function app in portal and go to Functions section in the Function app blade. There you can monitor, code & test, check integration for your Azure function.

setting up a python http function in firestore

I have an app that is meant to integrate with third-party apps. These apps should be able to trigger a function when data changes.
The way I was envisioning this, I would use a node function to safely prepare data for the third parties, and get the url to call from the app's configuration on firestore. I would call that url from the node function, and wait for it to return, updating results as necessary (actually, triggering a push notification). -- these third-party functions would tend to be python functions, so my demo should be in python.
I have the initial node function and firestore setup so that I am currently triggering a ECONNREFUSED -- because I don't know how to set up the third-party function.
Let's say this is the function I need to trigger:
def hello_world(request):
request_json = request.get_json()
if request_json and 'name' in request_json:
name = request_json['name']
else:
name = 'World'
return 'Hello, {}!\n'.format(name)
Do I need to set up a separate gcloud account to host this function, or can I include it in my firestore functions? If so, how do I deploy this to firestore? Typically with my node functions, I am running firebase deploy and it automagically finds my functions from my index.js file.
If you're asking whether Cloud Functions that are triggered by Cloud Firestore can co-exist in a project with Cloud Functions that are triggered by HTTP(S) requests, then the answer is "yes they can". There is no need to set up a separate (Firebase or Cloud) project for each function type.
However: when you deploy your Cloud Functions through the Firebase CLI with firebase deploy, it will remove any functions that it finds in the project, that are not in the code. If you have functions both in Python and in Node.js, there is never a single codebase that contains both, so a blanket deploy would always delete some of your functions. So in that case, you should use the granular deploy option of the Firebase CLI.

Discovering peer instances in Azure Virtual Machine Scale Set

Problem: Given N instances launched as part of VMSS, I would like my application code on each azure instance to discover the IP address of the other peer instances. How do I do this?
The overall intent is to cluster the instances so, as to provide active passive HA or keep the configuration in sync.
Seems like there is some support for REST API based querying : https://learn.microsoft.com/en-us/rest/api/virtualmachinescalesets/
Would like to know any other way to do it, i.e. either python SDK or instance meta data URL etc.
The RestAPI you mentioned has a Python SDK, the "azure-mgmt-compute" client
https://learn.microsoft.com/python/api/azure.mgmt.compute.compute.computemanagementclient
One way to do this would be to use instance metadata. Right now instance metadata only shows information about the VM it's running on, e.g.
curl -H Metadata:true "http://169.254.169.254/metadata/instance/compute?api-version=2017-03-01"
{"compute":
{"location":"westcentralus","name":"imdsvmss_0","offer":"UbuntuServer","osType":"Linux","platformFaultDomain":"0","platformUpdateDomain":"0",
"publisher":"Canonical","sku":"16.04-LTS","version":"16.04.201703300","vmId":"e850e4fa-0fcf-423b-9aed-6095228c0bfc","vmSize":"Standard_D1_V2"},
"network":{"interface":[{"ipv4":{"ipaddress":[{"ipaddress":"10.0.0.4","publicip":"52.161.25.104"}],"subnet":[{"address":"10.0.0.0","dnsservers":[],"prefix":"24"}]},
"ipv6":{"ipaddress":[]},"mac":"000D3AF8BECE"}]}}
You could do something like have each VM send the info to a listener on VM#0, or to an external service, or you could combine this with Azure Files, and have each VM output to a common share. There's an Azure template proof of concept here which outputs information from each VM to an Azure File share.. https://github.com/Azure/azure-quickstart-templates/tree/master/201-vmss-azure-files-linux - every VM has a mountpoint which contains info written by every VM.

Categories

Resources