In this document:
- Serverless Microservices reference architecture
- Getting Started
- Resources
- Provision
- Manual via the Portal
- Create the Resource Group
- Create the Azure Cosmos DB assets
- Create the Storage account
- Create the Azure Function Apps
- Create the Web App Service Plan
- Create the Web App
- Create the Azure SQL Database assets
- Create the Event Grid Topic
- Create the Application Insights resource
- Create the API Management Service
- Create the SignalR Service
- Create Azure Key Vault
- Create the Azure AD B2C tenant
- Add Azure AD Graph API
- Configure Azure AD B2C tenant
- Create a sign-up or sign-in policy
- Cake Provision
- Deploy from Bicep
- Manual via the Portal
- Setup
- Add secrets to Key Vault
- Function App Application Settings
- Configure your Function Apps to connect to Key Vault
- Build the solution
- Deployment
- Seeding
- Containers
In your local development environment you will need latest versions of:
- Visual Studio or VS Code
- git
func
CLIaz
CLI- Powershell
- Nodejs & npm
Deploy Azure resources:
copy bicep/parameters.json bicep/parameters.local.json
# Change params in @bicep/parameters.local.json to suit
az group create -n serverless-microservices-dev -l westus2
az deployment group create -g serverless-microservices-dev -f bicep/main.bicep -p @bicep/parameters.local.json
Create local settings:
cd dotnet
# create copies of the Functions settings example files
copy ServerlessMicroservices.FunctionApp.Drivers/local.settings.example.json ServerlessMicroservices.FunctionApp.Drivers/local.settings.json
copy ServerlessMicroservices.FunctionApp.Orchestrators/local.settings.example.json ServerlessMicroservices.FunctionApp.Orchestrators/local.settings.json
copy ServerlessMicroservices.FunctionApp.Passengers/local.settings.example.json ServerlessMicroservices.FunctionApp.Passengers/local.settings.json
copy ServerlessMicroservices.FunctionApp.Trips/local.settings.example.json ServerlessMicroservices.FunctionApp.Trips/local.settings.json
# Now update local settings with your environment's values
cd ../nodejs
# create a copy of the Nodejs settings example file
copy serverless-microservices-functionapp-triparchiver/local.settings.example.json serverless-microservices-functionapp-triparchiver/local.settings.json
# Now update local settings with your environment's values
Build and run local:
cd scripts
./run-local.ps1
Run integration test:
./test-local.ps1
The following is a summary of all Azure resources required to deploy the solution:
Prod Resource Name | Dev Resource Name | Type | Provision Mode |
---|---|---|---|
serverless-microservices | serverless-microservices-dev | Resource Group | Auto |
rideshare | rideshare | Cosmos DB Account | Auto |
Main | Main | Cosmos DB Container | Auto |
Archive | Archive | Cosmos DB Container | Auto |
ridesharefunctionstore | ridesharefunctiondev | Storage Account | Auto |
RideShareFunctionAppPlan | RideShareFunctionAppPlan | Consumption Plan | Auto |
RideShareDriversFunctionApp | RideShareDriversFunctionAppDev | Function App | Auto |
RideShareTripsFunctionApp | RideShareTripsFunctionAppDev | Function App | Auto |
RideSharePassengersFunctionApp | RideSharePassengersFunctionAppDev | Function App | Auto |
RideShareOrchestratorsFunctionApp | RideShareOrchestratorsFunctionAppDev | Function App | Auto |
RideShareTripArchiverFunctionApp | RideShareTripArchiverFunctionAppDev | Function App | Auto |
RideShareAppServicePlan | RideShareAppServicePlanDev | Web App Service Plan | Auto |
RelecloudRideshare | RelecloudRideshareDev | Web App Service | Auto |
rideshare-db | rideshare-db-dev | SQL Database Server | Auto |
RideShare | RideShare | SQL Database | Auto |
TripFact | TripFact | SQL Database Table | Manual |
RideShareTripExternalizations | RideShareTripExternalizationsDev | Event Grid Topic | Manual |
rideshare | rideshare-dev | Application Insights | Manual |
ProcessTripExternalization | ProcessTripExternalizationDev | Logic App | Manual |
rideshare | N/A | API Management Service | Manual |
rideshare | rideshare-dev | SignalR Service | Manual |
RideshareVault | RideshareVaultDev | Azure Key Vault | Manual |
relecloudrideshare.onmicrosoft.com | N/A | B2C Tenant | Manual |
✳️ Please note that, in some cases, the resource names must be unique globally. We suggest you append an identifier to the above resource names so they become unique i.e. ridesharefunctionstore-xyzw
, rideshare-xyzw
, etc.
✳️ Please note that, if you are planning to use Cake
to provision or deploy, you must adjust the cake/paths.cake
file to match your resource names. The public static class Resources
class defines the resource names.
There are 4 ways to provision the required resources:
- Manual via the Portal
- ARM Template
- Cake
- Bicep (recommended)
⚠️ Manual provisioning docs are obsolete will be deprecated soon. Please use Bicep provisioning instead.
Log in to the Azure portal.
-
Type Resource into the Search box at the top of the
All Services
page, then select Resource Groups section. -
Click the Add button to create a new resource group.
-
Complete the resource group creation form with the following:
- Name: Enter a unique value for the resource group i.e.
serverless-microservices
. - Subscription: Select your Azure subscription.
- Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Name: Enter a unique value for the resource group i.e.
-
Type Cosmos into the Search box at the top of the
All Services
page, then select Azure Cosmos DB section. -
Click the Add button to create a new Cosmos DB Account.
-
Complete the resource group creation form with the following:
- Subscription: Select your Azure subscription.
- Resource Group: Select the Resource Group you created above, such as
serverless-microservices
. - Account Name: Enter a unique ID for the Cosmos DB Account, such as
ridesharedata
. - API: Select
Core (SQL)
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Geo-Redundancy: Disable.
- Multi-region Writes: Disable.
-
Select Review + Create, then select Create on the review screen.
Please note that this process of creating a Cosmos DB Account can take between 5-10 minutes.
-
Once Cosmos Database is online, open it and select Data Explorer on the left-hand menu.
-
Select New Container on the toolbar. In the Add Container form that appears, enter the following:
- Database ID: Select Create new and enter
RideShare
. - Container Id: Enter
Main
. - Partition key: Enter
/code
. - Throughput: Select 400.
- Database ID: Select Create new and enter
-
Repeat step 4 -> 2 for a new container called
Archiver
-
Take note of the DB Account keys:
-
Type Storage into the Search box at the top of the
All Services
page, then select Storage accounts section. -
Click the Add button to create a new Storage Account.
-
Complete the storage creation form with the following:
- Name: Enter a unique name for the Storage Account i.e.
ridesharefuncstore
. - Deployment Model: Select
Resource Manager
. - Account Kind: Select
Storage V2
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
.
- Name: Enter a unique name for the Storage Account i.e.
-
Take note of the DB Account keys:
In this step, you will be creating six new Azure Function Apps in the Azure portal. There are many ways this can be accomplished, such as publishing from Visual Studio, Visual Studio Code, the Azure CLI, Azure Cloud Shell, an Azure Resource Manager (ARM) template, and through the Azure portal.
Each of these Function Apps act as a hosting platform for one or more functions. In our solution, they double as microservices with each function serving as an endpoint or method. Having functions distributed amongst multiple function apps enables isolation, providing physical boundaries between the microservices, as well as independent release schedules, administration, and scaling.
-
Log in to the Azure portal.
-
Type Function App into the Search box at the top of the page, then select Function App within the Marketplace section.
-
Complete the function app creation form with the following:
- App name: Enter a unique value for the Drivers function app.
- Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - OS: Select Windows.
- Hosting Plan: Select Consumption Plan.
- Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Runtime Stack: Select .NET Core.
- Storage: Select Create new and supply a unique name. You will use this storage account for the remaining function apps.
- Application Insights: Set to Disabled. We will create an Application Insights instance later that will be associated with all of the Function Apps and other services.
-
Repeat the steps above to create the Trips function app.
- Enter a unique value for the App name, ensuring it has the word Trips within the name so you can easily identify it.
- Make sure you enter the same remaining settings and select the storage account you created in the previous step.
-
Repeat the steps above to create the Orchestrators function app.
-
Repeat the steps above to create the Passengers function app.
-
Repeat the steps above to create the TripArchiver function app.
- Important: Select Node.js for the Runtime Stack, since this Function App will use Node.js.
-
Type App Service into the Search box at the top of the
All Services
page, then select App Service Plans section. -
Click the Add button to create a new app service plan.
-
Complete the app service plan creation form with the following:
- App Service Plan: Enter a unique value for the App Service Plan i.e.
RideShareAppServicePlan
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Operating system: Select
Windows
- Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Pricing Tier: Select
Free
.
- App Service Plan: Enter a unique value for the App Service Plan i.e.
-
Type App Service into the Search box at the top of the
All Services
page, then select App Services section. -
Click the Add button to create a new app service and select
Web App
from the marketplace. ClickCreate
. -
Complete the app service creation form with the following:
- App Name: Enter a unique value for the App Name i.e.
RelecloudRideShare
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Operating system: Select
Windows
- App Service PLan: Select the pan you created in the previous step.
- Application Insights: Select
Off
.
- App Name: Enter a unique value for the App Name i.e.
-
Type SQL into the Search box at the top of the
All Services
page, then select SQL Database section. -
Click the Add button to create a new SQL Database.
-
Complete the SQL Database creation form with the following:
- Name: Enter a unique value for the Database i.e.
RideShare
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Source: Select
Blank Database
. - Server: Select and Create a new server.
- Elastic Pool: Select
Not Now
. - Pricing Tier: Will be filled in automaticlaly once you complete the server creation i.e
10 DTUs, 250 GB
- Collation: Select
SQL_Latin_1_General_CP1_CI_AS
.
- Name: Enter a unique value for the Database i.e.
-
Complete the SQL Database Server creation form with the following:
- Name: Enter a unique value for the SQL Database Server i.e.
rideshare-db
. - Server admin login: Select your login.
- Password: select your password.
- Confirm password: Re-type your password.
- Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Allow Azure services to access server: Select
Checked
.
- Name: Enter a unique value for the SQL Database Server i.e.
-
Take note of the newly-created database connection string:
-
Type Event Grid into the Search box at the top of the
All Services
page, then select Event Grid Topic section. -
Click the Add button to create a new Event Grid Topic.
-
Complete the event grid topic creation form with the following:
- Name: Enter a unique value for the Event Grid Topic i.e.
RideShareExternalizations
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Event Schema: Select
Event Grid Schema
.
- Name: Enter a unique value for the Event Grid Topic i.e.
-
Take note of the newly-created topic key:
-
Take note of the newly-created topic endpoint URL:
-
Type Application Insights into the Search box at the top of the
All Services
page, then select Application Insights section. -
Click the Add button to create a new Application Insights resource.
-
Complete the application insights creation form with the following:
- Name: Enter a unique value for the application Insights i.e.
rideshare
. - Application Type: Select
General
. This is required by Function Apps. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Name: Enter a unique value for the application Insights i.e.
-
Take note of the newly-created resource instrumentation key:
-
Type API Management into the Search box at the top of the
All Services
page, then select API Management section. -
Click the Add button to create a new API Management service.
-
Complete the API Management service creation form with the following:
- Name: Enter a unique value for the APIM Service i.e.
rideshare
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Organization name: Type in your organization name.
- Administrator email: Type in an admin email.
- Pricing tier: Select
Developer (No SLA)
.
- Name: Enter a unique value for the APIM Service i.e.
-
Click Create a resource and type SignalR into the Search box, then select SignalR Service section.
-
Click the Create button to create a new SignalR service.
-
Complete the SignalR service creation form with the following:
- Resource Name: Enter a unique value for the SignalR Service i.e.
rideshare
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Pricing tier: Select
Free
. - ServiceMode: Select
Serverless
.
- Resource Name: Enter a unique value for the SignalR Service i.e.
-
Take note of the newly-created resource connection string:
Azure Key Vault is used to securely store all secrets, such as database connection strings and keys. It is accessible by all Function Apps, which helps prevent storing duplicate values.
-
Click Create a resource and type Key Vault into the Search box, then select Key Vault from the search results.
-
Click the Create button to create a new Key Vault.
-
Complete the Key Vault service creation form with the following:
- Resource Name: Enter a unique value for Key Vault, such as
RideshareVault
. - Subscription: Select your Azure subscription.
- Resource Group: Select the resource group to which you have added your other services, such as
serverless-microservices
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Pricing tier: Select
Standard
. - Access policies: Leave as default.
- Virtual Network Access: Leave as default (all networks can access).
- Resource Name: Enter a unique value for Key Vault, such as
The Azure Active Directory B2C tenant is used to store customer/passenger accounts and information, such as their full name, address, etc.
-
Sign in to the Azure portal.
-
Make sure that you are using the directory that contains your subscription by clicking the Directory and subscription filter in the top menu and choosing the directory that contains it. This is a different directory than the one that will contain your Azure AD B2C tenant.
-
Choose Create a resource in the top-left corner of the Azure portal.
-
Search for and select Active Directory B2C, and then click Create.
-
Choose Create a new Azure AD B2C Tenant, enter an organization name and initial domain name, which is used in the tenant name, select the country (it can't be changed later), and then click Create.
In this example the tenant name is contoso0926Tenant.onmicrosoft.com
-
On the Create new B2C Tenant or Link to an exiting Tenant page, choose Link an existing Azure AD B2C Tenant to my Azure subscription, select the tenant that you created, select your subscription, click Create new and enter a name for the resource group that will contain the tenant, select the location, and then click Create.
-
To start using your new tenant, make sure you are using the directory that contains your Azure AD B2C tenant by clicking the Directory and subscription filter in the top menu and choosing the directory that contains it.
-
Once you have switched to your new tenant directory, select Azure Active Directory from the left-hand menu.
-
Select Users from the Azure AD blade.
-
Select Password reset, then select Properties.
-
Select All underneath the "Self service password reset enabled" setting. Select Save.
-
Close the Password reset blade to go back to the Users blade.
-
Select User settings. Select Yes underneath "App registrations", and No underneath "Administration Portal". Select Save.
Adding the Azure AD Graph API to the new Azure AD B2C tenant will allow you to query user data from the Passengers Azure Function App.
-
Make sure you are still switched to your new Azure AD B2C tenant directory. Select Azure Active Directory on the left-hand menu, if not already selected.
-
In the left-hand navigation pane, select App registrations, and select New application registration.
-
Follow the prompts and create a new application.
-
Enter a Name, such as "Relecloud Rideshare Graph API".
-
Select Web App / API as the Application Type.
-
Provide any Sign-on URL (e.g. https://foo) as it's not relevant for this example.
-
-
The application will now show up in the list of applications, click on it to obtain the Application ID (also known as Client ID). Copy it as you'll need it in a later section. This will be the value for the
GraphClientId
App Setting for your Passengers Function App. -
In the Settings menu, click Keys.
-
In the Passwords section, enter the key description and select a duration, and then click Save. Copy the key value (also known as Client Secret) for use in a later section. This will be the value for the
GraphClientSecret
App Setting for your Passengers Function App. -
In the Settings menu, click on Required permissions.
-
In the Required permissions menu, click on Windows Azure Active Directory.
-
In the Enable Access menu, select the Read and write directory data permission from Application Permissions and click Save.
-
Finally, back in the Required permissions menu, click on the Grant Permissions button.
Your new Azure AD B2C tenant must be configured before it can be used from the website. The Reply URLs you add will allow the website to successfully route the users to the login/logout forms.
-
Switch back to your Azure subscription tenant that contains the resources you have created.
-
Navigate to your
serverless-microservices
Resource Group, or the one you created at the beginning of this document. -
Find and select the B2C Tenant you created.
-
Copy the Tenant ID value located within the Essentials section of the Overview blade. Save the value for later. This will be the value for the
GraphTenantId
App Setting for your Passengers Function App. -
Select the Azure AD B2C Settings tile.
-
Select Applications in the left-hand navigation menu, then select Add.
-
In the New application form, provide the following:
-
Name: rideshare-site
-
Web App / Web API: select Yes
-
Allow implicit flow: select Yes
-
Native client: select No
-
App ID URI: api
-
Enter the following Reply URL values (replace YOUR-WEB-APP with the name of the web app you created):
-
-
Select Create.
-
After the new application is created, open it and copy the Application ID. Save the value for later. This will be the value for the
ApiApplicationId
App Setting for several of your Function Apps. This value will also be used as the value for thewindow.authClientId
setting in thesettings.js
file within the website project. -
With the application still open, select Published scopes on the left-hand navigation menu.
-
Add two new scopes as defined below:
Scope Description rideshare Rideshare API user_impersonation Access this app on behalf of the signed-in user -
Select Save. After saving is complete, you will see a Full Scope Value next to each scope you added.
-
Copy the Full Scope Value for the new rideshare scope you created. Save the value for later. This will be the value for the
window.authScopes
setting in thesettings.js
file within the website project. -
Select API access from the left-hand navigation menu. You should have, at minimum, the published rideshare-site scope listed here. If not, perform the following:
-
Select Add.
-
Under Select API, select rideshare-site.
-
Under Select Scopes, select all.
-
Select OK.
-
Close the rideshare-site API access blade to navigate back to the Azure AD B2C - Applications blade.
You must create a policy for the sign-up/in user workflow. Without this, users will not be able to sign up for an account or sign in.
-
While still within the Azure AD B2C portal page, select Sign-up or sign-in policies and click Add.
To configure your policy, use the following settings:
Setting Suggested value Description Name default-signin Enter a Name for the policy. The policy name is prefixed with b2c_1_. You use the full policy name b2c_1_default-signin in the application code. Record this name for later. Identity provider Email signup The identity provider used to uniquely identify the user. Sign up attributes City, Country/Region, Display Name, Email Address, Given Name, Postal Code, State/Province, Street Address, and Surname Select attributes to be collected from the user during signup. Application claims City, Country/Region, Display Name, Email Address, Given Name, Postal Code, State/Province, Street Address, Surname, User is new, and User's Object ID Select claims you want to be included in the access token. -
Click Create to create your policy.
-
After your policy has been created, select Overview on the left-hand navigation menu for Azure AD B2C.
-
Make note of the Domain name value. Use this to construct the B2C Authority URL. The URL is structured as follows:
https://DOMAIN-NAME.b2clogin.com/tfp/DOMAIN-NAME.onmicrosoft.com/b2c_1_POLICY-NAME/v2.0
. For example, in the screenshot below, you see that the domain name isrelecloudshare.onmicrosoft.com
. In addition, the policy name we used isdefault-signin
, as shown in Step 1 above. Therefore, the B2C Authority URL in our example is:https://relecloudrideshare.b2clogin.com/tfp/relecloudrideshare.onmicrosoft.com/b2c_1_default-signin/v2.0
. Record this URL for later. You will use it as the value for theAuthorityUrl
setting in the App Setting for several of your Function Apps. In addition, it will be used as the value for thewindow.authAuthority
setting in thesettings.js
file within your website project.
Once completed, please jump to the setup section to continue.
⚠️ Cake provisioning support and docs will be deprecated soon. Please use Bicep provisioning instead.
The Cake
script responsible to deploy
and provision
is included in the dotnet
source directory. In order to run the Cake Script locally and deploy to your Azure Subscription, there are some pre-requisites:
- Create a service principal that can be used to authenticate the script to use your Azure subscription. This can be easily accomplished using the following PowerShell script:
# Login
Login-AzureRmAccount
# Set the Subscriptions
Get-AzureRmSubscription
# Set the Subscription to your preferred subscription
Select-AzureRmSubscription -SubscriptionId "<your_subs_id>"
# Create an application in Azure AD
$pwd = convertto-securestring "<your_pwd>" -asplaintext -force
$app = New-AzureRmADApplication -DisplayName "RideSharePublisher" -HomePage "http://rideshare" -IdentifierUris "http://rideshare" -Password $pwd
# Create a service principal
New-AzureRmADServicePrincipal -ApplicationId $app.ApplicationId
# Assign role
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $app.ApplicationId.Guid
- Place two text files in the
dotnet
directory that can tell the Cake script about the service principal that you just created. The two text files are:dev_authfile.txt
andprod_authfile.txt
. They contain the following:
subscription=<your_subs_id>
client=<your_client_id_produced_by_ps_above>
key=<your_pwd_you_set_up_in_ps_above>
tenant=<your_azure_tenant_id>
managementURI=https\://management.core.windows.net/
baseURL=https\://management.azure.com/
authURL=https\://login.windows.net/
graphURL=https\://graph.windows.net/
If your dev
and prod
environments are hosted on the same Azure subscription, then the two auth files will be identical.
✳️ Please note that you must adjust the cake/paths.cake
file to match your resource names. The public static class Resources
class defines the resource names.
Once the above is completed, from a PowerShell command, use the following commands to provision the Dev
and Prod
environments:
./build.ps1 -Target Provision -ScriptArgs '--Env=Dev'
./build.ps1 -Target Provision -ScriptArgs '--Env=Prod'
Please note that provisioning a Cosmos DB Account takes a long time to be online. If you proceed with creating a database and the collections while the DB account status is Creating
, you will get an error that says something like bad request
without much of an explanation. Once the DB Account becomes Online
, you can continue to provision the rest (by re-invoking the provision
command). The exact error is: One or more errors occurred. Long running operation failed with status BadRequest
.
Unfortunately, the Cake script cannot provision the following resources because they are currently not supported in the Azure Management Libraries for .NET. So please complete the following provisions manually as described in the manual steps above:
- Event Grid
- Application Insights
- Logic App
- API Management Service
- SignalR Service
- Azure Key Vault
- B2C Tenant
Once completed, please jump to the setup section to continue.
You can provision most of the services required through the supplied Bicep with command-line using Azure PowerShell or Azure CLI. The Azure portal provides a nice user interface for deploying resources when using a Bicep file. To use this interface, start by clicking this link.
Unfortunately, the Bicep cannot provision the following resources. Please provision these manually as described in the steps above:
Once completed, please jump to the setup section to continue.
After you have provisioned all your resources, there are some manual steps that you need to do to complete the setup:
- Add APIM Products and APIs
- Connect Event Grid to Function Apps
- Connect Event Grid to Logic App
- Run a script to create the TripFact table
Please note that you should have created the Create the API Management Service before you can proceed with this step. In addition, you should have already deployed the Function Apps to Azure before you can make them available in the API Management Service.
APIM defines two top-level entities: Product
and Api
. They are related this way:
Therefore we want to create a new product and add to it several APIs.
-
Type API Management into the Search box at the top of the
All Services
page, then select API Management Service section. -
Select the resource you created earlier i.e.
rideshare
. -
Select it to go to detail and click on
products
. Click the Add button to create a new API Management product. -
Complete the API Management product creation form with the following:
- Display Name: Enter a name i.e.
RideShare
. - Id: Enter a unique identifier
rideshare-product
. - Description: Enter an optional description.
- State: Select
Not Published
. - Requires subscription: Checked.
- Display Name: Enter a name i.e.
-
Re-select the API Management Service to go to detail and click on
APIs
. Click the Add a new API and select Function App. -
On the Create from Function App form, select Browse next to Function App.
-
On the Import Azure Functions form, select Configure required settings under Function App.
-
Select the Drivers Function App you created. Make sure all the functions are selected, then click Select.
-
Complete the API Management API creation form for
Drivers
with the following:- Display Name: Enter a name i.e.
RideShare Drivers API
. - Name: Enter an identifier
rideshare-drivers
. - API URL Suffix: Enter
d
. - Product: Select the
RideShare
product you created earlier. This is how the API is linked to the product.
- Display Name: Enter a name i.e.
-
Repeat steps 5-9 for the
Trips
andPassengers
Function Apps. TheOrchestrators
are not exposed to the outside world and hence they should not be added to APIM.- For the
Trips
API, entert
for the API URL Suffix. - For the
Passengers
API, enterp
for the API URL Suffix.
- For the
The following APIs should have been created when you imported the functions:
Display Name | Name | URL | Template | Query |
---|---|---|---|---|
Get Drivers | get-drivers | GET /drivers |
None | GetDrivers Auth Code |
Get Drivers Within Location | get-drivers-within-location | GET /drivers/{latitude}/{longitude}/{miles} |
latitude = double, longitude = double, miles = double | GetDriversWithinLocation Auth Code |
Get Active Drivers | get-active-drivers | GET /activedrivers |
None | GetActiveDrivers Auth Code |
Get Driver | get-driver | GET /drivers/{code} |
code = driver code = string | GetDriver Auth Code |
Create Driver | create-driver | POST /drivers |
None | CreateDriver Auth Code |
Update Driver | update-driver | PUT /drivers |
None | UpdateDriver Auth Code |
Update Driver Location | update-driver-location | PUT /driverlocations |
None | UpdateDriverLocation Auth Code |
Get Driver Locations | get-driver-locations | GET /driverlocations/{code} |
code = driver code = string | GetDriverLocations Auth Code |
Delete Driver | delete-driver | DELETE /drivers/{code} |
code = driver code = string | DeleteDriver Auth Code |
Display Name | Name | URL | Template | Query |
---|---|---|---|---|
Get Trips | get-trips | GET /trips |
None | GetTrips Auth Code |
Get Active Trips | get-active-trips | GET /activetrips |
None | GetActiveTrips Auth Code |
Get Trip | get-trip | GET /trips/{code} |
code = trip code = string | GetTrip Auth Code |
Create Trip | create-trip | POST /trips |
None | CreateTrip Auth Code |
Assign Trip Driver | assign-trip-driver | POST /trips/{code}/drivers/{drivercode} |
code = trip code = string, drivercode = driver code = string | AssignTripDriver Auth Code |
Display Name | Name | URL | Template | Query |
---|---|---|---|---|
Get Passengers | get-passengers | GET /passengers |
None | GetPassengers Auth Code |
Get Passenger | get-passenger | GET /passengers/{code} |
code = passenger code = string | GetPassenger Auth Code |
Now that you've added your APIs, you need to publish your RideShare
product. To do this, select Products on the left-hand menu of your APIM service. You will see that the RideShare
product is in the Not published state.
Select the ellipses (...) on the right-hand side of the RideShare
product, then select Publish.
Please note that you should have created the Create the API Management Service as well as added the RideShare APIM Product before you can proceed with this step.
When accessing APIs hosted by APIM, you are required to pass an Ocp-Apim-Subscription-Key
HTTP header value that contains an API Key from a valid subscription to the RideShare Product you created. Perform the following steps to retrieve this key:
-
Type API Management into the Search box at the top of the
All Services
page, then select API Management Service section. -
Select the resource you created earlier i.e.
rideshare
. -
Select the Users link in the left-hand navigation menu. At this point, you should have one user named "Administrator". Select this account.
-
Select the ellipses (...) next to the RideShare product that you created, then select Show/hide keys. Finally, copy the Primary Key value. Save this value for later. This value will be used for the
window.apiKey
setting in thesettings.js
file within the website project.
Please note that you should have created the Event Grid Topic and the Function Apps before you can proceed with this step. In addition, you should have already deployed the Function Apps to Azure before you can make them listen to an Event Grid Topic.
-
Type Function Apps into the Search box at the top of the
All Services
page, then select Function Apps section. -
Select the RideShareTripsFunctionApp app.
-
Expand the Functions (Read Only) tree leaf:
-
Select the
EVGH_TripExternalizations2PowerBI
Function and click on Add Event Grid Subscription. This will show a dialog to allow you to make this function a listener for the Event Grid Topic:- Name: Select a name i.e.
RideShareTripExternalizations2PpowerBI
. - Topic Type: Select
Event Grid Topic
. - Subscription: Select your Azure subscription.
- Resource Group: Either select an existing Resource Group or create a new one such as
serverless-microservices
. - Instance: Select the Event Grid Topic you are subscribing to i.e.
RideShareTripExternalizations
- Check the Subscribe to all event types
- Name: Select a name i.e.
-
Repeat step 4 for the
EVGH_TripExternalizations2SignalR
Function. -
Repeat step 5 for the
EVGH_TripExternalizations2CosmosDB
Function. This is located in the Trip Archiver Node.js Function App.
Please note that you should have created the Event Grid Topic before you can proceed with this step.
-
Type Logic Apps into the Search box at the top of the
All Services
page, then select Logic Apps section. -
Click the Add button to create a new Logic App.
-
Complete the logic app creation form with the following:
- Name: Enter a unique value for the logic app i.e.
ProcessTripExternalization
. - Subscription: Select your Azure subscription.
- Resource Group: Either select an existing Resource Group or create a new one such as
serverless-microservices
. - Location: Select a region closest to you. Make sure you select the same region for the rest of your resources.
- Name: Enter a unique value for the logic app i.e.
-
Once the resource is created, navigate to it and select
Blank Logic App
. In theSearch connectors and triggers
, typeEvent Grid
and select theAzure Event Grid
trigger: -
Then select the
When Event source occurs
: -
Finally select the
When Event source occurs
:- Subscription: Select your Azure subscription.
- Resource Type: Select
Microsoft.EventGrid.Topic
. - Resource Name: Select the Event Grid Topic you provisioned.
-
Then click on the
New Step
and type in theChoose an action
search boxSendGrid
:- Select `Send Email (v2) (preview).
- You may need to setup a SendGrid account if you have not done so already. Alternatively you can choose Office 365 Email email sender or Gmail sender or whatever Logic App supports.
-
Fill out the Email Sender form:
- From: The email address you wish this notification be sent from
- To: The email address you wish this notification be sent to
- Subject: If you select this field, you can either type whatever you want the subject or pick from one the dynamic fields shown. The Event Grid
Subject
is what makes sense.
- Body: If you select this field, you can either type whatever you want the body or pick from one the dynamic fields shown. The Event data does not appear automatically in the list of available dynamic content. You must switch to
Code view
to access the event data i.e.@{triggerBody()?['data']}
.
Connect to the SQL database and run the following script to create the TripFact
table and its indices:
USE [RideShare]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE[dbo].TripFact (
[Id][int] IDENTITY(1, 1) NOT NULL,
[StartDate][datetime] NOT NULL,
[EndDate][datetime] NULL,
[AcceptDate][datetime] NULL,
[TripCode] [nvarchar] (20) NOT NULL,
[PassengerCode] [nvarchar] (20) NULL,
[PassengerName] [nvarchar] (100) NULL,
[PassengerEmail] [nvarchar] (100) NULL,
[AvailableDrivers] [int] NULL,
[DriverCode] [nvarchar] (20) NULL,
[DriverName] [nvarchar] (100) NULL,
[DriverLatitude] [float] NULL,
[DriverLongitude] [float] NULL,
[DriverCarMake] [nvarchar] (100) NULL,
[DriverCarModel] [nvarchar] (100) NULL,
[DriverCarYear] [nvarchar] (4) NULL,
[DriverCarColor] [nvarchar] (20) NULL,
[DriverCarLicensePlate] [nvarchar] (20) NULL,
[SourceLatitude] [float] NULL,
[SourceLongitude] [float] NULL,
[DestinationLatitude] [float] NULL,
[DestinationLongitude] [float] NULL,
[Duration] [float] NULL,
[MonitorIterations] [int] NULL,
[Status] [nvarchar] (20) NULL,
[Error] [nvarchar] (200) NULL,
[Mode] [nvarchar] (20) NULL
CONSTRAINT[PK_dbo.TripFact] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
CREATE INDEX IX_TRIP_START_DATE ON dbo.TripFact(StartDate);
CREATE INDEX IX_TRIP_CODE ON dbo.TripFact(TripCode);
CREATE INDEX IX_TRIP_PASSENGER_CODE ON dbo.TripFact(PassengerCode);
CREATE INDEX IX_TRIP_DRIVER_CODE ON dbo.TripFact(DriverCode);
In this step, you will add all of your secrets to Key Vault. These secrets will be referred to by the App Settings within your Function Apps.
-
Open your Key Vault instance in the portal.
-
Select Secrets under Settings in the left-hand menu.
-
Select Generate/Import to add a new key.
-
Use the table below as a guide to add each new Name and Value pair:
Name Value AppInsightsInstrumentation The Application Insights Resource Instrumentation Key. This key is required by the Function App so it knows there is an application insights resource associated with it DocDbApiKey The Cosmos DB API Key DocDbEndpointUri The Cosmos DB Endpoint URI DocDbRideShareDatabaseName The Cosmos DB database i.e. RideShare
DocDbRideShareMainCollectionName The Cosmos DB Main container i.e. Main
DocDbThroughput The provisioned container RUs i.e. 400 AuthorityUrl The B2C Authority URL i.e. https://relecloudrideshare.b2clogin.com/tfp/relecloudrideshare.onmicrosoft.com/b2c_1_default-signin/v2.0 ApiApplicationId The B2C Client ID ApiScopeName The Scope Name i.e. rideshare GraphTenantId Azure Tenant ID GraphClientId Azure Graph client ID GraphClientSecret Azure Graph secret TripExternalizationsEventGridTopicApiKey The API Key of the event grid topic SqlConnectionString The connection string to the Azure SQL Database where TripFact
is provisionedAzureSignalRConnectionString The connection string to the SignalR Service When you are finished creating the secrets, your list should look similar to the following:
When you set the App Settings for each of your Function App in the next section below, you will need to reference the URI of a secret in Key Vault, including the version number. To do this, perform the following steps for each secret.
-
Open your Key Vault instance in the portal.
-
Select Secrets under Settings in the left-hand menu.
-
Select the secret whose URI value you wish to obtain.
-
Select the Current Version of the secret.
-
Copy the Secret Identifier.
When you add the Key Vault reference to this secret within a Function App's App Settings, you will use the following format: @Microsoft.KeyVault(SecretUri={referenceString})
, where {referenceString}
is replaced by the Secret Identifier (URI) value above.
For example, a complete reference would look like the following:
@Microsoft.KeyVault(SecretUri=https://ridesharevault.vault.azure.net/secrets/ApiApplicationId/b520d8c3f0124f9a82d88b7064d9715e)
The reference implementation solution requires several settings for each Function App. The settings
directory contains the setting file for each Function App. The files are a collection of KEY
and VALUE
delimited by a |
. They need to be imported as Application Settings
for each Function App. The Cake deployment script can auto-import these files into the Application Settings
.
Note: If you are manually adding the Application Settings for your Function Apps, use the tables below as a guide. To get to Application Settings, open you Function App, then select Configuration.
KEY | DESCRIPTION |
---|---|
APPINSIGHTS_INSTRUMENTATIONKEY | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AppInsightsInstrumentation Key Vault secret |
FUNCTIONS_EXTENSION_VERSION | Must be set to ~2 since the solution uses V2 |
DocDbApiKey | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbApiKey Key Vault secret |
DocDbEndpointUri | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbEndpointUri Key Vault secret |
DocDbRideShareDatabaseName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareDatabaseName Key Vault secret |
DocDbRideShareMainCollectionName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareMainCollectionName Key Vault secret |
DocDbThroughput | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbThroughput Key Vault secret |
InsightsInstrumentationKey | Same value as APPINSIGHTS_INSTRUMENTATIONKEY. This value is used by the Function App while the other is used by the Function framework |
AuthorityUrl | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AuthorityUrl Key Vault secret |
ApiApplicationId | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the ApiApplicationId Key Vault secret |
ApiScopeName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the ApiScopeName Key Vault secret |
EnableAuth | if set to true, the JWT token validation will be enforced |
KEY | DESCRIPTION |
---|---|
APPINSIGHTS_INSTRUMENTATIONKEY | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AppInsightsInstrumentation Key Vault secret |
FUNCTIONS_EXTENSION_VERSION | Must be set to ~2 since the solution uses V2 |
AzureWebJobsDashboard | The Storage Account Connection String |
AzureWebJobsStorage | The Storage Account Connection String |
DocDbApiKey | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbApiKey Key Vault secret |
DocDbEndpointUri | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbEndpointUri Key Vault secret |
DocDbRideShareDatabaseName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareDatabaseName Key Vault secret |
DocDbRideShareMainCollectionName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareMainCollectionName Key Vault secret |
DocDbThroughput | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbThroughput Key Vault secret |
InsightsInstrumentationKey | Same value as APPINSIGHTS_INSTRUMENTATIONKEY. This value is used by the Function App while the other is used by the Function framework |
AuthorityUrl | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AuthorityUrl Key Vault secret |
ApiApplicationId | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the ApiApplicationId Key Vault secret |
ApiScopeName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the ApiScopeName Key Vault secret |
EnableAuth | if set to true, the JWT token validation will be enforced |
GraphTenantId | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the GraphTenantId Key Vault secret |
GraphClientId | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the GraphClientId Key Vault secret |
GraphClientSecret | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the GraphClientSecret Key Vault secret |
KEY | DESCRIPTION |
---|---|
APPINSIGHTS_INSTRUMENTATIONKEY | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AppInsightsInstrumentation Key Vault secret |
FUNCTIONS_EXTENSION_VERSION | Must be set to ~2 since the solution uses V2 |
AzureWebJobsDashboard | The Storage Account Connection String |
AzureWebJobsStorage | The Storage Account Connection String |
DocDbApiKey | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbApiKey Key Vault secret |
DocDbEndpointUri | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbEndpointUri Key Vault secret |
DocDbRideShareDatabaseName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareDatabaseName Key Vault secret |
DocDbRideShareMainCollectionName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareMainCollectionName Key Vault secret |
DocDbThroughput | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbThroughput Key Vault secret |
InsightsInstrumentationKey | Same value as APPINSIGHTS_INSTRUMENTATIONKEY. This value is used by the Function App while the other is used by the Function framework |
DriversAcknowledgeMaxWaitPeriodInSeconds | The number of seconds to wait before the solution times out waiting for drivers to accept a trip i.e. 120 |
DriversLocationRadiusInMiles | The miles radius that the solution locates available drivers within i.e. 15 |
TripMonitorIntervalInSeconds | The number of seconds the TripMonitor waits in its monitoring loop i.e. 10 |
TripMonitorMaxIterations | The number of maximum iterations the TripMonitor loops before it aborts the trip i.e. 20 |
IsPersistDirectly | If true, the orchestrators access the data storage layer directly. Default to true |
TripManagersQueue | The TripManagers queue name i.e. trip-managers |
TripMonitorsQueue | The TripMonitors queue name i.e. trip-monitors |
TripDemosQueue | The TripDemos queue name i.e. trip-demos |
TripDriversQueue | The TripDrivers queue name i.e. trip-drivers |
TripExternalizationsEventGridTopicUrl | The URL of the event grid topic i.e. https://ridesharetripexternalizations.eastus-1.eventgrid.azure.net/api/events |
TripExternalizationsEventGridTopicApiKey | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the TripExternalizationsEventGridTopicApiKey Key Vault secret |
KEY | DESCRIPTION |
---|---|
APPINSIGHTS_INSTRUMENTATIONKEY | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AppInsightsInstrumentation Key Vault secret |
FUNCTIONS_EXTENSION_VERSION | Must be set to ~2 since the solution uses V2 |
AzureWebJobsDashboard | The Storage Account Connection String |
AzureWebJobsStorage | The Storage Account Connection String |
DocDbApiKey | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbApiKey Key Vault secret |
DocDbEndpointUri | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbEndpointUri Key Vault secret |
DocDbRideShareDatabaseName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareDatabaseName Key Vault secret |
DocDbRideShareMainCollectionName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbRideShareMainCollectionName Key Vault secret |
DocDbThroughput | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the DocDbThroughput Key Vault secret |
InsightsInstrumentationKey | Same value as APPINSIGHTS_INSTRUMENTATIONKEY. This value is used by the Function App while the other is used by the Function framework |
IsEnqueueToOrchestrators | Trigger Orchestrators via queues instead of HTTP. Default and recommended value is true |
TripManagersQueue | The TripManagers queue name i.e. trip-managers |
TripMonitorsQueue | The TripMonitors queue name i.e. trip-monitors |
TripDemosQueue | The TripDemos queue name i.e. trip-demos |
AuthorityUrl | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AuthorityUrl Key Vault secret |
ApiApplicationId | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the ApiApplicationId Key Vault secret |
ApiScopeName | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the ApiScopeName Key Vault secret |
EnableAuth | if set to true, the JWT token validation will be enforced |
SqlConnectionString | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the SqlConnectionString Key Vault secret |
AzureSignalRConnectionString | Enter @Microsoft.KeyVault(SecretUri={referenceString}) , where {referenceString} is the URI for the AzureSignalRConnectionString Key Vault secret |
StartTripManagerOrchestratorApiKey | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Start Trip Manager Orchestrator trigger endpoint function code key |
StartTripManagerOrchestratorBaseUrl | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Start Trip Manager Orchestrator trigger endpoint function base url |
StartTripDemoOrchestratorApiKey | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Trip Start Demo Orchestrator trigger endpoint function code key |
StartTripDemoOrchestratorBaseUrl | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Trip Start Demo Orchestrator trigger endpoint function base url |
TerminateTripManagerOrchestratorApiKey | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Terminate Trip Manager Orchestrator trigger endpoint function code key |
TerminateTripManagerOrchestratorBaseUrl | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Trip Manager Orchestrator trigger endpoint function base url |
TerminateTripMonitorOrchestratorApiKey | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Terminate Trip Demo Orchestrator trigger endpoint function code key |
TerminateTripMonitorOrchestratorBaseUrl | (Optional: only needed if IsEnqueueToOrchestrators is false ) The Trip Terminate Demo Orchestrator trigger endpoint function base url |
KEY | DESCRIPTION |
---|---|
FUNCTIONS_EXTENSION_VERSION | Must be set to ~1 since this Function App uses 1.0.11959.0 |
DocDbConnectionStringKey | The Cosmos DB Connection String |
In order for your Function Apps to be able to access Key Vault to read the secrets, you must create a system-assigned managed identity for each Function App, and create an access policy in Key Vault for each application identity.
Perform these steps for each of your Function Apps (Drivers, Passengers, Orchestrators, and Trips):
-
Open the Function App and navigate to Platform features.
-
Select Identity.
-
Within the System assigned tab, switch Status to On. Select Save.
Perform these steps for each of your Function Apps to create an access policy that enables the "Get" secret permission:
-
Open your Key Vault service.
-
Select Access policies.
-
Select + Add new.
-
Select the Select principal section on the Add access policy form.
-
In the Principal blade, search for your Rideshare Function App's service principal, select it, then click the Select button.
-
Expand the Secret permissions and check Get under Secret Management Operations.
-
Select OK to add the new access policy. Repeat these steps for each Function App identity.
-
When you are done, you should have an access policy for each Function App's managed identity. Select Save to finish the process.
In order to build .NET solution from Visual Studio, you need:
- VS 2017 15.7 or later
- .NET Core 2.1 SDK Installed
In order to build Node.js Archiver Function App, you need:
- Node.js 8.11.4 or later
It is not required to build the website on your local machine. It is ready to deploy as-is. However, if you wish to build and run the website locally, you need:
- Node.js 8.9 or later
- Install Vue.js with NPM:
npm install vue
You may run the website in developer mode by opening a command prompt or terminal window, navigating to the web directory (/web/serverless-microservices-web
), running npm install
to download the NPM packages, then running the following command:
npm run serve
The website uses a settings.js
file to store site-wide settings that are specific to your environment, such as URLs and keys. Due to the sensitive nature of these settings, the settings.js
file is excluded from the source code by way of the .gitignore
file located in the web project directory.
Creating the settings.js
file locally in your web project directory is optional and only used if you decide to run the website locally. You will be adding this file to the deployed website that is hosted in your Azure Web App that was created earlier. However, it might be easier to create it locally first, then copy the contents of the file to its deployed location later.
-
Open the /web/serverless-microservices-web directory in your favorite IDE, such as Visual Studio Code. Optionally, open a file explorer and navigate to this directory.
-
Expand the /public/js folder. Copy
settings.sample.js
and save it assettings.js
in this folder. -
Update the values within as follows:
KEY | DESCRIPTION |
---|---|
window.authClientId | The B2C Client ID you recorded earlier |
window.authAuthority | The B2C Authority URL you recorded earlier i.e. https://relecloudrideshare.b2clogin.com/tfp/relecloudrideshare.onmicrosoft.com/b2c_1_default-signin/v2.0 |
window.authScopes | The B2C full Scope name (including URL) you recorded earlier i.e. https://relecloudrideshare.onmicrosoft.com/api/rideshare |
window.authEnabled | true |
window.apiKey | The APIM API key you saved earlier |
window.apiBaseUrl | The APIM Gateway URL, found on the Overview blade of your APIM service, i.e. https://rideshare.azure-api.net |
window.apiDriversBaseUrl | ${window.apiBaseUrl}/d |
window.apiTripsBaseUrl | ${window.apiBaseUrl}/t |
window.apiPassengersBaseUrl | ${window.apiBaseUrl}/p |
window.signalrInfoUrl | The URL to your signalrinfo function within the Trips Function App, i.e. https://ridesharetripsfunctionapp.azurewebsites.net/api/signalrinfo |
Because the SPA website runs on static files with no server-side rendering, you must compile and minify the files to make them production-ready. Your build configuration for the website that you create in the next section within Azure DevOps will handle this for you in preparation for releasing the build to Azure. However, if you wish to run the command locally and manually copy the files yourself, perform the following:
- Open a command prompt or terminal window and navigate to the web directory (/web/serverless-microservices-web). Execute the following command:
npm run build
-
Copy the files within the
/dist/
directory to your chosen deployment location. To manually upload these files to your Azure Web App, perform the following steps:-
Open a web browser and go to:
https://{sitename}.scm.azurewebsites.net/DebugConsole
(Kudu). -
Navigate to:
site/wwwroot
. -
Drag and drop the files from your newly created
/dist/
directory tosite/wwwroot
in Kudu.
-
You must upload or create the settings.js
file for your website in Azure before it can function properly.
Perform these steps after deploying your website to Azure. You may continue on to the Azure DevOps section below to initially deploy your site before coming back to this section to add the settings.js file. Otherwise, if you have followed the steps above to manually deploy the website, continue with the steps below:
-
Open a web browser and go to:
https://{sitename}.scm.azurewebsites.net/DebugConsole
(Kudu). -
Navigate to:
site/wwwroot/js
. -
Click the + button next to / js on the upper-left above the file list, then select New file.
-
Type the file name settings.js, then hit Enter on the keyboard.
-
Select the Edit icon (looks like a pencil) located to the left of the new settings.js file.
-
Paste the contents of the local
settings.js
file you created earlier, then click Save.
Alternatively, you can drag and drop your local settings.js file to the site/wwwroot/js
directory in Kudu.
At this point, your website is all set. Your settings.js file will not get overwritten in subsequent deployments from Azure DevOps.
Function App deployments can happen from Visual Studio IDE, Azure DevOps by defining a build pipeline that can be triggered upon push to the code repository, for example, or a build script such as Cake or Psake.
Relecloud decided to use Azure DevOps for production build and deployment and Cake for development build and deployment.
Azure DevOps provides development collaboration tools, source code repositories, and DevOps-specific services, such as DevOps Pipelines, that help you easily set up continuous integration and continuous delivery (CI/CD) for your applications.
When configuring build pipelines, Azure Pipelines enables you to configure and automate your build and delivery tools and environments in YAML (as Infrastructure-as-Code) or through the visual designer in your Azure DevOps web portal at https://dev.azure.com. The preferred method is to use YAML files, as build configurations can be managed in code and included as part of the CI/CD process. The visual designer is good when you are new to creating CI/CD pipelines or are unsure of the available options.
While it is possible to define your release pipeline to Azure from within the YAML file, it is best practice to create a separate release pipeline. This gives you the flexibility to build once and release to several places, including deployment slots.
This section will walk you through creating YAML-based build pipelines and separate release pipelines.
-
You need an Azure DevOps organization. If you don't have one, you can create one for free. If your team already has one, then make sure you're an administrator of the Azure DevOps project that you want to use.
-
You need a GitHub account, where you can fork the serverless-microservices repository.
-
Sign in to https://dev.azure.com.
-
Go to your Azure DevOps project. If you don't have one, create one.
-
Select Pipelines from the menu, then Builds. Click the New pipeline button.
-
Select GitHub as your source, then click the Authorize using OAuth button to create a secure connection with your GitHub account.
-
Select the ellipses button (...) next to Repository to browse and select your forked repository. Make sure master is selected as the default branch, then select Continue.
-
Click the Apply button next to the YAML template.
-
In the YAML build template form, enter Rideshare-StaticWebsite-CI into the Name field. Select Hosted Linux Preview under Agent pool, then select the ellipses button (...) next to the YAML file path textbox.
-
In the Select path modal dialog box, expand the root repository folder, then expand
pipelines
, and expandbuild
. Select Rideshare-StaticWebsite-CI.yaml, then select OK. -
Select the Save & queue menu item, then select Save.
-
Select Builds under Pipelines, then select the + New dropdown above the list of build pipelines on the left, then select New build pipeline.
-
Repeat steps 5 through 9 to create the following additional build pipelines:
Name | Agent pool | YAML file path |
---|---|---|
Rideshare-DotnetFunctionApps-CI | Hosted Linux Preview | pipelines/build/Rideshare-DotnetFunctionApps-CI.yaml |
Rideshare-NodeFunctionApps-CI | Hosted Linux Preview | pipelines/build/Rideshare-NodeFunctionApps-CI.yaml |
When you are finished creating the three build pipelines, your Builds page should look similar to the following screenshot:
Notice that there are both manual builds and CI builds listed in the history. "CI build" represents the build agent that performed a continuous integration (CI) build, triggered by changes made to the master branch. Try committing new changes to automatically trigger a build.
To manually trigger a build, select Queue, then click the Queue button in the modal dialog box that appears:
Be sure to manually queue each of the three builds before continuing to the next section. This is an important step, as it will allow you to select the build artifacts when you configure the release pipeline for each.
We will begin by creating a new release pipeline for the static website, using the web interface. The section that follows will have you import the remaining two release pipelines to speed up the process.
✳️ Make sure the App Settings (under Application settings) for each of your Azure Function Apps are configured as shown in the Function App Application Settings section to reflect your own resource app settings and connection strings. Your deployed Function Apps will not work without these settings.
-
Select Pipelines from the menu, then Releases. Click the New pipeline button.
-
Select Azure App Service deployment within the list of templates.
-
Change the Stage name to "Web app".
-
Close the Stage dialog box, then select + Add an artifact within the Artifacts box on the left-hand side of the pipeline.
-
Select the Build source type. Select Rideshare-StaticWebsite-CI in the Source dropdown. Set Default version to Latest. Keep the default source alias, then select Add.
-
Select the 1 job, 1 task link under the Web app stage you created.
-
Under the Web app stage configuration, select your Azure subscription from the dropdown list, then select Authorize. This creates a secure connection the release pipeline can use to deploy your website to Azure.
-
Set the App type to Web App, then select your Web App you provisioned earlier, within the App service name dropdown list.
-
Select the Deploy Azure App Service task on the left-hand side. Scroll down and select the ellipses button (...) next to the Package or folder textbox to browse the artifacts directory.
-
In the modal dialog box that appears, expand the Linked artifacts folder, then the _Rideshare-StaticWebsite-CI folder, and finally, the drop folder. Select dist, then click the OK button.
-
The Package or folder textbox should now be populated with
$(System.DefaultWorkingDirectory)/_Rideshare-StaticWebsite-CI/drop/dist
. -
Select the "New release pipeline" title, and change it to Static website release pipeline. Next, select the Save button to the right. When the save dialog appears, click OK.
-
After saving, select + Release, then Create a release.
-
In the dialog that appears, select Create. You will see a notification afterwards that a release has been created. You can select the name of the release to view its progress.
-
If all goes well, you should see that the release was successful, as indicated by the Succeeded status underneath the Web app stage.
Release pipelines can be exported as a .json
file. This is especially useful for more complex pipelines, as you will see with the .NET-based Function App release pipeline. In this section, you will import the release pipelines for the Azure Function Apps.
-
Select Pipelines from the menu, then Releases.
-
Select + New, then Import a pipeline.
-
Select Browse within the "Import release pipeline" dialog box. Browse your file system to the local directory containing this project. Select the following file:
\pipelines\release\Function-apps-release-pipeline.json
. Finally, click the OK button on the dialog. -
You should see the following release pipeline, which includes stages for the Drivers, Trips, Orchestrators, and Passengers Function Apps:
-
Notice that the Tasks menu items has a red circle icon with an exclamation mark inside. This means it contains tasks that need your attention. Select Tasks, then Drivers Function App. First, select your linked Azure subscription, then select the App service name for your Drivers Function App.
-
Select the Run on agent job on the left-hand side. Select Hosted VS2017 underneath Agent pool.
-
Repeat steps 5 and 6 for the remaining tasks: Trips Function App, Orchestrators Function App, and Passengers Function App. When you are done, there should be no more tasks that need your attention (marked with the red circle icon). When you are done, select Save on top of the page.
Note: When you select the "Deploy Azure App Service" step on any of the tasks, make note of the following items of interest: each has a specific Zip file selected for the "Package or folder" setting. This is what allows us to deploy from several different Function App projects that are part of the Visual Studio solution. Also, the RunFromZip deployment method is selected for each. This allows us to reduce cold start times for the Function Apps by copying just the Zip file and running from it without extracting it, each time a new VM is provisioned to host the Function App. This is much faster than copying many small files or even extracting a Zip.
-
After saving, select + Release, then Create a release.
-
In the dialog that appears, select Create. You will see a notification afterwards that a release has been created. You can select the name of the release to view its progress.
-
Verify that the release to each Function App was successfully completed.
-
Select Pipelines from the menu, then Releases.
-
Select + New, then Import a pipeline.
-
Select Browse within the "Import release pipeline" dialog box. Browse your file system to the local directory containing this project. Select the following file:
\pipelines\release\Node-function-apps-release-pipeline.json
. Finally, click the OK button on the dialog. -
You should see the following release pipeline, which deploys the node.js-based Trip Archiver Function App:
-
Select Tasks, then Trip Archiver Function App.
-
Select the Run on agent job on the left-hand side. Select Hosted VS2017 underneath Agent pool.
-
Select the Azure App Service Deploy task on the left-hand side. Select your linked Azure subscription, then select your Trip Archiver's Function App from the App Service name dropdown.
-
Select Save on top of the page.
-
After saving, select + Release, then Create a release.
-
In the dialog that appears, select Create. You will see a notification afterwards that a release has been created. You can select the name of the release to view its progress.
-
Verify that the release to the Trip Archiver Function App was successfully completed.
The Cake
script responsible to deploy
and provision
is included in the dotnet
source directory. In order to run the Cake Script locally and deploy to your Azure Subscription, there are some pre-requisites. Please refer to the Cake provision section to know how to do this.
Make sure the settings
are updated as shown in the Function App Application Settings section to reflect your own resource app settings and connection strings.
Once all of the above is in place, Cake is now able to authenticate and deploy the C# function apps.
✳️ Please note that you must adjust the cake/paths.cake
file to match your resource names. The public static class Resources
class defines the resource names.
From a PowerShell command, use the following commands for the Dev
environment:
./build.ps1 -Target Deploy -Configuration Debug -ScriptArgs '--Site=Drivers','--App=ServerlessMicroservices.FunctionApp.Drivers','--Env=Dev'
./build.ps1 -Target Deploy -Configuration Debug -ScriptArgs '--Site=Orchestrators','--App=ServerlessMicroservices.FunctionApp.Orchestrators','--Env=Dev'
./build.ps1 -Target Deploy -Configuration Debug -ScriptArgs '--Site=Trips','--App=ServerlessMicroservices.FunctionApp.Trips','--Env=Dev'
./build.ps1 -Target Deploy -Configuration Debug -ScriptArgs '--Site=Passengers','--App=ServerlessMicroservices.FunctionApp.Passengers','--Env=Dev'
From a PowerShell command, use the following commands for the Prod
environment:
./build.ps1 -Target Deploy -Configuration Release -ScriptArgs '--Site=Drivers','--App=ServerlessMicroservices.FunctionApp.Drivers','--Env=Prod'
./build.ps1 -Target Deploy -Configuration Release -ScriptArgs '--Site=Orchestrators','--App=ServerlessMicroservices.FunctionApp.Orchestrators','--Env=Prod'
./build.ps1 -Target Deploy -Configuration Release -ScriptArgs '--Site=Trips','--App=ServerlessMicroservices.FunctionApp.Trips','--Env=Prod'
./build.ps1 -Target Deploy -Configuration Release -ScriptArgs '--Site=Passengers','--App=ServerlessMicroservices.FunctionApp.Passengers','--Env=Prod'
The .NET ServerlessMicroservices.Seeder
project contains a seeding command that can be used to seed drivers
using the Drivers APIs
.
Please note that the seed
command will seed drivers only if there are no drivers.
You must set the EnableAuth App Setting on the Drivers and Passengers Function Apps to
false
for the seeder to work.
> ServerlessMicroservices.Seeder.exe seed --help
Usage: seed [options]
Options:
--help Show help information
-t|--seeddriversurl Set seed drivers url
-t|--testurl Set test url
-i|--testiterations Set test iterations
-s|--testseconds Set test seconds
-v|--signalrinfourl Set SignalR Info URL
> ServerlessMicroservices.Seeder.exe seed --seeddriversurl https://ridesharedrivers.azurewebsites.net
We have seen in the deployment section, Function Apps in Azure are usually hosted in App Services
. They can also run locally during development. However, there are other compelling deployment options if we are able to containerize the Functions Apps as Docker images.
This is made easier in Function Apps v2 since they run on .NET Core
and hence cross-platform. This means that Function Apps can be containerized as Docker images and then deployed to one of many possibilities:
- Docker on a VM or development machine with Docker installed
- Azure Container Instances ACI
- Azure Kubernetes Service AKS
- Other Cloud providers
- On-Premises
Please note that when Function Apps are not run in Azure consumption plan, it implies that:
- The micro billing and the auto-scaling features are no longer applicable.
- The connected Azure resources such as storage and event grids will still run in Azure.
It turned out that Microsoft produces a Docker image for Azure Functions .NET Core V2 and is available via DockerHub:
microsoft/azure-functions-dotnet-core2.0
In the Dockerfiles
folder of the .NET
source code, there is a docker
file for each .NET function app:
Drivers:
FROM microsoft/azure-functions-dotnet-core2.0:2.0
COPY ./ServerlessMicroservices.FunctionApp.Drivers/bin/Debug/netstandard2.0 /home/site/wwwroot
Passengers:
FROM microsoft/azure-functions-dotnet-core2.0:2.0
COPY ./ServerlessMicroservices.FunctionApp.Passengers/bin/Debug/netstandard2.0 /home/site/wwwroot
Orchestrators:
FROM microsoft/azure-functions-dotnet-core2.0:2.0
COPY ./ServerlessMicroservices.FunctionApp.Orchestrators/bin/Debug/netstandard2.0 /home/site/wwwroot
Trips:
FROM microsoft/azure-functions-dotnet-core2.0:2.0
COPY ./ServerlessMicroservices.FunctionApp.Trips/bin/Debug/netstandard2.0 /home/site/wwwroot
The Dockerfile
is straightforward! We base it on the microsoft/azure-functions-dotnet-core2.0
image with v2~2
tag (as it is the current version) and we copy the output of the bin\<build>\netstandard2.0
to the wwwroot
of the image.
Please note that this assumes that you have Docker
installed on your Windows or Mac development machine.
Once the .NET solution is built, we can generate the Docker
images for each function app:
docker build -t rideshare-drivers:v1 -f dockerfiles/drivers .
docker build -t rideshare-passengers:v1 -f dockerfiles/passengers .
docker build -t rideshare-orchestrators:v1 -f dockerfiles/orchestrators .
docker build -t rideshare-trips:v1 -f dockerfiles/trips .
The docker build
command uses the dockerfile
specified in the -f
switch, produces an image and tags it i.e. rideshare-drivers:v1
.
Once the above commands are run, issue docker images
to make sure that the images do exist with proper tags. By the way, you can remove an image by ID using docker rmi <id>
.
Please note that there are some source code changes to support containers:
- The Functions Authorization Level is set to
Anonymous
instead ofFunction
:
[FunctionName("GetTrips")]
public static async Task<IActionResult> GetTrips([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "trips")] HttpRequest req,
ILogger log)
- Changed the
DocumentDB
client not to useTCP Direct Mode
! It turned out it is not supported inLinux
which causedUpserts
against Cosmos to causeService Unavailable
error. There is an issue inGitHub
on this.
Now that the Docker
images are produced, we can use docker
commands to start the containers locally. Because the containers require the environment variables to be fed into the container at run
time, we point to a file that contains the settings in the format required by Docker
:
docker run --env-file settings/RideShareDriversDockerDev-AppSettings.csv -p 8080:80 rideshare-drivers:v1
docker run --env-file settings/RideSharePassengersDockerDev-AppSettings.csv -p 8081:80 rideshare-passengers:v1
docker run --env-file settings/RideShareOrchestratorsDockerDev-AppSettings.csv -p 8082:80 rideshare-orchestrators:v1
docker run --env-file settings/RideShareTripsDockerDev-AppSettings.csv -p 8083:80 rideshare-trips:v1
Please note:
- The
settings
folder contains all the environment variables for each function app. - The Docker environment variables via a file requires that KEYs and VALUEs be separated by a
=
. - Map different port for each function app so we do not get into a conflict. So
Drivers
is mapped to 8080,Passengers
is mapped to 8081,Orchestrators
is mapped to 8082 andTrips
is mapped to 8083. - The connected Azure resources are still in Azure. For example, Cosmos DB and the storage accounts are still pointing to Azure.
- Issue
docker ps
command to make sure that the containers are running.
Once the containers are running, use something like Postman
to interact with the different function apps using their respective ports. For example:
Function App | Verb | URL | Description |
---|---|---|---|
Drivers | GET | GET http://localhost:8080/api/drivers |
Retrieve all drivers |
Trips | GET | GET http://localhost:8083/api/trips |
Retrieve all trips |
Trips | POST | POST http://localhost:8083/api/trips |
Create a new trip |
In order to deploy our containers to Azure Container Instances ACI, we must first publish them to a container registry such as DockerHub or Azure Container Registry. For our demo purposes, we can publish to DockerHub. Let us tag
each container and push
:
docker tag rideshare-drivers:v1 joesmith/rideshare-drivers:v1
docker push joesmith/rideshare-drivers:v1
docker tag rideshare-passengers:v1 joesmith/rideshare-passengers:v1
docker push joesmith/rideshare-passengers:v1
docker tag rideshare-orchestrators:v1 joesmith/rideshare-orchestrators:v1
docker push joesmith/rideshare-orchestrators:v1
docker tag rideshare-trips:v1 joesmith/rideshare-trips:v1
docker push joesmith/rideshare-trips:v1
Please note that the above requires that you have signed in to Docker hub account.
In order to actually create an ACI , we use Azure CLI with YAML
files to drive the properties and setup the container environment variables. In the yamlfiles
folder of the .NET
source code, there is a yaml
file for each .NET function app. Here is a sample:
apiVersion: 2018-06-01
location: eastus
name: rideshare-drivers
properties:
containers:
- name: rideshare-drivers
properties:
environmentVariables:
- "name": "APPINSIGHTS_INSTRUMENTATIONKEY"
"value": "<your-own>"
- "name": "FUNCTIONS_EXTENSION_VERSION"
"value": "~2"
- "name": "AzureWebJobsDashboard"
"value": "<your-own>"
- "name": "AzureWebJobsStorage"
"value": "<your-own>"
- "name": "DocDbApiKey"
"value": "<your-own>"
- "name": "DocDbEndpointUri"
"value": "<your-own>"
- "name": "DocDbRideShareDatabaseName"
"value": "RideShare"
- "name": "DocDbRideShareMainCollectionName"
"value": "Main"
- "name": "DocDbThroughput"
"value": 400
- "name": "InsightsInstrumentationKey"
"value": "<your-own>"
- "name": "IsRunningInContainer"
"value": "true"
- "name": "IsPersistDirectly"
"value": "true"
- "name": "AuthorityUrl"
"value": "<your-own>"
- "name": "ApiApplicationId"
"value": "<your-own>"
- "name": "ApiScopeName"
"value": "rideshare"
- "name": "EnableAuth"
"value": "false"
image: joesmith/rideshare-drivers:v1
ports:
- port: 80
resources:
requests:
cpu: 1.0
memoryInGB: 1.5
osType: Linux
ipAddress:
type: Public
dnsNameLabel: rideshare-drivers
ports:
- protocol: tcp
port: '80'
restartPolicy: Always
tags: null
type: Microsoft.ContainerInstance/containerGroups
Please note the following requires that you log in to Azure using az login
. To learn how to manage ACI using Azure CLI, please use this link.
In order to create the ACIs:
az container create --resource-group serverless-microservices-dev --file yamlfiles/aci_drivers.yaml
az container create --resource-group serverless-microservices-dev --file yamlfiles/aci_passengers.yaml
az container create --resource-group serverless-microservices-dev --file yamlfiles/aci_orchestrators.yaml
az container create --resource-group serverless-microservices-dev --file yamlfiles/aci_trips.yaml
In order to check on the provision status of the ACIs:
az container show -n rideshare-drivers -g serverless-microservices-dev
az container show -n rideshare-passengers -g serverless-microservices-dev
az container show -n rideshare-orchestrators -g serverless-microservices-dev
az container show -n rideshare-trips -g serverless-microservices-dev
In order to check the logs of the ACIs:
az container logs -n rideshare-drivers -g serverless-microservices-dev
az container logs -n rideshare-passengers -g serverless-microservices-dev
az container logs -n rideshare-orchestrators -g serverless-microservices-dev
az container logs -n rideshare-trips -g serverless-microservices-dev
In order to delete the ACIs:
az container delete -n rideshare-drivers -g serverless-microservices-dev --yes -y
az container delete -n rideshare-passengers -g serverless-microservices-dev --yes -y
az container delete -n rideshare-orchestrators -g serverless-microservices-dev --yes -y
az container delete -n rideshare-trips -g serverless-microservices-dev --yes -y
Once the containers are running, use something like Postman
to interact with the different function apps using their respective urls. For example:
Function App | Verb | URL | Description |
---|---|---|---|
Drivers | GET | GET http://rideshare-drivers.eastus.azurecontainer.io/api/drivers |
Retrieve all drivers |
Trips | GET | GET http://rideshare-trips.eastus.azurecontainer.io/api/trips |
Retrieve all trips |
Trips | POST | POST http://rideshare-trips.eastus.azurecontainer.io/api/trips |
Create a new trip |
Azure AKS provides much more robust way to deploy and manage the different containers as it provides orchestration and self-healing capabilities.
Since we already have the rideshare Docker images pushed to DockerHub, all we really need to do is to create an AKS cluster and deploy the rideshare app.
Here is a PowerShell script that can be used to provision a 1-node AKS cluster:
# Login to Azure - the client-id, the client-password and the tenant password are the same as setup in the Cake provision section
az login --service-principal -u <your-client-id> -p <your-client-password> --tenant <your-tenant-id>
# Display all accounts
az account list --output table
# Make sure you are using the proper subs
az account set --subscription "your-subs"
# Create a resource group
az group create --name serverless-microservices-k8s --location eastus
# Create a 1-node AKS cluster
az aks create --resource-group serverless-microservices-k8s --name rideshareAKSCluster --service-principal <your-client-id> --client-secret <your-password> --node-count 1 --enable-addons monitoring --generate-ssh-keys
# Make sure Kubectl is installed
az aks install-cli
# Allow Kubectrl use the cluster by getting the cluster credentials
az aks get-credentials --resource-group serverless-microservices-k8s --name rideshareAKSCluster
# Check the cluster nodes
kubectl get nodes
# Deploy the rideshare app
kubectl apply -f rideshare-app.yaml
# Wait until the services expose drivers, passengers and trips to the Internet
kubectl get service rideshare-drivers --watch
kubectl get service rideshare-passengers --watch
kubectl get service rideshare-trips --watch
The rideshare-app-yaml
can be defined this way:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: rideshare-drivers
spec:
replicas: 1
template:
metadata:
labels:
app: rideshare-drivers
spec:
containers:
- name: rideshare-drivers
image: khaledhikmat/rideshare-drivers:v1
ports:
- containerPort: 80
env:
- "name": "APPINSIGHTS_INSTRUMENTATIONKEY"
"value": "<your-own>"
- "name": "FUNCTIONS_EXTENSION_VERSION"
"value": "~2"
- "name": "AzureWebJobsDashboard"
"value": "<your-own>"
- "name": "AzureWebJobsStorage"
"value": "<your-own>"
- "name": "DocDbApiKey"
"value": "<your-own>"
- "name": "DocDbEndpointUri"
"value": "<your-own>"
- "name": "DocDbRideShareDatabaseName"
"value": "RideShare"
- "name": "DocDbRideShareMainCollectionName"
"value": "Main"
- "name": "DocDbThroughput"
"value": "400"
- "name": "InsightsInstrumentationKey"
"value": "<your-own>"
- "name": "IsRunningInContainer"
"value": "true"
- "name": "IsPersistDirectly"
"value": "true"
- "name": "AuthorityUrl"
"value": "https://relecloudrideshare.b2clogin.com/tfp/relecloudrideshare.onmicrosoft.com/b2c_1_default-signin/v2.0"
- "name": "ApiApplicationId"
"value": "<your-own>"
- "name": "ApiScopeName"
"value": "rideshare"
- "name": "EnableAuth"
"value": "false"
---
apiVersion: v1
kind: Service
metadata:
name: rideshare-drivers
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: rideshare-drivers
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: rideshare-passengers
spec:
replicas: 1
template:
metadata:
labels:
app: rideshare-passengers
spec:
containers:
- name: rideshare-passengers
image: khaledhikmat/rideshare-passengers:v1
ports:
- containerPort: 80
env:
- "name": "APPINSIGHTS_INSTRUMENTATIONKEY"
"value": "<your-own>"
- "name": "FUNCTIONS_EXTENSION_VERSION"
"value": "~2"
- "name": "AzureWebJobsDashboard"
"value": "<your-own>"
- "name": "AzureWebJobsStorage"
"value": "<your-own>"
- "name": "DocDbApiKey"
"value": "<your-own>"
- "name": "DocDbEndpointUri"
"value": "<your-own>"
- "name": "DocDbRideShareDatabaseName"
"value": "RideShare"
- "name": "DocDbRideShareMainCollectionName"
"value": "Main"
- "name": "DocDbThroughput"
"value": "400"
- "name": "InsightsInstrumentationKey"
"value": "<your-own>"
- "name": "IsRunningInContainer"
"value": "true"
- "name": "IsPersistDirectly"
"value": "true"
- "name": "GraphTenantId"
"value": "<your-own>"
- "name": "GraphClientSecret"
"value": "<your-own>"
- "name": "AuthorityUrl"
"value": "https://relecloudrideshare.b2clogin.com/tfp/relecloudrideshare.onmicrosoft.com/b2c_1_default-signin/v2.0"
- "name": "ApiApplicationId"
"value": "<your-own>"
- "name": "ApiScopeName"
"value": "rideshare"
- "name": "EnableAuth"
"value": "false"
---
apiVersion: v1
kind: Service
metadata:
name: rideshare-passengers
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: rideshare-passengers
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: rideshare-orchestrators
spec:
replicas: 1
template:
metadata:
labels:
app: rideshare-orchestrators
spec:
containers:
- name: rideshare-orchestrators
image: khaledhikmat/rideshare-orchestrators:v1
ports:
- containerPort: 80
env:
- "name": "APPINSIGHTS_INSTRUMENTATIONKEY"
"value": "<your-own>"
- "name": "FUNCTIONS_EXTENSION_VERSION"
"value": "~2"
- "name": "AzureWebJobsDashboard"
"value": "<your-own>"
- "name": "AzureWebJobsStorage"
"value": "<your-own>"
- "name": "DocDbApiKey"
"value": "<your-own>"
- "name": "DocDbEndpointUri"
"value": "<your-own>"
- "name": "DocDbRideShareDatabaseName"
"value": "RideShare"
- "name": "DocDbRideShareMainCollectionName"
"value": "Main"
- "name": "DocDbThroughput"
"value": "400"
- "name": "DriversAcknowledgeMaxWaitPeriodInSeconds"
"value": "120"
- "name": "DriversLocationRadiusInMiles"
"value": "15"
- "name": "TripMonitorIntervalInSeconds"
"value": "10"
- "name": "TripMonitorMaxIterations"
"value": "20"
- "name": "InsightsInstrumentationKey"
"value": "<your-own>"
- "name": "IsRunningInContainer"
"value": "true"
- "name": "IsPersistDirectly"
"value": "true"
- "name": "TripManagersQueue"
"value": "trip-managers"
- "name": "TripMonitorsQueue"
"value": "trip-monitors"
- "name": "TripDemosQueue"
"value": "trip-demos"
- "name": "TripExternalizationsEventGridTopicUrl"
"value": "<your-own>"
- "name": "TripExternalizationsEventGridTopicApiKey"
"value": "<your-own>"
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: rideshare-trips
spec:
replicas: 1
template:
metadata:
labels:
app: rideshare-trips
spec:
containers:
- name: rideshare-trips
image: khaledhikmat/rideshare-trips:v1
ports:
- containerPort: 80
env:
- "name": "APPINSIGHTS_INSTRUMENTATIONKEY"
"value": "<your-own>"
- "name": "FUNCTIONS_EXTENSION_VERSION"
"value": "~2"
- "name": "AzureWebJobsDashboard"
"value": "<your-own>"
- "name": "AzureWebJobsStorage"
"value": "<your-own>"
- "name": "DocDbApiKey"
"value": "<your-own>"
- "name": "DocDbEndpointUri"
"value": "<your-own>"
- "name": "DocDbRideShareDatabaseName"
"value": "RideShare"
- "name": "DocDbRideShareMainCollectionName"
"value": "Main"
- "name": "DocDbThroughput"
"value": "400"
- "name": "SqlConnectionString"
"value": "<your-own>"
- "name": "AzureSignalRConnectionString"
"value": "<your-own>"
- "name": "InsightsInstrumentationKey"
"value": "<your-own>"
- "name": "IsRunningInContainer"
"value": "true"
- "name": "IsEnqueueToOrchestrators"
"value": "true"
- "name": "IsPersistDirectly"
"value": "true"
- "name": "TripManagersQueue"
"value": "trip-managers"
- "name": "TripMonitorsQueue"
"value": "trip-monitors"
- "name": "TripDemosQueue"
"value": "trip-demos"
- "name": "AuthorityUrl"
"value": "https://relecloudrideshare.b2clogin.com/tfp/relecloudrideshare.onmicrosoft.com/b2c_1_default-signin/v2.0"
- "name": "ApiApplicationId"
"value": "<your-own>"
- "name": "ApiScopeName"
"value": "rideshare"
- "name": "EnableAuth"
"value": "false"
---
apiVersion: v1
kind: Service
metadata:
name: rideshare-trips
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: rideshare-trips
Please note that the rideshare-orchestrators
deployment does not have an associated service. This is because the orchestrators
does not need to be exposed to the Internet.
Alternatively, please refer to the following post to see how to run Functions in Kubernetes with AKS: https://medium.com/@asavaritayal/azure-functions-on-kubernetes-75486225dac0