We had a cluster environment which we needed to update the /etc/hosts
file. Which would help communicate between the server over a private network. Our servers have multiple interfaces and we need them to communicate between each other using the private network.
Goal for us is to create a /etc/hosts
with all the nodes within the cluster with their private IP addresses.
Chef Setup (Assumptions).
- We have multiple cluster.
- Each cluster has an environment set.
- We have a multiple interfaces on each node (But this solutions should work for single interface as well).
Steps we do to get information from each node.
- We take 3 attributes to look for in the cluster.
- Each server with the cluster have specific
fqdn
. - Private IP (string search).
- Interface to look for on the node.
- Each server with the cluster have specific
all_nodes_in_cluster
will get all the nodes with thatfqdn
(This can be change based on requirement tonode
,tags
,roles
,hostname
).- For every node we look for the specific interface and get the private IP.
Before we start.
Before we start we need to check for our search criteria using knife search
more details here
Below if an example to look for server fqdn
containing string env-lab-1
.
knife search node "fqdn:*env-lab-1*"
About the Recipe.
The interface information on hash is located as below
node_in_cluster['network']['interfaces']['bond.007']['addresses']
This has a Dictionary
with multiple values, we are specifically looking for IP
.
if private_interface[0].include? node['private_ip_search_filter']
Above we are looking for the interface which matches out search filter. Required information is in private_interface[0]
Here is how we would write the information in our /etc/hosts
file, IP
,FQDN
,HOSTNAME
.
puts "#{private_interface[0]} #{node_in_cluster['fqdn']} #{node_in_cluster['hostname']}"
Here is complete ruby
code which does the same thing as in the erb
template file.
all_nodes_in_cluster.each do |node_in_cluster|
node_in_cluster['network']['interfaces'][int_to_look]['addresses'].each do |private_interface|
if private_interface[0].include? node['private_ip_search_filter']
puts "#{private_interface[0]} #{node_in_cluster['fqdn']} #{node_in_cluster['hostname']}"
end
end
end
Attribute File.
# Example : We take the search Criteria to generate /etc/hosts
default['env_search_filter'] = "fqdn:*lab-env-1*"
default['private_ip_search_filter'] = "192.168.149"
default['interface_to_look'] = 'bond.007'
Recipe
# Search Criteria
all_nodes_in_cluster = search(:node, node['env_search_filter'])
int_to_look = node['interface_to_look']
template '/etc/hosts' do
source 'etc_hosts_file.erb'
mode '0755'
owner 'root'
group 'root'
variables({
all_nodes_in_cluster: all_nodes_in_cluster,
int_to_look: int_to_look,
private_ip_search_filter: node['private_ip_search_filter']
})
end
Template File etc_hosts_file.erb
.
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
<% @all_nodes_in_cluster.each do |node_in_cluster| -%>
<% node_in_cluster['network']['interfaces'][@int_to_look]['addresses'].each do |private_interface| -%>
<% if private_interface[0].include? @private_ip_search_filter -%>
<%= private_interface[0] %> <%= node_in_cluster['fqdn'] %> <%= node_in_cluster['hostname'] %> # Serial Number: <%= node_in_cluster['dmi']['system']['serial_number'] %> ( <%= node_in_cluster['dmi']['system']['manufacturer'] %> ) <%= node_in_cluster['dmi']['system']['product_name'] %>
<% end -%>
<% end -%>
<% end -%>
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
Disclaimer.
- This does not look like an optimized solution, but something which worked for me.
search
method which will be run every 30mins, will query to get all information for all the nodes, which I think would be time/bandwidth consuming operation if we have a very large cluster. (A single node information was about 10000 lines of rubyHash
for our nodes).- If any one has a better way to do it, please post it in the comments below. Thanks :)