Computer Engineering/Django

Python/Django NewRelic 셋업 및 환경 분리하기.

jordan.bae 2022. 6. 29. 19:39

Introduction

NewRelic Dashboard

이번 글에서는 Python 및 Django에서 NewRelic을 셋업하고 환경 별로 분리하는 방법을 소개합니다.

NewRelic은 대표적인 APM (Application Performance Monitoring) 서비스 중 하나입니다. NewRelic을 설치해서 쉽게 어플리케이션의 병목을 확인할 수 있습니다. 

대부분의 내용은 공식 문서에 명시되어 있는 내용이고 제 기준으로 이해하기 쉽도록 정리한 글이라고 생각하시면 될 것 같습니다.

 

 

 

Set up

newrelic 셋업은 굉장히 간단한 편입니다. 대부분의 웹 프레임워크(Django, Flask 등) 및 호스팅 프로그램(WSGI, Gunicorn, uWSGI)등은 기본 Python Agent 설정만 해주면 됩니다. 웹이 아닌 단순 스크립트나 백그라운드 작업은 custom 설정이 필요합니다. 이 글에서는 기본 설정만을 다룹니다.

 

설치하는 방법은 아래와 같습니다.

 

1. NewRelic 패키지 설치

pip install newrelic

 

2. Agent 구성 파일 생성

 

newrelic-admin generate-config YOUR_LICENSE_KEY newrelic.ini

 

3. 스크립트 명령을 서버 시작 명령 옵션 앞에 사용.

NEW_RELIC_CONFIG_FILE=newrelic.ini newrelic-admin run-program YOUR_COMMAND_OPTIONS

# example
NEW_RELIC_CONFIG_FILE=newrelic.ini newrelic-admin run-program python manage.py runserver
NEW_RELIC_CONFIG_FILE=newrelic.ini newrelic-admin run-program gunicorn wsgi

 

3단계만 거치면 해당 Application에 대해서 APM을 활용할 수 있습니다.

 

newrelic.ini 파일의 환경 변수들을 보면 아래와 같습니다. app_name을 통해서 표시 될 이름을 정할 수 있고, 모니터링과 관련된 설정들 그리고 환경변수를 주입한 경우 overriding할 수 있는 섹션이 존재합니다. [newrelic:development] 이 부분이 환경 분리와 관련된 부분이라서 아래서 다시 살펴볼 예정입니다.

[newrelic]

# You must specify the license key associated with your New
# Relic account. This key binds the Python Agent's data to your
# account in the New Relic service.
license_key = your-license-key

# The application name. Set this to be the name of your
# application as you would like it to show up in New Relic UI.
# The UI will then auto-map instances of your application into a
# entry on your home dashboard page.
app_name = your-app-name

# New Relic offers distributed tracing for monitoring and analyzing modern
# distributed systems.Enable distributed tracing.
distributed_tracing.enabled = true

# When "true", the agent collects performance data about your
# application and reports this data to the New Relic UI at
# newrelic.com. This global switch is normally overridden for
# each environment below.
monitor_mode = true

# Sets the name of a file to log agent messages to. Useful for
# debugging any issues with the agent. This is not set by
# default as it is not known in advance what user your web
# application processes will run as and where they have
# permission to write to. Whatever you set this to you must
# ensure that the permissions for the containing directory and
# the file itself are correct, and that the user that your web
# application runs as can write to the file. If not able to
# write out a log file, it is also possible to say "stderr" and
# output to standard error output. This would normally result in
# output appearing in your web server log.
#log_file = /tmp/newrelic-python-agent.log

# Sets the level of detail of messages sent to the log file, if
# a log file location has been provided. Possible values, in
# increasing order of detail, are: "critical", "error", "warning",
# "info" and "debug". When reporting any agent issues to New
# Relic technical support, the most useful setting for the
# support engineers is "debug". However, this can generate a lot
# of information very quickly, so it is best not to keep the
# agent at this level for longer than it takes to reproduce the
# problem you are experiencing.
log_level = info

# The Python Agent communicates with the New Relic service using
# SSL by default. Note that this does result in an increase in
# CPU overhead, over and above what would occur for a non SSL
# connection, to perform the encryption involved in the SSL
# communication. This work is though done in a distinct thread
# to those handling your web requests, so it should not impact
# response times. You can if you wish revert to using a non SSL
# connection, but this will result in information being sent
# over a plain socket connection and will not be as secure.
ssl = true

# High Security Mode enforces certain security settings, and
# prevents them from being overridden, so that no sensitive data
# is sent to New Relic. Enabling High Security Mode means that
# SSL is turned on, request parameters are not collected, and SQL
# can not be sent to New Relic in its raw form. To activate High
# Security Mode, it must be set to 'true' in this local .ini
# configuration file AND be set to 'true' in the server-side
# configuration in the New Relic user interface. For details, see
# https://docs.newrelic.com/docs/subscriptions/high-security
high_security = false

# The Python Agent will attempt to connect directly to the New
# Relic service. If there is an intermediate firewall between
# your host and the New Relic service that requires you to use a
# HTTP proxy, then you should set both the "proxy_host" and
# "proxy_port" settings to the required values for the HTTP
# proxy. The "proxy_user" and "proxy_pass" settings should
# additionally be set if proxy authentication is implemented by
# the HTTP proxy. The "proxy_scheme" setting dictates what
# protocol scheme is used in talking to the HTTP proxy. This
# would normally always be set as "http" which will result in the
# agent then using a SSL tunnel through the HTTP proxy for end to
# end encryption.
# proxy_scheme = http
# proxy_host = hostname
# proxy_port = 8080
# proxy_user =
# proxy_pass =

# Capturing request parameters is off by default. To enable the
# capturing of request parameters, first ensure that the setting
# "attributes.enabled" is set to "true" (the default value), and
# then add "request.parameters.*" to the "attributes.include"
# setting. For details about attributes configuration, please
# consult the documentation.
# attributes.include = request.parameters.*

# The transaction tracer captures deep information about slow
# transactions and sends this to the UI on a periodic basis. The
# transaction tracer is enabled by default. Set this to "false"
# to turn it off.
transaction_tracer.enabled = true

# Threshold in seconds for when to collect a transaction trace.
# When the response time of a controller action exceeds this
# threshold, a transaction trace will be recorded and sent to
# the UI. Valid values are any positive float value, or (default)
# "apdex_f", which will use the threshold for a dissatisfying
# Apdex controller action - four times the Apdex T value.
transaction_tracer.transaction_threshold = apdex_f

# When the transaction tracer is on, SQL statements can
# optionally be recorded. The recorder has three modes, "off"
# which sends no SQL, "raw" which sends the SQL statement in its
# original form, and "obfuscated", which strips out numeric and
# string literals.
transaction_tracer.record_sql = obfuscated

# Threshold in seconds for when to collect stack trace for a SQL
# call. In other words, when SQL statements exceed this
# threshold, then capture and send to the UI the current stack
# trace. This is helpful for pinpointing where long SQL calls
# originate from in an application.
transaction_tracer.stack_trace_threshold = 0.5

# Determines whether the agent will capture query plans for slow
# SQL queries. Only supported in MySQL and PostgreSQL. Set this
# to "false" to turn it off.
transaction_tracer.explain_enabled = true

# Threshold for query execution time below which query plans
# will not not be captured. Relevant only when "explain_enabled"
# is true.
transaction_tracer.explain_threshold = 0.5

# Space separated list of function or method names in form
# 'module:function' or 'module:class.function' for which
# additional function timing instrumentation will be added.
transaction_tracer.function_trace =

# The error collector captures information about uncaught
# exceptions or logged exceptions and sends them to UI for
# viewing. The error collector is enabled by default. Set this
# to "false" to turn it off.
error_collector.enabled = true

# To stop specific errors from reporting to the UI, set this to
# a space separated list of the Python exception type names to
# ignore. The exception name should be of the form 'module:class'.
error_collector.ignore_errors =

# Browser monitoring is the Real User Monitoring feature of the UI.
# For those Python web frameworks that are supported, this
# setting enables the auto-insertion of the browser monitoring
# JavaScript fragments.
browser_monitoring.auto_instrument = true

# A thread profiling session can be scheduled via the UI when
# this option is enabled. The thread profiler will periodically
# capture a snapshot of the call stack for each active thread in
# the application to construct a statistically representative
# call tree.
thread_profiler.enabled = true

# ---------------------------------------------------------------------------

#
# The application environments. These are specific settings which
# override the common environment settings. The settings related to a
# specific environment will be used when the environment argument to the
# newrelic.agent.initialize() function has been defined to be either
# "development", "test", "staging" or "production".
#

[newrelic:development]
monitor_mode = false

[newrelic:test]
monitor_mode = false

[newrelic:staging]

[newrelic:production]

 

 

 

환경별로 Application name을 분리하기

위에서 환경 별로 환경 변수를 overriding 할 수 있는 섹션을 잠깐 설명드렸었습니다.

[newrelic:development]
monitor_mode = false

[newrelic:test]
monitor_mode = false

[newrelic:staging]

[newrelic:production]

 

Overriding을 하기위해서는 환경 값을 주입해야 합니다. 이 부분을 application code에서 주입이 가능합니다.

이부분을 이해하기 위해서는 NewRelic의 환경 변수를 setting하는 방법을 이해해야 합니다.

configuration 관련 적용 순서

보시는 것처럼 Server side config가 Agent config file을 overriding할 수 있기 때문에 server side config에서 env를 주입해서 config파일에서 존재하는 환경별 section 값을 사용할 수 있습니다. wsgi.py와 manage.py의 예시 코드를 보면 아래와 같습니다.

 

Django wsgi.py 코드

from pathlib import Path

import newrelic.agent
from django.conf import settings
from django.core.wsgi import get_wsgi_application

newrelic.agent.initialize("newrelic.ini", environment=settings.ENVIRONMENT)
.....

 

 

Django manage.py 코드 (manage.py runserver에서 필요할 때만 newrelic.ini에서 newrelic:development section에서 monitor_mode 를 True로 설정해서 잠시 profiling해볼 수 있습니다.

 

from pathlib import Path

import newrelic.agent

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
    newrelic.agent.initialize("newrelic.ini", environment="local")
    newrelic.agent.register_application()

....

 

 

이렇게 설정하고 프로그램을 시작하면 환경별로 다른 App이 생성되어 분리해서 볼 수 있고 configuration을 롼경 별로 분리할 수 있습니다. 간단하게 python에서 newrelic을 셋업하고 환경 별로 분리하는 방법을 정리해 봤습니다. 

다음에 기회가 되면 function레벨로 tracing할 수 있는 custom instrumentation 기능을 소개하도록 하겠습니다.

반응형