github.com/mweagle/Sparta@v1.15.0/docs_source/content/reference/operations/profiling.md (about)

     1  ---
     2  date: 2016-03-09T19:56:50+01:00
     3  title: Profiling
     4  weight: 40
     5  ---
     6  
     7  One of Lambda's biggest strengths, its ability to automatically scale across ephemeral containers in response to increased load, also creates one of its biggest problems: observability. The traditional set of [tools](https://jvns.ca/) used to identify performance bottlenecks are no longer valid, as there is no host into which one can SSH and interactively interrogate. Identifying performance bottlenecks is even more significant due to the Lambda [pricing model](https://aws.amazon.com/lambda/pricing/), where idle time often directly translates into increased costs.
     8  
     9  However, Go offers the excellent [pprof](https://rakyll.org/pprof-ui/) tool to visualize and cost allocate program execution. Beginning with [Sparta 0.20.4](https://github.com/mweagle/Sparta/blob/master/CHANGES.md#v0204), it's possible to enable per-lambda instance snapshotting which can be locally visualized. This documentation provides an overview of how to enable profiling. The full source is available at the [SpartaPProf](https://github.com/mweagle/SpartaPProf) repo.
    10  
    11  To learn more about `pprof` itself, please visit:
    12  
    13    * <a href="https://twitter.com/rakyll" class="fas fas-twitter">@rakyll's</a> [blog](https://rakyll.org/)
    14    * [Profiling Go programs with pprof](https://jvns.ca/blog/2017/09/24/profiling-go-with-pprof/)
    15    * [Profiling Golang](https://medium.com/@tjholowaychuk/profiling-golang-851db2d9ae24)
    16    * [Profiling and optimizing Go web programs](http://artem.krylysov.com/blog/2017/03/13/profiling-and-optimizing-go-web-applications/)
    17  
    18  ## Enabling Profiling
    19  
    20  To enable profiling add a reference to [ScheduleProfileLoop](https://godoc.org/github.com/mweagle/Sparta#ScheduleProfileLoop)
    21  in your `main()` function as in:
    22  
    23  ```go
    24  sparta.ScheduleProfileLoop(nil,
    25    5*time.Second,
    26    30*time.Second,
    27    "goroutine",
    28    "heap",
    29    "threadcreate",
    30    "block",
    31    "mutex")
    32  ```
    33  
    34  This function accepts the following arguments:
    35  
    36    - `s3Bucket`: The S3 bucket to which profile snapshots should be written. If `nil`, the bucket used to host the original ZIP code archive is used.
    37    - `snapshotInterval` - The interval between each snapshot.
    38    - `cpuProfileDuration` - The duration for the [CPUProfile](https://golang.org/pkg/runtime/pprof/#StartCPUProfile) sample.
    39    - `profileNames...` - The profile types to snapshot. In addition to the [standard profiles](https://golang.org/pkg/runtime/pprof/#Profile), Sparta includes a "cpu" profile iff the `cpuProfileDuration` is non-zero.
    40  
    41  ### Profiling Implementation
    42  
    43  During the `provision` step, the `ScheduleProfileLoop` adds an [IAMRolePrivilege](https://godoc.org/github.com/mweagle/Sparta#IAMRolePrivilege) _Allow_ entry (if possible) to each Lambda function's IAM policy. This policy extension is a minimal privilege and only enables `s3:PutObject` against the Sparta managed key prefix (see below).
    44  
    45  The `provision` implementation also annotates the Lambda's [Environment](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-environment) map so that the publishing loop knows where to publish snapshots.
    46  
    47  During the `execute` step when the Sparta binary is executing in AWS Lambda, the `ScheduleProfileLoop` installs the requested sampling and publishing steps so that profile snapshots, serialized as [proto](https://github.com/google/pprof/blob/master/proto/profile.proto) files, are properly saved to S3. Profiles are published to a reserved location in S3 with the form:
    48  
    49  s3:://{BUCKET_NAME}/sparta/pprof/{STACK_NAME}/profiles/{PROFILE_TYPE}/{SNAPSHOT_INDEX}-{PROFILE_TYPE}.λ-{INSTANCE_ID}.profile
    50  
    51  To manage profile sprawl, each lambda instance uses a rolling `SNAPSHOT_INDEX` to maintain a fixed size window. The new `profile` command is responsible for aggregating them into a single local, consolidated profile that can be visualized using the existing tools.
    52  
    53  ## Deploying
    54  With profiling enabled, the next step is to deploy the *SpartaPProf* service using the `provision` command:
    55  
    56  {{<highlight plain>}}
    57  $ go run main.go provision --s3Bucket MY-S3-BUCKET
    58  INFO[0000] ════════════════════════════════════════════════
    59  INFO[0000] ╔═╗╔═╗╔═╗╦═╗╔╦╗╔═╗   Version : 1.4.0
    60  INFO[0000] ╚═╗╠═╝╠═╣╠╦╝ ║ ╠═╣   SHA     : 8f199e1
    61  INFO[0000] ╚═╝╩  ╩ ╩╩╚═ ╩ ╩ ╩   Go      : go1.11.1
    62  INFO[0000] ════════════════════════════════════════════════
    63  INFO[0000] Service: SpartaPProf-mweagle                  LinkFlags= Option=provision UTC="2018-10-11T14:59:48Z"
    64  INFO[0000] ════════════════════════════════════════════════
    65  INFO[0000] Using `git` SHA for StampedBuildID            Command="git rev-parse HEAD" SHA=c3fbe8c289c3184efec842dca56b9bf541f39d21
    66  INFO[0000] Provisioning service                          BuildID=c3fbe8c289c3184efec842dca56b9bf541f39d21 CodePipelineTrigger= InPlaceUpdates=false NOOP=false Tags=
    67  INFO[0000] Verifying IAM Lambda execution roles
    68  INFO[0000] IAM roles verified                            Count=2
    69  INFO[0000] Checking S3 versioning                        Bucket=MY-S3-BUCKET VersioningEnabled=true
    70  INFO[0000] Checking S3 region                            Bucket=MY-S3-BUCKET Region=us-west-2
    71  INFO[0000] Running `go generate`
    72  INFO[0000] Compiling binary                              Name=Sparta.lambda.amd64
    73  INFO[0002] Creating code ZIP archive for upload          TempName=./.sparta/SpartaPProf_mweagle-code.zip
    74  INFO[0002] Lambda code archive size                      Size="17 MB"
    75  INFO[0002] Uploading local file to S3                    Bucket=MY-S3-BUCKET Key=SpartaPProf-mweagle/SpartaPProf_mweagle-code.zip Path=./.sparta/SpartaPProf_mweagle-code.zip Size="17 MB"
    76  INFO[0009] Calling WorkflowHook                          ServiceDecoratorHook= WorkflowHookContext="map[]"
    77  INFO[0009] Uploading local file to S3                    Bucket=MY-S3-BUCKET Key=SpartaPProf-mweagle/SpartaPProf_mweagle-cftemplate.json Path=./.sparta/SpartaPProf_mweagle-cftemplate.json Size="7.1 kB"
    78  INFO[0010] Issued CreateChangeSet request                StackName=SpartaPProf-mweagle
    79  INFO[0013] Issued ExecuteChangeSet request               StackName=SpartaPProf-mweagle
    80  INFO[0026] CloudFormation Metrics ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    81  INFO[0026]     Operation duration                        Duration=11.71s Resource=SpartaPProf-mweagle Type="AWS::CloudFormation::Stack"
    82  INFO[0026]     Operation duration                        Duration=1.60s Resource=HelloWorldLambda7d01d27fe422d278bcc652b4a989528718eb88af Type="AWS::Lambda::Function"
    83  INFO[0026]     Operation duration                        Duration=1.33s Resource=KinesisLogConsumerLambda275ace0435c45228161570811178ce06fbcb359c Type="AWS::Lambda::Function"
    84  INFO[0026] Stack Outputs ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    85  INFO[0026]     HelloWorldFunctionARN                     Description="Hello World Lambda ARN" Value="arn:aws:lambda:us-west-2:123412341234:function:SpartaPProf-mweagle_Hello_World"
    86  INFO[0026]     KinesisLogConsumerFunctionARN             Description="KinesisLogConsumer Lambda ARN" Value="arn:aws:lambda:us-west-2:123412341234:function:SpartaPProf-mweagle_KinesisLogConsumer"
    87  INFO[0026] Stack provisioned                             CreationTime="2018-10-03 23:34:21.142 +0000 UTC" StackId="arn:aws:cloudformation:us-west-2:123412341234:stack/SpartaPProf-mweagle/da781540-c764-11e8-9bf1-0aceeffcea3c" StackName=SpartaPProf-mweagle
    88  INFO[0026] ════════════════════════════════════════════════
    89  INFO[0026] SpartaPProf-mweagle Summary
    90  INFO[0026] ════════════════════════════════════════════════
    91  INFO[0026] Verifying IAM roles                           Duration (s)=0
    92  INFO[0026] Verifying AWS preconditions                   Duration (s)=1
    93  INFO[0026] Creating code bundle                          Duration (s)=2
    94  INFO[0026] Uploading code                                Duration (s)=8
    95  INFO[0026] Ensuring CloudFormation stack                 Duration (s)=17
    96  INFO[0026] Total elapsed time                            Duration (s)=27
    97  
    98  {{</highlight>}}
    99  
   100  ## Generating Load
   101  
   102  While the **SpartaPProf** binary does include functions that are likely to generate profiling data, we still need to issue a sufficient series of events to produce a non-empty profile data set. **SpartaPProf** includes a simple tool (_cmd/load.go_) that directly calls the provisioned lambda function on a regular interval. It accepts a single command line argument, the _ARN_ of the lambda function listed as a _Stack output_ in the log output:
   103  
   104  {{<highlight plain>}}
   105  INFO[0058] FunctionARN                                   Description="Lambda function ARN" Value="arn:aws:lambda:us-west-2:123412341234:function:SpartaPProf_mweagle_Hello_World"
   106  {{</highlight>}}
   107  
   108  Run the simple load generation script with the ARN value as in:
   109  
   110  {{<highlight plain>}}
   111  $ cd cmd
   112  $ go run load.go arn:aws:lambda:us-west-2:012345678910:function:SpartaPProf-mweagle-Hello_World
   113  Lambda response (0 of 60): "Hi there 🌍"
   114  Lambda response (1 of 60): "Hi there 🌍"
   115  Lambda response (2 of 60): "Hi there 🌍"
   116  Lambda response (3 of 60): "Hi there 🌍"
   117  Lambda response (4 of 60): "Hi there 🌍"
   118  Lambda response (5 of 60): "Hi there 🌍"
   119  Lambda response (6 of 60): "Hi there 🌍"
   120  Lambda response (7 of 60): "Hi there 🌍"
   121  Lambda response (8 of 60): "Hi there 🌍"
   122  Lambda response (9 of 60): "Hi there 🌍"
   123  Lambda response (10 of 60): "Hi there 🌍"
   124  Lambda response (11 of 60): "Hi there 🌍"
   125  ...
   126  {{</highlight>}}
   127  
   128  After all the requests have completed for this sample against a stack provisioned in `us-west-2`, a set of named profiles was published. Since each container's instance id is randomly assigned, the profile names you see will have slightly different names
   129  
   130  {{<highlight plain>}}
   131  ---------------------------------------------------------------
   132  S3 bucket: s3://weagle/sparta/pprof/SpartaPProf-mweagle/profiles
   133  ---------------------------------------------------------------
   134  2017-11-26 11:32:28   41 Bytes sparta/pprof/SpartaPProf-mweagle/profiles/block/0-block.λ-3838737145763622974.profile
   135  2017-11-26 11:32:27    1.8 KiB sparta/pprof/SpartaPProf-mweagle/profiles/cpu/0-cpu.λ-3838737145763622974.profile
   136  2017-11-26 11:32:28    1.8 KiB sparta/pprof/SpartaPProf-mweagle/profiles/goroutine/0-goroutine.λ-3838737145763622974.profile
   137  2017-11-26 11:32:28    2.2 KiB sparta/pprof/SpartaPProf-mweagle/profiles/heap/0-heap.λ-3838737145763622974.profile
   138  2017-11-26 11:32:28   54 Bytes sparta/pprof/SpartaPProf-mweagle/profiles/mutex/0-mutex.λ-3838737145763622974.profile
   139  2017-11-26 11:32:30  200 Bytes sparta/pprof/SpartaPProf-mweagle/profiles/threadcreate/0-threadcreate.λ-3838737145763622974.profile
   140  {{</highlight>}}
   141  
   142  
   143  ## Visualizing Profiles
   144  
   145  Sparta delegates to the pprof [webui](https://github.com/google/pprof) to visualize profile snapshots. Ensure you have the latest version of this by running `go get -u -v go get github.com/google/pprof` first.
   146  
   147  The final step is to provide the profile snapshots to `pprof`. Sparta exposes a `profile` command that accomplishes this, by fetching and consolidating all published profiles for a single type.
   148  
   149  {{<highlight plain>}}
   150  $ go run main.go profile --s3Bucket weagle
   151  INFO[0000] ════════════════════════════════════════════════
   152  INFO[0000] ╔═╗┌─┐┌─┐┬─┐┌┬┐┌─┐   Version : 1.0.2
   153  INFO[0000] ╚═╗├─┘├─┤├┬┘ │ ├─┤   SHA     : b37b93e
   154  INFO[0000] ╚═╝┴  ┴ ┴┴└─ ┴ ┴ ┴   Go      : go1.9.2
   155  INFO[0000] ════════════════════════════════════════════════
   156  INFO[0000] Service: SpartaPProf-mweagle                  LinkFlags= Option=profile UTC="2018-01-29T15:23:18Z"
   157  INFO[0000] ════════════════════════════════════════════════
   158  ? Which stack would you like to profile: SpartaPProf-mweagle
   159  ? What type of profile would you like to view? heap
   160  ? What profile snapshot(s) would you like to view? Download new snapshots from S3
   161  ? Please select a heap profile type: alloc_space
   162  INFO[0028] Refreshing cached profiles                    CacheRoot=.sparta/profiles/SpartaPProf-mweagle/heap ProfileRootKey=sparta/pprof/SpartaPProf-mweagle/profiles/heap S3Bucket=MY-S3-BUCKET StackName=SpartaPProf-mweagle Type=heap
   163  INFO[0028] Aggregating profile                           Input=".sparta/profiles/SpartaPProf-mweagle/heap/0-heap.λ-8850662459689822644.profile"
   164  INFO[0028] Consolidating profiles                        ProfileCount=1
   165  INFO[0028] Creating consolidated profile                 ConsolidatedProfile=.sparta/heap.consolidated.profile
   166  INFO[0028] Starting pprof webserver on http://localhost:8080. Enter Ctrl+C to exit.{{</highlight>}}
   167  
   168  The `profile` command downloads the published profiles and consolidates them into a single cached version in the _./sparta_ directory with a name of the form:
   169  
   170  ./.sparta/{PROFILE_TYPE}.consolidated.profile
   171  
   172  You can choose to use the cached file if it exists.
   173  
   174  For this sample run, the _heap_ profile output is made available to the `pprof` webserver which produces the following layout:
   175  
   176  ![Main Alloc Space](/images/profiling/main_alloc_space.jpg)
   177  
   178  The latest `pprof` also includes flamegraph support to help identify issues:
   179  
   180  ![Main Alloc Space Flamegraph](/images/profiling/main_alloc_space_flamegraph.jpg)
   181  
   182  To view another profile type, enter `Ctrl+C` to exit the blocking web ui loop and launch another `profile` session.
   183  
   184  # Conclusion
   185  
   186  Go includes a very powerful set of tools that can help diagnose performance bottlenecks. With the Sparta `profile` command, it's possible to bring that same visibility to bear to AWS Lambda, despite running on ephemeral, (typically) unaddressable hosts. Get started optimizing today! And also, don't forget to disable the profiling loop before pushing to production.
   187  
   188  # Notes
   189    - [CPU Flame Graphs](http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html) provides a great overview.
   190    - It's not currently possible to use [custom profiles](https://medium.com/@cep21/creating-custom-go-profiles-with-pprof-b737dfc58e11)
   191    - Lambda instances are limited to a window size of 3 rolling snapshots
   192    - The `explore` command also exposes the `pprof` [web handlers](https://github.com/mweagle/Sparta/blob/ead2872585dc0da81f222577a898707c6a486ab1/execute_utils.go#L35) for local exploration as well.
   193