Skip to content

Commit bae6305

Browse files
committed
Add Azure Functions deployment implementation
This commit includes: - Azure Functions deployment logic implementation - Addressed Hancheng's PR comments (PEP 8 formatting, code cleanups) - Added documentation (loader.md) for Azure Functions deployment Signed-off-by: Kavithran <104263022+cavinkavi@users.noreply.github.com>
1 parent 11fc411 commit bae6305

13 files changed

+529
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# azurefunctionsconfig.yaml
2+
azurefunctionsconfig:
3+
resource_group: ExperimentResourceGroup # Name of the resource group
4+
storage_account_name: invitrostorage # Name of the storage account
5+
function_app_name: invitrofunctionapp # Name of the function app
6+
location: EastUS # Region where the resources will be created

azurefunctions_setup/host.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"applicationInsights": {
5+
"samplingSettings": {
6+
"isEnabled": true,
7+
"excludedTypes": "Request"
8+
}
9+
}
10+
},
11+
"extensionBundle": {
12+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
13+
"version": "[4.*, 5.0.0)"
14+
}
15+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"IsEncrypted": false,
3+
"Values": {
4+
"FUNCTIONS_WORKER_RUNTIME": "python",
5+
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
6+
"AzureWebJobsStorage": ""
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import time
2+
import socket
3+
import json
4+
import azure.functions as func
5+
import logging
6+
from time import perf_counter
7+
8+
# Global variable for hostname
9+
hostname = socket.gethostname()
10+
11+
# Simulate the busySpin function
12+
def busy_spin(duration_ms: int) -> None:
13+
end_time = perf_counter() + duration_ms / 1000 # Convert ms to seconds
14+
while perf_counter() < end_time:
15+
continue
16+
17+
# Convert TraceFunctionExecution
18+
def trace_function_execution(start: float, time_left_milliseconds: int) -> str:
19+
time_consumed_milliseconds = int((time.time() - start) * 1000)
20+
if time_consumed_milliseconds < time_left_milliseconds:
21+
time_left_milliseconds -= time_consumed_milliseconds
22+
if time_left_milliseconds > 0:
23+
busy_spin(time_left_milliseconds)
24+
25+
return f"OK - {hostname}"
26+
27+
# The handler function for Azure Functions (Python)
28+
def main(req: func.HttpRequest) -> func.HttpResponse:
29+
logging.info("Processing request.")
30+
31+
start_time = time.time()
32+
33+
# Parse JSON request body
34+
try:
35+
req_body = req.get_json()
36+
logging.info(f"Request body: {req_body}")
37+
except ValueError:
38+
logging.error("Invalid JSON received.")
39+
return func.HttpResponse(
40+
json.dumps({"error": "Invalid JSON"}),
41+
status_code=400,
42+
mimetype="application/json"
43+
)
44+
45+
runtime_milliseconds = req_body.get('RuntimeInMilliSec', 1000)
46+
memory_mebibytes = req_body.get('MemoryInMebiBytes', 128)
47+
48+
logging.info(f"Runtime requested: {runtime_milliseconds} ms, Memory: {memory_mebibytes} MiB")
49+
50+
# Trace the function execution (busy work simulation)
51+
result_msg = trace_function_execution(start_time, runtime_milliseconds)
52+
53+
# Prepare the response
54+
response = {
55+
"Status": "Success",
56+
"Function": req.url.split("/")[-1],
57+
"MachineName": hostname,
58+
"ExecutionTime": int((time.time() - start_time) * 1_000_000),
59+
"DurationInMicroSec": int((time.time() - start_time) * 1_000_000),
60+
"MemoryUsageInKb": memory_mebibytes * 1024,
61+
"Message": result_msg
62+
}
63+
64+
logging.info(f"Response: {response}")
65+
66+
return func.HttpResponse(
67+
json.dumps(response),
68+
status_code=200,
69+
mimetype="application/json"
70+
)
71+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"bindings": [
3+
{
4+
"authLevel": "anonymous",
5+
"type": "httpTrigger",
6+
"direction": "in",
7+
"name": "req",
8+
"methods": ["post"]
9+
},
10+
{
11+
"type": "http",
12+
"direction": "out",
13+
"name": "$return"
14+
}
15+
],
16+
"scriptFile": "azureworkload.py"
17+
}
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
azure-functions

cmd/config_azure_trace.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"Seed": 42,
3+
4+
"Platform": "AzureFunctions",
5+
"InvokeProtocol" : "http1",
6+
"EndpointPort": 80,
7+
8+
"BusyLoopOnSandboxStartup": false,
9+
10+
"TracePath": "data/traces/example",
11+
"Granularity": "minute",
12+
"OutputPathPrefix": "data/out/experiment",
13+
"IATDistribution": "exponential",
14+
"CPULimit": "1vCPU",
15+
"ExperimentDuration": 15,
16+
"WarmupDuration": 0,
17+
18+
"IsPartiallyPanic": false,
19+
"EnableZipkinTracing": false,
20+
"EnableMetricsScrapping": false,
21+
"MetricScrapingPeriodSeconds": 15,
22+
"AutoscalingMetric": "concurrency",
23+
24+
"GRPCConnectionTimeoutSeconds": 15,
25+
"GRPCFunctionTimeoutSeconds": 900,
26+
27+
"DAGMode": false
28+
}

cmd/loader.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func main() {
102102
"Dirigent-RPS",
103103
"Dirigent-Dandelion-RPS",
104104
"Dirigent-Dandelion",
105+
"AzureFunctions",
105106
}
106107

107108
if !slices.Contains(supportedPlatforms, cfg.Platform) {
@@ -157,7 +158,7 @@ func parseYAMLSpecification(cfg *config.LoaderConfiguration) string {
157158
case "firecracker":
158159
return "workloads/firecracker/trace_func_go.yaml"
159160
default:
160-
if cfg.Platform != "Dirigent" && cfg.Platform != "Dirigent-RPS" && cfg.Platform != "Dirigent-Dandelion-RPS" && cfg.Platform != "Dirigent-Dandelion" {
161+
if cfg.Platform != "Dirigent" && cfg.Platform != "Dirigent-RPS" && cfg.Platform != "Dirigent-Dandelion-RPS" && cfg.Platform != "Dirigent-Dandelion" && cfg.Platform != "AzureFunctions" {
161162
log.Fatal("Invalid 'YAMLSelector' parameter.")
162163
}
163164
}

docs/loader.md

+39-1
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,42 @@ Note:
262262
- Under `Manage Quota`, select `AWS Lambda` service and click `View quotas` (Alternatively, click [here](https://us-east-1.console.aws.amazon.com/servicequotas/home/services/lambda/quotas))
263263
- Under `Quota name`, select `Concurrent executions` and click `Request increase at account level` (Alternatively, click [here](https://us-east-1.console.aws.amazon.com/servicequotas/home/services/lambda/quotas/L-B99A9384))
264264
- Under `Increase quota value`, input `1000` and click `Request`
265-
- Await AWS Support Team to approve the request. The request may take several days or weeks to be approved.
265+
- Await AWS Support Team to approve the request. The request may take several days or weeks to be approved.
266+
267+
## Using Azure Functions
268+
269+
**Pre-requisites:**
270+
1. Microsoft Azure account with an active subscription ID
271+
2. Go installed
272+
273+
**Quick Setup for Azure Deployment:**
274+
1. Install the Azure CLI and verify installation:
275+
```bash
276+
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
277+
az --version
278+
```
279+
2. Log in as a user (Note: This will open a browser window to select Azure account):
280+
```bash
281+
az login
282+
```
283+
3. Create an Azure Service Principal:
284+
```bash
285+
az ad sp create-for-rbac --name "InVitro" --role Contributor --scopes /subscriptions/<your-subscription-id>
286+
```
287+
4. Set the following environment values:
288+
```bash
289+
export AZURE_APP_ID=<appId>
290+
export AZURE_PASSWORD=<password>
291+
export AZURE_TENANT=<tenant>
292+
```
293+
5. Use the Service Principal credentials in order to run automated scripts without manual login:
294+
```bash
295+
az login --service-principal --username $AZURE_APP_ID --password $AZURE_PASSWORD --tenant $AZURE_TENANT
296+
```
297+
6. Start the Azure Functions deployment experiment:
298+
```bash
299+
go run cmd/loader.go --config cmd/config_azure_trace.json
300+
```
301+
---
302+
Note:
303+
- Current deployment is via ZIP

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,5 @@ require (
5757
golang.org/x/net v0.33.0
5858
golang.org/x/text v0.21.0 // indirect
5959
google.golang.org/protobuf v1.34.2 // indirect
60+
gopkg.in/yaml.v2 v2.4.0
6061
)

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn
136136
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
137137
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
138138
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
139+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
140+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
139141
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
140142
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
141143
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)