2013-03-14 51 views
34

在Nginx中,我試圖定義一個變量,它允許我爲所有位置塊配置一個子文件夾。我這樣做:在Nginx位置規則中使用變量

set $folder '/test'; 

location $folder/ { 
    [...] 
} 

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

不幸的是,這似乎並不奏效。雖然Nginx不會抱怨語法,但在請求/test/時會返回404。如果我明確寫入文件夾,它就會起作用。那麼如何在位置塊中使用變量?

回答

46

你不能。 Nginx的真的不支持配置文件中的變量,它的開發嘲笑添加大家誰問這個功能:

「[變量]是相當昂貴相比,純靜態配置[A]宏擴展和應使用「include」指令[例如sed + make或其他常用模板機制]。「 http://nginx.org/en/docs/faq/variables_in_config.html

您應該編寫或下載一個小工具,該工具將允許您從佔位符配置文件生成配置文件。

更新下面仍然代碼工作,但我已經把它包起來都成一個小PHP程序/庫調用ConfiguratorPackagist,這使得易產生的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 
+0

好了,感謝你的答案並分享您解決這一問題。 – tomka 2013-03-15 18:24:52

+0

感謝您的回覆。爲什麼開發人員不允許這些變量的特定原因? – 2015-04-20 15:59:25

+0

http:// nginx。org/en/docs/faq/variables_in_config.html「變量不應該作爲模板宏使用,變量在處理每個請求的過程中會在運行時進行評估,因此與純靜態配置相比,它們相當昂貴。存儲靜態字符串也是一個壞主意,相反,應該使用宏擴展和「include」指令更容易地生成配置,並且可以使用外部工具完成,例如sed + make或任何其他常用模板機制。 – Danack 2015-04-20 16:01:04

0

你可以做你提議的相反。

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

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

這會是什麼好處? – nus 2014-04-28 23:49:07

+0

我假設問題的作者試圖告訴Nginx他的應用程序期望的URL。我只是建議,而不是那樣做,Nginx可以告訴他的應用程序使用什麼網址來訪問它。匹配([^ /] +)比匹配(/ test)更有用,就像我給出的例子,但結果是一樣的。 – rstackhouse 2014-05-02 19:15:32

0

修正的python版本的@ danack的PHP生成腳本。它生成所有文件&生活在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) 
+0

如果您使用python,建議使用jinja模板恕我直言 – Boaz 2017-08-21 06:20:39