Не вдаваясь глубоко в сорняки, Nginx заставляет мою руку совершить некую магию с vhosts и map
директива.
Есть ли элегантное (относительное) решение для совместного использования переменной несколькими вызовами определения, которое позволяет каждому вызову определения добавлять данные в глобальную переменную? В программном обеспечении это будет известно как синглтон.
- Сорняки -
Nginx имеет map
директива, которая определяет, в какой пул серверов восходящего направления следует передать запрос, например:
map $http_host $upstream_pool {
hostnames;
blog.example.com blog_example_com;
repo.example.com repo_example_com;
default example_com;
}
Как видите, любые запросы для blog.example.com будут передаваться в пул вышестоящих серверов blog_example_com (посредством proxy_pass
).
Проблема в том map
синтаксис директивы таков, что он может быть включен только в основной http
block (nginx.conf), тогда как специфические директивы vhost, такие как upstream
и location
может быть включен в server
блок конфига vhost.
Мой манифест node.pp выглядит примерно так:
service-a-1.example.com inherits project_dev {
nginx::vhost { 'mothership': }
nginx::vhost { 'mothership_blog': }
nginx::vhost { 'repo': }
}
Как видите, после успешного запуска марионетки я должен получить 3 отдельных файла конфигурации vhost в /etc/nginx/vhost.d/
реж.
Проблема, с которой я столкнулся, заключается в том, что для того, чтобы map
для работы, мне нужно знать, какие vhosts были загружены, поэтому я могу добавить их соответствующие идентификаторы восходящего потока в map
директива, которую я определил в основной конфигурации: /etc/nginx/nginx.conf
в пределах http
блок (из которых может быть только один).
- Что я пробовал --
У меня есть файл global.pp, который выполняет некоторую "загрузку", и в этот файл я добавил $singleton = ''
синтаксис, то в определении nginx :: vhost я добавил этот синтаксис:
$tpl_upstream_pool_labels = inline_template("<% tpl_upstream_pools.keys.sort.each do |role| %><%= role %>_<%= tpl_domain_primary %>_<%= tpl_domain_top_level %>|<% end %>")
$singleton = "${singleton}${tpl_upstream_pool_labels}"
notify { "\n--------------------- Nginx::Conf::Vhost::Touch | Timestamp: ${timestamp} | Pool labels: ${singleton} -------------------------\n": }
В результате должен получиться список идентификаторов восходящего потока, разделенных вертикальной чертой. Как упоминалось ранее, в манифесте nodes.pp я делаю три вызова nginx :: vhost и ожидаю, что $singleton
глобальная переменная, которая будет добавляться для каждого вызова, но это не так, она содержит только данные последнего вызова.
Я также попытался обойти это, написав временный файл следующим образом:
$temp_file_upstream_pool_labels_uri = "/tmp/puppet_known_upstreams_${timestamp}.txt"
exec { "event_record_known_upstream_${name}" :
command => "touch ${temp_file_upstream_pool_labels_uri} && echo ${tpl_upstream_pool_labels} >> ${temp_file_upstream_pool_labels_uri}",
provider => 'shell'
}
Затем в определении nginx :: conf :: touch, где основная конфигурация nginx.conf должна быть написана марионеткой, я попробовал следующее:
$temp_file_upstream_pool_labels_uri = "/tmp/puppet_known_upstreams_${timestamp}.txt"
$contents = file($temp_file_upstream_pool_labels_uri)
Теоретически это должно загрузить содержимое файла в переменную $ contents. Но когда я запускаю марионетку, используя этот подход, я получаю сообщение об ошибке, что файл не существует. Я убедился, что вызов nginx :: conf :: touch не будет выполняться до тех пор, пока не будут рассмотрены все vhosts, но все равно безрезультатно.
Добавляем еще один ответ, чтобы вопросы были разделены.
Подход, сочетающий exec
и file()
ошибочен, потому что они работают на самых разных уровнях.
В exec
ресурс добавляется в каталог и отправляется агенту, где он оценивается после завершения компиляции. Поскольку вы пытаетесь собрать информацию в течение время компиляции (перед тем, как собрать его с помощью file()
функция), вы не можете полагаться ни на какие ресурсы.
Должно быть возможно построить то, что вы задумали, полагаясь на генерировать функция вместо exec
Ресурсы.
Проблема с глобальными переменными в Puppet заключается в том, что вы не можете добавить к ним. В +=
синтаксис разрешен, но он создает локальная копия глобального значения с добавленным значением правой стороны.
Я реализовал шаблон, который делает то, что вы хотите, но я не горжусь им и не могу рекомендовать его использовать. Тем не менее, раз уж вы спрашиваете, вот что:
class nginx::conf {
$global = ''
file { '/etc/nginx/nginx.conf':
content => template(...)
}
}
Переменная из класса жестяная банка быть добавленным, по крайней мере, в 2.7.x
.
define nginx::vhost(...) {
include nginx::conf
$nginx::conf::global += "new content here"
nginx::override_conf_content { "for-vhost-$name": }
}
Магия происходит в загадочной последней строке, которая создает еще один экземпляр. define
.
define nginx::override_conf_content() {
include nginx::conf
$global = $nginx::conf::global
File<| title == '/etc/nginx/nginx.conf' |> {
content => template(...)
}
}
В content
собственность для nginx.conf
заменяется результатом другой оценки шаблона с новым значением переменной.
Полное раскрытие, я не уверен, почему это работает. Вероятно, этого не должно быть, и это может быть связано с неясной ошибкой. Он может перестать работать в будущих версиях.
Обратите внимание, что я назвал это $global
вместо того $singleton
, потому что это то, что есть. Синглтоны могут использоваться для реализации семантики глобальных объектов, но это не одно и то же.
Наконец, даже несмотря на то, что я чувствую вашу боль перед обновлением Puppet 3, вам действительно стоит потратить время и приступить к нему. Мы все, скорее всего, не сможем позволить себе бегать 2.x
намного длиннее.