From 12a289cc8924f6fbca478635405086d982ef8245 Mon Sep 17 00:00:00 2001 From: Alexandre Gattiker Date: Sun, 19 Jan 2020 22:08:46 +0100 Subject: [PATCH] Generate a Docker container within the CI build --- .gitignore | 2 + .pipelines/azdo-base-pipeline.yml | 91 +++++++++++++----- .pipelines/azdo-ci-build-train.yml | 26 ++--- .pipelines/azdo-pr-build-train.yml | 10 +- .../azdo-template-get-model-version.yml | 2 +- .pipelines/azdo-variables.yml | 3 + README.md | 2 +- code/scoring/score.py | 2 +- code/training/R/train_with_r_on_databricks.py | 4 +- docs/code_description.md | 8 +- docs/development_setup.md | 44 +++++++++ docs/getting_started.md | 34 ++++++- docs/images/create-acr-service-connection.png | Bin 0 -> 77545 bytes environment_setup/build-image/Dockerfile | 18 ++++ .../build-image/requirements.txt | 12 +++ environment_setup/install_requirements.sh | 4 +- .../{ => mlops-image}/Dockerfile | 11 +-- .../docker-image-pipeline.yml | 5 +- .../{ => mlops-image}/requirements.txt | 3 - ml_service/util/smoke_test_scoring_service.py | 2 +- tests/unit/code_test.py | 4 +- tests/unit/data_test.py | 8 +- tox.ini | 20 ++++ 23 files changed, 228 insertions(+), 87 deletions(-) create mode 100644 docs/development_setup.md create mode 100644 docs/images/create-acr-service-connection.png create mode 100644 environment_setup/build-image/Dockerfile create mode 100644 environment_setup/build-image/requirements.txt rename environment_setup/{ => mlops-image}/Dockerfile (51%) rename environment_setup/{ => mlops-image}/docker-image-pipeline.yml (77%) rename environment_setup/{ => mlops-image}/requirements.txt (72%) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 7bac8768..e0a583ec 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,8 @@ coverage.xml *.cover .hypothesis/ .pytest_cache/ +*-testresults.xml +test-output.xml # Translations *.mo diff --git a/.pipelines/azdo-base-pipeline.yml b/.pipelines/azdo-base-pipeline.yml index 926b404f..665a25f0 100644 --- a/.pipelines/azdo-base-pipeline.yml +++ b/.pipelines/azdo-base-pipeline.yml @@ -1,26 +1,65 @@ -# this pipeline should be ignored for now -parameters: - pipelineType: 'training' - -steps: -- script: | - flake8 --output-file=$(Build.BinariesDirectory)/lint-testresults.xml --format junit-xml - workingDirectory: '$(Build.SourcesDirectory)' - displayName: 'Run code quality tests' - enabled: 'true' - -- script: | - pytest --junitxml=$(Build.BinariesDirectory)/unit-testresults.xml $(Build.SourcesDirectory)/tests/unit - displayName: 'Run unit tests' - enabled: 'true' - env: - SP_APP_SECRET: '$(SP_APP_SECRET)' - -- task: PublishTestResults@2 - condition: succeededOrFailed() - inputs: - testResultsFiles: '$(Build.BinariesDirectory)/*-testresults.xml' - testRunTitle: 'Linting & Unit tests' - failTaskOnFailedTests: true - displayName: 'Publish linting and unit test results' - enabled: 'true' +jobs: +- job: BuildEnvironment + displayName: "Set up CI environment" + steps: + - task: AzureCLI@1 + displayName: Generate build container + name: BuildContainer + inputs: + azureSubscription: AzureResourceConnection + scriptLocation: inlineScript + inlineScript: | + set -euxo pipefail # fail on error + + # Get name and server of the container registry linked to the Azure ML workspace. + acrId=$(az resource show -g "$RESOURCE_GROUP" -n "$WORKSPACE_NAME" --resource-type Microsoft.MachineLearningServices/workspaces --query properties.containerRegistry -o tsv) + read -r acrName acrServer <<< $(az resource show --id "$acrId" --query '[[name,properties.loginServer]]' -o tsv) + + dockerDir=environment_setup/build-image + # Generate an tag with a reproducible checksum of all files in $dockerDir by doing a checksum of all files + # in alphabetical order, then another checksum of their names and checksums. + imageTag=$(find "$dockerDir" -type f -exec md5sum {} \; | sort -k 2 | md5sum | cut -f1 -d ' ') + + # If the image with the generated tag doesn't already exist, build it. + repo="modelbuild/$(BUILDCONTAINER_NAME)" + if ! az acr repository show -n $acrName --image "$repo:$imageTag" -o table; then + az acr build \ + -r "$acrName" \ + -t "$repo:$imageTag" \ + -t "$repo:latest" \ + "$dockerDir" + fi + + # Make image name available to subsequent jobs. + echo "##vso[task.setvariable variable=BUILD_IMAGE;isOutput=true]$acrServer/$repo:$imageTag" + +- job: "Model_CI" + displayName: "Model CI" + dependsOn: BuildEnvironment + variables: + BUILD_IMAGE: $[ dependencies.BuildEnvironment.outputs['BuildContainer.BUILD_IMAGE'] ] + container: + image: $(BUILD_IMAGE) + endpoint: ContainerRegistry + timeoutInMinutes: 0 + steps: + + - script: | + tox + displayName: 'Linting & unit tests' + + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '*-testresults.xml' + testRunTitle: 'Linting & Unit tests' + failTaskOnFailedTests: true + displayName: 'Publish test results' + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish coverage report' + condition: succeededOrFailed() + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: 'coverage.xml' + failIfCoverageEmpty: true diff --git a/.pipelines/azdo-ci-build-train.yml b/.pipelines/azdo-ci-build-train.yml index e1ed0dc6..9ca472d5 100644 --- a/.pipelines/azdo-ci-build-train.yml +++ b/.pipelines/azdo-ci-build-train.yml @@ -7,26 +7,26 @@ trigger: exclude: - docs/ - environment_setup/ - - ml_service/util/create_scoring_image.* + - ml_service/util/create_scoring_image.py - ml_service/util/smoke_test_scoring_service.py variables: - template: azdo-variables.yml - group: devopsforai-aml-vg +pool: + vmImage: ubuntu-latest stages: - stage: 'Model_CI' displayName: 'Model CI' jobs: - - job: "Model_CI_Pipeline" - displayName: "Model CI Pipeline" - pool: - vmImage: 'ubuntu-latest' + - template: azdo-base-pipeline.yml + - job: "Publish_Pipeline" + displayName: "Publish AML Pipeline" container: mcr.microsoft.com/mlops/python:latest timeoutInMinutes: 0 steps: - - template: azdo-base-pipeline.yml - task: AzureCLI@1 inputs: azureSubscription: '$(WORKSPACE_SVC_CONNECTION)' @@ -35,7 +35,7 @@ stages: set -e # fail on error export SUBSCRIPTION_ID=$(az account show --query id -o tsv) # Invoke the Python building and publishing a training pipeline - python $(Build.SourcesDirectory)/ml_service/pipelines/${{ variables.BUILD_TRAIN_SCRIPT }} + python ml_service/pipelines/${{ variables.BUILD_TRAIN_SCRIPT }} displayName: 'Publish Azure Machine Learning Pipeline' - stage: 'Trigger_AML_Pipeline' @@ -44,8 +44,6 @@ stages: - job: "Get_Pipeline_ID" condition: and(succeeded(), eq(coalesce(variables['auto-trigger-training'], 'true'), 'true')) displayName: "Get Pipeline ID for execution" - pool: - vmImage: 'ubuntu-latest' container: mcr.microsoft.com/mlops/python:latest timeoutInMinutes: 0 steps: @@ -56,7 +54,7 @@ stages: inlineScript: | set -e # fail on error export SUBSCRIPTION_ID=$(az account show --query id -o tsv) - python $(Build.SourcesDirectory)/ml_service/pipelines/run_train_pipeline.py --output_pipeline_id_file "pipeline_id.txt" --skip_train_execution + python ml_service/pipelines/run_train_pipeline.py --output_pipeline_id_file "pipeline_id.txt" --skip_train_execution # Set AMLPIPELINEID variable for next AML Pipeline task in next job AMLPIPELINEID="$(cat pipeline_id.txt)" echo "##vso[task.setvariable variable=AMLPIPELINEID;isOutput=true]$AMLPIPELINEID" @@ -89,8 +87,6 @@ stages: - job: "Training_Run_Report" dependsOn: "Run_ML_Pipeline" displayName: "Determine if evaluation succeeded and new model is registered" - pool: - vmImage: 'ubuntu-latest' container: mcr.microsoft.com/mlops/python:latest timeoutInMinutes: 0 steps: @@ -102,8 +98,6 @@ stages: jobs: - job: "Deploy_ACI" displayName: "Deploy to ACI" - pool: - vmImage: 'ubuntu-latest' container: mcr.microsoft.com/mlops/python:latest timeoutInMinutes: 0 steps: @@ -137,8 +131,6 @@ stages: jobs: - job: "Deploy_AKS" displayName: "Deploy to AKS" - pool: - vmImage: 'ubuntu-latest' container: mcr.microsoft.com/mlops/python:latest timeoutInMinutes: 0 steps: @@ -173,8 +165,6 @@ stages: jobs: - job: "Deploy_Webapp" displayName: "Deploy to Webapp" - pool: - vmImage: 'ubuntu-latest' container: mcr.microsoft.com/mlops/python:latest timeoutInMinutes: 0 steps: diff --git a/.pipelines/azdo-pr-build-train.yml b/.pipelines/azdo-pr-build-train.yml index 24231b2a..b1be1958 100644 --- a/.pipelines/azdo-pr-build-train.yml +++ b/.pipelines/azdo-pr-build-train.yml @@ -4,16 +4,12 @@ pr: include: - master -pool: +pool: vmImage: 'ubuntu-latest' -container: mcr.microsoft.com/mlops/python:latest - - variables: - template: azdo-variables.yml - group: devopsforai-aml-vg - -steps: -- template: azdo-base-pipeline.yml \ No newline at end of file +jobs: +- template: azdo-base-pipeline.yml diff --git a/.pipelines/azdo-template-get-model-version.yml b/.pipelines/azdo-template-get-model-version.yml index f69f3366..4a7dd39d 100644 --- a/.pipelines/azdo-template-get-model-version.yml +++ b/.pipelines/azdo-template-get-model-version.yml @@ -6,7 +6,7 @@ steps: inlineScript: | set -e # fail on error export SUBSCRIPTION_ID=$(az account show --query id -o tsv) - python $(Build.SourcesDirectory)/ml_service/pipelines/verify_train_pipeline.py --build_id $(Build.BuildId) --output_model_version_file "model_version.txt" + python ml_service/pipelines/verify_train_pipeline.py --build_id $(Build.BuildId) --output_model_version_file "model_version.txt" # Output model version to Azure DevOps job MODEL_VERSION="$(cat model_version.txt)" echo "##vso[task.setvariable variable=MODEL_VERSION]$MODEL_VERSION" diff --git a/.pipelines/azdo-variables.yml b/.pipelines/azdo-variables.yml index 0691e673..745c8a76 100644 --- a/.pipelines/azdo-variables.yml +++ b/.pipelines/azdo-variables.yml @@ -2,6 +2,9 @@ variables: # Azure ML Workspace Variables - name: EXPERIMENT_NAME value: mlopspython + # Azure DevOps build container +- name: BUILDCONTAINER_NAME + value: diabetes # AML Compute Cluster Config - name: AML_COMPUTE_CLUSTER_CPU_SKU value: STANDARD_DS2_V2 diff --git a/README.md b/README.md index e622ba75..3ce71c3f 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ This reference architecture shows how to implement continuous integration (CI), Once you have registered your ML model, you can use Azure ML + Azure DevOps to deploy it. -[Azure DevOps release pipeline](https://docs.microsoft.com/en-us/azure/devops/pipelines/release/?view=azure-devops) packages the new model along with the scoring file and its python dependencies into a [docker image](https://docs.microsoft.com/en-us/azure/machine-learning/service/concept-azure-machine-learning-architecture#image) and pushes it to [Azure Container Registry](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-intro). This image is used to deploy the model as [web service](https://docs.microsoft.com/en-us/azure/machine-learning/service/concept-azure-machine-learning-architecture#web-service) across QA and Prod environments. The QA environment is running on top of [Azure Container Instances (ACI)](https://azure.microsoft.com/en-us/services/container-instances/) and the Prod environment is built with [Azure Kubernetes Service (AKS)](https://docs.microsoft.com/en-us/azure/aks/intro-kubernetes). +The [Azure DevOps multi-stage pipeline](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml) packages the new model along with the scoring file and its python dependencies into a [docker image](https://docs.microsoft.com/en-us/azure/machine-learning/service/concept-azure-machine-learning-architecture#image) and pushes it to [Azure Container Registry](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-intro). This image is used to deploy the model as [web service](https://docs.microsoft.com/en-us/azure/machine-learning/service/concept-azure-machine-learning-architecture#web-service) across QA and Prod environments. The QA environment is running on top of [Azure Container Instances (ACI)](https://azure.microsoft.com/en-us/services/container-instances/) and the Prod environment is built with [Azure Kubernetes Service (AKS)](https://docs.microsoft.com/en-us/azure/aks/intro-kubernetes). ### Repo Details diff --git a/code/scoring/score.py b/code/scoring/score.py index b78a435c..4a1c6152 100644 --- a/code/scoring/score.py +++ b/code/scoring/score.py @@ -57,7 +57,7 @@ def run(raw_data, request_headers): request_headers.get("X-Ms-Request-Id", ""), request_headers.get("Traceparent", ""), len(result) - )) + )) return {"result": result.tolist()} diff --git a/code/training/R/train_with_r_on_databricks.py b/code/training/R/train_with_r_on_databricks.py index 1a120bd0..c571d609 100644 --- a/code/training/R/train_with_r_on_databricks.py +++ b/code/training/R/train_with_r_on_databricks.py @@ -11,5 +11,5 @@ args, unknown = parser.parse_known_args() folder = args.AZUREML_SCRIPT_DIRECTORY_NAME -os.system("cd " + "/dbfs/" + folder + - " && Rscript r_train.r && ls -ltr model.rds") +os.system("cd " + "/dbfs/" + folder + + " && Rscript r_train.r && ls -ltr model.rds") diff --git a/docs/code_description.md b/docs/code_description.md index 472e781b..75049d5c 100644 --- a/docs/code_description.md +++ b/docs/code_description.md @@ -2,15 +2,15 @@ ### Environment Setup -- `environment_setup/requirements.txt` : It consists of a list of python packages which are needed by the train.py to run successfully on host agent (locally). +- `environment_setup/build-image/Dockerfile` : Dockerfile of a build agent containing Python 3.6 and all required packages. + +- `environment_setup/build-image/requirements.txt` : List of python packages which are needed by the train.py and associated unit tests to run successfully on host agent (locally). - `environment_setup/install_requirements.sh` : This script prepares the python environment i.e. install the Azure ML SDK and the packages specified in requirements.txt - `environment_setup/iac-*.yml, arm-templates` : Infrastructure as Code piplines to create and delete required resources along with corresponding arm-templates. -- `environment_setup/Dockerfile` : Dockerfile of a build agent containing Python 3.6 and all required packages. - -- `environment_setup/docker-image-pipeline.yml` : An AzDo pipeline for building and pushing [microsoft/mlopspython](https://hub.docker.com/_/microsoft-mlops-python) image. +- `environment_setup/mlops-image/docker-image-pipeline.yml` : An AzDo pipeline for building and pushing [microsoft/mlopspython](https://hub.docker.com/_/microsoft-mlops-python) image. ### Pipelines diff --git a/docs/development_setup.md b/docs/development_setup.md new file mode 100644 index 00000000..e625e7d8 --- /dev/null +++ b/docs/development_setup.md @@ -0,0 +1,44 @@ +## Development environment setup + +### Setup + +Please be aware that the local environment also needs access to the Azure subscription so you have to have Contributor access on the Azure ML Workspace. + +In order to configure the project locally, create a copy of `.env.example` in the root directory and name it `.env`. Fill out all missing values and adjust the existing ones to suit your requirements. + +### Installation + +[Install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). The Azure CLI will be used to log you in interactively. + +Create a virtual environment using [venv](https://docs.python.org/3/library/venv.html), [conda](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html) or [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv). + +Here is an example for setting up and activating a `venv` environment with Python 3: + +``` +python3 -mvenv .venv +source .venv/bin/activate +``` + +Install the required Python modules in your virtual environment. + +``` +pip install -r environment_setup/build-image/requirements.txt +``` + +### Running local code + +To run your local ML pipeline code on Azure ML, run a command such as the following (in bash, all on one line): + +``` +export BUILD_BUILDID=$(uuidgen); python ml_service/pipelines/build_train_pipeline.py && python ml_service/pipelines/run_train_pipeline.py +``` + +BUILD_BUILDID is a variable used to uniquely identify the ML pipeline between the +`build_train_pipeline.py` and `run_train_pipeline.py` scripts. In Azure DevOps it is +set to the current build number. In a local environment, we can use a command such as +`uuidgen` so set a different random identifier on each run, ensuring there are +no collisions. + +### Local testing + +Before committing, run `tox` to execute linter and unit test checks. diff --git a/docs/getting_started.md b/docs/getting_started.md index a46d5304..4f4da5a0 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -80,10 +80,7 @@ There are more variables used in the project. They're defined in two places, one ### Local configuration -In order to configure the project locally, create a copy of `.env.example` in the root directory and name it `.env`. Fill out all missing values and adjust the existing ones to suit your requirements. - -For local development, you will also need to [install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). The Azure CLI will be used to log you in interactively. -Please be aware that the local environment also needs access to the Azure subscription so you have to have Contributor access on the Azure ML Workspace. +For instructions on how to set up a local development environment, refer to the [Development environment setup instructions](development_setup.md). ### Azure DevOps configuration @@ -139,6 +136,18 @@ your Azure AD tenant, or receive the ID and secret of a service principal from your Azure AD Administrator. That principal must have Contributor permissions on the Azure ML Workspace. +## Create a Docker Service Connection to manage your build container + +The pipeline requires a **Docker Registry** +[service connection](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-docreg). +As **Registry type**, choose **Azure Container Registry**. +Point to the Azure Container Registry deployed by your IaC pipeline. + +Use **``ContainerRegistry``** as the connection name, since it is used +in the Azure ML pipeline definition. + +![configure container registry service connection](images/create-acr-service-connection.png) + ## Set up Build, Release Trigger, and Release Multi-Stage Pipeline Now that you have all the required resources created from the IaC pipeline, @@ -172,9 +181,24 @@ Great, you now have the build pipeline set up which automatically triggers every * The first stage of the pipeline, **Model CI**, performs linting, unit testing, build and publishes an **ML Training Pipeline** in an **ML Workspace**. + * The **Generate build container** job creates a Docker container with + your ML dependencies, such as sklearn, or reuses the container + from a previous run if your container definition has not changed. + * The **Model CI** job runs on the Docker container generated in the + previous job and runs linting and unit tests. Test results and + code coverage reports can be found on your job output page. + * The **Publish AML Pipeline** job publishes the Azure ML pipeline + in your workspace. To save time, this job runs in parallel with + the **Model CI** job. If CI fails (for instance with unit test failures), + the AML Pipeline might have been published but will not be used + for training. The next run will publish a new AML Pipeline with + the same name and updated tags. + **Note:** The build pipeline also supports building and publishing ML pipelines using R to train a model. This is enabled -by changing the `build-train-script` pipeline variable to either `build_train_pipeline_with_r.py`, or `build_train_pipeline_with_r_on_dbricks.py`. For pipeline training a model with R on Databricks you'll need +by changing the `build-train-script` pipeline variable to either `build_train_pipeline_with_r.py`, or `build_train_pipeline_with_r_on_dbricks.py` +and uncommenting the R installation step in the `Dockerfile`. +For a pipeline training a model with R on Databricks you'll need to manually create a Databricks cluster and attach it to the ML Workspace as a compute (Values DB_CLUSTER_ID and DATABRICKS_COMPUTE_NAME variables should be specified). diff --git a/docs/images/create-acr-service-connection.png b/docs/images/create-acr-service-connection.png new file mode 100644 index 0000000000000000000000000000000000000000..ed9d0c25902b062ef175fc9d63e931893f4157fd GIT binary patch literal 77545 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

iZ6dsL`OEYAC>TNxw*~ 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 literal 0 HcmV?d00001 diff --git a/environment_setup/build-image/Dockerfile b/environment_setup/build-image/Dockerfile new file mode 100644 index 00000000..20ef7365 --- /dev/null +++ b/environment_setup/build-image/Dockerfile @@ -0,0 +1,18 @@ +FROM conda/miniconda3 + +RUN apt-get update && apt-get install -y \ + curl \ + apt-transport-https \ + gcc + +# Install dotnet runtime used to generate code coverage report +RUN curl -O https://packages.microsoft.com/config/ubuntu/19.04/packages-microsoft-prod.deb +RUN dpkg -i packages-microsoft-prod.deb +RUN apt-get update && apt-get install -y dotnet-runtime-3.1 + +# Uncomment to run R +# RUN conda install -c r r-essentials + +# Install Python modules +COPY requirements.txt /setup/ +RUN pip install --upgrade -r /setup/requirements.txt diff --git a/environment_setup/build-image/requirements.txt b/environment_setup/build-image/requirements.txt new file mode 100644 index 00000000..babb1ddc --- /dev/null +++ b/environment_setup/build-image/requirements.txt @@ -0,0 +1,12 @@ +pytest>=5.3 +pytest-cov>=2.8.1 +requests>=2.22 +numpy>=1.17 +pandas>=0.25 +scikit-learn>=0.21.3 +azureml-sdk>=1.0 +python-dotenv>=0.10.3 +flake8>=3.7 +flake8_formatter_junit_xml>=0.0.6 +tox>=3.14.3 +azure-cli==2.0.76 diff --git a/environment_setup/install_requirements.sh b/environment_setup/install_requirements.sh index 1bdd081d..6cb76a00 100644 --- a/environment_setup/install_requirements.sh +++ b/environment_setup/install_requirements.sh @@ -26,6 +26,4 @@ python --version -pip install azure-cli==2.0.46 -pip install --upgrade azureml-sdk[cli] -pip install -r requirements.txt \ No newline at end of file +pip install -r files/requirements.txt diff --git a/environment_setup/Dockerfile b/environment_setup/mlops-image/Dockerfile similarity index 51% rename from environment_setup/Dockerfile rename to environment_setup/mlops-image/Dockerfile index 5e7b7581..cdd23048 100644 --- a/environment_setup/Dockerfile +++ b/environment_setup/mlops-image/Dockerfile @@ -4,11 +4,10 @@ LABEL org.label-schema.vendor = "Microsoft" \ org.label-schema.url = "https://hub.docker.com/r/microsoft/mlopspython" \ org.label-schema.vcs-url = "https://github.com/microsoft/MLOpsPython" - +RUN apt-get update && apt-get install -y \ + gcc -COPY environment_setup/requirements.txt /setup/ - -RUN apt-get update && apt-get install gcc -y && pip install --upgrade -r /setup/requirements.txt && \ - conda install -c r r-essentials -CMD ["python"] \ No newline at end of file +# Install Python modules +COPY requirements.txt /setup/ +RUN pip install --upgrade -r /setup/requirements.txt diff --git a/environment_setup/docker-image-pipeline.yml b/environment_setup/mlops-image/docker-image-pipeline.yml similarity index 77% rename from environment_setup/docker-image-pipeline.yml rename to environment_setup/mlops-image/docker-image-pipeline.yml index 7678d96d..99392e8f 100644 --- a/environment_setup/docker-image-pipeline.yml +++ b/environment_setup/mlops-image/docker-image-pipeline.yml @@ -11,7 +11,7 @@ trigger: paths: include: - - environment_setup/Dockerfile + - environment_setup/mlops-image/* variables: containerRegistry: $[coalesce(variables['acrServiceConnection'], 'acrconnection')] @@ -27,5 +27,4 @@ steps: tags: | $(Build.BuildNumber) latest - buildContext: '$(Build.SourcesDirectory)' - dockerFile: '$(Build.SourcesDirectory)/environment_setup/Dockerfile' + buildContext: '$(Build.SourcesDirectory)/environment_setup/mlops-image' diff --git a/environment_setup/requirements.txt b/environment_setup/mlops-image/requirements.txt similarity index 72% rename from environment_setup/requirements.txt rename to environment_setup/mlops-image/requirements.txt index f99e7f4b..446b359f 100644 --- a/environment_setup/requirements.txt +++ b/environment_setup/mlops-image/requirements.txt @@ -1,8 +1,5 @@ pytest>=5.3 requests>=2.22 -numpy>=1.17 -pandas>=0.25 -scikit-learn>=0.21.3 azureml-sdk>=1.0 python-dotenv>=0.10.3 flake8>=3.7 diff --git a/ml_service/util/smoke_test_scoring_service.py b/ml_service/util/smoke_test_scoring_service.py index 753ef23e..136656f6 100644 --- a/ml_service/util/smoke_test_scoring_service.py +++ b/ml_service/util/smoke_test_scoring_service.py @@ -52,7 +52,7 @@ def call_web_app(url, headers): response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: - if i == retries-1: + if i == retries - 1: raise e print(e) print("Retrying...") diff --git a/tests/unit/code_test.py b/tests/unit/code_test.py index 06654b2f..1703d54b 100644 --- a/tests/unit/code_test.py +++ b/tests/unit/code_test.py @@ -8,9 +8,9 @@ def test_train_model(): - X_train = np.array([1, 2, 3, 4, 5, 6]).reshape(-1, 1) + X_train = np.array([1, 2, 3, 4, 5, 6]).reshape(-1, 1) y_train = np.array([10, 9, 8, 8, 6, 5]) - X_test = np.array([3, 4]).reshape(-1, 1) + X_test = np.array([3, 4]).reshape(-1, 1) y_test = np.array([8, 7]) data = {"train": {"X": X_train, "y": y_train}, "test": {"X": X_test, "y": y_test}} diff --git a/tests/unit/data_test.py b/tests/unit/data_test.py index 8b40b8bc..4148f029 100644 --- a/tests/unit/data_test.py +++ b/tests/unit/data_test.py @@ -120,8 +120,8 @@ def test_check_distribution(): mean = np.mean(dataset.values, axis=0) std = np.mean(dataset.values, axis=0) assert ( - np.sum(abs(mean - historical_mean) > - shift_tolerance * abs(historical_mean)) - or np.sum(abs(std - historical_std) > - shift_tolerance * abs(historical_std)) > 0 + np.sum(abs(mean - historical_mean) + > shift_tolerance * abs(historical_mean)) + or np.sum(abs(std - historical_std) + > shift_tolerance * abs(historical_std)) > 0 ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..17faeda5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,20 @@ +[tox] +minversion = 3.14.3 +skipsdist = True + +[flake8] +# ignore obsolete warning +ignore = W503 +exclude = .git,__pycache__,.venv,.tox,**/site-packages/**/*.py,**/lib/**.py,**/bin/**.py + +[pytest] +junit_family = legacy + +[testenv] +whitelist_externals = + flake8 + pytest +passenv = HOME +commands = + flake8 --output-file=lint-testresults.xml --format junit-xml + pytest tests/unit --cov=code --cov-report=xml --junitxml=unit-testresults.xml