Django 在部署到 Elastic Beanstalk 时看不到环境变量

Posted

技术标签:

【中文标题】Django 在部署到 Elastic Beanstalk 时看不到环境变量【英文标题】:Django doesn't see environment variables when deployed to Elastic Beanstalk 【发布时间】:2017-04-07 18:52:18 【问题描述】:

我正在尝试在 Elastic Beanstalk 上设置 Django/DRF 应用程序,但无论出于何种原因,Django 都无法看到所需的环境变量。当我登录时,我可以使用

很好地看到它们
$ eb ssh
$ cat /opt/python/current/env

我也可以看到,除了涉及RDS比较敏感的,直接用$eb printenv就可以了。

所有似乎都已设置并正常工作。但是,Django 喜欢在启动时立即读取环境,而且似乎还没有设置环境变量。我已经尝试在settings.py 中简单地插入print(os.environ),当我这样做时,我发现了一大堆我不需要的环境变量(即'SUPERVISOR_GROUP_NAME': 'httpd'),而我没有设置自己,比如DJ_SECRET_KEY

我已经更改了代码以在加载设置时报告特定环境变量的缺失,并且从最近的运行中,它生成了以下内容:

[Wed Nov 23 15:56:38.164153 2016] [:error] [pid 15708] DJ_SECRET_KEY not in environment; falling back to hardcoded value.
[Wed Nov 23 15:56:38.189717 2016] [:error] [pid 15708] RDS_DB_NAME not in environment; falling back to sqlite
[Wed Nov 23 15:56:38.189751 2016] [:error] [pid 15708] AWS_STORAGE_BUCKET_NAME not in environment; falling back to local static storage.

同样,这些变量是在设置中设置的,它们会与 EB 提供的任何其他报告工具一起显示。它们只是没有及时设置好让 Django 在启动并读取 settings.py 时读取它们。

This 看起来非常接近这个问题,但实际上并不相同:我知道在 ssh 进入 eb 实例时如何查看/加载环境变量到 shell;当我需要他们进行实际项目时,他们只是没有出现。

This 几乎正是我遇到的问题,但接受的正确答案对我来说毫无意义,投票最多的答案也不适用;这些文件已经在 git 中了。

我应该如何配置才能让 Django 看到环境变量?

【问题讨论】:

不确定 Elastic Beanstalk,但我在使用 Apache/nginx 设置 Django 项目时遇到了类似的问题。而且我必须在他们的配置文件中设置环境变量。可能在这里应该适用。 你想完成什么? @Gustaf 我正在尝试使用环境变量来配置 Django。例如,给定一个关联的 RDS 实例,EB 会自动设置几个变量,例如RDS_DB_NAME,通过这些变量可以连接到该数据库。问题是这些变量在 Django 读取它们时没有设置。一个解决方案可能是我们可以延迟 WSGI 初始化直到环境变量全部设置的方法。它们最终都会被设置好,但显然还不够快。 我无法在此处格式化正确的评论,因此我将其作为答案。我希望它有所帮助,即使它不是您问题的确切答案。 【参考方案1】:

鉴于 EB 将所有这些环境变量作为 bash 脚本存储在规范位置,我最终只是让 bash 执行脚本,并根据解析结果更新环境。

我与settings.py 并行创建了文件get_eb_env.py。其主要内容:

import os
import subprocess

ENV_PATH = '/opt/python/current/env'

def patch_environment(path=ENV_PATH):
    "Patch the current environment, os.environ, with the contents of the specified environment file."
    # mostly pulled from a very useful snippet: http://***.com/a/3505826/504550
    command = ['bash', '-c', 'source path && env'.format(path=path)]

    proc = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
    proc_stdout, _ = proc.communicate(timeout=5)

    # proc_stdout is just a big string, not a file-like object
    # we can't iterate directly over its lines.
    for line in proc_stdout.splitlines():
        (key, _, value) = line.partition("=")
        os.environ[key] = value

然后,我只需在我的settings.py 的头部附近导入并调用patch_environment()

【讨论】:

这就像一个魅力。只有一件事:在我运行 python 2.7.12 的 EB 中,proc.communicate 不采用 timeout 参数。你有什么解释吗?尽管如此,你还是让我开心。 我为此运行了 Python 3.4; timeout 参数是在 Python 3.2 中引入的 IIRC。如果您在早期的 Python 中需要该功能,您可以考虑使用subprocess32 backport。 天啊,你是我的救星。现在是 2019 年,亚马逊仍然没有解决这个问题!【参考方案2】:

我解决了修改密钥“DJANGO_AWS_SECRET_ACCESS_KEY”的问题,因为 DJANGO 生成了一个带引号 (`) 的密钥并将其解释为命令。

【讨论】:

【参考方案3】:

这并不是你想要的,但我希望这能解决你的问题。

我在设置中使用了很多环境变量。我认为 Elastic Beanstalk 在运行容器命令等之前设置了所有内容,然后变量不可用,这就是您的日志记录显示它们为空的原因。但是你真的需要他们那个时候的变量吗?

您不能将所需的任何本地开发设置放入 local_settings.py 并使其不受版本控制。

我们是这样使用它们的。

if 'DB_HOST' in os.environ:
    DATABASES = 
        'default': 
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'ebdb',
            'USER': 'ebroot',
            'PASSWORD': 'ebpassword',
            'HOST': os.environ['DB_HOST'],
            'PORT': '3306',
        
    

try:
    from local_settings import *
except ImportError:
    pass

它们在您运行 container_commands 时也可用:

container_commands:
  01_do_something:
    command: "do_something_script_$PARAM1.sh"

【讨论】:

我们确实需要环境变量。您的示例确切地说明了原因。我在settings.py 中有一些非常相似的东西,除了它回退到 Sqlite DB 而不是 localhost 上的 MySql。将此设置部署到 EB always 最终会运行 Sqlite,而不是正确连接到共享数据库。这不是我们想要的生产环境行为! 那么别无其他,我们有一个本地设置文件。让我更新我的答案。

以上是关于Django 在部署到 Elastic Beanstalk 时看不到环境变量的主要内容,如果未能解决你的问题,请参考以下文章

Django 在部署到 Elastic Beanstalk 时看不到环境变量

使用 WhiteNoise 在生产模式下将 django 部署到 Elastic Beanstalk

将 Django 应用程序部署到 Amazon AWS Elastic Beanstalk 时遇到问题

在移动设备或台式计算机之间部署到 aws elastic beanstalk 的 django 网站是不是有区别?

ALLOWED_HOSTS 在部署到 Elastic Beanstalk 的 Django 应用程序中不起作用

Elastic Beanstalk 上的 Docker + Django