【问题标题】:Using variables in Nginx location rules在 Nginx 定位规则中使用变量
【发布时间】:2013-03-03 06:08:09
【问题描述】:

在 Nginx 中,我正在尝试定义一个变量,该变量允许我为我的所有位置块配置一个子文件夹。我这样做了:

set $folder '/test';

location $folder/ {
   [...]
}

location $folder/something {
   [...]
}

不幸的是,这似乎不起作用。虽然 Nginx 不会抱怨语法,但它在请求 /test/ 时会返回 404。如果我明确写入文件夹,它可以工作。那么如何在位置块中使用变量呢?

【问题讨论】:

    标签: variables nginx webserver


    【解决方案1】:

    你不能。 Nginx 并不真正支持配置文件中的变量,它的开发人员嘲笑所有要求添加此功能的人:

    “与普通静态配置相比,[变量] 的成本相当高。[A] 宏扩展和“包含”指令应该与 [with] 一起使用,例如 sed + make 或任何其他常见的模板机制。” http://nginx.org/en/docs/faq/variables_in_config.html

    您应该编写或下载一个小工具,让您可以从占位符配置文件生成配置文件。

    更新 下面的代码仍然有效,但我已将其全部打包到一个名为 Configurator 的小型 PHP 程序/库中,也在 Packagist 上,它允许轻松生成 nginx/php -fpm 等配置文件,来自模板和各种形式的配置数据。

    例如我的 nginx 源配置文件如下所示:

    location  / {
        try_files $uri /routing.php?$args;
        fastcgi_pass   unix:%phpfpm.socket%/php-fpm-www.sock;
        include       %mysite.root.directory%/conf/fastcgi.conf;
    }
    

    然后我有一个定义了变量的配置文件:

    phpfpm.socket=/var/run/php-fpm.socket
    mysite.root.directory=/home/mysite
    

    然后我使用它生成实际的配置文件。看起来你是一个 Python 人,所以基于 PHP 的示例可能对你没有帮助,但对于其他使用 PHP 的人来说:

    <?php
    
    require_once('path.php');
    
    $filesToGenerate = array(
        'conf/nginx.conf' => 'autogen/nginx.conf',
        'conf/mysite.nginx.conf' => 'autogen/mysite.nginx.conf',
        'conf/mysite.php-fpm.conf' => 'autogen/mysite.php-fpm.conf',
        'conf/my.cnf' => 'autogen/my.cnf',
    );
    
    $environment = 'amazonec2';
    
    if ($argc >= 2){
        $environmentRequired = $argv[1];
    
        $allowedVars = array(
            'amazonec2',
            'macports',
        );
    
        if (in_array($environmentRequired, $allowedVars) == true){
            $environment = $environmentRequired;
        }
    }
    else{
        echo "Defaulting to [".$environment."] environment";
    }
    
    $config = getConfigForEnvironment($environment);
    
    foreach($filesToGenerate as $inputFilename => $outputFilename){
        generateConfigFile(PATH_TO_ROOT.$inputFilename, PATH_TO_ROOT.$outputFilename, $config);
    }
    
    
    function    getConfigForEnvironment($environment){
        $config = parse_ini_file(PATH_TO_ROOT."conf/deployConfig.ini", TRUE);
        $configWithMarkers = array();
        foreach($config[$environment] as $key => $value){
            $configWithMarkers['%'.$key.'%'] = $value;
        }
    
        return  $configWithMarkers;
    }
    
    
    function    generateConfigFile($inputFilename, $outputFilename, $config){
    
        $lines = file($inputFilename);
    
        if($lines === FALSE){
            echo "Failed to read [".$inputFilename."] for reading.";
            exit(-1);
        }
    
        $fileHandle = fopen($outputFilename, "w");
    
        if($fileHandle === FALSE){
            echo "Failed to read [".$outputFilename."] for writing.";
            exit(-1);
        }
    
        $search = array_keys($config);
        $replace = array_values($config);
    
        foreach($lines as $line){
            $line = str_replace($search, $replace, $line);
            fwrite($fileHandle, $line);
        }
    
        fclose($fileHandle);
    }
    
    ?>
    

    然后 deployConfig.ini 看起来像:

    [global]
    
    ;global variables go here.
    
    [amazonec2]
    nginx.log.directory = /var/log/nginx
    nginx.root.directory = /usr/share/nginx
    nginx.conf.directory = /etc/nginx
    nginx.run.directory  = /var/run
    nginx.user           = nginx
    
    [macports]
    nginx.log.directory = /opt/local/var/log/nginx
    nginx.root.directory = /opt/local/share/nginx
    nginx.conf.directory = /opt/local/etc/nginx
    nginx.run.directory  = /opt/local/var/run
    nginx.user           = _www
    

    【讨论】:

    • 好的,感谢您的回答并分享您对该问题的解决方案。
    • 感谢您的回复。开发人员不允许使用这些变量的任何特殊原因?
    • nginx.org/en/docs/faq/variables_in_config.html "变量不应用作模板宏。变量在处理每个请求期间在运行时进行评估,因此与普通静态配置相比,它们的成本相当高。使用变量来存储静态字符串也是一个坏主意。相反,应该使用宏扩展和“包含”指令来更轻松地生成配置,并且可以使用外部工具来完成,例如 sed + make 或任何其他常见的模板机制。"
    • 我确定 nginx 在启动时编译静态变量并不难,就像他们如何处理包含(逻辑假设)一样
    • 与@RickyB 的想法相同。为什么不具有在启动、重新启动或重新加载期间被替换并作为静态配置保存在内存中的变量(如在 Apache 中)的宏功能?所以不需要额外的工具和解决方法。
    【解决方案2】:

    这已经晚了很多年,但是自从我找到了解决方案后,我将在此处发布。通过使用maps,可以执行所要求的操作:

    map $http_host $variable_name {
        hostnames;
    
        default       /ap/;
        example.com   /api/;
        *.example.org /whatever/;
    }
    
    server {
        location $variable_name/test {
            proxy_pass $auth_proxy;
        }
    }
    

    如果您需要跨多个服务器共享同一个端点,您还可以通过简单地默认值来降低成本:

    map "" $variable_name {
        default       /test/;
    }
    

    Map 可用于根据字符串的内容初始化变量,并可在http 范围内使用,允许变量是全局的并且可跨服务器共享。

    【讨论】:

    • 地图模块对于我的团队动态设置变量(即每个请求)绝对是非常宝贵的,但由于可以使用mapif 的限制,它非常混乱。如果变量是静态的,我认为模板是更好的解决方案。
    • 很好的答案!这帮助我设置了一个变量,该变量在我的 http-serve.conf 中被多次重用,特定于该单个站点
    【解决方案3】:

    你可以做与你提议的相反的事情。

    location (/test)/ {
       set $folder $1;
    }
    
    location (/test_/something {
       set $folder $1;
    }
    

    【讨论】:

    • 我假设问题的作者试图告诉 Nginx 他的应用程序期望什么 url。我只是建议,与其那样做,Nginx 可以告诉他的应用程序使用什么 url 来访问它。匹配 ([^/]+) 会比匹配 (/test) 更有用,就像我给出的示例一样,但结果是一样的。
    • 因为 +1 其他人很有趣。
    【解决方案4】:

    @danack 的 PHP 生成脚本的修改 python 版本。它将所有位于build/ 中的文件和文件夹生成到父目录,替换所有{{placeholder}} 匹配项。在运行脚本之前,您需要将cd 转换为build/

    文件结构

    build/
    -- (files/folders you want to generate)
    -- build.py
    
    sites-available/...
    sites-enabled/...
    nginx.conf
    ...
    

    build.py

    import os, re
    
    # Configurations
    target = os.path.join('.', '..')
    variables = {
      'placeholder': 'your replacement here'
    }
    
    
    # Loop files
    def loop(cb, subdir=''):
      dir = os.path.join('.', subdir);
    
      for name in os.listdir(dir):
        file = os.path.join(dir, name)
        newsubdir = os.path.join(subdir, name)
    
        if name == 'build.py': continue
        if os.path.isdir(file): loop(cb, newsubdir)
        else: cb(subdir, name)
    
    
    # Update file
    def replacer(subdir, name):
      dir  = os.path.join(target, subdir)
      file = os.path.join(dir, name)
      oldfile = os.path.join('.', subdir, name)
    
      with open(oldfile, "r") as fin:
        data = fin.read()
    
      for key, replacement in variables.iteritems():
        data = re.sub(r"{{\s*" + key + "\s*}}", replacement, data)
    
      if not os.path.exists(dir):
        os.makedirs(dir)
    
      with open(file, "w") as fout:
        fout.write(data)
    
    
    # Start variable replacements.
    loop(replacer)
    

    【讨论】:

    • 如果您使用 python,建议使用 jinja 模板恕我直言
    • 这不能正确处理嵌套扩展
    猜你喜欢
    • 2014-01-02
    • 1970-01-01
    • 2019-07-21
    • 2018-12-02
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 2017-08-11
    相关资源
    最近更新 更多