{"id":87084,"date":"2020-07-03T13:42:34","date_gmt":"2020-07-03T11:42:34","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron"},"modified":"2020-07-03T13:42:34","modified_gmt":"2020-07-03T11:42:34","slug":"osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron","status":"publish","type":"post","link":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron","title":{"rendered":"The Essentials of Ansible, Without Which Your Playbooks Are Just a Blob of Stuck Together Macaroni","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>I conduct many code reviews on Ansible and write a lot myself. Through analyzing mistakes (both others' and my own), as well as a number of interviews, I realized the main error users make with Ansible is diving into the complex without mastering the basics.<\/p>\n<p><\/p>\n<p>To correct this universal injustice, I decided to write an introduction to Ansible for those who already know it. I warn you, this is not a recap of the manuals; it\u2019s a long read filled with words and devoid of pictures.<\/p>\n<p><\/p>\n<p>The expected reader level is that you have already written several thousand lines of YAML and have something in production, but \"something seems off.\"<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Names<\/h1>\n<p><\/p>\n<p>The main mistake of an Ansible user is not knowing the terminology. If you don't know the names, you can't understand what's written in the documentation. A live example: during an interview, a person who claimed to have written a lot in Ansible couldn't answer the question \"what are the elements of a playbook?\" When I hinted that \"the expected answer was that a playbook consists of plays,\" the devastating comment followed: \"we don't use that.\" People write in Ansible for money and don't use plays. <em>In reality, they do use them, but don\u2019t know what they are.<\/em><\/p>\n<p><\/p>\n<p>So let\u2019s start with the basics: what things are called. Maybe you already know this, or maybe you don\u2019t, because you didn\u2019t pay attention when reading the documentation.<\/p>\n<p><\/p>\n<p>ansible-playbook executes the playbook. A playbook is a file with the extension yml\/yaml, within which something like this exists:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">---\n- hosts: group1\n  roles:\n    - role1\n\n- hosts: group2,group3\n  tasks:\n    - debug:<\/code><\/pre>\n<p><\/p>\n<p>We\u2019ve already understood that the entire file is the playbook. We can show where the roles are and where the tasks are. But where is the play here? And how does play differ from role or playbook?<\/p>\n<p><\/p>\n<p>It's all in the documentation. And people overlook it. Beginners overlook it because there\u2019s too much information to memorize all at once. Experienced users overlook it because of its \"triviality.\" If you're experienced, reread those pages at least once every six months, and your code will improve significantly.<\/p>\n<p><\/p>\n<p>So remember: A playbook is a list consisting of plays and <code>import_playbook<\/code>.<br \/>\nHere is one play:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">- hosts: group1\n  roles:\n    - role1<\/code><\/pre>\n<p><\/p>\n<p>And here is another play:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">- hosts: group2,group3\n  tasks:\n    - debug:<\/code><\/pre>\n<p><\/p>\n<p>So what is a play? What is its purpose?<\/p>\n<p><\/p>\n<p>A play is a key element for a playbook because it is the play that connects the list of roles and\/or tasks with the list of hosts on which they need to be executed. Deep in the documentation, you can find a mention of <code>delegate_to<\/code>, local lookup plugins, network-cli-specific settings, jump hosts, etc. They allow slightly changing the execution location of tasks. But, forget about it. Each of these tricky options has very specific applications, and they are definitely not universal. We're talking about the basics that everyone should know and use.<\/p>\n<p><\/p>\n<p>If you want to execute \"something\" \"somewhere\" \u2014 you write a play. Not a role. Not a role with modules and delegates. You simply write a play. In which, in the hosts field, you list where to execute it, and in roles\/tasks \u2014 what to execute.<\/p>\n<p><\/p>\n<p>Simple, isn't it? How could it be otherwise?<\/p>\n<p><\/p>\n<p>One typical moment when people feel the urge to do this not through a play is \"a role that sets everything up.\" They want a role that configures and <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/en\/server\/dts-los-angeles\/\"   title=\"server\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"3633\">server<\/a> type one and type two servers.<\/p>\n<p><\/p>\n<p>An archetypical example is monitoring. You want to have a monitoring role that sets up monitoring. The monitoring role is assigned to the monitoring hosts (in the corresponding play). But it turns out we need to install packages on the hosts we are monitoring. Why not use a delegate? And we also need to configure iptables. Delegate? We also need to write\/update the config for the DBMS to allow monitoring. Delegate! And if creativity kicks in, it can turn into delegating <code>include_role<\/code> in a nested loop through a clever filter on the group list, and inside <code>include_role<\/code> you can still do <code>delegate_to<\/code> starts over again. And here we go\u2026<\/p>\n<p><\/p>\n<p>The well-intentioned wish to have a single monitoring role that \"does everything\" leads us to a chaotic hell from which the only exit is usually to rewrite everything from scratch.<\/p>\n<p><\/p>\n<p>Where did the mistake happen? At the moment you realized that to perform task \"x\" on host X you needed to go to host Y and do \"y\" there, you should have done a simple exercise: go and write a play that does y on host Y. Not to add something to \"x,\" but to write it from scratch, even if it means hardcoding variables.<\/p>\n<p><\/p>\n<p>It seems that everything mentioned in the previous paragraphs is correct. But this is not your case! Because you want to write reusable code that is DRY and resembles a library, and you need to find a way to do that.<\/p>\n<p><\/p>\n<p>Here lies another serious mistake. A mistake that turned many projects from tolerably written (it could be better, but everything works and it's easy to extend) into a complete horror, where even the author can't make sense of it. It works, but heaven forbid you try to change something.<\/p>\n<p><\/p>\n<p>This mistake sounds like this: a role is a library function. This analogy has doomed so many good initiatives that it's simply sad to watch. A role is not a library function. It cannot perform calculations and it cannot make decisions at the play level. Remind me, what decisions does play make?<\/p>\n<p><\/p>\n<p>Thank you, you are right. Play makes a decision (more precisely, it contains information) about which tasks and roles to execute on which hosts.<\/p>\n<p><\/p>\n<p>If you delegate this decision to a role, especially with calculations involved, you are condemning yourself (and anyone who will try to decipher your code) to a miserable existence. A role does not determine where it runs. That decision is made by play. A role does what it is told, where it is told.<\/p>\n<p><\/p>\n<p>Why programming in Ansible is dangerous and why COBOL is better than Ansible will be discussed in the chapter about variables and jinja. For now, let's say one thing \u2014 every computation you perform leaves an indelible mark from changing global variables, and you can't do anything about it. Once two \"marks\" intersect \u2014 it's all over.<\/p>\n<p><\/p>\n<p>Note for the nitpickers: a role can certainly influence control flow. There are <code>delegate_to<\/code> and it has reasonable applications. There are <code>meta: end host\/play<\/code>. But! Remember, we are learning the basics? Forgot about <code>delegate_to<\/code>. We are talking about the simplest and most beautiful code in Ansible. Code that is easy to read, easy to write, easy to debug, easy to test, and easy to extend. So, once again:<\/p>\n<p><\/p>\n<p><strong>play and only play decides on which hosts what is executed.<\/strong><\/p>\n<p><\/p>\n<p>In this section, we have dealt with the opposition between play and role. Now let's talk about the relationship between tasks and role.<\/p>\n<p><\/p>\n<h1>Tasks and Roles<\/h1>\n<p><\/p>\n<p>Let\u2019s consider play:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">- hosts: somegroup\n  pre_tasks:\n    - some_tasks1:\n  roles:\n     - role1\n     - role2\n  post_tasks:\n     - some_task2:\n     - some_task3:<\/code><\/pre>\n<p><\/p>\n<p>Suppose you need to do foo. It looks like <code>foo: name=foobar state=present<\/code>. Where to write this? In pre? Post? Create a role?<\/p>\n<p><\/p>\n<p>\u2026 And where did the tasks go?<\/p>\n<p><\/p>\n<p>We are starting again with the basics \u2014 the play structure. If you are not familiar with this topic, you cannot use play as the foundation for everything else, and your results will be 'unstable.'<\/p>\n<p><\/p>\n<p>Playbook: hosts directive, play settings, and sections pre_tasks, tasks, roles, post_tasks. Other parameters for play are not important to us at this time.<\/p>\n<p><\/p>\n<p>The order of their sections with tasks and roles: <code>pre_tasks<\/code>, <code>roles<\/code>, <code>tasks<\/code>, <code>post_tasks<\/code>. Since the semantic order of execution is <code>tasks<\/code> and <code>roles<\/code> unclear, best practices suggest adding the section <code>tasks<\/code>, only if there is no <code>roles<\/code>. If there is <code>roles<\/code>, then all accompanying tasks are placed in the sections <code>pre_tasks<\/code>\/<code>post_tasks<\/code>.<\/p>\n<p><\/p>\n<p>Only what is semantically clear remains: first <code>pre_tasks<\/code>, then <code>roles<\/code>, then <code>post_tasks<\/code>.<\/p>\n<p><\/p>\n<p>But we still haven't answered the question: where should the module call <code>foo<\/code> be written? Do we need to write a whole role for each module? Or is it better to have a broad role for everything? If not a role, then where to write it \u2014 in pre or post?<\/p>\n<p><\/p>\n<p>If there are no well-reasoned answers to these questions, it indicates a lack of intuition, meaning those 'unstable foundations.' Let's get into it. First, a control question: If play has <code>pre_tasks<\/code> and <code>post_tasks<\/code> (and there are neither tasks nor roles), can anything break if I move the first task from <code>post_tasks<\/code> to the end of <code>pre_tasks<\/code>?<\/p>\n<p><\/p>\n<p>Naturally, the phrasing of the question suggests that it will break. But what exactly?<\/p>\n<p><\/p>\n<p>\u2026 Handlers. Understanding the basics reveals an important fact: all handlers are flushed automatically after each section. That is, all tasks from <code>pre_tasks<\/code>are executed, then all handlers that were notified. Then all roles and all handlers that were notified in the roles are executed. Then <code>post_tasks<\/code> and their handlers.<\/p>\n<p><\/p>\n<p>Thus, if you move a task from <code>post_tasks<\/code> downward API support (simultaneously with this in <code>pre_tasks<\/code>, then you would potentially execute it before the handler runs. For example, if in <code>pre_tasks<\/code> something is installed and configured, and something is sent to it in <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/en\/server\/\"   title=\"web server\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"1155\">web server<\/a>, then moving this task to the section <code>post_tasks<\/code> will lead to the moment when 'sending' occurs, and the server will not yet be started, causing everything to break. <code>pre_tasks<\/code> it will lead to a situation where at the moment of 'sending' the server will not yet be running, and everything will break.<\/p>\n<p><\/p>\n<p>to allow us to work with the results of the execution of roles (including handlers). <code>pre_tasks<\/code> and <code>post_tasks<\/code>? \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0432\u0441\u0451 \u043d\u0443\u0436\u043d\u043e\u0435 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u044b) \u0434\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u043e\u043b\u0438. \u0410 <code>post_tasks<\/code> A meticulous Ansible expert will tell us that there is<\/p>\n<p><\/p>\n<p>meta: flush_handlers <code>, but why do we need flush_handlers if we can rely on the order of execution of sections in the play? Moreover, using meta: flush_handlers might give us unexpected results with repeating handlers, causing strange warnings in cases of using<\/code>when <code>block<\/code> at <code>, etc. The better you know Ansible, the more nuances you will be able to name for a 'crafty' solution. A simple solution \u2014 using natural separation between pre\/roles\/post \u2014 does not raise nuances.<\/code> etc. The better you know Ansible, the more nuances you can identify for a 'clever' solution. A simple solution \u2014 using natural separation between pre\/roles\/post \u2014 does not raise any nuances.<\/p>\n<p><\/p>\n<p>And returning to our 'foo.' Where should we place it? In pre, post, or roles? Obviously, this depends on whether we need the results of the handler's work for foo. If not, then foo does not need to go into either pre or post \u2014 these sections have a special meaning \u2014 executing tasks before and after the main array of code.<\/p>\n<p><\/p>\n<p>Now the answer to the question 'role or task' comes down to what already exists in play \u2014 if there are tasks there, then you need to add it to tasks. If there are roles, you should create a role (even from a single task). I remind you, tasks and roles are not used simultaneously.<\/p>\n<p><\/p>\n<p>Understanding the basics of Ansible provides well-founded answers to what seem like subjective questions.<\/p>\n<p><\/p>\n<h1>Tasks and Roles (Part Two)<\/h1>\n<p><\/p>\n<p>Now let's discuss the situation when you are just starting to write a playbook. You need to do foo, bar, and baz. Is that three tasks, one role, or three roles? To generalize the question: at what point should roles be introduced? What\u2019s the point of writing roles when tasks can be written?\u2026 And what is a role?<\/p>\n<p><\/p>\n<p>One of the biggest mistakes (I have mentioned this before) is to think that a role is like a function in a program\u2019s library. What does a generalized description of a function look like? It takes arguments as input, interacts with side causes, produces side effects, and returns a value.<\/p>\n<p><\/p>\n<p>Now, attention. What can be done in a role? Calling side effects \u2014 always possible, that is the essence of all Ansible \u2014 to create side effects. Having side causes? Elementary. However, for 'passing a value and returning it' \u2014 here we encounter difficulties. Firstly, you cannot pass a value to a role. You can set a global variable with a lifetime extending through the play in the vars section for the role. You can set a global variable with a lifetime in the play within the role. Or even with a playbook lifetime (<code>set_fact<\/code>\/<code>register<\/code>). But you cannot have 'local variables.' You cannot 'accept a value' and 'return it.'<\/p>\n<p><\/p>\n<p>From this follows an important point: you cannot write something in Ansible that doesn't trigger side effects. Changing global variables is always a side effect for a function. In Rust, for example, changing a global variable is <code>unsafe<\/code>In Ansible, there is a single way to influence the values for a role. Note the wording used: not 'pass a value to the role,' but 'change the values that the role uses.' There is no isolation between roles. There is no isolation between tasks and roles.<\/p>\n<p><\/p>\n<p>Total: <strong>A role is not a function.<\/strong>.<\/p>\n<p><\/p>\n<p>What is good about roles? First of all, roles have default values (<code>\/default\/main.yaml<\/code>), and secondly, roles have additional directories for storing files.<\/p>\n<p><\/p>\n<p>What are the advantages of default values? In Maslow's rather twisted pyramid of variable priority in Ansible, role defaults are the least prioritized (except for command-line parameters in Ansible). This means that if you need to provide default values without worrying about them overriding values from inventory or group variables, role defaults are the only correct place for you. (I'm slightly misleading\u2014there's also <code>|d(your_default_here)<\/code>, but when it comes to static locations\u2014it's only role defaults).<\/p>\n<p><\/p>\n<p>What else is good about roles? They have their own directories. These are directories for variables, both static (i.e., computed for the role) and dynamic (there's a sort of pattern or anti-pattern\u2014 <code>include_vars<\/code> together with <code>{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml<\/code>.). These directories are for <code>files\/<\/code>, <code>templates\/. Additionally, it allows having roles with their own modules and plugins (<\/code>library\/<code>). But, compared to tasks in playbooks (which can also have all this), the benefit here is only that files are not dumped in one big pile, but in several separate piles.<\/code>). However, compared to tasks in the playbook (which can also have all this), the benefit here is only that the files are not all thrown into one heap, but rather into several separate heaps.<\/p>\n<p><\/p>\n<p>Thus, roles have two important features: they have defaults (a unique feature) and they allow structuring code.<\/p>\n<p><\/p>\n<p>Returning to the original question: when to make tasks and when to make roles? Tasks in playbooks are most often used either as 'glue' before\/after roles or as independent building blocks (in which case there should be no roles in the code). A mix of normal tasks with roles is undoubtedly messy. It is advisable to stick to a specific style\u2014either tasks or roles. Roles offer separation of entities and defaults, while tasks allow for faster code readability. Typically, more 'static' (important and complex) code is moved to roles, while auxiliary scripts are written in the tasks style.<\/p>\n<p><\/p>\n<p>Returning to the original question: when to use tasks and when to use roles? Tasks in a playbook are most often used either as 'glue' before\/after roles, or as standalone building blocks (in which case there should be no roles in the code). A jumble of normal tasks mixed with roles is undoubtedly untidy. One should adhere to a specific style \u2014 either tasks or roles. Roles provide entity separation and defaults, while tasks allow for faster code reading. Typically, you extract the more 'static' (important and complex) code into roles and write helper scripts in a task style.<\/p>\n<p><\/p>\n<p>It is possible to do import_role as a task, but if you are writing this, be prepared to explain yourself about why you want to do it.<\/p>\n<p><\/p>\n<p>A nitpicking reader might say that roles can import roles, that roles can have dependencies through galaxy.yml, and there\u2019s also the scary and terrible <code>include_role<\/code> \u2014 I remind you, we are enhancing skills in basic Ansible, not in rhythmic gymnastics.<\/p>\n<p><\/p>\n<h1>Handlers and Tasks<\/h1>\n<p><\/p>\n<p>Let\u2019s discuss one more obvious thing: handlers. Knowing how to use them correctly is almost an art. What is the difference between a handler and a task?<\/p>\n<p><\/p>\n<p>Since we are recalling the basics, here is an example:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">- hosts: group1\n  tasks:\n    - foo:\n      notify: handler1\n  handlers:\n     - name: handler1\n       bar:<\/code><\/pre>\n<p><\/p>\n<p>Handlers for a role are located in rolename\/handlers\/main.yaml. Handlers are shared among all participants in the play: pre\/post_tasks can call the role's handlers, and a role can call handlers from plays. However, 'cross-role' handler calls cause much more confusion than simply repeating a trivial handler. (Another best practice element is to try to avoid repeating handler names.)<\/p>\n<p><\/p>\n<p>The main difference is that a task is executed (idempotently) always (plus\/minus tags and <code>block<\/code>), while a handler is triggered by a change in state (notify only triggers if there was a change). What are the implications of this? For example, if you run the play again and there were no changes, the handler will not run. But why might we need to execute a handler when there was no change in the triggering task? For instance, because something broke and there was a change, but the execution did not reach the handler. For example, because the network was temporarily down. The config changed, but the service was not restarted. On the next run, the config does not change and the service remains with the old version of the config.<\/p>\n<p><\/p>\n<p>The situation with the configuration is not resolvable (more precisely, one can invent a special restart protocol with file flags, etc., but that's no longer 'basic Ansible' in any form). However, there is another common story: we installed the application and recorded it <code>.service<\/code>-file, and now we want to <code>daemon_reload<\/code> and <code>state=started<\/code>And the natural place for this seems to be the handler. However, if it is made not a handler but a task at the end of the task list or a role, it will be idempotently executed every time. Even if the playbook breaks in the middle. This does not solve the problem of restarted (you cannot make a task with the restarted attribute, as idempotency is lost), but it is definitely worth doing state=started. The overall stability of the playbooks increases, as the number of dependencies and dynamic states decreases.<\/p>\n<p><\/p>\n<p>Another positive property of a handler is that it does not clutter the output. If there were no changes, there are no extra skipped or ok messages in the output \u2014 it's easier to read. This is also a negative property \u2014 if you find a typo in a linearly executed task on the first run, the handlers will only be executed if changed, i.e., under certain conditions \u2014 very rarely. For example, the first time in five years. And, of course, there will be a typo in the name, and everything will break. You won't be able to run them a second time \u2014 there is no changed state.<\/p>\n<p><\/p>\n<p>We must separately discuss the accessibility of variables. For example, if you notify a task with a loop, what will happen to the variables? You can guess analytically, but it is not always trivial, especially when variables come from different places.<\/p>\n<p><\/p>\n<p>Handlers are therefore much less useful and much more problematic than they seem. If something can be written nicely (without tricks) without handlers, it's better to do it without them. If it can't be done nicely, it's better to use them.<\/p>\n<p><\/p>\n<p>A meticulous reader will rightly point out that we have not discussed <code>listen<\/code>, that a handler can notify another handler, that a handler can include import_tasks (which can utilize include_role with with_items), that the system of handlers in Ansible is Turing-complete, that handlers from include_role intriguingly intersect with handlers from plays, etc. \u2014 all of this is clearly not &quot;the basics&quot;).<\/p>\n<p><\/p>\n<p>Although there is one particular WTF that is actually a feature and should be remembered. If you have a task executing with <code>delegate_to<\/code> and it has notify, then the corresponding handler is executed without <code>delegate_to<\/code>, i.e., on the host to which the play is assigned. (Although the handler can, of course, have <code>delegate_to<\/code> too).<\/p>\n<p><\/p>\n<p>I want to say a few words about reusable roles separately. Before collections appeared, there was an idea that universal roles could be created, which could be <code>ansible-galaxy install<\/code> and off I went. It works on all OSs in every scenario. My opinion is this: it does not work. Any role with mass support for 100,500 cases is doomed to the depths of corner case bugs. They can be patched through massive testing, but like any testing, you either have a Cartesian product of input values and a total function, or you have 'covered individual scenarios'. In my opinion, it's much better if the role is linear (cyclomatic complexity 1). <code>include_vars<\/code>, supporting an extraordinary number of cases is doomed to the depths of corner case bugs. They can be covered with extensive testing, but like with any testing, you either have a Cartesian product of input values and a total function, or you have &quot;certain scenarios covered&quot;. In my opinion, it\u2019s much better if the role is linear (cyclomatic complexity 1).<\/p>\n<p><\/p>\n<p>The fewer if statements (explicit or declarative \u2014 in the form of <code>block<\/code> by the set of variables), the better the role. Sometimes you have to create branches, but I repeat, the fewer there are, the better. So, seemingly a good role with galaxy (it works!) with a ton of <code>include_vars<\/code> may be less preferable than your own role with five tasks. The moment when a role with galaxy is better is when you start writing something. The moment it becomes worse is when something breaks, and you suspect it\u2019s due to 'the role with galaxy'. You open it, and there are five includes, eight task lists, and a stack of <code>block<\/code> ) may be less preferable than a &quot;custom&quot; role made up of five tasks. The moment when a galaxy role is better is when you are starting to write something. The moment it becomes worse is when something breaks, and you suspect it\u2019s due to the &quot;galaxy role&quot;. You open it up, and there are five inclusions, eight task lists, and a stack <code>block<\/code>\u2018s\u2026 And you need to sort this out. Instead of five tasks in a linear list where there\u2019s hardly anything that can break.<\/p>\n<p><\/p>\n<h1>A bit about inventory, group variables, host_group_vars plugin, hostvars. How to tie spaghetti into a Gordian knot. Scope and precedence of variables, Ansible's memory model. 'So where should we actually store the database username?'<\/h1>\n<p><\/p>\n<ul>\n<li>A bit about inventory, group variables, host_group_vars plugin, hostvars. How to turn spaghetti into a Gordian knot. Scope and precedence of variables, the Ansible memory model. &quot;So where should we store the database username?&quot;.<\/li>\n<li><code>\u2014 nosql notype nonsensical soft clay. It's everywhere, even where you least expect it. A bit about<\/code> !!unsafe <code>and tasty yaml.<\/code> Release of the openSUSE Leap 15.2 distribution<\/li>\n<\/ul>\n<p>Source: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/508762\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u042f \u0434\u0435\u043b\u0430\u044e \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0432\u044c\u044e \u0434\u043b\u044f \u0447\u0443\u0436\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0430 \u0410\u043d\u0441\u0438\u0431\u043b \u0438 \u043c\u043d\u043e\u0433\u043e \u043f\u0438\u0448\u0443 \u0441\u0430\u043c. \u0412 \u0445\u043e\u0434\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u043e\u0448\u0438\u0431\u043e\u043a (\u043a\u0430\u043a \u0447\u0443\u0436\u0438\u0445, \u0442\u0430\u043a \u0438 \u0441\u0432\u043e\u0438\u0445), \u0430 \u0442\u0430\u043a \u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0439, \u044f \u043f\u043e\u043d\u044f\u043b \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u043e\u0448\u0438\u0431\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u044e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0410\u043d\u0441\u0438\u0431\u043b\u0430 \u2014 \u043e\u043d\u0438 \u043b\u0435\u0437\u0443\u0442 \u0432 \u0441\u043b\u043e\u0436\u043d\u043e\u0435, \u043d\u0435 \u043e\u0441\u0432\u043e\u0438\u0432 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e. \u0414\u043b\u044f \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u0432\u0441\u0435\u043b\u0435\u043d\u0441\u043a\u043e\u0439 \u043d\u0435\u0441\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u043e\u0441\u0442\u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0432 \u0410\u043d\u0441\u0438\u0431\u043b [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-87084","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u042f \u0434\u0435\u043b\u0430\u044e \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0432\u044c\u044e \u0434\u043b\u044f \u0447\u0443\u0436\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0430 \u0410\u043d\u0441\u0438\u0431\u043b \u0438 \u043c\u043d\u043e\u0433\u043e \u043f\u0438\u0448\u0443 \u0441\u0430\u043c.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u0441\u043d\u043e\u0432\u044b Ansible, \u0431\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u0430\u0448\u0438 \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0438 \u2014 \u043a\u043e\u043c\u043e\u043a \u0441\u043b\u0438\u043f\u0448\u0438\u0445\u0441\u044f \u043c\u0430\u043a\u0430\u0440\u043e\u043d | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u042f \u0434\u0435\u043b\u0430\u044e \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0432\u044c\u044e \u0434\u043b\u044f \u0447\u0443\u0436\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0430 \u0410\u043d\u0441\u0438\u0431\u043b \u0438 \u043c\u043d\u043e\u0433\u043e \u043f\u0438\u0448\u0443 \u0441\u0430\u043c.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-07-03T11:42:34+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-07-03T11:42:34+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"I create many reviews for other people's Ansible code and write a lot myself.","description":"\ud83e\udd47The fundamentals of Ansible, without which your playbooks are a lump of stuck-together noodles | ProHoster","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u0441\u043d\u043e\u0432\u044b Ansible, \u0431\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u0430\u0448\u0438 \u043f\u043b\u0435\u0439\u0431\u0443\u043a\u0438 \u2014 \u043a\u043e\u043c\u043e\u043a \u0441\u043b\u0438\u043f\u0448\u0438\u0445\u0441\u044f \u043c\u0430\u043a\u0430\u0440\u043e\u043d | ProHoster","og:description":"\u042f \u0434\u0435\u043b\u0430\u044e \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0432\u044c\u044e \u0434\u043b\u044f \u0447\u0443\u0436\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0430 \u0410\u043d\u0441\u0438\u0431\u043b \u0438 \u043c\u043d\u043e\u0433\u043e \u043f\u0438\u0448\u0443 \u0441\u0430\u043c.","og:url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-07-03T11:42:34+00:00","article:modified_time":"2020-07-03T11:42:34+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"87084","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 13:58:53","updated":"2026-02-22 15:29:30","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/87084","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=87084"}],"version-history":[{"count":2,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/87084\/revisions"}],"predecessor-version":[{"id":162161,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/87084\/revisions\/162161"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=87084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=87084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=87084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}