{"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\/es\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron","title":{"rendered":"Los fundamentos de Ansible, sin los cuales tus playbooks son un l\u00edo de pasta pegada","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hago muchas revisiones del c\u00f3digo de otras personas en Ansible y escribo bastante yo mismo. A trav\u00e9s del an\u00e1lisis de errores (tanto ajenos como propios), y algunos entrevistas, comprend\u00ed el error principal que cometen los usuarios de Ansible: se adentran en lo complejo sin haber dominado lo b\u00e1sico.<\/p>\n<p><\/p>\n<p>Para corregir esta injusticia universal, decid\u00ed escribir una introducci\u00f3n a Ansible para aquellos que ya lo conocen. Advierto que no es un resumen de los manuales, es un texto extenso con muchas letras y sin im\u00e1genes.<\/p>\n<p><\/p>\n<p>El nivel de lector esperado es aquel que ya ha escrito varios miles de l\u00edneas de YAML y tiene algo en producci\u00f3n, pero siente que \"todo est\u00e1 torcido\".<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1>Nombres<\/h1>\n<p><\/p>\n<p>El principal error de los usuarios de Ansible es no saber c\u00f3mo se llaman las cosas. Si no conoces los nombres, no puedes entender lo que dice la documentaci\u00f3n. Un ejemplo vivo: en una entrevista, una persona que supuestamente hab\u00eda escrito mucho en Ansible no pudo responder a la pregunta \"\u00bfde qu\u00e9 elementos consta un playbook?\". Cuando le suger\u00ed que \"se esperaba que respondiera que un playbook consta de plays\", recibi\u00f3 el comentario devastador \"nosotros no usamos eso\". La gente escribe en Ansible por dinero y no utiliza plays. <em>En realidad, se utilizan, pero no saben lo que son.<\/em><\/p>\n<p><\/p>\n<p>As\u00ed que empecemos por lo simple: c\u00f3mo se llaman las cosas. Tal vez ya lo sepas, o tal vez no, porque no prestaste atenci\u00f3n al leer la documentaci\u00f3n.<\/p>\n<p><\/p>\n<p>ansible-playbook ejecuta playbooks. Un playbook es un archivo con la extensi\u00f3n yml\/yaml, dentro del cual hay algo como esto:<\/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>Ya hemos comprendido que todo este archivo es un playbook. Podemos se\u00f1alar d\u00f3nde est\u00e1n los roles (roles), d\u00f3nde est\u00e1n las tareas (tasks). Pero, \u00bfd\u00f3nde est\u00e1 el play? \u00bfY en qu\u00e9 se diferencia un play de un role o un playbook?<\/p>\n<p><\/p>\n<p>Todo esto est\u00e1 en la documentaci\u00f3n. Y lo pasan por alto. Los principiantes lo hacen porque hay demasiada informaci\u00f3n y no pueden retenerla toda de una vez. Los experimentados, porque piensan que son \"cosas triviales\". Si eres experimentado, lee estas p\u00e1ginas al menos una vez cada seis meses, y tu c\u00f3digo mejorar\u00e1 dr\u00e1sticamente.<\/p>\n<p><\/p>\n<p>As\u00ed que recuerda: un playbook es una lista que consta de plays y <code>import_playbook<\/code>.<br \/>\nEste es un play:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">- hosts: group1\n  roles:\n    - role1<\/code><\/pre>\n<p><\/p>\n<p>Y este tambi\u00e9n es otro play:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">- hosts: group2,group3\n  tasks:\n    - debug:<\/code><\/pre>\n<p><\/p>\n<p>\u00bfQu\u00e9 es exactamente un play? \u00bfPara qu\u00e9 sirve?<\/p>\n<p><\/p>\n<p>Un play es un elemento clave para el playbook, porque el play y solo el play conecta la lista de roles y\/o tareas con la lista de hosts en los que deben ejecutarse. En las profundas entra\u00f1as de la documentaci\u00f3n, se puede encontrar una menci\u00f3n a <code>delegate_to<\/code>, plugins de b\u00fasqueda locales, configuraciones espec\u00edficas de network-cli, hosts de salto, etc. Permiten modificar ligeramente el lugar donde se ejecutan las tareas. Pero, olv\u00eddate de eso. Cada una de estas opciones ingeniosas tiene aplicaciones muy espec\u00edficas y no son universales. Y estamos hablando de las cosas b\u00e1sicas que todos deben saber y utilizar.<\/p>\n<p><\/p>\n<p>Si quieres \"hacer algo\" \"en alg\u00fan lugar\", escribes un play. No una rol. No una rol con m\u00f3dulos y delegados. Simplemente tomas y escribes un play. En el que, en el campo hosts, enumeras d\u00f3nde ejecutar, y en roles\/tareas, qu\u00e9 ejecutar.<\/p>\n<p><\/p>\n<p>\u00bfEs simple, verdad? \u00bfY c\u00f3mo podr\u00eda ser diferente?<\/p>\n<p><\/p>\n<p>Uno de los momentos caracter\u00edsticos en los que las personas desean hacer esto sin un play es \"una rol que lo configura todo\". Quieren tener una rol que configure y <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/es\/server\/dts-los-angeles\/\"   title=\"servidores\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"3633\">servidores<\/a> del primer tipo y servidores del segundo tipo.<\/p>\n<p><\/p>\n<p>Un ejemplo arquet\u00edpico es la monitorizaci\u00f3n. Se desea tener un rol de monitorizaci\u00f3n que configure la monitorizaci\u00f3n. El rol de monitorizaci\u00f3n se asigna a los hosts de monitorizaci\u00f3n (en el correspondiente play). Pero resulta que para la monitorizaci\u00f3n necesitamos instalar paquetes en los hosts que estamos monitorizando. \u00bfPor qu\u00e9 no usar un delegate? Adem\u00e1s, hay que configurar iptables. \u00bfdelegate? Y tambi\u00e9n hay que escribir\/corregir la configuraci\u00f3n para la base de datos, para que la monitorizaci\u00f3n funcione. \u00a1delegate! Y si surge la creatividad, se puede hacer delegaci\u00f3n <code>include_role<\/code> en un ciclo anidado con un filtro ingenioso en la lista de grupos, y dentro de <code>include_role<\/code> tambi\u00e9n se puede hacer <code>delegate_to<\/code> de nuevo. Y aqu\u00ed vamos...<\/p>\n<p><\/p>\n<p>El deseo loable es tener una \u00fanica rol de monitoreo que \"lo haga todo\", lo que nos lleva a un infierno del cual la mayor\u00eda de las veces solo hay una salida: reescribir todo desde cero.<\/p>\n<p><\/p>\n<p>\u00bfD\u00f3nde ocurri\u00f3 el error? En el momento en que te das cuenta de que para realizar la tarea \"x\" en el host X necesitas ir al host Y y hacer \"y\", deber\u00edas haber realizado un ejercicio simple: ir y escribir un play que en el host Y haga y. No agregar algo en \"x\", sino escribirlo desde cero. Aunque sea con variables codificadas.<\/p>\n<p><\/p>\n<p>Parece que en los p\u00e1rrafos anteriores todo se dijo correctamente. \u00a1Pero ese no es tu caso! Porque quieres escribir c\u00f3digo reutilizable que sea DRY y parezca una biblioteca, y necesitas buscar un m\u00e9todo para hacerlo.<\/p>\n<p><\/p>\n<p>Aqu\u00ed hay otro error grave oculto. Un error que ha convertido muchos proyectos de aceptablemente escritos (se puede mejorar, pero todo funciona y es f\u00e1cil de agregar) en un completo desastre, en el que incluso el autor no puede entenderse. Funciona, pero Dios no permita que cambies algo.<\/p>\n<p><\/p>\n<p>Este error se describe as\u00ed: un rol es una funci\u00f3n de biblioteca. Esta analog\u00eda ha arruinado tantos buenos comienzos que es triste verlo. Un rol no es una funci\u00f3n de biblioteca. No puede hacer c\u00e1lculos y no puede tomar decisiones a nivel de play. Recu\u00e9rdame, \u00bfqu\u00e9 decisiones toma play?<\/p>\n<p><\/p>\n<p>Gracias, tienes raz\u00f3n. Play toma decisiones (m\u00e1s bien, contiene informaci\u00f3n) sobre qu\u00e9 tareas y roles ejecutar en qu\u00e9 hosts.<\/p>\n<p><\/p>\n<p>Si delegas esa decisi\u00f3n a un rol, y adem\u00e1s con c\u00e1lculos, te condenas a ti mismo (y a quien intente desentra\u00f1ar tu c\u00f3digo) a una existencia miserable. Un rol no decide d\u00f3nde ejecutarse. Esa decisi\u00f3n la toma play. Un rol hace lo que se le dice, all\u00ed donde se le dice.<\/p>\n<p><\/p>\n<p>Hablaremos de por qu\u00e9 programar en Ansible es peligroso y de por qu\u00e9 COBOL es mejor que Ansible en el cap\u00edtulo sobre variables y Jinja. Por ahora, digamos una cosa: cada uno de tus c\u00e1lculos deja una huella imborrable de cambios en las variables globales, y no puedes hacer nada al respecto. En cuanto dos \"huellas\" se cruzan, todo se pierde.<\/p>\n<p><\/p>\n<p>Nota para los que son meticulosos: un rol, sin duda, puede influir en el control de flujo. Hay <code>delegate_to<\/code> y tiene aplicaciones razonables. Hay <code>meta: end host\/play<\/code>. \u00a1Pero! Recuerda, \u00bfestamos ense\u00f1ando lo b\u00e1sico? Olvidaste sobre <code>delegate_to<\/code>. Hablamos del c\u00f3digo m\u00e1s simple y hermoso en Ansible. Que es f\u00e1cil de leer, f\u00e1cil de escribir, f\u00e1cil de depurar, f\u00e1cil de probar y f\u00e1cil de agregar. As\u00ed que, de nuevo:<\/p>\n<p><\/p>\n<p><strong>play y solo play decide en qu\u00e9 hosts se ejecuta qu\u00e9.<\/strong><\/p>\n<p><\/p>\n<p>En esta secci\u00f3n hemos abordado la confrontaci\u00f3n entre play y rol. Ahora hablemos sobre la relaci\u00f3n entre tasks y roles.<\/p>\n<p><\/p>\n<h1>Tareas y Roles<\/h1>\n<p><\/p>\n<p>Consideremos 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>Supongamos que necesitas hacer foo. Y se ve as\u00ed <code>foo: name=foobar state=present<\/code>. \u00bfD\u00f3nde se escribe esto? \u00bfen pre? \u00bfpost? \u00bfCrear un rol?<\/p>\n<p><\/p>\n<p>\u2026 \u00bfY ad\u00f3nde fueron las tasks?<\/p>\n<p><\/p>\n<p>Volvemos a comenzar desde lo b\u00e1sico: el play. Si tienes dudas sobre este tema, no puedes usar el play como base para todo lo dem\u00e1s, y tu resultado ser\u00e1 \"inestable\".<\/p>\n<p><\/p>\n<p>Dispositivo play: directiva hosts, configuraciones del play y secciones pre_tasks, tasks, roles, post_tasks. Otros par\u00e1metros para el play no son importantes ahora.<\/p>\n<p><\/p>\n<p>El orden de sus secciones con tareas y roles: <code>pre_tasks<\/code>, <code>roles<\/code>, <code>tasks<\/code>, <code>post_tasks<\/code>. Dado que sem\u00e1nticamente el orden de ejecuci\u00f3n entre <code>tasks<\/code> y <code>roles<\/code> no es claro, las mejores pr\u00e1cticas indican que agregamos la secci\u00f3n <code>tasks<\/code>, solo si no hay <code>roles<\/code>. Si hay <code>roles<\/code>, entonces todas las tareas correspondientes se colocan en las secciones <code>pre_tasks<\/code>\/<code>post_tasks<\/code>.<\/p>\n<p><\/p>\n<p>Solo queda lo que sem\u00e1nticamente est\u00e1 claro: primero <code>pre_tasks<\/code>, luego <code>roles<\/code>, luego <code>post_tasks<\/code>.<\/p>\n<p><\/p>\n<p>Pero a\u00fan no hemos respondido la pregunta: \u00bfd\u00f3nde escribir la llamada al m\u00f3dulo <code>foo<\/code> ? \u00bfEs necesario escribir un rol entero para cada m\u00f3dulo? \u00bfO es mejor tener un rol general para todo? \u00bfY si no es un rol, entonces d\u00f3nde escribir: en pre o en post?<\/p>\n<p><\/p>\n<p>Si no hay una respuesta fundamentada a estas preguntas, es un signo de falta de intuici\u00f3n, es decir, esas mismas \"bases inestables\". Vamos a aclararlo. Primero, una pregunta de control: Si el play tiene <code>pre_tasks<\/code> y <code>post_tasks<\/code> (y no hay ni tasks, ni roles), \u00bfpuede romperse algo si llevo la primera tarea de <code>post_tasks<\/code> al final? <code>pre_tasks<\/code>?<\/p>\n<p><\/p>\n<p>Por supuesto, la formulaci\u00f3n de la pregunta insin\u00faa que se romper\u00e1. Pero, \u00bfqu\u00e9 exactamente?<\/p>\n<p><\/p>\n<p>\u2026 manejadores. Leer los fundamentos revela un hecho importante: todos los manejadores se descargan autom\u00e1ticamente despu\u00e9s de cada secci\u00f3n. Es decir, se ejecutan todas las tareas de <code>pre_tasks<\/code>, luego todos los handlers que fueron notificados. Despu\u00e9s se ejecutan todos los roles y todos los handlers notificados en los roles. Luego <code>post_tasks<\/code> y sus handlers.<\/p>\n<p><\/p>\n<p>Por lo tanto, si arrastras una tarea de <code>post_tasks<\/code> en <code>pre_tasks<\/code>, por lo que, potencialmente, la ejecutar\u00e1s antes de que se ejecute el manejador. Por ejemplo, si en <code>pre_tasks<\/code> se instala y configura <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/es\/server\/\"   title=\"servidor web\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"1155\">servidor web<\/a>, mientras que en <code>post_tasks<\/code> se env\u00eda algo, entonces mover esta tarea a la secci\u00f3n <code>pre_tasks<\/code> llevar\u00e1 a que en el momento de \"enviar\", el servidor a\u00fan no est\u00e9 en marcha y todo falle.<\/p>\n<p><\/p>\n<p>Y ahora pensemos otra vez, \u00bfpor qu\u00e9 necesitamos <code>pre_tasks<\/code> y <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> nos permitir\u00e1 trabajar con los resultados de la ejecuci\u00f3n de roles (incluidos los handlers).<\/p>\n<p><\/p>\n<p>Un conocedor meticuloso de Ansible nos dir\u00e1 que hay <code>meta: flush_handlers<\/code>, pero \u00bfpara qu\u00e9 necesitamos flush_handlers, si podemos confiar en el orden de ejecuci\u00f3n de las secciones en el play? M\u00e1s a\u00fan, el uso de meta: flush_handlers puede traernos problemas inesperados con handlers duplicados, haci\u00e9ndonos recibir advertencias extra\u00f1as en caso de usar <code>when<\/code> sa-logic-subsets-canary-vs.yaml <code>block<\/code> y as\u00ed sucesivamente. Cuanto mejor conozcas Ansible, m\u00e1s matices podr\u00e1s se\u00f1alar para una soluci\u00f3n \"inteligente\". Y una soluci\u00f3n simple \u2014 usar una separaci\u00f3n natural entre pre\/roles\/post \u2014 no genera complicaciones.<\/p>\n<p><\/p>\n<p>Y, volviendo a nuestro 'foo'. \u00bfD\u00f3nde debe colocarse? \u00bfEn pre, post o roles? Obviamente, depende de si necesitamos los resultados del trabajo del manejador para foo. Si no los hay, entonces foo no necesita estar ni en pre ni en post \u2014 estas secciones tienen un significado especial: ejecutar tareas antes y despu\u00e9s del bloque de c\u00f3digo principal.<\/p>\n<p><\/p>\n<p>Ahora la respuesta a la pregunta \"\u00bfrol o tarea?\" se reduce a lo que ya hay en el play \u2014 si hay tareas, entonces hay que a\u00f1adirlas a las tareas. Si hay roles, hay que hacer un rol (aunque sea de una sola tarea). Recuerdo que no se utilizan tareas y roles simult\u00e1neamente.<\/p>\n<p><\/p>\n<p>Entender las bases de Ansible proporciona respuestas fundamentadas a lo que podr\u00edan parecer preguntas de gusto personal.<\/p>\n<p><\/p>\n<h1>Tareas y roles (parte dos)<\/h1>\n<p><\/p>\n<p>Ahora discutamos la situaci\u00f3n en la que apenas est\u00e1s comenzando a escribir un playbook. Necesitas hacer foo, bar y baz. \u00bfSon estas tres tareas, un rol o tres roles? Resumiendo la pregunta: \u00bfen qu\u00e9 momento deber\u00edamos comenzar a escribir roles? \u00bfCu\u00e1l es el sentido de escribir roles cuando podemos escribir tareas?\u2026 \u00bfQu\u00e9 es un rol?<\/p>\n<p><\/p>\n<p>Uno de los errores m\u00e1s groseros (ya he hablado de esto) es pensar que un rol es como una funci\u00f3n en una biblioteca de programaci\u00f3n. \u00bfC\u00f3mo se ve una descripci\u00f3n general de una funci\u00f3n? Toma argumentos de entrada, interact\u00faa con efectos colaterales y devuelve un valor.<\/p>\n<p><\/p>\n<p>Ahora, atenci\u00f3n. \u00bfQu\u00e9 de esto se puede hacer en un rol? Invocar efectos secundarios \u2014 siempre puedes, esa es la esencia de Ansible: provocar efectos secundarios. \u00bfTener causas secundarias? Elemental. Pero con \"transferir un valor y devolverlo\" \u2014 aqu\u00ed es donde no. Primero, no puedes pasar un valor a un rol. Puedes establecer una variable global con una duraci\u00f3n igual al play en la secci\u00f3n vars para el rol. Puedes establecer una variable global con una duraci\u00f3n en el play dentro del rol. O incluso con una duraci\u00f3n en el libro de jugadas (<code>set_fact<\/code>\/<code>register<\/code>). Pero no puedes tener \"variables locales\". No puedes \"recibir un valor\" y \"devolverlo\".<\/p>\n<p><\/p>\n<p>De esto se deduce lo principal: no se puede escribir algo en Ansible sin provocar efectos colaterales. Cambiar variables globales es siempre un efecto colateral para una funci\u00f3n. En Rust, por ejemplo, cambiar una variable global es <code>unsafe<\/code>. En Ansible, existe un \u00fanico m\u00e9todo para influir en los valores de un rol. Tenga en cuenta las palabras utilizadas: no se dice \"pasar un valor al rol\", sino \"cambiar los valores que utiliza el rol\". No hay aislamiento entre los roles. No hay aislamiento entre las tareas y los roles.<\/p>\n<p><\/p>\n<p>Total: <strong>un rol no es una funci\u00f3n<\/strong>.<\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 hay de bueno en los roles? En primer lugar, los roles tienen valores predeterminados (<code>\/default\/main.yaml<\/code>), y en segundo lugar, los roles cuentan con cat\u00e1logos adicionales para almacenar archivos.<\/p>\n<p><\/p>\n<p>\u00bfCu\u00e1l es la ventaja de los valores predeterminados? En la tabla de prioridades de variables de la pir\u00e1mide de Maslow bastante retorcida de Ansible, los valores predeterminados de los roles son los menos prioritarios (excepto por los par\u00e1metros de la l\u00ednea de comandos de Ansible). Esto significa que si necesitas proporcionar valores predeterminados sin preocuparte de que interrumpan los valores de inventario o variables grupales, los valores predeterminados del rol son el lugar correcto para ti. (Estoy exagerando un poco, tambi\u00e9n existe <code>|d(your_default_here)<\/code>, pero hablando de lugares estacionarios, solo son los valores predeterminados del rol).<\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 m\u00e1s es bueno en los roles? Que tienen sus propios directorios. Son directorios para variables, tanto permanentes (es decir, calculadas para el rol), como din\u00e1micas (hay un patr\u00f3n o antipatrones en juego \u2014 <code>include_vars<\/code> junto con <code>{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml<\/code>). Son directorios para <code>files\/<\/code>, <code>templates\/. Adem\u00e1s, permiten a los roles tener sus propios m\u00f3dulos y complementos (<\/code>library\/<code>). Sin embargo, en comparaci\u00f3n con las tareas de los playbooks (cuyas caracter\u00edsticas tambi\u00e9n pueden incluir todo esto), la ventaja aqu\u00ed radica en que los archivos no est\u00e1n en un solo lugar, sino en varios grupos separados.<\/code>). Sin embargo, comparado con las tareas en el playbook (que tambi\u00e9n puede tener todo esto), la ventaja aqu\u00ed radica \u00fanicamente en que los archivos no est\u00e1n todos amontonados, sino en varias pilas separadas.<\/p>\n<p><\/p>\n<p>As\u00ed, los roles poseen dos caracter\u00edsticas importantes: tienen valores predeterminados (una caracter\u00edstica \u00fanica) y permiten estructurar el c\u00f3digo.<\/p>\n<p><\/p>\n<p>Volviendo a la pregunta inicial: \u00bfcu\u00e1ndo hacer tareas y cu\u00e1ndo roles? Las tareas en el playbook se utilizan con mayor frecuencia como \"pegamento\" antes\/despu\u00e9s de los roles, o como elementos constructivos independientes (en cuyo caso no deber\u00edan haber roles en el c\u00f3digo). Un mont\u00f3n de tareas normales mezcladas con roles es sin duda desordenado. Debe seguirse un estilo concreto: o tareas o roles. Los roles ofrecen separaci\u00f3n de entidades y valores predeterminados, las tareas permiten leer el c\u00f3digo m\u00e1s r\u00e1pidamente. Generalmente, en los roles se lleva el c\u00f3digo m\u00e1s \"estacionario\" (importante y complejo), mientras que en el estilo de tareas se escriben scripts auxiliares.<\/p>\n<p><\/p>\n<p>Volviendo a la pregunta original: \u00bfcu\u00e1ndo usar tareas y cu\u00e1ndo usar roles? Las tareas en el playbook se utilizan m\u00e1s com\u00fanmente como \"pegamento\" antes\/despu\u00e9s de los roles, o como un elemento constructivo independiente (en cuyo caso no deber\u00eda haber roles en el c\u00f3digo). Una mezcla desordenada de tareas normales con roles es una clara falta de orden. Se debe mantener un estilo concreto: ya sea tareas o roles. Los roles proporcionan separaci\u00f3n de entidades y valores predeterminados, mientras que las tareas permiten leer el c\u00f3digo m\u00e1s r\u00e1pidamente. Normalmente, el c\u00f3digo m\u00e1s \"est\u00e1tico\" (importante y complejo) se coloca en roles, mientras que las tareas se utilizan para escribir scripts auxiliares.<\/p>\n<p><\/p>\n<p>Existe la posibilidad de hacer un import_role como tarea, pero si escribes esto, prep\u00e1rate para explicar por qu\u00e9 lo deseas hacer.<\/p>\n<p><\/p>\n<p>Un lector perspicaz podr\u00eda decir que los roles pueden importar roles, que los roles pueden tener dependencias a trav\u00e9s de galaxy.yml, y adem\u00e1s existe algo horrible y aterrador <code>include_role<\/code> \u2014 les recuerdo, estamos mejorando nuestras habilidades en Ansible b\u00e1sico, no en gimnasia art\u00edstica.<\/p>\n<p><\/p>\n<h1>Manejadores y tareas<\/h1>\n<p><\/p>\n<p>Hablemos de otra cosa obvia: los manejadores. Saber usarlos correctamente es casi un arte. \u00bfCu\u00e1l es la diferencia entre un manejador y una tarea?<\/p>\n<p><\/p>\n<p>Dado que estamos recordando lo b\u00e1sico, aqu\u00ed hay un ejemplo:<\/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>En los roles, los handlers se encuentran en rolename\/handlers\/main.yaml. Los handlers se comparten entre todos los participantes en el play: pre\/post_tasks pueden invocar los handlers del rol, y el rol puede invocar handlers desde el play. Sin embargo, las llamadas a los handlers \"cross-role\" generan mucha m\u00e1s confusi\u00f3n que la repetici\u00f3n de un handler trivial. (Otro elemento de las mejores pr\u00e1cticas es tratar de no repetir los nombres de los handlers).<\/p>\n<p><\/p>\n<p>La diferencia principal es que una tarea se ejecuta (idempotente) siempre (m\u00e1s\/menos etiquetas y <code>when<\/code>), mientras que un manejador se ejecuta por cambio de estado (notify se activa solo si hubo un cambio). \u00bfQu\u00e9 implica esto? Por ejemplo, que al volver a ejecutar, si no hubo cambios, entonces no habr\u00e1 manejador. \u00bfY por qu\u00e9 puede ser que necesitemos ejecutar un manejador cuando no hubo cambios en la tarea que lo gener\u00f3? Por ejemplo, porque algo se rompi\u00f3 y hubo un cambio, pero la ejecuci\u00f3n no lleg\u00f3 al manejador. Por ejemplo, porque la red estuvo ca\u00edda temporalmente. La configuraci\u00f3n cambi\u00f3, el servicio no se reinici\u00f3. En la siguiente ejecuci\u00f3n, la configuraci\u00f3n ya no cambia y el servicio sigue con la versi\u00f3n anterior de la configuraci\u00f3n.<\/p>\n<p><\/p>\n<p>La situaci\u00f3n con la configuraci\u00f3n no es solucionable (m\u00e1s bien, uno mismo puede inventar un protocolo especial de reinicio con banderas de archivo, etc., pero esto ya no es 'ansible b\u00e1sico' en ninguna forma). Sin embargo, hay otra historia com\u00fan: hemos instalado la aplicaci\u00f3n, la hemos registrado. <code>.service<\/code>-archivo, y ahora queremos <code>daemon_reload<\/code> y <code>state=started<\/code>. Y el lugar natural para esto parece ser un handler. Pero si lo hacemos no un handler, sino una tarea al final de la lista de tareas o de un rol, se ejecutar\u00e1 idempotentemente cada vez. Incluso si el playbook falla a la mitad. Esto no resuelve absolutamente el problema de restarted (no se puede hacer una tarea con el atributo restarted, ya que se pierde la idempotencia), pero definitivamente vale la pena hacerlo state=started, ya que la estabilidad general de los playbooks aumenta, a medida que se reduce la cantidad de conexiones y estado din\u00e1mico.<\/p>\n<p><\/p>\n<p>Otra propiedad positiva del handler es que no ensucia la salida. No hubo cambios, no hay 'skipped' o 'ok' innecesarios en la salida, lo que facilita la lectura. Este tambi\u00e9n es un aspecto negativo: si usted encuentra un error tipogr\u00e1fico en una tarea ejecutada linealmente en la primera ejecuci\u00f3n, los handlers solo se ejecutar\u00e1n si hubo cambios, es decir, bajo ciertas condiciones, lo que ocurre con muy poca frecuencia. Por ejemplo, la primera vez en cinco a\u00f1os. Y, por supuesto, habr\u00e1 un error tipogr\u00e1fico en el nombre y todo se romper\u00e1. Y no se podr\u00e1n invocar de nuevo, ya que no hubo cambios.<\/p>\n<p><\/p>\n<p>Es necesario hablar por separado sobre la disponibilidad de las variables. Por ejemplo, si notificas para una tarea con un bucle, \u00bfqu\u00e9 habr\u00e1 en las variables? Se puede deducir de manera anal\u00edtica, pero no siempre es trivial, especialmente si las variables provienen de diferentes lugares.<\/p>\n<p><\/p>\n<p>As\u00ed que los handlers son mucho menos \u00fatiles y mucho m\u00e1s problem\u00e1ticos de lo que parece. Si se puede escribir algo bien (sin trucos) sin handlers, es mejor hacerlo sin ellos. Si no se puede hacer de forma bonita, es mejor hacerlo con ellos.<\/p>\n<p><\/p>\n<p>Un lector perspicaz se\u00f1ala correctamente que no hemos discutido <code>listen<\/code>, que un handler puede llamar a notify para otro handler, que un handler puede incluir import_tasks (que puede hacer include_role con with_items), que el sistema de handlers en Ansible es Turing completo, que los handlers de include_role se cruzan de manera muy curiosa con los handlers del play, etc. \u2013 todo esto claramente no son \"fundamentos\".<\/p>\n<p><\/p>\n<p>Aunque hay un cierto WTF que en realidad es una caracter\u00edstica y que debe recordarse. Si una tarea se ejecuta con <code>delegate_to<\/code> y tiene notify, entonces el handler correspondiente se ejecuta sin <code>delegate_to<\/code>, es decir, en el host al que se asign\u00f3 el play. (Aunque el handler, por supuesto, puede tener <code>delegate_to<\/code> tambi\u00e9n).<\/p>\n<p><\/p>\n<p>Quiero decir unas palabras sobre roles reutilizables. Antes de la aparici\u00f3n de las colecciones, hab\u00eda la idea de que se pod\u00edan hacer roles universales, que se pod\u00edan <code>ansible-galaxy install<\/code> y se fue. Funciona en todos los sistemas operativos en todas las variantes en todas las situaciones. As\u00ed que, mi opini\u00f3n es: no funciona. Cualquier rol con soporte para 100500 casos est\u00e1 condenado a las profundidades de errores corner case. Se pueden solucionar con pruebas masivas, pero como con cualquier prueba, o tienes el producto cartesiano de las entradas y una funci\u00f3n total, o tienes \"cobiertos escenarios individuales\". Mi opini\u00f3n es que es mucho mejor si el rol es lineal (complejidad ciclom\u00e1tica 1). <code>include_vars<\/code>, el soporte de 100500 casos est\u00e1 condenado a las profundidades de errores corner case. Se pueden tapar con pruebas masivas, pero como con cualquier prueba, o tienes el producto cartesiano de los valores de entrada y una funci\u00f3n total, o tienes \"cobiertos escenarios individuales\". Mi opini\u00f3n es que es mejor cuando el rol es lineal (complejidad ciclom\u00e1tica 1).<\/p>\n<p><\/p>\n<p>Cuantos menos ifs (expl\u00edcitos o declarativos \u2013 en forma de <code>when<\/code> por conjunto de variables), mejor es el rol. A veces es necesario hacer bifurcaciones, pero, repito, cuanto menos haya, mejor. As\u00ed que parece que un buen rol con galaxy (\u00a1funciona!) con un mont\u00f3n <code>include_vars<\/code> puede ser menos preferible que \"su\" rol de cinco tareas. El momento en que el rol con galaxy es mejor \u2014 es cuando comienzas a escribir algo. El momento en que se vuelve peor \u2014 es cuando algo se rompe, y sospechas que es por \"el rol con galaxy\". Lo abres, y all\u00ed hay cinco inclusiones, ocho listas de tareas y una pila <code>when<\/code> puede ser menos preferible que \"su\" propio rol de cinco tareas. El momento en que un rol con galaxy es mejor es cuando comienzas a escribir algo. El momento en que se vuelve peor es cuando algo se rompe, y tienes la sospecha de que es por \"el rol con galaxy\". Lo abres y all\u00ed hay cinco inclusiones, ocho listas de tareas y un mont\u00f3n <code>when<\/code>\u2018... Y hay que resolver eso. En lugar de 5 tareas en una lista lineal, donde tampoco hay nada que romper.<\/p>\n<p><\/p>\n<h1>Un poco sobre inventarios, variables de grupo, plugin host_group_vars, hostvars. C\u00f3mo atar un nudo gordiano de espagueti. Alcance y precedencia de variables, modelo de memoria de Ansible. \"\u00bfD\u00f3nde se debe guardar el nombre de usuario para la base de datos?\".<\/h1>\n<p><\/p>\n<ul>\n<li>Un poco sobre inventario, variables grupales, plugin host_group_vars, hostvars. C\u00f3mo conectar los espaguetis en un nudo gordiano. Alcance y precedencia de las variables, modelo de memoria de Ansible. \"\u00bfD\u00f3nde deber\u00eda guardar el nombre de usuario para la base de datos?\".<\/li>\n<li><code>\u2014 nosql notype nosense plastilina blanda. Est\u00e1 en todas partes, incluso donde no lo esperas. Un poco sobre<\/code> !!unsafe <code>y un delicioso yaml.<\/code> \u00bfC\u00f3mo ayudan los gigantes de la tecnolog\u00eda a la educaci\u00f3n? Parte 2: Microsoft<\/li>\n<\/ul>\n<p>Fuente: <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\/es\/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=\"es_ES\" \/>\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\/es\/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":"\u00a1Aqu\u00ed tienes!","description":"\ud83e\udd47Fundamentos de Ansible, sin los cuales sus playbooks son un mont\u00f3n de fideos pegados | ProHoster","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/osnovy-ansible-bez-kotoryh-vashi-plejbuki-komok-slipshihsya-makaron","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/87084","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=87084"}],"version-history":[{"count":2,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/87084\/revisions"}],"predecessor-version":[{"id":162161,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/87084\/revisions\/162161"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=87084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=87084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=87084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}