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 workspace

    az 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.

    image 2023 01 03 08 31 58 018

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:

    image 2023 01 03 17 58 33 718
  • add params:

    image 2023 01 03 18 07 57 499

    results in:

    image 2023 01 03 18 10 33 567

Enable App Service Authentication & Access control (IAM)

Continuing with the README:

  • add an identity provider to the function app:

    image 2023 01 03 18 16 01 614
  • add contributor role assignment:

    image 2023 01 03 18 22 59 542

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:

    image 2023 01 03 18 27 32 832
  • complete dialog:

    image 2023 01 04 06 23 41 099

    and Submit.

  • The docs say "after a few tens of seconds, the certificate will be issued". Indeed so:

    image 2023 01 04 06 26 42 960

    In the DNS zone, it creates this TXT record:

    image 2023 01 04 06 29 02 083
  • Also create a CNAME for the app:

    image 2023 01 04 06 25 56 926
  • Confirm that the app can be accessed:

    image 2023 01 04 06 31 36 293

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 in apps.causeway.dev DNS zone

  • Using the acmebot function app, add the certificate for the container app

demo-wicket-jpa

Done previously

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:

  • register an application

    My guess is that this is a 'deploy' application rather than the application being deployed.

    image 2023 01 05 11 44 09 855
  • 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"