Elegantly handle site-specific settings/configuration in svn/hg/git/etc?
我一直在寻找更好的方法来处理特定于站点的设置(在本例中是django settings.py文件)。
settings.py结构和字段相当一致,但开发人员的框、集成、QA、测试和生产环境之间的值不同。
什么是一种优雅的方式来控制源代码,同时允许在不同的框之间进行更改?
我还担心在源代码管理中使用敏感数据(如数据库密码),但我确实需要自动部署。
我们使用的示例:
settings.py设置公共值,然后根据主机名或用户名加载辅助设置文件。
使用部署脚本将值注入settings.py文件。但这只会将问题转移到管理部署脚本,而不是settings.py脚本。
有人有特别优雅的方法吗?
创建一个main settings.py文件,其中应包括:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Pull in hostname-based changes. import socket HOSTNAME = socket.gethostname().lower().split('.')[0].replace('-','') try: exec"from myproject.settings.host_%s import *" % HOSTNAME except ImportError: pass # Pull in the local changes. try: from myproject.settings.local import * except ImportError: pass |
现在,为您关心的每个主机名创建一个新的设置文件。但这些真的很小。生产服务器的每个文件只包含:
1 | from myproject.settings.production import * |
您的登台服务器有:
1 | from myproject.settings.staging import * |
现在,您可以创建一个production.py文件,其中包含设置、staging.py等的生产覆盖。您可以为服务器扮演的每个角色创建新文件。
最后,您可以在任何具有本地重写的计算机(包括开发人员的计算机)上创建local.py文件,并将此文件标记为被源代码管理忽略,这样更改就不会被签入。
我们用这个结构已经很多年了,它工作得很好。
+1对于内德的回答,但要提及细微的变化。
我认为Django设置分为两个区域:项目和实例。
项目设置对所有实例都是通用的(开发、测试、生产、生产中的多个站点),实例设置仅对该特定服务器实例是本地的。
项目设置类似于
项目设置进入
我试过其他几种不同的方法,但都是在这里结束的,因为它简单得多。
唯一一次我不喜欢这种设置是针对多个站点(使用django站点框架),它们最终会扩散到像
最后,我在Git中保留了一个
让我们将这两个截然不同的问题分开:1)管理特定于站点的设置;2)管理机密。
1)站点特定设置
所有版本(机密除外),甚至是特定于开发人员的设置。
使用django和许多其他软件,配置文件是一段可执行代码,这使得加载公共配置设置和覆盖需要覆盖的内容变得容易。这样你就可以保持干燥。
1 2 3 | # settings_prod.py from settings_base import * ... # override whatever needs to be overridden for production environment |
现在你有了
我相信,将适当的设置文件部署到服务器是部署脚本的一项任务。部署脚本将知道您要部署到生产服务器的主机prod17,因此它将动态生成一个
1 2 | # settings.py (generated by deployment script) from settings_prod import * |
另一种解决方案是在通用
1 2 3 4 5 | # settings.py import os if os.environ["MY_APP_ENV"] =="prod": from settings_prod import * elif ... |
这里描述了我最喜欢的django设置解决方案。
对于任何其他没有配置文件那么灵活的软件,最好的选择可能是让部署脚本生成配置文件,可能使用模板(像chef或puppet这样的工具可以使这变得容易)。这样可以让您保持干燥:例如,假设一个软件需要一个扁平的
管理秘密
首先,不要将密码存储在版本控制系统中。-)
管理秘密的一个解决方案是让部署脚本传输秘密。例如,Bob负责部署Web应用程序,他知道数据库的密码,所以当他启动部署脚本时,会提示他输入数据库密码,脚本会将其传输到服务器。或者部署脚本只是读取Bob计算机上文件中的密码并将其传输。这可能是最常见的解决方案。大多数情况下都可以。
1 2 | secrets deployer ================> server |
如果您需要自动创建虚拟机,并且不希望自动部署程序知道任何秘密,那么您可以在虚拟机映像中包含这些秘密。当然,首先必须有人将秘密包含在虚拟机映像中。
1 2 3 4 5 6 | VM image including secrets human deployer -------------------------------+ | | image_name v automated deployer ==============> Cloud Service ========> VM including secrets |
这个解决方案的问题是,每次秘密更改时都需要生成一个新的VM映像。如果你想避免这种情况,那么你可能需要一个"秘密服务器":一个管理其他服务器秘密的服务器。那么,您需要在VM映像中包含的唯一秘密就是连接到"秘密服务器"所需的引导程序秘密。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | step 1: VM image including bootstrap secret human deployer -----------------------------------+ | | image_name v automated deployer ==================> Cloud Service ========> VM including secrets step 2: bootstrap secret ==================> VM Secret Server <================== secrets |
例如,秘密服务器可以是Chef服务器,秘密可以存储在加密的数据包中,引导程序秘密将是解密这些包的密钥。
我处理这个问题的方法是为每个环境创建一个base settings.py文件,然后创建一个设置文件(例如dev_settings.py、live_settings.py)
在每个特定于环境的文件的顶部
1 | from settings import * |
然后,我可以简单地覆盖任何需要根据特定环境更改的设置。
为了确保每个环境使用正确的设置,我只需修改django_settings_模块环境变量。如何做到这一点取决于您如何部署django(mod_wsgi、mod_python等等…)