Google information on
Ansible , stumbled upon
an article on Habré. I read it and was very surprised: you can make it more beautiful! If you are interested - welcome under the cat!
Immediately I will note that everything works on a real project (the number of nodes is more than 80, but less than 100).
Here are the tricky tasks with unknowns that are encountered by at least one in three, and maybe the second DevOps.
- As a parameter of the configuration file, use previously unknown node addresses, and these nodes belong to another role ( solution )
- To generate inventory from unknown addresses of nodes dynamically - through a call to AWS ( solution )
- Create the Nginx configuration file, which should contain previously unknown addresses of the backend nodes ( solution )
So first, the simplest:
Substitution of node addresses
Take an example: we have a group of “app_nodes” nodes, on which we put some kind of application using the following task file:
')
application.yml ---
- hosts: app_nodes
gather_facts: yes
roles:
- role: common
- role: application
Imagine that we have a ZooKeeper service, and in the configuration file of the application you need to write a setting for working with this service:
zk.connect=127.0.0.1:2181,127.0.0.2:2181,127.0.0.3:2181,127.0.0.4:2181, 127.0.0.5:2181
It is clear that the pens prescribe the addresses of all, for example, five nodes, each of which is installed ZooKeeper, on the nodes with the application - no pleasure. What can we do to make Ansible insert all the addresses of these nodes into the configuration file template?
Nothing special. Ansible uses the Jinja2 templating engine over YAML, so we use the templating loop:
zk.connect={% for host in groups['zk_nodes'] %}{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}:{{ zk_port }}, {% endfor %}
As a result, after the work of the template engine, the required line should turn out, but here's the bad luck: we work with the app_nodes nodes, and we use information (“facts”) about the zk_nodes nodes in this template. How to get the addresses of the zk_nodes nodes if in this task file we don’t work at all with these nodes? Let us assign to this particular group of nodes (zk_nodes) an empty list of tasks:
application.yml ---
- hosts: zk_nodes
gather_facts: yes
tasks: []
- hosts: app_nodes
gather_facts: yes
roles:
- role: common
- role: application
For this task file to work, the necessary groups of nodes must be specified in the inventory file:
environments / test / inventory[zk_nodes]
127.0.0.1
127.0.0.2
127.0.0.3
127.0.0.4
127.0.0.5
[app_nodes]
127.0.0.10
127.0.0.11
127.0.0.12
And what to do if the host addresses are unknown in advance? For example, are virtual machines used in EC2? Here we smoothly proceed to answer the second question.
Work with dynamic inventory
Information on this topic on the Internet is not too much - as I understand it, due to the existence of Ansible Tower.
So, you have a number of nodes in EC2, and you want to manage them centrally and easily. One possible solution is to use the Ansible + dynamic inventory script.
Here is the structure of the corresponding inventory directory:
tree ./environments/dynamic .
2.── ec2.ini
2.── ec2.py
Groups── groups
Group── group_vars
All── all
Z── zk_nodes
Prox── proxies
App── app_nodes
Here:
ec2.py - the script itself, you can take it
here , the link is direct;
ec2.ini is a file with script settings, you can take it
here , the link is direct;
groups is a file describing the groups of nodes with which you intend to work in this inventory,
group_vars is a directory containing the values of variables for each specific group, and common to all.
Next, under the spoiler, those settings that I have differ from the ini-file by the link:
Ec2.ini changed options# regions where our nodes are located
regions = us-east-5, us-west-2
# work only with live nodes
instance_states = running
# all parameters group_by _.... are commented out, except one:
group_by_tag_keys = true
# We select which nodes will enter our inventory. In this case, those that have a “Name” tag with the specified values.
instance_filters = tag: Name = zk_node, tag: Name = app_node, tag: Name = proxy
In order for Ansible to correctly recognize these groups and nodes, we write the
groups file:
groups file [all: children]
zk_nodes
proxies
app_nodes
[zk_nodes: children]
tag_Name_zk_node
[proxies: children]
tag_Name_proxy
[app_nodes: children]
tag_Name_app_node
[tag_Name_zk_node]
[tag_Name_proxy]
[tag_Name_app_node]
Here we inform Ansible that the
all node group with which it works by default contains nested groups
zk_nodes ,
proxies ,
app_nodes . We further inform that these groups also have nested groups that are dynamically formed, and therefore nodes are not indicated in their descriptions at all. Such is black magic - during its work, the dynamic inventory script will create groups of the form tag_ <Tag name> _ <Tag value> and fill these groups with nodes, and then you can work with these groups using the usual Ansible tools.
Yes, immediately on the
group_vars directory. It is automagically read by Ansible when loading inventory, and each group in this inventory gets the variables from the
group_vars / group_name file. One example of use is a separate key for a specific group of nodes:
group_vars / zk_nodesansible_ssh_private_key_file: "/tmp/key.pem"
Having considered dynamic inventory, we can elegantly solve the third problem:
Formation of the Nginx configuration file
It is clear that the Nginx configuration template does not surprise anyone on Habré, so I’ll confine myself to an
upstream block with explanations.
nginx.conf upstreamupstream app_nodes {
{% for item in groups ['app_nodes']%} server {{hostvars [item] ['ec2_private_ip_address']}}: {{app_port}};
{% endfor%}
keepalive 600;
}
This configuration block defines a group of upstream servers with different addresses and a common port number.
The template engine will run through all the nodes of the app_nodes group, generating a line for each node. It turns out like this
Result example upstream app_nodes {
127.0.0.1 true;
127.0.0.2:37000;
127.0.0.3.3000;
127.0.0.4.1000;
keepalive 600;
}
Here, the difference from the situation with the first
solution is that there is no need to additionally handle the empty list of tasks for the group of nodes “app_nodes” - this group is automatically created, among others, according to the
groups file given above, thanks to the dynamic inventory script. Well and, of course, the address on internal addresses VPC is used.
Afterword
Names of environments, tasks, inventory, nodes, IP addresses are replaced with fictional ones. Any matches are random. Where names of files or directories are important for a functional, explanations are given why they are so called.
Remember, you and only you are responsible for the status of your projects to the manager, customer and your own conscience. I do not pretend to be complete. I hope this article will save someone time, and someone will push in the right direction. To the best of my ability and understanding, I will answer questions in the comments.