管理不同环境的配置

Managing configurations for different environments

我和工作中的一些人讨论过,我们无法得出结论。
我们遇到了一个难题 - 您如何管理不同环境的不同配置值?

我们提出了一些选项,但其中 none 似乎令我们满意:
- 单独的配置文件(即 config.testconfig.prod 等),并有一个指向所选文件的文件(例如 ~/env),或一个指向它的环境变量。
- 使用单个数据库存储所有配置(您使用环境查询并获取相应的配置值)
- 在部署时创建配置文件(使用 CI/CD 系统,如 Atlassian Bamboo)

哪个选项使用更广泛?有没有更好的方法?
配置文件是否应与其余代码一起保存在 git 存储库中? 我们的系统是用 python(2.7 和 3)

编写的

您可以将所有这些配置放在一个配置文件中 config.py。

class Base():
    DEBUG = False
    TESTING = False

class DevelopmentConfig(Base):
    DEBUG = True
    DEVELOPMENT = True
    DATABASE_URI = "mysql+mysqldb://root:root@localhost/demo"

class TestingConfig(Base):
    DEBUG = False
    TESTING = True
    DATABASE_URI = "mysql+mysqldb://root:root@test_server_host_name/demo_test"

class ProductionConfig(Base):
    DEBUG = False
    TESTING = False
    DATABASE_URI = "mysql+mysqldb://root:root@prod_host_name/demo_prod"

在 shell 上设置环境变量,如

APP_SETTINGS = config.DevelopmentConfig

在您的主应用程序 app.py 中,加载此环境变量(以 flask 应用程序为例)

from flask import Flask
import os

app = Flask(__name__)
app.config.from_object(os.environ['APP_SETTINGS'])

希望对您有所帮助

一种方法是为每种配置文件编写一个 "template",其中模板主要包含纯文本,但有一些占位符。这是一个示例模板配置文件,使用符号 ${foo} 表示占位符。

serverName  = "${serverName}"
listenPort  = "${serverPort}"
logDir      = "/data/logs/${serverName}";
idleTimeout = "5 minutes";
workingDir  = "/tmp";

如果您对应用程序使用的所有配置文件都执行此操作,那么您可能会发现,使用相对较少的占位符值对模板配置文件执行全局搜索和替换会产生特定部署的 ready-to-运行 配置文件。如果您正在寻找一种简单的方法来对模板文件中的占位符执行全局搜索和替换,并且对 Java 感到满意,那么您可能需要考虑 Apache Velocity。但我想在 Python.

中开发类似的功能是微不足道的

将您的配置设置提交给源代码管理通常不是一个好主意,尤其是当这些设置包含密码或其他机密时。我更喜欢使用环境变量将这些值传递给程序。我发现的最灵活的方法是使用 argparse 模块,并将环境变量用作默认值。这样,您就可以在命令行上覆盖环境变量。不过,在命令行上输入密码时要小心,因为其他用户可能会在进程列表中看到您的命令行参数。

这里的 an example 使用了 argparse 和环境变量:

def parse_args(argv=None):
    parser = ArgumentParser(description='Watch the raw data folder for new runs.',
                            formatter_class=ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '--kive_server',
        default=os.environ.get('MICALL_KIVE_SERVER', 'http://localhost:8000'),
        help='server to send runs to')
    parser.add_argument(
        '--kive_user',
        default=os.environ.get('MICALL_KIVE_USER', 'kive'),
        help='user name for Kive server')
    parser.add_argument(
        '--kive_password',
        default=SUPPRESS,
        help='password for Kive server (default not shown)')

    args = parser.parse_args(argv)
    if not hasattr(args, 'kive_password'):
        args.kive_password = os.environ.get('MICALL_KIVE_PASSWORD', 'kive')
    return args

设置这些环境变量可能有点混乱,尤其是对于系统服务。如果您使用的是 systemd,请查看 service unit,并小心使用 EnvironmentFile 而不是 Environment 来获取任何机密信息。 Environment 值可以被任何具有 systemctl show.

的用户查看

我通常将默认值设置为对开发人员 运行 在他们的工作站上有用,这样他们就可以在不更改任何配置的情况下开始开发。

另一种选择是将配置设置放在 settings.py 文件中,注意不要将该文件提交给源代码管理。我经常提交一个用户可以复制的settings_template.py文件。

我们最终使用了类似于此 的方法。 我们有一个基本设置文件,以及简单地从基本文件导入所有内容的环境特定文件 base.py:

SAMPLE_CONFIG_VARIABLE = SAMPLE_CONFIG_VALUE

prod.py:

from base.py import *

因此,当从配置中访问值时,我们所要做的就是读取环境变量并创建与之对应的文件。

如果我们使用 Flask,那么我们可以像这样进行特定于环境的配置管理:

-- project folder structure

config/
   default.py
   production.py
   development.py
instance/
  config.py
myapp/
  __init__.py

在应用程序初始化期间。,

# app/__init__.py

app = Flask(__name__, instance_relative_config=True)

# Load the default configuration
app.config.from_object('config.default')

# Load the configuration from the instance folder
app.config.from_pyfile('config.py')

# Load the file specific to environment based on ENV environment variable
# Variables defined here will override those in the default configuration
app.config.from_object('config.'+os.getenv("ENV"))

同时 运行 应用程序:

# start.sh
# ENV should match the file name    
ENV=production python run.py

以上方法是我比较喜欢的方法。此方法基于 best practices described here 并进行了少量修改,例如基于环境变量的文件名。

但还有其他选择