Nightly Deploys
This page describes how we deploy the SimpleApp, HelloWorld App and the Demo App each night, to a Azure App Container Apps (a PaaS based on top of k8s).
The actual CI jobs can be found in the apache-causeway-committers/causeway-nightly-deploys repo.
Azure Container Apps
Following azure docs for ACA
Authenticate
Authenticate and prepare working environment
-
login
az login
Confirm using web browser.
-
add ACA extension
az extension add --name containerapp --upgrade
This takes a minute or two.
-
register the
Microsoft.OperationalInsights
provider for the Azure Monitor Log Analytics workspaceaz provider register --namespace Microsoft.OperationalInsights
(as have not used it before).
Setup the resource group and ACA environment
In Azure, all resources live in resource groups. We will use a single resource group.
One of the resources to be set up is an ACA environment, which creates a secure boundary around a group of container apps. Container Apps deployed to the same environment are deployed in the same virtual network and write logs to the same Log Analytics workspace. We will use a single ACA environment.
Create a resource group to organize the services related to the container apps.
-
set these environment variables:
RESOURCE_GROUP="causeway-nightlies-rg" LOCATION="uksouth" CONTAINERAPPS_ENVIRONMENT="causeway-nightlies-aca-env"
-
create the resource group
az group create \ --name $RESOURCE_GROUP \ --location $LOCATION
resulting in:
{ "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/causeway-nightlies-rg", "location": "ukwest", "managedBy": null, "name": "causeway-nightlies-rg", "properties": { "provisioningState": "Succeeded" }, "tags": null, "type": "Microsoft.Resources/resourceGroups" }
-
next, create the ACA environment:
az containerapp env create \ --name $CONTAINERAPPS_ENVIRONMENT \ --resource-group $RESOURCE_GROUP \ --location $LOCATION
resulting in:
No Log Analytics workspace provided. Generating a Log Analytics workspace with name "workspace-causewaynightliesrgT0WN" Container Apps environment created. To deploy a container app, use: az containerapp create --help { "id": "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/causeway-nightlies-rg/providers/Microsoft.App/managedEnvironments/causeway-nightlies-aca-env", "location": "uksouth", "name": "causeway-nightlies-aca-env", "properties": { "appLogsConfiguration": { "destination": "log-analytics", "logAnalyticsConfiguration": { "customerId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } }, "customDomainConfiguration": { "customDomainVerificationId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }, "defaultDomain": "graytree-90c75749.uksouth.azurecontainerapps.io", "provisioningState": "Succeeded", "staticIp": "20.108.221.167", "zoneRedundant": false }, "resourceGroup": "causeway-nightlies-rg", "sku": { "name": "Consumption" }, "systemData": { "createdAt": "2023-01-02T06:54:25.6911574", "createdBy": "dan@haywood-associates.co.uk", "createdByType": "User", "lastModifiedAt": "2023-01-02T06:54:25.6911574", "lastModifiedBy": "dan@haywood-associates.co.uk", "lastModifiedByType": "User" }, "type": "Microsoft.App/managedEnvironments" }
Create Container App
We will create a container app manually. Later, in the automation section, we will simply update the container (replacing the image with a new one).
We’ll start with the apache/causeway-app-demo-jpa Docker image:
-
set further environment variables:
APP_NAME="demo-wicket-jpa" IMAGE_NAME="apache/causeway-demo-jpa" IMAGE_VERSION="2.0.0-M9.20221018-1911-d3980668"
-
create the app
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
resulting in:
Container app created. Access your app at https://demo-wicket-jpa.graytree-90c75749.uksouth.azurecontainerapps.io/
-
Change scaling from default (1-10) to just a single instance.
Use Lets Encrypt for SSL Certs
as per: this blog post blog, the shibayan/containerapps-acmebot repo provides an Azure deployment that handles the LetsEncrypt certificates.
Following its Getting-Started README:
DNS management
Created DNS zone for causeway.dev
, and then a child zone apps.causeway.dev
.
the intention is to change this to a more suitable domain. |
Deploy the Services
-
from the README:
-
add params:
results in:
Enable App Service Authentication & Access control (IAM)
Continuing with the README:
-
add an identity provider to the function app:
-
add contributor role assignment:
Access function app
Continuing further with the README:
-
locate the name of the function app (
func-acmebot-y3a6
above) -
navigate to https://func-acmebot-y3a6.azurewebsites.net/add-certificate, and grant permissions to access the site:
-
complete dialog:
and
Submit
. -
The docs say "after a few tens of seconds, the certificate will be issued". Indeed so:
In the DNS zone, it creates this
TXT
record: -
Also create a
CNAME
for the app: -
Confirm that the app can be accessed:
Deploy Remaining Apps
For each app, the steps are:
-
Set common environment variables
RESOURCE_GROUP="causeway-nightlies-rg" CONTAINERAPPS_ENVIRONMENT="causeway-nightlies-aca-env"
-
Set app-specific environment variables
-
Create the container app
-
Manually configure scaling
-
Manually setup
CNAME
record inapps.causeway.dev
DNS zone -
Using the acmebot function app, add the certificate for the container app
demo-wicket-jpa-snapshot
Steps:
-
Environment variables
APP_NAME="demo-wicket-jpa-snapshot" IMAGE_NAME="apache/causeway-demo-jpa" IMAGE_VERSION="latest"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
demo-wicket-jpa-snapshot.apps.causeway.dev
-
Register with LetsEncrypt Function App
demo-wicket-jdo
Steps:
-
Environment variables
APP_NAME="demo-wicket-jdo" IMAGE_NAME="apache/causeway-demo-jdo" IMAGE_VERSION="2.0.0-M9.20221018-1911-d3980668"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
demo-wicket-jdo.apps.causeway.dev
-
Register with LetsEncrypt Function App
demo-wicket-jdo-snapshot
Steps:
-
Environment variables
APP_NAME="demo-wicket-jdo-snapshot" IMAGE_NAME="apache//causeway-demo-jdo" IMAGE_VERSION="latest"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
demo-wicket-jdo-snapshot.apps.causeway.dev
-
Register with LetsEncrypt Function App
simpleapp (jpa)
Steps:
-
Environment variables
APP_NAME="simpleapp-jpa" IMAGE_NAME="apache/causeway-app-simpleapp" IMAGE_VERSION="jpa"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
simpleapp-jpa.apps.causeway.dev
-
Register with LetsEncrypt Function App
simpleapp (jpa-SNAPSHOT)
Steps:
-
Environment variables
APP_NAME="simpleapp-jpa-snapshot" IMAGE_NAME="apache/causeway-app-simpleapp" IMAGE_VERSION="jpa-SNAPSHOT"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
simpleapp-jpa-snapshot.apps.causeway.dev
-
Register with LetsEncrypt Function App
simpleapp (jdo)
Steps:
-
Environment variables
APP_NAME="simpleapp-jdo" IMAGE_NAME="apache/causeway-app-simpleapp" IMAGE_VERSION="jdo"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
simpleapp-jdo.apps.causeway.dev
-
Register with LetsEncrypt Function App
simpleapp (jdo-SNAPSHOT)
Steps:
-
Environment variables
APP_NAME="simpleapp-jdo-snapshot" IMAGE_NAME="apache/causeway-app-simpleapp" IMAGE_VERSION="jdo-SNAPSHOT"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
simpleapp-jdo.apps.causeway.dev
-
Register with LetsEncrypt Function App
helloworld (jpa)
Steps:
-
Environment variables
APP_NAME="helloworld-jpa" IMAGE_NAME="apache/causeway-app-helloworld" IMAGE_VERSION="jpa"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
helloworld-jpa.apps.causeway.dev
-
Register with LetsEncrypt Function App
helloworld (jpa-SNAPSHOT)
Steps:
-
Environment variables
APP_NAME="helloworld-jpa-snapshot" IMAGE_NAME="apache/causeway-app-helloworld" IMAGE_VERSION="jpa-SNAPSHOT"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
helloworld-jpa-snapshot.apps.causeway.dev
-
Register with LetsEncrypt Function App
helloworld (jdo)
Steps:
-
Environment variables
APP_NAME="helloworld-jdo" IMAGE_NAME="apache/causeway-app-helloworld" IMAGE_VERSION="jdo"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
helloworld-jdo.apps.causeway.dev
-
Register with LetsEncrypt Function App
helloworld (jdo-SNAPSHOT)
Steps:
-
Environment variables
APP_NAME="helloworld-jdo-snapshot" IMAGE_NAME="apache/causeway-app-helloworld" IMAGE_VERSION="jdo-SNAPSHOT"
-
Deploy
az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $CONTAINERAPPS_ENVIRONMENT \ --environment-variables PROTOTYPING=true \ --image $IMAGE_NAME:$IMAGE_VERSION \ --target-port 8080 \ --ingress 'external' \ --query properties.configuration.ingress.fqdn
-
Manually Configure Scaling
-
Manually setup DNS
to
helloworld-jdo-snapshot.apps.causeway.dev
-
Register with LetsEncrypt Function App
Automation
With all of the applications created above, the nightly automation simply needs to update the apps with new images, using az container revision copy
.
There is no need to create ACA environments etc from scratch.
Prereqs
We require an Azure Service Principal for the CI/CD job to use.
Following this blog post and also MS docs:
-
My guess is that this is a 'deploy' application rather than the application being deployed.
-
lookup your subscriptionId
-
as per the MS doc:
az ad sp create-for-rbac --name CausewayNightliesDeployPrincipal --role Owner --scopes "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
must use powershell here. should result in:
Found an existing application instance: (id) XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. We will patch it. Creating 'Owner' role assignment under scope '/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli { "appId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "displayName": "CausewayNightliesDeployPrincipal", "password": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "tenant": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }
CI Jobs
The actual CI jobs can be found in the apache-causeway-committers/causeway-nightly-deploys repo.
The main script consists of these commands:
# ... derive $APPNAME
# ... derive $IMAGE
TIMESTAMP=$(date "+%Y%m%d-%H%M%S")
REVISION_SUFFIX="r${TIMESTAMP}"
az login --service-principal \
--username "$AZ_APP_ID" \
--password "$AZ_PASSWORD" \
--tenant "$AZ_TENANT_ID"
az containerapp revision copy \
--name "$APPNAME" \
--resource-group "$RESOURCE_GROUP" \
--image "$IMAGE" \
--cpu "$CPU" \
--memory "$MEMORY" \
--revision-suffix "$REVISION_SUFFIX"