<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Livio Ribeiro</title>
    <description>The latest articles on Forem by Livio Ribeiro (@livioribeiro).</description>
    <link>https://forem.com/livioribeiro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F45935%2F6252251e-5041-4e9f-aa4b-ce785e71f85a.jpeg</url>
      <title>Forem: Livio Ribeiro</title>
      <link>https://forem.com/livioribeiro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/livioribeiro"/>
    <language>en</language>
    <item>
      <title>Discuss: Return a response or write to the response?</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Mon, 04 Aug 2025 22:36:55 +0000</pubDate>
      <link>https://forem.com/livioribeiro/return-a-response-or-write-to-the-response-4n7p</link>
      <guid>https://forem.com/livioribeiro/return-a-response-or-write-to-the-response-4n7p</guid>
      <description>&lt;p&gt;In most web frameworks (Spring, Django, FastAPI, Axum) the handlers return an object to the framework that will be used to build the response that is sent to the client.&lt;/p&gt;

&lt;p&gt;However, the Go lang &lt;code&gt;net/http&lt;/code&gt; package uses a different approach: the handlers write data directly into the response and have no return value.&lt;/p&gt;

&lt;p&gt;I believe that this behavior comes from a time that the most frameworks worked with blocking IO and employed optimizations using queues and threads dedicated to process the responses, so your code would return an object describing the response and the framework would put it in the queue to be processed by a thread.&lt;/p&gt;

&lt;p&gt;With non-blocking IO, there is no need for these optimizations, since the async runtime already takes care of everything related to IO, allowing handlers to write data directly to the response.&lt;/p&gt;

&lt;p&gt;So, which is better? Return a response or write to the response?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>programming</category>
      <category>backend</category>
    </item>
    <item>
      <title>Announcing Selva web framework</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Sat, 28 Oct 2023 10:12:59 +0000</pubDate>
      <link>https://forem.com/livioribeiro/announcing-selva-web-framework-57nj</link>
      <guid>https://forem.com/livioribeiro/announcing-selva-web-framework-57nj</guid>
      <description>&lt;p&gt;I'd like to announce &lt;a href="https://github.com/livioribeiro/selva" rel="noopener noreferrer"&gt;Selva web framework&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I've been working on it for quite a while, did a lot of back and forth, changed how some things work, then changed back... until I finally felt it was good enough to do a public announcement.&lt;/p&gt;

&lt;p&gt;The framework uses under the hood another project of mine, &lt;a href="https://pypi.org/project/asgikit/" rel="noopener noreferrer"&gt;asgikit&lt;/a&gt;, which is a toolkit that provides just the bare minimum to create an asgi application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Quickstart

&lt;ul&gt;
&lt;li&gt;What's going on?&lt;/li&gt;
&lt;li&gt;Why is that?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Controllers and handlers

&lt;ul&gt;
&lt;li&gt;Path and query parameters&lt;/li&gt;
&lt;li&gt;Request body and Pydantic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Dependency injection

&lt;ul&gt;
&lt;li&gt;Factory functions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Configuration

&lt;ul&gt;
&lt;li&gt;Accessing the configuration&lt;/li&gt;
&lt;li&gt;Typed configuration&lt;/li&gt;
&lt;li&gt;Configuration profiles&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Logging&lt;/li&gt;

&lt;li&gt;Final words&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;First install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;selva uvicorn[standard]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;application.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@get&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then run uvicorn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn selva.run:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's going on?
&lt;/h3&gt;

&lt;p&gt;First, you do not need to create an application, you just use &lt;code&gt;selva.run:app&lt;/code&gt;. Selva will look for a module or package named &lt;code&gt;application&lt;/code&gt; and scan it. Controllers will be registered in the routing system and services will be registered with the dependency injection system. Controllers are also services and take part in the dependency injection system as well.&lt;/p&gt;

&lt;p&gt;But the most notable difference from other tools is that, in the request handler, instead of returning a response, you write to the response. This behavior comes from &lt;code&gt;asgikit&lt;/code&gt; and is similar to how Go's &lt;code&gt;net/http&lt;/code&gt; works.&lt;/p&gt;

&lt;p&gt;Asgikit provides several helper functions to handle common use cases. The example above could be written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_text&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@get&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why is that?
&lt;/h3&gt;

&lt;p&gt;In the common way of returning a response, extensibility is achieved by extending the usual &lt;code&gt;Response&lt;/code&gt; class. Now you have to deal with inheritance and need to know, to some extent, the inner workings of the response class.&lt;/p&gt;

&lt;p&gt;With the approach used by Asgikit/Selva (which was inspired by Go's &lt;code&gt;net/http&lt;/code&gt;), extensibility is achieved through helper functions that only need to know how to write to the response.&lt;/p&gt;

&lt;p&gt;Reading the request body uses a similar approach, you use helper function, such as &lt;code&gt;asgikit.requests.read_json&lt;/code&gt;, to read the request body as json.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controllers and handlers
&lt;/h2&gt;

&lt;p&gt;Controllers are classes that have methods to handle request. They can be decorated with the path they respond to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method hello will respond in the path &lt;code&gt;localhost:8000/api/hello&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://localhost:8000/api/hello
{"message": "Hello, World!"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Path and query parameters
&lt;/h3&gt;

&lt;p&gt;Path parameters are defined in the handler decorator with the format &lt;code&gt;:parameter&lt;/code&gt; to match path segments (between &lt;code&gt;/&lt;/code&gt;), or &lt;code&gt;*parameter&lt;/code&gt; to match paths with the &lt;code&gt;/&lt;/code&gt; character.&lt;/p&gt;

&lt;p&gt;Parameters can be extracted from the request by annotating the handler function arguments, such as &lt;code&gt;selva.web.FromPath&lt;/code&gt; or &lt;code&gt;selva.web.FromQuery&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromQuery&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello/:name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromPath&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromQuery&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type conversion is done based on the parameter type and can be extended.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request body and Pydantic
&lt;/h3&gt;

&lt;p&gt;If you annotate a handler parameter with a type that inherits from &lt;code&gt;pydantic.BaseModel&lt;/code&gt;, or a &lt;code&gt;list[pydantic.BaseModel]&lt;/code&gt;, Selva will read the request body and parse using the &lt;code&gt;pydantic.BaseModel&lt;/code&gt; class from the annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dependency injection
&lt;/h2&gt;

&lt;p&gt;Selva comes with a dependency injection system. You just need to decorate your services with &lt;code&gt;@service&lt;/code&gt; and annotate the classes where the service will be injected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.di&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromPath&lt;/span&gt;


&lt;span class="nd"&gt;@service&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreeterService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GreeterService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello/:name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromPath&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that classes decorated with &lt;code&gt;@controller&lt;/code&gt; are services too and therefore can inject other services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Factory functions
&lt;/h3&gt;

&lt;p&gt;Services can also be defined by decorating a function with &lt;code&gt;@service&lt;/code&gt;, making them &lt;em&gt;factory functions&lt;/em&gt;. The same &lt;code&gt;GreeterService&lt;/code&gt; example could be written could be written like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.di&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromPath&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreeterService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@service&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greeter_service_factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GreeterService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;GreeterService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GreeterService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello/:name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FromPath&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Selva uses YAML files the provide configuration settings to the application. These files are located in the &lt;code&gt;configuration&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── application/
│   ├── __init__.py
│   ├── controller.py
│   └── service.py
└── configuration/
    ├── settings.yaml
    ├── settings_dev.yaml
    └── settings_prod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The settings files are parsed using &lt;a href="https://pypi.org/project/strictyaml/" rel="noopener noreferrer"&gt;strictyaml&lt;/a&gt;, so only a subset of the yaml spec is used. This decisions is to make the settings files safer to parse and more consistent.&lt;/p&gt;

&lt;p&gt;Selva provides a way to reference environment variables in the settings files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PROPERTY}&lt;/span&gt;
&lt;span class="na"&gt;with_default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PROPERTY:default}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Accessing the configuration
&lt;/h3&gt;

&lt;p&gt;You can access the configuration by injection the class &lt;code&gt;selva.configuration.Settings&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="c1"&gt;## settings.yaml
# message: Hello, World!
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.configuration&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.di&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Typed configuration
&lt;/h3&gt;

&lt;p&gt;You can use Pydantic and the di system to provided typed settings to your services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="c1"&gt;## settings.yaml
# application:
#   value: 42
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asgikit.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;respond_json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.configuration&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.di&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selva.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;


&lt;span class="nd"&gt;@service&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_settings_factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MySettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MySettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;respond_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration profiles
&lt;/h3&gt;

&lt;p&gt;We can create different configuration profiles that can be activated by settings the environment variable &lt;code&gt;SELVA_PROFILE&lt;/code&gt;. When a profile is set, the framework will look for a file named &lt;code&gt;configuration/settings_${PROFILE}.yaml&lt;/code&gt; and merge its settings into the main settings. For example, if we define &lt;code&gt;SELVA_PROFILE=dev&lt;/code&gt;, the file &lt;code&gt;configuration/settings_dev.yaml&lt;/code&gt; will be loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# settings.yaml&lt;/span&gt;
&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;value&lt;/span&gt;
&lt;span class="na"&gt;mapping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nested value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# settings_dev.yaml&lt;/span&gt;
&lt;span class="na"&gt;dev_property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev value&lt;/span&gt;
&lt;span class="na"&gt;mapping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev nested value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# final settings&lt;/span&gt;
&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;value&lt;/span&gt;
&lt;span class="na"&gt;mapping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev nested value&lt;/span&gt;
&lt;span class="na"&gt;dev_property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Logging
&lt;/h2&gt;

&lt;p&gt;Selva uses &lt;a href="https://pypi.org/project/loguru/" rel="noopener noreferrer"&gt;loguru&lt;/a&gt; for logging and provides some facilities on top of it: it configures by default an interceptor for the stardand &lt;code&gt;logging&lt;/code&gt; module, so all logs go through loguru, and a custom filter that allows settings the log level for individual python packages in the yaml configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# settings.yaml&lt;/span&gt;
&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# log level to be used when not specified by package&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WARNING&lt;/span&gt;
  &lt;span class="c1"&gt;# specification of log level by package&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;application&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ERROR&lt;/span&gt;
    &lt;span class="na"&gt;application.service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WARNING&lt;/span&gt;
    &lt;span class="na"&gt;sqlalchemy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;INFO&lt;/span&gt;
    &lt;span class="na"&gt;sqlalchemy.orm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a package has disabled loguru (&lt;code&gt;logger.disable("package")&lt;/code&gt;) you can enable it again in the configuration file, and you can disable logging for a package as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;package&lt;/span&gt;
  &lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;other_package&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Selva is still in its infancy, and there is work to do, more tests need to be written, documentation can be improved, but I believe it is a great tool and I hope it can have its place in the Python ecosystem.&lt;/p&gt;

&lt;p&gt;Let me know in the comments what you think about Selva.&lt;/p&gt;

&lt;p&gt;And by the way, "Selva" is portuguese for "Jungle" :)&lt;/p&gt;

</description>
      <category>python</category>
      <category>development</category>
      <category>announcement</category>
      <category>backend</category>
    </item>
    <item>
      <title>Why I prefer Rust over Go</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Sun, 06 Aug 2023 20:31:08 +0000</pubDate>
      <link>https://forem.com/livioribeiro/why-i-prefer-rust-over-go-327i</link>
      <guid>https://forem.com/livioribeiro/why-i-prefer-rust-over-go-327i</guid>
      <description>&lt;p&gt;Recently I decided to learn Go and my first impressions were quite good. Go is a very nice language and easy to get started with. The go routines is probably the best feature of the language and I really wish other languages followed this model. The &lt;code&gt;defer&lt;/code&gt; feature is also very nice.&lt;/p&gt;

&lt;p&gt;However, as I continued learning, some things started feeling odd, some design decisions that looked strange. For example, the only loop construct is &lt;code&gt;for&lt;/code&gt;, and there is no way to create a loop that iterates over a sequence of numbers, like &lt;code&gt;for i in range(10)&lt;/code&gt; in Python or &lt;code&gt;for i in 0..10&lt;/code&gt; in Rust, you have to use a for loop as in C: &lt;code&gt;for i := 0; i &amp;lt;= 10; i++&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After a while I realized: Go tries so hard to be like C that it does not even try make improvements that make it look too different than C.&lt;/p&gt;

&lt;p&gt;Now let's compare the &lt;code&gt;RWMutex&lt;/code&gt; from Go with &lt;code&gt;RWLock&lt;/code&gt; from Rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RWMutex&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// do someting&lt;/span&gt;
&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will compile just fine, but will fail at runtime, because I called &lt;code&gt;lock.Unlock()&lt;/code&gt; instead of &lt;code&gt;lock.RUnlock()&lt;/code&gt;. You might say that is the developer's fault for no paying attention and calling the wrong method, and you are right, but let's take a look at the equivalent in Rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RwLock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First thing we notice is that the &lt;code&gt;RWLock&lt;/code&gt; operates with a value, since is very common the use a lock to synchronize access to something. Then we open curly braces to start a scope because &lt;code&gt;lock.read()&lt;/code&gt; returns a guard that releases the lock when it goes out of scope, so there is no wrong method to be called. The error handling may look annoying but &lt;a href="https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html#errors" rel="noopener noreferrer"&gt;it is there for a reason&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For me this design decision for the Go &lt;code&gt;RWMutex&lt;/code&gt; seems very strange, it could be much better if &lt;code&gt;RLock&lt;/code&gt; returned a handle in which you would call &lt;code&gt;Unlock&lt;/code&gt; and I cannot think of any reason for this other than to look more like C.&lt;/p&gt;

&lt;p&gt;But Rust is not perfect, though, function signatures can become messy, specially with async, and dealing with lifetimes is not fun. And compilation time...&lt;/p&gt;

&lt;p&gt;I will continue learning Go, it is really a nice language, but I still prefer Rust, I feel much safer with it (pun intended).&lt;/p&gt;

</description>
      <category>rust</category>
      <category>go</category>
    </item>
    <item>
      <title>Angular dynamic settings</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Fri, 27 Aug 2021 11:47:08 +0000</pubDate>
      <link>https://forem.com/livioribeiro/angular-dynamic-settings-4hae</link>
      <guid>https://forem.com/livioribeiro/angular-dynamic-settings-4hae</guid>
      <description>&lt;p&gt;(You can find the code shown here in &lt;a href="https://github.com/livioribeiro/angular-dynamic-settings-example" rel="noopener noreferrer"&gt;https://github.com/livioribeiro/angular-dynamic-settings-example&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;When developing an Angular application, usually your application settings lives in &lt;code&gt;src/environments/environment.ts&lt;/code&gt; for development and &lt;code&gt;src/environments/environment.prod.ts&lt;/code&gt; for production, and Angular takes care of swapping those during a production build.&lt;/p&gt;

&lt;p&gt;But if we have another environment, a &lt;em&gt;staging&lt;/em&gt; environment, then we have a problem, since we expect to approve the application running in staging and promote the same code to production, but with Angular's approach to configuration we need to run another build to configure our app to production.&lt;/p&gt;

&lt;p&gt;To overcome this problem, I came up a very simple, yet very effective, strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load a javascript file before the application starts that will define a settings object in &lt;code&gt;window.$environment&lt;/code&gt;. This is essentially the same as &lt;code&gt;environment.ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;environment.ts&lt;/code&gt;, export the object defined in &lt;code&gt;window.$environment&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tell Angular to add the configuration directory to the build output directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First we need to create a directory called &lt;code&gt;src/config&lt;/code&gt; and put the javascript file &lt;code&gt;environment.js&lt;/code&gt; there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/config/environment.js&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev.my-backend.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// and any other configuration that would go in "environment.ts"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then load the script on &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;MyApp&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/x-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"favicon.ico"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- add the following line --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/config/environment.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;app-root&amp;gt;&amp;lt;/app-root&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in &lt;code&gt;environment.ts&lt;/code&gt;, export the configuration object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/environments/environment.ts&lt;/span&gt;

&lt;span class="c1"&gt;// this interface is just to making things more typed&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;$environment&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, change &lt;code&gt;angular.json&lt;/code&gt; build options, adding &lt;code&gt;"src/config"&lt;/code&gt; the the assets, and remove the &lt;code&gt;"fileReplacements"&lt;/code&gt; completely. I also changed &lt;code&gt;"outputPath"&lt;/code&gt; to just &lt;code&gt;"dist"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@angular-devkit/build-angular:browser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"outputPath"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outputPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/main.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"polyfills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/polyfills.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tsConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.app.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"src/favicon.ico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"src/assets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;following&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"src/config"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"styles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"src/styles.css"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"budgets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maximumWarning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"500kb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maximumError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1mb"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"anyComponentStyle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maximumWarning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2kb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maximumError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4kb"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fileReplacements"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;removed&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"outputHashing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can safely remove &lt;code&gt;src/environments/environment.prod.ts&lt;/code&gt;, we don't need it anymore.&lt;/p&gt;

&lt;p&gt;Now we can inject a configuration script instead of doing another build.&lt;/p&gt;

&lt;p&gt;This approach works great with docker and kubernetes, and we can test it right now!&lt;/p&gt;

&lt;p&gt;First, we need a Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:latest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:latest&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist/ /usr/share/nginx/html/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a configuration to inject (I called it "environment.js"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// environment.js&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod.my-backend.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now build the image and run the container with our new configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-app &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/environment.js:/usr/share/nginx/html/config/environment.js &lt;span class="se"&gt;\&lt;/span&gt;
  my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Kubernetes you can use a configmap to store the "environment.js" and mount it as a volume under "/usr/share/nginx/html/config".&lt;/p&gt;

&lt;p&gt;And that's it! No more rebuilding angular for staging and production!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>FastAPI with OIDC Discovery</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Wed, 23 Jun 2021 11:34:53 +0000</pubDate>
      <link>https://forem.com/livioribeiro/fastapi-with-oidc-discovery-4507</link>
      <guid>https://forem.com/livioribeiro/fastapi-with-oidc-discovery-4507</guid>
      <description>&lt;p&gt;&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; is a very nice tool for writing web applications in Python, it is very fast and is a joy to use. But saddly it still lacks support for openid-connect discovery, so I cannot simply point to my Keycloak instance and it configures itself.&lt;/p&gt;

&lt;p&gt;Because of this I wrote &lt;a href="https://github.com/livioribeiro/fastapi-resource-server/" rel="noopener noreferrer"&gt;FastAPI Resource Server&lt;/a&gt;, a helper that adds support to this scenario: setup issuer address, receive a JWT, validate and decode claims and return claims.&lt;/p&gt;

&lt;p&gt;Usage is pretty straightforward, install with &lt;code&gt;pip install fastapi-resource-server&lt;/code&gt; and configure your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Security&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi_resource_server&lt;/span&gt; &lt;span class="n"&gt;OidcResourceServer&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;auth_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OidcResourceServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://issuer.url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;scheme_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My Issuer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;given_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;family_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Security&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_scheme&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;preferred_username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_obj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/me&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_current_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood it will fetch the openid configuration from &lt;code&gt;{issuer_url}/.well-known/openid-configuration&lt;/code&gt;, then fetch the jwks from the &lt;code&gt;jwks_uri&lt;/code&gt; property and use &lt;a href="https://pypi.org/project/python-jose/" rel="noopener noreferrer"&gt;python-jose&lt;/a&gt; to decode the JWT.&lt;/p&gt;

&lt;p&gt;And it works with the swagger ui with no problems.&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>authentication</category>
      <category>openidconnect</category>
    </item>
    <item>
      <title>Manage frontend dependencies in Python web projects with FrontMan</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Mon, 08 Feb 2021 12:09:27 +0000</pubDate>
      <link>https://forem.com/livioribeiro/manage-frontend-dependencies-in-python-web-projects-with-frontman-1bo7</link>
      <guid>https://forem.com/livioribeiro/manage-frontend-dependencies-in-python-web-projects-with-frontman-1bo7</guid>
      <description>&lt;p&gt;Web applications always need some Javascript and CSS to look good and make user experience better (JQuery and Bootstrap, for instance), but managing all the libraries we need can be a source of trouble, specially when executing CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Using npm to declare and retrieve frontend dependencies is a solution, but it is a bit too much if we are not building single page application with Angular or Vuejs, and besides that, it adds one significant dependency that in some cases could be avoided.&lt;/p&gt;

&lt;p&gt;Because of that, and inspired by &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/client-side/libman" rel="noopener noreferrer"&gt;AspNet LibMan&lt;/a&gt;, I built &lt;a href="https://github.com/livioribeiro/frontman" rel="noopener noreferrer"&gt;FrontMan&lt;/a&gt;, a dependency manager written in Python for frontend dependencies that fetches files from CDNs (cdnjs, jsdelivr and unpkg).&lt;/p&gt;

&lt;p&gt;You can install FrontMan by running &lt;code&gt;pip install frontman&lt;/code&gt; (&lt;code&gt;pipenv install --dev frontman&lt;/code&gt; for &lt;a href="https://docs.pipenv.org/" rel="noopener noreferrer"&gt;Pipenv&lt;/a&gt; and &lt;code&gt;poetry add --dev frontman&lt;/code&gt; for &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;Poetry&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Usage is simple, create a manifest (frontman.json) and execute &lt;code&gt;frontman install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The manifest is a json file like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jsdelivr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jquery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.5.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cdnjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jquery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"jquery.min.js"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@popperjs/core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/umd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popper.min.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"rename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popper.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"popper"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"js/bootstrap.min.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"css/bootstrap.min.css"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will be like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OK   https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js -&amp;gt; assets/jquery/jquery.min.js
OK   https://cdn.jsdelivr.net/npm/@popperjs/core@2.6.0/dist/umd/popper.min.js -&amp;gt; assets/popper/popper.js
OK   https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js -&amp;gt; assets/bootstrap/js/bootstrap.min.js
OK   https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css -&amp;gt; assets/bootstrap/css/bootstrap.min.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After runnig &lt;code&gt;frontman install&lt;/code&gt; there will be a file structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assets
├── bootstrap
│   ├── css
│   │   └── bootstrap.min.css
│   └── js
│       └── bootstrap.min.js
├── jquery
│   └── jquery.min.js
└── popper
    └── popper.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the install command again will give the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SKIP https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js -&amp;gt; assets/jquery/jquery.min.js
SKIP https://cdn.jsdelivr.net/npm/@popperjs/core@2.6.0/dist/umd/popper.min.js -&amp;gt; assets/popper/popper.js
SKIP https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js -&amp;gt; assets/bootstrap/js/bootstrap.min.js
SKIP https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css -&amp;gt; assets/bootstrap/css/bootstrap.min.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a file is already downloaded, FrontMan will not download it again, unless you pass the &lt;code&gt;--force&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;I hope you like FrontMan and find it useful.&lt;/p&gt;

</description>
      <category>python</category>
      <category>web</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>Trying Hashicorp Nomad 1.0 with LXD and Terraform</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Wed, 06 Jan 2021 19:05:50 +0000</pubDate>
      <link>https://forem.com/livioribeiro/trying-hashicorp-nomad-1-0-with-lxd-and-terraform-2489</link>
      <guid>https://forem.com/livioribeiro/trying-hashicorp-nomad-1-0-with-lxd-and-terraform-2489</guid>
      <description>&lt;p&gt;A while ago I wrote an article about &lt;a href="https://dev.to/livioribeiro/using-lxd-and-ansible-to-simulate-infrastructure-2g8l"&gt;deploying Hashicorp Nomad using LXD and Ansible&lt;/a&gt; and since Nomad hit 1.0 I decided to revisit it. It is an interesting and I wanted to try &lt;a href="https://terraform.io" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and learn a bit about it.&lt;/p&gt;

&lt;p&gt;You can find everything &lt;a href="https://github.com/livioribeiro/nomad-lxd-terraform" rel="noopener noreferrer"&gt;in this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To deploy the cluster, install Terraform and Packer and then &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://packer.io" rel="noopener noreferrer"&gt;Packer&lt;/a&gt;, another tool from Hashicorp, to create the container images and I also added a Prometheus instance to monitor the Nomad cluster and services. I will probably add Grafana in the future and if you know a good dashboard for Nomad, please share :)&lt;/p&gt;

&lt;p&gt;Terraform is a great tool, but Ansible is still more flexible, although it can be a bit harder to deal with and sometimes I feel lost in its documentation.&lt;/p&gt;

&lt;p&gt;Nomad is a good alternative to Kubernetes, specially if you are deploying your own cluster, with focus in simplicity and it is so easy to test it on your own machine.&lt;/p&gt;

</description>
      <category>nomad</category>
      <category>terraform</category>
      <category>lxd</category>
      <category>containers</category>
    </item>
    <item>
      <title>Openshift behind a proxy with SSL termination</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Tue, 06 Aug 2019 17:50:11 +0000</pubDate>
      <link>https://forem.com/livioribeiro/openshift-behind-a-proxy-with-ssl-termination-4gd8</link>
      <guid>https://forem.com/livioribeiro/openshift-behind-a-proxy-with-ssl-termination-4gd8</guid>
      <description>&lt;p&gt;The Openshift Container Platform is a great tool to deploy your applications and services in a container environment: It hides the complexity of Kubernetes and offers many facilities to manage builds, configurations, volumes and environment variables.&lt;/p&gt;

&lt;p&gt;Everything was fine until I had this weird problem where I could not get Keycloak to work properly. I was unable to authenticate because the service kept telling me that it required HTTPS... and I had HTTPS!&lt;/p&gt;

&lt;p&gt;In order to track down the problem, I wrote a small service (in &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; :) that just returned the HTTP headers it received and the result was like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[other headers]
X-Forwarded-Port: 80
X-Forwarded-Proto: http
[some more headers]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was strange! The proxy was sending the right &lt;code&gt;X-Forwarded-*&lt;/code&gt; headers, but somewhere they were being overwriden!&lt;/p&gt;

&lt;p&gt;I searched this behavior and found &lt;a href="https://access.redhat.com/solutions/3986281" rel="noopener noreferrer"&gt;this anwswer&lt;/a&gt; (needs a redhat account to visualize) about changing the openshift router configuration. My solution was a bit different but worked the same.&lt;/p&gt;

&lt;p&gt;We need to create a custom router configuration according to &lt;a href="https://docs.okd.io/3.11/install_config/router/customized_haproxy_router.html" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;. First let's get a copy of the default configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc &lt;span class="nt"&gt;-n&lt;/span&gt; default rsh dc/router &lt;span class="nb"&gt;cat &lt;/span&gt;haproxy-config.template &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; haproxy-config.template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open the &lt;code&gt;haproxy-config.template&lt;/code&gt; file and search for the following text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Port&lt;/span&gt; %[&lt;span class="n"&gt;dst_port&lt;/span&gt;]
&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; !{ &lt;span class="n"&gt;ssl_fc&lt;/span&gt; }
&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; { &lt;span class="n"&gt;ssl_fc&lt;/span&gt; }
&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt;-&lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; { &lt;span class="n"&gt;ssl_fc_alpn&lt;/span&gt; -&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These lines above override the &lt;code&gt;X-Forwarded-*&lt;/code&gt; headers sent by the proxy. My solution was to add a conditional to only set the headers if they are not present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Port&lt;/span&gt; %[&lt;span class="n"&gt;dst_port&lt;/span&gt;]  &lt;span class="n"&gt;if&lt;/span&gt; { &lt;span class="n"&gt;req&lt;/span&gt;.&lt;span class="n"&gt;hdr_cnt&lt;/span&gt;(&lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Port&lt;/span&gt;) &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; }
&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;        &lt;span class="n"&gt;if&lt;/span&gt; { &lt;span class="n"&gt;req&lt;/span&gt;.&lt;span class="n"&gt;hdr_cnt&lt;/span&gt;(&lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt;) &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; } !{ &lt;span class="n"&gt;ssl_fc&lt;/span&gt; }
&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;       &lt;span class="n"&gt;if&lt;/span&gt; { &lt;span class="n"&gt;req&lt;/span&gt;.&lt;span class="n"&gt;hdr_cnt&lt;/span&gt;(&lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt;) &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; } { &lt;span class="n"&gt;ssl_fc&lt;/span&gt; }
&lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;-&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt;-&lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;  &lt;span class="n"&gt;if&lt;/span&gt; { &lt;span class="n"&gt;req&lt;/span&gt;.&lt;span class="n"&gt;hdr_cnt&lt;/span&gt;(&lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;Proto&lt;/span&gt;-&lt;span class="n"&gt;Version&lt;/span&gt;) &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; } { &lt;span class="n"&gt;ssl_fc_alpn&lt;/span&gt; -&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the file changed, now we can add it to the router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create a configmap to hold the file&lt;/span&gt;
oc &lt;span class="nt"&gt;-n&lt;/span&gt; default create configmap customrouter &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;haproxy-config.template
&lt;span class="c"&gt;# add the configmap as a volume&lt;/span&gt;
oc &lt;span class="nt"&gt;-n&lt;/span&gt; default &lt;span class="nb"&gt;set &lt;/span&gt;volume dc/router &lt;span class="nt"&gt;--add&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config-volume &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--mount-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/haproxy/conf/custom &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"configMap": { "name": "customrouter"}}'&lt;/span&gt;
&lt;span class="c"&gt;# set an environment variable to tell the router were to find the configuration&lt;/span&gt;
oc &lt;span class="nt"&gt;-n&lt;/span&gt; default &lt;span class="nb"&gt;set env &lt;/span&gt;dc/router &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;TEMPLATE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/haproxy/conf/custom/haproxy-config.template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, Openshift will redeploy the router and the configuration will take effect.&lt;/p&gt;

</description>
      <category>openshift</category>
      <category>router</category>
      <category>proxy</category>
      <category>ssl</category>
    </item>
    <item>
      <title>Solving minishift fresh instance problems</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Sun, 04 Aug 2019 21:21:14 +0000</pubDate>
      <link>https://forem.com/livioribeiro/solving-minishift-fresh-instance-problems-5a5j</link>
      <guid>https://forem.com/livioribeiro/solving-minishift-fresh-instance-problems-5a5j</guid>
      <description>&lt;p&gt;When I started working with minishift, I did not have much experience with Openshift. I was struggling with some problems and I had no idea what was going on. These were the problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Builds failed to push to registry&lt;/li&gt;
&lt;li&gt;Deployments got stuck when pulling image&lt;/li&gt;
&lt;li&gt;Could not access an application via a Route&lt;/li&gt;
&lt;li&gt;Could not log in to the web console as administrator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first three problems were caused by the "docker-registry" and "router" deployments on the "default" namespace that failed to start.&lt;/p&gt;

&lt;p&gt;The solution to these problems is quite simple and involve just a few commands on the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# login first as admin user&lt;/span&gt;
oc login &lt;span class="nt"&gt;-u&lt;/span&gt; system:admin
&lt;span class="c"&gt;# restart "docker-registry"&lt;/span&gt;
oc &lt;span class="nt"&gt;-n&lt;/span&gt; default rollout latest dc/docker-registry
&lt;span class="c"&gt;# restart "router"&lt;/span&gt;
oc &lt;span class="nt"&gt;-n&lt;/span&gt; default rollout latest dc/router
&lt;span class="c"&gt;# allow "admin" user to log in to the web console&lt;/span&gt;
oc adm policy add-cluster-role-to-user cluster-admin admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can work with minishift with no more problems!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>minishift</category>
      <category>openshift</category>
      <category>fix</category>
    </item>
    <item>
      <title>Using LXD and Ansible to simulate infrastructure</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Wed, 05 Dec 2018 01:25:33 +0000</pubDate>
      <link>https://forem.com/livioribeiro/using-lxd-and-ansible-to-simulate-infrastructure-2g8l</link>
      <guid>https://forem.com/livioribeiro/using-lxd-and-ansible-to-simulate-infrastructure-2g8l</guid>
      <description>&lt;p&gt;When dealing with software like &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt;, &lt;a href="https://www.okd.io/" rel="noopener noreferrer"&gt;Openshift&lt;/a&gt; and &lt;a href="https://rancher.com/" rel="noopener noreferrer"&gt;Rancher&lt;/a&gt;, it may be challenging to test an application locally. It is true that we have tools like &lt;a href="https://github.com/kubernetes/minikube" rel="noopener noreferrer"&gt;minikube&lt;/a&gt; and &lt;a href="https://www.okd.io/minishift/" rel="noopener noreferrer"&gt;minishift&lt;/a&gt;, but if we are working with the infrastructure, simulating a cluster with virtual machines can quickly consume all our available RAM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linuxcontainers.org/lxd/" rel="noopener noreferrer"&gt;LXD&lt;/a&gt; can help solve the memory problem by using containers as if they were full blown virtual machines! Unlike Docker, which uses containers to run a single process until it finishes, LXD uses containers to spawn an operating system image and use it as a complete server.&lt;/p&gt;

&lt;p&gt;To install LXD you can follow the &lt;a href="https://linuxcontainers.org/lxd/getting-started-cli/" rel="noopener noreferrer"&gt;instructions on the official website&lt;/a&gt;, but after installing we have to initialize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The default values are good enough&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lxd init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed the &lt;a href="https://linuxcontainers.org/lxd/getting-started-cli/" rel="noopener noreferrer"&gt;getting started&lt;/a&gt;, you may have created some containers using the cli, but now comes the interesting part: We can use &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt; to automate the creation and provisioning of our servers!&lt;/p&gt;

&lt;p&gt;To our LXD/Ansible project we are going to create a &lt;a href="https://www.nomadproject.io/" rel="noopener noreferrer"&gt;Nomad&lt;/a&gt; cluster with &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; and &lt;a href="https://traefik.io/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I chose Nomad because of its simplicity and versatility: it can run services as docker containers, like Kubernetes, but also java applications and any executable available in the host, isolated using the operating system resources (e.g. cgroups, namespaces and chroot on linux). You can &lt;a href="https://www.nomadproject.io/docs/drivers/index.html" rel="noopener noreferrer"&gt;see in the documentation&lt;/a&gt; what Nomad is able to run.&lt;/p&gt;

&lt;p&gt;Consul is used by Nomad for service discovery, key-value storage and to bootstrap the cluster. Nomad can also run without Consul.&lt;/p&gt;

&lt;p&gt;Traefik will proxy requests to the services deployed in the cluster. It will use Consul service catalog as the configuration backend, so the routes to services will be automatically configured.&lt;/p&gt;

&lt;p&gt;The completed tutorial can be found &lt;a href="https://github.com/livioribeiro/nomad-lxd-ansible" rel="noopener noreferrer"&gt;in this repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;step 0: Planning&lt;/li&gt;
&lt;li&gt;step 1: Consul&lt;/li&gt;
&lt;li&gt;step 2: Nomad&lt;/li&gt;
&lt;li&gt;step 3: Traefik&lt;/li&gt;
&lt;li&gt;step 4: Deploying Services&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  step 0: Planning
&lt;/h2&gt;

&lt;p&gt;Our cluster will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 Consul nodes operating in server mode

&lt;ul&gt;
&lt;li&gt;consul1: &lt;code&gt;10.99.0.101&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;consul2: &lt;code&gt;10.99.0.102&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;consul3: &lt;code&gt;10.99.0.103&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;3 Nomad nodes operating in server mode

&lt;ul&gt;
&lt;li&gt;nomad-server1: &lt;code&gt;10.99.0.111&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;nomad-server2: &lt;code&gt;10.99.0.112&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;nomad-server3: &lt;code&gt;10.99.0.113&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;3 Nomad nodes operating in client mode

&lt;ul&gt;
&lt;li&gt;nomad-client1: &lt;code&gt;10.99.0.121&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;nomad-client2: &lt;code&gt;10.99.0.122&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;nomad-client3: &lt;code&gt;10.99.0.123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The nomad clients will docker and openjdk installed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;1 Traefik node

&lt;ul&gt;
&lt;li&gt;proxy: &lt;code&gt;10.99.0.100&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;All non Consul nodes will have Consul operating in client node.&lt;/p&gt;

&lt;p&gt;And for ansible, we will create a project with the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/projects/nomad-lxd-ansible
├── cache/
├── inventory/
│   └── hosts
├── roles/
├── ansible.cfg
└── playbook.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure can be created with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/projects/nomad-lxd-ansible
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/nomad-lxd-ansible
&lt;span class="nb"&gt;mkdir &lt;/span&gt;cache inventory roles
&lt;span class="nb"&gt;touch &lt;/span&gt;ansible.cfg playboook.yml inventory/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the &lt;code&gt;cache&lt;/code&gt; directory, we will download the Consul, Nomad and Traefik binaries only once in the host. This way, we avoid downloading again in every host.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;ansible.cfg&lt;/code&gt; we will tell ansible to use our inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# ansible.cfg
&lt;/span&gt;&lt;span class="nn"&gt;[defaults]&lt;/span&gt;
&lt;span class="py"&gt;inventory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;inventory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add our cluster nodes to the inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# inventory/hosts
&lt;/span&gt;&lt;span class="err"&gt;proxy&lt;/span&gt;           &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.100&lt;/span&gt;

&lt;span class="nn"&gt;[consul_servers]&lt;/span&gt;
&lt;span class="err"&gt;consul1&lt;/span&gt;         &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.101&lt;/span&gt;
&lt;span class="err"&gt;consul2&lt;/span&gt;         &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.102&lt;/span&gt;
&lt;span class="err"&gt;consul3&lt;/span&gt;         &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.103&lt;/span&gt;

&lt;span class="nn"&gt;[nomad_servers]&lt;/span&gt;
&lt;span class="err"&gt;nomad-server1&lt;/span&gt;   &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.111&lt;/span&gt;
&lt;span class="err"&gt;nomad-server2&lt;/span&gt;   &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.112&lt;/span&gt;
&lt;span class="err"&gt;nomad-server3&lt;/span&gt;   &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.113&lt;/span&gt;

&lt;span class="nn"&gt;[nomad_clients]&lt;/span&gt;
&lt;span class="err"&gt;nomad-client1&lt;/span&gt;   &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.121&lt;/span&gt;
&lt;span class="err"&gt;nomad-client2&lt;/span&gt;   &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.122&lt;/span&gt;
&lt;span class="err"&gt;nomad-client3&lt;/span&gt;   &lt;span class="py"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.99.0.123&lt;/span&gt;

&lt;span class="nn"&gt;[all:vars]&lt;/span&gt;
&lt;span class="py"&gt;ansible_connection&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;lxd&lt;/span&gt;
&lt;span class="py"&gt;ansible_python_interpreter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/python3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ip_address&lt;/code&gt; variable will be referenced in the playbook.&lt;/p&gt;

&lt;p&gt;And to create the containers, add the following to &lt;code&gt;playbook.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# playbook.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
  &lt;span class="c1"&gt;# run this task in the host&lt;/span&gt;
  &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create containers&lt;/span&gt;
      &lt;span class="c1"&gt;# get all host names from inventory&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;groups['all']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="c1"&gt;# use lxd_container module from ansible to create containers&lt;/span&gt;
      &lt;span class="na"&gt;lxd_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# container name is the hostname&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image&lt;/span&gt;
          &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
          &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://images.linuxcontainers.org&lt;/span&gt;
          &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu/bionic/amd64&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# nomad clients need some privileges to be able to run docker containers&lt;/span&gt;
          &lt;span class="na"&gt;security.nesting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;security.privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;devices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# configure network interface&lt;/span&gt;
          &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nic&lt;/span&gt;
            &lt;span class="na"&gt;nictype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridged&lt;/span&gt;
            &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lxdbr0&lt;/span&gt;
            &lt;span class="c1"&gt;# get ip address from inventory&lt;/span&gt;
            &lt;span class="na"&gt;ipv4.address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hostvars[item].ip_address&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="c1"&gt;# # uncomment if you installed lxd using snap&lt;/span&gt;
        &lt;span class="c1"&gt;# url: unix:/var/snap/lxd/common/lxd/unix.socket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run &lt;code&gt;ansible-playbook playbook.yml&lt;/code&gt; to create our nodes with lxd.&lt;/p&gt;

&lt;h2&gt;
  
  
  step 1: Consul
&lt;/h2&gt;

&lt;p&gt;Let's tell ansible to download and setup Consul.&lt;/p&gt;

&lt;p&gt;Edit the playbook to be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# playbook.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
  &lt;span class="c1"&gt;# run this task in the host&lt;/span&gt;
  &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="c1"&gt;# set urls as variables&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;consul_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.4.0"&lt;/span&gt;
    &lt;span class="na"&gt;consul_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://releases.hashicorp.com/consul/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/consul_{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}_linux_amd64.zip"&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create containers&lt;/span&gt;
      &lt;span class="c1"&gt;# get all host names from inventory&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;groups['all']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="c1"&gt;# use lxd_container module from ansible to create containers&lt;/span&gt;
      &lt;span class="na"&gt;lxd_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# container name is the hostname&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image&lt;/span&gt;
          &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
          &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://images.linuxcontainers.org&lt;/span&gt;
          &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu/bionic/amd64&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# nomad clients need some privileges to be able to run docker containers&lt;/span&gt;
          &lt;span class="na"&gt;security.nesting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;security.privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;devices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# configure network interface&lt;/span&gt;
          &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nic&lt;/span&gt;
            &lt;span class="na"&gt;nictype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridged&lt;/span&gt;
            &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lxdbr0&lt;/span&gt;
            &lt;span class="c1"&gt;# get ip address from inventory&lt;/span&gt;
            &lt;span class="na"&gt;ipv4.address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hostvars[item].ip_address&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="c1"&gt;# # uncomment if you installed lxd using snap&lt;/span&gt;
        &lt;span class="c1"&gt;# url: unix:/var/snap/lxd/common/lxd/unix.socket&lt;/span&gt;

    &lt;span class="c1"&gt;# ensure cache directory exists&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create cache directory&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;directory&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fetch applications&lt;/span&gt;
      &lt;span class="na"&gt;unarchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
        &lt;span class="na"&gt;creates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;remote_src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul_servers&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hosts belonging to the &lt;code&gt;consul_servers&lt;/code&gt; group will have the role &lt;code&gt;consul_server&lt;/code&gt;. We will also create another role called &lt;code&gt;consul_service&lt;/code&gt; that will copy the consul binary to the host and setup the service. We split the role this way in order to have a role &lt;code&gt;consul_client&lt;/code&gt; that also needs consul binary and service, but with different configuration.&lt;/p&gt;

&lt;p&gt;Roles are located under the &lt;code&gt;roles&lt;/code&gt; directory, and for the three roles for Consul we will have the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;roles/
├── consul_client
│   ├── tasks
│   │   └── main.yml
│   └── templates
│       └── consul.hcl.j2
├── consul_server
│   ├── tasks
│   │   └── main.yml
│   └── templates
│       └── consul.hcl.j2
└── consul_service
    ├── files
    │   └── consul.service
    └── tasks
        └── main.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can create the structure with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_service/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_service/files &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_server/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_server/templates &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_client/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_client/templates &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_service/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_service/files/consul.service &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_server/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_server/templates/consul.hcl.j2 &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_client/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/consul_client/templates/consul.hcl.j2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role: consul_service
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;roles/consul_service/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/consul_service/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install consul&lt;/span&gt;
  &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache/consul&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/local/bin/&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create consul service&lt;/span&gt;
  &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul.service&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/systemd/system/&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create consul directories&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;directory&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/consul.d&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/consul&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/consul_service/files/consul.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# roles/consul_service/files/consul.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"HashiCorp Consul - A service mesh solution"&lt;/span&gt;
&lt;span class="py"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://www.consul.io/&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;ConditionFileNotEmpty&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/consul.d/consul.hcl&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/consul agent -config-dir=/etc/consul.d&lt;/span&gt;
&lt;span class="py"&gt;ExecReload&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/consul reload&lt;/span&gt;
&lt;span class="py"&gt;KillMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;process&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;LimitNOFILE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;65536&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role: consul_server
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;roles/consul_server/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/consul_server/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;import_role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul_service&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy consul config&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul.hcl.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/consul.d/consul.hcl&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start consul&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/consul_server/templates/consul.hcl.j2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;# roles/consul_server/templates/consul.hcl.j2
data_dir = "/var/consul"

server = true
advertise_addr = "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"

client_addr = "127.0.0.1 &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"
enable_script_checks = true

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;ansible_hostname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'consul1'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;
ui = true
bootstrap_expect = 3
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;
retry_join = [ "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;hostvars.consul1.ansible_hostname&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;" ]
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role: consul_client
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;roles/consul_client/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/consul_client/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;import_role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul_service&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy consul config&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul.hcl.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/consul.d/consul.hcl&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start consul&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/consul_client/templates/consul.hcl.j2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;# roles/consul_client/templates/consul.hcl.j2
data_dir = "/var/consul"
server = false

advertise_addr = "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"
client_addr = "127.0.0.1 &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"
enable_script_checks = true
retry_join = [ "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;hostvars.consul1.ansible_hostname&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;" ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  step 2: Nomad
&lt;/h2&gt;

&lt;p&gt;Nomad setup will be very similar to Consul.&lt;/p&gt;

&lt;p&gt;Edit the playbook to include Nomad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# playbook.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
  &lt;span class="c1"&gt;# run this task in the host&lt;/span&gt;
  &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="c1"&gt;# set urls as variables&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;consul_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.4.0"&lt;/span&gt;
    &lt;span class="na"&gt;nomad_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.8.6"&lt;/span&gt;
    &lt;span class="na"&gt;consul_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://releases.hashicorp.com/consul/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/consul_{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}_linux_amd64.zip"&lt;/span&gt;
    &lt;span class="na"&gt;nomad_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://releases.hashicorp.com/nomad/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nomad_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/nomad_{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nomad_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}_linux_amd64.zip"&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create containers&lt;/span&gt;
      &lt;span class="c1"&gt;# get all host names from inventory&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;groups['all']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="c1"&gt;# use lxd_container module from ansible to create containers&lt;/span&gt;
      &lt;span class="na"&gt;lxd_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# container name is the hostname&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image&lt;/span&gt;
          &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
          &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://images.linuxcontainers.org&lt;/span&gt;
          &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu/bionic/amd64&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# nomad clients need some privileges to be able to run docker containers&lt;/span&gt;
          &lt;span class="na"&gt;security.nesting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;security.privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;devices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# configure network interface&lt;/span&gt;
          &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nic&lt;/span&gt;
            &lt;span class="na"&gt;nictype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridged&lt;/span&gt;
            &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lxdbr0&lt;/span&gt;
            &lt;span class="c1"&gt;# get ip address from inventory&lt;/span&gt;
            &lt;span class="na"&gt;ipv4.address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hostvars[item].ip_address&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="c1"&gt;# uncomment if you installed lxd using snap&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unix:/var/snap/lxd/common/lxd/unix.socket&lt;/span&gt;

    &lt;span class="c1"&gt;# ensure cache directory exists&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create cache directory&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;directory&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fetch applications&lt;/span&gt;
      &lt;span class="na"&gt;unarchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
        &lt;span class="na"&gt;creates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;remote_src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nomad_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul_servers&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_server&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad_servers&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_client&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nomad_server&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad_clients&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_client&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nomad_client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to Consul, we will have the roles &lt;code&gt;nomad_service&lt;/code&gt;, &lt;code&gt;nomad_server&lt;/code&gt; and &lt;code&gt;nomad_client&lt;/code&gt;. But now we have two groups, &lt;code&gt;nomad_servers&lt;/code&gt; and &lt;code&gt;nomad_clients&lt;/code&gt;, each having its respective role, but both having the &lt;code&gt;consul_client&lt;/code&gt; role.&lt;/p&gt;

&lt;p&gt;We will also have a similar structure for the nomad roles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;roles/
├── nomad_client
│   ├── tasks
│   │   └── main.yml
│   └── templates
│       └── nomad.hcl.j2
├── nomad_server
│   ├── tasks
│   │   └── main.yml
│   └── templates
│       └── nomad.hcl.j2
└── nomad_service
    ├── files
    │   └── nomad.service
    └── tasks
        └── main.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create this structure with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_service/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_service/files &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_server/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_server/templates &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_client/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_client/templates &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_service/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_service/files/nomad.service &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_server/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_server/templates/nomad.hcl.j2 &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_client/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/nomad_client/templates/nomad.hcl.j2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role: nomad_service
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;roles/nomad_service/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/nomad_service/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install nomad&lt;/span&gt;
  &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache/nomad&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/local/bin/&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create nomad service&lt;/span&gt;
  &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad.service&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/systemd/system/&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create nomad directories&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;directory&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/nomad.d&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/nomad&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/nomad_service/files/nomad.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# roles/nomad_service/files/nomad.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"HashiCorp Nomad - Application scheduler"&lt;/span&gt;
&lt;span class="py"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://www.nomadproject.io/&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;
&lt;span class="py"&gt;ConditionFileNotEmpty&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/nomad.d/nomad.hcl&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/nomad agent -config=/etc/nomad.d/nomad.hcl&lt;/span&gt;
&lt;span class="py"&gt;ExecReload&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -HUP $MAINPID&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role: nomad_server
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;roles/nomad_server/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/nomad_server/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;import_role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad_service&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy nomad config&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad.hcl.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/nomad.d/nomad.hcl&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start nomad&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/nomad_server/templates/nomad.hcl.j2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;# roles/nomad_server/templates/nomad.hcl.j2
data_dir  = "/var/nomad"

advertise {
  http = "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"
  rpc  = "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"
  serf = "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"
}

server {
  enabled          = true
  bootstrap_expect = 3
  raft_protocol    = 3
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role: nomad_client
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;roles/nomad_client/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/nomad_client/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;import_role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad_service&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;update apt cache&lt;/span&gt;
  &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install docker and openjdk&lt;/span&gt;
  &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;packages&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker.io&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;openjdk-11-jdk-headless&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start docker service&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy nomad config&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad.hcl.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/nomad.d/nomad.hcl&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start nomad&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/nomad_client/templates/nomad.hcl.j2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;data_dir  = "/var/nomad"

bind_addr = "&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;"

client {
  enabled = true
  network_interface = "eth0"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  step 3: Traefik
&lt;/h2&gt;

&lt;p&gt;Setting up Traefik will be similar to Consul and Nomad, but a bit simpler: there will be only one role named &lt;code&gt;proxy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Edit the playbook to include Traefik:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# playbook.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
  &lt;span class="c1"&gt;# run this task in the host&lt;/span&gt;
  &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="c1"&gt;# set urls as variables&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;consul_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.4.0"&lt;/span&gt;
    &lt;span class="na"&gt;nomad_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.8.6"&lt;/span&gt;
    &lt;span class="na"&gt;traefik_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.7.5"&lt;/span&gt;
    &lt;span class="na"&gt;consul_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://releases.hashicorp.com/consul/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/consul_{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}_linux_amd64.zip"&lt;/span&gt;
    &lt;span class="na"&gt;nomad_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://releases.hashicorp.com/nomad/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nomad_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/nomad_{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nomad_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}_linux_amd64.zip"&lt;/span&gt;
    &lt;span class="na"&gt;traefik_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://github.com/containous/traefik/releases/download/v{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;traefik_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/traefik_linux-amd64"&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create containers&lt;/span&gt;
      &lt;span class="c1"&gt;# get all host names from inventory&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;groups['all']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="c1"&gt;# use lxd_container module from ansible to create containers&lt;/span&gt;
      &lt;span class="na"&gt;lxd_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# container name is the hostname&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image&lt;/span&gt;
          &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
          &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://images.linuxcontainers.org&lt;/span&gt;
          &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu/bionic/amd64&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# nomad clients need some privileges to be able to run docker containers&lt;/span&gt;
          &lt;span class="na"&gt;security.nesting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;security.privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;['nomad-client1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client2',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'nomad-client3']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'false'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;devices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# configure network interface&lt;/span&gt;
          &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nic&lt;/span&gt;
            &lt;span class="na"&gt;nictype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridged&lt;/span&gt;
            &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lxdbr0&lt;/span&gt;
            &lt;span class="c1"&gt;# get ip address from inventory&lt;/span&gt;
            &lt;span class="na"&gt;ipv4.address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hostvars[item].ip_address&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="c1"&gt;# # uncomment if you installed lxd using snap&lt;/span&gt;
        &lt;span class="c1"&gt;# url: unix:/var/snap/lxd/common/lxd/unix.socket&lt;/span&gt;

    &lt;span class="c1"&gt;# ensure cache directory exists&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create cache directory&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;directory&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fetch applications&lt;/span&gt;
      &lt;span class="na"&gt;unarchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
        &lt;span class="na"&gt;creates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;remote_src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consul_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nomad_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fecth traefik&lt;/span&gt;
      &lt;span class="na"&gt;get_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;traefik_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache/traefik&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul_servers&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_server&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad_servers&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_client&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nomad_server&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nomad_clients&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_client&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nomad_client&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;consul_client&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure needed for the &lt;code&gt;proxy&lt;/code&gt; role will be like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;roles/
└── proxy
    ├── files
    │   └── traefik.service
    ├── tasks
    │   └── main.yml
    └── templates
        └── traefik.toml.j2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can create the structure with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  roles/proxy/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  roles/proxy/files &lt;span class="se"&gt;\&lt;/span&gt;
  roles/proxy/templates &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  roles/proxy/tasks/main.yml &lt;span class="se"&gt;\&lt;/span&gt;
  roles/proxy/files/traefik.service &lt;span class="se"&gt;\&lt;/span&gt;
  roles/proxy/templates/traefik.toml.j2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/proxy/tasks/main.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# roles/proxy/tasks/main.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install traefik&lt;/span&gt;
  &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache/traefik&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/local/bin/&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create traefik service&lt;/span&gt;
  &lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.service&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/systemd/system/&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create traefik config directory&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/traefik&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;directory&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy traefik config&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.toml.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/traefik/traefik.toml&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start traefik&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/proxy/files/traefik.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# roles/proxy/files/traefik.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Traefik Proxy"&lt;/span&gt;
&lt;span class="py"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://traefik.io&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;ConditionFileNotEmpty&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/traefik/traefik.toml&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/traefik --configfile=/etc/traefik/traefik.toml&lt;/span&gt;
&lt;span class="py"&gt;ExecReload&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -HUP $MAINPID&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;roles/proxy/templates/traefik.toml.j2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;# roles/proxy/templates/traefik.toml.j2
[file]

# Backends
[backends]
  [backends.consul]
    [backends.consul.servers]
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'consul_servers'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      [backends.consul.servers.&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;]
        url = "http://&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;hostvars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;:8500"
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

  [backends.nomad]
    [backends.nomad.servers]
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'nomad_servers'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      [backends.nomad.servers.&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;]
        url = "http://&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;hostvars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;:4646"
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

# Frontends
[frontends]
  [frontends.consul]
  backend = "consul"
    [frontends.consul.routes.route1]
    rule = "Host:consul.&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;.nip.io"

  [frontends.nomad]
  backend = "nomad"
    [frontends.nomad.routes.route1]
    rule = "Host:nomad.&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;.nip.io"

[consulCatalog]
endpoint = "127.0.0.1:8500"
exposedByDefault = false
domain = "service.&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_eth0.ipv4.address&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;.nip.io"

[api]
dashboard = true
debug = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Traefik configuration grants access to Consul and Nomad dashboards and configures Consul Catalog backend. This way, services can be automatically discovered and exposed.&lt;/p&gt;

&lt;p&gt;We set &lt;code&gt;exposedByDefault = false&lt;/code&gt; so only the services marked with a specific tag will be exposed, therefore reducing the risk of accidentally making an internal service public.&lt;/p&gt;

&lt;h2&gt;
  
  
  step 4: Deploying Services
&lt;/h2&gt;

&lt;p&gt;Now that we have everything in place, we can build our cluster and see it working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-playbook playbook.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went OK, we can now access the Trafik dashboard at &lt;a href="http://10.99.0.100:8080" rel="noopener noreferrer"&gt;http://10.99.0.100:8080&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn73zl17ejhuzsjspyptu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn73zl17ejhuzsjspyptu.png" alt="Traefik dashboard 1" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not very impressive, since we do not have any service deployed yet, but if we go to the &lt;code&gt;file&lt;/code&gt; tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7z4dbf7wc2m61aebs2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7z4dbf7wc2m61aebs2k.png" alt="Traefik dashboard 2" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There it is! It shows Consul and Nomad!... Still not very impressive, we configured them statically in Traefik configuration. Let's do something more interesting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter any nomad server node:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lxc &lt;span class="nb"&gt;exec &lt;/span&gt;nomad-server1 &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the nomad service definition
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hello.nomad &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
job "hello-world" {
  datacenters = ["dc1"]

  group "example" {
    count = 3
    task "server" {
      # we will run a docker container
      driver = "docker"

      # resouces required by the task
      resources {
        network {
          # require a random port named "http"
          port "http" {}
        }
      }

      config {
        # docker image to run
        image = "hashicorp/http-echo"
        args = [
          "-listen", ":8080",
          "-text", "hello world",
        ]

        # map the random port to port 8080 on the task
        port_map = {
          http = 8080
        }
      }

      # exposed service
      service {
        # service name, compose the url like 'hello-world.service.myorg.com'
        name = "hello-world"
        # service will bind to this port
        port = "http"
        # tell traefik to expose this service
        tags = ["traefik.enable=true"]
      }
    }
  }
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;service&lt;/code&gt; section, &lt;code&gt;tags = ["traefik.enable=true"]&lt;/code&gt; is what will tell Traefik to expose the service.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy!
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad job run hello.nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will output something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@nomad-server1:~# nomad job run hello.nomad 
==&amp;gt; Monitoring evaluation "be583c44"
    Evaluation triggered by job "hello-world"
    Allocation "a19978c9" created: node "d9d3daa0", group "example"
    Allocation "0e0e0015" created: node "7fdbbd1f", group "example"
    Allocation "690efcc2" created: node "ab36a46e", group "example"
    Evaluation status changed: "pending" -&amp;gt; "complete"
==&amp;gt; Evaluation "be583c44" finished with status "complete"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if we go to Traefik dashboard again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h3pzl5zked1udrup2yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h3pzl5zked1udrup2yb.png" alt="Traefik dashboard 3" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's there!&lt;/p&gt;

&lt;p&gt;We can access the service by opening &lt;a href="http://hello-world.service.10.99.0.100.nip.io" rel="noopener noreferrer"&gt;http://hello-world.service.10.99.0.100.nip.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But wait a minute, what is that &lt;code&gt;10.99.0.100.nip.io&lt;/code&gt; url? It is a service that to the ip address you put before it. Pretty handy for testing. Go to [nip.io] for more info.&lt;/p&gt;

&lt;p&gt;This service, however, is quite boring, let's deploy something more interesting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the nomad service definition
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gitea.nomad &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
job "gitea" {
  datacenters = ["dc1"]

  group "gitea" {
    count = 1

    ephemeral_disk {
      # try to deploy this service on the same node every time
      sticky  = true
      # try to migrate the ephemeral disk if possible 
      migrate = true
      # set the ephemeral disk size to 2GB 
      size    = "2048"
    }

    task "server" {
      driver = "docker"

      config {
        image = "gitea/gitea:1.6"

        port_map = {
          http = 3000
        }

        # with docker driver, it is possible to mount volumes insinde the container from the ephemeral disk
        volumes = [
          "local/gitea-data:/data"
        ]
      }

      resources {
        network {
          port "http" {}
        }
      }

      service {
        name = "gitea"
        port = "http"
        tags = ["traefik.enable=true"]
      }
    }
  }
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Plan the job deployment
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad job plan gitea.nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will output something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+ Job: "gitea"
+ Task Group: "gitea" (1 create)
  + Task: "server" (forces create)

Scheduler dry-run:
- All tasks successfully allocated.

Job Modify Index: 0
To submit the job with version verification run:

nomad job run -check-index 0 gitea.nomad

When running the job with the check-index flag, the job will only be run if the
server side version matches the job modify index returned. If the index has
changed, another user has modified the job and the plan's results are
potentially invalid.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We went a bit different here. We planned the deployment or, in other words, we validated the deployment config (gitea.nomad) and generated an index number (&lt;code&gt;0&lt;/code&gt; in the case of a new deployment), so we do not risk updating a deployment after another operator did a deployment of his own.&lt;/p&gt;

&lt;p&gt;To deploy the service, just follow the instructions nomad gave us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nomad job run &lt;span class="nt"&gt;-check-index&lt;/span&gt; 0 gitea.nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a few minutes it will appear in the Traefik dashboard and will be accessible at &lt;a href="http://gitea.service.10.99.0.100.nip.io:" rel="noopener noreferrer"&gt;http://gitea.service.10.99.0.100.nip.io:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxdsw6vewarvp6upkv7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxdsw6vewarvp6upkv7n.png" alt="Gitea" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;LXD is a very useful tool to test solutions that would be otherwise impossible or impractical with virtual machines. When combined with Ansible, you can quickly create test environments to evaluate these solutions in a way that is closer to a production environment than a scaled down tool like minikube or minishift (which are still completely valid tools if you are focusing only in the applications deployed in these solutions).&lt;/p&gt;

&lt;p&gt;Nomad is a great software. Along with Consul, you have a simple yet very powerful solution to orchestrate your services. It can run docker, rkt and lxc containers, java applications packaged in a &lt;code&gt;.jar&lt;/code&gt; file (&lt;a href="http://spring.io/" rel="noopener noreferrer"&gt;like a Spring Boot application&lt;/a&gt;), and even binaries (&lt;a href="https://rocket.rs/" rel="noopener noreferrer"&gt;like a Rocket application&lt;/a&gt;), &lt;a href="https://www.nomadproject.io/docs/job-specification/artifact.html" rel="noopener noreferrer"&gt;which can be retrieved by nomad in the job definition&lt;/a&gt; and executed using the &lt;a href="https://www.nomadproject.io/docs/drivers/exec.html" rel="noopener noreferrer"&gt;isolation primitives provided by the operating system&lt;/a&gt;. It is not as feature complete as Kubernetes but is a lot easier to operate.&lt;/p&gt;

&lt;p&gt;Traefik integrates with a lot of services to provide auto configuration. Its Consul Catalog integration provides an incredible solution to a Nomad cluster.&lt;/p&gt;

</description>
      <category>lxd</category>
      <category>ansible</category>
      <category>nomad</category>
      <category>traefik</category>
    </item>
    <item>
      <title>Going further on Openshift</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Sun, 19 Aug 2018 00:25:17 +0000</pubDate>
      <link>https://forem.com/livioribeiro/going-further-on-openshift-4m80</link>
      <guid>https://forem.com/livioribeiro/going-further-on-openshift-4m80</guid>
      <description>&lt;p&gt;In my previous post I gave a brief introduction about what is Openshift (&lt;a href="https://okd.io" rel="noopener noreferrer"&gt;now OKD&lt;/a&gt;) and how we could easily deploy &lt;a href="https://gitea.io" rel="noopener noreferrer"&gt;Gitea&lt;/a&gt;. Now we will focus on building and deploying java applications using the "source2image" (S2I) strategy.&lt;/p&gt;

&lt;p&gt;But what is this S2I?&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://github.com/openshift/source-to-image" rel="noopener noreferrer"&gt;S2I project site&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Source-to-Image (S2I) is a toolkit and workflow for building reproducible Docker images from source code. S2I produces ready-to-run images by injecting source code into a Docker container and letting the container prepare that source code for execution. By creating self-assembling builder images, you can version and control your build environments exactly like you use Docker images to version your runtime environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What will happen in practice is that Openshift will take your project's source, combine it with a &lt;em&gt;builder&lt;/em&gt; docker image and generate the final runtime image used to run the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the development environment
&lt;/h2&gt;

&lt;p&gt;To run our development environment, we will use &lt;a href="https://github.com/minishift/minishift" rel="noopener noreferrer"&gt;minishift&lt;/a&gt; to create an openshift cluster. Use the commands below to download and install minishift (if you are using linux):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MINISHIFT_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.22.0
&lt;span class="nv"&gt;MINISHIFT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/minishift/minishift/releases/download/v&lt;span class="nv"&gt;$MINISHIFT_VERSION&lt;/span&gt;/minishift-&lt;span class="nv"&gt;$MINISHIFT_VERSION&lt;/span&gt;&lt;span class="nt"&gt;-linux-amd64&lt;/span&gt;.tgz

curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/minishift.tgz &lt;span class="nv"&gt;$MINISHIFT_URL&lt;/span&gt;

&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /tmp &lt;span class="nt"&gt;--wildcards&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;/minishift &lt;span class="nt"&gt;--strip&lt;/span&gt; 1 &lt;span class="nt"&gt;-zf&lt;/span&gt; /tmp/minishift.tgz
&lt;span class="nb"&gt;sudo install&lt;/span&gt; /tmp/minishift /usr/local/bin/minishift

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With minishift installed, we can configure and start it (which may take several minutes):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# change values according to your workstation&lt;/span&gt;
&lt;span class="nv"&gt;MINISHIFT_VM_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;virtualbox
&lt;span class="nv"&gt;MINISHIFT_MEMORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8G
&lt;span class="nv"&gt;MINISHIFT_CPUS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4
&lt;span class="nv"&gt;MINISHIFT_DISK_SIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100G

&lt;span class="c"&gt;# configure minishift&lt;/span&gt;
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;vm-driver &lt;span class="nv"&gt;$MINISHIFT_VM_DRIVER&lt;/span&gt;
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;memory &lt;span class="nv"&gt;$MINISHIFT_MEMORY&lt;/span&gt;
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;cpus &lt;span class="nv"&gt;$MINISHIFT_CPUS&lt;/span&gt;
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;disk-size &lt;span class="nv"&gt;$MINISHIFT_DISK_SIZE&lt;/span&gt;

minishift start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To interact with Openshift, we need to download the &lt;code&gt;oc&lt;/code&gt; command line tool, but with minishift we can use the bundled one, just execute the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;minishift oc-env&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we login to openshift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc login 192.168.99.100:8443 &lt;span class="nt"&gt;-u&lt;/span&gt; admin
&lt;span class="c"&gt;# use 'admin' as password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying the application
&lt;/h2&gt;

&lt;p&gt;Now that we have minishift up and running, we can start deploying some application. Let's start with a &lt;a href="https://github.com/sclorg/django-ex" rel="noopener noreferrer"&gt;django example application&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But first, let's create the &lt;em&gt;project&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc new-project django-example &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="s1"&gt;'Django Example'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will print something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now using project "django-example" on server "https://192.168.99.100:8443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app centos/ruby-22-centos7~https://github.com/openshift/ruby-ex.git

to build a new example application in Ruby.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Do not worry with the message about a ruby application, we are actually deploying a &lt;strong&gt;python&lt;/strong&gt; application ;)&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;project&lt;/em&gt; is the openshift terminology to a namespace, a place to group related resources and define access rules to these resources. For example, we could allow user "snake" to view our "django-example" project, user "rattlesnake" to view and edit (trigger builds and deploys, etc.) and not give any access to user "badger".&lt;/p&gt;

&lt;p&gt;With the project created, we can create the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc new-app python:3.6~https://github.com/sclorg/django-ex.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might be asking "what is going on?". Well, we are telling openshift to take the &lt;code&gt;python:3.6&lt;/code&gt; image and combine it with the source code available at &lt;a href="https://github.com/sclorg/django-ex.git" rel="noopener noreferrer"&gt;https://github.com/sclorg/django-ex.git&lt;/a&gt;. The syntax is "image"~"source".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But be careful!&lt;/strong&gt; The &lt;code&gt;python:3.6&lt;/code&gt; is &lt;strong&gt;not the official python image from docker hub&lt;/strong&gt;! It is instead from &lt;a href="https://hub.docker.com/r/centos/python-36-centos7/" rel="noopener noreferrer"&gt;centos/python-36-centos7&lt;/a&gt; and was imported to the openshift integrated registry! It is an S2I image to build and run python projects! You can view other images using the command &lt;code&gt;oc get imagestream -n openshift&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The output of the &lt;code&gt;oc new-app&lt;/code&gt; will have some information about the application, with the following at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; Success
    Build scheduled, use 'oc logs -f bc/django-ex' to track its progress.
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose svc/django-ex' 
    Run 'oc status' to view your app.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suggested command &lt;code&gt;oc expose svc/django-ex&lt;/code&gt; will do something very important: it will expose a &lt;em&gt;Service&lt;/em&gt; (a Kubernetes Service) to external traffic through a &lt;em&gt;Route&lt;/em&gt;. The &lt;em&gt;Route&lt;/em&gt; is very similar to a Service, but will handle external requests that matches a specific hostname and can also handle encryption (https).&lt;/p&gt;

&lt;p&gt;Let's expose our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc expose svc/django-ex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike previous commands, exposing a service does not produce much output, so let's list the available routes to get the one we just created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get routes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME        HOST/PORT                                        PATH      SERVICES    PORT       TERMINATION   WILDCARD
django-ex   django-ex-django-example.192.168.99.100.nip.io             django-ex   8080-tcp                 None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the route listens for the hostname &lt;code&gt;django-ex-django-example.192.168.99.100.nip.io&lt;/code&gt;. This &lt;code&gt;nip.io&lt;/code&gt; is a special service service that will redirect requests to the ip address contained in the hostname itself, is this case, 192.168.99.100, the ip address of the minishift vm.&lt;/p&gt;

&lt;p&gt;Accessing the route address will show the following screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzg3cnvvw3wtpvi9el1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzg3cnvvw3wtpvi9el1p.png" alt="openshift django example welcome screen" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Openshift web console
&lt;/h2&gt;

&lt;p&gt;Openshift also has a very nice web console accessible at [&lt;a href="https://192.168.99.100:8443/console" rel="noopener noreferrer"&gt;https://192.168.99.100:8443/console&lt;/a&gt;]. Just login with the same credentials we used before ("admin" for both username and password).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uxjilszu1phqrj3pfql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uxjilszu1phqrj3pfql.png" alt="openshift web console" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening the link to our project on the right will take us to the following page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcryv3hc0fvqox2ckh5bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcryv3hc0fvqox2ckh5bx.png" alt="openshift project overview" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is the project dashboard where we have an overview of the project, the applications deployed, routes, container status, etc. We can even scale the container!&lt;/p&gt;

&lt;p&gt;The web console has lots of featues, even though some actions still require using the cli, like creating a service by exposing the &lt;em&gt;Deployment&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is it for now. Next time we will get into the web console.&lt;/p&gt;

</description>
      <category>openshift</category>
      <category>devops</category>
      <category>docker</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>My journey through Openshift</title>
      <dc:creator>Livio Ribeiro</dc:creator>
      <pubDate>Thu, 19 Jul 2018 02:20:41 +0000</pubDate>
      <link>https://forem.com/livioribeiro/my-journey-through-openshift-13f2</link>
      <guid>https://forem.com/livioribeiro/my-journey-through-openshift-13f2</guid>
      <description>&lt;p&gt;Recently I started working with &lt;a href="https://www.openshift.org/" rel="noopener noreferrer"&gt;Openshift&lt;/a&gt; since the company I work for adopted it. It will be quite an interesting journey because I will have to adapt our services that currently run on JBoss and Tomcat to run on containers.&lt;/p&gt;

&lt;p&gt;But first, what exactly is Openshift?&lt;/p&gt;

&lt;p&gt;From my understanding, it is a distribution of &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; focused on providing an easier way to migrate, deploy and manage your applications on a containerized environment. It hides some of the complexities of Kubernetes while providing some nice features like its dashboard and the source-to-image (S2I) strategy.&lt;/p&gt;

&lt;p&gt;Openshift is maintained by RedHat.&lt;/p&gt;

&lt;p&gt;The S2I is very interesting: it combines a specially built docker image with your application's source code and build another docker image ready to be deployed. Each S2I image is specialized in building and deploying a different technology stack. For example, the JBoss S2I image will look for a &lt;code&gt;pom.xml&lt;/code&gt; in the sources, run &lt;code&gt;mvn package&lt;/code&gt;, copy the generated artifact to the deployments directory and, when run, will configure the datasource using environment variables. The Tomcat S2I image works similarly. RedHat provides images for Java, Python, Ruby, Nodejs and others.&lt;/p&gt;

&lt;p&gt;You can also deploy an image from docker hub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;To get started with Openshift, you can download the &lt;a href="https://github.com/minishift/minishift/releases" rel="noopener noreferrer"&gt;minishift&lt;/a&gt; tool, which will start a virtual machine running an instance of Openshift.&lt;/p&gt;

&lt;p&gt;But before we begin, we should tweak the virtual machine a little bit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minishift config &lt;span class="nb"&gt;set &lt;/span&gt;vm-driver virtualbox
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;memory 8G
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;cpus 4
minishift config &lt;span class="nb"&gt;set &lt;/span&gt;disk-size 100G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will tell minishift to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use virtualbox instead of KVM/Hyper-V/xhyve (you can still use those)&lt;/li&gt;
&lt;li&gt;set the memory to 8GB (but 4GB will do too)&lt;/li&gt;
&lt;li&gt;set the number of cpus to 4, so we can run a few more containers&lt;/li&gt;
&lt;li&gt;use a disk size of 100GB, so we do not run out of storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use lower values for memory and cpus, but it will be very limited (and sometimes openshift may freeze).&lt;/p&gt;

&lt;p&gt;To start minishift we can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minishift start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the machine is ready, we can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;minishift oc-env&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to have &lt;code&gt;oc&lt;/code&gt; (the openshift command line tool) in our path.&lt;/p&gt;

&lt;p&gt;Before we can interact with Openshift, we need to login (use admin/admin as credencials):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With everything running, it is time to deploy an application. For this example we will deploy &lt;a href="https://gitea.io" rel="noopener noreferrer"&gt;Gitea&lt;/a&gt;, a git repository manager forked from &lt;a href="https://gogs.io/" rel="noopener noreferrer"&gt;Gogs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But first, we need to create a project (which is effectively a kubernetes namespace):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc new-project scm &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="s2"&gt;"Source Control Management"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we tell openshift to create an "app" from Gitea docker image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc new-app gitea/gitea:1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which will end up creating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;a href="https://docs.openshift.org/latest/dev_guide/managing_images.html" rel="noopener noreferrer"&gt;Image Stream&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;a &lt;a href="https://docs.openshift.org/latest/dev_guide/deployments/kubernetes_deployments.html" rel="noopener noreferrer"&gt;DeploymentConfig&lt;/a&gt;, similar to (and will eventually be replaced by) a kubernetes Deployment&lt;/li&gt;
&lt;li&gt;a Service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To expose the service to the external world, we can create a &lt;a href="https://docs.openshift.org/latest/architecture/networking/routes.html" rel="noopener noreferrer"&gt;Route&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc expose svc/gitea &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; gitea-http &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--port&lt;/span&gt; 3000-tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;3000-tcp&lt;/code&gt; is the name of the port on the service.&lt;/p&gt;

&lt;p&gt;With the Route created, Gitea will now be available at &lt;a href="http://gitea-scm.192.168.99.100.nip.io" rel="noopener noreferrer"&gt;http://gitea-scm.192.168.99.100.nip.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also expose Gitea's ssh endpoint&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc expose svc/gitea &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; gitea-ssh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--port&lt;/span&gt; 22-tcp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--hostname&lt;/span&gt; gitea-scm-ssh.192.168.99.100.nip.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gitea is working, but its data will be lost on container restart, so we need a persistent volume to hold that data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# remove automatically create volume&lt;/span&gt;
oc volume dc/gitea &lt;span class="nt"&gt;--remove&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; gitea-volume-1

oc volume dc/gitea &lt;span class="nt"&gt;--add&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s1"&gt;'gitea-volume-data'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--type&lt;/span&gt; &lt;span class="s1"&gt;'pvc'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--mount-path&lt;/span&gt; &lt;span class="s1"&gt;'/data/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--claim-name&lt;/span&gt; &lt;span class="s1"&gt;'gitea-data'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--claim-size&lt;/span&gt; &lt;span class="s1"&gt;'1G'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing that is missing is configuration. Fortunately, Gitea can be configured &lt;a href="https://docs.gitea.io/en-us/install-with-docker/" rel="noopener noreferrer"&gt;using environment variables&lt;/a&gt;. So now we will create a ConfigMap to hold the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc create configmap gitea-config &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-literal&lt;/span&gt; &lt;span class="nv"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"My Gitea Instance"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-literal&lt;/span&gt; &lt;span class="nv"&gt;RUN_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-literal&lt;/span&gt; &lt;span class="nv"&gt;SSH_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gitea-scm-ssh.192.168.99.100.nip.io &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-literal&lt;/span&gt; &lt;span class="nv"&gt;ROOT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://gitea-scm.192.168.99.100.nip.io &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-literal&lt;/span&gt; &lt;span class="nv"&gt;DB_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sqlite3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--from-literal&lt;/span&gt; &lt;span class="nv"&gt;INSTALL_LOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add the environment to the DeploymentConfig:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc &lt;span class="nb"&gt;set env&lt;/span&gt; &lt;span class="nt"&gt;--from&lt;/span&gt; configmap/gitea-config dc/gitea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you access our Gitea instance right now, you will notice that our configuration was not applied! This happened because Gitea already have a configuration file in the volume we created. To solve this, we need to add another volume, an EmptyDir volume, so Gitea can rebuild the configuration using the environment variables we set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc volume dc/gitea &lt;span class="nt"&gt;--add&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s1"&gt;'gitea-volume-config'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--type&lt;/span&gt; &lt;span class="s1"&gt;'emptyDir'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--mount-path&lt;/span&gt; &lt;span class="s1"&gt;'/data/gitea/conf'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may take a bit longer for the application to be ready since it is creating the database tables, but then you will see that our settings were applied.&lt;/p&gt;

&lt;p&gt;Now we just need to setup &lt;a href="https://docs.openshift.org/latest/dev_guide/application_health.html" rel="noopener noreferrer"&gt;probes to monitor application health&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc &lt;span class="nb"&gt;set &lt;/span&gt;probe dc/gitea &lt;span class="nt"&gt;--liveness&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--failure-threshold&lt;/span&gt; 3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--initial-delay-seconds&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;ok

oc &lt;span class="nb"&gt;set &lt;/span&gt;probe dc/gitea &lt;span class="nt"&gt;--readiness&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--failure-threshold&lt;/span&gt; 3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--initial-delay-seconds&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--get-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://:3000/user/login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The readiness probe is particularly useful during rollouts so openshift can tell if it can safely remove the old container and redirect requests to the new one.&lt;/p&gt;

&lt;p&gt;Update:&lt;/p&gt;

&lt;p&gt;I made a &lt;a href="https://docs.openshift.org/3.9/dev_guide/templates.html" rel="noopener noreferrer"&gt;Template&lt;/a&gt; to make easier to deploy Gitea. &lt;a href="https://github.com/livioribeiro/devops-stuff/blob/master/openshift/gitea-persistent-template.yaml" rel="noopener noreferrer"&gt;It is available here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Gitea has more settings than the ones configurable through the environment variables, but right now setting the config file (app.ini) from a configmap does not work very well since Gitea tries to write to the file and fails because it is readonly. However, &lt;a href="https://github.com/go-gitea/gitea/pull/4412" rel="noopener noreferrer"&gt;there is a pull request to fix that&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>container</category>
      <category>orchestration</category>
      <category>openshift</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
