From aff921b83a71afcf220f42b9c9ffa2be9cf1cd85 Mon Sep 17 00:00:00 2001 From: Raki Rahman Date: Sat, 24 Jul 2021 09:58:56 -0400 Subject: [PATCH] Initial fork of Data scenarios with DC deployments cleaned up. Also included Arck ML notebook and CLI examples --- .../aks/arm_template/VNET.json | 54 + .../aks/arm_template/aks.json | 150 +++ .../aks/arm_template/artifacts/Bootstrap.ps1 | 142 +++ .../artifacts/DataServicesLogonScript.ps1 | 132 +++ .../simple-train-cli/environment.yml | 10 + .../artifacts/simple-train-cli/job.yml | 18 + .../artifacts/simple-train-cli/src/train.py | 50 + .../artifacts/simple-train-cli/src/utils.py | 27 + .../arc-img-classification-training.ipynb | 949 ++++++++++++++++++ .../artifacts/simple-train-sdk/utils.py | 27 + .../aks/arm_template/azuredeploy.json | 239 +++++ .../arm_template/azuredeploy.parameters.json | 45 + .../aks/arm_template/clientVm.json | 309 ++++++ .../aks/arm_template/logAnalytics.json | 46 + 14 files changed, 2198 insertions(+) create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/VNET.json create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/aks.json create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/Bootstrap.ps1 create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/DataServicesLogonScript.ps1 create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/environment.yml create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/job.yml create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/train.py create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/utils.py create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/arc-img-classification-training.ipynb create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/utils.py create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.json create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.parameters.json create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/clientVm.json create mode 100644 azure_arc_ml_jumpstart/aks/arm_template/logAnalytics.json diff --git a/azure_arc_ml_jumpstart/aks/arm_template/VNET.json b/azure_arc_ml_jumpstart/aks/arm_template/VNET.json new file mode 100644 index 0000000000..f259c64d16 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/VNET.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Name of the VNET" + } + }, + "subnetName": { + "type": "string", + "metadata": { + "description": "Name of the subnet in the virtual network" + } + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "VNET CIDR" + } + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "Subnet CIDR" + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks", + "comments": "Deploys a VNET and Subnet for Client and K8s VM", + "apiVersion": "2019-04-01", + "name": "[parameters('virtualNetworkName')]", + "location": "[resourceGroup().location]", + "properties": { + "addressSpace": { + "addressPrefixes": ["[parameters('addressPrefix')]"] + }, + "subnets": [ + { + "name": "[parameters('subnetName')]", + "properties": { + "addressPrefix": "[parameters('subnetAddressPrefix')]", + "privateEndpointNetworkPolicies": "Enabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + } + ] + } + } + ] +} diff --git a/azure_arc_ml_jumpstart/aks/arm_template/aks.json b/azure_arc_ml_jumpstart/aks/arm_template/aks.json new file mode 100644 index 0000000000..536c174266 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/aks.json @@ -0,0 +1,150 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "clusterName": { + "type": "string", + "defaultValue": "Arc-Data-AKS", + "metadata": { + "description": "The name of the Kubernetes cluster resource.." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location of the Managed Cluster resource." + } + }, + "dnsPrefix": { + "type": "string", + "metadata": { + "description": "Optional DNS prefix to use with hosted Kubernetes API server FQDN." + } + }, + "osDiskSizeGB": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Disk size (in GB) to provision for each of the agent pool nodes. This value ranges from 0 to 1023. Specifying 0 will apply the default disk size for that agentVMSize." + }, + "minValue": 0, + "maxValue": 1023 + }, + "agentCount": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "The number of nodes for the cluster." + }, + "minValue": 1, + "maxValue": 50 + }, + "agentVMSize": { + "type": "string", + "defaultValue": "Standard_D8s_v3", + "metadata": { + "description": "The size of the Virtual Machine." + } + }, + "linuxAdminUsername": { + "type": "string", + "defaultValue": "arcdemo", + "metadata": { + "description": "User name for the Linux Virtual Machines." + } + }, + "sshRSAPublicKey": { + "type": "string", + "metadata": { + "description": "Configure all linux machines with the SSH RSA public key string. Your key should include three parts, for example 'ssh-rsa AAAAB...snip...UcyupgH azureuser@linuxvm'" + } + }, + "spnClientId": { + "metadata": { + "description": "Client ID (used by cloudprovider)" + }, + "type": "securestring" + }, + "spnClientSecret": { + "metadata": { + "description": "The Service Principal Client Secret." + }, + "type": "securestring" + }, + "enableRBAC": { + "defaultValue": true, + "type": "bool", + "metadata": { + "description": "boolean flag to turn on and off of RBAC" + } + }, + "osType": { + "type": "string", + "defaultValue": "Linux", + "allowedValues": ["Linux"], + "metadata": { + "description": "The type of operating system." + } + }, + "kubernetesVersion": { + "defaultValue": "1.18.17", + "type": "string", + "metadata": { + "description": "The version of Kubernetes." + } + }, + "resourceTags": { + "type": "object", + "defaultValue": { + "Project": "jumpstart_azure_arc_data_services" + } + } + }, + "resources": [ + { + "apiVersion": "2020-03-01", + "type": "Microsoft.ContainerService/managedClusters", + "location": "[parameters('location')]", + "name": "[parameters('clusterName')]", + "tags": "[parameters('resourceTags')]", + "properties": { + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "enableRBAC": "[parameters('enableRBAC')]", + "dnsPrefix": "[parameters('dnsPrefix')]", + "agentPoolProfiles": [ + { + "name": "agentpool", + "mode": "System", + "osDiskSizeGB": "[parameters('osDiskSizeGB')]", + "count": "[parameters('agentCount')]", + "vmSize": "[parameters('agentVMSize')]", + "osType": "[parameters('osType')]", + "storageProfile": "ManagedDisks", + "type": "VirtualMachineScaleSets" + } + ], + "linuxProfile": { + "adminUsername": "[parameters('linuxAdminUsername')]", + "ssh": { + "publicKeys": [ + { + "keyData": "[parameters('sshRSAPublicKey')]" + } + ] + } + }, + "servicePrincipalProfile": { + "clientId": "[parameters('spnClientId')]", + "Secret": "[parameters('spnClientSecret')]" + } + } + } + ], + "outputs": { + "controlPlaneFQDN": { + "type": "string", + "value": "[reference(parameters('clusterName')).fqdn]" + } + } +} diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/Bootstrap.ps1 b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/Bootstrap.ps1 new file mode 100644 index 0000000000..9fb444c03c --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/Bootstrap.ps1 @@ -0,0 +1,142 @@ +param ( + [string]$adminUsername, + [string]$spnClientId, + [string]$spnClientSecret, + [string]$spnTenantId, + [string]$spnAuthority, + [string]$subscriptionId, + [string]$resourceGroup, + [string]$azdataUsername, + [string]$azdataPassword, + [string]$acceptEula, + [string]$arcDcName, + [string]$azureLocation, + [string]$workspaceName, + [string]$clusterName, + [string]$deploySQLMI, + [string]$deployPostgreSQL, + [string]$templateBaseUrl +) + +[System.Environment]::SetEnvironmentVariable('adminUsername', $adminUsername,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('spnClientID', $spnClientId,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('spnClientSecret', $spnClientSecret,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('spnTenantId', $spnTenantId,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('spnAuthority', $spnAuthority,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('resourceGroup', $resourceGroup,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('AZDATA_USERNAME', $azdataUsername,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('AZDATA_PASSWORD', $azdataPassword,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('ACCEPT_EULA', $acceptEula,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('arcDcName', $arcDcName,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('subscriptionId', $subscriptionId,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('azureLocation', $azureLocation,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('workspaceName', $workspaceName,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('deploySQLMI', $deploySQLMI,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('deployPostgreSQL', $deployPostgreSQL,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('clusterName', $clusterName,[System.EnvironmentVariableTarget]::Machine) +[System.Environment]::SetEnvironmentVariable('templateBaseUrl', $templateBaseUrl,[System.EnvironmentVariableTarget]::Machine) + +# Create path +Write-Output "Create deployment path" +$tempDir = "C:\Temp" +New-Item -Path $tempDir -ItemType directory -Force + +Start-Transcript "C:\Temp\Bootstrap.log" + +$ErrorActionPreference = 'SilentlyContinue' + +# Uninstall Internet Explorer +Disable-WindowsOptionalFeature -FeatureName Internet-Explorer-Optional-amd64 -Online -NoRestart + +# Disabling IE Enhanced Security Configuration +Write-Host "Disabling IE Enhanced Security Configuration" +function Disable-ieESC { + $AdminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" + $UserKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" + Set-ItemProperty -Path $AdminKey -Name "IsInstalled" -Value 0 + Set-ItemProperty -Path $UserKey -Name "IsInstalled" -Value 0 + Stop-Process -Name Explorer + Write-Host "IE Enhanced Security Configuration (ESC) has been disabled." -ForegroundColor Green +} +Disable-ieESC + +# Extending C:\ partition to the maximum size +Write-Host "Extending C:\ partition to the maximum size" +Resize-Partition -DriveLetter C -Size $(Get-PartitionSupportedSize -DriveLetter C).SizeMax + +# Downloading GitHub artifacts for DataServicesLogonScript.ps1 +Invoke-WebRequest ($templateBaseUrl + "artifacts/settingsTemplate.json") -OutFile "C:\Temp\settingsTemplate.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/DataServicesLogonScript.ps1") -OutFile "C:\Temp\DataServicesLogonScript.ps1" +Invoke-WebRequest ($templateBaseUrl + "artifacts/DeploySQLMI.ps1") -OutFile "C:\Temp\DeploySQLMI.ps1" +Invoke-WebRequest ($templateBaseUrl + "artifacts/DeployPostgreSQL.ps1") -OutFile "C:\Temp\DeployPostgreSQL.ps1" +Invoke-WebRequest ($templateBaseUrl + "artifacts/dataController.json") -OutFile "C:\Temp\dataController.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/dataController.parameters.json") -OutFile "C:\Temp\dataController.parameters.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/SQLMI.json") -OutFile "C:\Temp\SQLMI.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/SQLMI.parameters.json") -OutFile "C:\Temp\SQLMI.parameters.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/postgreSQL.json") -OutFile "C:\Temp\postgreSQL.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/postgreSQL.parameters.json") -OutFile "C:\Temp\postgreSQL.parameters.json" +Invoke-WebRequest ($templateBaseUrl + "artifacts/wallpaper.png") -OutFile "C:\Temp\wallpaper.png" + +# Installing tools +workflow ClientTools_01 + { + $chocolateyAppList = 'azure-cli,az.powershell,kubernetes-cli,vcredist140,microsoft-edge,azcopy10,vscode,putty.install,kubernetes-helm,grep' + #Run commands in parallel. + Parallel + { + InlineScript { + param ( + [string]$chocolateyAppList + ) + if ([string]::IsNullOrWhiteSpace($using:chocolateyAppList) -eq $false) + { + try{ + choco config get cacheLocation + }catch{ + Write-Output "Chocolatey not detected, trying to install now" + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + } + } + if ([string]::IsNullOrWhiteSpace($using:chocolateyAppList) -eq $false){ + Write-Host "Chocolatey Apps Specified" + + $appsToInstall = $using:chocolateyAppList -split "," | foreach { "$($_.Trim())" } + + foreach ($app in $appsToInstall) + { + Write-Host "Installing $app" + & choco install $app /y -Force| Write-Output + } + } + } + Invoke-WebRequest "https://azuredatastudio-update.azurewebsites.net/latest/win32-x64-archive/stable" -OutFile "C:\Temp\azuredatastudio.zip" + Invoke-WebRequest "https://aka.ms/azdata-msi" -OutFile "C:\Temp\AZDataCLI.msi" + } + } + +ClientTools_01 | Format-Table + +workflow ClientTools_02 + { + #Run commands in parallel. + Parallel + { + InlineScript { + Expand-Archive C:\Temp\azuredatastudio.zip -DestinationPath 'C:\Program Files\Azure Data Studio' + Start-Process msiexec.exe -Wait -ArgumentList '/I C:\Temp\AZDataCLI.msi /quiet' + } + } + } + +ClientTools_02 | Format-Table + +New-Item -path alias:kubectl -value 'C:\ProgramData\chocolatey\lib\kubernetes-cli\tools\kubernetes\client\bin\kubectl.exe' +New-Item -path alias:azdata -value 'C:\Program Files (x86)\Microsoft SDKs\Azdata\CLI\wbin\azdata.cmd' + +# Creating scheduled task for DataServicesLogonScript.ps1 +$Trigger = New-ScheduledTaskTrigger -AtLogOn +$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument 'C:\Temp\DataServicesLogonScript.ps1' +Register-ScheduledTask -TaskName "DataServicesLogonScript" -Trigger $Trigger -User $adminUsername -Action $Action -RunLevel "Highest" -Force + +# Disabling Windows Server Manager Scheduled Task +Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/DataServicesLogonScript.ps1 b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/DataServicesLogonScript.ps1 new file mode 100644 index 0000000000..29823f11a2 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/DataServicesLogonScript.ps1 @@ -0,0 +1,132 @@ +Start-Transcript -Path C:\Temp\DataServicesLogonScript.log + +# Deployment environment variables +$connectedClusterName = "Arc-Data-AKS" + +Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False + +az login --service-principal --username $env:spnClientId --password $env:spnClientSecret --tenant $env:spnTenantId + +# Temporary!!!!!!! +az account set --subscription "Azure Data Demos" +################################################### + +Write-Host "Installing Azure Data Studio Extensions" +Write-Host "`n" + +$env:argument1="--install-extension" +$env:argument2="Microsoft.arc" +$env:argument3="microsoft.azuredatastudio-postgresql" + +& "C:\Program Files\Azure Data Studio\bin\azuredatastudio.cmd" $env:argument1 $env:argument2 +& "C:\Program Files\Azure Data Studio\bin\azuredatastudio.cmd" $env:argument1 $env:argument3 + +Write-Host "Creating Azure Data Studio Desktop shortcut" +Write-Host "`n" +$TargetFile = "C:\Program Files\Azure Data Studio\azuredatastudio.exe" +$ShortcutFile = "C:\Users\$env:adminUsername\Desktop\Azure Data Studio.lnk" +$WScriptShell = New-Object -ComObject WScript.Shell +$Shortcut = $WScriptShell.CreateShortcut($ShortcutFile) +$Shortcut.TargetPath = $TargetFile +$Shortcut.Save() + +# Registering Azure Arc providers +Write-Host "Registering Azure Arc providers, hold tight..." +Write-Host "`n" +az provider register --namespace Microsoft.Kubernetes --wait +az provider register --namespace Microsoft.KubernetesConfiguration --wait +az provider register --namespace Microsoft.ExtendedLocation --wait +az provider register --namespace Microsoft.AzureArcData --wait + +az provider show --namespace Microsoft.Kubernetes -o table +Write-Host "`n" +az provider show --namespace Microsoft.KubernetesConfiguration -o table +Write-Host "`n" +az provider show --namespace Microsoft.ExtendedLocation -o table +Write-Host "`n" +az provider show --namespace Microsoft.AzureArcData -o table +Write-Host "`n" + +# Adding Azure Arc CLI extensions +Write-Host "Adding Azure Arc CLI extensions" +Write-Host "`n" +az extension add --name "connectedk8s" -y +az extension add --name "k8s-configuration" -y +az extension add --name "k8s-extension" -y +az extension add --name "customlocation" -y + +Write-Host "`n" +az -v + +# Getting AKS cluster credentials kubeconfig file +Write-Host "Getting AKS cluster credentials" +Write-Host "`n" +az aks get-credentials --resource-group $env:resourceGroup ` + --name $env:clusterName --admin + +Write-Host "Checking kubernetes nodes" +Write-Host "`n" +kubectl get nodes +Write-Host "`n" + +# Onboarding the AKS cluster as an Azure Arc enabled Kubernetes cluster +Write-Host "Onboarding the cluster as an Azure Arc enabled Kubernetes cluster" +Write-Host "`n" + +# Monitor pods across namespaces +$kubectlMonShell = Start-Process -PassThru PowerShell {for (0 -lt 1) {kubectl get pods --all-namespaces; Start-Sleep -Seconds 5; Clear-Host }} + +# Create Kubernetes - Azure Arc Cluster +az connectedk8s connect --name $connectedClusterName ` + --resource-group $env:resourceGroup ` + --location $env:azureLocation ` + --tags 'Project=jumpstart_azure_arc_data_services' ` --custom-locations-oid '51dfe1e8-70c6-4de5-a08e-e18aff23d815' + # This is the Custom Locations Enterprise Application ObjectID from AAD + +Start-Sleep -Seconds 10 + +# Create Azure Machine Learning extension +az k8s-extension create --name amlarc-compute ` + --extension-type Microsoft.AzureML.Kubernetes ` + --cluster-type connectedClusters ` + --cluster-name $connectedClusterName ` + --resource-group $env:resourceGroup ` + --scope cluster ` + --configuration-settings enableTraining=True enableInference=True privateEndpointNodeport=True + +# Print out extension status +az k8s-extension show --name amlarc-compute ` + --cluster-type connectedClusters ` + --cluster-name $connectedClusterName ` + --resource-group $env:resourceGroup + +# Changing to Client VM wallpaper +$imgPath="C:\Temp\wallpaper.png" +$code = @' +using System.Runtime.InteropServices; +namespace Win32{ + + public class Wallpaper{ + [DllImport("user32.dll", CharSet=CharSet.Auto)] + static extern int SystemParametersInfo (int uAction , int uParam , string lpvParam , int fuWinIni) ; + + public static void SetWallpaper(string thePath){ + SystemParametersInfo(20,0,thePath,3); + } + } + } +'@ + +add-type $code +[Win32.Wallpaper]::SetWallpaper($imgPath) + +# Kill the open PowerShell monitoring kubectl get pods +# Stop-Process -Id $kubectlMonShell.Id + +# Removing the LogonScript Scheduled Task so it won't run on next reboot +Unregister-ScheduledTask -TaskName "DataServicesLogonScript" -Confirm:$false +Start-Sleep -Seconds 5 + +# Stop-Process -Name powershell -Force + +Stop-Transcript \ No newline at end of file diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/environment.yml b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/environment.yml new file mode 100644 index 0000000000..2f27b9fa73 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/environment.yml @@ -0,0 +1,10 @@ +name: tutorial-env +channels: + - anaconda + - conda-forge +dependencies: + - python=3.6.2 + - pip: + - 'azureml-dataset-runtime[pandas,fuse]~=1.26.0.0' + - azureml-defaults~=1.26.0.0 + - scikit-learn==0.22.1 \ No newline at end of file diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/job.yml b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/job.yml new file mode 100644 index 0000000000..a17556d0ce --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/job.yml @@ -0,0 +1,18 @@ +experiment_name: Tutorial-sklearn-mnist +code: + local_path: ./src +command: python train.py --data-folder {inputs.mnist} --regularization 0.5 +environment: + name: tutorial-env + version: 1 + path: . + conda_file: file:./environment.yml + docker: + image: mcr.microsoft.com/azureml/intelmpi2018.3-ubuntu16.04:20210301.v1 +compute: + target: azureml:arc +inputs: + mnist: + data: azureml:mnist_opendataset:1 + mode: mount + \ No newline at end of file diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/train.py b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/train.py new file mode 100644 index 0000000000..871d5d76d5 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/train.py @@ -0,0 +1,50 @@ + +import argparse +import os +import numpy as np +import glob + +from sklearn.linear_model import LogisticRegression +import joblib + +from azureml.core import Run +from utils import load_data + +# let user feed in 2 parameters, the dataset to mount or download, and the regularization rate of the logistic regression model +parser = argparse.ArgumentParser() +parser.add_argument('--data-folder', type=str, dest='data_folder', help='data folder mounting point') +parser.add_argument('--regularization', type=float, dest='reg', default=0.01, help='regularization rate') +args = parser.parse_args() + +data_folder = args.data_folder +print('Data folder:', data_folder) + +# load train and test set into numpy arrays +# note we scale the pixel intensity values to 0-1 (by dividing it with 255.0) so the model can converge faster. +X_train = load_data(glob.glob(os.path.join(data_folder, '**/train-images-idx3-ubyte.gz'), recursive=True)[0], False) / 255.0 +X_test = load_data(glob.glob(os.path.join(data_folder, '**/t10k-images-idx3-ubyte.gz'), recursive=True)[0], False) / 255.0 +y_train = load_data(glob.glob(os.path.join(data_folder, '**/train-labels-idx1-ubyte.gz'), recursive=True)[0], True).reshape(-1) +y_test = load_data(glob.glob(os.path.join(data_folder, '**/t10k-labels-idx1-ubyte.gz'), recursive=True)[0], True).reshape(-1) + +print(X_train.shape, y_train.shape, X_test.shape, y_test.shape, sep = '\n') + +# get hold of the current run +run = Run.get_context() + +print('Train a logistic regression model with regularization rate of', args.reg) +clf = LogisticRegression(C=1.0/args.reg, solver="liblinear", multi_class="auto", random_state=42) +clf.fit(X_train, y_train) + +print('Predict the test set') +y_hat = clf.predict(X_test) + +# calculate accuracy on the prediction +acc = np.average(y_hat == y_test) +print('Accuracy is', acc) + +run.log('regularization rate', np.float(args.reg)) +run.log('accuracy', np.float(acc)) + +os.makedirs('outputs', exist_ok=True) +# note file saved in the outputs folder is automatically uploaded into experiment record +joblib.dump(value=clf, filename='outputs/sklearn_mnist_model.pkl') diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/utils.py b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/utils.py new file mode 100644 index 0000000000..98170adae8 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-cli/src/utils.py @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import gzip +import numpy as np +import struct + + +# load compressed MNIST gz files and return numpy arrays +def load_data(filename, label=False): + with gzip.open(filename) as gz: + struct.unpack('I', gz.read(4)) + n_items = struct.unpack('>I', gz.read(4)) + if not label: + n_rows = struct.unpack('>I', gz.read(4))[0] + n_cols = struct.unpack('>I', gz.read(4))[0] + res = np.frombuffer(gz.read(n_items[0] * n_rows * n_cols), dtype=np.uint8) + res = res.reshape(n_items[0], n_rows * n_cols) + else: + res = np.frombuffer(gz.read(n_items[0]), dtype=np.uint8) + res = res.reshape(n_items[0], 1) + return res + + +# one-hot encode a 1-D array +def one_hot_encode(array, num_of_classes): + return np.eye(num_of_classes)[array.reshape(-1)] diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/arc-img-classification-training.ipynb b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/arc-img-classification-training.ipynb new file mode 100644 index 0000000000..87a5f86078 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/arc-img-classification-training.ipynb @@ -0,0 +1,949 @@ +{ + "cells": [ + { + "cell_type": "code", + "source": [ + "%matplotlib inline\r\n", + "import numpy as np\r\n", + "import matplotlib.pyplot as plt\r\n", + "\r\n", + "import azureml.core\r\n", + "from azureml.core import Workspace\r\n", + "\r\n", + "# check core SDK version number, note AML Python SDK 1.30 or above is required for this sample notebook\r\n", + "print(\"Azure ML SDK Version: \", azureml.core.VERSION)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Azure ML SDK Version: 1.31.0\n" + ] + } + ], + "execution_count": 1, + "metadata": { + "gather": { + "logged": 1626959728577 + } + } + }, + { + "cell_type": "code", + "source": [ + "# Load workspace configuration\r\n", + "ws = Workspace.from_config()\r\n", + "print(ws.name, ws.location, ws.resource_group, ws.subscription_id, sep='\\t')" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "aia-arc-aks-aml-ws\teastus\taia-arc-aml-ws-rg\tce859648-30e1-4135-9d0f-8358aebfe789\n" + ] + } + ], + "execution_count": 2, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959729331 + } + } + }, + { + "cell_type": "code", + "source": [ + "experiment_name = 'Tutorial-sklearn-mnist-arc'\r\n", + "\r\n", + "from azureml.core import Experiment\r\n", + "exp = Experiment(workspace=ws, name=experiment_name)" + ], + "outputs": [], + "execution_count": 3, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959729420 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "We see:\r\n", + "![Experiment](https://i.imgur.com/NGY2iSi.png)" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "from azureml.core.compute import KubernetesCompute\r\n", + "from azureml.core.compute import ComputeTarget\r\n", + "import os\r\n", + "\r\n", + "# choose a name for your Azure Arc-enabled Kubernetes compute\r\n", + "amlarc_compute_name = os.environ.get(\"AML_COMPUTE_CLUSTER_NAME\", \"arc\")\r\n", + "\r\n", + "if amlarc_compute_name in ws.compute_targets:\r\n", + " amlarc_compute = ws.compute_targets[amlarc_compute_name]\r\n", + " if amlarc_compute and type(amlarc_compute) is KubernetesCompute:\r\n", + " print(\"found compute target: \" + amlarc_compute_name)\r\n", + "else:\r\n", + " print(\"do this manually...\")\r\n", + " # ... " + ], + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "found compute target: arc\n" + ] + } + ], + "execution_count": 4, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959729740 + } + } + }, + { + "cell_type": "code", + "source": [ + "from azureml.core import Dataset\r\n", + "from azureml.opendatasets import MNIST\r\n", + "\r\n", + "data_folder = os.path.join(os.getcwd(), 'data')\r\n", + "os.makedirs(data_folder, exist_ok=True)\r\n", + "\r\n", + "mnist_file_dataset = MNIST.get_file_dataset()\r\n", + "mnist_file_dataset.download(data_folder, overwrite=True)\r\n", + "\r\n", + "mnist_file_dataset = mnist_file_dataset.register(workspace=ws,\r\n", + " name='mnist_opendataset',\r\n", + " description='training and test dataset',\r\n", + " create_new_version=True)" + ], + "outputs": [], + "execution_count": 5, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959737500 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "We see:\r\n", + "![Dataset](https://i.imgur.com/GwX3O2a.png)\r\n", + "\r\n", + "And:\r\n", + "![Dataset](https://i.imgur.com/SPNAF5S.png)" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "ls data/https%3A/%2Fazureopendatastorage.azurefd.net/mnist/" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[0m\u001b[01;32mt10k-images-idx3-ubyte.gz\u001b[0m* \u001b[01;32mtrain-images-idx3-ubyte.gz\u001b[0m*\r\n", + "\u001b[01;32mt10k-labels-idx1-ubyte.gz\u001b[0m* \u001b[01;32mtrain-labels-idx1-ubyte.gz\u001b[0m*\r\n" + ] + } + ], + "execution_count": 6, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959737703 + } + } + }, + { + "cell_type": "code", + "source": [ + "# make sure utils.py is in the same directory as this code\r\n", + "from utils import load_data\r\n", + "import glob\r\n", + "\r\n", + "# note we also shrink the intensity values (X) from 0-255 to 0-1. This helps the model converge faster.\r\n", + "X_train = load_data(glob.glob(os.path.join(data_folder,\"**/train-images-idx3-ubyte.gz\"), recursive=True)[0], False) / 255.0\r\n", + "X_test = load_data(glob.glob(os.path.join(data_folder,\"**/t10k-images-idx3-ubyte.gz\"), recursive=True)[0], False) / 255.0\r\n", + "y_train = load_data(glob.glob(os.path.join(data_folder,\"**/train-labels-idx1-ubyte.gz\"), recursive=True)[0], True).reshape(-1)\r\n", + "y_test = load_data(glob.glob(os.path.join(data_folder,\"**/t10k-labels-idx1-ubyte.gz\"), recursive=True)[0], True).reshape(-1)\r\n", + "\r\n", + "# now let's show some randomly chosen images from the traininng set.\r\n", + "count = 0\r\n", + "sample_size = 30\r\n", + "plt.figure(figsize = (16, 6))\r\n", + "for i in np.random.permutation(X_train.shape[0])[:sample_size]:\r\n", + " count = count + 1\r\n", + " plt.subplot(1, sample_size, count)\r\n", + " plt.axhline('')\r\n", + " plt.axvline('')\r\n", + " plt.text(x=10, y=-10, s=y_train[i], fontsize=18)\r\n", + " plt.imshow(X_train[i].reshape(28, 28), cmap=plt.cm.Greys)\r\n", + "plt.show()" + ], + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + } + } + ], + "execution_count": 7, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959739993 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "# Training specs" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "import os\r\n", + "script_folder = os.path.join(os.getcwd(), \"sklearn-mnist-arc\")\r\n", + "os.makedirs(script_folder, exist_ok=True)" + ], + "outputs": [], + "execution_count": 8, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959740061 + } + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile $script_folder/train.py\r\n", + "\r\n", + "import argparse\r\n", + "import os\r\n", + "import numpy as np\r\n", + "import glob\r\n", + "\r\n", + "from sklearn.linear_model import LogisticRegression\r\n", + "import joblib\r\n", + "\r\n", + "from azureml.core import Run\r\n", + "from utils import load_data\r\n", + "\r\n", + "# let user feed in 2 parameters, the dataset to mount or download, and the regularization rate of the logistic regression model\r\n", + "parser = argparse.ArgumentParser()\r\n", + "parser.add_argument('--data-folder', type=str, dest='data_folder', help='data folder mounting point')\r\n", + "parser.add_argument('--regularization', type=float, dest='reg', default=0.01, help='regularization rate')\r\n", + "args = parser.parse_args()\r\n", + "\r\n", + "data_folder = args.data_folder\r\n", + "print('Data folder:', data_folder)\r\n", + "\r\n", + "# load train and test set into numpy arrays\r\n", + "# note we scale the pixel intensity values to 0-1 (by dividing it with 255.0) so the model can converge faster.\r\n", + "X_train = load_data(glob.glob(os.path.join(data_folder, '**/train-images-idx3-ubyte.gz'), recursive=True)[0], False) / 255.0\r\n", + "X_test = load_data(glob.glob(os.path.join(data_folder, '**/t10k-images-idx3-ubyte.gz'), recursive=True)[0], False) / 255.0\r\n", + "y_train = load_data(glob.glob(os.path.join(data_folder, '**/train-labels-idx1-ubyte.gz'), recursive=True)[0], True).reshape(-1)\r\n", + "y_test = load_data(glob.glob(os.path.join(data_folder, '**/t10k-labels-idx1-ubyte.gz'), recursive=True)[0], True).reshape(-1)\r\n", + "\r\n", + "print(X_train.shape, y_train.shape, X_test.shape, y_test.shape, sep = '\\n')\r\n", + "\r\n", + "# get hold of the current run\r\n", + "run = Run.get_context()\r\n", + "\r\n", + "print('Train a logistic regression model with regularization rate of', args.reg)\r\n", + "clf = LogisticRegression(C=1.0/args.reg, solver=\"liblinear\", multi_class=\"auto\", random_state=42)\r\n", + "clf.fit(X_train, y_train)\r\n", + "\r\n", + "print('Predict the test set')\r\n", + "y_hat = clf.predict(X_test)\r\n", + "\r\n", + "# calculate accuracy on the prediction\r\n", + "acc = np.average(y_hat == y_test)\r\n", + "print('Accuracy is', acc)\r\n", + "\r\n", + "run.log('regularization rate', np.float(args.reg))\r\n", + "run.log('accuracy', np.float(acc))\r\n", + "\r\n", + "os.makedirs('outputs', exist_ok=True)\r\n", + "# note file saved in the outputs folder is automatically uploaded into experiment record\r\n", + "joblib.dump(value=clf, filename='outputs/sklearn_mnist_model.pkl')" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting /mnt/batch/tasks/shared/LS_root/mounts/clusters/mdrrahman1/code/Users/mdrrahman/arcdemo1/sklearn-mnist-arc/train.py\n" + ] + } + ], + "execution_count": 9, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "import shutil\r\n", + "shutil.copy('utils.py', script_folder)" + ], + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 10, + "data": { + "text/plain": "'/mnt/batch/tasks/shared/LS_root/mounts/clusters/mdrrahman1/code/Users/mdrrahman/arcdemo1/sklearn-mnist-arc/utils.py'" + }, + "metadata": {} + } + ], + "execution_count": 10, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959740178 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "We see:\r\n", + "![New folder and training](https://i.imgur.com/DvA8FVZ.png)" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "markdown", + "source": [ + "# Connect to Arc now" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "from azureml.core.environment import Environment\r\n", + "from azureml.core.conda_dependencies import CondaDependencies\r\n", + "\r\n", + "# to install required packages\r\n", + "env = Environment('arc-training-env')\r\n", + "cd = CondaDependencies.create(pip_packages=['azureml-dataset-runtime[pandas,fuse]', 'azureml-defaults'], conda_packages = ['scikit-learn==0.22.1'])\r\n", + "\r\n", + "env.python.conda_dependencies = cd\r\n", + "\r\n", + "# Register environment to re-use later\r\n", + "env.register(workspace = ws)" + ], + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 11, + "data": { + "text/plain": "{\n \"databricks\": {\n \"eggLibraries\": [],\n \"jarLibraries\": [],\n \"mavenLibraries\": [],\n \"pypiLibraries\": [],\n \"rcranLibraries\": []\n },\n \"docker\": {\n \"arguments\": [],\n \"baseDockerfile\": null,\n \"baseImage\": \"mcr.microsoft.com/azureml/openmpi3.1.2-ubuntu18.04:20210531.v1\",\n \"baseImageRegistry\": {\n \"address\": null,\n \"password\": null,\n \"registryIdentity\": null,\n \"username\": null\n },\n \"enabled\": false,\n \"platform\": {\n \"architecture\": \"amd64\",\n \"os\": \"Linux\"\n },\n \"sharedVolumes\": true,\n \"shmSize\": null\n },\n \"environmentVariables\": {\n \"EXAMPLE_ENV_VAR\": \"EXAMPLE_VALUE\"\n },\n \"inferencingStackVersion\": null,\n \"name\": \"arc-training-env\",\n \"python\": {\n \"baseCondaEnvironment\": null,\n \"condaDependencies\": {\n \"channels\": [\n \"anaconda\",\n \"conda-forge\"\n ],\n \"dependencies\": [\n \"python=3.6.2\",\n {\n \"pip\": [\n \"azureml-dataset-runtime[pandas,fuse]~=1.31.0\",\n \"azureml-defaults~=1.31.0\"\n ]\n },\n \"scikit-learn==0.22.1\"\n ],\n \"name\": \"azureml_99b2427e2469a67faef42942cc516383\"\n },\n \"condaDependenciesFile\": null,\n \"interpreterPath\": \"python\",\n \"userManagedDependencies\": false\n },\n \"r\": null,\n \"spark\": {\n \"packages\": [],\n \"precachePackages\": true,\n \"repositories\": []\n },\n \"version\": \"1\"\n}" + }, + "metadata": {} + } + ], + "execution_count": 11, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959740557 + } + } + }, + { + "cell_type": "code", + "source": [ + "from azureml.core import ScriptRunConfig\r\n", + "\r\n", + "args = ['--data-folder', mnist_file_dataset.as_mount(), '--regularization', 0.5]\r\n", + "\r\n", + "src = ScriptRunConfig(source_directory=script_folder,\r\n", + " script='train.py', \r\n", + " arguments=args,\r\n", + " compute_target=amlarc_compute,\r\n", + " environment=env)" + ], + "outputs": [], + "execution_count": 12, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959740617 + } + } + }, + { + "cell_type": "code", + "source": [ + "run = exp.submit(config=src)\r\n", + "run" + ], + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 13, + "data": { + "text/plain": "Run(Experiment: Tutorial-sklearn-mnist-arc,\nId: Tutorial-sklearn-mnist-arc_1626959741_57706634,\nType: azureml.scriptrun,\nStatus: Starting)", + "text/html": "
ExperimentIdTypeStatusDetails PageDocs Page
Tutorial-sklearn-mnist-arcTutorial-sklearn-mnist-arc_1626959741_57706634azureml.scriptrunStartingLink to Azure Machine Learning studioLink to Documentation
" + }, + "metadata": {} + } + ], + "execution_count": 13, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959741715 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "We see:\r\n", + "![Run 1](https://i.imgur.com/vhI67L5.png)\r\n", + "\r\n", + "Image build logs:\r\n", + "![Image build](https://i.imgur.com/rhG3br2.png)\r\n", + "\r\n", + "Container build:\r\n", + "![Container build](https://i.imgur.com/OpevDVP.png)\r\n", + "\r\n", + "Goes into queued:\r\n", + "![Job stage](https://i.imgur.com/6IK2gmE.png)" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "from azureml.widgets import RunDetails\r\n", + "RunDetails(run).show()" + ], + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "_UserRunWidget(widget_settings={'childWidgetDisplay': 'popup', 'send_telemetry': False, 'log_level': 'INFO', '…", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "c8a2bebbaf8c41879f743ecd0cb08615" + } + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "application/aml.mini.widget.v1": "{\"status\": \"Completed\", \"workbench_run_details_uri\": \"https://ml.azure.com/runs/Tutorial-sklearn-mnist-arc_1626959741_57706634?wsid=/subscriptions/ce859648-30e1-4135-9d0f-8358aebfe789/resourcegroups/aia-arc-aml-ws-rg/workspaces/aia-arc-aks-aml-ws&tid=72f988bf-86f1-41af-91ab-2d7cd011db47\", \"run_id\": \"Tutorial-sklearn-mnist-arc_1626959741_57706634\", \"run_properties\": {\"run_id\": \"Tutorial-sklearn-mnist-arc_1626959741_57706634\", \"created_utc\": \"2021-07-22T13:15:41.980828Z\", \"properties\": {\"_azureml.ComputeTargetType\": \"kubernetes\", \"ContentSnapshotId\": \"ac47ff6d-57c5-4a61-b596-d52215c05ada\", \"ProcessInfoFile\": \"azureml-logs/process_info.json\", \"ProcessStatusFile\": \"azureml-logs/process_status.json\", \"JobType\": \"RegularJob\", \"GpuCount\": \"0\", \"Cluster\": \"Arc-Data-AKS\"}, \"tags\": {\"Kubernetes job status\": \"Succeeded\", \"Queue Information\": \"Job is in scheduling state. at 07/22/2021 13:16:00 +00:00\", \"amlarc job id\": \"a98dbfd3bbbc0386035172c25d5cc150\"}, \"script_name\": null, \"arguments\": null, \"end_time_utc\": \"2021-07-22T13:20:37.105039Z\", \"status\": \"Completed\", \"log_files\": {\"azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt?sv=2019-02-02&sr=b&sig=RGmkk7DL3AyRyR4V3fp6ikxd658t6fe1Su7VGgfWxF0%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt?sv=2019-02-02&sr=b&sig=r0fVTFbQgm7xGzS7WjIRd%2Bw5RYfdePygno6dci0PxAc%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"azureml-logs/70_driver_log.txt\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/70_driver_log.txt?sv=2019-02-02&sr=b&sig=aW5xXLIk4qO86iOC1goRLL10VvKhu1tQZxRH8tLffnA%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"azureml-logs/75_job_post-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/75_job_post-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt?sv=2019-02-02&sr=b&sig=8BKRmaAlIPQnedj2I63EYASICPXPKsLdGmY%2BleYpVwE%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0?sv=2019-02-02&sr=b&sig=F%2FO%2B6SeyJLjHY6a%2Fp3WmLDzUwioXtCXSmukYKYjjhtY%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"logs/azureml/dataprep/backgroundProcess.log\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/dataprep/backgroundProcess.log?sv=2019-02-02&sr=b&sig=9bm3R3DE5UITUqMNIUkqVic4398lGAK3Trtmw4ndGWg%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"logs/azureml/dataprep/backgroundProcess_Telemetry.log\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/dataprep/backgroundProcess_Telemetry.log?sv=2019-02-02&sr=b&sig=PcG752p4%2FQ3bl%2BTtfloHFPKcMTfnnLRGdq%2Bi%2BjtemCY%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"logs/azureml/job_prep_azureml.log\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/job_prep_azureml.log?sv=2019-02-02&sr=b&sig=upVpuBjKekDPzb%2Bqgg1ZpU%2FMn1hFwmjy3bfuMwdnBbg%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"logs/azureml/job_release_azureml.log\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/job_release_azureml.log?sv=2019-02-02&sr=b&sig=jb6kSbD9yLvwIFEBLMDGoZtgAp0bv5V%2FA5btZwvrEcM%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\", \"logs/azureml/master0_670_azureml.log\": \"https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/master0_670_azureml.log?sv=2019-02-02&sr=b&sig=fr9i02wMtgshGv19iTimIWsVQ%2FkTZ70XTRU9kuJEf7g%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r\"}, \"log_groups\": [[\"logs/azureml/dataprep/backgroundProcess.log\", \"logs/azureml/dataprep/backgroundProcess_Telemetry.log\", \"logs/azureml/job_prep_azureml.log\", \"logs/azureml/job_release_azureml.log\"], [\"logs/azureml/master0_670_azureml.log\"], [\"azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\"], [\"azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\"], [\"azureml-logs/70_driver_log.txt\"], [\"azureml-logs/75_job_post-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\"], [\"azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0\"]], \"run_duration\": \"0:04:55\", \"run_number\": \"2\", \"run_queued_details\": {\"status\": \"Completed\", \"details\": null}}, \"child_runs\": [], \"children_metrics\": {}, \"run_metrics\": [{\"name\": \"regularization rate\", \"run_id\": \"Tutorial-sklearn-mnist-arc_1626959741_57706634\", \"categories\": [0], \"series\": [{\"data\": [0.5]}]}, {\"name\": \"accuracy\", \"run_id\": \"Tutorial-sklearn-mnist-arc_1626959741_57706634\", \"categories\": [0], \"series\": [{\"data\": [0.9193]}]}], \"run_logs\": \"2021-07-22 13:18:37 2021-07-22 13:18:37,465 [INFO] root: load config from env successfully\\n2021-07-22 13:18:37 2021-07-22 13:18:37,751 [INFO] root: register artifacts: ['/azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0'], status_code: 200\\n2021-07-22 13:18:37 2021-07-22 13:18:37,825 [INFO] root: watch streamable file /root/job_log_agent.log, will upload to /azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0\\n2021-07-22 13:18:37 2021-07-22 13:18:37,825 [INFO] root: check if target file /var/log/compute/00_stdout.txt exist, if not, waitting...\\n2021-07-22 13:18:40 2021-07-22 13:18:38,033 [INFO] root: register artifacts: ['/azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt'], status_code: 200\\n2021-07-22 13:18:40 2021-07-22 13:18:38,052 [INFO] root: watch streamable file /var/log/compute/00_stdout.txt, will upload to /azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\\n2021-07-22 13:18:40 2021-07-22 13:18:38,052 [INFO] root: /var/log/compute/services folder does not exist, creating it with mod 777...\\n2021-07-22 13:18:40 2021-07-22 13:18:40,353 [INFO] root: register artifacts: ['/azureml-logs/70_driver_log.txt'], status_code: 200\\n2021-07-22 13:18:40 2021-07-22 13:18:40,392 [INFO] root: watch streamable file /var/log/compute/local_logs/70_driver_log.txt, will upload to /azureml-logs/70_driver_log.txt\\n2021-07-22 13:18:40 2021-07-22 13:18:40,658 [INFO] root: register artifacts: ['/azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt'], status_code: 200\\n2021-07-22 13:18:40 2021-07-22 13:18:40,675 [INFO] root: watch streamable file /var/log/compute/local_logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt, will upload to /azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt\\n2021-07-22 13:18:43 2021-07-22 13:18:40,857 [INFO] root: register artifacts: ['/logs/azureml/master0_670_azureml.log'], status_code: 200\\n2021-07-22 13:18:43 2021-07-22 13:18:40,885 [INFO] root: watch streamable file /workspaceblobstore/azureml/Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/master0_670_azureml.log, will upload to /logs/azureml/master0_670_azureml.log\\n2021-07-22 13:18:43 2021-07-22 13:18:41,059 [INFO] root: register artifacts: ['/logs/azureml/job_prep_azureml.log'], status_code: 200\\n2021-07-22 13:18:43 2021-07-22 13:18:41,078 [INFO] root: watch streamable file /workspaceblobstore/azureml/Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/job_prep_azureml.log, will upload to /logs/azureml/job_prep_azureml.log\\n2021-07-22 13:18:43 2021-07-22 13:18:41,324 [INFO] root: register artifacts: ['/logs/azureml/dataprep/backgroundProcess.log'], status_code: 200\\n2021-07-22 13:18:43 2021-07-22 13:18:41,342 [INFO] root: watch streamable file /workspaceblobstore/azureml/Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/dataprep/backgroundProcess.log, will upload to /logs/azureml/dataprep/backgroundProcess.log\\n2021-07-22 13:18:43 2021-07-22 13:18:41,528 [INFO] root: register artifacts: ['/logs/azureml/dataprep/backgroundProcess_Telemetry.log'], status_code: 200\\n2021-07-22 13:18:43 2021-07-22 13:18:41,584 [INFO] root: watch streamable file /workspaceblobstore/azureml/Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/dataprep/backgroundProcess_Telemetry.log, will upload to /logs/azureml/dataprep/backgroundProcess_Telemetry.log\\n\\nRun is completed.\", \"graph\": {}, \"widget_settings\": {\"childWidgetDisplay\": \"popup\", \"send_telemetry\": false, \"log_level\": \"INFO\", \"sdk_version\": \"1.31.0\"}, \"loading\": false}" + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n" + ] + } + ], + "execution_count": 14, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626959741815 + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "# specify show_output to True for a verbose log\r\n", + "run.wait_for_completion(show_output=True)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "RunId: Tutorial-sklearn-mnist-arc_1626959741_57706634\n", + "Web View: https://ml.azure.com/runs/Tutorial-sklearn-mnist-arc_1626959741_57706634?wsid=/subscriptions/ce859648-30e1-4135-9d0f-8358aebfe789/resourcegroups/aia-arc-aml-ws-rg/workspaces/aia-arc-aks-aml-ws&tid=72f988bf-86f1-41af-91ab-2d7cd011db47\n", + "\n", + "Execution Summary\n", + "=================\n", + "RunId: Tutorial-sklearn-mnist-arc_1626959741_57706634\n", + "Web View: https://ml.azure.com/runs/Tutorial-sklearn-mnist-arc_1626959741_57706634?wsid=/subscriptions/ce859648-30e1-4135-9d0f-8358aebfe789/resourcegroups/aia-arc-aml-ws-rg/workspaces/aia-arc-aks-aml-ws&tid=72f988bf-86f1-41af-91ab-2d7cd011db47\n", + "\n" + ] + }, + { + "output_type": "execute_result", + "execution_count": 15, + "data": { + "text/plain": "{'runId': 'Tutorial-sklearn-mnist-arc_1626959741_57706634',\n 'target': 'arc',\n 'status': 'Completed',\n 'startTimeUtc': '2021-07-22T13:18:05.846496Z',\n 'endTimeUtc': '2021-07-22T13:20:37.105039Z',\n 'properties': {'_azureml.ComputeTargetType': 'kubernetes',\n 'ContentSnapshotId': 'ac47ff6d-57c5-4a61-b596-d52215c05ada',\n 'ProcessInfoFile': 'azureml-logs/process_info.json',\n 'ProcessStatusFile': 'azureml-logs/process_status.json',\n 'JobType': 'RegularJob',\n 'GpuCount': '0',\n 'Cluster': 'Arc-Data-AKS'},\n 'inputDatasets': [{'dataset': {'id': 'bb1def71-0161-40d9-92b5-7b86482cd68e'}, 'consumptionDetails': {'type': 'RunInput', 'inputName': 'input__bb1def71', 'mechanism': 'Mount'}}],\n 'outputDatasets': [],\n 'runDefinition': {'script': 'train.py',\n 'command': '',\n 'useAbsolutePath': False,\n 'arguments': ['--data-folder',\n 'DatasetConsumptionConfig:input__bb1def71',\n '--regularization',\n '0.5'],\n 'sourceDirectoryDataStore': None,\n 'framework': 'Python',\n 'communicator': 'None',\n 'target': 'arc',\n 'dataReferences': {},\n 'data': {'input__bb1def71': {'dataLocation': {'dataset': {'id': 'bb1def71-0161-40d9-92b5-7b86482cd68e',\n 'name': 'mnist_opendataset',\n 'version': '1'},\n 'dataPath': None,\n 'uri': None},\n 'mechanism': 'Mount',\n 'environmentVariableName': 'input__bb1def71',\n 'pathOnCompute': None,\n 'overwrite': False}},\n 'outputData': {},\n 'datacaches': [],\n 'jobName': None,\n 'maxRunDurationSeconds': 2592000,\n 'nodeCount': 1,\n 'priority': None,\n 'credentialPassthrough': False,\n 'identity': None,\n 'environment': {'name': 'arc-training-env',\n 'version': '1',\n 'python': {'interpreterPath': 'python',\n 'userManagedDependencies': False,\n 'condaDependencies': {'channels': ['anaconda', 'conda-forge'],\n 'dependencies': ['python=3.6.2',\n {'pip': ['azureml-dataset-runtime[pandas,fuse]~=1.31.0',\n 'azureml-defaults~=1.31.0']},\n 'scikit-learn==0.22.1'],\n 'name': 'azureml_99b2427e2469a67faef42942cc516383'},\n 'baseCondaEnvironment': None},\n 'environmentVariables': {'EXAMPLE_ENV_VAR': 'EXAMPLE_VALUE'},\n 'docker': {'baseImage': 'mcr.microsoft.com/azureml/openmpi3.1.2-ubuntu18.04:20210531.v1',\n 'platform': {'os': 'Linux', 'architecture': 'amd64'},\n 'baseDockerfile': None,\n 'baseImageRegistry': {'address': None, 'username': None, 'password': None},\n 'enabled': False,\n 'arguments': []},\n 'spark': {'repositories': [], 'packages': [], 'precachePackages': True},\n 'inferencingStackVersion': None},\n 'history': {'outputCollection': True,\n 'directoriesToWatch': ['logs'],\n 'enableMLflowTracking': True,\n 'snapshotProject': True},\n 'spark': {'configuration': {'spark.app.name': 'Azure ML Experiment',\n 'spark.yarn.maxAppAttempts': '1'}},\n 'parallelTask': {'maxRetriesPerWorker': 0,\n 'workerCountPerNode': 1,\n 'terminalExitCodes': None,\n 'configuration': {}},\n 'amlCompute': {'name': None,\n 'vmSize': None,\n 'retainCluster': False,\n 'clusterMaxNodeCount': None},\n 'aiSuperComputer': {'instanceType': None,\n 'imageVersion': None,\n 'location': None,\n 'aiSuperComputerStorageData': None,\n 'interactive': False,\n 'scalePolicy': None,\n 'virtualClusterArmId': None,\n 'tensorboardLogDirectory': None,\n 'sshPublicKey': None,\n 'enableAzmlInt': True,\n 'priority': None,\n 'slaTier': None},\n 'kubernetesCompute': {'instanceType': None},\n 'tensorflow': {'workerCount': 1, 'parameterServerCount': 1},\n 'mpi': {'processCountPerNode': 1},\n 'pyTorch': {'communicationBackend': 'nccl', 'processCount': None},\n 'hdi': {'yarnDeployMode': 'Cluster'},\n 'containerInstance': {'region': None, 'cpuCores': 2.0, 'memoryGb': 3.5},\n 'exposedPorts': None,\n 'docker': {'useDocker': False,\n 'sharedVolumes': True,\n 'shmSize': '2g',\n 'arguments': []},\n 'cmk8sCompute': {'configuration': {}},\n 'commandReturnCodeConfig': {'returnCode': 'Zero',\n 'successfulReturnCodes': []},\n 'environmentVariables': {},\n 'applicationEndpoints': {},\n 'parameters': []},\n 'logFiles': {'azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt?sv=2019-02-02&sr=b&sig=RGmkk7DL3AyRyR4V3fp6ikxd658t6fe1Su7VGgfWxF0%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt?sv=2019-02-02&sr=b&sig=r0fVTFbQgm7xGzS7WjIRd%2Bw5RYfdePygno6dci0PxAc%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'azureml-logs/70_driver_log.txt': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/70_driver_log.txt?sv=2019-02-02&sr=b&sig=aW5xXLIk4qO86iOC1goRLL10VvKhu1tQZxRH8tLffnA%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'azureml-logs/75_job_post-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/75_job_post-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt?sv=2019-02-02&sr=b&sig=8BKRmaAlIPQnedj2I63EYASICPXPKsLdGmY%2BleYpVwE%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0?sv=2019-02-02&sr=b&sig=F%2FO%2B6SeyJLjHY6a%2Fp3WmLDzUwioXtCXSmukYKYjjhtY%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'logs/azureml/dataprep/backgroundProcess.log': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/dataprep/backgroundProcess.log?sv=2019-02-02&sr=b&sig=9bm3R3DE5UITUqMNIUkqVic4398lGAK3Trtmw4ndGWg%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'logs/azureml/dataprep/backgroundProcess_Telemetry.log': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/dataprep/backgroundProcess_Telemetry.log?sv=2019-02-02&sr=b&sig=PcG752p4%2FQ3bl%2BTtfloHFPKcMTfnnLRGdq%2Bi%2BjtemCY%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'logs/azureml/job_prep_azureml.log': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/job_prep_azureml.log?sv=2019-02-02&sr=b&sig=upVpuBjKekDPzb%2Bqgg1ZpU%2FMn1hFwmjy3bfuMwdnBbg%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'logs/azureml/job_release_azureml.log': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/job_release_azureml.log?sv=2019-02-02&sr=b&sig=jb6kSbD9yLvwIFEBLMDGoZtgAp0bv5V%2FA5btZwvrEcM%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r',\n 'logs/azureml/master0_670_azureml.log': 'https://aiaarcaksamlws0982414193.blob.core.windows.net/azureml/ExperimentRun/dcid.Tutorial-sklearn-mnist-arc_1626959741_57706634/logs/azureml/master0_670_azureml.log?sv=2019-02-02&sr=b&sig=fr9i02wMtgshGv19iTimIWsVQ%2FkTZ70XTRU9kuJEf7g%3D&st=2021-07-22T13%3A10%3A13Z&se=2021-07-22T21%3A20%3A13Z&sp=r'},\n 'submittedBy': 'Raki Rahman'}" + }, + "metadata": {} + } + ], + "execution_count": 15, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626960139258 + } + } + }, + { + "cell_type": "code", + "source": [ + "print(run.get_metrics())" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{'regularization rate': 0.5, 'accuracy': 0.9193}\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n" + ] + } + ], + "execution_count": 16, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626960145645 + } + } + }, + { + "cell_type": "code", + "source": [ + "print(run.get_file_names())" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "['azureml-logs/55_azureml-execution-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt', 'azureml-logs/65_job_prep-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt', 'azureml-logs/70_driver_log.txt', 'azureml-logs/75_job_post-tvmps_a98dbfd3bbbc0386035172c25d5cc150-master-0_d.txt', 'azureml-logs/azureml/job_log_agent.log.a98dbfd3bbbc0386035172c25d5cc150-master-0', 'logs/azureml/dataprep/backgroundProcess.log', 'logs/azureml/dataprep/backgroundProcess_Telemetry.log', 'logs/azureml/job_prep_azureml.log', 'logs/azureml/job_release_azureml.log', 'logs/azureml/master0_670_azureml.log', 'outputs/sklearn_mnist_model.pkl']\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n" + ] + } + ], + "execution_count": 17, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626960163110 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "![Run results](https://i.imgur.com/j2A8Qt2.png)" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + }, + { + "cell_type": "code", + "source": [ + "# register model \r\n", + "model = run.register_model(model_name='sklearn_mnist', model_path='outputs/sklearn_mnist_model.pkl')\r\n", + "print(model.name, model.id, model.version, sep='\\t')" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "sklearn_mnist\tsklearn_mnist:1\t1\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n", + "Class KubernetesCompute: This is an experimental class, and may change at any time. Please see https://aka.ms/azuremlexperimental for more information.\n" + ] + } + ], + "execution_count": 18, + "metadata": { + "collapsed": true, + "jupyter": { + "source_hidden": false, + "outputs_hidden": false + }, + "nteract": { + "transient": { + "deleting": false + } + }, + "gather": { + "logged": 1626960271673 + } + } + }, + { + "cell_type": "markdown", + "source": [ + "![New model](https://i.imgur.com/e1F2Oah.png)" + ], + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + } + } + ], + "metadata": { + "kernelspec": { + "name": "python3-azureml", + "language": "python", + "display_name": "Python 3.6 - AzureML" + }, + "language_info": { + "name": "python", + "version": "3.6.9", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "kernel_info": { + "name": "python3-azureml" + }, + "nteract": { + "version": "nteract-front-end@1.0.0" + }, + "microsoft": { + "host": { + "AzureML": { + "notebookHasBeenCompleted": true + } + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/utils.py b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/utils.py new file mode 100644 index 0000000000..98170adae8 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/artifacts/simple-train-sdk/utils.py @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import gzip +import numpy as np +import struct + + +# load compressed MNIST gz files and return numpy arrays +def load_data(filename, label=False): + with gzip.open(filename) as gz: + struct.unpack('I', gz.read(4)) + n_items = struct.unpack('>I', gz.read(4)) + if not label: + n_rows = struct.unpack('>I', gz.read(4))[0] + n_cols = struct.unpack('>I', gz.read(4))[0] + res = np.frombuffer(gz.read(n_items[0] * n_rows * n_cols), dtype=np.uint8) + res = res.reshape(n_items[0], n_rows * n_cols) + else: + res = np.frombuffer(gz.read(n_items[0]), dtype=np.uint8) + res = res.reshape(n_items[0], 1) + return res + + +# one-hot encode a 1-D array +def one_hot_encode(array, num_of_classes): + return np.eye(num_of_classes)[array.reshape(-1)] diff --git a/azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.json b/azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.json new file mode 100644 index 0000000000..d09f1e22d1 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.json @@ -0,0 +1,239 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "windowsAdminUsername": { + "type": "string", + "metadata": { + "description": "Username for Windows account" + } + }, + "windowsAdminPassword": { + "type": "securestring", + "minLength": 12, + "maxLength": 123, + "metadata": { + "description": "Password for Windows account. Password must have 3 of the following: 1 lower case character, 1 upper case character, 1 number, and 1 special character. The value must be between 12 and 123 characters long." + } + }, + "myIpAddress": { + "type": "string", + "metadata": { + "description": "IP address allowed SSH and RDP access to Azure resources. Usually this is your home or office public IP address." + } + }, + "sshRSAPublicKey": { + "type": "securestring", + "metadata": { + "description": "RSA public key used for securing SSH access to Azure resources." + } + }, + "spnClientId": { + "type": "string", + "metadata": { + "description": "Azure service principal client id" + } + }, + "spnClientSecret": { + "type": "securestring", + "metadata": { + "description": "Azure service principal client secret" + } + }, + "spnTenantId": { + "type": "string", + "metadata": { + "description": "Azure AD tenant id for your service principal" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Name for your log analytics workspace" + } + }, + "kubernetesVersion": { + "defaultValue": "1.19.11", + "type": "string", + "metadata": { + "description": "The version of Kubernetes." + } + }, + "clusterName": { + "type": "string", + "defaultValue": "Arc-Data-AKS", + "metadata": { + "description": "The name of the Kubernetes cluster resource." + } + }, + "dnsPrefix": { + "type": "string", + "metadata": { + "description": "Optional DNS prefix to use with hosted Kubernetes API server FQDN." + } + }, + "deploySQLMI": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "SQL Managed Instance deployment" + } + }, + "deployPostgreSQL": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "PostgreSQL Hyperscale deployment" + } + }, + "templateBaseUrl": { + "type": "string", + "metadata": { + "description": "Base URL for ARM template" + } + } + }, + "variables": { + "clientVmTemplateUrl": "[uri(parameters('templateBaseUrl'), 'clientVm.json')]", + "aksTemplateUrl": "[uri(parameters('templateBaseUrl'), 'aks.json')]", + "logAnalyticsUrl": "[uri(parameters('templateBaseUrl'), 'logAnalytics.json')]", + "VNETUrl": "[uri(parameters('templateBaseUrl'), 'VNET.json')]", + // Virtual Network configuration + "virtualNetworkName": "Arc-Data-VNet", + "subnetName": "Arc-Data-Subnet", + "addressPrefix": "172.16.0.0/16", + "subnetAddressPrefix": "172.16.1.0/24" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "comments": "Deploys a VNET and Subnet for Client VM", + "apiVersion": "2019-10-01", + "name": "VNETDeployment", + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('VNETUrl')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "virtualNetworkName": { + "value": "[variables('virtualNetworkName')]" + }, + "subnetName": { + "value": "[variables('subnetName')]" + }, + "addressPrefix": { + "value": "[variables('addressPrefix')]" + }, + "subnetAddressPrefix": { + "value": "[variables('subnetAddressPrefix')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "comments": "Deploys an AKS Cluster", + "apiVersion": "2019-10-01", + "name": "aksDeployment", + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('aksTemplateUrl')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "sshRSAPublicKey": { + "value": "[parameters('sshRSAPublicKey')]" + }, + "spnClientId": { + "value": "[parameters('spnClientId')]" + }, + "spnClientSecret": { + "value": "[parameters('spnClientSecret')]" + }, + "kubernetesVersion": { + "value": "[parameters('kubernetesVersion')]" + }, + "dnsPrefix": { + "value": "[parameters('dnsPrefix')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "comments": "Deploys the Client Windows VM", + "apiVersion": "2019-10-01", + "name": "clientVmDeployment", + "dependsOn": ["VNETDeployment"], + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('clientVmTemplateUrl')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "windowsAdminUsername": { + "value": "[parameters('windowsAdminUsername')]" + }, + "windowsAdminPassword": { + "value": "[parameters('windowsAdminPassword')]" + }, + "spnClientId": { + "value": "[parameters('spnClientId')]" + }, + "spnClientSecret": { + "value": "[parameters('spnClientSecret')]" + }, + "spnTenantId": { + "value": "[parameters('spnTenantId')]" + }, + "myIpAddress": { + "value": "[parameters('myIpAddress')]" + }, + "workspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "clusterName": { + "value": "[parameters('clusterName')]" + }, + "templateBaseUrl": { + "value": "[parameters('templateBaseUrl')]" + }, + "virtualNetworkName": { + "value": "[variables('virtualNetworkName')]" + }, + "subnetName": { + "value": "[variables('subnetName')]" + }, + "deploySQLMI": { + "value": "[parameters('deploySQLMI')]" + }, + "deployPostgreSQL": { + "value": "[parameters('deployPostgreSQL')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "comments": "Deploys Azure Log Analytics workspace to support Azure Arc enabled data services logs upload", + "apiVersion": "2019-10-01", + "name": "logAnalyticsDeployment", + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('logAnalyticsUrl')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "workspaceName": { + "value": "[parameters('logAnalyticsWorkspaceName')]" + } + } + } + } + ] +} diff --git a/azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.parameters.json b/azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.parameters.json new file mode 100644 index 0000000000..1478506e38 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/azuredeploy.parameters.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "sshRSAPublicKey": { + "value": "" + }, + "spnClientId": { + "value": "" + }, + "spnClientSecret": { + "value": "" + }, + "spnTenantId": { + "value": "" + }, + "windowsAdminUsername": { + "value": "arcdemo" + }, + "windowsAdminPassword": { + "value": "ArcPassword123!!" + }, + "myIpAddress": { + "value": "" + }, + "logAnalyticsWorkspaceName": { + "value": "" + }, + "deploySQLMI": { + "value": "" + }, + "deployPostgreSQL": { + "value": "" + }, + "templateBaseUrl": { + "value": "https://raw.githubusercontent.com/microsoft/azure_arc/main/azure_arc_ml_jumpstart/aks/arm_template/" + }, + "kubernetesVersion": { + "value": "1.18.17" + }, + "dnsPrefix": { + "value": "arcdata" + } + } +} diff --git a/azure_arc_ml_jumpstart/aks/arm_template/clientVm.json b/azure_arc_ml_jumpstart/aks/arm_template/clientVm.json new file mode 100644 index 0000000000..78e65b4d00 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/clientVm.json @@ -0,0 +1,309 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "myIpAddress": { + "type": "string", + "metadata": { + "description": "Your public IP address, used to RDP to the client VM" + } + }, + "vmName": { + "type": "string", + "defaultValue": "Arc-Data-Client", + "metadata": { + "description": "The name of your Virtual Machine" + } + }, + "windowsAdminUsername": { + "type": "string", + "defaultValue": "arcdemo", + "metadata": { + "description": "Username for the Virtual Machine" + } + }, + "windowsAdminPassword": { + "type": "securestring", + "minLength": 12, + "maxLength": 123, + "defaultValue": "ArcPassword123!!", + "metadata": { + "description": "Password for Windows account. Password must have 3 of the following: 1 lower case character, 1 upper case character, 1 number, and 1 special character. The value must be between 12 and 123 characters long." + } + }, + "windowsOSVersion": { + "type": "string", + "defaultValue": "2019-Datacenter", + "metadata": { + "description": "The Windows version for the VM. This will pick a fully patched image of this given Windows version" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources" + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D8s_v3", + "metadata": { + "description": "The size of the VM" + } + }, + "virtualNetworkName": { + "type": "string", + "defaultValue": "Arc-Data-VNet", + "metadata": { + "description": "Name of the VNET" + } + }, + "subnetName": { + "type": "string", + "defaultValue": "Arc-Data-Subnet", + "metadata": { + "description": "Name of the subnet in the virtual network" + } + }, + "networkSecurityGroupName": { + "type": "string", + "defaultValue": "Arc-Data-Client-NSG", + "metadata": { + "description": "Name of the Network Security Group" + } + }, + "resourceTags": { + "type": "object", + "defaultValue": { + "Project": "jumpstart_azure_arc_data_services" + } + }, + "spnClientId": { + "type": "string", + "metadata": { + "description": "Client id of the service principal" + } + }, + "spnClientSecret": { + "type": "securestring", + "metadata": { + "description": "Client secret of the service principal" + } + }, + "spnAuthority": { + "type": "string", + "defaultValue": "https://login.microsoftonline.com" + }, + "spnTenantId": { + "type": "string", + "metadata": { + "description": "Tenant id of the service principal" + } + }, + "azdataUsername": { + "type": "string", + "defaultValue": "arcdemo" + }, + "azdataPassword": { + "type": "securestring", + "defaultValue": "ArcPassword123!!" + }, + "acceptEula": { + "type": "string", + "defaultValue": "yes" + }, + "arcDcName": { + "type": "string", + "defaultValue": "arcdatactrl" + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Name for the environment Azure Log Analytics workspace" + } + }, + "deploySQLMI": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "SQL Managed Instance deployment" + } + }, + "deployPostgreSQL": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "PostgreSQL Hyperscale deployment" + } + }, + "templateBaseUrl": { + "type": "string", + "metadata": { + "description": "Base URL for ARM template" + } + }, + "clusterName": { + "type": "string", + "defaultValue": "Arc-Data-AKS", + "metadata": { + "description": "The name of the Kubernetes cluster resource." + } + } + }, + "variables": { + "vmName": "[concat(parameters('vmName'))]", + "publicIpAddressName": "[concat(parameters('vmName'), '-PIP' )]", + "networkInterfaceName": "[concat(parameters('vmName'),'-NIC')]", + "subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]", + "osDiskType": "Premium_LRS" + }, + "resources": [ + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2018-10-01", + "name": "[variables('networkInterfaceName')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]", + "[resourceId('Microsoft.Network/publicIpAddresses/', variables('publicIpAddressName'))]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "[variables('subnetRef')]" + }, + "privateIPAllocationMethod": "Dynamic", + "publicIpAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]" + } + } + } + ], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups',parameters('networkSecurityGroupName'))]" + } + } + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2019-02-01", + "name": "[parameters('networkSecurityGroupName')]", + "location": "[parameters('location')]", + "properties": { + "securityRules": [ + { + "name": "allow_RDP_3389", + "properties": { + "priority": 1001, + "protocol": "TCP", + "access": "Allow", + "direction": "Inbound", + "sourceAddressPrefix": "[parameters('myIpAddress')]", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "3389" + } + } + ] + } + }, + { + "type": "Microsoft.Network/publicIpAddresses", + "apiVersion": "2019-02-01", + "name": "[variables('publicIpAddressName')]", + "location": "[parameters('location')]", + "properties": { + "publicIpAllocationMethod": "Static", + "publicIPAddressVersion": "IPv4", + "idleTimeoutInMinutes": 4 + }, + "sku": { + "name": "Basic", + "tier": "Regional" + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2019-03-01", + "name": "[variables('vmName')]", + "location": "[parameters('location')]", + "tags": "[parameters('resourceTags')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces/', variables('networkInterfaceName'))]" + ], + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "storageProfile": { + "osDisk": { + "name": "[concat(variables('vmName'),'-OSDisk')]", + "caching": "ReadWrite", + "createOption": "fromImage", + "managedDisk": { + "storageAccountType": "[variables('osDiskType')]" + }, + "diskSizeGB": 1024 + }, + "imageReference": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "[parameters('windowsOSVersion')]", + "version": "latest" + } + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]" + } + ] + }, + "osProfile": { + "computerName": "[variables('vmName')]", + "adminUsername": "[parameters('windowsAdminUsername')]", + "adminPassword": "[parameters('windowsAdminPassword')]" + } + } + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "name": "[concat(variables('vmName'),'/Bootstrap')]", + "apiVersion": "2019-07-01", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]" + ], + "tags": { + "displayName": "config-choco" + }, + "properties": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[uri(parameters('templateBaseUrl'), 'artifacts/Bootstrap.ps1')]" + ], + "commandToExecute": "[concat('powershell.exe -ExecutionPolicy Bypass -File Bootstrap.ps1', ' -adminUsername ', parameters('windowsAdminUsername'), ' -spnClientId ', parameters('spnClientId'), ' -spnClientSecret ', parameters('spnClientSecret'), ' -spnTenantId ', parameters('spnTenantId'), ' -spnAuthority ', parameters('spnAuthority'), ' -subscriptionId ', subscription().subscriptionId, ' -resourceGroup ', resourceGroup().name, ' -azdataUsername ', parameters('azdataUsername'), ' -azdataPassword ', parameters('azdataPassword'), ' -acceptEula ', parameters('acceptEula'), ' -arcDcName ', parameters('arcDcName'), ' -azureLocation ', parameters('location'), ' -workspaceName ', parameters('workspaceName'), ' -deploySQLMI ', parameters('deploySQLMI'), ' -deployPostgreSQL ', parameters('deployPostgreSQL'), ' -clusterName ', parameters('clusterName'), ' -templateBaseUrl ', parameters('templateBaseUrl'))]" + } + } + } + ], + + "outputs": { + "adminUsername": { + "type": "string", + "value": "[parameters('windowsAdminUsername')]" + }, + "publicIP": { + "type": "string", + "value": "[concat(reference(variables('publicIPAddressName')).IpAddress)]" + } + } +} diff --git a/azure_arc_ml_jumpstart/aks/arm_template/logAnalytics.json b/azure_arc_ml_jumpstart/aks/arm_template/logAnalytics.json new file mode 100644 index 0000000000..e80d2b8ec5 --- /dev/null +++ b/azure_arc_ml_jumpstart/aks/arm_template/logAnalytics.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Name for your log analytics workspace" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure Region to deploy the Log Analytics Workspace" + } + }, + "sku": { + "type": "string", + "defaultValue": "pergb2018", + "metadata": { + "description": "SKU, leave default pergb2018" + } + }, + "tags": { + "type": "object", + "defaultValue": { + "Project": "jumpstart_azure_arc_data_services" + } + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2020-03-01-preview", + "name": "[parameters('workspaceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "sku": { + "name": "[parameters('sku')]" + } + } + } + ] +}