---
slug: "django-docker-chane-image-alpine-uwsgi-to-debian-daphne"
title: "Django の Docker 環境を Alpine + uWSGI から Debian + Daphne に変えた → やっぱり uvicorn"
description: "Django の Docker イメージを Alpine + uWSGI 構成から Debian + Daphne (ASGI) 構成に切り替える理由と、Dockerfile / Kubernetes マニフェストの書き換え例。"
url: "https://www.ytyng.com/blog/django-docker-chane-image-alpine-uwsgi-to-debian-daphne"
publish_date: "2022-09-03T11:03:29Z"
created: "2022-09-03T11:03:29Z"
updated: "2026-05-11T13:21:29.421Z"
categories: ["Django", "Docker", "Python"]
keywords: ""
featured_image_url: "https://media.ytyng.com/resize/20230812/d6989469f6624bb59f440de7a9fc82a4.png.webp?width=768"
has_video: false
has_music: false
video_urls: []
music_urls: []
lang: "ja"
---

# Django の Docker 環境を Alpine + uWSGI から Debian + Daphne に変えた → やっぱり uvicorn

<p>今まで、Alpine Linux + uWSGI で Django のイメージを作ることが多かったのですが、<br />Alpine Linux で Python を実行すると遅い問題があります。</p>
<ul>
<li><a href="https://applingo.tokyo/article/6860" target="_blank">DockerでPythonを使用する場合はベースイメージにAlpine Linuxを使わない方が良い理由</a></li>
<li><a href="https://qiita.com/kawamou/items/8c3c13c40929dbaaaae3" target="_blank">PythonをAlpineイメージで安易に動かす問題点 - Qiita</a></li>
</ul>
<p>また、その Alpine 上で起動する HTTPサーバには、 uWSGI を使っていたのですが、設定が複雑で、Kubernetes でサービスするには冗長な気になっいました。</p>
<p>そのため、Django サービスの環境を一通り変更しました。</p>
<p>ベースイメージは、Alpine から <strong>Debian</strong> ベースの Python に変更し、HTTP サーバは <strong>Daphne</strong> を使うことにしました。</p>
<p>追記: Daphne だと並列リクエストが受けられないため、後日 <strong>Uvicorn</strong> に変えました</p>
<h1>Docker イメージの変更</h1>
<h3>Alpine &rarr; Python (Debian) に変更</h3>
<p>マルチステージビルドにし、Python のフルイメージで pipenv install ( pipenv sync ) を行い、成果物を python のスリムイメージを元とした公開用ステージにコピーする形としました。</p>
<p>今まで Alpine にソースコードや依存関係、uWSGI を含めて入れてビルドした場合、私のアプリでは 277MB。<br />python:3.10-bullseye + python:3.10-slim-bullseye のマルチステージビルドした場合、最終イメージは 313MB。<br />容量としては若干増えましたが、ほぼかわらないサイズでした。</p>
<p>Dockerfile は下に書きます。</p>
<h1>HTTPサーバの変更</h1>
<h3>uWSGI &rarr; Daphne に変更</h3>
<p>uWSGI は非常に良いライブラリだと思いますが、チューニング項目が多く、使っていく上で少し疲れます。</p>
<p>uWSGI は特性上、レスポンスを繰り返すごとにメモリが増えていく傾向があり、それを防ぐためにある一定回数のリクエストを受け取った時にワーカーを再起動させることができます(max-requests)。メモリリークと言いましたか? 違います。GCです。</p>
<p>ワーカーの再起動時、そのワーカーは一時的にサービス不能になりますが、他のワーカーが生きていれば全体的なサービス停止は防げます。しかし、一定リクエスト回数ごとに動作するため、近いタイミングで発生することが多く、結局サービスが停止してしまします。</p>
<p>再起動のリクエスト数閾値をワーカーごとにずらすオプション(max-requests-delta)があり、それを適用すればサービス停止は避けられるはず&hellip;なのですが、この設定は最新ビルドで使えません。何年も前にドキュメントに掲載されているのに、ずっと使えてなかった（効いてるものと思っていた) オプションです。</p>
<ul>
<li><a href="https://qiita.com/mushoku_toumei/items/6bda4e3e077218fbd8d8" target="_blank">あなたの使ってるuWSGI、本当にmax-requests-delta効いてますか？ - Qiita</a></li>
<li><a href="https://qiita.com/__m/items/7dcd44e4aeed0b66d4a1">uWSGI の max-requets-delta、マジで効いていない - Qiita</a></li>
</ul>
<p>そのため、これを期に別のアプリケーションサーバに変えることにしました。</p>
<p>候補としては、定番の guinicorn の他に、FastAPI で使われる Uvicorn, Hypercorn, あとは Django チームが開発している Daphne というものがあり、今回は Daphne にしました。</p>
<p>Daphne, Uvicorn, Hypercorn, は、ともに ASGI をサポートしており、Django 3 以降の主流のサーバとなっています。</p>
<p>今回は、 Django を使うため、Django チームが開発している Daphne を採用しました。</p>
<ul>
<li><a href="https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/daphne/" target="_blank">How to use Django with Daphne | Django documentation | Django<br /></a></li>
</ul>
<h3>追記: Daphne &rarr; Uvicorn に変えました</h3>
<p>Daphne は、連続のリクエストが来た時に順番にコルーチンで処理します。すべて async の View であれば問題無いと思うのですが、既存サービスではそうなってないため、並列リクエストが処理できない問題がありました。</p>
<p>シングル Pod で動かすアプリも多いため、応答があまり良くなかったので、複数ワーカーの起動が簡単な Uvicorn に変えました。</p>
<h1>静的コンテンツ配信</h1>
<p>uWSGI から Daphne に変更するにあたり、静的コンテンツをどうやってサービスするかの問題があります。</p>
<p>実際に多くのお客さんが使う大規模なサービスでは、静的コンテンツは CloudFront + S3 のような構成でサービスすることが多いと思います。その場合は問題にはなりませんが、社内ツールや管理サイトのような小さなサービスは、よりシンプルな形での静的ファイルサービスをしたい所です。</p>
<p>uWSGI は、static-map という静的ファイルを簡易的にサービスする機能があり、社内ツール等に使う分にはぴったりでいつも使っていました。ただし、 Daphne には静的ファイルのサービスはありません。</p>
<p>Daphne の前に Nginx で受け、Nginx 内で静的コンテンツと Django リクエストを振り分けるのも良いと思いますが、デーモンをあまり増やしたくはなかったため、別の方法を探しました。</p>
<p>比較的最近人気のあるソリューションと思われるのは、WhiteNoise というアプリケーションです。</p>
<ul>
<li><a href="https://whitenoise.evans.io/en/stable/" target="_blank">WhiteNoise documentation</a></li>
</ul>
<p>WhiteNoise は、Python で書かれた静的コンテンツサーバで、Django の MiddleWare に差し込んで簡単に使うことができます。</p>
<p>Python で静的コンテンツサーバを実行するなんてナンセンスじゃないか、という気がしなくもないですが、その回答は公式ドキュメントにあります。</p>
<p><a href="https://whitenoise.evans.io/en/stable/#infrequently-asked-questions" target="_blank">https://whitenoise.evans.io/en/stable/#infrequently-asked-questions</a></p>
<p>S3 でも Nginx でもない選択肢として、まさに私のニーズとしてはぴったりでした。</p>
<h1>Dockerfile</h1>
<p>Dockerfile は以下のようになりました。</p>
<pre>FROM python:3.10-bullseye AS builder<br /><br /># Pipfileをコピー<br />COPY Pipfile /tmp/Pipfile<br />COPY Pipfile.lock /tmp/Pipfile.lock<br /><br /># pipenv sync<br /># プロジェクトによっては pipenv install --system --ignore-pipfile --deploy<br />RUN python3 -m pip install pipenv \<br />  &amp;&amp; PIPENV_PIPFILE=/tmp/Pipfile pipenv sync --system \<br />  &amp;&amp; python3 -m pip install uvicorn<br /># 前: &amp;&amp; python3 -m pip install daphne<br /><br /><br />FROM python:3.10-slim-bullseye<br /><br /># ビルダーステージから、MySQL クライアントに必要な SO をコピー<br />COPY --from=builder \<br />  /usr/lib/x86_64-linux-gnu/libmariadb.a \<br />  /usr/lib/x86_64-linux-gnu/libmariadb.so.3 \<br />  /usr/lib/x86_64-linux-gnu/<br />COPY --from=builder /usr/lib/x86_64-linux-gnu/libmariadb3/ \<br />  /usr/lib/x86_64-linux-gnu/libmariadb3/<br /># ビルダーステージから、Pipenv でインストールしたライブラリをコピー<br />COPY --from=builder /usr/local/lib/python3.10/site-packages \<br />  /usr/local/lib/python3.10/site-packages<br />COPY --from=builder /usr/local/lib/python3.10/lib-dynload \<br />  /usr/local/lib/python3.10/lib-dynload<br />COPY --from=builder /usr/local/bin /usr/local/bin<br /><br /># SOのシンボリックリンクを作っておく<br />RUN ln -s /usr/lib/x86_64-linux-gnu/libmariadb.a \<br />  /usr/lib/x86_64-linux-gnu/libmariadbclient.a \<br />  &amp;&amp; ln -s /usr/lib/x86_64-linux-gnu/libmariadb.so.3 \<br />  /usr/lib/x86_64-linux-gnu/libmariadb.so \<br />  &amp;&amp; ln -s /usr/lib/x86_64-linux-gnu/libmariadb.so.3 \<br />  /usr/lib/x86_64-linux-gnu/libmariadbclient.so<br /><br />COPY my_app /var/app/my_app<br />RUN chown -R nobody:nogroup /var/app<br /><br />USER nobody<br />WORKDIR /var/app/my_app<br />RUN cd /var/app/my_app &amp;&amp; python3 ./manage.py collectstatic --noinput<br />EXPOSE 8002<br />CMD ["uvicorn", \<br />  "my_app.asgi:application", \<br />  "--host", "0.0.0.0", \<br />  "--port", "8002", \<br />  "--workers", "4" \<br />]<br /><br /># 前: CMD ["daphne", "-b", "0.0.0.0", "-p", "8002", "my_app.asgi:application"]<br /><br /></pre>
<p></p>
<h2>Django で WhiteNoise を動かす</h2>
<p>Django の設定の MIDDLEWARE の中に、</p>
<pre>whitenoise.middleware.WhiteNoiseMiddleware</pre>
<p>を追加することで、静的ファイルがホストされます。</p>
<p><a href="https://whitenoise.evans.io/en/stable/django.html" target="_blank">Using WhiteNoise with Django - WhiteNoise 6.2.0 documentation</a></p>
<h3>キャッシュ時間の延長</h3>
<p>WhiteNoise のキャッシュヘッダーの寿命 ( max-age ) は、デフォルトで&nbsp;</p>
<pre>60 if not settings.DEBUG else 0</pre>
<p>となっています。</p>
<p></p>
<p><a href="http://whitenoise.evans.io/en/stable/django.html#WHITENOISE_MAX_AGE">http://whitenoise.evans.io/en/stable/django.html#WHITENOISE_MAX_AGE</a></p>
<p>60秒だと短いと思いますので、7日に変更します。</p>
<pre>WHITENOISE_MAX_AGE = 86400 * 7</pre>
<p>(本番用 settings の中で設定)</p>
<h3>メディアサーバ</h3>
<p>WhiteNoise は、Django の MEDIA_URL をサーブできるようにはなっていません。</p>
<p><a href="http://whitenoise.evans.io/en/stable/django.html#serving-media-files" target="_blank">http://whitenoise.evans.io/en/stable/django.html#serving-media-files</a></p>
<p>理由は上記ページに書いてある通りです。そのため、メディアを扱う場合は、<a href="https://django-storages.readthedocs.io/en/latest/" target="_blank">django-storages</a> などと連携し、S3 や nginx でサービスする仕組みを構築する必要があります。</p>
