{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Enabling App Insights for Services in Production\n",
        "With this notebook, you can learn how to enable App Insights for standard service monitoring, plus, we provide examples for doing custom logging within a scoring files in a model. \n",
        "\n",
        "\n",
        "## What does Application Insights monitor?\n",
        "It monitors request rates, response times, failure rates, etc. For more information visit [App Insights docs.](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-overview)\n",
        "\n",
        "\n",
        "## What is different compared to standard production deployment process?\n",
        "If you want to enable generic App Insights for a service run:\n",
        "```python\n",
        "aks_service= Webservice(ws, \"aks-w-dc2\")\n",
        "aks_service.update(enable_app_insights=True)```\n",
        "Where \"aks-w-dc2\" is your service name. You can also do this from the Azure Portal under your Workspace--> deployments--> Select deployment--> Edit--> Advanced Settings--> Select \"Enable AppInsights diagnostics\"\n",
        "\n",
        "If you want to log custom traces, you will follow the standard deplyment process for AKS and you will:\n",
        "1. Update scoring file.\n",
        "2. Update aks configuration.\n",
        "3. Build new image and deploy it. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. Import your dependencies"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from azureml.core import Workspace, Run\n",
        "from azureml.core.compute import AksCompute, ComputeTarget\n",
        "from azureml.core.webservice import Webservice, AksWebservice\n",
        "from azureml.core.image import Image\n",
        "from azureml.core.model import Model\n",
        "\n",
        "import azureml.core\n",
        "print(azureml.core.VERSION)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. Set up your configuration and create a workspace\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "ws = Workspace.from_config()\n",
        "print(ws.name, ws.resource_group, ws.location, ws.subscription_id, sep = '\\n')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. Register Model\n",
        "Register an existing trained model, add descirption and tags."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "#Register the model\n",
        "from azureml.core.model import Model\n",
        "model = Model.register(model_path = \"sklearn_regression_model.pkl\", # this points to a local file\n",
        "                       model_name = \"sklearn_regression_model.pkl\", # this is the name the model is registered as\n",
        "                       tags = {'area': \"diabetes\", 'type': \"regression\"},\n",
        "                       description = \"Ridge regression model to predict diabetes\",\n",
        "                       workspace = ws)\n",
        "\n",
        "print(model.name, model.description, model.version)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. *Update your scoring file with custom print statements*\n",
        "Here is an example:\n",
        "### a. In your init function add:\n",
        "```python\n",
        "print (\"model initialized\" + time.strftime(\"%H:%M:%S\"))```\n",
        "\n",
        "### b. In your run function add:\n",
        "```python\n",
        "print (\"Prediction created\" + time.strftime(\"%H:%M:%S\"))```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%%writefile score.py\n",
        "import pickle\n",
        "import json\n",
        "import numpy \n",
        "from sklearn.externals import joblib\n",
        "from sklearn.linear_model import Ridge\n",
        "from azureml.core.model import Model\n",
        "import time\n",
        "\n",
        "def init():\n",
        "    global model\n",
        "    #Print statement for appinsights custom traces:\n",
        "    print (\"model initialized\" + time.strftime(\"%H:%M:%S\"))\n",
        "    \n",
        "    # note here \"sklearn_regression_model.pkl\" is the name of the model registered under the workspace\n",
        "    # this call should return the path to the model.pkl file on the local disk.\n",
        "    model_path = Model.get_model_path(model_name = 'sklearn_regression_model.pkl')\n",
        "    \n",
        "    # deserialize the model file back into a sklearn model\n",
        "    model = joblib.load(model_path)\n",
        "  \n",
        "\n",
        "# note you can pass in multiple rows for scoring\n",
        "def run(raw_data):\n",
        "    try:\n",
        "        data = json.loads(raw_data)['data']\n",
        "        data = numpy.array(data)\n",
        "        result = model.predict(data)\n",
        "        print (\"Prediction created\" + time.strftime(\"%H:%M:%S\"))\n",
        "        # you can return any datatype as long as it is JSON-serializable\n",
        "        return result.tolist()\n",
        "    except Exception as e:\n",
        "        error = str(e)\n",
        "        print (error + time.strftime(\"%H:%M:%S\"))\n",
        "        return error"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. *Create myenv.yml file*"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from azureml.core.conda_dependencies import CondaDependencies \n",
        "\n",
        "myenv = CondaDependencies.create(conda_packages=['numpy','scikit-learn'])\n",
        "\n",
        "with open(\"myenv.yml\",\"w\") as f:\n",
        "    f.write(myenv.serialize_to_string())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6. Create your new Image"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from azureml.core.image import ContainerImage\n",
        "\n",
        "image_config = ContainerImage.image_configuration(execution_script = \"score.py\",\n",
        "                                                  runtime = \"python\",\n",
        "                                                  conda_file = \"myenv.yml\",\n",
        "                                                  description = \"Image with ridge regression model\",\n",
        "                                                  tags = {'area': \"diabetes\", 'type': \"regression\"}\n",
        "                                                 )\n",
        "\n",
        "image = ContainerImage.create(name = \"myimage1\",\n",
        "                              # this is the model object\n",
        "                              models = [model],\n",
        "                              image_config = image_config,\n",
        "                              workspace = ws)\n",
        "\n",
        "image.wait_for_creation(show_output = True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Deploy to ACI (Optional)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from azureml.core.webservice import AciWebservice\n",
        "\n",
        "aciconfig = AciWebservice.deploy_configuration(cpu_cores = 1, \n",
        "                                               memory_gb = 1, \n",
        "                                               tags = {'area': \"diabetes\", 'type': \"regression\"}, \n",
        "                                               description = 'Predict diabetes using regression model',\n",
        "                                               enable_app_insights = True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from azureml.core.webservice import Webservice\n",
        "\n",
        "aci_service_name = 'my-aci-service-4'\n",
        "print(aci_service_name)\n",
        "aci_service = Webservice.deploy_from_image(deployment_config = aciconfig,\n",
        "                                           image = image,\n",
        "                                           name = aci_service_name,\n",
        "                                           workspace = ws)\n",
        "aci_service.wait_for_deployment(True)\n",
        "print(aci_service.state)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%%time\n",
        "import json\n",
        "\n",
        "test_sample = json.dumps({'data': [\n",
        "    [1,28,13,45,54,6,57,8,8,10], \n",
        "    [101,9,8,37,6,45,4,3,2,41]\n",
        "]})\n",
        "test_sample = bytes(test_sample,encoding='utf8')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "if aci_service.state == \"Healthy\":\n",
        "    prediction = aci_service.run(input_data=test_sample)\n",
        "    print(prediction)\n",
        "else:\n",
        "    raise ValueError(\"Service deployment isn't healthy, can't call the service\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7. Deploy to AKS service"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Create AKS compute if you haven't done so."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Use the default configuration (can also provide parameters to customize)\n",
        "prov_config = AksCompute.provisioning_configuration()\n",
        "\n",
        "aks_name = 'my-aks-test3' \n",
        "# Create the cluster\n",
        "aks_target = ComputeTarget.create(workspace = ws, \n",
        "                                  name = aks_name, \n",
        "                                  provisioning_configuration = prov_config)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%%time\n",
        "aks_target.wait_for_completion(show_output = True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "print(aks_target.provisioning_state)\n",
        "print(aks_target.provisioning_errors)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "If you already have a cluster you can attach the service to it:"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "```python \n",
        "%%time\n",
        "resource_id = '/subscriptions/<subscriptionid>/resourcegroups/<resourcegroupname>/providers/Microsoft.ContainerService/managedClusters/<aksservername>'\n",
        "create_name= 'myaks4'\n",
        "attach_config = AksCompute.attach_configuration(resource_id=resource_id)\n",
        "aks_target = ComputeTarget.attach(workspace = ws, \n",
        "                                  name = create_name, \n",
        "                                  attach_configuration=attach_config)\n",
        "## Wait for the operation to complete\n",
        "aks_target.wait_for_provisioning(True)```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### a. *Activate App Insights through updating AKS Webservice configuration*\n",
        "In order to enable App Insights in your service you will need to update your AKS configuration file:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "#Set the web service configuration\n",
        "aks_config = AksWebservice.deploy_configuration(enable_app_insights=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### b. Deploy your service"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "if aks_target.provisioning_state== \"Succeeded\": \n",
        "    aks_service_name ='aks-w-dc5'\n",
        "    aks_service = Webservice.deploy_from_image(workspace = ws, \n",
        "                                               name = aks_service_name,\n",
        "                                               image = image,\n",
        "                                               deployment_config = aks_config,\n",
        "                                               deployment_target = aks_target\n",
        "                                               )\n",
        "    aks_service.wait_for_deployment(show_output = True)\n",
        "    print(aks_service.state)\n",
        "else:\n",
        "    raise ValueError(\"AKS provisioning failed.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 8. Test your service "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%%time\n",
        "import json\n",
        "\n",
        "test_sample = json.dumps({'data': [\n",
        "    [1,28,13,45,54,6,57,8,8,10], \n",
        "    [101,9,8,37,6,45,4,3,2,41]\n",
        "]})\n",
        "test_sample = bytes(test_sample,encoding='utf8')\n",
        "\n",
        "if aks_service.state == \"Healthy\":\n",
        "    prediction = aks_service.run(input_data=test_sample)\n",
        "    print(prediction)\n",
        "else:\n",
        "     raise ValueError(\"Service deployment isn't healthy, can't call the service\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 9. See your service telemetry in App Insights\n",
        "1. Go to the [Azure Portal](https://portal.azure.com/)\n",
        "2. All resources--> Select the subscription/resource group where you created your Workspace--> Select the App Insights type\n",
        "3. Click on the AppInsights resource. You'll see a highlevel dashboard with information on Requests, Server response time and availability.\n",
        "4. Click on the top banner \"Analytics\"\n",
        "5. In the \"Schema\" section select \"traces\" and run your query.\n",
        "6. Voila! All your custom traces should be there."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Disable App Insights"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "aks_service.update(enable_app_insights=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Clean up"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "%%time\n",
        "aks_service.delete()\n",
        "aci_service.delete()\n",
        "image.delete()\n",
        "model.delete()"
      ]
    }
  ],
  "metadata": {
    "authors": [
      {
        "name": "marthalc"
      }
    ],
    "kernelspec": {
      "display_name": "Python 3.6",
      "language": "python",
      "name": "python36"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}