Add API deployment instructions and artifacts. (#7)

This commit is contained in:
Patrick Flickinger 2020-04-20 16:26:57 -07:00 коммит произвёл GitHub
Родитель 2da72244f1
Коммит 7dcdab4311
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
27 изменённых файлов: 639 добавлений и 72 удалений

Просмотреть файл

@ -1,7 +0,0 @@
apiVersion: azure.com/v1alpha2
kind: CustomMetric
metadata:
name: camera-trap-detection-sync-appinsights-metric-detect
spec:
metric:
metricName: customMetrics//CURRENT_REQUESTS/v1/camera-trap/detection-sync/detect

Просмотреть файл

@ -0,0 +1,7 @@
apiVersion: azure.com/v1alpha2
kind: CustomMetric
metadata:
name: service-name-appinsights-metric-endpointname
spec:
metric:
metricName: customMetrics//CURRENT_REQUESTS/service-name/endpointname

Просмотреть файл

@ -0,0 +1,12 @@
apiVersion: v1
description: My description.
name: service-name
version: 1.0.0
keywords:
- sync
home: "https://www.microsoft.com/en-us/aiforearth"
sources:
- "https://github.com/microsoft/AIforEarth-API-Platform"
maintainers:
- name: AI for Earth Engineering
- email: aiforearthteam@microsoft.com

Просмотреть файл

@ -0,0 +1,21 @@
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: service-name-autoscaler
namespace: default
spec:
scaleTargetRef:
apiVersion: extensions/v1beta1
kind: Deployment
name: service-name
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: 100
- type: Pods
pods:
metricName: appinsights-metric-connector-name
targetAverageValue: 1

Просмотреть файл

@ -0,0 +1,42 @@
global:
registry_location: "off-cluster"
replicaCount: 1
image:
repository: acrrepo.azurecr.io/image-name
name: image-name
tag: 1
version: v1
pullPolicy: Always
secret: registrysecretname
service:
internalPort: 8021
type: ClusterIP
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 1001m
memory: 500m
env:
CACHE_CONNECTOR_GET_URI: ""
CACHE_CONNECTOR_UPSERT_URI: ""
APPINSIGHTS_INSTRUMENTATIONKEY: ""
DISABLE_CURRENT_REQUEST_METRIC: "False"
SERVICE_CLUSTER: ""
SERVICE_MODEL_NAME: ""
SERVICE_MODEL_FRAMEWORK: ""
SERVICE_MODEL_FRAMEOWRK_VERSION: ""
SERVICE_MODEL_VERSION: "1.0"
SERVICE_NAME: ""
SERVICE_VERSION: "1.0"
SERVICE_CONTAINER_VERSION: ""
SERVICE_CONTAINER_NAME: ""
DEBUG: "FALSE"
volumeMounts:
- name: nvidia
mountPath: /usr/local/nvidia
volumes:
- name: nvidia
hostPath:
path: /usr/local/nvidia

Просмотреть файл

Просмотреть файл

@ -0,0 +1,34 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "mdsd.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "mdsd-%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "privatekey.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "privatekey-%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

Просмотреть файл

@ -0,0 +1,55 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ .Values.image.name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: {{ .Values.image.name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["/startup.sh"]
ports:
- containerPort: {{ .Values.service.internalPort }}
name: http
resources:
{{ toYaml .Values.resources | indent 10 }}
env:
- name: CACHE_CONNECTOR_UPSERT_URI
value: {{ .Values.env.CACHE_CONNECTOR_UPSERT_URI | quote }}
- name: CACHE_CONNECTOR_GET_URI
value: {{ .Values.env.CACHE_CONNECTOR_GET_URI | quote }}
- name: APPINSIGHTS_INSTRUMENTATIONKEY
value: {{ .Values.env.APPINSIGHTS_INSTRUMENTATIONKEY | quote }}
- name: SERVICE_MODEL_NAME
value: {{ .Values.env.SERVICE_MODEL_NAME | quote }}
- name: SERVICE_MODEL_FRAMEOWRK_VERSION
value: {{ .Values.env.SERVICE_MODEL_FRAMEOWRK_VERSION | quote }}
- name: SERVICE_MODEL_VERSION
value: {{ .Values.env.SERVICE_MODEL_VERSION | quote }}
- name: SERVICE_NAME
value: {{ .Values.env.SERVICE_NAME | quote }}
- name: SERVICE_VERSION
value: {{ .Values.env.SERVICE_VERSION | quote }}
- name: SERVICE_CONTAINER_VERSION
value: {{ .Values.env.SERVICE_CONTAINER_VERSION | quote }}
- name: SERVICE_CONTAINER_NAME
value: {{ .Values.env.SERVICE_CONTAINER_NAME | quote }}
- name: SERVICE_CLUSTER
value: {{ .Values.env.SERVICE_CLUSTER | quote }}
- name: APPINSIGHTS_INSTRUMENTATIONKEY
value: {{ .Values.env.APPINSIGHTS_INSTRUMENTATIONKEY | quote }}
- name: NEXT_API_NAME_IN_PIPELINE
value: {{ .Values.env.NEXT_API_NAME_IN_PIPELINE | quote }}
volumeMounts:
{{ toYaml .Values.volumeMounts | indent 10 }}
volumes:
{{ toYaml .Values.volumes | indent 8 }}
imagePullSecrets:
- name: {{ .Values.image.secret }}

Просмотреть файл

@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.image.name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
app: {{ .Values.image.name }}
version: {{ .Values.image.version }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.internalPort }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector:
app: {{ .Values.image.name }}

Просмотреть файл

@ -0,0 +1,28 @@
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: service-name
spec:
hosts:
- "*"
gateways:
- istio-gateway-name
http:
- match:
- uri:
prefix: ""
route:
- destination:
host: service-name.default.svc.cluster.local
port:
number: 8021
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: dr-service-name
spec:
host: service-name.default.svc.cluster.local
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN

Просмотреть файл

@ -0,0 +1,12 @@
apiVersion: v1
description: My description.
name: service-name
version: 1.0.0
keywords:
- sync
home: "https://www.microsoft.com/en-us/aiforearth"
sources:
- "https://github.com/microsoft/AIforEarth-API-Platform"
maintainers:
- name: AI for Earth Engineering
- email: aiforearthteam@microsoft.com

Просмотреть файл

@ -0,0 +1,21 @@
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: service-name-autoscaler
namespace: default
spec:
scaleTargetRef:
apiVersion: extensions/v1beta1
kind: Deployment
name: service-name
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: 100
- type: Pods
pods:
metricName: appinsights-metric-connector-name
targetAverageValue: 1

Просмотреть файл

@ -0,0 +1,32 @@
global:
registry_location: "off-cluster"
replicaCount: 1
image:
repository: acrrepo.azurecr.io/image-name
name: image-name
tag: 1
version: v1
pullPolicy: Always
secret: registrysecretname
service:
internalPort: 8021
type: ClusterIP
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 1001m
memory: 500m
env:
APPINSIGHTS_INSTRUMENTATIONKEY: ""
SERVICE_CLUSTER: "my-aks-cluster"
SERVICE_MODEL_NAME: ""
SERVICE_MODEL_FRAMEWORK: ""
SERVICE_MODEL_FRAMEOWRK_VERSION: ""
SERVICE_MODEL_VERSION: "1.0"
SERVICE_NAME: ""
SERVICE_VERSION: "1.0"
SERVICE_CONTAINER_VERSION: ""
SERVICE_CONTAINER_NAME: ""
DEBUG: "FALSE"

Просмотреть файл

Просмотреть файл

@ -0,0 +1,34 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "mdsd.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "mdsd-%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "privatekey.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "privatekey-%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

Просмотреть файл

@ -0,0 +1,55 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ .Values.image.name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: {{ .Values.image.name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["/startup.sh"]
ports:
- containerPort: {{ .Values.service.internalPort }}
name: http
resources:
{{ toYaml .Values.resources | indent 10 }}
env:
- name: CACHE_CONNECTOR_UPSERT_URI
value: {{ .Values.env.CACHE_CONNECTOR_UPSERT_URI | quote }}
- name: CACHE_CONNECTOR_GET_URI
value: {{ .Values.env.CACHE_CONNECTOR_GET_URI | quote }}
- name: APPINSIGHTS_INSTRUMENTATIONKEY
value: {{ .Values.env.APPINSIGHTS_INSTRUMENTATIONKEY | quote }}
- name: SERVICE_MODEL_NAME
value: {{ .Values.env.SERVICE_MODEL_NAME | quote }}
- name: SERVICE_MODEL_FRAMEOWRK_VERSION
value: {{ .Values.env.SERVICE_MODEL_FRAMEOWRK_VERSION | quote }}
- name: SERVICE_MODEL_VERSION
value: {{ .Values.env.SERVICE_MODEL_VERSION | quote }}
- name: SERVICE_NAME
value: {{ .Values.env.SERVICE_NAME | quote }}
- name: SERVICE_VERSION
value: {{ .Values.env.SERVICE_VERSION | quote }}
- name: SERVICE_CONTAINER_VERSION
value: {{ .Values.env.SERVICE_CONTAINER_VERSION | quote }}
- name: SERVICE_CONTAINER_NAME
value: {{ .Values.env.SERVICE_CONTAINER_NAME | quote }}
- name: SERVICE_CLUSTER
value: {{ .Values.env.SERVICE_CLUSTER | quote }}
- name: APPINSIGHTS_INSTRUMENTATIONKEY
value: {{ .Values.env.APPINSIGHTS_INSTRUMENTATIONKEY | quote }}
- name: NEXT_API_NAME_IN_PIPELINE
value: {{ .Values.env.NEXT_API_NAME_IN_PIPELINE | quote }}
volumeMounts:
{{ toYaml .Values.volumeMounts | indent 10 }}
volumes:
{{ toYaml .Values.volumes | indent 8 }}
imagePullSecrets:
- name: {{ .Values.image.secret }}

Просмотреть файл

@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.image.name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
app: {{ .Values.image.name }}
version: {{ .Values.image.version }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.internalPort }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector:
app: {{ .Values.image.name }}

Просмотреть файл

@ -22,26 +22,14 @@ To publish a container to the AI for Earth API Platform, the API must be built u
## Production Preparation
### Create a production AI for Earth dockerfile for your API
For all of the following steps, please reference the camera-trap example, located in the [camera-trap directory](Projects/camera-trap/detection-sync.dockerfile).
1. In the [APIs/Projects](./Projects/) folder, you will notice separate folders for each project. Create a new folder for your project.
2. Create a new <api_name>.dockerfile and place it in your folder (pay close attention to the tech stack - Python or R; be sure to use the correct one).
3. Modify the FROM clause to point to your registry and <project_moniker> (where you pushed your image in the "Push to ACR" step). The version and tag arguments are required.
### Create a Helm chart, etc. for your API
For all of the following steps, please reference the camera-trap example, located in the [camera-trap directory](APIs/Charts/camera-trap/detection-sync).
1. In the charts/ folder, create a new folder for your project.
2. Copy and existing chart project/api folder and place it under your project folder.
3. Modify the [Chart.yaml](./Charts/camera-trap/detection-sync/Chart.yaml) by changing the description, name, and keywords fields. The name should be of the format <project_moniker>-<api_name>.
4. Modify the [autoscaler.yaml](./Charts/camera-trap/detection-sync/autoscaler.yaml) by changing both name fields. The name should be of the format <project_moniker>-<api_name>-autoscaler and <project_moniker>-<api_name>, respectfully. Modify the maxReplicas to the maximum number of instances of your service that should be run and modify the targetCPUUtilizationPercentage to indicate when a new instance should be created. The API Platform supports [auto-scaling on custom Application Insights metrics](https://github.com/Azure/azure-k8s-metrics-adapter). The camera-trap example uses one such metric that tracks the number of requests that are being processed.
5. Modify the [prod-values.yaml](./Charts/camera-trap/detection-sync/prod-values.yaml) by changing the image.repository to <your_registry>/<project_moniker>/<major_version>-ai4e-api-<api_name>, the name to <project_moniker>-<api_name>, a port that is not taken by ANY other chart by ANY other project API, the resources, and the env variables.
6. Modify the [routing.yml](./Charts/camera-trap/detection-sync/routing.yml). The VirtualService name should be of the format <project_moniker>-<api_name>. Modify the uri.prefix to indicate the incoming path to match, modify the destination.host to a name that reflects <project_moniker>-<api_name>.default.svc.cluster.local, destination.port to reflect the port that is to be used, and destination.subset to the version of the release. Change the DestinationRule to a unique name for your API, modify the host to the destination.host value from above, and esure that the name and label version match the path version of the release. To read more about Istio routing, please see the [docs](https://istio.io/docs/tasks/traffic-management/request-routing/).
## Building and Deploying a Production API
### Build Distributed-capable Image
The distributed image must be built from this (APIs) directory. The following illustrates how to build the camera-trap example.
The distributed image must be built from this (APIs) directory. The following illustrates how to build the species example.
```bash
docker build . -f Projects/camera-trap/detection-sync.dockerfile -t ai4eapibackendv2gpu3registry.azurecr.io/camera-trap/1.0-ai4e-api-detection-sync:4
docker build . -f Projects/species/species-sync.dockerfile -t registry_name.azurecr.io/species/1.0-ai4e-species-sync:1
```
Once complete, push the new image to your container registry:
@ -50,16 +38,24 @@ Once complete, push the new image to your container registry:
az acr login -n <acr_name>
# Push your image to your ACR.
docker push ai4eapibackendv2gpu3registry.azurecr.io/camera-trap/1.0-ai4e-api-detection-sync:4
docker push registry_name.azurecr.io/species/1.0-ai4e-species-sync:1
```
You now have a distributed-capable image stored in your container registry that is ready to be hosted on the API Platform.
### Create a Helm chart, etc. for your API
For all of the following steps, please reference the camera-trap example, located in the [camera-trap directory](APIs/Charts/camera-trap/detection-sync).
1. In the charts/ folder, create a new folder for your project.
2. Copy a template chart from the [Charts/templates](./Charts/templates) folder and place it under your project folder.
3. Modify the [Chart.yaml](./Charts/templates/async-gpu/Chart.yaml) by changing the description, name, and keywords fields. The name should be of the format <project_moniker>-<api_name>.
4. Modify the [autoscaler.yaml](./Charts/templates/async-gpu/autoscaler.yaml) by changing both name fields. The name should be of the format <project_moniker>-<api_name>-autoscaler and <project_moniker>-<api_name>, respectfully. Modify the maxReplicas to the maximum number of instances of your service that should be run and modify the targetCPUUtilizationPercentage to indicate when a new instance should be created. The API Platform supports [auto-scaling on custom Application Insights metrics](https://github.com/Azure/azure-k8s-metrics-adapter).
5. Modify the [prod-values.yaml](./Charts/templates/async-gpu/prod-values.yaml) by changing the image.repository to <your_registry>/<project_moniker>/<major_version>-ai4e-api-<api_name>, the name to <project_moniker>-<api_name>, a port that is not taken by ANY other chart by ANY other project API, the resources, and the env variables.
6. Modify the [routing.yml](./Charts/templates/routing.yml). The VirtualService name should be of the format <project_moniker>-<api_name>. Modify the uri.prefix to indicate the incoming path to match, modify the destination.host to a name that reflects <project_moniker>-<api_name>.default.svc.cluster.local, destination.port to reflect the port that is to be used, and destination.subset to the version of the release. Change the DestinationRule to a unique name for your API, modify the host to the destination.host value from above, and esure that the name and label version match the path version of the release. To read more about Istio routing, please see the [docs](https://istio.io/docs/tasks/traffic-management/request-routing/).
## Deploying a Production API
### Configure API Variables
Before deploying to the cluster, edit the chart's prod-values.yaml file. This contains all configuration values to be used by your service. The Azure Function URLs can be retrieved by issuing the following command:
```bash
func azure functionapp list-functions $FUNCTION_APP_NAME --show-keys
```
Before deploying to the cluster, edit the chart's prod-values.yaml file. This contains all configuration values to be used by your service. The Azure Function URLs can be retrieved by using the [async_retrieve_cache_connector_urls helper function](async_retrieve_cache_connector_urls.sh).
Azure Function URLs are mapped to variables according to the following table:
@ -69,17 +65,23 @@ Azure Function URLs are mapped to variables according to the following table:
| CACHE_CONNECTOR_GET_URI | cache-connector-get |
| CURRENT_PROCESSING_UPSERT_URI | CurrentProcessingUpsert |
### Deploy the API to Production
### Deploy the API Service to Production
```bash
# Deploy instance.
helm install --values ./Charts/camera-trap/detection-sync/prod-values.yaml --name camera-trap-detection-sync ./Charts/camera-trap/detection-sync
helm install --values ./Charts/service-name/prod-values.yaml --name service-name ./Charts/service-name
# Apply auto-scaling.
kubectl apply -f ./Charts/camera-trap/detection-sync/autoscaler.yaml
kubectl apply -f ./Charts/service-name/autoscaler.yaml
# Apply service routing.
kubectl apply -f ./Charts/camera-trap/detection-sync/routing.yaml
kubectl apply -f ./Charts/service-name//routing.yaml
# Apply optional Application Insights custom scaling metric.
kubectl apply -f ./Charts/camera-trap/detection-sync/appinsights-metric.yaml
kubectl apply -f ./Charts/service-name/appinsights-metric.yaml
```
### Add the API to API Management
The [create_sync_api_management_api.sh](./create_sync_api_management_api.sh) is used as an example of how to add sync APIs to your API Management instance, whereas the [create_async_api_management_api.sh](./create_async_api_management_api.sh) is used as an example of how to add async APIs. Copy the create_sync_api_management_api.sh file and use it as a template. To deploy to Azure, run the following:
```bash
bash ../APIManagement/create_sync_api_management_api.sh
```

Просмотреть файл

@ -0,0 +1,78 @@
#!/bin/bash
AZURE_SUBSCRIPTION_ID=""
INFRASTRUCTURE_RESOURCE_GROUP_NAME="" # Azure Resource Group
API_MANAGEMENT_NAME=""
API_PATH="landcover"
API_DISPLAY_NAME="AI for Earth Land Cover Mapping API"
API_DESCRIPTION="API for retrieving pre-computed classifications of aerial image pixels into natural and human-made terrain types."
if test -z "$INFRASTRUCTURE_RESOURCE_GROUP_NAME"
then
echo "setupenv.sh must be completed first."
exit 1
fi
az account set --subscription $AZURE_SUBSCRIPTION_ID
if [ $? -ne 0 ]
then
echo "Could not set subscription $AZURE_SUBSCRIPTION_ID."
exit $?
fi
# Deploy API Management
upsert_key=$(az rest --method post --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.Web/sites/$FUNCTION_APP_NAME/functions/CacheConnectorUpsert/listKeys?api-version=2018-11-01")
if [ $? -ne 0 ]
then
echo "Could not get the CacheConnectorUpsert Azure Functions key."
exit $?
fi
upsert_key=$(echo $upsert_key | jq '.default' | sed -e 's/^"//' -e 's/"$//')
upsert_fun_url="https://$CACHE_MANAGER_FUNCTION_APP_NAME.azurewebsites.net/api/CacheConnectorUpsert?code=$upsert_key"
ingress_ip=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
if [ $? -ne 0 ]
then
echo "Could not get the istio-ingressgateway ip."
exit $?
fi
# Create the API
api_body="{\"properties\": {\"displayName\": \"$API_DISPLAY_NAME\",\"path\": \"$API_PATH\",\"protocols\": [\"https\"]}}"
az rest --method put --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.ApiManagement/service/$API_MANAGEMENT_NAME/apis/$API_PATH?api-version=2019-01-01" --body $api_body
if [ $? -ne 0 ]
then
echo "Could not create the $API_DISPLAY_NAME API."
exit $?
fi
# Create the API policy
python3 -c "import APIManagement.api_management_customizer as customizer; customizer.customize_api_policy('$upsert_fun_url')"
az rest --method put --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.ApiManagement/service/$API_MANAGEMENT_NAME/apis/$API_PATH/policies/policy?api-version=2019-01-01" --body @customized_api_policy.json
if [ $? -ne 0 ]
then
echo "Could not create the $API_DISPLAY_NAME API POST operation's policy."
exit $?
fi
# Create the API POST operation
api_operation_body="{\"properties\": {\"displayName\": \"$API_OPERATION_DISPLAY_NAME\",\"method\": \"POST\",\"urlTemplate\": \"$API_OPERATION_URL\"}}"
az rest --method put --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.ApiManagement/service/$API_MANAGEMENT_NAME/apis/$API_PATH/operations/$API_OPERATION_URL?api-version=2019-01-01" --body $api_operation_body
if [ $? -ne 0 ]
then
echo "Could not create the $API_DISPLAY_NAME API operation."
exit $?
fi
# Create the API operation's policy
api_backend_policy="<policies><inbound><set-backend-service base-url=https://\"$ingress_ip\" /><rewrite-uri template=\"$BACKEND_API_URL\" /><base /></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>"
az rest --method put --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.ApiManagement/service/$API_MANAGEMENT_NAME/apis/$API_PATH/operations/$API_OPERATION_URL/policies/policy?api-version=2019-01-01" --body $api_backend_policy
if [ $? -ne 0 ]
then
echo "Could not create the $API_DISPLAY_NAME API POST operation's policy."
exit $?
fi

5
APIs/helpers/README.md Normal file
Просмотреть файл

@ -0,0 +1,5 @@
## assign_storage_auth_to_aks.sh
Grants "Storage Blob Data Contributor" access to AKS via AAD. This gives services in the AKS cluster access to the provided storage account through Azure Active Directory.
## async_retrieve_cache_connector_urls.sh
For async APIs that use the task management system, Cache Connector URLs must be provided. This script retrieves those URLs.

Просмотреть файл

@ -0,0 +1,17 @@
#!/bin/bash
AZURE_SUBSCRIPTION_ID=""
STORAGE_ACCOUNT_RESOURCE_GROUP="-supporting-services-rg"
STORAGE_ACCOUNT_NAME=""
AKS_AAD_APPLICATION_ID="" # AAD application id for AKS
# Get storage account resource id
resource_id=$(az storage account show --resource-group $STORAGE_ACCOUNT_RESOURCE_GROUP --name $STORAGE_ACCOUNT_NAME --subscription $AZURE_SUBSCRIPTION_ID --query "id" --output tsv)
az role assignment create --assignee $AKS_AAD_APPLICATION_ID --role "Storage Blob Data Contributor" --scope $resource_id
if [ $? -ne 0 ]
then
echo "Could not create an ACR role in the AKS cluster."
echo "customize_aks.sh failed"
exit $?
fi

Просмотреть файл

@ -0,0 +1,28 @@
#!/bin/bash
AZURE_SUBSCRIPTION_ID=""
INFRASTRUCTURE_RESOURCE_GROUP_NAME="-api-backend-rg"
FUNCTION_APP_NAME="-api-backend-cache-app"
CACHE_MANAGER_FUNCTION_APP_NAME="-api-backend-cache-app"
get_key=$(az rest --method post --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.Web/sites/$FUNCTION_APP_NAME/functions/CacheConnectorGet/listKeys?api-version=2018-11-01")
if [ $? -ne 0 ]
then
echo "Could not get the CacheConnectorGet Azure Functions key."
exit $?
fi
get_key=$(echo $get_key | jq '.default' | sed -e 's/^"//' -e 's/"$//')
get_fun_url="https://$CACHE_MANAGER_FUNCTION_APP_NAME.azurewebsites.net/api/CacheConnectorGet?code=$get_key"
echo "CACHE_CONNECTOR_GET_URI: \"$get_fun_url\""
upsert_key=$(az rest --method post --uri "https://management.azure.com/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$INFRASTRUCTURE_RESOURCE_GROUP_NAME/providers/Microsoft.Web/sites/$FUNCTION_APP_NAME/functions/CacheConnectorUpsert/listKeys?api-version=2018-11-01")
if [ $? -ne 0 ]
then
echo "Could not get the CacheConnectorUpsert Azure Functions key."
exit $?
fi
upsert_key=$(echo $upsert_key | jq '.default' | sed -e 's/^"//' -e 's/"$//')
upsert_fun_url="https://$CACHE_MANAGER_FUNCTION_APP_NAME.azurewebsites.net/api/CacheConnectorUpsert?code=$upsert_key"
echo "CACHE_CONNECTOR_UPSERT_URI: \"$upsert_fun_url\""

Просмотреть файл

@ -11,7 +11,62 @@ There are three types of files required for deployment:
- Master script that runs each deployment script in sequence.
- Individual component/feature deployment scripts.
### Component/feature deployment scripts
## Contents
1. [Installation Process](#Installation-Process)
2. [Component/feature deployment scripts](#Component/feature-deployment-scripts)
## Installation Process
To quickly get up and running, follow these steps.
1. Edit the [setup_env.sh](setup_env.sh) file. This is where you configure the deployment.
2. From the top-level directory, run the following script. Note that connection issues and service creation latencies may result in errors. The scripts are designed such that you can rerun and services will not be recreated. There are some commented out resolutions in the scripts that may be of value.
```bash
bash InfrastructureDeployment/deploy_infrastructure.sh
```
3. [Secure the Istio Gateway](https://istio.io/docs/tasks/traffic-management/ingress/secure-ingress-mount/#configure-a-tls-ingress-gateway-with-a-file-mount-based-approach). This is optional, but should be completed for production instances. All of these steps are documented at the above link, but are listed here for brevity. To secure the gateway, please follow these steps:
1. Get the ingress IP and ports of the Istio gateway:
```bash
kubectl get svc istio-ingressgateway -n istio-system
```
2. Generate server certificate and private key:
```bash
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
```
3. Create a certificate and a private key (replace httpbin.example.com and organization):
```bash
openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt
```
4. Create a Kubernetes secret to hold the servers certificate and private key (the secret must be named istio-ingressgateway-certs in the istio-system namespace):
```bash
kubectl create -n istio-system secret tls istio-ingressgateway-certs --key httpbin.example.com.key --cert httpbin.example.com.crt
```
5. Modify the default Istio gateway to use the HTTPS protocol (replace httpbin.example.com):
```bash
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ai4e-gateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
privateKey: /etc/istio/ingressgateway-certs/tls.key
hosts:
- "httpbin.example.com"
EOF
```
## Component/feature deployment scripts
Provided that prerequisites have been deployed, component deployment scripts may be run outside of the [deploy_infrastructure.sh](./deploy_infrastructure.sh) script. The scripts are typically executed in the following order.
### [deploy_prerequisites.sh](./deploy_prerequisites.sh)

Просмотреть файл

@ -6,6 +6,7 @@ echo "Creating the Azure Function Apps."
if "$DEPLOY_CACHE_MANAGER_FUNCTION_APP" = "true"
then
echo "Creating the cache manager Azure Function App."
echo "az functionapp create --name $CACHE_MANAGER_FUNCTION_APP_NAME --storage-account $FUNCTION_STORAGE_NAME --resource-group $INFRASTRUCTURE_RESOURCE_GROUP_NAME --plan $FUNCTION_APP_NAME-plan --deployment-container-image-name $CACHE_MANAGER_IMAGE --app-insights $APP_INSIGHTS_RESOURCE_NAME --docker-registry-server-user $FUNCTION_IMAGE_DOCKER_USER_NAME --docker-registry-server-password $FUNCTION_IMAGE_DOCKER_USER_PASSWORD"
az functionapp create --name $CACHE_MANAGER_FUNCTION_APP_NAME --storage-account $FUNCTION_STORAGE_NAME --resource-group $INFRASTRUCTURE_RESOURCE_GROUP_NAME --plan $FUNCTION_APP_NAME-plan --deployment-container-image-name $CACHE_MANAGER_IMAGE --app-insights $APP_INSIGHTS_RESOURCE_NAME --docker-registry-server-user $FUNCTION_IMAGE_DOCKER_USER_NAME --docker-registry-server-password $FUNCTION_IMAGE_DOCKER_USER_PASSWORD
if [ $? -ne 0 ]
then

Просмотреть файл

@ -1,67 +1,64 @@
#!/bin/bash
AZURE_SUBSCRIPTION_ID=""
INFRASTRUCTURE_RESOURCE_GROUP_NAME="-api-backend-rg"
FUNCTION_APP_NAME="-api-backend-cache-app"
CACHE_MANAGER_FUNCTION_APP_NAME="-api-backend-cache-app"
FUNCTION_IMAGE_DOCKER_USER_NAME=""
FUNCTION_IMAGE_DOCKER_USER_PASSWORD=""
AZURE_SUBSCRIPTION_ID=""
DEPLOYMENT_PREFIX="ai4e-api-backend-master-test-2"
# Storage account names must be fewer than 24 characters.
FUNCTION_STORAGE_NAME="" # Azure Function Storage
CONTAINER_REGISTRY_NAME="" # ACR name
CONTAINER_REGISTRY_RESOURCE_GROUP=""
# Container registry that contains images to be used as services in Kubernetes
SERVICE_CONTAINER_REGISTRY_NAME="containerregistry"
SERVICE_CONTAINER_REGISTRY_RESOURCE_GROUP="-supporting-services-rg"
INSTALL_ISTIO="true"
INSTALL_CUSTOM_METRICS_ADAPTER="false"
INFRASTRUCTURE_RESOURCE_GROUP_NAME="$DEPLOYMENT_PREFIX-rg" # Azure Resource Group
INFRASTRUCTURE_LOCATION="eastus"
APP_INSIGHTS_RESOURCE_NAME="-api-backend-app-insights" # Application Services name
APP_INSIGHTS_RESOURCE_NAME="$DEPLOYMENT_PREFIX-app-insights" # Application Services name
CREATE_CONTAINER_REGISTRY="false"
# Container registry required for Azure Functions
CREATE_CONTAINER_REGISTRY="true"
CONTAINER_REGISTRY_NAME="testregistry" # ACR name
CONTAINER_REGISTRY_RESOURCE_GROUP="-registry-rg"
AKS_RESOURCE_GROUP_NAME="$DEPLOYMENT_PREFIX-aks-rg" # Azure Resource Group Name
AKS_CLUSTER_NAME="$DEPLOYMENT_PREFIX" # AKS Cluster Name
KUBERNETES_VERSION="1.15.10" # Kubernetes version to deploy
DNS_NAME_PREFIX="$DEPLOYMENT_PREFIX" # Custom DNS prefix for your cluster
AKS_RESOURCE_GROUP_NAME="-api-backend-aks-rg" # Azure Resource Group Name
AKS_CLUSTER_NAME="-api-backend" # AKS Cluster Name
KUBERNETES_VERSION="1.14.8" # Kubernetes version to deploy
DNS_NAME_PREFIX="-api-backend" # Custom DNS prefix for your cluster
CLUSTER_GPU_NODE_COUNT=1 # Number of GPU nodes to be used for API hosting
CLUSTER_GPU_NODE_COUNT=2 # Number of GPU nodes to be used for API hosting
CLUSTER_GPU_NODE_VM_SKU="Standard_NC6" # Azure GPU SKU representing the type of VM to use for the nodes
GPU_SCALE_MIN_NODE_COUNT=1 # The minimum number of GPU nodes to keep available
GPU_SCALE_MAX_NODE_COUNT=3 # The most number of GPU nodes to auto-scale
GPU_SCALE_MIN_NODE_COUNT=2 # The minimum number of GPU nodes to keep available
GPU_SCALE_MAX_NODE_COUNT=2 # The most number of GPU nodes to auto-scale
CLUSTER_CPU_NODE_COUNT=2 # Number of CPU nodes to be used for API hosting
CLUSTER_CPU_NODE_VM_SKU="Standard_DS2_v2" # Azure CPU SKU representing the type of VM to use for the nodes
CPU_SCALE_MIN_NODE_COUNT=1 # The minimum number of CPU nodes to keep available
CPU_SCALE_MAX_NODE_COUNT=3 # The most number of CPU nodes to auto-scale
CPU_SCALE_MIN_NODE_COUNT=2 # The minimum number of CPU nodes to keep available
CPU_SCALE_MAX_NODE_COUNT=2 # The most number of CPU nodes to auto-scale
ISTIO_VERSION="1.4.5" # The version of Istio to install
AZURE_CACHE_NAME="-api-backend-cache" # Azure Cache Name
AZURE_CACHE_NAME="$DEPLOYMENT_PREFIX-cache" # Azure Cache Name
FUNCTION_STORAGE_NAME="testfuncstorage" # Azure Function Storage
FUNCTION_APP_NAME="$DEPLOYMENT_PREFIX-cache-app" # Azure Function App Name
DEPLOY_CACHE_MANAGER_FUNCTION_APP="true"
DEPLOY_BACKEND_WEBHOOK_FUNCTION_APP="true"
DEPLOY_REQUEST_REPORTER_FUNCTION_APP="true"
DEPLOY_TASK_PROCESS_LOGGER_FUNCTION_APP="true"
BACKEND_WEBHOOK_FUNCTION_APP_NAME="-api-backend-webhook-app"
REQUEST_REPORTER_FUNCTION_APP_NAME="-api-backend-requests-app"
TASK_PROCESS_LOGGER_FUNCTION_APP_NAME="-api-backend-processes-app"
CACHE_MANAGER_FUNCTION_APP_NAME="$DEPLOYMENT_PREFIX-cache-app"
BACKEND_WEBHOOK_FUNCTION_APP_NAME="$DEPLOYMENT_PREFIX-webhook-app"
REQUEST_REPORTER_FUNCTION_APP_NAME="$DEPLOYMENT_PREFIX-requests-app"
TASK_PROCESS_LOGGER_FUNCTION_APP_NAME="$DEPLOYMENT_PREFIX-processes-app"
# DO NOT CHANGE
CACHE_MANAGER_IMAGE="containerregistry.azurecr.io/func-cache-manager:1.0"
BACKEND_WEBHOOK_IMAGE="containerregistry.azurecr.io/func-backend-webhook:1.0"
REQUEST_REPORTER_IMAGE="containerregistry.azurecr.io/func-request-reporter:1.0"
TASK_PROCESS_LOGGER_IMAGE="containerregistry.azurecr.io/func-task-process-logger:1.0"
FUNCTION_IMAGE_DOCKER_USER_NAME="containerregistry"
FUNCTION_IMAGE_DOCKER_USER_PASSWORD="Dv/v0gPrnqIwNkEU5JSwZKPZu9lqgrwV"
CACHE_MANAGER_IMAGE="mcr.microsoft.com/aiforearth/func-cache-manager:1.0"
BACKEND_WEBHOOK_IMAGE="mcr.microsoft.com/aiforearth/func-backend-webhook:1.0"
REQUEST_REPORTER_IMAGE="mcr.microsoft.com/aiforearth/func-request-reporter:1.0"
TASK_PROCESS_LOGGER_IMAGE="mcr.microsoft.com/aiforearth/func-task-process-logger:1.0"
EVENT_GRID_TOPIC_NAME="-api-backend-grid-topic" # Event Grid topic name
EVENT_GRID_TOPIC_NAME="$DEPLOYMENT_PREFIX-grid-topic" # Event Grid topic name
SERVICE_PRINCIPAL_METRIC_ADAPTER_NAME="-metric-adapter-sp"
SERVICE_PRINCIPAL_METRIC_ADAPTER_NAME="$DEPLOYMENT_PREFIX-metric-adapter-sp"
API_MANAGEMENT_NAME="-api-backend-api-mgmt"
API_MANAGEMENT_ORGANIZATION_NAME=""
API_MANAGEMENT_ADMIN_EMAIL=""
API_MANAGEMENT_NAME="$DEPLOYMENT_PREFIX-api-mgmt"
API_MANAGEMENT_ORGANIZATION_NAME="AI for Earth"
API_MANAGEMENT_ADMIN_EMAIL="test@microsoft.com"
API_MANAGEMENT_REGION="East US"
API_MANAGEMENT_SKU="Consumption"

Просмотреть файл

@ -6,6 +6,7 @@ After developing an algorithm or machine learning model, researchers face the pr
2. [AI for Earth API Platform](#AI-for-Earth-API-Platform) - Overview and features.
3. [Platform Elements and Costs](#Platform-Elements-and-Costs)
4. [Platform Deployment](#Platform-Deployment)
5. [API Deployment](#API-Deployment)
6. [Platform Alternatives](#Alternatives)
7. [Contributions](#Contributing)
@ -136,6 +137,9 @@ API Management can be [configured with a consumption plan or a dedicated plan](h
## Platform Deployment
The platform may be deployed using scripts provided in the [InfrastructureDeployment](./InfrastructureDeployment) directory. Please follow the [instructions](./InfrastructureDeployment/README.md) in that directory to deploy the API Platform to Azure.
## API Deployment
Your API can be deployed to the API Platform by following the [API instructions](./APIs/README.md).
## Alternatives
Development on the AI for Earth API Platform began in the Spring of 2018. Recently, there have been a number of improvements to the [Azure Machine Learning Service](https://docs.microsoft.com/en-us/azure/machine-learning/service/how-to-deploy-azure-kubernetes-service) and [MLOps](https://docs.microsoft.com/en-us/azure/machine-learning/service/concept-model-management-and-deployment) that have greatly bridged the inference service gaps that we had initially identified.