<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sensemint_.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 13 Aug 2021 06:41:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sensemint_.log</title>
            <url>https://images.velog.io/images/sensemint_/profile/46aee408-60e2-4f03-8239-5c926c51eecd/EE5QkSsVAAAFgmh.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sensemint_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sensemint_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Elastic APM 을 이용한 FastAPI 모니터링]]></title>
            <link>https://velog.io/@sensemint_/Elastic-APM-%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-FastAPI-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@sensemint_/Elastic-APM-%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-FastAPI-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81</guid>
            <pubDate>Fri, 13 Aug 2021 06:41:55 GMT</pubDate>
            <description><![CDATA[<h1 id="elastic-apm">Elastic APM</h1>
<p>모니터링을 위하여 FastAPI에 Elastic APM 을 적용한 방법을 기록한 글입니다.</p>
<p><a href="https://saramin.github.io/2020-03-24-elastic-apm-2/">Elastic APM 적용 해보기 - #2. 장점 및 Newrelic 비교</a></p>
<p>위 사람인에서 적용한 글을 보시면 도움이 되실겁니다.</p>
<h2 id="docker-compose-설정">Docker-compose 설정</h2>
<pre><code class="language-dockerfile">version: &#39;3.8&#39;

services:

  apm-server:
    image: docker.elastic.co/apm/apm-server:7.14.0
    container_name: &quot;apm-server&quot;
    depends_on:
      elasticsearch:
        condition: service_healthy
      kibana:
        condition: service_healthy
    cap_add: [&quot;CHOWN&quot;, &quot;DAC_OVERRIDE&quot;, &quot;SETGID&quot;, &quot;SETUID&quot;]
    cap_drop: [&quot;ALL&quot;]
    ports:
    - 8200:8200
    command: &gt;
        apm-server -e
          -E apm-server.rum.enabled=true
          -E setup.kibana.host=kibana:5601
          -E setup.template.settings.index.number_of_replicas=0
          -E apm-server.kibana.enabled=true
          -E apm-server.kibana.host=kibana:5601
          -E output.elasticsearch.hosts=[&quot;elasticsearch:9200&quot;]
    healthcheck:
      interval: 10s
      retries: 12
      test: curl --write-out &#39;HTTP %{http_code}&#39; --fail --silent --output /dev/null http://localhost:8200/

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.14.0
    container_name: &quot;elasticsearch&quot;
    environment:
    - bootstrap.memory_lock=true
    - cluster.name=docker-cluster
    - cluster.routing.allocation.disk.threshold_enabled=false
    - discovery.type=single-node
    - ES_JAVA_OPTS=-XX:UseAVX=2 -Xms1g -Xmx1g
    ulimits:
      memlock:
        hard: -1
        soft: -1
    volumes:
    - esdata:/usr/share/elasticsearch/data
    ports:
    - 9200:9200
    healthcheck:
      interval: 20s
      retries: 10
      test: curl -s http://localhost:9200/_cluster/health | grep -vq &#39;&quot;status&quot;:&quot;red&quot;&#39;

  kibana:
    image: docker.elastic.co/kibana/kibana:7.14.0
    container_name: &quot;kibana&quot;
    depends_on:
      elasticsearch:
        condition: service_healthy
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
      ELASTICSEARCH_HOSTS: http://elasticsearch:9200
    ports:
    - 5601:5601
    healthcheck:
      interval: 10s
      retries: 20
      test: curl --write-out &#39;HTTP %{http_code}&#39; --fail --silent --output /dev/null http://localhost:5601/api/status

volumes:
  esdata:
    driver: local</code></pre>
<h2 id="python-module-설치">python module 설치</h2>
<pre><code>pip install elastic-apm</code></pre><h2 id="fastapi-설정">FastAPI 설정</h2>
<pre><code>💡 Elastic APM의 최적화를 위한 설정에 대한 설명은 아래 주소에서 확인하실 수 있습니다.
   성능에 영향을 미치는 주요 설정은 데이터 수집 주기라고 생각이 되는데
   TRANSACTION_MAX_SPANS, STACK_TRACE_LIMIT 이 둘의 수치를 조정하면 됩니다.(default 500)</code></pre><p><a href="https://medium.com/squad-engineering/how-to-optimize-elastic-apm-6f7f6d58bed5">https://medium.com/squad-engineering/how-to-optimize-elastic-apm-6f7f6d58bed5</a></p>
<pre><code class="language-python">from elasticapm.contrib.starlette import make_apm_client, ElasticAPM

# Elastic APM
# https://medium.com/squad-engineering/how-to-optimize-elastic-apm-6f7f6d58bed5
apm_config = {
    &quot;SERVICE_NAME&quot;: &quot;laplace&quot;,
    &quot;SERVER_URL&quot;: &quot;http://apm-server:8200&quot;,
    &quot;ENVIRONMENT&quot;: &quot;dev&quot;,
    &quot;GLOBAL_LABELS&quot;: &quot;platform=Platform, application=Application&quot;,
    &quot;TRANSACTION_MAX_SPANS&quot;: 250,
    &quot;STACK_TRACE_LIMIT&quot;: 250,
    &quot;TRANSACTION_SAMPLE_RATE&quot;: 0.5,
    &quot;APTURE_HEADERS&quot;: &quot;false&quot;
}

apm = make_apm_client(apm_config)
app.add_middleware(ElasticAPM, client=apm)</code></pre>
<p>Kibana 주소로 들어가신후 왼쪽 사이드바에서 Observability 메뉴의 APM항목을 가시면 아래 이미지 처럼 Services에 나타나게 됩니다.
<img src="https://images.velog.io/images/sensemint_/post/d0640a58-670b-4614-9871-6fb6749408e5/apm.png" alt=""></p>
<p><img src="https://images.velog.io/images/sensemint_/post/d7b5f971-658d-4787-951b-5a439872122c/elastic%20APM.png" alt=""></p>
<h2 id="참고자료">참고자료</h2>
<p><a href="https://towardsdatascience.com/monitoring-flask-fastapi-python-applications-with-elastic-apm-33237a39d7b6">https://towardsdatascience.com/monitoring-flask-fastapi-python-applications-with-elastic-apm-33237a39d7b6</a></p>
<p><a href="https://www.elastic.co/guide/en/apm/get-started/current/quick-start-overview.html">https://www.elastic.co/guide/en/apm/get-started/current/quick-start-overview.html</a></p>
<p><a href="https://www.elastic.co/guide/en/apm/agent/python/master/starlette-support.html">https://www.elastic.co/guide/en/apm/agent/python/master/starlette-support.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Model Service를 위한 Celery 구성 및 모니터링(Flower + Prometheus + Grafana)]]></title>
            <link>https://velog.io/@sensemint_/1</link>
            <guid>https://velog.io/@sensemint_/1</guid>
            <pubDate>Thu, 12 Aug 2021 05:21:13 GMT</pubDate>
            <description><![CDATA[<p>추론 시간이 오래 걸리는 모델 서비스를 위한 Celery 구성을 기록한 글입니다.</p>
<p>더 좋은 방법이 있으면 알려주세요!</p>
<h2 id="docker-compose-설정">Docker-compose 설정</h2>
<pre><code class="language-Dockerfile">redis:
    image: redis
    container_name: redis
    restart: always

rabbitmq:
  image: rabbitmq
  container_name: rabbitmq
  restart: always

worker:
    build: .
    container_name: worker
    restart: always
    command: &quot;celery -A app.worker.celery_worker worker --without-heartbeat --without-mingle -P solo -l info -E --concurrency=1 -Ofair&quot;
    depends_on:
      - redis
      - rabbitmq

prometheus:
    image: prom/prometheus
    container_name: prometheus
    restart: always
    volumes:
      - ./prometheus/:/etc/prometheus/
    command:
      - &#39;--config.file=/etc/prometheus/prometheus.yml&#39;
    ports:
      - 9090:9090

  flower:  
    image: mher/flower
    container_name: flower
    environment:
      - CELERY_BROKER_URL=amqp://guest@rabbitmq:5672//
      - FLOWER_PORT=7777
    ports:  
      - 7777:7777

  grafana:
    image: grafana/grafana
    container_name: grafana
    restart: always
    depends_on:
      - prometheus
    ports:
      - &quot;3000:3000&quot;</code></pre>
<ul>
<li><span style="color:red">app.worker.celery_worker</span>: worker로 실행할 코드가 있는 경로</li>
<li><span style="color:red">--without-heartbeat --without-mingle</span>: diagnostic and redundant heartbeat messages 보내지 않음</li>
<li><span style="color:red">-P solo</span>: 서비스 특성에 맞게 설정 여기선 single모드인 solo로 설정</li>
<li><span style="color:red">-E</span>: prometheus로 monitoring을 위한 event 설정</li>
<li><span style="color:red">--concurrency=1</span>: 서비스 특성에 맞게 설정</li>
<li><span style="color:red">-Ofair</span>: <a href="https://daeguowl.tistory.com/157">https://daeguowl.tistory.com/157</a></li>
</ul>
<h2 id="celery-설정">Celery 설정</h2>
<pre><code class="language-python">from celery import Celery

app = Celery(&quot;worker&quot;, backend=&quot;redis://redis:6379&quot;, broker=&quot;amqp://guest@rabbitmq:5672//&quot;)

# https://ichi.pro/ko/dib-leoning-algolijeum-eul-seobiseulo-silhaeng-117261469126622
app.conf.update(
    broker_pool_limit=None,
    task_acks_late=True,
    broker_heartbeat=None,
    worker_prefetch_multiplier=1,
    task_track_started=True,
    worker_send_task_events=True,
    task_send_sent_event=True,
    result_expires=86400,  # one day,
    task_serializer=&quot;json&quot;,
    result_serializer=&quot;json&quot;,
    accept_content=[&quot;json&quot;],
    timezone=&quot;Asia/Seoul&quot;,
    enable_utc=False
)</code></pre>
<ul>
<li><span style="color:green">broker_pool_limit</span>: connection pool에서 열 수 있는 최대 connection수입니다. None으로 설정하면 connection pool이 비활성화되고 connection이 설정되고 사용할 때마다 닫힙니다.</li>
<li><span style="color:green">task_acks_late</span>: 기본적으로 작업을 수행하면 작업이 실행되기 직전에  &quot;acked&quot; 되지만 오랜시간이 걸리는 딥러닝 작업의 경우 계산 된 후에만 &quot;acked&quot; 되기 위한 설정</li>
<li><span style="color:green">broker_heartbeat</span>: hearbeat 모니터링을 할지 안할지 결정한다. 작업이 오래걸리므로 None 설정</li>
<li><span style="color:green">worker_prefetch_multiplier</span>: worker가 받을 메세지 수량을 결정한다. 1로 설정하여 한번에 하나의 메시지만 받도록 설정</li>
<li><span style="color:green">task_track_started</span>: True 설정시 작업의 상태를 알 수 있다.</li>
<li><span style="color:green">worker_send_task_events, task_send_sent_event</span>: prometheus로 모니터링을 하기 위한 설정</li>
<li><span style="color:green">result_expires</span>: backend에 데이터가 남아있는 기간을 설정</li>
</ul>
<h2 id="celery-실행">Celery 실행</h2>
<pre><code class="language-python">from celery import uuid

def celery_start():
  task_id = uuid()
  celery_test.apply_async(...),task_id=task_id)

  return task_id</code></pre>
<p>apply_async()를 이용하여 uuid로 생성한 task_id를 이용하여 실행된 함수의 상태가 backend로 지정한 redis에 저장되며  우리는 task_id를 이용하여 task info를알수있다.</p>
<h2 id="celery-실행-함수">Celery 실행 함수</h2>
<pre><code class="language-python">from celery.contrib.abortable import AbortableTask

@app.task(bind=True, max_retries=0, base=AbortableTask)
def celery_test():
    ...</code></pre>
<ul>
<li><span style="color:green">bind</span>: The bind argument means that the function will be a “bound method” so that you can access attributes and methods on the task type instance.</li>
<li><span style="color:green">max_retries</span>: max_retries 설정된 수 만큼  작업을 재시도 한다. 0으로 설정하여 재시도를 안하도록함</li>
<li><span style="color:green">base</span>: AbortableTask 를 설정하여 celery가 돌아가는 중간에 중단시킬 수 있도록한다.</li>
</ul>
<h2 id="celery-상태-확인">Celery 상태 확인</h2>
<pre><code class="language-python">from celery.result import AsyncResult

def celery_state(task_id):
    task = AsyncResult(id=task_id, app=app)

    return {&#39;state&#39;:task.state, &#39;info&#39;=task.info}
</code></pre>
<p>task_id를 이용하여 celery의 상태값인 STARTED, SUCCESS, PENDING, REVOKED 같은 정보를 알 수 있다. SUCCESS상태면 추론 완료, REVOKED 상태면 추론 중단 등 활용이 가능하다.</p>
<h2 id="celery-실행-중단">Celery 실행 중단</h2>
<pre><code class="language-python">from celery.contrib.abortable import AbortableAsyncResult
from celery.result import AsyncResult

def celery_stop(task_id):
  try:
    AbortableAsyncResult(task_id).abort()
    AsyncResult(id=task_id).revoke(terminate=True, signal=&quot;SIGKILL&quot;)
    log.info(&quot;task kill&quot;)
    return Response(&quot;success task kill&quot;)
  except Exception:
    return Response(&quot;fail task kill&quot;)</code></pre>
<p>사용자가 추론시간이 긴 작업을 수행중 중단을 할 경우 celery는 계속 돌아가고 있기 떄문에 task를 중단 해줘야함</p>
<h2 id="celery-모니터링">Celery 모니터링</h2>
<h2 id="celery---flower---prometheus---grafana">Celery -&gt; Flower -&gt; Prometheus -&gt; Grafana</h2>
<p><code>💡 Flower는 따로 설정 안하셔도 됩니다.</code></p>
<h2 id="prometheus-설정">prometheus 설정</h2>
<pre><code class="language-yaml"># prometheus.yml

global:
  scrape_interval:     15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: flower
    static_configs:
      - targets: [&#39;flower:5555&#39;]</code></pre>
<p><a href="http://localhost:9090">localhost:9090</a> 에서 flower가 검색되는지 확인합니다. flower가 검색이 안되면 각 설정의 URL들을 다시 확인합니다.
<img src="https://images.velog.io/images/sensemint_/post/e72e3d84-595f-4657-b90b-54a38cc13e4e/Untitled.png" alt=""></p>
<h2 id="grafana-설정">Grafana 설정</h2>
<p><a href="https://flower.readthedocs.io/en/latest/prometheus-integration.html">Prometheus Integration - Flower 1.0.1 documentation</a></p>
<pre><code>💡 celery-monitoring-grafana-dashboard.json 파일은 밑 주소에서 확인할 수 있습니다.</code></pre><p><a href="https://github.com/mher/flower/blob/master/examples/celery-monitoring-grafana-dashboard.json">https://github.com/mher/flower/blob/master/examples/celery-monitoring-grafana-dashboard.json</a></p>
<p><img src="https://images.velog.io/images/sensemint_/post/295d0768-a09a-464f-aba8-a24c071ac034/Untitled%201.png" alt="">
위 Grafana 설정대로 따라하셨으면 dashboard결과를 확인할 수 있습니다.</p>
<h2 id="참고자료">참고자료</h2>
<p><a href="https://ichi.pro/ko/dib-leoning-algolijeum-eul-seobiseulo-silhaeng-117261469126622">딥 러닝 알고리즘을 서비스로 실행</a></p>
<p><a href="https://flower.readthedocs.io/en/latest/prometheus-integration.html">https://flower.readthedocs.io/en/latest/prometheus-integration.html</a></p>
<p><a href="https://www.cloudamqp.com/docs/celery.html">https://www.cloudamqp.com/docs/celery.html</a></p>
<p><a href="https://daeguowl.tistory.com/157">https://daeguowl.tistory.com/157</a></p>
<p><a href="https://newbiestory.tistory.com/61">https://newbiestory.tistory.com/61</a></p>
<p><a href="https://kyoungmookryu.medium.com/python-celery-d347ac155720">https://kyoungmookryu.medium.com/python-celery-d347ac155720</a></p>
]]></description>
        </item>
    </channel>
</rss>