Python 在S3上存储静态文件,但在本地存储staticfiles.json清单
我有一个Django应用程序在Heroku上运行。为了存储和服务静态文件,我使用S3存储桶以及标准的DjangoPython 在S3上存储静态文件,但在本地存储staticfiles.json清单,python,django,amazon-s3,deployment,django-storage,Python,Django,Amazon S3,Deployment,Django Storage,我有一个Django应用程序在Heroku上运行。为了存储和服务静态文件,我使用S3存储桶以及标准的DjangomanifestFileMixin。我也在用 代码: from django.contrib.staticfiles.storage import ManifestFilesMixin from storages.backends.s3boto import S3BotoStorage from pipeline.storage import PipelineMixin class
manifestFileMixin
。我也在用
代码:
from django.contrib.staticfiles.storage import ManifestFilesMixin
from storages.backends.s3boto import S3BotoStorage
from pipeline.storage import PipelineMixin
class S3PipelineManifestStorage(PipelineMixin, ManifestFilesMixin, S3BotoStorage):
pass
安装程序可以工作,但是staticfiles.json
manifest也存储在S3上。我认为这有两个问题:
staticfiles.json
,而不仅仅是从本地文件系统获取。这在性能方面没有什么意义。清单文件的唯一使用者是服务器应用程序本身,因此它也可以存储在本地文件系统上,而不是远程存储
我不确定这个问题有多重要,因为我认为(或希望)服务器应用程序在读取一次文件后会缓存该文件collectstatic
编写的,因此,如果服务器应用程序早期版本的任何已运行实例在部署完成和新slug接管之前从S3读取清单文件,他们可能会获取错误的静态文件——这些文件应该只用于新slug的实例
请注意,特别是在Heroku上,新的应用程序实例可能会动态弹出,因此即使应用程序缓存清单文件,也有可能在部署新slug期间首次获取清单文件
所描述的这个场景是特定于Heroku的,但我想其他环境也会有类似的问题在最后一段中有以下内容: staticfiles.json在哪里? 默认情况下,
staticfiles.json
将驻留在STATIC\u ROOT
中,它是
收集所有静态文件的目录我们将所有静态资产托管在一个S3存储桶上,这意味着默认情况下,
staticfiles.json
将最终同步到S3。但是,我们希望它位于代码目录中,这样我们就可以将它打包并发送到每个应用服务器。
因此,ManifestStaticFilesStorage
将查找
STATIC\u ROOT
中的staticfiles.json
,以读取映射。我们有
为了覆盖此行为,我们将ManifestStaticFilesStorage
子类化:
from django.contrib.staticfiles.storage import
ManifestStaticFilesStorage from django.conf import settings
class KoganManifestStaticFilesStorage(ManifestStaticFilesStorage):
def read_manifest(self):
"""
Looks up staticfiles.json in Project directory
"""
manifest_location = os.path.abspath(
os.path.join(settings.PROJECT_ROOT, self.manifest_name)
)
try:
with open(manifest_location) as manifest:
return manifest.read().decode('utf-8')
except IOError:
return None
通过上述更改,Django静态模板标记现在将读取
来自驻留在项目根目录中的staticfiles.json
的映射
我自己没用过,所以如果有用的话请告诉我 关于@John and的答案很好,但没有给出实现这一功能所需的完整代码:@Danra提到,要实现这一功能,您还需要将staticfiles.json
保存在源文件夹中。以下是我根据上述答案创建的代码:
import json
import os
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
from whitenoise.storage import CompressedManifestStaticFilesStorage
# or if you don't use WhiteNoiseMiddlware:
# from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
class LocalManifestStaticFilesStorage(CompressedManifestStaticFilesStorage):
"""
Saves and looks up staticfiles.json in Project directory
"""
manifest_location = os.path.abspath(settings.BASE_DIR) # or settings.PROJECT_ROOT depending on how you've set it up in your settings file.
manifest_storage = FileSystemStorage(location=manifest_location)
def read_manifest(self):
try:
with self.manifest_storage.open(self.manifest_name) as manifest:
return manifest.read().decode('utf-8')
except IOError:
return None
def save_manifest(self):
payload = {'paths': self.hashed_files, 'version': self.manifest_version}
if self.manifest_storage.exists(self.manifest_name):
self.manifest_storage.delete(self.manifest_name)
contents = json.dumps(payload).encode('utf-8')
self.manifest_storage._save(self.manifest_name, ContentFile(contents))
现在,您可以将LocalManifestStaticFilesStorage
用于STATICFILES\u存储
。运行manage.py collectstatic
时,清单将保存到根项目文件夹中,Django在提供内容时将在那里查找清单
如果部署有多个虚拟机,请确保仅运行一次
collectstatic
,并将staticfiles.json
文件复制到部署中的所有计算机,作为代码部署的一部分。这样做的好处是,即使某些计算机还没有最新的更新,它们仍然会提供正确的内容(对应于当前版本的代码),因此您可以在存在混合状态的情况下执行逐步部署。Django ticket解决了这个问题。票证有一个实现解决方案的拉取请求,但尚未对其进行审查。这很有帮助,尽管可能不完整,正如您发布的链接中有人提到的-save\u manifest
可能也需要被覆盖。很高兴知道@Darna!通过自定义存储类StaticStorage(manifestfilemixin,S3Boto3Storage)
将django存储与manifestfilemixin一起使用。此解决方案似乎有效。此配置导致我的项目中出现两个问题。首先,我遇到了botocore.exceptions.ClientError:调用DeleteObject操作时出错(速度减慢)(达到最大重试次数:4):降低请求速率。
运行collectstatic
命令后出错。其次,在正常负载下,我的CPU开始最大化。虽然botocore错误显然与自定义文件逻辑有关,但CPU性能峰值却很奇怪。我将它与ManifestFilesMixin
一起使用,有没有一种方法可以在不访问私有保存函数的情况下实现这一点?派林理所当然地抱怨\u save
调用,因为它被故意标记为private,可能会被破坏。