В команде 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!