Azure Spring Apps Enterprise enables you to easily run Spring Boot and polyglot applications on Azure.
This quickstart shows you how to deploy existing applications written in Java, Python, and C# to Azure. When you're finished, you can continue to manage the application via the Azure CLI or switch to using the Azure Portal.
- Deploy Applications to Azure Spring Apps
- What will you experience
- What you will need
- Install the Azure CLI extension
- Clone the repo
- Unit 0 - Prepare Environment
- Unit 1 - Deploy and Build Applications
- Unit 2 - Configure Single Sign On
- Unit 3 - Integrate with Azure Database for PostgreSQL and Azure Cache for Redis
- Unit 4 - Securely Load Application Secrets
- Unit 5 - Monitor End-to-End
- Unit 6 - Set Request Rate Limits
- Unit 7 - Automate from idea to production
- Unit 8 - Infuse AI into Fitness Store
You will:
- Provision an Azure Spring Apps service instance.
- Configure Application Configuration Service repositories
- Deploy polyglot applications to Azure and build using Tanzu Build Service
- Configure routing to the applications using Spring Cloud Gateway
- Open the application
- Explore the application API with Api Portal
- Configure Single Sign On (SSO) for the application
- Monitor applications
- Automate provisioning and deployments using GitHub Actions
The following diagram shows the architecture of the ACME Fitness Store that will be used for this guide:
This application is composed of several services:
-
4 Java Spring Boot applications:
- A catalog service for fetching available products. This application will use Microsoft Entra ID (formerly Azure Active Directory) authentication to connect to PostgreSQL
- A payment service for processing and approving payments for users' orders
- An identity service for referencing the authenticated user
- An assist service for infusing AI into fitness store
-
1 Python application:
- A cart service for managing a users' items that have been selected for purchase
-
1 ASP.NET Core applications:
- An order service for placing orders to buy products that are in the users' carts
-
1 NodeJS and static HTML Application
- A frontend shopping application
In order to deploy a Java app to cloud, you need an Azure subscription. If you do not already have an Azure subscription, you can activate your MSDN subscriber benefits or sign up for a free Azure account.
In addition, you will need the following:
| Azure CLI version 2.50.0 or higher
| Git
| jq
utility
|
Note - On Windows, the jq
utility should be renamed from jq-win64.exe
to jq.exe
and added to the PATH
Note - The Bash shell. While Azure CLI should behave identically on all environments, shell semantics vary. Therefore, only bash can be used with the commands in this repo. To complete these repo steps on Windows, use Git Bash that accompanies the Windows distribution of Git. Use only Git Bash to complete this training on Windows. Do not use WSL.
Or, you can use the Azure Cloud Shell. Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use the Bash with Cloud Shell to work with Azure services. You can use the Cloud Shell pre-installed commands to run the code in this README without having to install anything on your local environment. To start Azure Cloud Shell: go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser.
To run the code in this article in Azure Cloud Shell:
-
Start Cloud Shell.
-
Select the Copy button on a code block to copy the code.
-
Paste the code into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux or by selecting Cmd+Shift+V on macOS.
-
Select Enter to run the code.
Install the Azure Spring Apps extension for the Azure CLI using the following command
az extension add --name spring
Note - spring
CLI extension 1.14.0
or later is a pre-requisite to enable the
latest Enterprise plan functionality to configure VMware Tanzu Components. Use the following
command to remove previous versions and install the latest Enterprise plan extension:
If the extension is already installed, update it with the following command
az extension update --name spring
Note - In some cases, the update command may fail and it will be necessary to reinstall the Azure Spring Apps extension. Use the following command to remove previous versions and install the latest Azure Spring Apps extension:
az extension remove --name spring-cloud
az extension remove --name spring
az extension add --name spring
If spring
's version still < 1.14.0
after above commands, you can try to re-install Azure CLI.
mkdir source-code
cd source-code
git clone https://github.com/Azure-Samples/acme-fitness-store
cd acme-fitness-store/azure-spring-apps-enterprise
Login to the Azure CLI and choose your active subscription.
az login
az account list -o table
from this table, please make note of the SubscriptionId you would like to use
Make sure you are operating from the ./scripts folder.
cd scripts
Then:
pwd
Should show something like:
./source-code/acme-fitness-store/azure-spring-apps-enterprise/scripts
Create a bash script with environment variables by making a copy of the supplied template:
cp ./setup-env-variables-template.sh ./setup-env-variables.sh -i
Using an editor of your choice, edit the file, (for the purposes of example we will use the nano editor), and add the following values.
nano `setup-env-variables.sh`
Enter the following information:
export SUBSCRIPTION=subscription-id # replace it with your subscription-id from above
export RESOURCE_GROUP=resource-group-name # existing resource group or one that will be created in next steps
export SPRING_APPS_SERVICE=azure-spring-apps-name # name of the service that will be created in the next steps
export LOG_ANALYTICS_WORKSPACE=log-analytics-name # existing workspace or one that will be created in next steps
export REGION=region-name # choose a region with Enterprise plan support
The REGION value should be one of available regions for Azure Spring Apps (e.g. eastus). Please visit here for all available regions for Azure Spring Apps.
Then, set the environment:
source ./setup-env-variables.sh
Then set the subscription (the script above does this for you as well)
az account set --subscription ${SUBSCRIPTION}
Note: Though-out this exercise, there are multiple times where you will create a script from a template, fill in your values, then 'source' that script. If you have to step way or your session disconnects/expires before you complete the workshop, you will need to re-run these scripts (ie 're-source') after you 'az login' again, so that you can set the environment variables again. There is a (One) 'cumulative' script that you can run, which will then call the other setup scripts(4), as long as you follow our naming below:
./setup-env-variables.sh
./setup-sso-variables-ad.sh
./setup-sso-variables.sh
./setup-db-env-variables.sh
./setup-ai-env-variables.sh
This cumulative script can be found at:
./scripts/setup-env-variables-ALL.sh
Prepare a name for your Azure Spring Apps service. The name must be between 4 and 32 characters long and can contain only lowercase letters, numbers, and hyphens. The first character of the service name must be a letter and the last character must be either a letter or a number.
Create a resource group to contain your Azure Spring Apps service.
Note: This step can be skipped if using an existing resource group
az group create --name ${RESOURCE_GROUP} \
--location ${REGION}
Accept the legal terms and privacy statements for the Enterprise plan.
Note: This step is necessary only if your subscription has never been used to create an Enterprise plan instance of Azure Spring Apps.
az provider register --namespace Microsoft.SaaS
az term accept --publisher vmware-inc --product azure-spring-cloud-vmware-tanzu-2 --plan asa-ent-hr-mtr
Create an instance of Azure Spring Apps Enterprise.
az spring create --name ${SPRING_APPS_SERVICE} \
--resource-group ${RESOURCE_GROUP} \
--location ${REGION} \
--sku Enterprise \
--enable-application-configuration-service \
--enable-service-registry \
--enable-gateway \
--enable-api-portal \
--enable-alv \
--enable-app-acc \
--build-pool-size S2
Note: If the
create
command fails, try updating the Azure Spring Apps extension described here
Note: The service instance will take around 10-15 minutes to deploy.
Set your default resource group name and cluster name using the following commands:
az configure --defaults \
group=${RESOURCE_GROUP} \
location=${REGION} \
spring=${SPRING_APPS_SERVICE}
Create a Log Analytics Workspace to be used for your Azure Spring Apps service.
Note: This step can be skipped if using an existing workspace
az monitor log-analytics workspace create \
--workspace-name ${LOG_ANALYTICS_WORKSPACE} \
--location ${REGION} \
--resource-group ${RESOURCE_GROUP}
Retrieve the resource ID for the recently create Azure Spring Apps Service and Log Analytics Workspace:
export LOG_ANALYTICS_RESOURCE_ID=$(az monitor log-analytics workspace show \
--resource-group ${RESOURCE_GROUP} \
--workspace-name ${LOG_ANALYTICS_WORKSPACE} \
--query id \
-o tsv)
export SPRING_APPS_RESOURCE_ID=$(az spring show \
--name ${SPRING_APPS_SERVICE} \
--resource-group ${RESOURCE_GROUP} \
--query id \
-o tsv)
Configure diagnostic settings for the Azure Spring Apps Service:
az monitor diagnostic-settings create --name "send-logs-and-metrics-to-log-analytics" \
--resource ${SPRING_APPS_RESOURCE_ID} \
--workspace ${LOG_ANALYTICS_RESOURCE_ID} \
--logs '[
{
"category": "ApplicationConsole",
"enabled": true,
"retentionPolicy": {
"enabled": false,
"days": 0
}
},
{
"category": "SystemLogs",
"enabled": true,
"retentionPolicy": {
"enabled": false,
"days": 0
}
},
{
"category": "IngressLogs",
"enabled": true,
"retentionPolicy": {
"enabled": false,
"days": 0
}
}
]' \
--metrics '[
{
"category": "AllMetrics",
"enabled": true,
"retentionPolicy": {
"enabled": false,
"days": 0
}
}
]'
Note: For Git Bash users, this command may fail when resource IDs are misinterpreted as file paths because they begin with
/
.If the above command fails, try setting MSYS_NO_PATHCONV using:
export MSYS_NO_PATHCONV=1
Create a configuration repository for Application Configuration Service using the Azure CLI:
az spring application-configuration-service git repo add --name acme-fitness-store-config \
--label main \
--patterns "catalog/default,catalog/key-vault,identity/default,identity/key-vault,payment/default" \
--uri "https://github.com/Azure-Samples/acme-fitness-store-config"
Make sure you are operating from the ./scripts folder
pwd
Should show something like:
./source-code/acme-fitness-store/azure-spring-apps-enterprise/scripts
Create a custom builder in Tanzu Build Service using the Azure CLI:
az spring build-service builder create -n ${CUSTOM_BUILDER} \
--builder-file ../resources/json/tbs/builder.json \
--no-wait
Prerequisites:
- Completion of Unit 0 - Prepare Environment
Create an application for each service:
az spring app create --name ${CART_SERVICE_APP} --instance-count 1 --memory 1Gi &
az spring app create --name ${ORDER_SERVICE_APP} --instance-count 1 --memory 1Gi &
az spring app create --name ${PAYMENT_SERVICE_APP} --instance-count 1 --memory 1Gi &
az spring app create --name ${CATALOG_SERVICE_APP} --instance-count 1 --memory 1Gi &
Then, create an app for the Front End:
az spring app create --name ${FRONTEND_APP} --instance-count 1 --memory 1Gi
At this time, wait until control is passed back to your console before proceeding.
Please check the Portal to make sure ALL services (4 Services & Frontend App) are created. Should look something like:
Several applications require configuration from Application Configuration Service, so create the bindings:
az spring application-configuration-service bind --app ${PAYMENT_SERVICE_APP}
az spring application-configuration-service bind --app ${CATALOG_SERVICE_APP}
Several application require service discovery using Service Registry, so create the bindings:
az spring service-registry bind --app ${PAYMENT_SERVICE_APP}
az spring service-registry bind --app ${CATALOG_SERVICE_APP}
Assign an endpoint and update the Spring Cloud Gateway configuration with API information:
az spring gateway update --assign-endpoint true
export GATEWAY_URL=$(az spring gateway show --query properties.url -o tsv)
az spring gateway update \
--api-description "Acme Fitness Store API" \
--api-title "Acme Fitness Store" \
--api-version "v1.0" \
--server-url "https://${GATEWAY_URL}" \
--allowed-origins "*" \
--no-wait
az spring gateway route-config create \
--name ${CART_SERVICE_APP} \
--app-name ${CART_SERVICE_APP} \
--routes-file ../resources/json/routes/cart-service.json
az spring gateway route-config create \
--name ${ORDER_SERVICE_APP} \
--app-name ${ORDER_SERVICE_APP} \
--routes-file ../resources/json/routes/order-service.json
az spring gateway route-config create \
--name ${CATALOG_SERVICE_APP} \
--app-name ${CATALOG_SERVICE_APP} \
--routes-file ../resources/json/routes/catalog-service.json
az spring gateway route-config create \
--name ${FRONTEND_APP} \
--app-name ${FRONTEND_APP} \
--routes-file ../resources/json/routes/frontend.json
Deploy and build each application, specifying its required parameters
# Deploy Payment Service
az spring app deploy --name ${PAYMENT_SERVICE_APP} \
--config-file-pattern payment/default \
--source-path ../../apps/acme-payment \
--build-env BP_JVM_VERSION=17
# Deploy Catalog Service
az spring app deploy --name ${CATALOG_SERVICE_APP} \
--config-file-pattern catalog/default \
--source-path ../../apps/acme-catalog \
--build-env BP_JVM_VERSION=17
# Deploy Order Service
az spring app deploy --name ${ORDER_SERVICE_APP} \
--builder ${CUSTOM_BUILDER} \
--source-path ../../apps/acme-order
# Deploy Cart Service
az spring app deploy --name ${CART_SERVICE_APP} \
--builder ${CUSTOM_BUILDER} \
--env "CART_PORT=8080" \
--source-path ../../apps/acme-cart
# Deploy Frontend App
az spring app deploy --name ${FRONTEND_APP} \
--builder ${CUSTOM_BUILDER} \
--source-path ../../apps/acme-shopping
Note: Deploying all applications will take 5-10 minutes
Retrieve the URL for Spring Cloud Gateway and open it in a browser:
open "https://${GATEWAY_URL}"
If using Azure Cloud Shell or Windows, open the output from the following command in a browser:
echo "https://${GATEWAY_URL}"
You should see the ACME Fitness Store Application:
Explore the application, but notice that not everything is functioning yet. Continue on to Unit 2 to configure Single Sign On to enable the rest of the functionality.
Assign an endpoint to API Portal and open it in a browser:
az spring api-portal update --assign-endpoint true
export PORTAL_URL=$(az spring api-portal show --query properties.url -o tsv)
open "https://${PORTAL_URL}"
If using Azure Cloud Shell or Windows, open the output from the following command in a browser:
echo "https://${PORTAL_URL}"
In this Unit, you will configure Single Sign-On for Spring Cloud Gateway using Azure Active Directory or an existing Identity Provider.
Prerequisites:
- Completion of Unit 1 - Deploy and Build Applications
- Permissions to manage Azure Active Directory Application Registrations or Credentials for a Single Sign-On Identity Provider.
Note: This Unit is optional. The application will continue to function without completing this unit. Certain features will remain unavailable including: log in, adding items to the cart, or completing an order. Continue on to Unit 3 - Integrate with Azure Database for PostgreSQL and Azure Cache For Redis to continue this guide without configuring SSO.
The following section steps through creating a Single Sign On Provider using Microsoft Entra ID. To use an existing provider, skip ahead to Using an Existing Identity Provider
Choose a unique display name for your Application Registration.
export AD_DISPLAY_NAME=change-me # unique application display name
az ad app create --display-name ${AD_DISPLAY_NAME} > ../resources/json/ad.json
export APPLICATION_ID=$(cat ../resources/json/ad.json | jq -r '.appId')
az ad app credential reset --id ${APPLICATION_ID} --append > ../resources/json/sso.json
az ad sp create --id ${APPLICATION_ID}
More detailed instructions on Application Registrations can be found here
Set the environment using the provided script and verify the environment variables are set:
Source this file:
source ./setup-sso-variables-ad.sh
echo ${CLIENT_ID}
echo ${CLIENT_SECRET}
echo ${ISSUER_URI}
echo ${JWK_SET_URI}
echo ${GATEWAY_URL}
echo ${PORTAL_URL}
The ISSUER_URI
should take the form https://login.microsoftonline.com/${TENANT_ID}/v2.0
The JWK_SET_URI
should take the form https://login.microsoftonline.com/${TENANT_ID}/discovery/v2.0/keys
az ad app update --id ${APPLICATION_ID} \
--web-redirect-uris "https://${GATEWAY_URL}/login/oauth2/code/sso" "https://${PORTAL_URL}/oauth2-redirect.html" "https://${PORTAL_URL}/login/oauth2/code/sso"
Detailed information about redirect URIs can be found here.
Note: Continue on to Configure Spring Cloud Gateway with SSO if you just created an app registration in Microsoft Entra ID
To use an existing SSO Identity Provider, copy the existing template
Again, make sure you are operating from the ./scripts folder.
pwd
Should return something like:
...../source-code/acme-fitness-store/azure-spring-apps-enterprise/scripts
Next, make a copy of setup-sso-variables-template.sh for your custom values.
cp ./setup-sso-variables-template.sh ./setup-sso-variables.sh -i
Echo the following values:
echo ${CLIENT_ID}
echo ${CLIENT_SECRET}
echo ${ISSUER_URI}
echo ${JWK_SET_URI}
Edit the copy:
nano `setup-sso-variables.sh`
Add the required values.
export CLIENT_ID="change-me" # Your SSO Provider Client ID
export CLIENT_SECRET="change-me" # Your SSO Provider Client Secret
export ISSUER_URI="change-me" # Your SSO Provider Issuer URI
export JWK_SET_URI="change-me" # Your SSO Provider Json Web Token URI
The
issuer-uri
configuration should follow Spring Boot convention, as described in the official Spring Boot documentation: The provider needs to be configured with an issuer-uri which is the URI that the it asserts as its Issuer Identifier. For example, if the issuer-uri provided is "https://example.com", then an OpenID Provider Configuration Request will be made to "https://example.com/.well-known/openid-configuration". The result is expected to be an OpenID Provider Configuration Response. Note that only authorization servers supporting OpenID Connect Discovery protocol can be used
The
JWK_SET_URI
typically takes the form${ISSUER_URI}/$VERSION/keys
chmod +x setup-sso-variables.sh
source setup-sso-variables.sh
echo "https://${GATEWAY_URL}/login/oauth2/code/sso"
echo "https://${PORTAL_URL}/oauth2-redirect.html"
echo "https://${PORTAL_URL}/login/oauth2/code/sso"
az spring gateway update \
--client-id ${CLIENT_ID} \
--client-secret ${CLIENT_SECRET} \
--scope ${SCOPE} \
--issuer-uri ${ISSUER_URI} \
--no-wait
az spring app create --name ${IDENTITY_SERVICE_APP} --instance-count 1 --memory 1Gi
az spring application-configuration-service bind --app ${IDENTITY_SERVICE_APP}
az spring service-registry bind --app ${IDENTITY_SERVICE_APP}
az spring gateway route-config create \
--name ${IDENTITY_SERVICE_APP} \
--app-name ${IDENTITY_SERVICE_APP} \
--routes-file ../resources/json/routes/identity-service.json
az spring app deploy --name ${IDENTITY_SERVICE_APP} \
--env "JWK_URI=${JWK_SET_URI}" \
--config-file-pattern identity/default \
--source-path ../../apps/acme-identity \
--build-env BP_JVM_VERSION=17
Note: The application will take around 3-5 minutes to deploy.
Update the existing applications to use authorization information from Spring Cloud Gateway:
# Update the Cart Service
az spring app update --name ${CART_SERVICE_APP} \
--env "AUTH_URL=https://${GATEWAY_URL}" "CART_PORT=8080"
# Update the Order Service
az spring app update --name ${ORDER_SERVICE_APP} \
--env "AcmeServiceSettings__AuthUrl=https://${GATEWAY_URL}"
Retrieve the URL for Spring Cloud Gateway and open it in a browser:
open "https://${GATEWAY_URL}"
If using Azure Cloud Shell or Windows, open the output from the following command in a browser:
echo "https://${GATEWAY_URL}"
You should see the ACME Fitness Store Application, and be able to log in using your SSO Credentials. Once logged in, the remaining functionality of the application will be available. This includes adding items to the cart and placing an order.
Configure API Portal with SSO enabled:
export PORTAL_URL=$(az spring api-portal show --query properties.url -o tsv)
az spring api-portal update \
--client-id ${CLIENT_ID} \
--client-secret ${CLIENT_SECRET}\
--scope "openid,profile,email" \
--issuer-uri ${ISSUER_URI}
Open API Portal in a browser, this will redirect you to log in now:
open "https://${PORTAL_URL}"
If using Azure Cloud Shell or Windows, open the output from the following command in a browser:
echo "https://${PORTAL_URL}"
To access the protected APIs, click Authorize and follow the steps that match your SSO provider. Learn more about API Authorization with API Portal here
By default, several services use in-memory data storage. This unit will create persistent stores outside the applications and connect applications to those stores.
Prerequisites:
- Completion of Unit 1 - Deploy and Build Applications
Make sure you are operating from the ./scripts folder.
cd scripts
Then:
pwd
Should show something like:
./source-code/acme-fitness-store/azure-spring-apps-enterprise/scripts
cp ./setup-db-env-variables-template.sh ./setup-db-env-variables.sh -i
nano `./setup-db-env-variables.sh`
export AZURE_CACHE_NAME=change-me # Unique name for Azure Cache for Redis Instance
export POSTGRES_SERVER=change-me # Unique name for Azure Database for PostgreSQL Flexible Server
export POSTGRES_SERVER_USER=change-name # Postgres server username to be created in next steps
export POSTGRES_SERVER_PASSWORD=change-name # Postgres server password to be created in next steps
Note: AZURE_CACHE_NAME and POSTGRES_SERVER must be unique names to avoid DNS conflicts
chmod +x ./setup-db-env-variables.sh
source ./setup-db-env-variables.sh
Note: The redis cache will take around 15-20 minutes to deploy.
az redis create \
--name ${AZURE_CACHE_NAME} \
--location ${REGION} \
--resource-group ${RESOURCE_GROUP} \
--sku Basic \
--vm-size c0
Note: The PostgreSQL Flexible Server will take 5-10 minutes to deploy
az postgres flexible-server create --name ${POSTGRES_SERVER} \
--resource-group ${RESOURCE_GROUP} \
--location ${REGION} \
--admin-user ${POSTGRES_SERVER_USER} \
--admin-password ${POSTGRES_SERVER_PASSWORD} \
--public-access 0.0.0.0 \
--tier Burstable \
--sku-name Standard_B1ms \
--version 14 \
--storage-size 32 \
--yes
az postgres flexible-server firewall-rule create --rule-name allAzureIPs \
--name ${POSTGRES_SERVER} \
--resource-group ${RESOURCE_GROUP} \
--start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0
az postgres flexible-server parameter set \
--resource-group ${RESOURCE_GROUP} \
--server-name ${POSTGRES_SERVER} \
--name azure.extensions --value uuid-ossp
az postgres flexible-server db create \
--database-name ${ORDER_SERVICE_DB} \
--server-name ${POSTGRES_SERVER}
az postgres flexible-server db create \
--database-name ${CATALOG_SERVICE_DB} \
--server-name ${POSTGRES_SERVER}
Note: wait for all services to be ready before continuing
The Order Service and Catalog Service use Azure Database for Postgres create Service Connectors for those applications:
az spring connection create postgres-flexible \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--connection ${ORDER_SERVICE_DB_CONNECTION} \
--app ${ORDER_SERVICE_APP} \
--deployment default \
--tg ${RESOURCE_GROUP} \
--server ${POSTGRES_SERVER} \
--database ${ORDER_SERVICE_DB} \
--secret name=${POSTGRES_SERVER_USER} secret=${POSTGRES_SERVER_PASSWORD} \
--client-type dotnet
Catalog service uses Microsoft Entra authentication to connect to Postgres, so it is not required to include the password
az spring connection create postgres-flexible \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--connection ${CATALOG_SERVICE_DB_CONNECTION} \
--app ${CATALOG_SERVICE_APP} \
--deployment default \
--tg ${RESOURCE_GROUP} \
--server ${POSTGRES_SERVER} \
--database ${CATALOG_SERVICE_DB} \
--client-type springboot \
--system-identity
Note: When the above command is run on iOS, it will require: postgresql and a PostGres installed extension 'serviceconnector-passwordless' to be present. If you do not have these installed, they will be installed as a result of this command.
After executing above command, the Azure Spring App application enables System assigned managed identity, Postgres database user will be created and assigned to the managed identity and permissions will be granted to the user.
az spring connection create redis \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--connection ${CART_SERVICE_CACHE_CONNECTION} \
--app ${CART_SERVICE_APP} \
--deployment default \
--tg ${RESOURCE_GROUP} \
--server ${AZURE_CACHE_NAME} \
--database 0 \
--client-type python
Note: Currently, the Azure Spring Apps CLI extension only allows for client types of Java, springboot, or dotnet. The cart service uses a client connection type of Java because the connection strings are the same for python and Java. This will be changed when additional options become available in the CLI.
Next, update the affected applications to use the newly created databases and redis cache.
az spring app restart --name ${CATALOG_SERVICE_APP}
export POSTGRES_CONNECTION_STR=$(az spring connection show \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--deployment default \
--connection ${ORDER_SERVICE_DB_CONNECTION} \
--app ${ORDER_SERVICE_APP} \
--query configurations[0].value \
-o tsv)"Trust Server Certificate=true;"
az spring app update \
--name order-service \
--env "DatabaseProvider=Postgres" "ConnectionStrings__OrderContext=${POSTGRES_CONNECTION_STR}" "AcmeServiceSettings__AuthUrl=https://${GATEWAY_URL}"
export REDIS_CONN_STR=$(az spring connection show \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--deployment default \
--connection ${CART_SERVICE_CACHE_CONNECTION} \
--app ${CART_SERVICE_APP} \
--query configurations[0].value \
-o tsv)
az spring app update \
--name cart-service \
--env "CART_PORT=8080" "REDIS_CONNECTIONSTRING=${REDIS_CONN_STR}" "AUTH_URL=https://${GATEWAY_URL}"
Verify cart data is now persisted in Redis by adding a few items to your cart. Then, restart the cart service:
az spring app restart --name ${CART_SERVICE_APP}
Notice that after restarting the cart service, the items in your cart will now persist.
Verify order data is now persisted in a PostgreSQL Database by placing an order. View your placed orders with the following URL:
https://${GATEWAY_URL}/order/${USER_ID}
Your USER_ID is your username URL encoded.
az spring app restart --name ${ORDER_SERVICE_APP}
After restarting, revisit the URL for your placed orders and notice that they persisted.
In this unit, you will use Azure Key Vault to securely store and load secrets to connect to Azure services.
Prerequisites:
- Completion of Unit 1 - Deploy and Build Applications
- Completion of Unit 3 - Integrate with Azure Database for PostgreSQL and Azure Cache for Redis
Choose a unique name for your Key Vault and set an environment variable:
export KEY_VAULT=change-me # customize this
az keyvault create --name ${KEY_VAULT} -g ${RESOURCE_GROUP}
export KEYVAULT_URI=$(az keyvault show --name ${KEY_VAULT} \
--query properties.vaultUri \
-o tsv)
export POSTGRES_SERVER_FULL_NAME="${POSTGRES_SERVER}.postgres.database.azure.com"
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "POSTGRES-SERVER-NAME" --value ${POSTGRES_SERVER_FULL_NAME}
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "ConnectionStrings--OrderContext" --value "${POSTGRES_CONNECTION_STR}"
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "CATALOG-DATABASE-NAME" --value ${CATALOG_SERVICE_DB}
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "POSTGRES-LOGIN-NAME" --value ${POSTGRES_SERVER_USER}
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "POSTGRES-LOGIN-PASSWORD" --value ${POSTGRES_SERVER_PASSWORD}
export REDIS_HOST=$(az redis show -n ${AZURE_CACHE_NAME} --query hostName -o tsv)
export REDIS_PORT=$(az redis show -n ${AZURE_CACHE_NAME} --query sslPort -o tsv)
export REDIS_PRIMARY_KEY=$(az redis list-keys -n ${AZURE_CACHE_NAME} --query primaryKey -o tsv)
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "CART-REDIS-CONNECTION-STRING" --value "${REDIS_CONN_STR}"
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "SSO-PROVIDER-JWK-URI" --value ${JWK_SET_URI}
Note: Creating the SSO-PROVIDER-JWK-URI Secret can be skipped if not configuring Single Sign On
az spring app identity assign --name ${CART_SERVICE_APP} --system-assigned
export CART_SERVICE_APP_IDENTITY=$(az spring app show --name ${CART_SERVICE_APP} --query identity.principalId -o tsv)
az spring app identity assign --name ${ORDER_SERVICE_APP} --system-assigned
export ORDER_SERVICE_APP_IDENTITY=$(az spring app show --name ${ORDER_SERVICE_APP} --query identity.principalId -o tsv)
az spring app identity assign --name ${CATALOG_SERVICE_APP} --system-assigned
export CATALOG_SERVICE_APP_IDENTITY=$(az spring app show --name ${CATALOG_SERVICE_APP} --query identity.principalId -o tsv)
az spring app identity assign --name ${IDENTITY_SERVICE_APP} --system-assigned
export IDENTITY_SERVICE_APP_IDENTITY=$(az spring app show --name ${IDENTITY_SERVICE_APP} --query identity.principalId -o tsv)
az keyvault set-policy --name ${KEY_VAULT} \
--object-id ${CART_SERVICE_APP_IDENTITY} --secret-permissions get list
az keyvault set-policy --name ${KEY_VAULT} \
--object-id ${ORDER_SERVICE_APP_IDENTITY} --secret-permissions get list
az keyvault set-policy --name ${KEY_VAULT} \
--object-id ${CATALOG_SERVICE_APP_IDENTITY} --secret-permissions get list
az keyvault set-policy --name ${KEY_VAULT} \
--object-id ${IDENTITY_SERVICE_APP_IDENTITY} --secret-permissions get list
Note: Identity Service will not exist if you haven't completed Unit 2. Skip configuring an identity or policy for this service if not configuring Single Sign-On at this point.
az spring connection delete \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--connection ${ORDER_SERVICE_DB_CONNECTION} \
--app ${ORDER_SERVICE_APP} \
--deployment default \
--yes
az spring connection delete \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--connection ${CATALOG_SERVICE_DB_CONNECTION} \
--app ${CATALOG_SERVICE_APP} \
--deployment default \
--yes
az spring connection delete \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--connection ${CART_SERVICE_CACHE_CONNECTION} \
--app ${CART_SERVICE_APP} \
--deployment default \
--yes
az spring app update --name ${ORDER_SERVICE_APP} \
--env "ConnectionStrings__KeyVaultUri=${KEYVAULT_URI}" "AcmeServiceSettings__AuthUrl=https://${GATEWAY_URL}" "DatabaseProvider=Postgres"
az spring app update --name ${CATALOG_SERVICE_APP} \
--config-file-pattern catalog/default,catalog/key-vault \
--env "SPRING_CLOUD_AZURE_KEYVAULT_SECRET_PROPERTY_SOURCES_0_ENDPOINT=${KEYVAULT_URI}" "SPRING_CLOUD_AZURE_KEYVAULT_SECRET_PROPERTY_SOURCES_0_NAME=${KEY_VAULT}" "SPRING_PROFILES_ACTIVE=default,key-vault"
az spring app update --name ${IDENTITY_SERVICE_APP} \
--config-file-pattern identity/default,identity/key-vault \
--env "SPRING_CLOUD_AZURE_KEYVAULT_SECRET_PROPERTY_SOURCES_0_ENDPOINT=${KEYVAULT_URI}" "SPRING_CLOUD_AZURE_KEYVAULT_SECRET_PROPERTY_SOURCES_0_NAME=${KEY_VAULT}" "SPRING_PROFILES_ACTIVE=default,key-vault"
az spring app update --name ${CART_SERVICE_APP} \
--env "CART_PORT=8080" "KEYVAULT_URI=${KEYVAULT_URI}" "AUTH_URL=https://${GATEWAY_URL}"
In this unit you will explore live application metrics and query logs to know the health of your applications.
Prerequisites:
- Completion of Unit 1 - Deploy and Build Applications
- Completion of Unit 3 - Integrate with Azure Database for PostgreSQL and Azure Cache for Redis
- Completion of Unit 4 - Securely Load Application Secrets
The Application Insights Instrumentation Key must be provided for the non-Java applications.
Note: In future iterations, the buildpacks for non-Java applications will support Application Insights binding and this step will be unnecessary.
Retrieve the Instrumentation Key for Application Insights and add to Key Vault
export INSTRUMENTATION_KEY=$(az monitor app-insights component show --app ${APPLICATION_INSIGHTS} | jq -r '.connectionString')
az keyvault secret set --vault-name ${KEY_VAULT} \
--name "ApplicationInsights--ConnectionString" --value ${INSTRUMENTATION_KEY}
Increase the sampling rate for the Application Insights binding.
az spring build-service builder buildpack-binding set \
--builder-name default \
--name default \
--type ApplicationInsights \
--properties sampling-rate=100 connection_string=${INSTRUMENTATION_KEY}
Restart applications to reload configuration. For the Java applications, this will allow the new sampling rate to take effect. For the non-Java applications, this will allow them to access the Instrumentation Key from Key Vault.
az spring app restart -n ${CART_SERVICE_APP}
az spring app restart -n ${ORDER_SERVICE_APP}
az spring app restart -n ${IDENTITY_SERVICE_APP}
az spring app restart -n ${CATALOG_SERVICE_APP}
az spring app restart -n ${PAYMENT_SERVICE_APP}
Use the following command to get the latest 100 lines of app console logs from the Catalog Service.
az spring app logs \
-n ${CATALOG_SERVICE_APP} \
--lines 100
By adding the -f
parameter you can get real-time log streaming from an app. Try log streaming for the Catalog Service.
az spring app logs \
-n ${CATALOG_SERVICE_APP} \
-f
You can use az spring app logs -h
to explore more parameters and log stream functionalities.
Use the ACME Fitness Shop Application to generate some traffic. Move throughout the application, view the catalog, or place an order.
To continuously generate traffic, use the traffic generator:
cd traffic-generator
GATEWAY_URL=https://${GATEWAY_URL} ./gradlew gatlingRun-com.vmware.acme.simulation.GuestSimulation
cd -
Continue on to the next sections while the traffic generator runs.
Open the Application Insights created by Azure Spring Apps and start monitoring Spring Boot applications. You can find the Application Insights in the same Resource Group where you created an Azure Spring Apps service instance.
You can see the performance number for dependencies, particularly SQL calls:
You can see the performance metrics for individual instances or roles:
Click on a SQL call to see the end-to-end transaction in context:
You can see a collection of exceptions:
You can see metrics contributed by Spring Boot apps,
Spring Cloud modules, and dependencies.
The chart below shows http_server_requests
and Heap Memory Used
.
Spring Boot registers a lot number of core metrics: JVM, CPU, Tomcat, Logback... The Spring Boot auto-configuration enables the instrumentation of requests handled by Spring MVC.
The REST controllers ProductController
, and PaymentController
have been instrumented by the @Timed
Micrometer annotation at class level.
acme-catalog
application has the following custom metrics enabled:- @Timed:
store.products
- @Timed:
acem-payment
application has the following custom metrics enabled:- @Timed:
store.payment
- @Timed:
You can see live metrics on screen with low latencies < 1 second:
Open the Log Analytics that you created - you can find the Log Analytics in the same Resource Group where you created an Azure Spring Apps service instance.
In the Log Analytics page, selects Logs
blade and run any of the sample queries supplied below
for Azure Spring Apps.
AppPlatformLogsforSpring
| where TimeGenerated > ago(24h)
| limit 500
| sort by TimeGenerated
| project TimeGenerated, AppName, Log
AppPlatformLogsforSpring
| where AppName has "catalog-service"
| limit 500
| sort by TimeGenerated
| project TimeGenerated, AppName, Log
AppPlatformLogsforSpring
| where Log contains "error" or Log contains "exception"
| extend FullAppName = strcat(ServiceName, "/", AppName)
| summarize count_per_app = count() by FullAppName, ServiceName, AppName, _ResourceId
| sort by count_per_app desc
| render piechart
AppPlatformIngressLogs
| project TimeGenerated, RemoteAddr, Host, Request, Status, BodyBytesSent, RequestTime, ReqId, RequestHeaders
| sort by TimeGenerated
Type and run the following Kusto query to see all the logs from Spring Cloud Gateway managed by Azure Spring Apps:
AppPlatformSystemLogs
| where LogType contains "SpringCloudGateway"
| project TimeGenerated,Log
Type and run the following Kusto query to see all the logs from Spring Cloud Service Registry managed by Azure Spring Apps:
AppPlatformSystemLogs
| where LogType contains "ServiceRegistry"
| project TimeGenerated, Log
In this unit you will use Spring Cloud Gateway filters to apply rate limiting to your API.
Prerequisites:
- Completion of Unit 1 - Deploy and Build Applications
Spring Cloud Gateway includes route filters from the Open Source version as well as several additional route filters. One of these additional filters is the RateLimit: Limiting user requests filter. The RateLimit filter limits the number of requests allowed per route during a time window.
When defining a Route, you can add the RateLimit filter by including it in the list of filters for the route. The filter accepts 4 options:
- Number of requests accepted during the window.
- Duration of the window: by default milliseconds, but you can use s, m or h suffix to specify it in seconds, minutes or hours.
- (Optional) User partition key: it's also possible to apply rate limiting per user, that is, different users can have its own throughput allowed based on an identifier found in the request. Set whether the key is in a JWT claim or HTTP header with '' or '' syntax.
- (Optional) It is possible to rate limit by IP addresses. Note, this cannot be combined with the rate limiting per user.
{
"predicates": [
"Path=/products",
"Method=GET"
],
"filters": [
"StripPrefix=0",
"RateLimit=2,5s"
]
}
When the limit is exceeded, response will fail with 429 Too Many Requests
status.
Apply the RateLimit
filter to the /products
route using the following command:
az spring gateway route-config update \
--name ${CATALOG_SERVICE_APP} \
--app-name ${CATALOG_SERVICE_APP} \
--routes-file azure/routes/catalog-service_rate-limit.json
Retrieve the URL for the /products
route in Spring Cloud Gateway using the following command:
GATEWAY_URL=$(az spring gateway show | jq -r '.properties.url')
echo "https://${GATEWAY_URL}/products"
Make several requests to the URL for /products
within a five second period to see requests fail with a status 429 Too Many Requests
.
Application Live View for VMware Tanzu is a lightweight insights and troubleshooting tool that helps app developers and app operators look inside running apps.
Application Live View only supports Spring Boot applications.
az spring dev-tool update \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} \
--assign-endpoint
export SPRING_DEV_TOOL=$(az spring dev-tool show \
--resource-group ${RESOURCE_GROUP} \
--service ${SPRING_APPS_SERVICE} | jq -r '.properties.url')
open "https://${SPRING_DEV_TOOL}/app-live-view"
If using Azure Cloud Shell or Windows, open the output from the following command in a browser:
echo "https://${SPRING_DEV_TOOL}/app-live-view"
Click on any of the running applications and select the desired instance to view the real time metrics.
To navigate to the Health page, select the Health option from the Information Category drop-down.
To navigate to the Memory page, select the Memory option from the Information Category drop-down.
To navigate to the Threads page, select the Threads option from the Information Category drop-down.
To get started with deploying this sample app from GitHub Actions, please:
- Complete an App registration in Microsoft Entra ID outlined here or have SSO Credentials prepared as described here
- Fork this repository and turn on GitHub Actions in your fork
Now you will create a Storage Account for maintaining terraform state as part of GitHub Actions.
Make sure you are operating from the ./scripts folder.
cd scripts
Then:
pwd
Should show something like:
./source-code/acme-fitness-store/azure-spring-apps-enterprise/scripts
cp ./setup-storage-env-variables-template.sh ./setup-storage-env-variables.sh -i
Using an editor of your choice, edit the file, (for the purposes of example we will use the nano editor), and add the following values.
nano `setup-storage-env-variables.sh`
Enter the following information:
export STORAGE_RESOURCE_GROUP='change-me' # different resource group from previous steps
export STORAGE_ACCOUNT_NAME='change-me' # choose a name for your storage account
Then, set the environment:
source ./setup-storage-env-variables.sh
az group create \
--name ${STORAGE_RESOURCE_GROUP} \
--location ${REGION}
az storage account create \
--name ${STORAGE_ACCOUNT_NAME} \
--resource-group ${STORAGE_RESOURCE_GROUP} \
--location ${REGION} \
--sku Standard_RAGRS \
--kind StorageV2
az storage container create \
--name terraform-state-container \
--account-name ${STORAGE_ACCOUNT_NAME} \
--auth-mode login
Make the name of the service principle something you will recognize.
az ad sp create-for-rbac --name "change-me" \
--role contributor \
--scopes /subscriptions/${SUBSCRIPTION} \
--sdk-auth
{
"clientId": "<GUID>",
"clientSecret": "<GUID>",
"subscriptionId": "<GUID>",
"tenantId": "<GUID>",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
This output will be needed as a secret value for the next step. Save this off to a file, in a secure location that you can reference later.
Detailed instructions for adding secrets to GitHub Actions can be found here.
Add the following secrets (8x) to GitHub Actions:
AZURE_CREDENTIALS
TF_PROJECT_NAME
This is also available as an Environment Variable.
AZURE_LOCATION
Defined in Unit 2, also available as an Environment Variable.
OIDC_JWK_SET_URI
Defined in Unit 2, also available as an Environment Variable.
OIDC_CLIENT_ID
Defined in Unit 2, also available as an Environment Variable.
OIDC_CLIENT_SECRET
Defined in Unit 2, also available as an Environment Variable.
OIDC_ISSUER_URI
Using the key & value from the blocks below, replace the values STORAGE_ACCOUNT_NAME and STORAGE_RESOURCE_GROUP with the current values from your environment variables.
Secret Key:
TF_BACKEND_CONFIG
Secret Value:
resource_group_name = "${STORAGE_RESOURCE_GROUP}"
storage_account_name = "${STORAGE_ACCOUNT_NAME}"
container_name = "terraform-state-container"
key = "dev.terraform.tfstate"
When Completed, you should see something like (8 Secrets):
Now you can run GitHub Actions in your repository. The provision
workflow will provision all resources created in the first four units. An example run is seen below:
Note: The entire provision workflow will run in approximately 60 minutes.
Each application has a Deploy
workflow that will redeploy the application when changes are made to that application. An example output from the catalog service is seen below:
The cleanup
workflow can be manually run to delete all resources created by the provision
workflow. The output can be seen below:
- JDK 17
- Python 3
- Maven
- Azure CLI
- An Azure subscription with access granted to Azure OpenAI (request access to Azure OpenAI here)
-
Please navigate to the root folder of this cloned repository.
-
Copy the AI environment variables template file, e.g.
cp azure-spring-apps-enterprise/scripts/setup-ai-env-variables-template.sh azure-spring-apps-enterprise/scripts/setup-ai-env-variables.sh -i
-
Update the values in
azure-spring-apps-enterprise/scripts/setup-ai-env-variables.sh
with your own values, as configured in Azure OpenAI instance:
- Name, e.g.
my-ai
- Endpoint, e.g.
https://my-ai.openai.azure.com
- Chat deployment ID, e.g. `gpt-35-turbo-16k``
- Embedding deployment ID, e.g. `text-embedding-ada-002``
- OpenAI API Key, to be updated once you create AI instance (in next step)
-
Run the following command to create an Azure OpenAI resource in the the resource group.
source ./azure-spring-apps-enterprise/scripts/setup-env-variables.sh export OPENAI_RESOURCE_NAME=<choose-a-resource-name> az cognitiveservices account create \ -n ${OPENAI_RESOURCE_NAME} \ -g ${RESOURCE_GROUP} \ -l eastus \ --kind OpenAI \ --sku s0 \ --custom-domain ${OPENAI_RESOURCE_NAME}
You can check the resource has been created in Azure Portal under
Azure AI Services
, e.g. -
Create the model deployments for
text-embedding-ada-002
andgpt-35-turbo-16k
in your Azure OpenAI service.az cognitiveservices account deployment create \ -g ${RESOURCE_GROUP} \ -n ${OPENAI_RESOURCE_NAME} \ --deployment-name text-embedding-ada-002 \ --model-name text-embedding-ada-002 \ --model-version "2" \ --model-format OpenAI \ --scale-type "Standard" az cognitiveservices account deployment create \ -g ${RESOURCE_GROUP} \ -n ${OPENAI_RESOURCE_NAME} \ --deployment-name gpt-35-turbo-16k \ --model-name gpt-35-turbo-16k \ --model-version "0613" \ --model-format OpenAI \ --scale-type "Standard"
This step could also be done in
Azure AI Studio
. You can go toAzure AI Studio
by going toDeployments
in your Open AI service and clicking onManage Deployments
button.Alternatively, you can click go to the link, e.g. https://oai.azure.com/
-
Update the values in
azure-spring-apps-enterprise/scripts/setup-ai-env-variables.sh
, e.g.
Note: You can get the endpoint by querying the
cognitiveservices
from Azure CLI, e.g.
az cognitiveservices account show \
--name ${OPENAI_RESOURCE_NAME} \
--resource-group ${RESOURCE_GROUP} \
--output json | jq -r '.properties.endpoint'
Before building the assist-service
service, we need to preprocess the data into the vector store. The vector store is a file that contains the vector representation of each product description. There's already a pre-built file vector_store.json
in the repo so you can skip this step. If you want to build the vector store yourself, please run the following commands:
source ./azure-spring-apps-enterprise/scripts/setup-ai-env-variables.sh
cd apps/acme-assist
./preprocess.sh data/bikes.json,data/accessories.json src/main/resources/vector_store.json
-
Configure AI environment variables, e.g.
source ./azure-spring-apps-enterprise/scripts/setup-ai-env-variables.sh
-
Create the new AI service
assist-service
, e.g.az spring app create --name ${AI_APP} --instance-count 1 --memory 1Gi
-
Configure Spring Cloud Gateway with the
assist-service
routes, e.g.az spring gateway route-config create \ --name ${AI_APP} \ --app-name ${AI_APP} \ --routes-file azure-spring-apps-enterprise/resources/json/routes/assist-service.json
-
Deploy the application, e.g.
az spring app deploy --name ${AI_APP} \ --source-path apps/acme-assist \ --build-env BP_JVM_VERSION=17 \ --env \ SPRING_AI_AZURE_OPENAI_ENDPOINT=${SPRING_AI_AZURE_OPENAI_ENDPOINT} \ SPRING_AI_AZURE_OPENAI_API_KEY=${SPRING_AI_AZURE_OPENAI_API_KEY}
-
Test the
acme-fitness
application in the browser again. Go toASK TO FITASSIST
and converse with the assistant, e.g.I need a bike for a commute to work.
-
Observe the output that was generated by the Assist application, e.g.
In this quickstart, you've deployed polyglot applications to Azure Spring Apps using Azure CLI. You also configured VMware Tanzu components in the enterprise plan. To learn more about Azure Spring Apps or VMware Tanzu components, go to: