azure-docs icon indicating copy to clipboard operation
azure-docs copied to clipboard

Migration from Standard to Enterprise Tier should emphasize the key changes on IaC

Open ezYakaEagle442 opened this issue 3 years ago • 3 comments

Migration from Standard to Enterprise Tier should emphasize the key changes on IaC, including :

  • Monitoring: not only the API is different than 'Microsoft.AppPlatform/Spring/monitoringSettings@2022-09-01-preview' but also the properties with bindingType: 'ApplicationInsights' and connection_string: appInsights.properties.ConnectionString
resource azureSpringAppsMonitoringSettings 'Microsoft.AppPlatform/Spring/buildServices/builders/buildpackBindings@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: '${azureSpringApps.name}/${buildServiceName}/${builderName}/${monitoringSettingsName}' // /default (for Build Service ) /default (Builder) /default (Build Pack binding name)
  properties: {
    bindingType: 'ApplicationInsights'
    launchProperties: {
      properties: {
        sampling_percentage: '10'
        connection_string: appInsights.properties.ConnectionString // /!\ ConnectionString for Enterprise tier ,  InstrumentationKey for Standard Tier 
      }
    }   
  }
  dependsOn: [
    appInsights
    buildService
  ]
}
  • The Config Server must be replaced by configuration Services which has a different API :
resource appconfigservice 'Microsoft.AppPlatform/Spring/configurationServices@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: applicationConfigurationServiceName
  parent: azureSpringApps
  properties: {
    settings: {
      gitProperty: {
        repositories: [
          {
            name: gitRepoName
            label: configServerLabel
            patterns: [
              'application'
            ]
            //searchPaths: [
            //  '/'
            //]
            uri: gitConfigURI
          }
        ]
      }
    }
  }
}
  • The App must now include the addonConfigs for applicationConfigurationService and serviceRegistry :
resource customersserviceapp 'Microsoft.AppPlatform/Spring/apps@2022-11-01-preview' = {
  name: 'customers-service'
  location: location
  parent: azureSpringApps
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${customersServicedentity.id}': {}
    }      
  }
  properties: {
    addonConfigs: {
      azureMonitor: {
        enabled: true
      }
      applicationConfigurationService: {
        resourceId: '${azureSpringApps.id}/configurationServices/${applicationConfigurationServiceName}'
      }
      serviceRegistry: {
          resourceId: '${azureSpringApps.id}/serviceRegistries/${serviceRegistryName}'
      }      
    }
    httpsOnly: false
    public: true
    temporaryDisk: {
      mountPath: '/tmp'
      sizeInGB: 5
    }
  }
  dependsOn: [
    appconfigservice
  ]  
}
  • The App bindings must be be added :
resource customersbinding 'Microsoft.AppPlatform/Spring/apps/bindings@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: 'customers-service-binding'
  parent: customersserviceapp
  properties: {
    bindingParameters: {}
    resourceId: customersserviceapp.id
    key: 'customers-service' // There is no API Key for MySQL
  }
  dependsOn: [
    serviceRegistry
  ]
}
  • serviceRegistry must be created:
resource serviceRegistry 'Microsoft.AppPlatform/Spring/serviceRegistries@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: serviceRegistryName
  parent: azureSpringApps

}
output serviceRegistryId string = serviceRegistry.id
  • API portal should be created
resource apiPortal 'Microsoft.AppPlatform/Spring/apiPortals@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: apiPortalName
  parent: azureSpringApps
  sku: {
    name: azureSpringAppsSkuName
    capacity: any(1) // Number of instance ?
    tier: azureSpringAppsTier
  }
  properties: {
    gatewayIds: [
        '${azureSpringApps.id}/gateways/${gatewayName}'
      ]
    httpsOnly: false
    public: true
    ssoProperties: {
      clientId: apiPortalSsoClientId
      clientSecret: apiPortalSsoClientSecret
      issuerUri: apiPortalSsoIssuerUri
      scope: [
        'openid'
        'profile'
        'email'
      ]
    }
  }
}
output apiPortalId string = apiPortal.id
output apiPortalUrl string = apiPortal.properties.url
output gatewayIds array = apiPortal.properties.gatewayIds
  • gateway should be created
resource gateway 'Microsoft.AppPlatform/Spring/gateways@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: gatewayName
  parent: azureSpringApps
  sku: {
    name: azureSpringAppsSkuName
    capacity: any(1)
    tier: azureSpringAppsTier
  }
  properties: {
    corsProperties: {
      allowCredentials: false
      allowedOrigins: [
        '*'
      ]
      allowedMethods: [
        'GET'
      ]
      allowedHeaders: [
        '*'
      ]
    }
    httpsOnly: false
    public: true
    // az spring gateway update --help
    resourceRequests: {
      cpu: '1' // CPU resource quantity. Should be 500m or number of CPU cores.
      memory: '1Gi' // Memory resource quantity. Should be 512Mi or #Gi, e.g., 1Gi, 3Gi.
    }
    ssoProperties: {
      clientId: apiPortalSsoClientId
      clientSecret: apiPortalSsoClientSecret
      issuerUri: apiPortalSsoIssuerUri
      scope: [
        'openid'
        'profile'
        'email'
      ]
    }  
  }
}
output gatewayId string = gateway.id
output gatewayUrl string = gateway.properties.url
  • routeConfigs must be created :
resource CustomersGatewayRouteConfig 'Microsoft.AppPlatform/Spring/gateways/routeConfigs@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: 'customers-service-gateway-route-config'
  parent: gateway
  properties: {
    appResourceId: customersserviceapp.id
    protocol: 'HTTP'
    filters: [
      'StripPrefix=0'
    ]
    predicates: [
      '/api/customer/**'
      '/owners'
    ]
    routes: [
      {
        description: 'customers-service'
        title: 'customers-service'
        uri: 'http://customers-service'
        order: 1
        ssoEnabled: apiPortalSsoEnabled

      }
    ]
  }
}
output CustomersGatewayRouteConfigId string = CustomersGatewayRouteConfig.id
output CustomersGatewayRouteConfigAppResourceId string = CustomersGatewayRouteConfig.properties.appResourceId
output CustomersGatewayRouteConfigRoutes array = CustomersGatewayRouteConfig.properties.routes
output CustomersGatewayRouteConfigIsSsoEnabled bool = CustomersGatewayRouteConfig.properties.ssoEnabled
output CustomersGatewayRouteConfigPredicates array = CustomersGatewayRouteConfig.properties.predicates
  • we should provide a tool to automate this , importing routes from the OSS Spring Cloud Gateway config, see my sample :
spring:
  cloud:
    gateway:
      discovery:
        # make sure a DiscoveryClient implementation (such as Netflix Eureka) is on the classpath and enabled
        locator: # https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-discoveryclient-route-definition-locator
          enabled: true #  to configure Spring Cloud Gateway to use the Spring Cloud Service Registry to discover the available microservices.    
      routes:
        - id: vets-service
          uri: http://vets-service
          predicates:
            - Path=/api/vet/**
          filters:
            - StripPrefix=2
        - id: visits-service
          uri: http://visits-service
          predicates:
            - Path=/api/visit/**
          filters:
            - StripPrefix=2
        - id: customers-service
          uri: http://customers-service
          predicates:
            - Path=/api/customer/**
          filters:
            - StripPrefix=2
      globalcors: # o allow CORS requests to our gateway. This can be helpful when you add a front-end that is not hosted on Azure Spring Apps.
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET            
  • All the build tools must be created :
resource buildService 'Microsoft.AppPlatform/Spring/buildServices@2022-11-01-preview' existing = if (azureSpringAppsTier=='Enterprise') {
  //scope: resourceGroup('my RG')
  name: '${azureSpringAppsInstanceName}/${buildServiceName}' 
}

resource buildagentpool 'Microsoft.AppPlatform/Spring/buildServices/agentPools@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  // '{your-service-name}/default/default'  //{your-service-name}/{build-service-name}/{agenpool-name}
  name: '${azureSpringAppsInstanceName}/${buildServiceName}/${buildAgentPoolName}' 
  properties: {
    poolSize: {
      name: 'S1'
    }
  }
  dependsOn: [
    azureSpringApps
  ]  
}

// az spring build-service builder create --help
// https://learn.microsoft.com/en-us/azure/spring-apps/how-to-enterprise-build-service?tabs=azure-portal#default-builder-and-tanzu-buildpacks
resource builder 'Microsoft.AppPlatform/Spring/buildServices/builders@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: builderName
  parent: buildService
  properties: {
    buildpackGroups: [
      {
        buildpacks: [
          {
            id: 'tanzu-buildpacks/java-azure'
          }
        ]
        name: 'java'
      }
    ]
    // https://docs.vmware.com/en/VMware-Tanzu-Buildpacks/services/tanzu-buildpacks/GUID-full-stack-release-notes.html
    // 
    stack: {
      id: 'io.buildpacks.stacks.bionic' // io.buildpacks.stacks.bionic-base or tanzu-base-bionic-stack ?   https://docs.pivotal.io/tanzu-buildpacks/stacks.html , OSS from https://github.com/paketo-buildpacks/java
      version: 'base' // 1.2.35 https://network.tanzu.vmware.com/products/tanzu-base-bionic-stack#/releases/1218795/artifact_references
    }
  }
  dependsOn: [
    azureSpringApps
  ]
}


// https://github.com/Azure/Azure-Spring-Apps/issues/28
resource build 'Microsoft.AppPlatform/Spring/buildServices/builds@2022-11-01-preview' = if (azureSpringAppsTier=='Enterprise') {
  name: buildName
  parent: buildService
  properties: {
    agentPool: buildagentpool.id
    builder: builder.id
    env: {}
    relativePath: '/'
  }
  dependsOn: [
    azureSpringApps
  ]
}

Document Details

Do not edit this section. It is required for learn.microsoft.com ➟ GitHub issue linking.

ezYakaEagle442 avatar Dec 16 '22 08:12 ezYakaEagle442

@ezYakaEagle442

I've delegated this to content author @KarlErickson to review and share his valuable insights.

also the migration impacts the Deployment Pipelines : GH Workflows , I hit :

ERROR: '--runtime-version' doesn't support for Enterprise tier Spring instance.

See my Workflow deployment at https://github.com/ezYakaEagle442/azure-spring-apps-petclinic-mic-srv/blob/azure/.github/workflows/build-deploy-apps-staging-CLI.yml#L467

          az spring app deployment create \
            --name $DEPLOYMENT_NAME \
            --app ${{ env.VETS_SERVICE }} \
            --service ${{ env.AZURE_SPRING_APPS_SERVICE }} -g ${{ env.RG_APP }} \
            --instance-count ${{ env.DEPLOYMENT_INSTANCE_COUNT }} \
            --cpu ${{ env.DEPLOYMENT_CPU }} \
            --memory ${{ env.DEPLOYMENT_MEMORY }} \
            --version ${{ env.DEPLOYMENT_VERSION }} \
            --runtime-version ${{ env.DEPLOYMENT_RUNTIME_VERSION }} \
            --skip-clone-settings       

ezYakaEagle442 avatar Dec 16 '22 16:12 ezYakaEagle442

Build reason(s): CONFIG
CONFIG:
	- resources: ***
	- source: ***
	+ resources:
	+   limits:
	+     cpu: "1"
	+     memory: 2Gi
	+ services:
	+ - apiVersion: v1
	+   kind: Secret
	+   name: 0-7default-7default-1
	+ source:
	+   blob:
	+     url: https://1826c50187634f49aeeb1f26.file.core.windows.net/119e2834b4524cc1982393c691e5c73f/resources/2022121617-fba87097-5f50-41d0-b7a4-01e9fa602870?sv=2020-08-04&se=2023-04-15T17%3A27%3A35Z&sr=f&sp=r&sig=Rira6bvz2%2Bof%2FD3vR6WGd1o6e9ma%2BZ6QRYgPYAEbF5Y%3D
Loading secrets for "acr3da[318](https://github.com/ezYakaEagle442/azure-spring-apps-petclinic-mic-srv/actions/runs/3715175103/jobs/6300056129#step:10:319)443a37414d8.azurecr.io" from secret "docker-kpack"
Loading cluster credential helpers
Downloading 1826c50187634f49aeeb1f26.file.core.windows.net/119e2834b4524cc1982393c691e5c73f/resources/2022121617-fba87097-5f50-41d0-b7a4-01e9fa602870...
Successfully downloaded 1826c50187634f49aeeb1f26.file.core.windows.net/119e2834b4524cc1982393c691e5c73f/resources/2022121617-fba87097-5f50-41d0-b7a4-01e9fa602870 in path "/workspace"
Builder:
 Image: acr3da318443a37414d8.azurecr.io/build-service-builder-119e2834b4524cc1982393c691e5c73f-default-default@sha256:a179e49eeaa9d4119a0be6917a701263983d2955570653aa40ad28fbf199830a 
 Name: default.default.1 
 Kind: Builder 
Previous image with name "acr3da318443a37414d8.azurecr.io/build-service-result-image-119e2834b4524cc1982393c691e5c73f-default-vets-service-blue-vets-service:result" not found
7 of 41 buildpacks participating
paketo-buildpacks/ca-certificates            3.4.0
paketo-buildpacks/microsoft-openjdk          2.6.0
paketo-buildpacks/syft                       1.10.1
paketo-buildpacks/executable-jar             6.5.0
paketo-buildpacks/dist-zip                   5.4.0
paketo-buildpacks/spring-boot                5.19.0
paketo-buildpacks/azure-application-insights 5.8.0
Warning: Not restoring cached layer data, no cache flag specified.
    $BPL_JAVA_NMT_ENABLED        true                                                         enables Java Native Memory Tracking (NMT)
    $BPL_JAVA_NMT_LEVEL          summary                                                      configure level of NMT, summary or detail
    Writing env/CLASSPATH.prepend
  Process types:
    executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
    task:           java org.springframework.boot.loader.JarLauncher (direct)
    web:            java org.springframework.boot.loader.JarLauncher (direct)

Paketo Buildpack for Spring Boot 5.19.0
  https://github.com/paketo-buildpacks/spring-boot
  Build Configuration:
    $BP_SPRING_CLOUD_BINDINGS_DISABLED   false  whether to contribute Spring Boot cloud bindings support
  Launch Configuration:
    $BPL_SPRING_CLOUD_BINDINGS_DISABLED  false  whether to auto-configure Spring Boot environment properties from bindings
    $BPL_SPRING_CLOUD_BINDINGS_ENABLED   true   Deprecated - whether to auto-configure Spring Boot environment properties from bindings
  Creating slices from layers index
    dependencies (84.8 MB)
    spring-boot-loader (282.2 KB)
    snapshot-dependencies (0.0 B)
    application (105.3 KB)
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
  Spring Cloud Bindings 1.10.0: Contributing to layer
    Reusing cached download from buildpack
    Copying to /layers/paketo-buildpacks_spring-boot/spring-cloud-bindings
  Web Application Type: Contributing to layer
    Servlet web application detected
    Writing env.launch/BPL_JVM_THREAD_COUNT.default
  4 application slices
  Image labels:
    org.opencontainers.image.title
    org.opencontainers.image.version
    org.springframework.boot.version

ezYakaEagle442 avatar Dec 16 '22 17:12 ezYakaEagle442

There are other changes that should be documented :

the base image has some difference between enterprise/standard tier, and the some memories may allocated by buildpacks, which results in setting more memories to App deployments with Enterprise Tier.

with with Standard Tier :

  DEPLOYMENT_JVM_OPTIONS: -Xms512m -Xmx1024m 
  DEPLOYMENT_RUNTIME_VERSION: Java_11
  DEPLOYMENT_CPU: 500m
  DEPLOYMENT_MEMORY: 1Gi
  DEPLOYMENT_INSTANCE_COUNT: 1

        az spring app deploy \
        --name ${{ env.VETS_SERVICE }} \
        --service ${{ env.AZURE_SPRING_APPS_SERVICE }} -g ${{ env.RG_APP }} \
        --artifact-path ${{ env.VETS_SERVICE_PACKAGE_PATH }} \
        --deployment $DEPLOYMENT_NAME \
        --disable-probe true \
        --env SPRING_CLOUD_AZURE_KEY_VAULT_ENDPOINT=${{ env.SPRING_CLOUD_AZURE_KEY_VAULT_ENDPOINT }} VETS_SVC_APP_IDENTITY_CLIENT_ID=${{ env.VETS_SVC_APP_IDENTITY_CLIENT_ID }} SPRING_CLOUD_AZURE_TENANT_ID=${{ env.SPRING_CLOUD_AZURE_TENANT_ID }} \
        --jvm-options="${{ env.DEPLOYMENT_JVM_OPTIONS }}" \
        --version ${{ env.DEPLOYMENT_VERSION }} \
        --runtime-version ${{ env.DEPLOYMENT_RUNTIME_VERSION }}   

with Enterprise Tier:

DEPLOYMENT_JVM_OPTIONS: -Xms1024m -Xmx2048m
DEPLOYMENT_CPU: 500m
DEPLOYMENT_MEMORY: 2Gi
DEPLOYMENT_INSTANCE_COUNT: 1

  BP_JVM_VERSION: 11 # just '11' NOT Java_11
  BUILD_ENV: BP_JVM_VERSION=11 # Space-separated environment variables in 'key[=value]' format: <key1=value1>, <key2=value2>
  BUILD_CPU: 1 # CPU resource quantity. Should be 500m or number of CPU cores. Default: 1.
  BUILD_MEMORY: 2Gi # Memory resource quantity. Should be 512Mi or #Gi, e.g., 1Gi, 3Gi. Default: 2Gi.
  BUILDER: default # default or java-builder
  CFG_FILE_PATTERNS: application # Config file  patterns separated with ',' to decide which patterns of Application Configuration Service will be used. Use '""' to clear existing configurations.

        az spring app deploy \
        --name ${{ env.VETS_SERVICE }} \
        --service ${{ env.AZURE_SPRING_APPS_SERVICE }} -g ${{ env.RG_APP }} \
        --artifact-path ${{ env.VETS_SERVICE_PACKAGE_PATH }} \
        --deployment $DEPLOYMENT_NAME \
        --disable-probe true \
        --env SPRING_CLOUD_AZURE_KEY_VAULT_ENDPOINT=${{ env.SPRING_CLOUD_AZURE_KEY_VAULT_ENDPOINT }} VETS_SVC_APP_IDENTITY_CLIENT_ID=${{ env.VETS_SVC_APP_IDENTITY_CLIENT_ID }} SPRING_CLOUD_AZURE_TENANT_ID=${{ env.SPRING_CLOUD_AZURE_TENANT_ID }} \
        --jvm-options="${{ env.DEPLOYMENT_JVM_OPTIONS }}" \
        --version ${{ env.DEPLOYMENT_VERSION }} \
        --build-env ${{ env.BUILD_ENV }}  \
        --build-cpu ${{ env.BUILD_CPU }} \
        --build-memory ${{ env.BUILD_MEMORY }} \
        --builder ${{ env.BUILDER }} \
        --config-file-patterns ${{ env.CFG_FILE_PATTERNS }}

ezYakaEagle442 avatar Dec 19 '22 10:12 ezYakaEagle442

Also, using CFG_FILE_PATTERNS= application and -Dspring.profiles.active was NOT enough to get my App configured using my application.yaml on my repo.

---
spring:
  config:
    activate:
      on-profile: mysql
  datasource:
    schema: classpath*:db/mysql/schema.sql
    data: classpath*:db/mysql/data.sql
    username: mys_adm
    url: jdbc:mysql://petcliasa.mysql.database.azure.com:3306/petclinic?useSSL=true&requireSSL=true&enabledTLSProtocols=TLSv1.2&verifyServerCertificate=true
    initialization-mode: NEVER # NEVER ALWAYS
    platform: mysql
    #driver-class-name: com.mysql.jdbc.Driver

I had to use EXPLICTLY application-mysql on my repo:

spring:
  datasource:
    schema: classpath*:db/mysql/schema.sql
    data: classpath*:db/mysql/data.sql
    username: mys_adm
    url: jdbc:mysql://petcliasa.mysql.database.azure.com:3306/petclinic?useSSL=true&requireSSL=true&enabledTLSProtocols=TLSv1.2&verifyServerCertificate=true 
    initialization-mode: NEVER # NEVER ALWAYS
    platform: mysql
    #driver-class-name: com.mysql.jdbc.Driver

And that was still NOT enough, I had then to EXPLICTLY set CFG_FILE_PATTERNS: application/mysql in the Workflow after having added a new repo named 'mysql' with profile application/mysql in the Application Configuration Service

  DEPLOYMENT_JVM_OPTIONS: -Dspring.profiles.active=mysql,key-vault,cloud
  CFG_FILE_PATTERNS: application/mysql
        az spring app deploy \
        --name ${{ env.VETS_SERVICE }} \
        --service ${{ env.AZURE_SPRING_APPS_SERVICE }} -g ${{ env.RG_APP }} \
        --artifact-path ${{ env.VETS_SERVICE_PACKAGE_PATH }} \
        --deployment $DEPLOYMENT_NAME \
        --disable-probe true \
        --env SPRING_CLOUD_AZURE_KEY_VAULT_ENDPOINT=${{ env.SPRING_CLOUD_AZURE_KEY_VAULT_ENDPOINT }} VETS_SVC_APP_IDENTITY_CLIENT_ID=${{ env.VETS_SVC_APP_IDENTITY_CLIENT_ID }} SPRING_CLOUD_AZURE_TENANT_ID=${{ env.SPRING_CLOUD_AZURE_TENANT_ID }} \
        --jvm-options="${{ env.DEPLOYMENT_JVM_OPTIONS }}" \
        --version ${{ env.DEPLOYMENT_VERSION }} \
        --build-env ${{ env.BUILD_ENV }}  \
        --build-cpu ${{ env.BUILD_CPU }} \
        --build-memory ${{ env.BUILD_MEMORY }} \
        --builder ${{ env.BUILDER }} \
        --config-file-patterns ${{ env.CFG_FILE_PATTERNS }}

ezYakaEagle442 avatar Dec 21 '22 14:12 ezYakaEagle442

Dingmeng is publishing a document on this topic and he should double check.

selvasingh avatar Mar 14 '24 23:03 selvasingh

We have represented this work in an Azure DevOps work item, and will investigate and fix.

#please-close

KarlErickson avatar Mar 18 '24 16:03 KarlErickson