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

Djangoアプリケーション運用のリアル〜問題発生から可視化、最適化への道〜 / django...

Djangoアプリケーション運用のリアル〜問題発生から可視化、最適化への道〜 / django-application-realities

https://djangocongress.jp/
#djangocongress

Kashun Yoshida

October 07, 2023
Tweet

More Decks by Kashun Yoshida

Other Decks in Programming

Transcript

  1. Who am I? / お前誰よ • 吉田花春 (Yoshida Kashun) •

    @kashew_nuts • BeProud Inc. • Software Developer • 開発環境整備とボルダリングが趣味
  2. 【理想】ログから判断できるようにする • 5W1Hがわかる:特にエラーログ ◦ 「いつ」「どこで」「だれが」「なぜ」「何が起きて」「何をす ればいいのか」 • (例) ◦ 「request_id:

    xxx 10:00 INFO 指示書の確定処理が完了」 ◦ 「request_id: yyy 11:00 WARNING 既存システムとの接続に 失敗。〇〇に連絡してください。」
  3. 【対策】ログから判断できるようにする 1. INFO、ERROR だけでなくログレベルを使い分ける 1.1. CRITICAL:システム全体や連携システムに影響する重大な問題発生時の情報 1.2. ERROR:プログラム上の処理が中断したり、停止した場合の情報 1.3. WARNING:プログラムの処理は続いているが何か良くないデータや通知の情報

    1.4. INFO:プログラムの状況やデータ数など後で挙動を把握しやすくする情報 1.5. DEBUG:ローカル環境で開発するときだけつかう情報 2. APIリクエストのstart/end時にログを出力 2.1. リクエストID, 日時, エンドポイント, ステータスコード 2.2. django-structlogのような構造化ロギングを可能にするライブラリを活用する 3. 5W1Hがわかるようにメッセージ内容を修正する
  4. django-structlogでのログ出力設定例 1. $ pip install django-structlog 2. getting_startedを例にsettings.pyに設定を追加 3. Pythonコードからログを出力する

    >>> import structlog >>> logger = structlog.get_logger(__name__) >>> logger.warning("既存システムとの接続に失敗。〇〇に連絡してください。 ") {"request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1,"ip": "0.0.0.0","level": "warning", "event": "既存システムとの接続に失敗。〇〇に連絡してください。 ", "timestamp": "2023-10-07T10:07:31.089925Z", "logger": "my_awesome_project.my_awesome_module"}
  5. 【対策】エラーがあったときすぐ検知できる • Sentry の導入 ◦ エラートラッキングサービスを活用 • ERRORやWARNINGがあったら Slackに通知がくる! ◦

    だれでも調査開始できる • ログ情報にとどまらず、様々な情報 がすぐわかる ◦ エラーが発生した環境 ◦ POSTされたデータや発行された SQL ◦ エラー発生頻度
  6. Sentryの導入例 1. $ pip install sentry-sdk 2. settings.pyに以下の内容を追記 import sentry_sdk

    sentry_sdk.init( dsn="<your DSN here>", traces_sample_rate=1.0 ) 3. Sentry側の設定を有効にした後、エラー発生時はSentryに通知 4. ORM用の設定する際はDjango integrationの設定を参考にする
  7. SentryでDjango integrationの設定 import sentry_sdk.utils sentry_sdk.utils.MAX_STRING_LENGTH = 2048 # monkey patch

    first import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.logging import LoggingIntegration # デフォルトでは ERROR 以上しかevent登録されないので、WARNING以上にする sentry_logging = LoggingIntegration(level=logging.INFO, event_level=logging.WARNING) sentry_sdk.init( dsn=SENTRY_DSN, integrations=[sentry_logging, DjangoIntegration()], # Associate users to errors (if using django.contrib.auth) send_default_pii=True, _experiments={'record_sql_params': True} )
  8. Sentryの導入例 1. $ pip install sentry-sdk 2. settings.pyに以下の内容を追記 import sentry_sdk

    sentry_sdk.init( dsn="<your DSN here>", traces_sample_rate=1.0 ) 3. エラー発生時にSentryに通知される
  9. Datadog導入例 1. $ pip install ddtrace 2. settings.pyのLOGGINGにDatadog用の記述追加(後述) 3. #

    ログ分析と統合したAPMを有効にしている例 4. $ DD_LOGS_INJECTION=1 \ ddtrace-run gunicorn myapp.wsgi:application 5. Datadogのエージェントを有効にしメトリクスとログを収集
  10. settings.py: python-json-loggerの設定例 LOGGING = { "formatters": { "json": { "class":

    "pythonjsonlogger.jsonlogger.JsonFormatter", "format": "%(pathname)s %(lineno)d %(asctime)s %(process)d %(thread)d %(levelname)s %(module)s %(message)s %(name)s %(dd.env)s %(dd.service)s %(dd.version)s %(dd.trace_id)s %(dd.span_id)s", }, }, "loggers": {"ddtrace": {"level": "WARNING"}}, # 以降略 }
  11. Tips: manage.pyコマンドもトレース # https://github.com/DataDog/dd-trace-py/issues/1341 # manage.py import sys import ddtrace

    name = sys.argv[1] if len(sys.argv) > 1 else None @ddtrace.tracer.wrap(name=name) def main(): # 略
  12. 実現できたこと • Sentry: エラートラッキング • Datadog: ログ分析やモニタリング、アラート • Aurora(MySQL): レーテンシー、メモリ低下、コネクション増加

    • ElastiCache(Redis):Auroraの項目+SwapUsage • ELB: 5xx response, unhealthy • OpenSearch(ElasticSearch): 死活監視 • Celeryのワーカー数/キュー数 • IPアドレス/UserAgent: アクセス数
  13. 可視化できるようになり楽になったこと • メンバー(nonエンジニア)が直接調査できる ◦ 開発者が開発に専念しやすい • ログやAPM、メトリクスなど各機能を横断して閲覧 • 各画面をパーマリンクで開けるのでチームへの共有が楽 •

    リソースの負荷が高まったときに何が原因かわかる ◦ 人数が多いイベントの通知のキュー数 ◦ 特定のIPアドレス/UserAgentからの攻撃の発生 ◦ 想定外に実行時間がかかっているSQLの発見