github.com/grafana/pyroscope@v1.18.0/docs/sources/get-started/ride-share-tutorial.md (about)

     1  ---
     2  description: Learn how to get started with Pyroscope using a simple Ride share app.
     3  menuTitle: Ride share tutorial
     4  title: Ride share tutorial with Pyroscope
     5  weight: 250
     6  killercoda:
     7    title: Ride share tutorial
     8    description: Learn how to get started with Pyroscope using a simple Ride share app.
     9    details:
    10        intro:
    11           foreground: docker-compose-update.sh
    12    backend:
    13      backend:
    14      imageid: ubuntu
    15  ---
    16  
    17  <!-- INTERACTIVE page intro.md START -->
    18  
    19  # Ride share tutorial with Pyroscope
    20  
    21  This tutorial demonstrates a basic use case of Pyroscope by profiling a "Ride Share" application.
    22  In this example, you learn:
    23  
    24  - How an application is instrumented with Pyroscope, including techniques for dynamically tagging functions.
    25  - How to view the resulting profile data in Grafana using the Profiles View.
    26  - How to integrate Pyroscope with Grafana to visualize the profile data.
    27  
    28  <!-- INTERACTIVE ignore START -->
    29  
    30  ## Before you begin
    31  
    32  You need to have the following prerequisites to complete this tutorial:
    33  - Git
    34  - [Docker](https://docs.docker.com/compose/install/)
    35  - The Docker Compose plugin (included with Docker Desktop)
    36  
    37  {{< admonition type="tip" >}}
    38  Try this tutorial in an interactive learning environment: [Ride share tutorial with Pyroscope](https://killercoda.com/grafana-labs/course/pyroscope/ride-share-tutorial).
    39  
    40  It's a fully configured environment with all the dependencies installed.
    41  
    42  Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
    43  {{< /admonition >}}
    44  
    45  <!-- INTERACTIVE ignore END -->
    46  
    47  ## Background
    48  
    49  In this tutorial, you will profile a simple "Ride Share" application. The application is a Python Flask app that simulates a ride-sharing service. The app has three endpoints which are found in the `lib/server.py` file:
    50  
    51  - `/bike`    : calls the `order_bike(search_radius)` function to order a bike
    52  - `/car`     : calls the `order_car(search_radius)` function to order a car
    53  - `/scooter` : calls the `order_scooter(search_radius)` function to order a scooter
    54  
    55  To simulate a highly available and distributed system, the app is deployed on three distinct servers in 3 different regions:
    56  - us-east
    57  - eu-north
    58  - ap-south
    59  
    60  This is simulated by running three instances of the server in Docker containers. Each server instance is tagged with the region it represents.
    61  
    62  {{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-demo.gif" caption="Getting started sample application" alt="Getting started sample application" >}}
    63  
    64  In this scenario, a load generator will send mock-load to the three servers as well as their respective endpoints. This lets you see how the application performs per region and per vehicle type.
    65  
    66  {{<docs/ignore>}}
    67  {{< admonition type="tip" >}}
    68  A setup script runs in the background to install the necessary dependencies. This should take no longer than 30 seconds. Your instance will be ready to use once you `Setup complete. You may now begin the tutorial`.
    69  {{< /admonition >}}
    70  {{</docs/ignore>}}
    71  
    72  <!-- INTERACTIVE page intro.md END -->
    73  
    74  <!-- INTERACTIVE page step1.md START -->
    75  
    76  ## Clone the repository
    77  
    78  1. Clone the repository to your local machine:
    79  
    80      ```bash
    81      git clone https://github.com/grafana/pyroscope.git && cd pyroscope
    82      ```
    83  
    84  1. Navigate to the tutorial directory:
    85  
    86      ```bash
    87      cd examples/language-sdk-instrumentation/python/rideshare/flask
    88      ```
    89  
    90  ## Start the application
    91  
    92  Start the application using Docker Compose:
    93  
    94  ```bash
    95  docker compose up -d
    96  ```
    97  
    98  This may take a few minutes to download the required images and build the demo application. Once ready, you will see the following output:
    99  
   100  ```console
   101   ✔ Network flask_default  Created
   102   ✔ Container flask-ap-south-1  Started
   103   ✔ Container flask-grafana-1  Started
   104   ✔ Container flask-pyroscope-1  Started
   105   ✔ Container flask-load-generator-1 Started
   106   ✔ Container flask-eu-north-1 Started
   107   ✔ Container flask-us-east-1 Started
   108  ```
   109  
   110  Optional: To verify the containers are running, run:
   111  
   112  ```bash
   113  docker ps -a
   114  ```
   115  <!-- INTERACTIVE page step1.md END -->
   116  
   117  <!-- INTERACTIVE page step2.md START -->
   118  
   119  ## Accessing Profiles Drilldown in Grafana
   120  
   121  Grafana includes the [Profiles Drilldown](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/simplified-exploration/profiles/) app that you can use to view profile data. To access Profiles Drilldown, open a browser and navigate to [http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer](http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer).
   122  
   123  ### How tagging works
   124  
   125  In this example, the application is instrumented with Pyroscope using the Python SDK.
   126  The SDK allows you to tag functions with metadata that can be used to filter and group the profile data in the Profiles Drilldown.
   127  This example uses static and dynamic tagging.
   128  
   129  To start, let's take a look at a static tag use case. Within the `lib/server.py` file, find the Pyroscope configuration:
   130  
   131  ```python
   132   pyroscope.configure(
   133       application_name = app_name,
   134       server_address   = server_addr,
   135       basic_auth_username = basic_auth_username, # for grafana cloud
   136       basic_auth_password = basic_auth_password, # for grafana cloud
   137       tags             = {
   138           "region":   f'{os.getenv("REGION")}',
   139       }
   140   )
   141  ```
   142  This tag is considered static because the tag is set at the start of the application and doesn't change.
   143  In this case, it's useful for grouping profiles on a per region basis, which lets you see the performance of the application per region.
   144  
   145  1. Open Grafana using the following url: [http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer](http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer).
   146  1. In the main menu, select **Drilldown** > **Profiles**.
   147  1. Select  **Labels** in the **Exploration** path.
   148  1. Select  **ride-sharing-app** in the **Service** drop-down menu.
   149  1. Select the **region** tab in the **Group by labels** section.
   150  
   151  You should now see a list of regions that the application is running in. You can see that `eu-north` is experiencing the most load.
   152  
   153  {{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-tag-region-2.png" caption="Region Tag" alt="Region Tag" >}}
   154  
   155  Next, look at a dynamic tag use case. Within the `lib/utility/utility.py` file,  find the following function:
   156  
   157  ```python
   158   def find_nearest_vehicle(n, vehicle):
   159       with pyroscope.tag_wrapper({ "vehicle": vehicle}):
   160           i = 0
   161           start_time = time.time()
   162           while time.time() - start_time < n:
   163               i += 1
   164           if vehicle == "car":
   165               check_driver_availability(n)
   166  ```
   167  
   168  This example uses `tag_wrapper` to tag the function with the vehicle type.
   169  Notice that the tag is dynamic as it changes based on the vehicle type.
   170  This is useful for grouping profiles on a per vehicle basis, allowing us to see the performance of the application per vehicle type being requested.
   171  
   172  Use Profiles Drilldown to see how this tag is used:
   173  1. Open Profiles Drilldown using the following url: [http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer](http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer).
   174  1. Select on **Labels** in the **Exploration** path.
   175  1. In the **Group by labels** section, select the **vehicle** tab.
   176  
   177  You should now see a list of vehicle types that the application is using. You can see that `car` is experiencing the most load.
   178  
   179  <!-- INTERACTIVE page step2.md END -->
   180  
   181  <!-- INTERACTIVE page step3.md START -->
   182  
   183  ## Identifying the performance bottleneck
   184  
   185  The first step when analyzing a profile outputted from your application, is to take note of the largest node which is where your application is spending the most resources.
   186  To discover this, you can use the **Flame graph** view:
   187  
   188  1. Open Profiles Drilldown using the following url: [http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer](http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer).
   189  1. Select **Flame graph** from the **Exploration** path.
   190  1. Verify that  `ride-sharing-app` is selected in the **Service** drop-down menu and `process_cpu/cpu` in the **Profile type** drop-down menu.
   191  
   192  It should look something like this:
   193  
   194  {{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-bottle-neck-3.png" caption="Bottleneck" alt="Bottleneck" >}}
   195  
   196  The flask `dispatch_request` function is the parent to three functions that correspond to the three endpoints of the application:
   197  - `order_bike`
   198  - `order_car`
   199  - `order_scooter`
   200  
   201  By tagging both `region` and `vehicle` and looking at the [**Labels** view](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/simplified-exploration/profiles/choose-a-view/#labels), you can hypothesize:
   202  
   203  - Something is wrong with the `/car` endpoint code where `car` vehicle tag is consuming **68% of CPU**
   204  - Something is wrong with one of our regions where `eu-north` region tag is consuming **54% of CPU**
   205  
   206  From the flame graph, you can see that for the `eu-north` tag the biggest performance impact comes from the `find_nearest_vehicle()` function which consumes close to **68% of cpu**.
   207  To analyze this, go directly to the comparison page using the comparison dropdown.
   208  
   209  ### Comparing two time periods
   210  
   211  The **Diff flame graph** view lets you compare two time periods side by side.
   212  This is useful for identifying changes in performance over time.
   213  This example compares the performance of the `eu-north` region within a given time period against the other regions.
   214  
   215  1. Open Profiles Drilldown in Grafana using the following url: [http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer](http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer).
   216  1. Select **Diff flame graph** in the **Exploration** path.
   217  1. Verify that  `ride-sharing-app` is selected in the **Service** drop-down menu and `process_cpu/cpu` in the **Profile type** drop-down menu.
   218  1. In **Baseline**, filter by `region` and select `!= eu-north`.
   219  1. In **Comparison**, filter by `region` and select `== eu-north`.
   220  1. In **Choose a preset** drop-down, select the time period you want to compare against.
   221  
   222  Scroll down to compare the two time periods side by side.
   223  Note that the `eu-north` region (right side) shows an excessive amount of time spent in the `find_nearest_vehicle` function.
   224  This looks to be caused by a mutex lock that is causing the function to block.
   225  
   226  {{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-time-comparison-2.png" caption="Time Comparison" alt="Time Comparison" >}}
   227  
   228  <!-- INTERACTIVE page step3.md END -->
   229  
   230  <!-- INTERACTIVE page step4.md START -->
   231  
   232  ## How was Pyroscope integrated with Grafana in this tutorial?
   233  
   234  The `docker-compose.yml` file includes a Grafana container that's pre-configured with the Pyroscope plugin:
   235  
   236  ```yaml
   237    grafana:
   238      image: grafana/grafana:latest
   239      environment:
   240      - GF_PLUGINS_PREINSTALL_SYNC=grafana-pyroscope-app
   241      - GF_AUTH_ANONYMOUS_ENABLED=true
   242      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
   243      - GF_AUTH_DISABLE_LOGIN_FORM=true
   244      volumes:
   245      - ./grafana-provisioning:/etc/grafana/provisioning
   246      ports:
   247      - 3000:3000
   248  ```
   249  
   250  Grafana is also pre-configured with the Pyroscope data source.
   251  
   252  ### Challenge
   253  
   254  As a challenge, see if you can generate a similar comparison with the `vehicle` tag.
   255  
   256  <!-- INTERACTIVE page step4.md END -->
   257  
   258  <!-- INTERACTIVE page finish.md START -->
   259  
   260  ## Summary
   261  
   262  In this tutorial, you learned how to profile a simple "Ride Share" application using Pyroscope.
   263  You have learned some of the core instrumentation concepts such as tagging and how to use Profiles Drilldown identify performance bottlenecks.
   264  
   265  ### Next steps
   266  
   267  - Learn more about the Pyroscope SDKs and how to [instrument your application with Pyroscope](https://grafana.com/docs/pyroscope/<PYROSCOPE_VERSION>/configure-client/).
   268  - Deploy Pyroscope in a production environment using the [Pyroscope Helm chart](https://grafana.com/docs/pyroscope/<PYROSCOPE_VERSION>/deploy-kubernetes/).
   269  - Continue exploring your profile data using [Profiles Drilldown](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/simplified-exploration/profiles/investigate/)
   270  <!-- INTERACTIVE page finish.md END -->
   271  
   272  
   273  
   274  
   275  
   276  
   277  
   278  
   279