Upgrade to Pro — share decks privately, control downloads, hide ads and more …

애플리케이션의 성능을 자동으로 계측하는 APM Python Agent 개발기

kakao
November 01, 2024

애플리케이션의 성능을 자동으로 계측하는 APM Python Agent 개발기

#monitoring #apm

애플리케이션 성능 모니터링과 애플리케이션 성능 측정 코드 추가에 필요한 주요 개념을 설명합니다.
더불어 Python 애플리케이션의 성능을 자동으로 계측하는 Python Agent 개발 과정에서 마주한 도전 과제들을 공유하며, Agent의 아키텍처에 대해 소개합니다.

발표자 : allen.k1m
카카오의 모니터링 시스템 MATRIX에서 APM, TRACING 라인업을 개발하고 있습니다.
OpenTelemetry, Clymene 오픈소스의 Contributor로 활동하고 있습니다.

kakao

November 01, 2024
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1. "1.੄೙ਃࢿ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ existing_user

    = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists")
  2. "1.੄೙ਃࢿ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ start_time

    = datetime.now() existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists")
  3. "1.੄೙ਃࢿ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ start_time

    = datetime.now() existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists") end_time = datetime.now()
  4. "1.੄೙ਃࢿ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ start_time

    = datetime.now() existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists") end_time = datetime.now() print(“create_user duration : ", (end_time - start_time).total_seconds())
  5. "1.੄೙ਃࢿ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ start_time

    = datetime.now() existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists") end_time = datetime.now() print(“create_user duration : ", (end_time - start_time).total_seconds()) print(“create_user params : ", user_create)
  6. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ existing_user

    = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists")
  7. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ try:

    trace_context = TraceContext() trace_context.trace_id = 1 trace_context.start_time = datetime.now() existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists”)
  8. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ try:

    trace_context = TraceContext() trace_context.trace_id = 1 trace_context.start_time = datetime.now() threading.local().current_context = trace_context existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists”)
  9. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ try:

    trace_context = TraceContext() trace_context.trace_id = 1 trace_context.start_time = datetime.now() threading.local().current_context = trace_context existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists”)
  10. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def get_user_by_email(db, email) -> User: # ੉ݫੌ੉ ઓ੤ೞח૑ ഛੋೞח

    ೣࣻ db_start_time = datetime.now() trace_context = threading.local().current_context user = db.query(User).filter(User.email == email).first() return user
  11. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def get_user_by_email(db, email) -> User: # ੉ݫੌ੉ ઓ੤ೞח૑ ഛੋೞח

    ೣࣻ db_start_time = datetime.now() trace_context = threading.local().current_context user = db.query(User).filter(User.email == email).first() trace_context.sql_count += 1 trace_context.sql_query = “SELECT * FROM users WHERE email = %s” db_end_time = datetime.now() trace_context.sql_time += (db_end_time - db_start_time).total_seconds() return user
  12. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ try:

    trace_context = TraceContext() trace_context.trace_id = 1 trace_context.start_time = datetime.now() threading.local().current_context = trace_context existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists”)
  13. "1.ؘ੉ఠࣻ૘ӝࠄݫழפ્ def create_user(db, user_create): # ࢎਊ੗ ҅੿ਸ ࢤࢿೞח ೣࣻ try:

    trace_context = TraceContext() trace_context.trace_id = 1 trace_context.start_time = datetime.now() threading.local().current_context = trace_context existing_user = get_user_by_email(db, user_create.email) if existing_user: raise HTTPException("User already exists”) finally: trace_context.end_time = datetime.now() send_transaction(trace_context)
  14. .POLFZ1BUDI .POLFZ1BUDIJOH਷۠ఋ੐઺ীӝઓ௿ېझա ݽٕ੄ز੘ਸز੸ਵ۽ࣻ੿ೞחӝߨ origin_print = print def custom_print(*args, **kwargs): origin_print(“Custom

    Function Start”) origin_print(*args, **kwargs) origin_print(“Custom Function End”) print = custom_print print(“Hello, World!”)
  15. .POLFZ1BUDI .POLFZ1BUDIJOH਷۠ఋ੐઺ীӝઓ௿ېझա ݽٕ੄ز੘ਸز੸ਵ۽ࣻ੿ೞחӝߨ import requests origin_get = requests.get def patched_get(*args,

    **kwargs): print(“Request start”) response = origin_get(*args, **kwargs) print(“Request finished”) return response
  16. .POLFZ1BUDI .POLFZ1BUDIJOH਷۠ఋ੐઺ীӝઓ௿ېझա ݽٕ੄ز੘ਸز੸ਵ۽ࣻ੿ೞחӝߨ import requests origin_get = requests.get def patched_get(*args,

    **kwargs): print(“Request start”) response = origin_get(*args, **kwargs) print(“Request finished”) return response requests.get = patched_get
  17. TJUFDVTUPNJ[FQZ TJUFDVTUPNJ[Fח1ZUIPOীࢲ੗زਵ۽۽٘ؼࣻ੓חݽٕ઺ೞաੑפ׮ # matrix-agent ੸ਊ ߑߨ $ matrix-agent ——apm-wsgi-enabled True

    unicorn -b 0.0.0.0:5000 flask_pg:app .___ ___. ___ .___________..______ __ ___ ___ | \/ | / \ | || _ \ | | \ \ / / | \ / | / ^ \ `---| |----`| |_) | | | \ V / | |\/| | / /_\ \ | | | / | | > < | | | | / _____ \ | | | |\ \--.| | / . \ |__| |__| /__/ \__\ |__| | _| `.___||__| /__/ \__\ Agent ver : 1.0.0 * Serving Flask app 'flask_mysql'
  18. TJUFDVTUPNJ[FQZ TJUFDVTUPNJ[Fח1ZUIPOীࢲ੗زਵ۽۽٘ؼࣻ੓חݽٕ઺ೞաੑפ׮ # matrix-agent ੸ਊ ߑߨ $ matrix-agent ——apm-wsgi-enabled True

    unicorn -b 0.0.0.0:5000 flask_pg:app .___ ___. ___ .___________..______ __ ___ ___ | \/ | / \ | || _ \ | | \ \ / / | \ / | / ^ \ `---| |----`| |_) | | | \ V / | |\/| | / /_\ \ | | | / | | > < | | | | / _____ \ | | | |\ \--.| | / . \ |__| |__| /__/ \__\ |__| | _| `.___||__| /__/ \__\ Agent ver : 1.0.0 * Serving Flask app 'flask_mysql'
  19. TJUFDVTUPNJ[FQZ TJUFDVTUPNJ[Fח1ZUIPOীࢲ੗زਵ۽۽٘ؼࣻ੓חݽٕ઺ೞաੑפ׮ # matrix-agent झ௼݀౟о प೯ೞח ೣࣻ def run() ->

    None: python_path = environ.get("PYTHONPATH") cwd_path = getcwd() if cwd_path not in python_path: python_path.insert(0, cwd_path) filedir_path = dirname(abspath(__file__)) # sitecustomize.py ҃۽৬ э਺ python_path = [path for path in python_path if path != filedir_path] python_path.insert(0, filedir_path) environ["PYTHONPATH"] = pathsep.join(python_path) executable = which(args.command) execl(executable, executable, *args.command_args)
  20. TJUFDVTUPNJ[FQZ TJUFDVTUPNJ[Fח1ZUIPOীࢲ੗زਵ۽۽٘ؼࣻ੓חݽٕ઺ೞաੑפ׮ # matrix-agent झ௼݀౟о प೯ೞח ೣࣻ def run() ->

    None: python_path = environ.get("PYTHONPATH") cwd_path = getcwd() if cwd_path not in python_path: python_path.insert(0, cwd_path) filedir_path = dirname(abspath(__file__)) # sitecustomize.py ҃۽৬ э਺ python_path = [path for path in python_path if path != filedir_path] python_path.insert(0, filedir_path) environ["PYTHONPATH"] = pathsep.join(python_path) executable = which(args.command) execl(executable, executable, *args.command_args)
  21. 84(*XPSLFS೐۽ࣁझ"1.ؘ੉ఠࣻ૘੉উظਃ 8PSLFS 'MBTLXTHJ@BQQ  8PSLFS 'MBTLXTHJ@BQQ  8PSLFS 'MBTLXTHJ@BQQ 

    GPSL /P5ISFBE /P5ISFBE /P5ISFBE 84(*4FSWFS (VOJDPSOV84(* 5$1$POOFDUJPO %PXOTUSFBN .FUSJD.BOBHFS
  22. 84(*XPSLFS೐۽ࣁझ"1.ؘ੉ఠࣻ૘੉উظਃ 84(*4FSWFS (VOJDPSOV84(* 8PSLFS 'MBTLXTHJ@BQQ  8PSLFS 'MBTLXTHJ@BQQ  8PSLFS

    'MBTLXTHJ@BQQ  6%1 5$1$POOFDUJPO %PXOTUSFBN .FUSJD.BOBHFS /P5ISFBE /P5ISFBE /P5ISFBE 6%14FSWFS
  23. 'BTU"1*ܳࢎਊೞݶ"1.ؘ੉ఠࣻ૘੉উظਃ @app.get(“/send_request/“) async def send_request(): # ࠺زӝ Requestsܳ ੹࣠ೞח API

    trace_context = TraceContext() trace_context.trace_id = 1 trace_context.start_time = datetime.now() threading.local().current_context = trace_context async with https.AsyncClient() as client: trace_context = threading.local().current_context start_time = datetime.now() response = await client.get(“http://localhost:8000/hello/") trace_context.apicall_duration = (datetime.now() - start_time).total_se trace_context.end_time = date time.now()
  24. 'BTU"1*ܳࢎਊೞݶ"1.ؘ੉ఠࣻ૘੉উظਃ class ContextVarsRuntimeContext(_RuntimeContext): _CONTEXT_KEY = “current_context” def __init__(self) -> None:

    self._current_context = ContextVar( self._CONTEXT_KEY, default=TraceContext() ) def attach(self, context: TraceContext) -> object: return self._current_context.set(context) def get_current(self) -> TraceContext: return self._current_context.get()
  25. 1ZUIPOী੉੹౟۽٘ݗ   "1."HFOU.71 "MQIB7FS  'SPOU - FOEোز #FUB7FS

     ௿۽ૉ߬ఋद੘ ܾܻझ   "1.ӝמҊبച ܾܻझ  য়೑߬ఋद੘ SDܾܻझ  ੿धߡ੹ ܾܻझ 'BTU"1*୶о ܾܻझ
  26. 1ZUIPOী੉੹౟۽٘ݗ   "1."HFOU.71 "MQIB7FS  'SPOU - FOEোز #FUB7FS

     ௿۽ૉ߬ఋद੘ ܾܻझ  1ZUIPO_  'MBTL %KBOHP 'BTU"1*  3RVFTUT  .ZTRM 1Z.Z42-  QTZDPQH TRMJUF   "1.ӝמҊبച ܾܻझ  য়೑߬ఋद੘ SDܾܻझ  ੿धߡ੹ ܾܻझ 'BTU"1*୶о ܾܻझ
  27. 1ZUIPOী੉੹౟۽٘ݗ పझ౟੗زച दझమѐߊ ૑ਗݽٕ୶о ؘ௏ۨ੉ఠܳ੉ਊೠ "HFOU4%,ѐߊ  ޷੿  

    "1."HFOU.71 "MQIB7FS  'SPOU - FOEোز #FUB7FS  ௿۽ૉ߬ఋद੘ ܾܻझ   "1.ӝמҊبച ܾܻझ  য়೑߬ఋद੘ SDܾܻझ  ੿धߡ੹ ܾܻझ 'BTU"1*୶о ܾܻझ
  28. 2"