Skip to content

Commit 1eb4a42

Browse files
dariuszparysdtzar
authored andcommitted
cleaner variables (#99)
1 parent 5372bb0 commit 1eb4a42

18 files changed

+328
-221
lines changed

Diff for: .env.example

+11-24
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,39 @@
11
# Azure Subscription Variables
22
SUBSCRIPTION_ID = ''
3-
LOCATION = ''
3+
LOCATION = 'westeurope'
44
TENANT_ID = ''
55
BASE_NAME = ''
66
SP_APP_ID = ''
77
SP_APP_SECRET = ''
8+
RESOUCE_GROUP = 'mlops-rg'
89

910
# Mock build/release ID for local testing - update ReleaseID each "release"
1011
BUILD_BUILDID = '001'
1112
RELEASE_RELEASEID = '001'
1213

1314
# Azure ML Workspace Variables
15+
WORKSPACE_NAME = ''
1416
EXPERIMENT_NAME = ''
15-
SCRIPT_FOLDER = './'
1617

1718
# AML Compute Cluster Config
18-
AML_COMPUTE_CLUSTER_NAME = ''
19-
AML_COMPUTE_CLUSTER_CPU_SKU = ''
20-
AML_CLUSTER_MAX_NODES = ''
21-
AML_CLUSTER_MIN_NODES = ''
19+
AML_COMPUTE_CLUSTER_NAME = 'train-cluster'
20+
AML_COMPUTE_CLUSTER_CPU_SKU = 'STANDARD_DS2_V2'
21+
AML_CLUSTER_MAX_NODES = '4'
22+
AML_CLUSTER_MIN_NODES = '0'
2223
AML_CLUSTER_PRIORITY = 'lowpriority'
2324
# Training Config
2425
MODEL_NAME = 'sklearn_regression_model.pkl'
2526
MODEL_VERSION = '1'
2627
TRAIN_SCRIPT_PATH = 'training/train.py'
2728
# AML Pipeline Config
28-
TRAINING_PIPELINE_NAME = ''
29-
PIPELINE_CONDA_PATH = 'aml_config/conda_dependencies.yml'
29+
TRAINING_PIPELINE_NAME = 'Training Pipeline'
3030
MODEL_PATH = ''
3131
EVALUATE_SCRIPT_PATH = 'evaluate/evaluate_model.py'
3232
REGISTER_SCRIPT_PATH = 'register/register_model.py'
3333
SOURCES_DIR_TRAIN = 'code'
3434

35-
# These are not mandatory for the core workflow
36-
# Remote VM Config
37-
REMOTE_VM_NAME = ''
38-
REMOTE_VM_USERNAME = ''
39-
REMOTE_VM_PASSWORD = ''
40-
REMOTE_VM_IP = ''
41-
# Image config
42-
IMAGE_NAME = ''
43-
IMAGE_DESCRIPTION = ''
44-
IMAGE_VERSION = ''
45-
# ACI Config
46-
ACI_CPU_CORES = ''
47-
ACI_MEM_GB = ''
48-
ACI_DESCRIPTION = ''
49-
5035
# Optional. Used by a training pipeline with R on Databricks
5136
DB_CLUSTER_ID = ''
52-
DATABRICKS_COMPUTE_NAME = ''
37+
38+
# Optional. Container Image name for image creation
39+
IMAGE_NAME = 'ml-trained'

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ wheels/
2424
.installed.cfg
2525
*.egg
2626
MANIFEST
27+
venv/
2728

2829
# PyInstaller
2930
# Usually these files are written by a python script from a template

Diff for: .pipelines/azdo-ci-build-train.yml

+3-8
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,9 @@ trigger:
1111
- ml_service/util/create_scoring_image.py
1212

1313
variables:
14+
- template: azdo-variables.yml
1415
- group: devopsforai-aml-vg
15-
# Choose from default, build_train_pipeline_with_r.py, or build_train_pipeline_with_r_on_dbricks.py
16-
- name: build-train-script
17-
value: 'build_train_pipeline.py'
18-
# Automatically triggers the train, evaluate, register pipeline after the CI steps.
19-
# Uncomment to set to false or add same variable name at queue time with value of false to disable.
20-
# - name: auto-trigger-training
21-
# value: false
16+
2217

2318
stages:
2419
- stage: 'Model_CI'
@@ -34,7 +29,7 @@ stages:
3429
- template: azdo-base-pipeline.yml
3530
- script: |
3631
# Invoke the Python building and publishing a training pipeline
37-
python3 $(Build.SourcesDirectory)/ml_service/pipelines/$(build-train-script)
32+
python3 $(Build.SourcesDirectory)/ml_service/pipelines/${{ variables.BUILD_TRAIN_SCRIPT }}
3833
failOnStderr: 'false'
3934
env:
4035
SP_APP_SECRET: '$(SP_APP_SECRET)'

Diff for: .pipelines/azdo-pr-build-train.yml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ container: mcr.microsoft.com/mlops/python:latest
1111

1212

1313
variables:
14+
- template: azdo-variables.yml
1415
- group: devopsforai-aml-vg
1516

1617

Diff for: .pipelines/azdo-variables.yml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
variables:
2+
# Azure ML Workspace Variables
3+
- name: EXPERIMENT_NAME
4+
value: mlopspython
5+
# AML Compute Cluster Config
6+
- name: AML_COMPUTE_CLUSTER_CPU_SKU
7+
value: STANDARD_DS2_V2
8+
- name: AML_COMPUTE_CLUSTER_NAME
9+
value: train-cluster
10+
- name: AML_CLUSTER_MIN_NODES
11+
value: 0
12+
- name: AML_CLUSTER_MAX_NODES
13+
value: 4
14+
- name: AML_CLUSTER_PRIORITY
15+
value: lowpriority
16+
# Training Config
17+
- name: BUILD_TRAIN_SCRIPT
18+
value: build_train_pipeline.py
19+
- name: TRAIN_SCRIPT_PATH
20+
value: training/train.py
21+
- name: MODEL_NAME
22+
value: sklearn_regression_model.pkl
23+
- name: MODEL_VERSION
24+
value: '1'
25+
# AML Pipeline Config
26+
- name: TRAINING_PIPELINE_NAME
27+
value: 'Training Pipeline'
28+
- name: MODEL_PATH
29+
value: ''
30+
- name: EVALUATE_SCRIPT_PATH
31+
value: evaluate/evaluate_model.py
32+
- name: REGISTER_SCRIPT_PATH
33+
value: register/register_model.py
34+
- name: SOURCES_DIR_TRAIN
35+
value: code
36+
- name: IMAGE_NAME
37+
value: ''
38+
# Optional. Used by a training pipeline with R on Databricks
39+
- name: DB_CLUSTER_ID
40+
value: ''

Diff for: docs/getting_started.md

+22-15
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,48 @@ Click on **Library** in the **Pipelines** section as indicated below:
4747
Please name your variable group **``devopsforai-aml-vg``** as we are using this
4848
name within our build yaml file.
4949

50-
The variable group should contain the following variables:
50+
The variable group should contain the following required variables:
5151

5252
| Variable Name | Suggested Value |
5353
| --------------------------- | -----------------------------------|
54-
| AML_COMPUTE_CLUSTER_CPU_SKU | STANDARD_DS2_V2 |
55-
| AML_COMPUTE_CLUSTER_NAME | train-cluster |
5654
| BASE_NAME | [unique base name] |
57-
| DB_CLUSTER_ID | [Optional Databricks cluster Id] |
58-
| DATABRICKS_COMPUTE_NAME | [Optional Databricks compute name] |
59-
| EVALUATE_SCRIPT_PATH | evaluate/evaluate_model.py |
60-
| EXPERIMENT_NAME | mlopspython |
6155
| LOCATION | centralus |
62-
| MODEL_NAME | sklearn_regression_model.pkl |
63-
| REGISTER_SCRIPT_PATH | register/register_model.py |
64-
| SOURCES_DIR_TRAIN | code |
6556
| SP_APP_ID | |
6657
| SP_APP_SECRET | |
6758
| SUBSCRIPTION_ID | |
6859
| TENANT_ID | |
69-
| TRAIN_SCRIPT_PATH | training/train.py |
70-
| TRAINING_PIPELINE_NAME | training-pipeline |
60+
| RESOURCE_GROUP | |
61+
| WORKSPACE_NAME | mlops-AML-WS |
7162

7263
Mark **SP_APP_SECRET** variable as a secret one.
7364

74-
**Note:** The **BASE_NAME** parameter is used throughout the solution for naming
65+
**Note:**
66+
67+
The **WORKSPACE_NAME** parameter is used for the Azure Machine Learning Workspace creation. You can provide here an existing AML Workspace if you have one.
68+
69+
The **BASE_NAME** parameter is used throughout the solution for naming
7570
Azure resources. When the solution is used in a shared subscription, there can
7671
be naming collisions with resources that require unique names like azure blob
7772
storage and registry DNS naming. Make sure to give a unique value to the
7873
BASE_NAME variable (e.g. MyUniqueML), so that the created resources will have
79-
unique names (e.g. MyUniqueML-AML-RG, MyUniqueML-AML-WS, etc.). The length of
80-
the BASE_NAME value should not exceed 10 characters.
74+
unique names (e.g. MyUniqueML-AML-RG, MyUniqueML-AML-KV, etc.). The length of
75+
the BASE_NAME value should not exceed 10 characters.
8176

8277
Make sure to select the **Allow access to all pipelines** checkbox in the
8378
variable group configuration.
8479

80+
## More variable options
81+
82+
There are more variables used in the project. They're defined in two places one for local execution one for using Azure DevOps Pipelines
83+
84+
### Local configuration
85+
86+
In order to configure the project locally you have to create a copy from `.env.example` to the root and name it `.env`. Fill out all missing values and adjust the existing ones to your needs. Please be aware that the local environment also needs access to the Azure subscription so you have to provide the credentials of your service principal and Azure account information here as well.
87+
88+
### Azure DevOps configuration
89+
90+
For using Azure DevOps Pipelines all other variables are stored in the file `.pipelines/azdo-variables.yml`. Adjust as needed the variables, also the defaults will give you an easy jump start.
91+
8592
Up until now you should have:
8693

8794
* Forked (or cloned) the repo

Diff for: environment_setup/arm-templates/cloud-environment.json

+24-5
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,35 @@
2626
"metadata": {
2727
"description": "Specifies the location for all resources."
2828
}
29+
},
30+
"workspace": {
31+
"type": "string"
32+
},
33+
"storageAccount": {
34+
"type": "string",
35+
"defaultValue": "[concat(toLower(parameters('baseName')), 'amlsa')]"
36+
},
37+
"keyvault": {
38+
"type": "string",
39+
"defaultValue": "[concat(parameters('baseName'),'-AML-KV')]"
40+
},
41+
"appInsights": {
42+
"type": "string",
43+
"defaultValue": "[concat(parameters('baseName'),'-AML-AI')]"
44+
},
45+
"acr": {
46+
"type": "string",
47+
"defaultValue": "[concat(toLower(parameters('baseName')),'amlcr')]"
2948
}
3049
},
3150
"variables": {
32-
"amlWorkspaceName": "[concat(parameters('baseName'),'-AML-WS')]",
33-
"storageAccountName": "[concat(toLower(parameters('baseName')), 'amlsa')]",
51+
"amlWorkspaceName": "[parameters('workspace')]",
52+
"storageAccountName": "[parameters('storageAccount')]",
3453
"storageAccountType": "Standard_LRS",
35-
"keyVaultName": "[concat(parameters('baseName'),'-AML-KV')]",
54+
"keyVaultName": "[parameters('keyvault')]",
3655
"tenantId": "[subscription().tenantId]",
37-
"applicationInsightsName": "[concat(parameters('baseName'),'-AML-AI')]",
38-
"containerRegistryName": "[concat(toLower(parameters('baseName')),'amlcr')]"
56+
"applicationInsightsName": "[parameters('appInsights')]",
57+
"containerRegistryName": "[parameters('acr')]"
3958
},
4059
"resources": [
4160
{

Diff for: environment_setup/iac-create-environment.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ steps:
2525
inputs:
2626
azureSubscription: 'AzureResourceConnection'
2727
action: 'Create Or Update Resource Group'
28-
resourceGroupName: '$(BASE_NAME)-AML-RG'
28+
resourceGroupName: '$(RESOURCE_GROUP)'
2929
location: $(LOCATION)
3030
templateLocation: 'Linked artifact'
3131
csmFile: '$(Build.SourcesDirectory)/environment_setup/arm-templates/cloud-environment.json'
32-
overrideParameters: '-baseName $(BASE_NAME) -location $(LOCATION)'
32+
overrideParameters: '-baseName $(BASE_NAME) -location $(LOCATION) -workspace $(WORKSPACE_NAME)'
3333
deploymentMode: 'Incremental'
3434
displayName: 'Deploy MLOps resources to Azure'
3535

Diff for: environment_setup/iac-remove-environment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ steps:
1818
inputs:
1919
azureSubscription: 'AzureResourceConnection'
2020
action: 'DeleteRG'
21-
resourceGroupName: '$(BASE_NAME)-AML-RG'
21+
resourceGroupName: '$(RESOURCE_GROUP)'
2222
location: $(LOCATION)
2323
displayName: 'Delete resources in Azure'
2424

Diff for: ml_service/pipelines/build_train_pipeline.py

+19-32
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,32 @@
55
# from azureml.core import Datastore
66
import os
77
import sys
8-
from dotenv import load_dotenv
98
sys.path.append(os.path.abspath("./ml_service/util")) # NOQA: E402
109
from workspace import get_workspace
1110
from attach_compute import get_compute
11+
from env_variables import Env
1212

1313

1414
def main():
15-
load_dotenv()
16-
workspace_name = os.environ.get("BASE_NAME")+"-AML-WS"
17-
resource_group = os.environ.get("BASE_NAME")+"-AML-RG"
18-
subscription_id = os.environ.get("SUBSCRIPTION_ID")
19-
tenant_id = os.environ.get("TENANT_ID")
20-
app_id = os.environ.get("SP_APP_ID")
21-
app_secret = os.environ.get("SP_APP_SECRET")
22-
sources_directory_train = os.environ.get("SOURCES_DIR_TRAIN")
23-
train_script_path = os.environ.get("TRAIN_SCRIPT_PATH")
24-
evaluate_script_path = os.environ.get("EVALUATE_SCRIPT_PATH")
25-
vm_size = os.environ.get("AML_COMPUTE_CLUSTER_CPU_SKU")
26-
compute_name = os.environ.get("AML_COMPUTE_CLUSTER_NAME")
27-
model_name = os.environ.get("MODEL_NAME")
28-
build_id = os.environ.get("BUILD_BUILDID")
29-
pipeline_name = os.environ.get("TRAINING_PIPELINE_NAME")
30-
15+
e = Env()
3116
# Get Azure machine learning workspace
3217
aml_workspace = get_workspace(
33-
workspace_name,
34-
resource_group,
35-
subscription_id,
36-
tenant_id,
37-
app_id,
38-
app_secret)
18+
e.workspace_name,
19+
e.resource_group,
20+
e.subscription_id,
21+
e.tenant_id,
22+
e.app_id,
23+
e.app_secret)
24+
print("get_workspace:")
3925
print(aml_workspace)
4026

4127
# Get Azure machine learning cluster
4228
aml_compute = get_compute(
4329
aml_workspace,
44-
compute_name,
45-
vm_size)
30+
e.compute_name,
31+
e.vm_size)
4632
if aml_compute is not None:
33+
print("aml_compute:")
4734
print(aml_compute)
4835

4936
run_config = RunConfiguration(conda_dependencies=CondaDependencies.create(
@@ -56,16 +43,16 @@ def main():
5643
run_config.environment.docker.enabled = True
5744

5845
model_name = PipelineParameter(
59-
name="model_name", default_value=model_name)
46+
name="model_name", default_value=e.model_name)
6047
release_id = PipelineParameter(
6148
name="release_id", default_value="0"
6249
)
6350

6451
train_step = PythonScriptStep(
6552
name="Train Model",
66-
script_name=train_script_path,
53+
script_name=e.train_script_path,
6754
compute_target=aml_compute,
68-
source_directory=sources_directory_train,
55+
source_directory=e.sources_directory_train,
6956
arguments=[
7057
"--release_id", release_id,
7158
"--model_name", model_name,
@@ -77,9 +64,9 @@ def main():
7764

7865
evaluate_step = PythonScriptStep(
7966
name="Evaluate Model ",
80-
script_name=evaluate_script_path,
67+
script_name=e.evaluate_script_path,
8168
compute_target=aml_compute,
82-
source_directory=sources_directory_train,
69+
source_directory=e.sources_directory_train,
8370
arguments=[
8471
"--release_id", release_id,
8572
"--model_name", model_name,
@@ -95,9 +82,9 @@ def main():
9582
train_pipeline = Pipeline(workspace=aml_workspace, steps=steps)
9683
train_pipeline.validate()
9784
published_pipeline = train_pipeline.publish(
98-
name=pipeline_name,
85+
name=e.pipeline_name,
9986
description="Model training/retraining pipeline",
100-
version=build_id
87+
version=e.build_id
10188
)
10289
print(f'Published pipeline: {published_pipeline.name}')
10390
print(f'for build {published_pipeline.version}')

0 commit comments

Comments
 (0)