index.html (45734B)
1 <!DOCTYPE html> 2 3 <html lang="fr"> 4 5 <head> 6 <meta charset="utf-8"> 7 <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 8 9 <link rel="start" href="https://vincent.demeester.fr" /> 10 11 <title>Vincent Demeester</title> 12 <link rel="canonical" href="https://vincent.demeester.fr/posts/2015-06-01-docker-1.6-ecosystem/"> 13 <link href="https://vincent.demeester.fr/index.xml" rel="alternate" type="application/rss+xml" title="Vincent Demeester" /> 14 15 <link rel="openid.server" href="https://indieauth.com/openid" /> 16 <link rel="openid.delegate" href="http://vincent.demeester.fr/" /> 17 <link rel="shortcut icon" type="image/x-icon" href="favicon.ico"> 18 19 <link rel="stylesheet" href="/css/screen.css" type="text/css" /> 20 <link rel="stylesheet" href="/css/sbrain.css" type="text/css" /> 21 <link rel="stylesheet" href="/css/syntax.css" type="text/css" /> 22 23 </head> 24 25 <body lang="fr"/> 26 27 28 29 30 31 32 <div id="main-container"> 33 <div id="page"> 34 <article class="post"> 35 <header> 36 <h1 class="emphnext">Docker 1.6 et son écosystème</h1><a href='https://vincent.demeester.fr/posts/2015-06-01-docker-1.6-ecosystem/'></a> 37 <address class="signature"> 38 <span class="date">Mon, 1 June, 2015</span> 39 <span class="words">(3100 Words)</span> 40 </address> 41 <ul class="tag_box inline"> 42 43 <li class="category"><a href="/categories/#zenika">zenika</a></li> 44 45 46 47 48 49 <li class="tag tag-docker"><a href="/tags/#docker">docker<span>3</span></a></li> 50 51 52 <li class="tag tag-compose"><a href="/tags/#compose">compose<span>1</span></a></li> 53 54 55 <li class="tag tag-swarm"><a href="/tags/#swarm">swarm<span>1</span></a></li> 56 57 58 <li class="tag tag-machine"><a href="/tags/#machine">machine<span>1</span></a></li> 59 60 61 <li class="tag tag-cluster"><a href="/tags/#cluster">cluster<span>1</span></a></li> 62 63 <br/> 64 65 </ul> 66 </header> 67 68 69 70 71 <div class="notice">Cet article est disponible sur le <a href="http://blog.zenika.com/">Blog de Zenika</a> à l'adresse suivante : <a href="http://blog.zenika.com/index.php?post/2015/06/01/Docker-1-6-et-son-ecosysteme">http://blog.zenika.com/index.php?post/2015/06/01/Docker-1-6-et-son-ecosysteme</a>. Cet publication me sert de mirroir / sauvegarde.</div> 72 73 <div id="outline-container-sec-1" class="outline-2"> 74 <h2 id="sec-1">Introduction</h2> 75 <div class="outline-text-2" id="text-1"> 76 <p> 77 Le 28 octobre dernier, nous avions parlé de la sortie de Docker 1.3, des évolutions entre la version 1 et cette dernière et de son écosystème. Je vous proposes de remettre ça, bientôt 6 mois après, avec un peu le même plan : les principales nouveautés entre la version 1.3 et 1.6 (et il y en a <code>;-)</code>), l'évolution de l'écosystème qui gravite autour et un peu de <i>social</i> avec les meetups et évènements qui se sont passés depuis. 78 </p> 79 80 81 <div class="figure"> 82 <p><img src="./img/docker-16/docker_container_engine_logo.png" alt="docker_container_engine_logo.png" /> 83 </p> 84 </div> 85 86 <p> 87 Rappel <i>ultra</i> rapide, <b>Docker est une plate-forme ouverte à destination des développeurs et administrateurs systèmes visant à faciliter la construction et le déploiement d'applications distribuées</b>. De manière moins marketing, l'idée derrière Docker est d'automatiser le déploiement d'environnements sous forme de conteneurs légers, portables et auto-suffisants ; les conteneurs permettant d'isoler l'exécution des applications dans des contextes d'exécution. Pour ce 88 faire, Docker, écrit en Go, reprend les bases de LXC, utilise les fonctionnalités du noyau Linux (CGroups, Namespaces, …) et se base initialement sur un système de fichier "en oignons" AUFS ; D'autres backends sont supportés également comme BTRFS ou devicemapper (LVM). 89 </p> 90 </div> 91 </div> 92 93 <div id="outline-container-sec-2" class="outline-2"> 94 <h2 id="sec-2">Ovelay filesystem storage driver (1.4.0)</h2> 95 <div class="outline-text-2" id="text-2"> 96 <p> 97 La release 1.4.0 de Docker (et la 1.3.3 en parallèle) a surtout été une gigantesque <i>bugfix party</i>, histoire de rendre les fonctionnalités arrivées auparavant plus stable — la release note se trouve <a href="https://github.com/docker/docker/blob/master/CHANGELOG.md#140-2014-12-11">ici</a>. 98 </p> 99 100 <p> 101 La principale nouveautée de cette version est l'apparation d'un nouveau <i>storage driver</i>, il s'agit d'<b>OverlayFs</b>. Il s'agit d'un mécanisme de montage permettant de superposer dans un répertoire le contenu de plusieurs autres répertoires. 102 </p> 103 104 <p> 105 Initialement Docker est basé <b><a href="http://en.wikipedia.org/wiki/Aufs">Aufs</a></b> qui fait, pour simplifier, la même chose. Le problème avec aufs est qu'il n'est pas intégré dans le noyau Linux (i.e. dans les sources officielles), contrairement à OverlayFS qui a fait son apparition avec le noyau <a href="https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=e9be9d5e76e34872f0c37d72e25bc27fe9e2c54c">3.18</a>. Il était donc nécessaire de disposer d'un noyau patché ; Le noyau Linux de Debian et de sess dérivés (Ubuntu, etc.) ont ce <i>patchset</i> aufs de base mais ce n'est pas le cas de toutes les distributions. L'idée de cette intégration est assez simple : supporter le maximum de distributions en se basant sur une <i>feature</i> du noyau. C'est donc bien évidemment le <i>driver</i> d'avenir pour Docker ; attention cependant, la peinture est encore un peu fraîche <code>;-P</code>. 106 </p> 107 </div> 108 </div> 109 110 <div id="outline-container-sec-3" class="outline-2"> 111 <h2 id="sec-3">Support d'IPv6 (1.5.0)</h2> 112 <div class="outline-text-2" id="text-3"> 113 <p> 114 Les adresses IPv4 commencent a se faire <a href="http://en.wikipedia.org/wiki/IPv4_address_exhaustion">rare</a>, il est donc important que Docker supporte IPv6. C'est désormais le cas avec la version 1.5.0, même si ce n'est pas activé par défaut. Pour activer le support de l'IPv6 (en plus de l'IPv4), il faut ajouter le <i>flag</i> <code>--ipv6</code> au daemon. Docker va donc mettre en place le <i>bridge</i> <code>docker0</code> avec en plus un IPv6 en mode local, avec l'adresse <code>fe80::1</code>. 115 </p> 116 117 <p> 118 Par défaut les containers qui seront créés n'auront qu'une adresse locale. Pour avoir une adresse IPv6 routable à votre conteneur, il est nécessaire de lui préciser un <i>sous-réseau</i> (subnet) dans lequel il va piocher son adresse. Cela se fait grâce à l'argument <code>--fixed-cidr-v6</code>. 119 </p> 120 121 122 <div class="org-src-container"> 123 124 <pre class="src src-sh"><span style="color: #783778;">docker</span> <span style="color: #43783f;">-d</span> <span style="color: #374478;">--ipv6</span> <span style="color: #78683f;">--fixed-cidr-v6</span>=<span style="color: #008000;">"2001:db8:1::/64"</span> 125 </pre> 126 </div> 127 128 <p> 129 Comme je ne suis pas un pro de l'IPv6, pour plus d'information, et si l'anglais ne vous fait pas peur, c'est dans la <a href="https://docs.docker.com/articles/networking/#ipv6">documentation "networking"</a> de Docker. 130 </p> 131 </div> 132 </div> 133 134 <div id="outline-container-sec-4" class="outline-2"> 135 <h2 id="sec-4">Conteneurs en lecture seule (1.5.0)</h2> 136 <div class="outline-text-2" id="text-4"> 137 <p> 138 Une autre fonctionnalité assez sympathique qui est arrivé avec cette version 1.5.0 est les conteneurs en lecture seule — c'est Michael Crosby qui s'est occupé d'<a href="https://github.com/docker/docker/pull/10093">implémenter ça</a>. L'intérêt des conteneurs en lecture seule est de permettre de <b>contrôler où l'application</b> à l'intérieur de votre conteneur <b>peut écrire ou modifier des fichiers</b>. En combinant ceci avec les volumes, vous pouvez vous assurez des emplacements dans lesquels votre conteneur va persister des états ou données (le/les volumes), puisqu'il ne sera pas possible d'écrire ailleurs de toute façon. 139 </p> 140 141 <p> 142 Pour activer cette fonctionnalité, c'est l'argument <code>--read-only</code>. 143 </p> 144 145 <div class="org-src-container"> 146 147 <pre class="src src-sh"><span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #43783f;">--read-only</span> <span style="color: #5e7837;">-v</span> <span style="color: #5e7837;">/volume/writable</span> <span style="color: #3f7178;">busybox</span> <span style="color: #783778;">touch</span> <span style="color: #5e7837;">/volume/writable</span> 148 </pre> 149 </div> 150 151 <p> 152 Une autre utilisation des conteneurs en lecture seule est que cela donne la possibilité de faire du debug <i>post-mortem</i> d'un conteneur (en production par exemple). Cela nous permet de redémarrer un conteneur qui aurait planté, en lecture seule avec le système de fichier dans l'état du crash. 153 </p> 154 </div> 155 </div> 156 157 <div id="outline-container-sec-5" class="outline-2"> 158 <h2 id="sec-5">Les labels pour le « daemon », les images et les conteneurs (1.6.0)</h2> 159 <div class="outline-text-2" id="text-5"> 160 <blockquote> 161 <p> 162 One Meta Data to Rule Them All 163 </p> 164 </blockquote> 165 166 <p> 167 Une des deux fonctionnalités très attendue de la récente version 1.6.0 sont les labels. En un mot, et pour le faire « à-la » <i>le seigneur des anneaux</i>, les labels peuvent se résumer en "<b>Une metadata pour les gouverner tous</b>" (ça le fait vachement mieux en anglais en fait). 168 </p> 169 170 <p> 171 Les labels s'appliquent sur le <i>daemon</i>, les images et les conteneurs. C'est un peu un mélange entre des tags et des variables d'environnements puisque il s'agit d'un couple <b>clé/valeur</b>. 172 </p> 173 174 <p> 175 L'ajout de label sur le <i>daemon</i> se fait grâce à l'argument — roulement de tambour — <code>--label</code> (<code>\o/</code>). La principale utilité pour l'instant est son utilisation conjointe avec Swarm dont nous parlerons un peu plus bas ; mais en deux mots, cela permet de filtrer les <i>engines</i> sur lesquels on va <i>taper</i>. 176 </p> 177 178 <div class="org-src-container"> 179 180 <pre class="src src-sh"><span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Souvent, c'est dans DOCKER_OPTS du fichier /etc/default/docker</span> 181 <span style="color: #783778;">docker</span> <span style="color: #43783f;">-d</span> <span style="color: #784437;">-H</span> <span style="color: #4f5c7e;">unix://var/run/docker.sock</span> <span style="color: #7a4f7e;">--label</span> <span style="color: #BA36A5;">storage</span>=<span style="color: #5e7837;">ssd</span> <span style="color: #7a4f7e;">--label</span> <span style="color: #BA36A5;">type</span>=<span style="color: #5e7837;">laptop</span> 182 </pre> 183 </div> 184 185 <p> 186 L'ajout d'un label sur une image se fait dans le fichier <code>Dockerfile</code>, et l'ajout d'un label sur un conteneur, grâce à l'argument <code>--label</code> pour rester cohérent. Construisons une image inutile mais en lui appliquant un label : 187 </p> 188 189 <div class="org-src-container"> 190 191 <pre class="src src-sh"><span style="color: #37785e;">FROM</span> <span style="color: #3f7178;">busybox</span> 192 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Support du multi-line pour LABEL</span> 193 <span style="color: #783778;">LABEL</span> <span style="color: #BA36A5;">vendor</span>=<span style="color: #78683f;">zenika</span> <span style="color: #008000;">\</span> 194 <span style="color: #784437;">com.zenika.lang</span>=<span style="color: #374478;">golang</span> <span style="color: #008000;">\</span> 195 <span style="color: #78683f;">com.zenika.version</span>=<span style="color: #43783f;">0.1</span> 196 <span style="color: #7a4f7e;">CMD</span> [<span style="color: #008000;">"echo"</span><span style="color: #7e544f;">,</span> <span style="color: #008000;">"zenika"</span>] 197 </pre> 198 </div> 199 200 <p> 201 Nous allons maintenant construire cette image et lancer un conteneur à partir de cette dernière avec un autre label. 202 </p> 203 204 <div class="org-src-container"> 205 206 <pre class="src src-sh">$ <span style="color: #783778;">docker</span> <span style="color: #7e544f;">build</span> <span style="color: #7a4f7e;">-t</span> <span style="color: #4f7e67;">zenikaapp</span> <span style="color: #374478;">.</span> 207 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…]</span> 208 $ <span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #4f5c7e;">--name</span> <span style="color: #783f5a;">test</span> <span style="color: #7a4f7e;">--label</span> <span style="color: #783f5a;">com.zenika.foo</span>=<span style="color: #784437;">bar</span> <span style="color: #4f7e67;">zenikaapp</span> 209 <span style="color: #78683f;">zenika</span> 210 </pre> 211 </div> 212 213 <p> 214 L'idée c'est que maintenant, lorsque l'on va regarder la liste d'images ou de conteneurs à disposition sur notre <i>engine</i>, nous allons pouvoir <b>filtrer</b> par label, comme suit : 215 </p> 216 217 <div class="org-src-container"> 218 219 <pre class="src src-sh">$ <span style="color: #783778;">docker</span> <span style="color: #707e4f;">images</span> <span style="color: #4f5c7e;">--filter</span> <span style="color: #008000;">"label=vendor=zenika"</span> <span style="color: #4f5c7e;">--filter</span> <span style="color: #008000;">"label=com.zenika.lang=golang"</span> 220 <span style="color: #784437;">REPOSITORY</span> <span style="color: #3f7178;">TAG</span> <span style="color: #707e4f;">IMAGE</span> <span style="color: #707e4f;">ID</span> <span style="color: #7e544f;">CREATED</span> <span style="color: #784437;">VIRTUAL</span> <span style="color: #4f7e67;">SIZE</span> 221 <span style="color: #4f7e67;">zenikaapp</span> <span style="color: #7a4f7e;">latest</span> <span style="color: #784437;">66ffda023118</span> <span style="color: #5e7837;">43</span> <span style="color: #7e544f;">seconds</span> <span style="color: #43783f;">ago</span> <span style="color: #783778;">2.433</span> <span style="color: #7e544f;">MB</span> 222 $ <span style="color: #783778;">docker</span> <span style="color: #43783f;">ps</span> <span style="color: #784437;">-a</span> <span style="color: #4f5c7e;">--filter</span> <span style="color: #008000;">"label=com.zenika.foo=bar"</span> 223 <span style="color: #374478;">CONTAINER</span> <span style="color: #707e4f;">ID</span> <span style="color: #707e4f;">IMAGE</span> <span style="color: #78683f;">COMMAND</span> <span style="color: #7e544f;">CREATED</span> [<span style="color: #374478;">…</span>] <span style="color: #707e4f;">NAMES</span> 224 <span style="color: #513f78;">37e9a37caf57</span> <span style="color: #513f78;">zenikaapp:latest</span> <span style="color: #008000;">"echo zenika"</span> <span style="color: #43783f;">About</span> <span style="color: #78683f;">a</span> <span style="color: #37785e;">minute</span> <span style="color: #43783f;">ago</span> [<span style="color: #374478;">…</span>] <span style="color: #783f5a;">test</span> 225 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">On peut également regarder les labels avec inspect</span> 226 $ <span style="color: #783778;">docker</span> <span style="color: #374478;">inspect</span> <span style="color: #3f7178;">-f</span> <span style="color: #008000;">"{{json .ContainerConfig.Labels }}"</span> <span style="color: #783f5a;">zenikaap</span> 227 {<span style="color: #008000;">"com.zenika.lang"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"golang"</span><span style="color: #7e544f;">,</span><span style="color: #008000;">"com.zenika.version"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"0.1"</span><span style="color: #7e544f;">,</span><span style="color: #008000;">"vendor"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"zenika"</span>} 228 $ <span style="color: #783778;">docker</span> <span style="color: #374478;">inspect</span> <span style="color: #3f7178;">-f</span> <span style="color: #008000;">"{{json .Config.Labels }}"</span> <span style="color: #783f5a;">test</span> 229 {<span style="color: #008000;">"com.zenika.foo"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"bar"</span><span style="color: #7e544f;">,</span><span style="color: #008000;">"com.zenika.lang"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"golang"</span><span style="color: #7e544f;">,</span><span style="color: #008000;">"com.zenika.version"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"0.1"</span><span style="color: #7e544f;">,</span><span style="color: #008000;">"vendor"</span><span style="color: #707e4f;">:</span><span style="color: #008000;">"zenika"</span>} 230 </pre> 231 </div> 232 233 <p> 234 On peut imaginer beaucoup d'usage de ces labels. Par example, avec <a href="http://rancher.com">Rancher</a>, ils sont utilisés pour faciliter la configuration du load-balancer (<a href="http://rancher.com/docker-labels/">ici</a>) — ils utilisent un label <code>io.rancher.service.provides</code> qui permettra à ce dernier de trouver automatiquement ces petits. Je vous laisse imaginer vos propres <i>use-cases</i>. 235 </p> 236 237 <p> 238 Il y a une partie de la documentation qui parle exclusivement des labels, avec une petite partie sur les <i>best-practice</i> de nommage des labels, c'est <a href="https://docs.docker.com/userguide/labels-custom-metadata/">ici</a>. 239 </p> 240 </div> 241 </div> 242 243 <div id="outline-container-sec-6" class="outline-2"> 244 <h2 id="sec-6">Logging drivers \o/ (1.6.0)</h2> 245 <div class="outline-text-2" id="text-6"> 246 <p> 247 Un <i>gros reproche</i> qui était fait à Docker était sa gestion très <b>simpliste</b> des logs des conteneurs. Plusieurs critiques étaient faites : 248 </p> 249 250 <ol class="org-ol"> 251 <li>Tout faire sortir sur <code>stdout</code> et <code>stderr</code> n'est pas vraiment une habitude de nos jours, surtout dans des langages comme Java où l'utilisation de <i>logger</i> (Log4j, Slf4j, …) est très répandue. Cela rend le portage vers docker de certaines applications un peu plus fastidieux. 252 </li> 253 <li>Il n'y avait aucun mécanisme de <i>rotation</i> de logs — et comme en plus le dossier dans lequel les logs étaient écris est un peu enfoui dans <code>/var/lib/docker</code>, cela pouvait poser quelques problème si des conteneurs étaient un peu trop bavards.. 254 </li> 255 <li>La solution utilisée pour sauvegarder ces logs et pourquoi pas les centraliser (avec ELK par exemple), était d'utiliser un volume, souvent partagé entre applications, et de démarrer un conteneur pour gérer cette analyse, centralisation, …. Non seulement ce n'est pas très optimal, mais cela nécessitait de configurer chaque application (donc chaque conteneur) — et <code>docker logs</code> perdait tout son intérêt. 256 </li> 257 </ol> 258 259 <p> 260 Avec la version 1.6.0, les <i>logging driver</i> permettent une gestion des logs un peu plus optimale, ou au moins plus flexible. Il est donc maintenant possible de préciser le <i>logging driver</i> à utiliser. Ils en existent 3 pour l'instant : 261 </p> 262 263 <ol class="org-ol"> 264 <li><code>json-file</code> correspond au comportement par défaut de Docker avant la 1.6 et reste la valeur par défaut 265 </li> 266 <li><code>syslog</code> qui permet de connecter les logs de nos conteneurs dans notre vénérable syslog (ou en tout cas quelqu'un qui parl le syslog). 267 </li> 268 <li><code>none</code> qui est le magicien puisqu'il nous permet de faire taire complètement un conteneur <code>o/</code>. 269 </li> 270 </ol> 271 272 <p> 273 Il est possible de définir le logging driver à deux endroits : 274 </p> 275 276 <ol class="org-ol"> 277 <li>sur le <b>daemon</b> pour la valeur par défaut de tous les conteneurs. 278 </li> 279 </ol> 280 281 <div class="org-src-container"> 282 283 <pre class="src src-sh">$ <span style="color: #783778;">docker</span> <span style="color: #43783f;">-d</span> <span style="color: #5e7837;">--log-driver</span>=<span style="color: #008000;">"json-file"</span> 284 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Pour faire taire les conteneurs par défaut</span> 285 $ <span style="color: #783778;">docker</span> <span style="color: #43783f;">-d</span> <span style="color: #5e7837;">--log-driver</span>=<span style="color: #008000;">"none"</span> 286 </pre> 287 </div> 288 289 <ol class="org-ol"> 290 <li>en option de la command <b>run</b> (ou de la commande <b>create</b>). 291 </li> 292 </ol> 293 294 <div class="org-src-container"> 295 296 <pre class="src src-sh">$ <span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #5e7837;">--log-driver</span>=<span style="color: #3f7178;">syslog</span> <span style="color: #3f7178;">ubuntu</span> <span style="color: #008000;">\</span> 297 <span style="color: #37785e;">/bin/bash</span> <span style="color: #784437;">-c</span> <span style="color: #008000;">'while true; do echo "Hello"; sleep1; done'</span> 298 $ <span style="color: #707e4f;">tail</span> <span style="color: #3f7178;">-f</span> <span style="color: #784437;">/var/log/syslog</span> 299 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…]</span> 300 <span style="color: #374478;">May</span> <span style="color: #3f7178;">28</span> <span style="color: #3f7178;">17:39:01</span> <span style="color: #7e544f;">dev1</span> <span style="color: #783778;">docker</span>[<span style="color: #5e7837;">116314</span>]<span style="color: #707e4f;">:</span> <span style="color: #513f78;">0e5b67244c00:</span> <span style="color: #4f7e67;">Hello</span> 301 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…]</span> 302 </pre> 303 </div> 304 305 <p> 306 Une option <code>--log-opts</code> est également présente pour passer des options additionnelles au driver si celui-ci les supporte. Notons également qu'un driver pour <code>systemd</code> devrait arriver avec la version 1.7. 307 </p> 308 309 <p> 310 La pull-request ayant permis d'intégrer cette fonctionnalité se trouve <a href="https://github.com/docker/docker/pull/10568">ici</a>. 311 </p> 312 </div> 313 </div> 314 315 <div id="outline-container-sec-7" class="outline-2"> 316 <h2 id="sec-7">Client Windows natif (1.6.0)</h2> 317 <div class="outline-text-2" id="text-7"> 318 <p> 319 Enfin, on s'en doutait un peu après l'annonce du partenariat entre Docker Microsoft, ça bosse beaucoup pour porter Docker vers Windows. La première étape était de fournir un client natif pour Windows. C'est chose faite avec cette version 1.6. Maintenant beaucoup de travail est effectué pour rendre le <i>engine</i> plus portable, il n'y a qu'à suivre un peu les pull-request avec un tag <code>os/windows</code> ou encore cette très récente pull-request avec un titre plutôt évocateur : « <a href="https://github.com/docker/docker/pull/13554">Windows: The real Windows exec driver is here</a> ». 320 </p> 321 </div> 322 </div> 323 324 <div id="outline-container-sec-8" class="outline-2"> 325 <h2 id="sec-8">Divers</h2> 326 <div class="outline-text-2" id="text-8"> 327 <p> 328 Il y a pas mal d'autres options qui sont arrivées depuis la version 1.3.0, nous allons en parcourir certaines rapidement — parce que sinon cet article va faire 100 pages ;-p : 329 </p> 330 331 <ul class="org-ul"> 332 <li><b>Stats</b> (1.5.0) : une commande <code>stats</code> (et donc une API derrière) permet de récupérer quelques statistiques par conteneur, c'est simple pour l'instant. 333 </li> 334 <li>Depuis la version 1.5.0 il est possible de spécifier le ficher Dockerfile grâce à l'option <code>-f</code> de la commande build — jusqu'à maintenant docker regardait uniquement le dossier spécifié et cherchait le fichier <code>Dockerfile</code>. Cela permet donc, par exemple, d'avoir plusieurs <code>Dockerfile</code> dans un dossier. 335 </li> 336 </ul> 337 338 <div class="org-src-container"> 339 340 <pre class="src src-sh">$ <span style="color: #783778;">docker</span> <span style="color: #7e544f;">build</span> <span style="color: #7a4f7e;">-t</span> <span style="color: #5e7837;">monimage</span> <span style="color: #3f7178;">-f</span> <span style="color: #43783f;">backend.Dockerfile</span> <span style="color: #374478;">.</span> 341 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…] Build the backend</span> 342 $ <span style="color: #783778;">docker</span> <span style="color: #7e544f;">build</span> <span style="color: #7a4f7e;">-t</span> <span style="color: #5e7837;">monimage</span> <span style="color: #3f7178;">-f</span> <span style="color: #4f7e67;">frontend.Dockerfile</span> <span style="color: #374478;">.</span> 343 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…] Build the frontend</span> 344 </pre> 345 </div> 346 347 <ul class="org-ul"> 348 <li>Le <i>registry</i> voit son API passer en V2, principalement pour améliorer les transferts. L'implémentation officielle a été réécrite en Go (à la place de Python) et se nomme maintenant <a href="https://github.com/docker/distribution">distribution</a>. 349 </li> 350 <li>La commande <code>commit est dotée, depuis la version 1.6.0, d'une option =--change</code> qui permet d'appliquer une instruction supportée par les <code>Dockerfile</code> — voir <a href="https://docs.docker.com/reference/commandline/cli/#commit">ici</a>. 351 </li> 352 <li>Docker a publié un petit document « <a href="https://github.com/docker/docker/blob/master/image/spec/v1.md">Docker Image Specification</a> » qui a pour but de définir le format des images utilisées par Docker, permettant à d'autres notamment des potentiels conccurents, d'implémenter des images qui seraient compatible. 353 </li> 354 </ul> 355 </div> 356 </div> 357 358 <div id="outline-container-sec-9" class="outline-2"> 359 <h2 id="sec-9">À venir</h2> 360 <div class="outline-text-2" id="text-9"> 361 <p> 362 L'une des principales nouveautés qui devrait arriver avec la version 1.7 de Docker, c'est l'intégration d'une <b>nouvelle stack réseau</b> avec l'intégration du projet <a href="https://github.com/docker/libnetwork">libnetwork</a>, si tout se passe bien. On pourra noter également de nouveaux <i>logging driver</i>, avec notamment un <code>rollover</code> driver ou encore le <code>systemd</code> driver. On peut noter également l'arrivée d'un <i>filesystem driver</i> pour ZFS (voici la <a href="https://github.com/docker/docker/pull/9411">pull-request</a>). Le Docker Birthday étant passé par là, beaucoup de corrections de bugs, de nouvelles petites fonctionnalités, une meilleure couverture de code par les tests unitaires (<code>o/</code>). 363 </p> 364 365 <p> 366 La RC1 est disponible depuis le 28 mai, <a href="https://github.com/docker/docker/releases/tag/v1.7.0-rc1">ici</a> et la <a href="https://github.com/docker/docker/pull/13528">pull-request</a> associée, donc à vos tests ! 367 </p> 368 </div> 369 </div> 370 371 <div id="outline-container-sec-10" class="outline-2"> 372 <h2 id="sec-10">Écosystème</h2> 373 <div class="outline-text-2" id="text-10"> 374 <p> 375 Trois projets « Docker » sont apparus depuis le dernier article : Compose, Swarm et Machine. Présentons les très rapidement. 376 </p> 377 </div> 378 379 <div id="outline-container-sec-10-1" class="outline-3"> 380 <h3 id="sec-10-1">Compose</h3> 381 <div class="outline-text-3" id="text-10-1"> 382 <p> 383 Compose est le nouveau nom de Fig. Fig était développé par Orchard qui a été racheté par Docker. Pour rappel, l'idée est de définir son environnement via un fichier YAML, que ce soit pour le code sur lequel nous travaillons mais également les services externes desquels notre application dépend (Base de données, ''Message queue'', etc.). 384 </p> 385 386 <div class="org-src-container"> 387 388 <pre class="src src-yaml"><span style="color: #BA36A5;">web</span>: 389 <span style="color: #BA36A5;">build</span>: . 390 <span style="color: #BA36A5;">command</span>: lein run 391 <span style="color: #BA36A5;">links</span>: 392 - db 393 <span style="color: #BA36A5;">ports</span>: 394 - <span style="color: #008000;">"8000:8000"</span> 395 <span style="color: #BA36A5;">db</span>: 396 <span style="color: #BA36A5;">image</span>: postgres 397 </pre> 398 </div> 399 400 <p> 401 Compose est en version 1.2 — depuis la version 1.0, la majorité des modifications sont des corrections de bugs et des ajouts pour suivre les modifications et nouvelles fonctionnalités de Docker (<code>env-file</code>, <code>dns_search</code>, <code>add_host</code>, <code>restart</code>, <code>volumes_from</code>, <code>net</code>, …). La commande est maintenant <code>docker-compose</code> à la place de <code>fig</code> et le fichier <code>docker-compose.yml</code> à la place de <code>fig.yml</code> — pour des raisons de rétro-compatibilité, Compose continue de lire les <code>fig.yml</code>. 402 </p> 403 </div> 404 </div> 405 406 <div id="outline-container-sec-10-2" class="outline-3"> 407 <h3 id="sec-10-2">Swarm & Machine</h3> 408 <div class="outline-text-3" id="text-10-2"> 409 <p> 410 Deux nouveaux projets ont fait leur apparition dans l'escarcelle de Docker Inc. : Swarm et Machine. Swarm est le <i>clustering</i> à moyenne échelle vu par Docker. Machine permet de provisionner Docker sur différents providers : amazon aws, google compute engine, azure, Virtualbox pour ne citer qu'eux — mais beaucoup d'autres sont déjà supportés. 411 </p> 412 413 <p> 414 Voilà ce que Jérôme Petazzoni dit à propos de Swarm : 415 </p> 416 417 <blockquote> 418 <p> 419 Un système de cluster utilisant l’API Docker et compatible avec tous les outils de l’écosystème maison. On peut utiliser les commandes classiques Docker pour piloter le cluster 420 </p> 421 </blockquote> 422 423 <p> 424 L'idée principale de Swarm est <b>Je veux parler à mon cluster Docker de la même façon que je parle avec mon daemon Docker</b>. Cela se traduit assez simplement par : <b>Swarm expose la même API que docker</b>. C'est une idée simple et terriblement puissante puisque cela veut dire que je peux administrer mon cluster avec les mêmes commandes que j'utilise quand je travaille en local. Swarm a pour but de piloter des clusters d'une taille relativement petite (moins de 1000 machines). Pour les clusters de plus grande taille, il existe de très bonnes solutions, comme <a href="http://mesos.apache.org/">Mesos</a>, et ce n'est pas le but de Docker Inc. de venir les concurrencer, bien au contraire. 425 </p> 426 427 <p> 428 Pour faire simple, swarm c'est un <b>manager</b> et des <b>agents</b> (un par engine) — les agents s'enregistrent auprès du master par le biais d'un <i>service discovery</i>. Swarm dispose d'un petit service de discovery mais qui n'est là que pour <i>la démo</i> ; il est possible et conseillé de le connecter à des solutions existantes, pour l'instant <a href="https://github.com/hashicorp/consul">consul</a> et <a href="https://github.com/coreos/etcd">etcd</a>. 429 </p> 430 431 <p> 432 Un bout de code vaut mieux qu'un long discours, voici comment <i>bootstraper</i> un cluster Swarm, avec l'aide de Machine pour être <i>funky</i>. 433 </p> 434 435 <div class="org-src-container"> 436 437 <pre class="src src-sh"><span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">On crée un cluster simple avec son id</span> 438 $ <span style="color: #7a4f7e;">swarm</span> <span style="color: #783f5a;">create</span> 439 <span style="color: #3f7178;">50862dcedd53c2f584adfb00e85bac4b</span> 440 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">On démarre des agents</span> 441 $ <span style="color: #43783f;">docker-machine</span> <span style="color: #783f5a;">create</span> <span style="color: #43783f;">-d</span> <span style="color: #374478;">azure</span> <span style="color: #4f5c7e;">--swarm</span> <span style="color: #4f5c7e;">--swarm-discovery</span> <span style="color: #4f7e67;">token://50862dcedd53c2f584adfb00e85bac4b</span> <span style="color: #43783f;">node1</span> 442 <span style="color: #3f7178;">INFO</span>[<span style="color: #7a4f7e;">0000</span>] <span style="color: #5e7837;">Creating</span> <span style="color: #513f78;">SSH</span> <span style="color: #5e7837;">key...</span> 443 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…]</span> 444 $ <span style="color: #43783f;">docker-machine</span> <span style="color: #783f5a;">create</span> <span style="color: #43783f;">-d</span> <span style="color: #7e544f;">digitalocean</span> <span style="color: #4f5c7e;">--swarm</span> <span style="color: #4f5c7e;">--swarm-discovery</span> <span style="color: #4f7e67;">token://50862dcedd53c2f584adfb00e85bac4b</span> <span style="color: #783f5a;">node2</span> 445 <span style="color: #3f7178;">INFO</span>[<span style="color: #7a4f7e;">0000</span>] <span style="color: #5e7837;">Creating</span> <span style="color: #513f78;">SSH</span> <span style="color: #5e7837;">key...</span> 446 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">[…]</span> 447 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">On démarre le master</span> 448 $ <span style="color: #43783f;">docker-machine</span> <span style="color: #783f5a;">create</span> <span style="color: #43783f;">-d</span> <span style="color: #78683f;">virtualbox</span> <span style="color: #4f5c7e;">--swarm</span> <span style="color: #707e4f;">--swarm-master</span> <span style="color: #4f5c7e;">--swarm-discovery</span> <span style="color: #4f7e67;">token://50862dcedd53c2f584adfb00e85bac4b</span> <span style="color: #374478;">manager</span> 449 </pre> 450 </div> 451 452 <p> 453 Maintenant que l'on dispose d'un petit cluster, en pointant dessus (merci Machine) on va pouvoir lancer des commandes docker. 454 </p> 455 456 <div class="org-src-container"> 457 458 <pre class="src src-sh">$ $(<span style="color: #43783f;">docker-machine</span> <span style="color: #7a4f7e;">env</span> <span style="color: #4f5c7e;">--swarm</span> <span style="color: #374478;">manager</span>) 459 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Le bon vieux info</span> 460 $ <span style="color: #783778;">docker</span> <span style="color: #784437;">info</span> 461 <span style="color: #3f7178;">Containers:</span> <span style="color: #4f5c7e;">4</span> 462 <span style="color: #4f5c7e;">Nodes:</span> <span style="color: #7e544f;">3</span> 463 <span style="color: #374478;">manager:</span> <span style="color: #7e544f;">192.168.99.103:2376</span> 464 <span style="color: #78683f;">└</span> <span style="color: #3f7178;">Containers:</span> <span style="color: #513f78;">2</span> 465 <span style="color: #78683f;">└</span> <span style="color: #4f7e67;">Reserved</span> <span style="color: #4f5c7e;">CPUs:</span> <span style="color: #784437;">0</span> <span style="color: #783778;">/</span> <span style="color: #4f5c7e;">4</span> 466 <span style="color: #78683f;">└</span> <span style="color: #4f7e67;">Reserved</span> <span style="color: #7a4f7e;">Memory:</span> <span style="color: #784437;">0</span> <span style="color: #4f7e67;">B</span> <span style="color: #783778;">/</span> <span style="color: #707e4f;">999.9</span> <span style="color: #783f5a;">MiB</span> 467 <span style="color: #78683f;">node1:</span> <span style="color: #513f78;">45.55.160.223:2376</span> 468 <span style="color: #78683f;">└</span> <span style="color: #3f7178;">Containers:</span> <span style="color: #78683f;">1</span> 469 <span style="color: #78683f;">└</span> <span style="color: #4f7e67;">Reserved</span> <span style="color: #4f5c7e;">CPUs:</span> <span style="color: #784437;">0</span> <span style="color: #783778;">/</span> <span style="color: #78683f;">1</span> 470 <span style="color: #78683f;">└</span> <span style="color: #4f7e67;">Reserved</span> <span style="color: #7a4f7e;">Memory:</span> <span style="color: #784437;">0</span> <span style="color: #4f7e67;">B</span> <span style="color: #783778;">/</span> <span style="color: #4f7e67;">490</span> <span style="color: #783f5a;">MiB</span> 471 <span style="color: #513f78;">node2:</span> <span style="color: #707e4f;">swarm-nwde2.cloudapp.net:2376</span> 472 <span style="color: #78683f;">└</span> <span style="color: #3f7178;">Containers:</span> <span style="color: #78683f;">1</span> 473 <span style="color: #78683f;">└</span> <span style="color: #4f7e67;">Reserved</span> <span style="color: #4f5c7e;">CPUs:</span> <span style="color: #784437;">0</span> <span style="color: #783778;">/</span> <span style="color: #78683f;">1</span> 474 <span style="color: #78683f;">└</span> <span style="color: #4f7e67;">Reserved</span> <span style="color: #7a4f7e;">Memory:</span> <span style="color: #784437;">0</span> <span style="color: #4f7e67;">B</span> <span style="color: #783778;">/</span> <span style="color: #7e544f;">1.639</span> <span style="color: #3f7178;">GiB</span> 475 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">On démarre des nginx</span> 476 $ <span style="color: #4f7e67;">for</span> <span style="color: #7a4f7e;">i</span><span style="color: #0000FF;"> in</span> <span style="color: #FF1493;">`seq 1 3`</span>; <span style="color: #0000FF;">do</span> <span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #43783f;">-d</span> <span style="color: #783f5a;">-p</span> <span style="color: #7a4f7e;">80:80</span> <span style="color: #783778;">nginx</span>; <span style="color: #0000FF;">done</span> 477 $ <span style="color: #783778;">docker</span> <span style="color: #43783f;">ps</span> 478 <span style="color: #374478;">CONTAINER</span> <span style="color: #707e4f;">ID</span> <span style="color: #707e4f;">IMAGE</span> <span style="color: #78683f;">COMMAND</span> <span style="color: #78683f;">...</span> <span style="color: #3f7178;">PORTS</span> <span style="color: #707e4f;">NAMES</span> 479 <span style="color: #374478;">9bff07d8ee18</span> <span style="color: #78683f;">nginx:1.7</span> <span style="color: #008000;">"nginx -g 'daemon of ... 443/tcp, 104.210.33.180:80->80/tcp node1/loving_torvalds</span> 480 <span style="color: #008000;">457ed59c9bb3 nginx:1.7 "</span><span style="color: #783778;">nginx</span> <span style="color: #78683f;">-g</span> <span style="color: #008000;">'daemon of ... 443/tcp, 45.55.160.223:80->80/tcp node2/drunk_swartz</span> 481 <span style="color: #008000;">6013be18cdbc nginx:1.7 "nginx -g '</span><span style="color: #3f7178;">daemon</span> <span style="color: #5e7837;">of</span> <span style="color: #78683f;">...</span> <span style="color: #784437;">443/tcp,</span> <span style="color: #4f7e67;">192.168.99.103:80-</span>><span style="color: #5e7837;">80/tcp</span> <span style="color: #707e4f;">manager/condescending_galileo</span> 482 </pre> 483 </div> 484 485 <p> 486 On voit qu'on a démarré nginx sur les 3 nœuds. Swarm a quelques stratégies pour démarrer un conteneur sur un nœud ou l'autre : 487 </p> 488 489 <ul class="org-ul"> 490 <li><code>spread</code> va éparpiller les conteneurs pour que chaque nœud en ait le moins possible (répartis). 491 </li> 492 <li><code>binpack</code> va faire l'inverse (tout sur le même nœud jusqu'à ce que ses ressources soient épuisés). 493 </li> 494 <li><code>random</code> qui fait <i>au pif</i>. 495 </li> 496 </ul> 497 498 <p> 499 Il est également possible de mettre des contraintes lors du lancement d'un conteneur, en utilisant le flag <code>-e</code> de <code>docker run</code> (<code>-e</code> = variables d'environnement). 500 </p> 501 502 503 <div class="org-src-container"> 504 505 <pre class="src src-sh"><span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Démarrer postgres sur un host qui a le label storage=ssd</span> 506 $ <span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #43783f;">-d</span> <span style="color: #707e4f;">-e</span> <span style="color: #4f5c7e;">constraint:</span><span style="color: #4f5c7e;">storage</span>==<span style="color: #5e7837;">ssd</span> <span style="color: #4f5c7e;">--name</span> <span style="color: #4f7e67;">postgres</span> <span style="color: #4f7e67;">postgres</span> 507 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Démarrer redis à coté du conteneur dont le nom est postgres</span> 508 $ <span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #43783f;">-d</span> <span style="color: #707e4f;">-e</span> <span style="color: #4f5c7e;">affinity:</span><span style="color: #4f5c7e;">container</span>==<span style="color: #4f7e67;">postgres</span> <span style="color: #4f5c7e;">--name</span> <span style="color: #7e544f;">redis</span> <span style="color: #7e544f;">redis</span> 509 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">Démarer backend où tu veux, mais comme les links sont des contraintes</span> 510 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">implicites il démarrera sur le même host que postgres ET redis</span> 511 <span style="color: #8D8D84;"># </span><span style="color: #8D8D84; font-style: italic;">(ou ne démarrera pas ces derniers ne sont pas au même endroit)</span> 512 $ <span style="color: #783778;">docker</span> <span style="color: #513f78;">run</span> <span style="color: #43783f;">-d</span> <span style="color: #374478;">--link</span> <span style="color: #3f7178;">redis:redis</span> <span style="color: #374478;">--link</span> <span style="color: #784437;">postgres:db</span> <span style="color: #4f5c7e;">--name</span> <span style="color: #78683f;">backend</span> <span style="color: #78683f;">backend</span> 513 </pre> 514 </div> 515 516 <p> 517 On pourrait faire un article dédié à Swarm (ce qui sera probablement le cas dans un avenir assez proche) donc je vous laisse le découvrir via <a href="https://github.com/docker/swarm/">github</a>. 518 </p> 519 </div> 520 </div> 521 </div> 522 523 <div id="outline-container-sec-11" class="outline-2"> 524 <h2 id="sec-11">Levées de fonds et acquisitons</h2> 525 <div class="outline-text-2" id="text-11"> 526 <p> 527 Docker est sur toutes les lèvres en ce moment. Il est donc normal que cela attire également des capitaux. Le 14 avril dernier, Docker annonçait une nouvelle levée de fonds de <b>95 millions</b> de dollars. Après celle de <i>40 millions</i> en Septembre 2014, on peut se dire que Docker Inc. a de beaux jours à venir. 528 </p> 529 530 <p> 531 Docker Inc. « mange » aussi quelques startups, puisque après Orchard, qui éditait fig (devenu docker-compose), ils ont fait l'acquisition de <a href="http://socketplane.io/">Socketplane</a> et <a href="https://kitematic.com/">Kitematic</a>. Kitematic est un outil <i>desktop</i> qui permet de facilement utiliser Docker sous Mac OS X, une belle application, un peu « clickodrome » <code>;-P</code>. <a href="http://socketplane.io/">Socketplane</a> est une solution réseau qui connectait Open vSwitch avec Docker — nulle doute que la récente libnetwork vient de là. 532 </p> 533 </div> 534 </div> 535 536 <div id="outline-container-sec-12" class="outline-2"> 537 <h2 id="sec-12">Évènements</h2> 538 <div class="outline-text-2" id="text-12"> 539 <p> 540 Nous allons finir avec une liste non-exhaustive et un peu orientée des évènements <i>marquants</i> qui se sont passés ces derniers mois : 541 </p> 542 543 <ul class="org-ul"> 544 <li>Le <a href="https://blog.docker.com/2014/11/docker-tour-de-france/">Docker Tour de France</a>, avec notament un <a href="http://www.meetup.com/Docker-Paris/events/218767688/">hackathon</a> organisé à l'<a href="http://www.epitech.eu/paris/ecole-informatique-paris.aspx">Epitech</a>, où notre <a href="https://twitter.com/mariolet">Mario Loriedo</a> national a bootstrapé son projet Sublime-docker avec <a href="https://github.com/mjbright">Mike Bright</a> et à du coup gagné sa place à la DockerCon de 2015. 545 </li> 546 <li>Les <a href="http://blog.zenika.com/index.php?post/2015/02/19/NightClazz-Docker-Avance">Nightclazz</a> <a href="http://zenika.github.io/NC-Docker-Decouverte/">découverte</a> et <a href="http://zenika.github.io/NC-Docker-Avance/#/">avancée</a> hébergé par Zenika, présenté par Mario Loriedo et moi-même ;-). 547 </li> 548 <li>La DockerCon Europe. 549 </li> 550 <li>Le <a href="http://docker.party/">Docker Birthday</a>, gigantesque <i>Open-source-athon</i> tout autour du monde — une véritable réussite, tant au niveau de l'organisation (des évènements, la préparation en amont des <i>issues</i>, etc.) que de ce qu'il en est <a href="https://blog.docker.com/2015/05/dockers-2nd-birthday-by-the-numbers/">ressorti</a>. 551 </li> 552 </ul> 553 </div> 554 </div> 555 556 557 </article> 558 <hr /> 559 <div class="prev-next"> 560 561 <a class="paging-link prev" href="/posts/2015-07-31-config-managment-intro/" title="Gestion de configuration : introduction">← Previous post</a> 562 563 564 565 <a class="paging-link next" href="/posts/2015-05-09-migration-to-hugo/" title="Migration vers hugo">Next post →</a> 566 567 </div> 568 569 </div> 570 </div> 571 572 <footer> 573 <nav> 574 575 <a href="/">home</a> 576 <span class="text-muted"> | </span> 577 578 <a href="/about">about</a> 579 <span class="text-muted"> | </span> 580 581 <a href="/archive">archive</a> 582 <span class="text-muted"> | </span> 583 584 <a href="/categories">categories</a> 585 <span class="text-muted"> | </span> 586 587 <a href="/tags">tags</a> 588 <span class="text-muted"> | </span> 589 590 <a href="https://twitter.com/vdemeest">twitter</a> 591 <span class="text-muted"> | </span> 592 593 <a href="https://github.com/vdemeester">github</a> 594 <span class="text-muted"> | </span> 595 596 <a href="https://vincent.demeester.fr/index.xml">rss</a> 597 </nav> 598 <br/> 599 <address> 600 <span class="copyright"> 601 Content and design by Vincent Demeester 602 (<a rel="licence" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Some rights reserved</a>) 603 </span><br /> 604 <span class="engine"> 605 Powered by <a href="https://gohugo.io/">Hugo</a> and <a href="https://github.com/kaushalmodi/ox-hugo/">ox-hugo</a> 606 </span> 607 </address> 608 </footer> 609 </body> 610