Django collectstatic 无法使用 S3 进行生产,但相同的设置在本地工作

Posted

技术标签:

【中文标题】Django collectstatic 无法使用 S3 进行生产,但相同的设置在本地工作【英文标题】:Django collectstatic not working on production with S3, but same settings work locally 【发布时间】:2020-04-21 16:06:48 【问题描述】:

我一直在调整一些设置以创建更明确的本地和生产环境,我一定搞砸了。

以下是大部分相关设置。如果我将 production.py 设置(目前仅包含与 AWS 相关的设置)移动到 base.py,我可以从本地计算机更新 S3 就好了。同样,如果我将这些 AWS 设置保存在 base.py 中并推送到生产环境,S3 会相应地更新。此外,如果我从 production.py 打印一些东西,它会打印。但是,如果我在 manage.py 上将 production.py 设置为我的“本地”设置,或者当我使用如下所示的设置推送到 Heroku 时,S3 不会更新。

我的设置不正确怎么办? (好吧,我确定有几件事,但具体是导致 S3 不更新?)

以下是一些相关代码:

__init__.py(在包含基础、本地和生产的目录中)

from cobev.settings.base import *

base.py

INSTALLED_APPS = [
    ...
    'whitenoise.runserver_nostatic',
    'django.contrib.staticfiles',
    ...
    'storages',
]

...

STATIC_URL = '/static/'

STATICFILES_DIRS = [os.path.join(BASE_DIR, "global_static"),
                    os.path.join(BASE_DIR, "media", )
                    ]

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

local.py

# local_settings.py
from .base import *

...

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

production.py

from .base import *

# AWS Settings

AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = 'cobev'

AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = 
    'CacheControl': 'max-age=86400',

AWS_LOCATION = 'static'

AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
DEFAULT_FILE_STORAGE = 'cobev.storage_backends.MediaStorage'
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)

ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/'

# End AWS

wsgi.py

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cobev.settings.production")

application = get_wsgi_application()

from whitenoise.django import DjangoWhiteNoise
application = DjangoWhiteNoise(application)

manage.py

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cobev.settings.local") 
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

【问题讨论】:

AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY 来自哪里?您是否检查过这些在 Heroku 环境中的定义是否正确?键是否包含任何尚未转义的特殊字符($@)(这会导致 Heroku 出现问题)? 它们作为环境变量存储在 heroku 上。我会三重检查,但如果 AWS 代码在 base.py 而不是 production.py 中,那么这些 var 可以工作。 S3 更新了什么?它是 django 管理命令,还是您正在从 Django 管理员等更新 S3 文件上传等? @Sigdev 可能是不久前设置 heroku 留下的。我会调查的! 【参考方案1】:

您似乎正在使用Whitenoise。 Whitenoise 允许 django 提供其静态文件。如果您想从 AWS 为它们提供服务,则采用不同的方法。

因此,您需要删除 Whitenoise 才能使用 django-storages。将其从设置、中间件、wsgi.py 等中删除。

此外,您可以在设置中删除 __init__.py 中的所有内容 - 要使用的设置文件由 DJANGO_SETTINGS_MODULE 环境变量设置。


根据您的STATICFILES_DIRSmedia 目录包含为静态文件。最好将媒体与静态文件分开提供(差异之一 - 静态文件更有可能被缓存和压缩) - 即也使用 AWS,但来自单独的 S3 存储桶。

您也可以稍后在您的存储桶前添加AWS CloudFront 作为 CDN。

【讨论】:

【参考方案2】:

那么,如果这可以帮助我在生产中对 AWS/Django/S3 的配置:

通用静态配置:

# STATIC FILE CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = str(ROOT_DIR('staticfiles'))

# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = '/static/'

# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = [
    str(APPS_DIR.path('static'))
]

# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder'
]

通用媒体配置:

# MEDIA CONFIGURATION
# ------------------------------------------------------------------------------

# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = str(APPS_DIR('media'))

# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/'

生产静态配置:

# STATIC CONFIG PRODUCTION
# ------------------------------------------------------------------------------
# See: http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html

AWS_STORAGE_BUCKET_NAME = 'mybucket-name-production'
AWS_ACCESS_KEY_ID = 'YOUR_KEY_ID'
AWS_SECRET_ACCESS_KEY = 'YOUR_SECRET_KEY'
AWS_S3_HOST = "s3.amazonaws.com"
AWS_S3_URL = 'https://bucker_name.s3.amazonaws.com/'.format(bucker_name=AWS_STORAGE_BUCKET_NAME)

AWS_LOCATION = 'static/'

AWS_S3_URL_PROTOCOL = 'https:'
AWS_S3_CUSTOM_DOMAIN = 'static.mydomain.com' # I use sub domaine to serve static 

STATIC_URL = 'AWS_S3_URL_PROTOCOL//AWS_S3_CUSTOM_DOMAIN/AWS_LOCATION'.format(
    AWS_S3_URL_PROTOCOL=AWS_S3_URL_PROTOCOL,
    AWS_S3_CUSTOM_DOMAIN=AWS_S3_CUSTOM_DOMAIN,
    AWS_LOCATION=AWS_LOCATION)

AWS_QUERYSTRING_AUTH = False

AWS_IS_GZIPPED = True
AWS_EXPIREY = 60 * 60 * 24 * 14

# For s3boto
AWS_HEADERS = 
    'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, AWS_EXPIREY)


# For s3boto3
AWS_S3_OBJECT_PARAMETERS = 
    'CacheControl': 'max-age=%d' % AWS_EXPIREY,


AWS_PRELOAD_METADATA = True
#AWS_S3_FILE_OVERWRITE = True

STATICFILES_STORAGE = 'config.storages.StaticStorage'
DEFAULT_FILE_STORAGE = 'config.storages.DefaultStorage'



# MEDIA S3 CONFIG PRODUCTION
# --------------------------------------------------------------------------------

AWS_MEDIA_DIR = 'media'
MEDIA_URL = AWS_S3_URL + AWS_MEDIA_DIR + '/'
MEDIA_ROOT = MEDIA_URL

这是我的静态存储类:

from storages.backends.s3boto3 import S3Boto3Storage


class StaticStorage(S3Boto3Storage):
    location = 'static'
    file_overwrite = False


class DefaultStorage(S3Boto3Storage):
    location = ''
    file_overwrite = False

之后,我在 .config 文件中的文件夹 .ebextensions 中为 collectstatic 添加命令:

# ./ebextensions/02_container_commands.config file : 

container_commands:
  0.3.0.push.static.to.s3:
    command: "source /opt/python/run/venv/bin/activate && python manage.py collectstatic --ignore=.scss --noinput"
    leader_only: true
    ignoreErrors: true

【讨论】:

希望对你有所帮助;)【参考方案3】:

好的,让我试试,正如从问题的 cmets 中发现的那样,您使用 collectstatic 进行 S3 更新,但这是一个使用 manage.py 文件调用的管理命令,您在该文件中将 cobev.settings.local 设置为不等于用于wsgi.py 文件的cobev.settings.production

我认为您应该使用普通的 Django 方式管理您的设置文件,操作系统环境变量名为 DJANGO_SETTINGS_MODULE

当然,您应该能够在您正在运行的任何生产环境中设置它。

【讨论】:

以上是关于Django collectstatic 无法使用 S3 进行生产,但相同的设置在本地工作的主要内容,如果未能解决你的问题,请参考以下文章

collectstatic 无法收集管理静态文件

使用 Django 存储和 S3 在 collectstatic 上出现 MemcachedError

Django collectstatic 不覆盖生产文件

Django学习——collectstatic错误

使用docker将django应用程序部署到heroku时在哪里运行collectstatic?

Heroku 上的 webpack 和 django:collectstatic 之前的捆绑