在我的笔记本电脑上本地运行命令python manage.py makemigrations
时,我在控制台上得到以下错误:
(mywebsite) C:UsersSanderPycharmProjectsmywebsite>python manage.py makemigrations
Invalid line: echo DATABASE_URL=postgres://myuser:mypassword@//cloudsql/mywebsite:europe-west6:mywebsite-db/mydb > .env
Invalid line: echo GS_BUCKET_NAME=mybucket >> .env
Invalid line: echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n
请注意,echo [... etc. ...] > .env
指令实际上是我在Google Cloud Platform的secret Manager上配置的一个秘密的内容,当我按照Google Cloud Platform的说明在Google Cloud Run上部署我的Django网站时。
现在我知道这些echo [... etc. ...] > .env
指令应该创建一个文件。env与变量DATABASE_URL, GS_BUCKET_NAME和SECRET_KEY在其中,但它没有,当然,因为它报告错误"无效行:…"相反。
我找到了这个StackOverflow答案,说明这些bash指令(echo [... etc. ...] > .env
)根本不能由python执行,我可以通过从shell脚本运行它们来简单地在本地执行它们,所以执行这个create-env-file.sh
工作:
DATABASE_URL=postgres://myuser:mypassword@//cloudsql/mywebsite:europe-we
st6:mywebsite-db/mydb > .env
GS_BUCKET_NAME=mybucket >> .env
echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n
然而,这会生成一个本地。env文件,显然,在将网站移动到生产之前,我想确认与秘密管理器一起工作的流程。,因为如果我保留.env文件并将其上传到生产环境中,那么仍然使用秘密管理器就没有意义了,它不再是必需的了。
当观察后面实际运行的代码时,这一点也变得非常清楚:
import io
import os
from pathlib import Path
import environ
import google.auth
from google.cloud import secretmanager
if os.path.isfile(env_file):
# Use a local secret file, if provided
env.read_env(env_file)
elif os.environ.get("GOOGLE_CLOUD_PROJECT", None):
# Pull secrets from Secret Manager
project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")
client = secretmanager.SecretManagerServiceClient()
settings_name = os.environ.get("SETTINGS_NAME", "django_settings")
name = f"projects/{project_id}/secrets/{settings_name}/versions/latest"
payload = client.access_secret_version(name=name).payload.data.decode("UTF-8")
env.read_env(io.StringIO(payload))
else:
raise Exception("No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.")
我调试了该代码,问题是在env.read_env(io.StringIO(payload))
的执行中,因为有效负载包含预期的内容(echo [... etc. ...] > .env
行)。
我看到了使用Google的秘密管理器的优势,但我开始认为要么是Django Cloud Run教程提供的秘密内容有问题,要么是代码解析并将其保存为环境变量。
我在Windows本地运行,使用以下库(我的requirements.txt
的内容):
django
# For integrating with Google Cloud Platform:
django-storages[google]>=1.12
django-environ>=0.8.1
psycopg2-binary>=2.9.1
gunicorn>=20.1.0
google-cloud-secret-manager>=2.7.2
这显然是由两个原因引起的:
-
在settings.py中,使用
env.read_env(io.StringIO(payload))
将秘密内容加载到环境变量中,如问题所述。read_env()函数显然做了以下工作:# ... for line in content.splitlines(): m1 = re.match(r'A(?:export )?([A-Za-z_0-9]+)=(.*)Z', line) if m1: # ... elif not line or line.startswith('#'): # ignore warnings for empty line-breaks or comments pass else: logger.warning('Invalid line: %s', line) # ...
因为秘密内容的行以"echo "开头,所以这些行被标记为无效(你可以在https://regex101.com上尝试一下)并且命令
python manage.py makemigrations
因此崩溃并产生该错误。 -
当编辑
payload
之前,它进入env.read_env(io.StringIO(payload))
,使它不再以echo
开始(解决上面的问题),也因为正则表达式匹配,也删除> .env
或>> .env
在最后,第三行也会导致问题:SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n
。读取/dev/urandom
生成伪随机的SECRET_KEY是Windows上的问题,/dev/urandom
只存在于linux操作系统。
所以看起来这个秘密应该在Google Cloud Run的基于linux的生产服务器上工作,而不是在运行Windows的本地计算机上,如果像部署Django网站的说明那样执行。
对于本地运行,让创建一个.env文件,可以通过运行问题(*)中提到的create-env-file.sh
脚本,或者在与settings.py:
DATABASE_URL=postgres://myuser:mypassword@//cloudsql/mywebsite:europe-west6:mywebsite-db/mydb
GS_BUCKET_NAME=mybucket
SECRET_KEY=some1279Long30String5You7Create2Yourself8136
(*)/dev/urandom确实对我有用,因为当我在PyCharm的终端窗口上执行create-env_file.sh
时,我的计算机上打开了一个新的Git Bash终端,可能是因为在我的计算机上这是执行shell文件的注册程序。