В команде DataSift Operations мы стараемся максимально автоматизировать, чтобы у нас было больше времени сосредоточиться на более важных задачах. Я хотел бы поделиться некоторыми интересными фрагментами из наших рецептов Opscode Chef, которые также могут помочь вам сэкономить время.
Обратное делегирование DNS для активных IP-адресов
Обратное делегирование DNS позволяет приложениям сопоставлять доменное имя с IP-адресом. Обратное делегирование достигается путем использования специальных доменных имен in-addr.arpa (IPv4) и ip6.arpa (IPv6).
Для всех блоков IP-адресов, которые IANA выделяет RIPE NCC, они также делегируют соответствующие обратные зоны DNS в центрально управляемых зонах «in-addr.arpa» и «ip6.arpa».
RIPE NCC также публикует «фрагменты зон». Это части зон, управляемые другими сторонами — другими региональными интернет-регистратурами (RIR), которые совместно используют управление зонами сетей ранней регистрации.
— Страница RIPE NCC
об обратном делегировании
Поскольку у нас есть свои собственные блоки IP-адресов от RIPE, нам необходимо поддерживать DNS-сервер, который является авторитетным для нашего небольшого пространства имен in-addr.arpa, и, поскольку Chef располагает всей этой информацией в рамках сбора данных OHAI, мы можем Хорошо, пусть Шеф справится с этим для нас.
Сначала мы определим серию пакетов данных с некоторыми данными:
{
"id": "0-16-172-in-addr-arpa",
"subnet": "172.16.0",
"hosts": {
"1": "misc-172-16-0-1.networksaremadeofstring.net",
"2": "misc-172-16-0-2.networksaremadeofstring.net",
"3": "misc-172-16-0-3.networksaremadeofstring.net",
.....
"252": "misc-172-16-0-252.networksaremadeofstring.net",
"253": "misc-172-16-0-253.networksaremadeofstring.net",
"254": "misc-172-16-0-254.networksaremadeofstring.net"
}
}
Значения по умолчанию в базе данных затем перезаписываются полным доменным именем всех хостов в Chef, соответствующих поисковому запросу.
#Do our reverse zones
reverse_zones.uniq.sort.each do |zone|
records = data_bag_item("bind", zone.gsub(".","-"))
# At some point do a search if the IP address of a zone matches
# a host then over the records array with the name of this host
search(:node, "ipaddress:#{records['subnet']}*").each do |node|
records['hosts'][node[:ipaddress].split('.')[3]] = node[:fqdn]
end
t = Time.now
template "/var/named/master/db.#{zone}" do
source "master_reverse_zone.erb"
owner "named"
group "named"
mode 0644
variables (
:records => records['hosts'],
:arpaAddr => zone,
:serial => t.strftime("%Y%m%d%H"))#%H%M%S
notifies :restart, "service[named]"
end
end
Шаблон Chef довольно прост:
$TTL 3D @ IN SOA ns1.networksaremadeofstring.net. operations.networksaremadeofstring.com. ( <%= @serial %> ;serial 1800 ;refresh 600 ;retry 604800 ;expire 86400 ;minimum ) <%= @arpaAddr %>. IN NS ns1.networksaremadeofstring.net. <%= @arpaAddr %>. IN NS ns2.networksaremadeofstring.net. 0 IN PTR nwrk.networksaremadeofstring.net. <% @records.sort.each do |octet, ptr| %> <%= octet %> IN PTR <%= ptr %>. <% end %> 255 IN PTR bcst.networksaremadeofstring.net.
Аналогично, наши зоны прямого DNS также заполняются Chef, поэтому в течение 30 минут после начальной загрузки узла он находится в наших зонах прямого и обратного DNS, готовых без какого-либо вмешательства человека.
Мониторинг Zenoss
Мы полагаемся на Zenoss, чтобы следить за вещами, которые мы получаем бесплатно, такими как дисковое пространство, показатели ЦП и т. Д., Но очевидно, что мы не хотим сами добавлять хосты. Это было бы сумасшествием.
Вместо этого мы выполняем поиск по так называемой «базовой» роли, которая определяет поставщика центра обработки данных, сам центр обработки данных, а затем кабину. Каждая роль содержит ряд атрибутов, поэтому любой данный сервер будет иметь в качестве своих атрибутов свое точное местоположение, каналы ИБП / генератора и т. Д.
Эти базовые роли затем добавляются в Zenoss как организаторы локаций и вкладываются соответствующим образом.
ServerList = []
FacilityList = []
RackList = []
search(:role, "name:base_*").each do |role|
if role.default_attributes.has_key?("location") && (role.default_attributes["location"].has_key?("building") || role.default_attributes["location"].has_key?("city"))
Facility = Hash.new
Facility[:building] = role.default_attributes["location"]["building"]
Facility[:city] = role.default_attributes["location"]["city"]
Facility[:country] = role.default_attributes["location"]["country"]
Facility[:gps] = role.default_attributes["location"]["gps"]
Facility[:description] = role.description
Facility[:name] = role.name
FacilityList.push(Facility)
#Chef::Log.info "Found a facility: ( #{Facility[:name]} ) #{Facility[:building]}, #{Facility[:city]}, #{Facility[:country]}, #{Facility[:gps]}"
end
if role.default_attributes.has_key?("location") && role.default_attributes["location"].has_key?("rack")
Rack = Hash.new
Rack[:id] = role.default_attributes["location"]["rack"]
Rack[:role] = role.name
Rack[:description] = role.description
Rack[:facility] = role.run_list[0].name
RackList.push(Rack)
#Chef::Log.info "Found a rack: #{Rack[:id]} in #{Rack[:facility]}"
end
end
FacilityList.each do |facility|
Chef::Log.info "Adding Facility #{facility[:name]}, #{Facility[:building]}, #{Facility[:city]}, #{Facility[:country]} to Zenoss"
Name = facility[:name].gsub('base_','/')
zenoss_zendmd Name do
location Name
description facility[:description]
address "#{facility[:building]}, #{facility[:city]}, #{facility[:country]}"
action :location
end
#Lets see what racks are in this facility
RackList.each do |rack|
if(rack[:facility] == facility[:name])
Chef::Log.info "Found a Rack #{rack[:id]} in #{rack[:facility]}"
RackName = "#{Name}/#{rack[:id]}"
zenoss_zendmd RackName do
location RackName
description rack[:description]
address "#{Facility[:building]}, #{Facility[:city]}, #{Facility[:country]}"
action :location
end
#Now we find the servers in this cab
search(:node, "role:#{rack[:role]}").each do |server|
#PP.pp(nodes)
Chef::Log.info "\t\t Found server #{server["fqdn"]} / #{server.ipaddress} in environment #{server.chef_environment}"
Device = Hash.new
Device[:fqdn] = server["fqdn"]
Device[:ipaddress] = server.ipaddress
Device[:environment] = server.chef_environment
Device[:location] = RackName
if(server.chef_environment == "production" || server.chef_environment == "staging")
ServerList.push(Device)
end
end
end
end
end
#Now we do the heavyweight loading into Zenoss
zenoss_lightweight_batchload "zenbatchloading devices" do
devices ServerList
action :run
end
Поставщик пакетной загрузки просто создает текстовый файл, совместимый с процессом Zenoss zenbatchload, но затем выполняет diff, чтобы гарантировать, что пакетную загрузку получают только новые хосты, что обеспечивает быстрый запуск Chef, поскольку тысячи хостов не загружаются пакетно при каждом запуске:
#write the content to a temp file
file "/tmp/chefzenbatch.batch.new" do
owner "zenoss"
mode "0600"
content batch
action :create
end
file "/tmp/chefzenbatch.batch" do
owner "zenoss"
mode "0600"
content "/Server/Linux\n"
action :create
end
execute "check for diff" do
command "sort -o /tmp/chefzenbatch.batch.new /tmp/chefzenbatch.batch.new && diff /tmp/chefzenbatch.batch.new /tmp/chefzenbatch.batch.old | grep \"<\" | awk '{print $2 \" \" $3 \" \" $4}' >> /tmp/chefzenbatch.batch"
end
#run the command as the zenoss user
.......
execute "Set file for later diff" do
command "mv /tmp/chefzenbatch.batch.new /tmp/chefzenbatch.batch.old"
end
Arista Zero Touch Provisioning
Я много писал о том, как мы используем переключатели Arista, и, хотя я люблю их, написание всех настроек вручную может стать скучным, так почему бы не сделать так, чтобы Chef сделал все это для меня?
Как упоминалось в разделе Zenoss выше, мы используем роли Chef для определения стоечного шкафа, и как часть атрибутов для этой роли будет сетевой раздел, который выглядит примерно так:
"network" => {
"routing" => {
"network" => "172.16.0.0",
"mask" => "27"
}
"switch" => {
"name" => "ACS-AS-39",
"uplink1" => {
"ip" => "1.1.1.1/24",
"mac" => "aa:bb:cc:dd:ee:ff"
},
"uplink2" => {
"ip" => "2.2.2.2/24",
"mac" => 11:22:33:44:55:66"
},
"management" => {
"ip" => "3.3.3.3/24",
"mac" => 77:88:99:10:10:aa"
}
}
}
The ZTP cookbook can then create the relevant files for pulling down by the switch as detailed here.
I have to admit that I’ve not yet gone as far as to bootstrap a switch with Chef-Client and use the various Arista cookbooks.
Here’s one I made earlier…
There’s plenty of other stuff involved in our data center automation but the end result is that we can have entire cabs of servers (and their top of rack switches) up and running, monitored and in forward/reverse DNS by creating just one new Chef role and then booting them up.
At our scale that’s one hell of a time saver!