Статьи

Краткий обзор некоторых элементов автоматизации платформы DataSift

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