@Naoki-Design (直樹 高橋)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Django&nginx&Gunicornの本番環境で画像ファイルが表示されない

解決したいこと

  • Djangoでブログアプリを作っています。

  • アプリ作成およびデプロイは以下のサイトを参考にしました。
    https://medium.com/@kjmczk/blogsite-django-747046b453f9#4e27
    https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-20-04

  • デプロイ後、VPSサーバー上でアプリが動くことを確認しましたが、開発環境(127.0.0.1:8000)では表示されていたfaviconが表示されていません。

  • さらに、Djangoのsettings.pyのDebugモードをFalseに変更したところ、本番環境(Debugモード:True)では表示されていたページ内画像も表示されないエラーが発生します。

  • 色々なウェブサイトを拝見したところ、おそらくDjangoとnginxのsettingの仕方に問題があるようなのですが、どうしてもうまくいきません。

  • Djangoのsettings.pyのMEDIA_ROOT、STATIC_ROOTにそれぞれ2つずつディレクトリ名が入っている理由もよくわからず。cf. MEDIA_ROOT('staticfiles', 'media_root')、STATIC_ROOT('staticfiles', 'static_root')

  • おそらく初歩的なDjangoとnginxの構造の理解が足りていないことが原因だと思いますが、自己解決しきれませんでした。

どなたか解決方法お分かりになる方いらっしゃいましたら助けてください。
よろしくお願いいたします。

発生している問題・エラー

- faviconが表示されるべきところにデフォルトの地球のようなマークが表示される
- 画像が表示されるべき場所に、写真の右下が破れたようなエラーアイコンが表示される

該当するソースコード

Django
# \Django_eighthPJ\eighthPJ\settings.py

from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '...'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['VPS アドレス', 'localhost']

# Application definition

INSTALLED_APPS = [
    'blog.apps.BlogConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'eighthPJ.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'eighthPJ.context_processors.common',
            ],
        },
    },
]

WSGI_APPLICATION = 'eighthPJ.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

MEDIA_ROOT = os.path.join(BASE_DIR, 'staticfiles', 'media_root')
MEDIA_URL = '/media/'

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles', 'static_root')
STATIC_URL = '/static/'

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

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
nginx
#/etc/nginx/sites-available/eighthPJ
server {
    listen 80;
    'VPS アドレス';

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/'user_name'/Django_eighthPJ;
    }
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

自分で試したこと

  • nginxのlocationにcollectstaticを実行した後で表示されるpath、Djangoのsettings.pyのMEDIA_ROOT、STATIC_ROOTのディレクトリ名を入れ替えてみましたが、うまくいきませんでした。
    (ご参考:collectstatic実行後のコメント)
    You have requested to collect static files at the destination
    location as specified in your settings:
    /home/'user_name'/Django_eighthPJ/staticfiles/static_root

  • 下記の動画を参考にさせていただき、nginxのerrorログを確認したところ、faviconのpermission errorが出ていたのでパーミッションしたところfaviconは表示されるようになりました。(現在、なぜか再度表示されないようになっています)
    https://www.youtube.com/watch?v=0RMkIX7pukU

  • 開発環境では問題なくアプリが立ち上がっているので、おそらくnginxの設定だと思い、下記のサイトを参考にさせていただき、nginxのlocationの設定をいろいろ変えてみましたが、うまくいきませんでした。
    https://daeudaeu.com/django-staticfile/

【以下、nginxで試してみた設定です】
Option 1.
location /static/ {
root /home/'user_name'/Django_eighthPJ/static;
}

Option 2.
location /static/ {
root /home/'user_name'/Django_eighthPJ/static/;
}

Option 3.
location static/ {
root /home/'user_name'/Django_eighthPJ;
}

Option 4.
location static/ {
root /home/'user_name'/Django_eighthPJ/static;
}

Option 5.
location static/ {
root /home/'user_name'/Django_eighthPJ/static/;
}

Option 6.
location /static/ {
alias /home/'user_name'/Django_eighthPJ;
}

Option 7.
location /static/ {
alias /home/'user_name'/Django_eighthPJ/static;
}

Option 8.
location /static/ {
alias /home/'user_name'/Django_eighthPJ/static/;
}

Option 9.
location static/ {
alias /home/'user_name'/Django_eighthPJ;
}

Option 10.
location static/ {
alias /home/'user_name'/Django_eighthPJ/static;
}

Option 11.
location static/ {
alias /home/'user_name'/Django_eighthPJ/static/;
}

0 likes

3Answer

気になる点

  1. STATIC_ROOTの引数
    STATIC_ROOTとMEDIA_ROOTはcollectstaticした時に、どこにファイルを集めるか指定するものです。なので、本番環境で集めたい場所を指定します。

  2. Mediaのlocation
    staticは指定されていますが、mediaが指定されていません。

ディレクトリの構造がどうなってるか分からないので、あくまで設定のサンプルです。

settings.py
STATIC_URL = '/static/'
MEDIA_URL = '/media/'

# パスにしてみる
STATIC_ROOT = '/home/USERNAME/eighthPJ/static'
MEDIA_ROOT = '/home/USERNAME/eighthPJ/media'
nginx
server {
    listen 80;
    server_name xxx.xxx.xxx.xxx;
    
    location /static {
        root /home/USERNAME/eighthPJ/static;
    }

    location /media {
        root /home/USERNAME/eighthPJ/media;
    }

    ...
テストしてみる。
$ sudo nginx -t

再起動
$ sudo systemctl restart nginx

必要ならエラーログ
$ sudo vi /var/log/nginx/error.log
2Like

Comments

  1. @Naoki-Design

    Questioner

    @NOIZEさま
    ありがとうございます!
    コメントを参考に編集してみました。結局今のところまだ表示されるに至っていないのですが、エラーコードを見るとメディアファイルがみつからないと言っているようです。
    設定の仕方をいろいろと変更してもみましたが、どうしてもうまくいきません。。

    #settings.py
    ...
    MEDIA_ROOT = '/home/gougou/Django_eighthPJ/media'
    MEDIA_URL = '/media/'

    STATIC_ROOT = '/home/gougou/Django_eighthPJ/staticfiles' *staticにするとconflict errorが起きたのでstaticfilesにしました
    STATIC_URL = '/static/'

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


    # /etc/nginx/sites-available/eighthPJ
    ...
    location /static/ {
    root /home/gougou/Django_eighthPJ/;
    }
    location /media/ {
    root /home/gougou/Django_eighthPJ/;
    }
    ...

    #error code

    2022/11/11 17:24:29 [error] 1341776#1341776: *1 open() "/home/gougou/Django_eighthPJ/media/post_content_images/dog-sit--v3.png" failed (2: No such file or directory), client: '...', server: '...', request: "GET /media/post_content_images/dog-sit--v3.png HTTP/1.1", host: "'...'", referrer: "http://'...'/post/2/" *VPSのアドレス等は伏字にさせていただきました
  2. location /static/ {
    root /home/gougou/Django_eighthPJ/staticfiles;
    }

    location /media/ {
    root /home/gougou/Django_eighthPJ/media;
    }

    でもだめでしょうか?

    locationは、本番環境での静的ファイルを見に行く場所を指定するので、必然的に、MEDIA_ROOT、STATIC_ROOTと同じパスになるかとおもます。
  3. location as specified in your settings:
    /home/'user_name'/Django_eighthPJ/staticfiles/static_root

    のメッセージを今みました。

    多分既に、Django_eighthPJ/staticfilesの中に、media_rootとstatic_rootのディレクトリが存在します。

    $ cd /home/gougou/Django_eighthPJ ls
    $ cd /home/gougou/Django_eighthPJ/staticfiles ls

    で中身がどうなっているか確認してみてください。

    一度ディレクトリの作成からやり直した方が綺麗になるので良いかもです。

    ルートを
    STATIC_ROOT = '/home/USERNAME/eighthPJ/static'
    MEDIA_ROOT = '/home/USERNAME/eighthPJ/media'

    で設定するなら

    Django_eighthPJ内のstaticfilesを削除し、Django_eighthPJ内にstaticとmediaのディレクトリを作成します。(もしあればそれを使ってもOK)

    collectstaticします。

    sites-available/eighthPJを

    location /static/ {
    root /home/gougou/Django_eighthPJ/static;
    }

    location /media/ {
    root /home/gougou/Django_eighthPJ/media;
    }

    に設定します。

    あとはテストと再起動でOKかな?
  4. @Naoki-Design

    Questioner

    @NOIZEさま
    度々の丁寧なご指導ありがとうございます!
    staticfilesディレクトリを削除して、mediaフォルダを作成、collectstaticをし直してトライしてみました。残念ながらやはり画像が表示されないままです。エラーコードは前回と同じでした。
    nginxとDjangoのsettingの構造はわかりましたので、いったん開発環境のPJから作り直してみることにします。

    #error code
    2022/11/12 17:16:17 [error] 1371100#1371100: *1 open() "/home/gougou/Django_eighthPJ/media/post_images/dog_in_autumn.png" failed (2: No such file or directory), client: '...', server: '...', request: "GET /media/post_images/dog_in_autumn.png HTTP/1.1", host: "...", referrer: "http://'...'/"
  5. あとはディレクトリのアクセス権や所有者を確認してみてください。

    Django_eighthPJを調べる(これは多分大丈夫そう?)
    $ cd /home/gougou/Django_eighthPJ
    $ ls -l

    あとはstaticとmediaのチェック
    $ cd /home/gougou/Django_eighthPJ/static
    $ ls -l

    $ cd /home/gougou/Django_eighthPJ/media
    $ ls -l

    アクセス権や所有者が想定しているものと違えば、chownやchmodで変更が必要かもです。

    例えば、Django_eighthPJの中身全ての所有者をgougouに変えるのであれば

    $ sudo chown -R gougou /home/gougou/Django_eighthPJ

    でいけるかと。
  6. @Naoki-Design

    Questioner

    @NOIZEさま
    コメントありがとうございます。
    教えていただいたアクセス権周りトライしてみましたが、やっぱり画像表示されるようにならなかったです。。せっかく教えていただいたのに、悔しいです!

    #ターミナル入力内容
    (env)gougou:~/Django_eighthPJ$ ls -l
    total 264
    drwxrwxr-x 6 gougou gougou 4096 Oct 30 17:53 blog
    -rw-rw-r-- 1 gougou gougou 217088 Nov 12 21:49 db.sqlite3
    drwxrwxr-x 5 gougou gougou 4096 Nov 12 21:59 eighthPJ
    drwxrwxr-x 4 gougou gougou 4096 Oct 24 22:55 eighthPJenv
    -rw-rw-r-- 1 gougou gougou 13626 Oct 24 22:52 favicon.ico
    -rw-rw-r-- 1 gougou gougou 763 Oct 24 22:52 main.py
    -rw-rw-r-- 1 gougou gougou 664 Oct 24 22:52 manage.py
    drwxrwxr-x 2 gougou gougou 4096 Nov 12 17:04 media
    drwxrwxr-x 3 gougou gougou 4096 Oct 24 22:52 static
    drwxrwxr-x 3 gougou gougou 4096 Nov 12 21:49 staticfiles
    drwxrwxr-x 3 gougou gougou 4096 Oct 24 22:52 templates
    (env) gougou:~/Django_eighthPJ$ cd static
    (env) gougou:~/Django_eighthPJ/static$ ls -l
    total 4
    drwxrwxr-x 2 gougou gougou 4096 Oct 24 22:52 img
    (env) gougou:~/Django_eighthPJ/static$ cd ..
    (env) gougou:~/Django_eighthPJ$ cd media
    (env) gougou:~/Django_eighthPJ/media$ ls -l
    total 0
    (env) gougou:~/Django_eighthPJ/media$ sudo chown -R gougou /home/gougou/Django_eighthPJ
    [sudo] password for gougou:
    (env) gougou:~/Django_eighthPJ/media$ ls -l
    total 0
    (env) gougou:~/Django_eighthPJ/media$ cd ..
    (env) gougou:~/Django_eighthPJ$ cd static
    (env) gougou:~/Django_eighthPJ/static$ ls -l
    total 4
    drwxrwxr-x 2 gougou gougou 4096 Oct 24 22:52 img
    (env) gougou:~/Django_eighthPJ/static$ cd ..
    (env) gougou:~/Django_eighthPJ$ sudo systemctl restart gunicorn
    (env) gougou:~/Django_eighthPJ$ sudo systemctl daemon-reload
    (env) gougou:~/Django_eighthPJ$ sudo systemctl restart gunicorn.socket gunicorn.service
    (env) gougou:~/Django_eighthPJ$ sudo nginx -t && sudo systemctl restart nginx
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  7. Django_eighthPJ/mediaに何も入ってないみたいですねぇ
    collectstaticが上手くいってないか、入れ忘れですかね?
    画像表示されない原因はmediaにファイルが無いのが原因ですね笑

    あとSQLiteが入っていますが、本番環境はSQLiteですか??
    私は本番環境でSQLiteを使ったことがないので分からないのですが、後々遅くなると思うのでPostgreSQLあたりに変えたほうが良い気もします。(VPSの会社で変わるんですかね??)

    あとfavicon.icoがルートディレクトリにあるのも気になります。
    静的ファイルはstaticの中にまとめたほうが良いですよん(^_^)v
  8. @Naoki-Design

    Questioner

    @NOIZEさま
    mediaに何も入っていない、そうなんですね!
    それすらわからないというお恥ずかしいスキルレベルです。本当に助かります。
    しかし、Debugモード:Trueだと問題ないので、collectstaticがうまくいっていないということだと思うのですが、理由は謎です。。研究してみます。
    実はPostgreSQLで本番環境構築しようとしていたのですが、errorが発生したので、とりあえず画像が解決したら次に、と思っていました。まだまだやることがいっぱいあってやりがいあります!(ふぅ。)
  9. (env) gougou:~/Django_eighthPJ/media$ ls -l
    total 0
    $ cd ~/Django_eighthPJ/media ls でmedia内のディレクトリやファイルの一覧がでるので見てみて下さい。

    githubからデプロイしていると思うのですが、githubに画像は反映されてますか?
    VPS側でpullはされていますか?

    >実はPostgreSQLで本番環境構築しようとしていた
    色々問題がありそうです笑
    DATABASEの設定、.envファイル、migrateなどなど・・・

    通常、開発環境と本番環境はなるべく差がないようにつくるので、開発環境から設定し直したほうが良いですね。。。差があるとエラーの原因になるので・・・

    開発環境でPostgresを設定できたら、ほとんど本番もほぼ同じなので是非チャレンジしてみてください。

    最近同じ様な内容を記事にしてるのでこちらも見てみて下さい。
    https://django.noizmoon.com/deploying-django-on-vps-part-1/
  10. @Naoki-Design

    Questioner

    NOIZEさま
    ありがとうございます。
    おっしゃる通り、mediaフォルダに画像がcollectされていませんでした。
    collectstaticの不具合の理由は依然としてよくわからないです。
    開発環境をgitにアップして、VPSでpullしています。
    pull時点で画像ファイルは確認できますし、DebugモードTrueでは画像表示されますので、おそらくcollectstaticの問題なのかなと思います。
    アップデートやアップグレードやってみて、やっぱりうまくいかないようでしたら、送っていただいたサイトを参考に、一からPJを作り直してみようかと思います。
  11. すいません、とんでもない勘違いをしていました!
    collectstaticはstaticディレクトリを集めるだけで、Mediaディレクトリは集めないようです。

    mediaはユーザーアップロードした画像(ブラウザでアップロードされた画像)等が保存される場所でした汗

    普段Cloudinaryを使っていたので、逆に良い勉強になりましたm(_ _)m

    https://docs.djangoproject.com/en/4.0/ref/settings/#media-root
  12. @Naoki-Design

    Questioner

    なるほど、こういうときにドキュメントを確認しに行けばいいのですね。
    何から何まで参考になります。
    しかし、完全に独学で対症療法的にやっていると限界を感じてきました。
    Pythonを包括的に学べるところを探してみようかと思いました。
  13. 私はTrial and errorの繰り返しでしたねぇ

    重要度・オススメ
    1.作りたいものがある
    2.本を読む・公式ドキュメントを読む
    3.誰かに聞く
    4.動画

    1~3は非常に重要かと思います。

    動画は、本だと数ページの内容を何十分もかけて説明したりするので効率は悪いです。
    特定の要素が分からなくて噛み砕いて知りたい場合は、動画は役立つかもしれません。

    個人的感想ですが動画はエンタメの域に感じています笑(分かった気にさせてくれる)

    Naokiさんは既にDjangoでアプリを作られているので、後はデプロイ関係を経験すれば、ポンポン作れるようになると思いますよ(^o^)

    P.S. VPS側ディレクトリの構造にも少し問題があるかもなので調整した方が良いかも?
  14. @Naoki-Design

    Questioner

    @NOIZEさま
    了解です。少しずつ前に進んでいるような気はしますので、引き続き頑張ってみます!
    今後またお世話になるかもしれませんので、その時はまた是非よろしくお願いいたします。

デプロイ後、VPSサーバー上でアプリが動くことを確認しましたが、開発環境(127.0.0.1:8000)では表示されていたfaviconが表示されていません。

gunicornは正常でfavicon.icoが表示されない点のみ、他は他の方に期待しましょう。

server {
    listen 80;
    root  /var/www/html; 全体に影響
  location = /favicon.ico { 
          root ここだけ影響;
      access_log off;
   }

rootの指定がないので、デホルトか?systemd,serviceのコマンド指定のパスの配下に、favicon.icoが見当たらないのだと
思います。
/var/www/html/favicon.ico に配置しましょう!
暇人x in 電車

1Like

Comments

  1. @Naoki-Design

    Questioner

    @HalHarada さま
    ありがとうございます!おかげさまでfaviconは表示されるようになりました。厉害!!
    nginxのデフォルトのフォルダはどこだったんでしょうね。。これについては宿題にします。
    ともあれ、ほっとしました。
    あとは、画像ファイルですね。どなたかお分かりになる人がいるといいのですが。。
  2. 小さな改善おめでとう!
    Debugモード:True では動作は問題ないのでしょうか?
    webブラウザー(http)=>nginx(http)=>gunicorn(unixsocket)=>wsgi(Django/python)の経路でDebugモードは末端のdjangoです。何故?

    開発環境はgunicorn(http)ではないですか?関係ないか?開発環境でDebugモード:Falseは正常なの?
  3. @Naoki-Design

    Questioner

    ご連絡ありがとうございます。
    コメントの内容に理解が追い付いていない中ですが、回答いたします。
    開発環境はgunicornは使っていないと思います。gunicornはデプロイ時に初めてセットしましたので。
    また、開発環境でDebugモード:False(ALLOWED_HOSTS = ['127.0.0.1.:8000'])にしてみたところ、ブラウザにはBad Request(400)が表示されました。
    回答になっていますでしょうか。。

【追伸】
画像ファイルの表示にようやく成功しました!
原因はcollectstaticをVPS上でしていたことによると思われます。
開発環境でcollectstaticしたところ、フォルダが作成され(画像ファイル格納確認)、この状態でgitにpush、VPSでpull。これでDebugモードFalseでもVPSからブラウザで画像ファイルを表示させることができました。
@HalHaradaさま、@NOIZEさま、いろいろ教えていただきまして本当にありがとうございました。おかげ様で少し自信がついてきたような気がします。
これからもたくさん質問することになると思いますので、見つけたらぜひご支援よろしくお願いいたします!

1Like

Your answer might help someone💌