<?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>DEV Community: Mayeu</title>
    <description>The latest articles on DEV Community by Mayeu (@mayeu).</description>
    <link>https://dev.to/mayeu</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%2F91887%2F4ca8069e-28d7-4b97-af36-e958fbf1315d.jpg</url>
      <title>DEV Community: Mayeu</title>
      <link>https://dev.to/mayeu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mayeu"/>
    <language>en</language>
    <item>
      <title>What's a good way of sending a big directory structure to a server using Ansible?</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Wed, 19 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/what-s-a-good-way-of-sending-a-big-directory-structure-to-a-server-using-ansible-2hdm</link>
      <guid>https://dev.to/mayeu/what-s-a-good-way-of-sending-a-big-directory-structure-to-a-server-using-ansible-2hdm</guid>
      <description>&lt;p&gt;Ansible has a &lt;code&gt;copy&lt;/code&gt; module that works really well for moving a small set of files around. But it can rapidly slow down your playbook since &lt;a href="https://docs.ansible.com/ansible/latest/modules/copy_module.html#notes"&gt;it does not scale well with hundreds of files&lt;/a&gt;. So how can you move a big set of files and folder without losing too much time? Well, you use another module named&lt;code&gt;synchronize&lt;/code&gt; for this.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;synchronize&lt;/code&gt; uses a tool named &lt;code&gt;rsync&lt;/code&gt; under the hood, so you first have to ensure it is installed on your machine, and on the targeted host (it comes with most Unixes). If you are using macOS or Ubuntu, it should be already there. Otherwise start by installing it on your computer.&lt;/p&gt;

&lt;p&gt;To be sure it is installed on your targeted server you should add a task to install it before you use the &lt;code&gt;synchronize&lt;/code&gt; module in your playbook. Here it is an example for Ubuntu or Debian hosts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&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 rsync running synchronize later&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="s"&gt;rsync&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By default &lt;code&gt;synchronize&lt;/code&gt; just require a &lt;code&gt;src&lt;/code&gt; and a &lt;code&gt;dest&lt;/code&gt;, but like a lot of Ansible modules &lt;a href="https://docs.ansible.com/ansible/latest/modules/synchronize_module.html#synchronize-module"&gt;there are many more options for finer control&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a minimal example of using the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&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;Synchronize a local folder to my remote server&lt;/span&gt;
  &lt;span class="na"&gt;synchronize&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;path/to/my/local/folder&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;/absolute/path/to/my/destination/folder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And with that you can copy a gazillion files (at least) without worrying that your copy will be slow!&lt;/p&gt;

&lt;p&gt;Happy syncing 👋&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on my website: &lt;a href="https://mayeu.me/post/what-good-way-sending-big-directory-structure-server-using-ansible/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>beginners</category>
      <category>devpos</category>
    </item>
    <item>
      <title>How to Read the Content of an External File With Ansible</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Thu, 13 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/how-to-read-the-content-of-an-external-file-with-ansible-l7h</link>
      <guid>https://dev.to/mayeu/how-to-read-the-content-of-an-external-file-with-ansible-l7h</guid>
      <description>&lt;p&gt;Do you want to load up JSON data from a file directly into your Ansible playbook, tasks or roles? Did you start by trying to read it via a &lt;code&gt;shell&lt;/code&gt; or command &lt;code&gt;module&lt;/code&gt; and wondered if there was a better way? Well, there is, in the form or &lt;code&gt;lookup&lt;/code&gt; plugins. Read on to see how to use it.&lt;/p&gt;

&lt;p&gt;Ansible comes with a whole bunch of &lt;a href="https://docs.ansible.com/ansible/latest/plugins/lookup.html#plugin-list"&gt;Lookup plugins&lt;/a&gt; that allow loading stuff from the outside your play. The one we are interested in today is the&lt;code&gt;file&lt;/code&gt; &lt;a href="https://docs.ansible.com/ansible/latest/plugins/lookup/file.html"&gt;one&lt;/a&gt;. So, how do I load the content of my file with this?&lt;/p&gt;

&lt;p&gt;Simply enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;file_contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{lookup('file',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'path/to/file.json')}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And bam, you have the content in the &lt;code&gt;file_contents&lt;/code&gt; variable!&lt;/p&gt;

&lt;p&gt;As you see, we are calling the &lt;code&gt;lookup()&lt;/code&gt; function, to which we are passing the plugin we want to use has the first argument (in that case &lt;code&gt;file&lt;/code&gt;), and then the path to our file. And just with that we loaded our file content!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note that the file must be on your Ansible controller&lt;/strong&gt; (i.e.: the computer where you are running the &lt;code&gt;ansible&lt;/code&gt; command).&lt;/p&gt;

&lt;p&gt;You can directly use this in any module without the need for an intermediate variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&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;Read the content of a file instead of copying it directly&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;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{lookup('file',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'path/to/file.json')}}"&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/config/file.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And voilà 🙂&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on my website: &lt;a href="https://mayeu.me/post/how-to-read-the-content-of-an-external-file-with-ansible/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Trigger Any Action When a File or Folder Changes on Macos on the Cheap</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Tue, 14 May 2019 05:16:08 +0000</pubDate>
      <link>https://dev.to/mayeu/how-to-trigger-any-action-when-a-file-or-folder-changes-on-macos-on-the-cheap-3dcp</link>
      <guid>https://dev.to/mayeu/how-to-trigger-any-action-when-a-file-or-folder-changes-on-macos-on-the-cheap-3dcp</guid>
      <description>&lt;p&gt;Did you ever want to trigger an action when a file changed? For example, automatically move a file that has just been downloaded? The action could be a script, app, anything really. Here I will describe how to do exactly that on macOS with only the tools provided by the default system.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side note: I know there are GUI app that does that in a much simpler manner; here I just want to highlight how to do that manually with what macOS is providing to us.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But before we start, I would like to stress the fact that this method may not be ideal for anything critical to take place. Quoting the docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ IMPORTANT: Use of this key is highly discouraged, as filesystem event monitoring is highly race-prone, and it is entirely possible for modifications to be missed. When modifications are caught, there is no guarantee that the file will be in a consistent state when the job is launched.    &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This basically means that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if there is too much change happening at once, some may be missed and your action won’t trigger&lt;/li&gt;
&lt;li&gt;You can’t be sure that your action will find the file in the exact state that triggered the script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I believe that for day-to-day workflows, which are not computationally demanding, those caveats are not really a problem.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to watch for changes?
&lt;/h1&gt;

&lt;p&gt;For this we will be using &lt;code&gt;launchd&lt;/code&gt; which is  &lt;a href="https://en.wikipedia.org/wiki/Launchd"&gt;the service manager running on macOS&lt;/a&gt;.  It has two main tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Booting the system&lt;/li&gt;
&lt;li&gt;Managing daemon and agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;code&gt;launchd&lt;/code&gt; lingo, an agent is a service run on a per user basis, and a daemon is a system service.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;launchd&lt;/code&gt; has multiple levels of configurations, some are system-wide and privileged, but you can also use it to run unprivileged user tasks. There are five folders in which you can find those definitions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/Library/LaunchAgents&lt;/code&gt;: per user agents provided by the user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/Library/LaunchAgents&lt;/code&gt;: per user agents provided by the administrator.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/Library/LaunchDaemons&lt;/code&gt;: systemwide daemons provided by the administrator.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/System/Library/LaunchAgents&lt;/code&gt;: per user agents provided by Apple.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/System/Library/LaunchDaemons&lt;/code&gt;: systemwide daemons provided by Apple.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case, we will be using a small &lt;code&gt;launchd&lt;/code&gt; agent that is going to be stored in &lt;code&gt;~/Library/LaunchAgents&lt;/code&gt;. We will see later on how we can load or unload our agent to active or deactivate it.&lt;/p&gt;

&lt;h1&gt;
  
  
  The test Setup
&lt;/h1&gt;

&lt;p&gt;Let’s get some action going on, hop in your terminal and go to a clean folder for our test. From here I will assume that the folder you are using is &lt;code&gt;~/file-watching-test&lt;/code&gt;, if you are using another one don’t forget to adapt your paths in the rest of the article!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir ~/file-watching-test
$ cd ~/file-watching-test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this folder we are going to create an empty &lt;code&gt;watched&lt;/code&gt; file that will be monitored by &lt;code&gt;launchd&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch ~/file-watching-test/watched
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are also going to create the script that will be called when the watched file changes, open the &lt;code&gt;~/file-watching-test/script.sh&lt;/code&gt; file in your favourite editor and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh
echo "$(date): 🐈 I has be summoned" &amp;gt;&amp;gt; ~/file-watching-test/result
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let’s break out what is happening here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;echo "$(date): 🐈 I has be summoned"&lt;/code&gt;: 

&lt;ul&gt;
&lt;li&gt;this echo command will get the return of the date command executed via &lt;code&gt;$()&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;print &lt;code&gt;&amp;lt;current date&amp;gt;: 🐈 I has be summoned&lt;/code&gt; to the standard output. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can try it directly in your shell to see what is happening. Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;: this is a redirection operator. This one takes whatever has been printed on the standard output (so not the errors, if any) and &lt;strong&gt;append&lt;/strong&gt; it to the file it points toward. With this we can log all the execution of our script.&lt;/li&gt;
&lt;li&gt;And finally,  &lt;code&gt;~/file-watching-test/result&lt;/code&gt; is our result file that will receive the output of our &lt;code&gt;echo&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, make sure the script can be executed with &lt;code&gt;chmod +x ~/file-watching-test/script.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To sum up, we have the following hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree file-watching-test
file-watching-test
├── script.sh      # Our script
├── watched        # The file we are watching
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Creating a service file for &lt;code&gt;launchd&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;We want our test service to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch the &lt;code&gt;~/file-watching-test/watched&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;When a change happens, we want to execute &lt;code&gt;~/file-watching-test/script.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before I show you the service file, be warned that &lt;code&gt;launchd&lt;/code&gt; use XML for service declaration, so brace yourself.&lt;/p&gt;

&lt;p&gt;Here is our &lt;code&gt;me.mayeu.watchtest.plist&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=“1.0” encoding=“UTF-8”?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;“1.0”&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;me.mayeu.watchtest&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/m/file-watching-test/script.sh&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;WatchPaths&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/m/file-watching-test/watched&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;OK let’s break that down, and be sure to adapt the various paths to the one you are using! If you are unsure, use the &lt;code&gt;pwd&lt;/code&gt; command in your shell when you are in the &lt;code&gt;file-watching-test&lt;/code&gt; folder to print absolute path of the folder.&lt;/p&gt;

&lt;p&gt;The very first part is not really interesting since it is a declaration of the format and the document type definition (DTD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=“1.0” encoding=“UTF-8”?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, we state that the file is using the &lt;code&gt;plist&lt;/code&gt; format version 1.0. A &lt;code&gt;plist&lt;/code&gt; is the named used for &lt;code&gt;Property List&lt;/code&gt; files that are use to store configuration, services, serialised objects and more in macOS. We also declare that this &lt;code&gt;plist&lt;/code&gt; contains a dictionary (&lt;code&gt;dict&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;“1.0”&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Inside those key we are going to declare our job. First, we have to provide a unique label to identify the job. Here I use &lt;code&gt;me.mayeu.watchtest&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;me.mayeu.watchtest&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By convention (not only but we don’t need to dive to deep in this), one uses the reversed domain following by some name representing the app. This ID is also used to identify the application and other related resources.&lt;/p&gt;

&lt;p&gt;For example, Evernote uses &lt;code&gt;com.evernote.Evernote&lt;/code&gt; as an application ID.&lt;/p&gt;

&lt;p&gt;Then, we declare the program we are going to run using the &lt;code&gt;ProgramArguments&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/m/file-watching-test/script.sh&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This key take an array listing all the arguments. In our case it is just the path to the script, but if we wanted to execute &lt;code&gt;git commit -m “My commit message”&lt;/code&gt; we would do this like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/bin/git&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;commit&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-m&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;My commit message&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And finally, we declare when to run the program, in that case we use the &lt;code&gt;WatchPaths&lt;/code&gt; key that take an array of paths to watch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;WatchPaths&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/m/file-watching-test/watched&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Done! We have our new service.&lt;/p&gt;

&lt;p&gt;If you are curious of what &lt;code&gt;launchd&lt;/code&gt; can do, you can find all the valid key detailed in the &lt;code&gt;launchd.plist&lt;/code&gt; manpage: &lt;code&gt;man 5 launchd.plist&lt;/code&gt;. &lt;code&gt;launchd&lt;/code&gt; should also be used for time-based jobs instead of using &lt;code&gt;cron&lt;/code&gt;. See &lt;a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html#//apple_ref/doc/uid/10000172i-CH1-SW2"&gt;the Apple documentation&lt;/a&gt; for more about that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Trigger it!
&lt;/h1&gt;

&lt;p&gt;Almost there! We have all the pieces we need so let’s copy our service file to the right place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cp me.mayeu.watchtest ~/Library/LaunchAgents/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will now tell &lt;code&gt;launchd&lt;/code&gt; to load it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ launchctl load ~/Library/LaunchAgents/me.mayeu.watchtest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that &lt;code&gt;launchd&lt;/code&gt; has loaded our service, we can change our watched file by adding content in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo ‘trigger the watcher’ &amp;gt; test/watched`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And this should have triggered the script; thus we will have some content in the result file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat ~/file-watching-test/result
Thu Apr 11 12:39:41 +07 2019: 🐈 I has be summoned
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;🎉&lt;/p&gt;

&lt;p&gt;If we trigger it again, we should see a new line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo ‘trigger the watcher’ &amp;gt; test/watched`
$ cat ~/file-watching-test/result
Thu Apr 11 12:39:41 +07 2019: 🐈 I has be summoned
Thu Apr 11 12:42:55 +07 2019: 🐈 I has be summoned
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are done with this test! To clean behind ourselves we are going to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unload the service&lt;/li&gt;
&lt;li&gt;delete the service &lt;code&gt;plist&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;delete our test folder
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ launchctl unload ~/Library/LaunchAgents/me.mayeu.watchtest
$ rm -rf ~/Library/LaunchAgents/me.mayeu.watchtest
$ rm -rf ~/file-watching-test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is it! You now have some basis on using &lt;code&gt;launchd&lt;/code&gt;, and maybe you learned one or two things about your shell and shell scripts at the same times 🙂&lt;/p&gt;

&lt;p&gt;One last point, you can also watch folders using this method and thus act on any changes that happen under those folders, may it be deleted files, new files, new folders, etc.&lt;/p&gt;

&lt;p&gt;Have fun! And if you build something cool with this, do not hesitate to poke me about this on &lt;a href="https://twitter.com/Mayeu"&gt;Twitter&lt;/a&gt; or by email: &lt;strong&gt;m [-at-] mayeu [-dot-] me&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://mayeu.me/post/how-to-trigger-any-action-when-a-file-or-folder-changes-on-macos-on-the-cheap/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blog</category>
      <category>macos</category>
      <category>cli</category>
    </item>
    <item>
      <title>Fix Mistakes in Shell Commands Easily With `fc`</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Mon, 13 May 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/fix-mistakes-in-shell-commands-easily-with-fc-bcg</link>
      <guid>https://dev.to/mayeu/fix-mistakes-in-shell-commands-easily-with-fc-bcg</guid>
      <description>&lt;p&gt;From time to time, it can be quite cumbersome to fix a mistyped command in your shell. What if you could fix the command you just typed in your favourite editor? Well, that’s exactly what &lt;code&gt;fc&lt;/code&gt; command is all about, and it is standard to all Unix system (this includes GNU/Linux &amp;amp; macOS). Let see how that work.&lt;/p&gt;

&lt;p&gt;From Wikipedia:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;fc&lt;/strong&gt; is a standard program on Unix that lists, edits and reexecutes commands previously entered to an interactive shell.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using &lt;code&gt;fc&lt;/code&gt; looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You type a non-working/wrong command&lt;/li&gt;
&lt;li&gt;You type &lt;code&gt;fc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The last typed command (before &lt;code&gt;fc&lt;/code&gt;) is open in your editor&lt;/li&gt;
&lt;li&gt;You fix the command, save, and quit&lt;/li&gt;
&lt;li&gt;When you exit the editor, the edited command is executed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is an interactive example in which I use the wrong path and fix it via fc (click on the image to see it in action):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asciinema.org/a/245863" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasciinema.org%2Fa%2F245863.png" alt="asciicast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course this example is a little bit contrived because you should use autocompletion to navigate folders and not type your path entirely.&lt;/p&gt;

&lt;p&gt;Checking my history, I have found this big command (which check if Homebrew’s ZSH path is part of the &lt;code&gt;/etc/shells&lt;/code&gt; file, and if not, add it):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export zshpath="$(brew --prefix)/bin/zsh"; \
grep -q "^${zshpath}" /ec/shells || \
sudo -E sh -c "echo '$zshpath' \&amp;gt;\&amp;gt; /etc/shells"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not obvious, but there is a mistake right in the middle: &lt;code&gt;/ec/shells&lt;/code&gt;instead of &lt;code&gt;/etc/shells&lt;/code&gt;. With &lt;code&gt;fc&lt;/code&gt; fixing this is really easy to fix since I will have access to my whole editor and not only my shell movement and shortcut.&lt;/p&gt;

&lt;p&gt;But that’s not all &lt;code&gt;fc&lt;/code&gt; can also take an argument to fix an arbitrary command from your shell history, not only the last one.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;fc -l&lt;/code&gt; to list your history&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ fc -l
6213 rm -rf test
6214 brew install asciinema
6215 vim ~/bin/mac-bootstrap
6216 exit
6221 ls
6222 mkdir right-path
6228 asciinema rec
6229 cd wrong-path
6230 fc
6231 cd right-path
6232 pwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can use &lt;code&gt;fc &amp;lt;history number&amp;gt;&lt;/code&gt; to fix a specific command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ fc 6214 # Will open “brew install asciinema” in your editor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is not much more to this tool, but you can find the full documentation &lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/fc.html" rel="noopener noreferrer"&gt;of fc here&lt;/a&gt; if want to check out the other arguments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on my website: &lt;a href="https://mayeu.me/post/fix-typo-in-shell-commands-easily-with-fc/" rel="noopener noreferrer"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>beginners</category>
      <category>shell</category>
    </item>
    <item>
      <title>How to Import a Managed Digital Ocean Kubernetes cluster in Rancher</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Sat, 27 Apr 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/how-to-import-a-managed-digital-ocean-kubernetes-cluster-in-rancher-3f8d</link>
      <guid>https://dev.to/mayeu/how-to-import-a-managed-digital-ocean-kubernetes-cluster-in-rancher-3f8d</guid>
      <description>&lt;p&gt;&lt;em&gt;This post contains affiliate links to &lt;a href="https://m.do.co/c/f1bcc66950f3" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt;. It will get you $100 to test out the platform, and if at some point you start using it for real I will earn $25.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://m.do.co/c/f1bcc66950f3" rel="noopener noreferrer"&gt;Digital ocean&lt;/a&gt; (DO) recently announced &lt;a href="https://blog.digitalocean.com/digitalocean-releases-k8s-as-a-service/?refcode=f1bcc66950f3" rel="noopener noreferrer"&gt;their managed offering for kubernetes&lt;/a&gt;. Rancher is a tool that helps you manage multiple Kubernetes clusters in one interface but it does not yet support the new DO offering natively, and I was wondering if one could import a DO managed cluster in your Rancher instance.&lt;/p&gt;

&lt;p&gt;Turn out you can, here is how.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: just follow the importer
&lt;/h2&gt;

&lt;p&gt;If you already know how to use the importer for existing clusters then you are good to go. If not, just go there, chose the “Existing Cluster Import” option and follow the steps to import your DO managed cluster 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Illustrated Guide
&lt;/h2&gt;

&lt;p&gt;Here is a step by step guide on doing the import. It assumes you already have a Rancher instance running somewhere. (Here is &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-multi-node-deployments-with-rancher-2-1-kubernetes-and-docker-machine-on-ubuntu-18-04/?refcode=f1bcc66950f3" rel="noopener noreferrer"&gt;a nice and straightforward guide on running Rancher on DO&lt;/a&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your very first step is to go &lt;a href="https://m.do.co/c/f1bcc66950f3" rel="noopener noreferrer"&gt;create a managed cluster on DO&lt;/a&gt;. For that connect to your account, and in the sidebar select the “Kubernetes” menu. When there, follow the guide to create your cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Fdo-01-kubernetes-setup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Fdo-01-kubernetes-setup.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now that your cluster is provisioning you can download its configuration. We are going to use it to connect to it via &lt;code&gt;kubectl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Fdo-02-download-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Fdo-02-download-config.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your rancher interface, and click on the “Add Cluster” button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-01-add-cluster.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-01-add-cluster.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the “Import existing cluster”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-02-select-importer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-02-select-importer.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The importer will give you a &lt;code&gt;kubectl&lt;/code&gt; command to run against your &lt;a href="https://m.do.co/c/f1bcc66950f3" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; cluster. Use the previously downloaded config file to run it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl --kubeconfig=config-from-do.yml apply -f \ https://your.rancher.domain/v3/import/lotofgibberishrlasxtowg.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-03-command-to-run.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-03-command-to-run.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the configuration has been applied, your cluster should be imported and available in your rancher interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-04-cluster-imported.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmayeu.me%2Fpost%2Fhow-to-import-digital-ocean-managed-k8s-in-rancher%2Francher-04-cluster-imported.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And voilà, you have imported a &lt;a href="https://m.do.co/c/f1bcc66950f3" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; managed cluster in your Rancher 🎉&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://mayeu.me/post/how-to-import-digital-ocean-managed-k8s-in-rancher/" rel="noopener noreferrer"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>digitalocean</category>
      <category>devops</category>
    </item>
    <item>
      <title>Économiser du temps et de la bande passante avec un cache Docker local</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Tue, 04 Dec 2018 06:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/conomiser-du-temps-et-de-la-bande-passante-avec-un-cache-docker-local-1mji</link>
      <guid>https://dev.to/mayeu/conomiser-du-temps-et-de-la-bande-passante-avec-un-cache-docker-local-1mji</guid>
      <description>&lt;p&gt;Régulièrement, j'utilise Docker 🐳 dans des VMs (vagrant) ou sur d'autres&lt;br&gt;
machine de mon réseau local, et je me retrouve à télécharger plusieurs fois la&lt;br&gt;
même image sur ces différentes machines. En plus du gâchis de bande passante,&lt;br&gt;
ça devient rapidement une grosse perte de temps sur de petites connexions !&lt;br&gt;
Pour régler ce problème j'utilise maintenant un registre (« registry ») Docker&lt;br&gt;
qui tourne localalement et qui cache de manières transparentes toutes les&lt;br&gt;
images récupérées par Docker. Voici comment mettre cela en place.&lt;/p&gt;

&lt;p&gt;D'abord, un peu de préparation. Nous allons créer un dossier qui va être&lt;br&gt;
utilisé par le registre pour stocker toutes ces données. Ce dossier peut-être&lt;br&gt;
n'importe où sur votre machine, personnellement je l'ai mis dans &lt;code&gt;/var/lib&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo mkdir /var/lib/docker-registry
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nous allons ajouter la configuration par défaut du registre dans ce dossier, et&lt;br&gt;
pour cela on va directement l'extraire depuis l'image Docker :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker run -it --rm registry:2       \
       cat /etc/docker/registry/config.yml  \
       &amp;gt; /var/lib/docker-registry/config.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;La configuration pourra être différente selon le moment ou vous la récupérez, à&lt;br&gt;
la création de cet article elle ressemblait à ça :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry&lt;/span&gt;
&lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;blobdescriptor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inmemory&lt;/span&gt;
  &lt;span class="na"&gt;filesystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rootdirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/registry&lt;/span&gt;
&lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;addr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;:5000&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;X-Content-Type-Options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;nosniff&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;health&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;storagedriver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pour activer la fonction de proxy du serveur, il faut ajouter la configuration&lt;br&gt;
suivante au fichier yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;remoteurl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://registry-1.docker.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;La clé &lt;code&gt;remoteurl&lt;/code&gt; peut pointer vers n'importe quel registre, ici j'ai mis&lt;br&gt;
celui de docker par défaut.&lt;/p&gt;

&lt;p&gt;La configuration finale ressemble à ça:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry&lt;/span&gt;
&lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;blobdescriptor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inmemory&lt;/span&gt;
  &lt;span class="na"&gt;filesystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rootdirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/registry&lt;/span&gt;
&lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;addr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;:5000&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;X-Content-Type-Options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;nosniff&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;health&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;storagedriver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;remoteurl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://registry-1.docker.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Maintenant que la configuration est finie, on peut démarrer notre registre. À&lt;br&gt;
noter que j'ai utilisé l'option &lt;code&gt;--restart=always&lt;/code&gt; dans la commande suivante&lt;br&gt;
pour m'assurer que le registre démarre automatiquement avec le démon Docker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker run --restart=always -p 5000:5000                         \
         --name v2-mirror -v /var/lib/docker-registry:/var/lib/registry \
         --detach registry:2 serve /var/lib/registry/config.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On utilise &lt;code&gt;-v&lt;/code&gt; pour monter le dossier créé précédemment dans l'image, et on&lt;br&gt;
démarre le registre avec l'option &lt;code&gt;serve&lt;/code&gt; suivit du chemin vers le fichier de&lt;br&gt;
configuration.&lt;/p&gt;

&lt;p&gt;Assurons-nous d'abord que le container est bien lancé avec &lt;code&gt;docker ps&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker ps
CONTAINER ID   IMAGE        CREATED          STATUS          PORTS                    NAMES
67425da4ea4c   registry:2   32 seconds ago   Up 29 seconds   0.0.0.0:5000-&amp;gt;5000/tcp   v2-mirror
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On peut ensuite lister le contenu du registre (vide) via &lt;code&gt;curl&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://localhost:5000/v2/_catalog
{"repositories":[]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Maintenant que notre registre est actif, on va configurer docker pour qu'il&lt;br&gt;
l'utilise. Pour cela il faut éditer le fichier &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; pour&lt;br&gt;
ajouter la configuration suivante (qui doit être du JSON valide) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;"registry-mirrors"&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="s2"&gt;"http://localhost:5000"&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;Il est possible que ce fichier (voir dossier) n'existe pas sur votre système.&lt;br&gt;
Si c'est le cas, vous pouvez le créer manuellement (avec l'utilisateur &lt;code&gt;root&lt;/code&gt;).&lt;br&gt;
Une fois la configuration changée il faut redémarrer docker. Ici j'assume que&lt;br&gt;
vous avez un système qui utilise Systemd:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nous sommes maintenant prêts à réaliser notre premier download pour vérifier que&lt;br&gt;
le proxy fonctionne correctement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker pull redis
Using default tag: latest
latest: Pulling from library/redis
f17d81b4b692: Pull complete
b32474098757: Pull complete
8980cabe8bc2: Pull complete
e614c66c2b9c: Pull complete
6eb43ec9256b: Pull complete
394ecf5f46d4: Pull complete
Digest: sha256:f30f134bd475d451ce3207fb128bcef8ff87d0f520a39cac0c4ea285819c42a9
Status: Downloaded newer image for redis:latest

~ took 40s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Vérifions que l'image est maintenant dans notre registre local:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://localhost:5000/v2/_catalog
{"repositories":["library/redis"]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Et voilà ! Nous venons de cacher notre première image 🎉. Vérifions maintenant&lt;br&gt;
que le cache fonctionne comme il faut. En premier lieu, on va effacer l'image&lt;br&gt;
de notre démon docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker rmi redis
Untagged: redis:latest
Untagged: redis@sha256:f30f134bd475d451ce3207fb128bcef8ff87d0f520a39cac0c4ea285819c42a9
Deleted: sha256:415381a6cb813ef0972eff8edac32069637b4546349d9ffdb8e4f641f55edcdd
Deleted: sha256:2a5a57892da005399e6ce7166c5521cdca43a07872f23995e210bde5dae2640e
Deleted: sha256:85e1fabde4fd4d6df993de44ef3e04d15cd69f9d309c0112c6a5054a6dc8351a
Deleted: sha256:2725175b62c7479ee209454110e8293080b9711e4f0a29219e358d1afba88787
Deleted: sha256:7ae66985fd3a3a132fab51b4a43ed32fd14174179ad8c3041262670523a6104c
Deleted: sha256:bf45690ef12cc54743675646a8e0bafe0394706b7f9ed1c9b11423bb5494665b
Deleted: sha256:237472299760d6726d376385edd9e79c310fe91d794bc9870d038417d448c2d5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Et on la récupère de nouveau:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker pull redis
Using default tag: latest
latest: Pulling from library/redis
f17d81b4b692: Pull complete
b32474098757: Pull complete
8980cabe8bc2: Pull complete
e614c66c2b9c: Pull complete
6eb43ec9256b: Pull complete
394ecf5f46d4: Pull complete
Digest: sha256:f30f134bd475d451ce3207fb128bcef8ff87d0f520a39cac0c4ea285819c42a9
Status: Downloaded newer image for redis:latest

~ took 13s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ce qui nous a pris 3x moins de temps ! 👍 Le téléchargement était quasiment&lt;br&gt;
instantané, et seule la décompression a pris du temps.&lt;/p&gt;

&lt;p&gt;Avec ça nous avons maintenant un cache local transparent pour toutes les images&lt;br&gt;
Docker que l'on télécharge. On peut maintenant pointer les différentes VM ou&lt;br&gt;
machine du réseau vers ce cache, et profiter du temps gagné pour faire des&lt;br&gt;
choses utiles plutôt que de télécharger des octets depuis internet :)&lt;/p&gt;

&lt;p&gt;Un effet secondaire intéressant de ce cache, et que si un &lt;code&gt;docker pull&lt;/code&gt; échoue&lt;br&gt;
au milieu du téléchargement, les images intermédiaires déjà téléchargées seront&lt;br&gt;
conservées dans le cache, et donc il ne sera pas nécessaire de les télécharger&lt;br&gt;
de nouveau. Vous pouvez vérifier ça en stoppant un &lt;code&gt;pull&lt;/code&gt; et en le relançant&lt;br&gt;
avec et sans le proxy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cet article a été initialement publié sur &lt;a href="https://mayeu.me/blog/economiser-temps-bande-passante-cache-docker-local/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blog</category>
      <category>docker</category>
      <category>french</category>
    </item>
    <item>
      <title>Saving Time and Bandwidth by Caching Docker Images With a Local Registry</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Tue, 04 Dec 2018 06:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/saving-time-and-bandwidth-by-caching-docker-images-with-a-local-registry-98b</link>
      <guid>https://dev.to/mayeu/saving-time-and-bandwidth-by-caching-docker-images-with-a-local-registry-98b</guid>
      <description>&lt;p&gt;On a regular basis I use Docker 🐳 in VM (vagrant), or on various machines of my local network. This lead me to download images on one machine while those were already downloaded on another one. Beside the waste of bandwidth, on a lowspeed or crowded connection this is also a huge waste of time! To solve this issue, I am now running a local registry on my laptop that automatically cache any images I request via docker.&lt;/p&gt;

&lt;p&gt;To achieve this, we are going to run the official Docker registry in proxy mode, and then we will instruct our Docker daemon to use this local registry as its default one.&lt;/p&gt;

&lt;p&gt;First we need to create a folder that will be used by the registry to store all the images and data it needs. It can be anywhere on your machine, I personally put it in the somewhat standard &lt;code&gt;/var/lib&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo mkdir /var/lib/docker-registry

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

&lt;/div&gt;



&lt;p&gt;To ensure we have an up-to-date configuration file of the current registry version, we can directly extract it from the docker image (and at the sametime, pull the image):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker run -it --rm registry:2 cat \
       /etc/docker/registry/config.yml &amp;gt; /var/lib/docker-registry/config.yml

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

&lt;/div&gt;



&lt;p&gt;Depending on your version of the docker registry image, the configuration maybe slightly different from mine. At the time of creation of this article it looked 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;version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

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

&lt;/div&gt;



&lt;p&gt;To activate the proxy behaviour of the registry we have to had the following key in the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;proxy:
  remoteurl: https://registry-1.docker.io

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

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;remoteurl&lt;/code&gt; is the URL of the remote registry to use by default. Here it is Docker’s official one.&lt;/p&gt;

&lt;p&gt;The final configuration looks 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;
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
proxy:
  remoteurl: https://registry-1.docker.io

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

&lt;/div&gt;



&lt;p&gt;With the configuration done, we can start our local registry. Note that I have added a &lt;code&gt;--restart=always&lt;/code&gt; option flag in the command, so that each time the Docker daemon will start the registry will also start automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker run --restart=always -p 5000:5000 \
         --name v2-mirror -v /var/lib/docker-registry:/var/lib/registry \
         --detach registry:2 serve /var/lib/registry/config.yml

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

&lt;/div&gt;



&lt;p&gt;In the command we use &lt;code&gt;-v&lt;/code&gt; to mount our previously created registry folder in the image, and we start the registry via &lt;code&gt;serve&lt;/code&gt; with the configuration file as the only parameter.&lt;/p&gt;

&lt;p&gt;We can check that it is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker ps
CONTAINER ID IMAGE CREATED STATUS PORTS NAMES
67425da4ea4c registry:2 32 seconds ago Up 29 seconds 0.0.0.0:5000-&amp;gt;5000/tcp v2-mirror

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

&lt;/div&gt;



&lt;p&gt;We can query the content of our empty registry with &lt;code&gt;curl&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:5000/v2/_catalog
{"repositories":[]}

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

&lt;/div&gt;



&lt;p&gt;Now that we have our local registry running we have to configure the Docker daemon so it will use it instead of the default one. This requires a change inthe &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; file (as &lt;code&gt;root&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;{
    "registry-mirrors": ["http://localhost:5000"]
}

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

&lt;/div&gt;



&lt;p&gt;This file and folder may not exist on your system yet. If so you can safely create it. After the change we need to restart the daemon. Assuming your system uses Systemd it should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl restart docker

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

&lt;/div&gt;



&lt;p&gt;We can now try to download an image to see if it correctly use our caching proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker pull redis
Using default tag: latest
latest: Pulling from library/redis
f17d81b4b692: Pull complete
b32474098757: Pull complete
8980cabe8bc2: Pull complete
e614c66c2b9c: Pull complete
6eb43ec9256b: Pull complete
394ecf5f46d4: Pull complete
Digest: sha256:f30f134bd475d451ce3207fb128bcef8ff87d0f520a39cac0c4ea285819c42a9
Status: Downloaded newer image for redis:latest

~ took 40s

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

&lt;/div&gt;



&lt;p&gt;Let’s check the content of the registry:&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:5000/v2/_catalog
{"repositories":["library/redis"]}

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

&lt;/div&gt;



&lt;p&gt;It seems that we just cached our first image 🎉. Let’s try the caching then!First we delete the redis image from our docker daemon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker rmi redis
Untagged: redis:latest
Untagged: redis@sha256:f30f134bd475d451ce3207fb128bcef8ff87d0f520a39cac0c4ea285819c42a9
Deleted: sha256:415381a6cb813ef0972eff8edac32069637b4546349d9ffdb8e4f641f55edcdd
Deleted: sha256:2a5a57892da005399e6ce7166c5521cdca43a07872f23995e210bde5dae2640e
Deleted: sha256:85e1fabde4fd4d6df993de44ef3e04d15cd69f9d309c0112c6a5054a6dc8351a
Deleted: sha256:2725175b62c7479ee209454110e8293080b9711e4f0a29219e358d1afba88787
Deleted: sha256:7ae66985fd3a3a132fab51b4a43ed32fd14174179ad8c3041262670523a6104c
Deleted: sha256:bf45690ef12cc54743675646a8e0bafe0394706b7f9ed1c9b11423bb5494665b
Deleted: sha256:237472299760d6726d376385edd9e79c310fe91d794bc9870d038417d448c2d5

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

&lt;/div&gt;



&lt;p&gt;And we pull it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker pull redis
Using default tag: latest
latest: Pulling from library/redis
f17d81b4b692: Pull complete
b32474098757: Pull complete
8980cabe8bc2: Pull complete
e614c66c2b9c: Pull complete
6eb43ec9256b: Pull complete
394ecf5f46d4: Pull complete
Digest: sha256:f30f134bd475d451ce3207fb128bcef8ff87d0f520a39cac0c4ea285819c42a9
Status: Downloaded newer image for redis:latest

~ took 13s

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

&lt;/div&gt;



&lt;p&gt;Nice, 3x time quicker! 👍 With the download part was almost instant, and the image decompression took most of the time!&lt;/p&gt;

&lt;p&gt;And done! We now have a local cache for all our Docker images that will start each time the docker daemon is started. We can point all our VM or machines in the network to it (by making listen to the outside). And we can now enjoy more time to focus on what matter to us and less on downloading bits from the internet :)&lt;/p&gt;

&lt;p&gt;A nice effect of this caching is that intermediate images are also cached. Which is really useful with unstable Internet access. Because now when the connection is going to timeout in the middle of a pull, all the bits already downloaded will be cached so you can continue your pull where you left it! You can try that by starting to pull and image, stop it, then start it again. With the default daemon without cache this leads to a downloading all of the intermediate image again, but not with the proxy 👏&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://mayeu.me/blog/saving-time-bandwidth-caching-docker-images-with-local-registry/" rel="noopener noreferrer"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blog</category>
      <category>docker</category>
      <category>qualitifyoflife</category>
    </item>
    <item>
      <title>Running Systemd commands on remote host from your local host</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Mon, 22 Oct 2018 05:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/running-systemd-commands-on-remote-host-from-your-local-host-2jea</link>
      <guid>https://dev.to/mayeu/running-systemd-commands-on-remote-host-from-your-local-host-2jea</guid>
      <description>&lt;p&gt;While I was reading the &lt;a href="https://wiki.archlinux.org/index.php/systemd"&gt;pretty awesome Archlinux wiki&lt;/a&gt; for something completely different, I found out that one can launch any &lt;code&gt;systemctl&lt;/code&gt;command to a remote host via the &lt;code&gt;--host&lt;/code&gt; (or &lt;code&gt;-H&lt;/code&gt;) flag.&lt;/p&gt;

&lt;p&gt;So let say you want to check the &lt;code&gt;cron&lt;/code&gt; process on a server named kitten:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl -H root@kitten status cron
● cron.service - Regular background program processing daemon
   Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2018-07-28 11:36:37 CST; 2 months 25 days ago
     Docs: man:cron(8)
 Main PID: 1045
    Tasks: 1 (limit: 4643)
   CGroup: /system.slice/cron.service
           └─1045 /usr/sbin/cron -f

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



&lt;p&gt;Nice right? Under the hood this use SSH so you must have access to the host. And you can, of course, use any &lt;code&gt;systemctl&lt;/code&gt; command, not just &lt;code&gt;status&lt;/code&gt; ;)&lt;/p&gt;

&lt;p&gt;Have fun 👋&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://mayeu.me/blog/running-systemd-command-on-remote-host-from-local-host/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>blog</category>
    </item>
    <item>
      <title>Lancer une commande systemd sur un serveur distant depuis la machine locale</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Mon, 22 Oct 2018 05:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/lancer-une-commande-systemd-sur-un-serveur-distant-depuis-la-machine-locale-5087</link>
      <guid>https://dev.to/mayeu/lancer-une-commande-systemd-sur-un-serveur-distant-depuis-la-machine-locale-5087</guid>
      <description>&lt;p&gt;Alors que j’étais en train de lire &lt;a href="https://wiki.archlinux.org/index.php/systemd"&gt;l’excellent wiki d’Archlinux&lt;/a&gt; pour quelque chose de complètement différent, j’ai découvert qu’il était possible d’utiliser l’option &lt;code&gt;--host&lt;/code&gt; (ou &lt;code&gt;-H&lt;/code&gt;) pour lancer une commande de&lt;code&gt;systemctl&lt;/code&gt; directement sur un serveur distant.&lt;/p&gt;

&lt;p&gt;Par exemple, disons que vous vouliez vérifier la santé du processus &lt;code&gt;cron&lt;/code&gt; sur un serveur nommé &lt;code&gt;chaton&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl -H root@chaton status cron
● cron.service - Regular background program processing daemon
   Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2018-07-28 11:36:37 CST; 2 months 25 days ago
     Docs: man:cron(8)
 Main PID: 1045
    Tasks: 1 (limit: 4643)
   CGroup: /system.slice/cron.service
           └─1045 /usr/sbin/cron -f

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



&lt;p&gt;Pratique non ? Les commandes sont exécutées par SSH, il est donc nécessaire d’avoir l’accès au serveur préalablement. Et ça n’est pas limité à &lt;code&gt;status&lt;/code&gt; n’importe quelle commande de &lt;code&gt;systemctl&lt;/code&gt; va pouvoir être lancée comme cela ;)&lt;/p&gt;

&lt;p&gt;Have fun 👋&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cet article a été initialement publié sur &lt;a href="https://mayeu.me/blog/lancer-une-commande-systemd-sur-un-serveur-distant/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blog</category>
      <category>linux</category>
      <category>french</category>
    </item>
    <item>
      <title>How to set the language of a post?</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Sun, 30 Sep 2018 02:44:40 +0000</pubDate>
      <link>https://dev.to/mayeu/how-to-set-the-language-of-a-post-ihd</link>
      <guid>https://dev.to/mayeu/how-to-set-the-language-of-a-post-ihd</guid>
      <description>&lt;p&gt;Hello 👋,&lt;/p&gt;

&lt;p&gt;I see in the preference that there is a &lt;code&gt;Languages&lt;/code&gt; option, and my blog uses French and English so almost all my post are in this two languages (two separated posts, not both language on the same posts).&lt;/p&gt;

&lt;p&gt;I am wondering how does that work with post I make federate on dev.to, should I add a language tag or a specific declaration in the front matter? The documentation does not says any thing about the language (yet?).&lt;/p&gt;

</description>
      <category>meta</category>
    </item>
    <item>
      <title>Nettoyer les données non utilisées par Docker</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Mon, 24 Sep 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/nettoyer-les-donnes-non-utilises-par-docker-3l7j</link>
      <guid>https://dev.to/mayeu/nettoyer-les-donnes-non-utilises-par-docker-3l7j</guid>
      <description>&lt;p&gt;&lt;em&gt;Attention: lisez l’article en entier avant de taper une commande que vous pourriez regretter&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Si vous utilisez Docker (que ça soit en production ou sur votre machine dedéveloppement) vous avez surement accumulé beaucoup de données maintenant inutiles. Il existe des outils comme &lt;a href="https://github.com/spotify/docker-gc"&gt;docker-gc&lt;/a&gt; pour nettoyer tout ça, mais depuis &lt;a href="https://docs.docker.com/engine/api/v1.25/"&gt;l’API 1.25&lt;/a&gt; docker intègre la commande &lt;code&gt;prune&lt;/code&gt; qui pourrait suffire amplement.&lt;/p&gt;

&lt;p&gt;L’usage de base est plutôt simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune
WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all dangling images
        - all build cache
Are you sure you want to continue? [y/N] y

Deleted Containers:
deleted: sha256:ea43728b2d10e7b0fe24036f9531caac96bd02f779b95a6620110f00ccd3b002
deleted: sha256:022db612b3070971ce7d51778806a1f995a9c3aa1a741a6c0be0bca603787387
...approximativement 2 gajillion de hashes...

Total reclaimed space: 5.64GB

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

&lt;/div&gt;



&lt;p&gt;Ça va enlever pas mal de choses, mais malheureusement pas tout ce qui est inutilisé (les images sans containers par exemple ne sont par retirées avec cette commande). Pour cela il faut ajouter l’option &lt;code&gt;--all&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;$ sudo docker system prune --all
WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all images without at least one container associated to them
        - all build cache
Are you sure you want to continue? [y/N] y
... moar hashes...

Total reclaimed space: 26.78GB

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

&lt;/div&gt;



&lt;p&gt;C’est pas mal tout ça, mais pas encore idéal. Garder un peu de cache c’est quand même utile pour accélérer les choses. Pour cela il y a l’option&lt;code&gt;--filter&lt;/code&gt; (disponible uniquement à partir de &lt;a href="https://docs.docker.com/engine/api/v1.28/"&gt;l’API 1.28&lt;/a&gt;). Au moment ou j’écris ces lignes il n’y a que deux filtres disponibles: &lt;code&gt;until&lt;/code&gt; et&lt;code&gt;label&lt;/code&gt;. Dans mon cas j’utilise surtout &lt;code&gt;until&lt;/code&gt; qui va tout nettoyer jusqu’à une certaine date (utilisant un timestamp).&lt;/p&gt;

&lt;p&gt;Évidemment, je ne m’amuse pas à taper un timestamp, mais j’utilise &lt;code&gt;date&lt;/code&gt; pour le générer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune --all --filter until=$(date -d "1 month ago" +%s)

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

&lt;/div&gt;



&lt;p&gt;Ici j’utilise une substitution avec &lt;code&gt;$( commande )&lt;/code&gt; qui permet d’exécuter une commande pour injecter son résultat sous forme de chaînes de caractères dans une autre commande. La commande &lt;code&gt;date&lt;/code&gt; se décompose en &lt;code&gt;date -d &amp;lt;description de&lt;br&gt;
la date&amp;gt; &amp;lt;format de sortie&amp;gt;&lt;/code&gt;. Ici le format de sortie &lt;code&gt;+%s&lt;/code&gt; signifie “timestamp”. Jetez un oeil à &lt;code&gt;man date&lt;/code&gt; si vous voulez en savoir plus.&lt;/p&gt;

&lt;p&gt;Et voilà 😃 J’ai rarement besoin de données plus vieilles qu’un mois (et même 1 mois est très conservateur, mais comme j’ai rarement accès à un bon débit je garde le plus possible).&lt;/p&gt;

&lt;p&gt;Finalement, si vous avez l’âme d’un aventurier vous pouvez ajouter l’option&lt;code&gt;--force&lt;/code&gt; qui va lancer la commande sans confirmation nécessaire. Je l’utilise sur mon serveur d’intégration continue dans une tâche cron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune --force --all --filter until=$(date -d "1 week ago" +%s)

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

&lt;/div&gt;



&lt;p&gt;Joyeux ménage ♻️&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Une note à propos de &lt;code&gt;sudo&lt;/code&gt;: sur ma machine de développement (qui est un système GNU/Linux), j’utilise Docker exclusivement via &lt;code&gt;sudo&lt;/code&gt;, car être dans le group &lt;code&gt;docker&lt;/code&gt; revient à donner les droits root à son utilisateur. Cela est dû au fait que docker est un daemon privilégié sur le système. Plus d’info dans la documentation officielle : &lt;a href="https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface"&gt;Docker security&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cet article a été initialement publié sur  &lt;a href="https://mayeu.me/blog/nettoyer-les-donnees-de-docker/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>blog</category>
      <category>french</category>
    </item>
    <item>
      <title>Cleaning Unused Docker Data Without External Tools</title>
      <dc:creator>Mayeu</dc:creator>
      <pubDate>Mon, 24 Sep 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/mayeu/cleaning-unused-docker-data-without-external-tools-e24</link>
      <guid>https://dev.to/mayeu/cleaning-unused-docker-data-without-external-tools-e24</guid>
      <description>&lt;p&gt;&lt;em&gt;Warning: read the full article before typing commands you may regret.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are a docker user (may it be in production or on your development machine), you may have accumulated quite a bit of now useless data. There are some existing tools to clean your daemon of unused images and containers (like &lt;a href="https://github.com/spotify/docker-gc"&gt;docker-gc&lt;/a&gt;), but since the &lt;a href="https://docs.docker.com/engine/api/v1.25/"&gt;docker API 1.25&lt;/a&gt; docker has an easy to use &lt;code&gt;prune&lt;/code&gt; command that could me good enough for you.&lt;/p&gt;

&lt;p&gt;Basic usage is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune
WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all dangling images
        - all build cache
Are you sure you want to continue? [y/N] y

Deleted Containers:
deleted: sha256:ea43728b2d10e7b0fe24036f9531caac96bd02f779b95a6620110f00ccd3b002
deleted: sha256:022db612b3070971ce7d51778806a1f995a9c3aa1a741a6c0be0bca603787387
... approximately 2 gajillion hashes later...

Total reclaimed space: 5.64GB

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

&lt;/div&gt;



&lt;p&gt;That’s nice, but it does not clean everything unused. For that you should add the &lt;code&gt;--all&lt;/code&gt; option to remove every image and not only the dangling ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune --all
WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all images without at least one container associated to them
        - all build cache
Are you sure you want to continue? [y/N] y
... moar hashes...

Total reclaimed space: 26.78GB

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

&lt;/div&gt;



&lt;p&gt;Of course that may not be ideal. Having caches is pretty useful after all. And there is just the option for that: &lt;code&gt;--filter&lt;/code&gt; (only available starting at &lt;a href="https://docs.docker.com/engine/api/v1.28/"&gt;API 1.28&lt;/a&gt;). At the time of my writing there are only two filters: &lt;code&gt;until&lt;/code&gt;and &lt;code&gt;label&lt;/code&gt;. In my case I only use the &lt;code&gt;until&lt;/code&gt; one which allows you to clean everything older than a specified timestamp.&lt;/p&gt;

&lt;p&gt;Of course I don’t put a timestamp in the command, I use &lt;code&gt;date&lt;/code&gt; to generate a timestamp based on a human readable date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune --all --filter until=$(date -d "1 month ago" +%s)

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

&lt;/div&gt;



&lt;p&gt;This command uses a substitution via &lt;code&gt;$( command )&lt;/code&gt;, this allows you to get the result of a command and inject it in another one as a string. The date command itself is decomposed as: &lt;code&gt;date -d &amp;lt;time description&amp;gt; &amp;lt;time format&amp;gt;&lt;/code&gt;, where&lt;code&gt;+%s&lt;/code&gt; is the format to get a timestamp. You can check &lt;code&gt;man date&lt;/code&gt; for more info.&lt;/p&gt;

&lt;p&gt;And voilà 😃 I rarely need the cache to be older than a month on my laptop (and one month is already pretty conservative, but that’s because I rarely have a good internet so I cache as much as possible).&lt;/p&gt;

&lt;p&gt;Finally, if you feel adventurous enough you can add the &lt;code&gt;--force&lt;/code&gt; option so that the command will not ask for confirmation. I use the following in acronjob on my continuous integration server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo docker system prune --force --all --filter until=$(date -d "1 week ago" +%s)

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

&lt;/div&gt;



&lt;p&gt;Happy cleaning ♻️&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A note about &lt;code&gt;sudo&lt;/code&gt;: on my dev machine (which is a GNU/Linux system) I only use docker via &lt;code&gt;sudo&lt;/code&gt; because not doing so mean that your user has the same power as root all the time. This is due to the fact that the docker daemon is a privileged process running as root. See the official &lt;a href="https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface"&gt;Docker security&lt;/a&gt; page.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://mayeu.me/blog/cleaning-unused-docker-data/"&gt;mayeu.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>blog</category>
    </item>
  </channel>
</rss>
