Djangoで静的ファイルとうまくやる

tell-k
DjangoCongress JP 2019 (2019.05.18)

おまえ誰よ?

https://pbs.twimg.com/profile_images/1045138776224231425/3GD8eWeG_200x200.jpg

BePROUD - Pythonメインの受託開発

connpass - エンジニアをつなぐIT勉強会支援プラットフォーム

PyQ - Pythonオンライン学習サービス

https://dl.dropboxusercontent.com/spa/ghyn87yb4ejn5yy/e9121e88c3b64179993a02198a7514f9.png

https://pyq.jp/ ★ Djangoを使ったWeb開発も学習できます! ★

目的/動機

対象

今日の目標

「わかるっ!俺には静的ファイルがわかる!!!」

http://4.bp.blogspot.com/-zTvzECyWEsk/VwIjHWMdszI/AAAAAAAA5e4/W_kAnVythXoHGzGO3AkgrHImS3cpvMiuQ/s800/internet_kanki_man1.png

前提

目次

静的ファイルとは

静的ファイルとは

静的ファイルとは

静的ファイルとは

静的Webサーバ、またはスタックは、コンピューター (ハードウェア) と HTTP サーバ (ソフトウェア) から構成されます サーバが保持しているファイルをブラウザーへ「そのまま」送るので、「静的」と呼ばれます

via Web サーバとは - Web 開発を学ぶ | MDN

静的Webサーバ

リクエストされたサーバー内のファイルをそのままHTTPレスポンスとして返すだけ

_images/static-server.png

動的コンテンツ

動的Webサーバは、静的Webサーバに加えて追加のソフトウェア、 一般的にはアプリケーションサーバおよびデータベースで構成されます アプリケーションサーバが、 HTTP サーバを通してブラウザーに送信する前に、保持しているファイルを更新するので「動的」と呼ばれます


「動的」はサーバがコンテンツを処理したり、データベースからその場で作成したりすることを意味します この方法はより柔軟性を提供できますが、 技術スタックがより扱いにくくなり、Webサイトを構築することは劇的に複雑になります

via Web サーバとは - Web 開発を学ぶ | MDN

動的Webサーバー

いわゆる Webアプリーケション

_images/dynamic-server.png

Django は?

ここまでのまとめ

呼び方の話

dummy

私が失敗した話

私が失敗した話

失敗1. CSSが全く当たってない…

nginx で静的ファイルを配信すればいいいのか!

失敗2. まだ Django Admin が真っ白

例) djangoのadminアプリの中に静的ファイルがある

/site-packages/django/contrib/admin/static/
└── admin
    ├── css
    ├── fonts
    ├── img
    └── js

全然「ヨシ!」 ではない

https://4.bp.blogspot.com/-EBpxVigkCCY/V5Xc1CHSeEI/AAAAAAAA8u0/9XIAzDJaQNU3HIiXi4PCPK3aMip3aoGyACLcB/s400/pose_sugoi_okoru_man.png

Djangoの静的ファイルの扱い

Djangoの静的ファイルの扱い

Djangoの静的ファイルの扱い

開発モードで静的ファイルを配信

なぜ?

あくまで開発時の補助用

上で述べたたように django.contrib.staticfiles を利用する場合、
DEBUG が True であれば runserver は自動的にこの処理を行います

〜略〜

この機能は本番環境で利用するのに適していません!
一般的なデプロイ方法に関しては 静的ファイルのデプロイ を参照ください

via 静的ファイル (画像、JavaScript、CSS など) を管理する > 開発時の静的ファイルの取扱い

失敗1の原因

言われてみれば当たり前だけど、当時は良くわかってなかった

DEBUG = True のまま本番公開してはダメ

DEBUG = True のまま
本番公開してはダメ

何がまずいの?

それ以外にも

via. https://docs.djangoproject.com/ja/2.2/ref/settings/#debug

center2

Q.Django が静的ファイルを配信しないなら、何が配信するの?

center3

A.静的ファイルを配信するHTTPサーバーを用意します

DEBUG = Falseの時の静的ファイル配信方法

# settings.py  ---

# 静的ファイルの探索はデフォルトでは アプリの staticディレクトリ以下
# ファイルを探したりするのが面倒なので、プロジェクトの直下に
# staticディレクトリをおきます

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, '..', 'static'),
)

DEBUG = Falseの時の静的ファイル配信方法

myproject
├── .gitignore
├── apps
│   ├── core
│   ├── manage.py
│   ├── polls
│   ├── static  # <- デフォはここから探すが、今回はここに置かない
│   └── apps
├── static       # <- 全部ここに置く
│   ├── css
│   ├── images
│   └── js
└── templates
    ├── base.html
    └── polls

HTTPサーバー で static 以下 を静的に配信

server {

    server_name example.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    # http://example.com/static 以下は全てファイルを静的にそのまま返す
    location /static/ {
        root /path/to/myproject;
    }

    # それ以外はDjangoアプリにプロキシする
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/uwsgi/myproject.sock;
    }
}

HTTPサーバーが静的/動的を振り分ける

URLパスによって、静的ファイルを配信するか、Djangoが受けるかを振り分けます

_images/http-server-1.png

静的ファイルを一つの場所に集める

静的ファイルを一つの場所に集める

静的ファイルを一つの場所に集める

server {

    server_name example.com;

    location = /favicon.ico { access_log off; log_not_found off; }

    # /static/admin だけは site-package以下から配信する <- 流石に辛い
    location /static/admin/ {
        root /path/to/venv/lib/python3.7/site-packages/django/contrib/admin/;
    }

    location /static/ {
        root /path/to/myproject;
    }

    # 〜 省略 〜

collectstatic で集める

# settings.py ---

STATIC_ROOT = os.path.join(BASE_DIR, '..', 'collected_static')

# 追加の静的ファイル探索パス
STATICFILES_DIRS =  (
   os.path.join(BASE_DIR, '..', 'static'),
)
$ python manage.py collectstatic

https://docs.djangoproject.com/ja/2.2/ref/contrib/staticfiles/#collectstatic

collectstatic で集める

site-packages/django/contrib/admin/static/admin  # ... ①

# ~ 省略 ~

├── apps
├── collected_static  # <- 全ての静的ファイルが集約される
│   ├── admin         # ... ①
│   ├── css           # ... ②
│   ├── images
│   └── js
├── static            # ... ② 設定で探索対象になっている
│   ├── css
│   ├── images
│   └── js
└── templates
# nginxの設定を変える

# ディレクトリ名が変わったので root -> alias に変更
location /static/ {
   alias /path/to/myproject/collcected_static;
}

結局、公開するときはどうしたらいい?

# 毎回集めるかどうかを聞かれずに、実行されます
$ python manage.py collectstatic --no-input

メディアファイル

メディアファイル

# settings.py ---
# 例) メディアファイル関連の設定例

MEDIA_URL = '/media/'  # メディアファイル配信URL
MEDIA_ROOT = os.path.join(BASE_DIR, '..', 'media') # メディアファイルの保存先

開発モードでメディアファイルを配信するには?

from django.urls import path
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # 省略
]

# 開発モードの時だけ、Djangoでメディアファイルを静的配信
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

メディアファイルのアップロード時の注意点

# 例) nginx で 10MBまで上限をあげる
client_max_body_size 10M;

http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size

本番環境で公開する

本番環境で公開する

本番環境で公開する

単一のサーバーが配信する

単一のサーバーが配信する

Nginxの設定例

server {

    server_name example.com;

    location /media/ { # <- メディアファイル
       root /path/to/myproject;
    }
    location /static/ { # <- 静的ファイル
       alias /path/to/myproject/collcected_static;
    }
    location / { # <- それ以外はDjangoアプリ
        include proxy_params;
        proxy_pass http://unix:/run/uwsgi/myproject.sock;
    }

# 〜 省略 〜

単一のサーバーが配信する

_images/http-server-2.png

複数サーバー構成

複数サーバー構成

デプロイする時は

静的ファイル

メディアファイル

どうやって同期するの?

複数サーバー構成

_images/multi-server.png

クラウドストレージを利用する

クラウドストレージを利用する

クラウドストレージを利用する

_images/cloud-multi-server.png

Django で どうやるの?

django-storages の使い方例

# settings.py --

AWS_ACCESS_KEY_ID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
AWS_SECRET_ACCESS_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
AWS_STORAGE_BUCKET_NAME = 'xxxxxxx'

# メディアファイルをS3に保存
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

# collectstatic を実行すると静的ファイルをS3に保存
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings

django-storages の使い方例

location ~ ^/(static/|media/) {
  resolver         10.0.0.2;
  resolver_timeout 5s;
  set $s3_bucket "xxxxxxx.s3.amazonaws.com";

  proxy_pass http://$s3_bucket;
}

Nginx -> S3 にプロキシする時の注意点

resolver         10.0.0.2;
resolver_timeout 5s;

Nginxでプロキシする必要があるの?

HTTPサーバーがない構成

HTTPサーバーがない構成

以下のチュートリアルは Heroku を想定したものです

どうすればいいか?

静的ファイル

メディアファイル

HTTPサーバーがない構成

_images/heroku-arch.png

WhiteNoise は大丈夫なの?

ちなみにGAEは?

runtime: python37

handlers:
- url: /static
  static_dir: static/  # <- 静的配信してくれる

- url: /.*
  script: auto

https://cloud.google.com/python/django/appengine?hl=ja

CDNを利用する

CDNを利用する

簡単に説明すると

代表的なサービスは

CDNを利用する(CloudFront)

_images/cloundfront-pattern.png

CDN は Heroku で 使うのも良いです。

その他Tips

その他Tips

Django Compressor

{% load compress %}

{% compress css %}
<link rel="stylesheet" href="/static/css/one.css" type="text/css" charset="utf-8">
<style type="text/css">p { border:5px solid green;}</style>
<link rel="stylesheet" href="/static/css/two.css" type="text/css" charset="utf-8">
{% endcompress %}

↓ 一つのCSSファイルに圧縮される

<link rel="stylesheet" href="/static/CACHE/css/f7c661b7a124.css" type="text/css" charset="utf-8">

認証付きの静的ファイル

認証付きの静的ファイル

_images/x-accell-redirect.png

参考

  • 10.4 静的ファイル関連の設定

  • 10.5 メディアファイル関連の設定

参考

Djangoのドキュメントも充実しています

参考

まとめ

下記のような話をいたしました。

静的ファイル配信というものについて何か参考になれば幸いです。

ご静聴ありがとうございました

ご静聴ありがとうございました

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)