<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Pierre Beitz]]></title><description><![CDATA[Notepad of a Software Engineer]]></description><link>https://PierreBtz.github.io</link><generator>RSS for Node</generator><lastBuildDate>Tue, 24 Jan 2017 22:50:27 GMT</lastBuildDate><atom:link href="https://PierreBtz.github.io/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Dealing with CORS in a Development Environment Using a Reverse Proxy]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre>No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://my-site' is therefore not allowed access</pre>
</div>
</div>
<div class="paragraph">
<p>As a front-end developer, there are chances you ended up with this message in your console quite a few time.
This article will present a solution I used on both professional and personal developments to be able to deal with the CORS.
It will also come back on other possible solutions I experimented but I was not satisfied with.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_is_cors_anyway">What is CORS anyway?</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_same_origin_policy">Same-origin policy</h3>
<div class="paragraph">
<p>The same-origin policy is a security implemented in browsers that makes sure that a web page A, served from a domain A cannot access ressources from another domain than domain A.
For more details, head to the <a href="https://www.w3.org/Security/wiki/Same_Origin_Policy">w3c wiki</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_cors">CORS</h3>
<div class="paragraph">
<p>CORS stands for Cross Origin Resource Sharing.
They allow a CORS compatible browser (any modern browser) to make cross domain requests to a compatible reverse proxy or a compatible application.</p>
</div>
<div class="paragraph">
<p><strong>This is not a security feature!</strong></p>
</div>
<div class="paragraph">
<p>In fact, it relaxes a security feature.
As you might have noticed, both ends of the channel need to be compatible: the browser on one side, the server (reverse proxy or application) on the other side.</p>
</div>
<div class="paragraph">
<p>With the CORS mechanism, the browser automatically adds control headers to the request.
Depending on the type of request it can also make a preflight request.
Request will be successful if the server&#8217;s answer contains a specific header allowing the domain.
For more information, you might want to read <a href="https://www.eriwen.com/javascript/how-to-cors/">Making Cross-Domain Requests with CORS</a>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_issue_for_the_front_end_developer">Issue for the front-end developer</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Now, here is the issue: I&#8217;m developing a web application that needs to query a RESTful API.
The issue is that this RESTful API is not allowing access to my domain (let&#8217;s say <a href="http://localhost:4200" class="bare">http://localhost:4200</a> ).</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_exploration_of_the_first_solutions">Exploration of the first solutions</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_on_the_application_side">On the Application Side</h3>
<div class="paragraph">
<p>CORS can be configured at the Application level (RESTful API in our case).</p>
</div>
<div class="paragraph">
<p>If I have a RESTful API developed with SpringBoot I can decide to set the CORS in SpringBoot using the @CrossOrigin annotation or using a Filter for instance.</p>
</div>
<div class="paragraph">
<p>This method has several issues:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Purely development stuff lands near production code.
There is a chance, somebody, sometimes, will activate the change in production.
As most of the time, the configuration will be to set a header accepting any domain ("Access-Control-Allow-Origin", "*"), I will let you decide if this is a risk worth taking.</p>
</li>
<li>
<p>It implies that you have access to the code of the API.
If it is a public API developped by a third party, then you cannot apply it.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_on_the_reverse_proxy_side">On the Reverse Proxy Side</h3>
<div class="paragraph">
<p>A common scenario is that your server is behind a reverse proxy like nginx or haproxy.
You can configure those reverse proxies for CORS.</p>
</div>
<div class="paragraph">
<p>Again, this method suffers from the same downsides as before.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_development_reverse_proxy">The development reverse proxy</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Now let&#8217;s study a purely client solution.
If we fire up a reverse proxy on our client, then we can hide both the web application and the RESTfull API behind it.
The browser will then always see one domain, therefore the same origin policy will be honored and there won&#8217;t be any CORS issue.</p>
</div>
<div class="paragraph">
<p>This solution has the following advantages:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>No modification of any code, the reverse proxy is a development tool like npm or maven.</p>
</li>
<li>
<p>No need to have access to any server, or to the code of the API running on the server.</p>
</li>
<li>
<p>In a lot of case, you are in fact in the same conditions as with your production environment which also has a reverse proxy.</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_hands_on">Hands On!</h3>
<div class="paragraph">
<p>The solution relies on docker.
It allows to run the reverse proxy as you would any tool.
Of course, you could also use it without docker, but there is more work to make everything work&#8230;&#8203;</p>
</div>
<div class="paragraph">
<p>You only need two files, a configuration file and a startup script.
We will use nginx, but you can do the same with any reverse proxy.</p>
</div>
<div class="sect3">
<h4 id="_linux">Linux</h4>
<div class="paragraph">
<p>First file you&#8217;ll need is the nginx configuration file, nginx.conf:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>events {
    worker_connections 1024;
}

http {
    server {
        listen 80;

        location /api {
            proxy_pass http://127.0.0.1:8080/api/;
        }

        location / {
            proxy_pass http://127.0.0.1:3000/;

            # The following is for the websocket connection of the webpack dev server (https://gist.github.com/simongfxu/ea128160c296f31e41e6)
            proxy_redirect off;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Of course you&#8217;ll need to adapt, this example is the one I use to develop a web application with a spring boot back end.
The web application is served on port 3000.
The back end is served on port 8080.
What I&#8217;m telling nginx is that if the path of the request starts with /api, then the request should be sent to the backend, otherwise, it should be sent to the front end.
The last section of the configuration is a special configuration I had to add so that the webpack dev server works correctly.</p>
</div>
<div class="paragraph">
<p>Now, let&#8217;s add a startup script:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">#!/usr/bin/env bash

docker run --rm --name dev-nginx -p 80:80 --net="host" -v $PWD/nginx.conf:/etc/nginx/nginx.conf:ro  nginx:1.11.8-alpine</code></pre>
</div>
</div>
<div class="paragraph">
<p>With this script, we are telling docker to boot up a container based on a minimal official nginx image.
The configuration that nginx should use is bind mounted using the previous configuration file.
The container is running on the same network stack as the host.
Finally we are exposing the port 80.</p>
</div>
<div class="paragraph">
<p>Once everything is set up, you can start up your front end server, your backend server and finally your reverse proxy.
Then direct your browser to localhost and you should be able to work.</p>
</div>
</div>
<div class="sect3">
<h4 id="_osx">OsX</h4>
<div class="paragraph">
<p>Sadly, the previous configuration won&#8217;t work on OsX because basically the host mode of Docker is <a href="https://forums.docker.com/t/should-docker-run-net-host-work/14215/27">not working like on Linux</a>.</p>
</div>
<div class="paragraph">
<p>The fastest solution I found was to use my hostname instead of localhost in the nginx.conf:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>events {
    worker_connections 1024;
}

http {
    server {
        listen 80;

        location /api {
            proxy_pass http://hostname:8080/api/;
        }

        location / {
            proxy_pass http://hostname:3000/;

            # The following is for the websocket connection of the webpack dev server (https://gist.github.com/simongfxu/ea128160c296f31e41e6)
            proxy_redirect off;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>And the startup script is not using the host network mode:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">#!/usr/bin/env bash

docker run --rm --name dev-nginx -p 80:80 -v $PWD/nginx.conf:/etc/nginx/nginx.conf:ro  nginx:1.11.8-alpine</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note that this method also implies that your server can bind to your IP (as you are using not using localhost anymore).</p>
</div>
</div>
<div class="sect3">
<h4 id="_what_about_windows">What about Windows?</h4>
<div class="paragraph">
<p>I did not have the opportunity to test either solution on Windows.
I&#8217;ll gladly edit this article with any feedback on Windows.</p>
</div>
<div class="paragraph">
<p>That&#8217;s it for this post.
I hope it will help you configure your development environment.
If you use any other method that you would like to share or discuss, please leave a comment!</p>
</div>
</div>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2017/01/24/Dealing-with-CORS-in-a-Development-Environment-Using-a-Reverse-Proxy.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2017/01/24/Dealing-with-CORS-in-a-Development-Environment-Using-a-Reverse-Proxy.html</guid><category><![CDATA[CORS]]></category><category><![CDATA[ nginx]]></category><category><![CDATA[ front-end development]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Tue, 24 Jan 2017 00:00:00 GMT</pubDate></item><item><title><![CDATA[Writing a Jenkinsfile: Tips and Tricks]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Jenkins Pipeline are great.
They allow the developer to keep their pipeline and their build near their code.</p>
</div>
<div class="paragraph">
<p>But developing a Pipeline can quickly become cumbersome:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The text area of the Jenkins UI is not really convenient when you start writing pipelines a bit more complex than your basic hello world.</p>
</li>
<li>
<p>Your code can grow and become hard to maintain (yeah, we end up having the same problems in our builds than in the rest of our code!).</p>
</li>
<li>
<p>Validation is not easy&#8230;&#8203;</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This post will sum up some technics I found useful to help me be efficient with pipelines.
Of course the list is not exhaustive.
I am more than interested by any new technic you would like to share in the comments.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_writing_the_jenkinsfile">Writing the Jenkinsfile</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This part will be focused on the tools you can use to assist you.
As already mentionned, the Jenkins UI is not really the best place to comfortably write a huge script.
Nevertheless, never forget its existence.
When I have doubts, I often create a quick dummy job to validate/invalidate some part of a script.</p>
</div>
<div class="sect2">
<h3 id="_the_ide">The IDE</h3>
<div class="paragraph">
<p>Well, I won&#8217;t go into an IDE war, this is far from being the point of the article.
Your favorite java IDE (with potentially a groovy support plugin) will do the trick.</p>
</div>
<div class="paragraph">
<p>That being said, you can do more than having a simple groovy IDE, you can also teach your IDE to understand the DSL used by Jenkins!
This works thanks to the GroovyDSL (GDSL).
A GDSL is basically a file that will describe the meta language that a DSL uses.
By putting this file in the classpath of a groovy project, your IDE will be able to assist you write code that follows the DSL.
The good news is that the Jenkins pipeline exposes a GDSL.
Go to <em>$jenkins_base_url/pipeline_syntax/gdsl</em> to get it.
Then add this file in the classpath of your project and voilà, your IDE can now autocomplete!</p>
</div>
<div class="paragraph">
<p>Note that I only tested this feature in Intellij, but this should work in other groovy compatible IDEs.
Also, plugins might enrich the DSL without being described in the GDSL, so don&#8217;t expect the autocomplete to be absolutely complete.</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_linter">The linter</h3>
<div class="paragraph">
<p>During development of a pipeline, it&#8217;s easy to make a typo in your Jenkinsfile and end up with an invalide Jenkinsfile.
This is a pain, as you lose time understanding the mistake, fix it, commit the fix and push it.
The good news is that there is a way to shorten this feedback loop.</p>
</div>
<div class="paragraph">
<p>Jenkins offers a linter for your Jenkinsfile.
Best way to use the linter is with the JenkinsCLI.
You will need the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Model+Definition+Plugin">Pipeline Model Definition</a> version 0.7 at least.
You can then ask Jenkins to lint your Jenkinsfile:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">curl -O $jenkins_base_url/jnlpJars/jenkins-cli.jar
java -jar jenkins-cli.jar -s $jenkins_base_url login --username $username --password $password
java -jar jenkins-cli.jar -s $jenkins_base_url declarative-linter&lt;Jenkinsfile</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_pipeline_syntax">The Pipeline Syntax</h3>
<div class="paragraph">
<p>Even with the IDE assisting you, it is sometimes hard to correctly write what you wish to do:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Plugins are not exposed in the GDSL</p>
</li>
<li>
<p>The syntax might be cumbersome</p>
</li>
<li>
<p>Some parameters might seem obscure</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>There is a Pipeline Syntax page in Jenkins that is here to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Help you understand the different options you have</p>
</li>
<li>
<p>Generate some code snippets for you</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You can reach this page by browsing <em>$jenkins_base_url/pipeline_syntax</em>.
I won&#8217;t go into details, the usage of the page is pretty self explanatory, you choose the step you wish to generate, then you fill in the options as you would in a good old freestyle job (click on the quotemarks if you need assistance), and finally you click the <code>Generate Pipeline Script</code> button to get your snippet.
On the following screenshot, I generated a junit step.
Note the presence of the help button (right red arrow) that allowed me to quickly understand the 'Retain long standard output/error' (which finally translated into <em>keepLingStdio</em> in the code):</p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://github.com/PierreBtz/pierrebtz.github.io/raw/master/images/writing-Jenkinsfile.png" alt="Pipeline Syntax Page">
</div>
</div>
<div class="paragraph">
<p>The final part of the Pipeline Syntax page (circled in red in the screenshot) is a reference to the many variables you have access to in the pipeline.</p>
</div>
<div class="paragraph">
<p>Like for the GDSL, plugin sadly do not always support that page.</p>
</div>
</div>
<div class="sect2">
<h3 id="_best_practices">Best Practices</h3>
<div class="paragraph">
<p>CloudBees proposed a good article with <a href="https://www.cloudbees.com/blog/top-10-best-practices-jenkins-pipeline-plugin">ten best practices for Jenkins Pipeline Plugin</a>.
This should help you getting started on the right track.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_organizing_your_pipeline">Organizing your pipeline</h2>
<div class="sectionbody">
<div class="paragraph">
<p>With pipelines, you can (and you should!) now treat your build as you treat your code!
This means for instance that you should keep your pipeline <a href="https://en.wikipedia.org/wiki/Don&#8217;t_repeat_yourself">DRY</a>.</p>
</div>
<div class="paragraph">
<p><strong>Clean coding can now be applied to your pipelines!</strong></p>
</div>
<div class="sect2">
<h3 id="_extracting_functions">Extracting functions</h3>
<div class="paragraph">
<p>As for the rest of the code, try to extract methods and reuse your code.
Do not just copy and paste.
Of course functions will not help you from a pipeline to another, but it is a good starting point.
If you have the same function duplicated in multiple pipelines, it will help you to go to the next step.
If the logic is duplicated throughout your pipelines, the next step will take more time (and will be more error prone).</p>
</div>
</div>
<div class="sect2">
<h3 id="_pipeline_shared_libraries">Pipeline Shared Libraries</h3>
<div class="paragraph">
<p>The <a href="https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Shared+Groovy+Libraries+Plugin">Pipeline Shared Libraries Plugin</a> allows a developer to share and reuse code between pipelines.
I won&#8217;t go into details on how to use it here, it is worth a complete article, but here is a simple example that I encountered that will convince you of its power.
All our builds had to notify an SCM.
The only way to correctly catch all the cases (build success, build error), was by building inside a try/catch block.
So we ended up starting all our builds duplicating the following logic:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">#!/usr/bin/groovy

try {
    notify(start)
    //put here all your pipeline
    notify(success)
} catch(exception) {
    notify(failure)
}

def notify(state) {
    //notify scm
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>As you can see, there is a lot of duplicated code between the builds, with a huge maintenance problem.</p>
</div>
<div class="paragraph">
<p>What is happening is the SCM API is changing for instance?
I would need to iterate over all the pipelines to update the code of the notify function.</p>
</div>
<div class="paragraph">
<p>Same issue if we can finally get rid of this try/catch structure (side note, that might soon be the case as we will see in the last part of this article).
I would have to manually change all the Jenkinsfiles.</p>
</div>
<div class="paragraph">
<p>With the Shared Libraries Plugin, I was able to enrich the DSL with our particular use case.
In the library I wrote:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">//vars/notifiableBuild.groovy
#!/usr/bin/groovy

def call(body) {
    try {
        notify(start)
        body()
    	notify(success)
    } catch(exception) {
        notify(failure)
    }
}

def notify(state) {
    //notify scm
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>And finally, in a Jenkinsfile, I end up writing:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">#!/usr/bin/groovy

notifiableBuild {
    //put here all your pipeline
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To learn more about this, you should visit the <a href="https://jenkins.io/doc/book/pipeline/shared-libraries/">official documentation of the plugin</a>, which will help you getting started.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_experimental_corner">The Experimental Corner</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In this part, I will share two projects you should keep an eye on as they should help a bit more spreading the usage of the pipelines.
Note that the two projects are at a very early stage of development.</p>
</div>
<div class="sect2">
<h3 id="_jenkins_blue_ocean_pipeline_editor_plugin">Jenkins Blue Ocean Pipeline Editor Plugin</h3>
<div class="paragraph">
<p><a href="https://github.com/jenkinsci/blueocean-pipeline-editor-plugin">This plugin</a> is using the <a href="https://jenkins.io/projects/blueocean/">Blue Ocean interface</a>.
Side note, if you don&#8217;t know Blue Ocean, you should definitively have a look as it is far superior to the Jenkins 'classic' interface to display pipelines.
Note that it is also work in progress (it is in a beta stage), but I use it in production everyday without any issue (except for the missing features of course).</p>
</div>
<div class="paragraph">
<p>That being said, this new Pipeline Editor plugin proposes a new interface to graphically design your pipeline.
This is at very alpha stage, but it was <a href="https://youtu.be/TsWkZLLU-s4?t=22m35s">demonstrated in the last Jenkins Meetup</a> (note that the rest of the meetup is also worth your time if you are into Jenkins) and seems very promising.</p>
</div>
</div>
<div class="sect2">
<h3 id="_declarative_pipeline">Declarative Pipeline</h3>
<div class="paragraph">
<p>The official name of this plugin is the <a href="https://github.com/jenkinsci/pipeline-model-definition-plugin">Pipeline Model Definition Plugin</a>.
Its aim is to give a more config like touch to the pipeline.
Code vs Config, here we are again (let&#8217;s not replay maven vs gradle and gulp vs grunt all over again&#8230;&#8203;)!
Again, the plugin was <a href="https://youtu.be/TsWkZLLU-s4?t=27m35s">demoed in the last Jenkins Online Meetup</a>.
The API does not seem stable for the moment, but some stuff seems very interesting.
For instance, the notification issue I raised before could be adressed with something like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">#!/usr/bin/groovy

post {
    always {
    }

    success {
    	notify(success)
    }

    failure {
    	notify(failure)
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>That&#8217;s it for this post, do not hesitate to post any comment you might have!</p>
</div>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2017/01/17/Writing-a-Jenkinsfile-Tips-and-Tricks.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2017/01/17/Writing-a-Jenkinsfile-Tips-and-Tricks.html</guid><category><![CDATA[Jenkins]]></category><category><![CDATA[ Jenkinsfile]]></category><category><![CDATA[ Pipelines]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Tue, 17 Jan 2017 00:00:00 GMT</pubDate></item><item><title><![CDATA[Docker starter kit part 3: Commits, Volumes and Data Management]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In the <a href="https://pierrebtz.github.io/2016/11/27/Docker-starter-kit-part-1-Getting-Started-Containers-lifecycle.html">first</a> part of this blog series on Docker, you had the opportunity to follow a Docker basic tutorial and to have an overview of the lifecycle of a Docker container.
In the <a href="https://pierrebtz.github.io/2016/12/04/Docker-starter-kit-part-2-Anatomy-of-an-Image-and-a-Container.html">second part</a> you then studied what a Docker Image and a Docker Container are made up.
This third post of the series will dive into how you can manage your data with Docker.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_unionfs_and_persistence">UnionFS and persistence</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_thin_layer_lifecycle">Thin Layer Lifecycle</h3>
<div class="paragraph">
<p>If you remember correctly what we saw until now, an image is the union of multiple read only layers.
When we create a container, Dockers adds a new read/write thin layer.
Everything that happens during the life of the container occurs in this thin layer.
When the container is destroyed this thin layer is destroyed with it and everything that happened during the life of the container is lost.</p>
</div>
</div>
<div class="sect2">
<h3 id="_docker_commit">Docker commit</h3>
<div class="paragraph">
<p>Let me reassure you right now, there is a way to avoid losing everything: the <code>docker commit</code> command.
Basically what this command does is commit the thin layer into a new image, a bit like you would commit your staged files into your git repository.
To illustrate this, we will use the debian:jessie image we already encountered.
We will run it starting an interactive bash session:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run --name demo -it debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>We will get back to this on a later post, for the moment, let&#8217;s just say to simplify that we told Docker to attach to the container so that we can run commands interactively.
You should now have something like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>root@&lt;container_id&gt;:/#</code></pre>
</div>
</div>
<div class="paragraph">
<p>You are ready to interact directly with your container.
Let&#8217;s create a simple dummy file to simulate data:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">cd home
touch hello</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now, if you remember correctly, we just have created a file named hello in the thin layer of the container.
First let&#8217;s check that if we stop and restart the container the file is still here.
We will exit the container:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">exit</code></pre>
</div>
</div>
<div class="paragraph">
<p>The container should be stopped (you can check with <code>docker ps -a</code>), let&#8217;s restart it and attach to it:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker start demo
docker exec -it demo bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>We are now attached to the container, let&#8217;s make sure our file is still here:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ls /home/</code></pre>
</div>
</div>
<div class="paragraph">
<p>You should see hello.
Now, let&#8217;s exit the container again (<code>exit</code>) and instanciate a new container from debian:jessie:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run --name demo2 -it debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>Let&#8217;s check the content of the home directory of this container:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ls /home/</code></pre>
</div>
</div>
<div class="paragraph">
<p>It&#8217;s empty as we expected.
This means that if we destroy the demo container, we lose our hello file.
Fortunately, the <code>docker commit</code> command is here to help us.
Let&#8217;s exit the demo2 container again.
Then we can commit our demo container:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker commit demo dummyimage</code></pre>
</div>
</div>
<div class="paragraph">
<p>That&#8217;s it, we have an image ready and we can create new containers which will contain our hello file.
I&#8217;ll let you make sure any container created from our dummyimage is containing the hello file.
You can also check the history of the image to see that we indeed commited a new layer:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
387fe6f1f01a        15 hours ago        bash                                            71 B
73e72bf822ca        4 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0 B
&lt;missing&gt;           4 weeks ago         /bin/sh -c #(nop) ADD file:41ea5187c501168...   123 MB</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre>The `docker commit` command is useful to commit changes you do not want to lose.
You can also leverage on it to move a custom container to another machine for instance.
However, for maintenability purpose, it is recommended to build your images using a Dockerfile as you saw in the first part of this series.
We will get back to the building of image using Dockerfile in more details later on.</pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_volumes">Volumes</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At this point, you might starting to understand that the UnionFS is not really what we wish for data persistence.
With it, data are bound to a container lifecycle.
You can quickly lose some data by just destroying a container.
Data sharing between containers is not easily possible either.
Now, volume comes into play.</p>
</div>
<div class="sect2">
<h3 id="_definition">Definition</h3>
<div class="paragraph">
<p>A volume is a directory that bypasses the UnionFS.
A volume decouples the data from the lifecycle of the container.</p>
</div>
<div class="paragraph">
<p>Among its properties are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A volume is initialized at container creation.</p>
</li>
<li>
<p>A volume can be shared and reused between different containers.</p>
</li>
<li>
<p>If you delete a container, its volumes are not deleted (except if you explicitely ask for their deletion).</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_data_volumes">Data Volumes</h3>
<div class="listingblock">
<div class="content">
<pre>The following assumes you are using at least Docker v1.9.
This is the first release that is using named volumes.</pre>
</div>
</div>
<div class="sect3">
<h4 id="_named_volumes">Named volumes</h4>
<div class="sect4">
<h5 id="_creation_and_mounting">Creation and Mounting</h5>
<div class="paragraph">
<p>There are two ways of creating named volumes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>either explicitely with the <code>docker volume create</code> command.</p>
</li>
<li>
<p>or implicitely when creating a new container.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>So both of the following will do the same:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">## explicit method
docker volume create --name my_data
docker run -it --name my_container -v my_data:/data debian:jessie bash
## implicit method
docker run -it --name my_container -v my_data:/data debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>I&#8217;ll let you run one of the two methods (not both!).
You should be attached to a container.
We will then write a file in our volume:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">touch /data/hello
exit</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then, let&#8217;s mount a new container.
This time, the my_data volume will be mounted under /home:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name my_container2 -v my_data:/home debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the container, let&#8217;s check that our volume is correctly mounted under /home:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ls /home
exit</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_mounting_volumes_used_by_other_containers">Mounting volumes used by other containers</h5>
<div class="paragraph">
<p>You can mount volumes used by other containers.
For instance, the following would mount a /data and a /home directory, both pointing to the my_data volume:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name my_container3 --volumes-from my_container --volumes-from my_container2 debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the container, we can check that both /data and /home contain the hello file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ls /home
ls /data
exit</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre>Be careful with what you do when manipulating volumes.
It may be not a good idea to mount the same volume on two different mount points in the same container!
Same thing with concurrent modifications: if you are mounting the same volume in different containers, you most probably want to check for concurrent modifications.</pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_mounting_on_an_existing_directory">Mounting on an existing directory</h5>
<div class="paragraph">
<p>If a container contains data in the mount point, then this data is copied onto the new volume.</p>
</div>
</div>
<div class="sect4">
<h5 id="_find_the_volume_s_used_by_a_container">Find the volume(s) used by a container</h5>
<div class="paragraph">
<p>You can use the <code>docker inspect --format "{{ .Mounts }}"</code> command to locate the volume(s) used by a container.
Running this command on my_container3, we have:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>[{ my_data /var/lib/docker/volumes/my_data/_data /data local  true }
 { my_data /var/lib/docker/volumes/my_data/_data /home local  true }
]</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_removing_a_volume">Removing a volume</h5>
<div class="paragraph">
<p>One thing important to know, is that removing a container won&#8217;t remove its volumes (even if they are not used by any other container).
This is coherent with the fact that volumes should lived independently from containers.
So if we do the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker rm my_container
docker rm my_container2
docker rm my_container3
docker volume ls | grep my_data</code></pre>
</div>
</div>
<div class="paragraph">
<p>We see that we still have a our my_data volume.
To finally delete the volume:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker volume rm my_data</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_read_only_mode">Read only mode</h5>
<div class="paragraph">
<p>Until now, we did not specify any option when mounting our volume.
It was mounted as read/write.
We could chose to mount it in read only mode:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name my_container -v my_data:/data:ro debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the container:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">root@325a440f674b:/# touch /data/hello
touch: cannot touch '/data/hello': Read-only file system</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_host_directory_mounting">Host directory mounting</h4>
<div class="sect4">
<h5 id="_declare_the_mount">Declare the mount</h5>
<div class="paragraph">
<p>Docker also allows to mount a directory of your host.
You can mount a host directory by specifying a <strong>full path</strong>.
Create a file called hello, containing the string hello world on your host.
Then issue:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name host_mounting -v $PWD:/data debian:jessie bash</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre>Carreful, if you are running under Windows or OsX, you need to let Docker access the shared directory by going into Docker options under the Shared Drives category.
Under Windows, you'll also need to specify the path manually as the PWD variable does not exist.</pre>
</div>
</div>
<div class="paragraph">
<p>Once you booted up your container, we will install vim and then open the hello file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">apt-get update &amp;&amp; apt-get install vim -y
cd /data
vim hello</code></pre>
</div>
</div>
<div class="paragraph">
<p>You should see the hello world string.
Now edit the file and add something to it.
For those not familiar with vim, you want to type <code>i</code> to enter edition mode, then type something, then type <code>esc</code> and finally <code>:wq</code>.
Now, open the file on a text editor in your host to see the changes.</p>
</div>
</div>
<div class="sect4">
<h5 id="_read_only_mode_2">Read only mode</h5>
<div class="paragraph">
<p>It works like volumes, check the previous section.</p>
</div>
</div>
<div class="sect4">
<h5 id="_find_the_volume_s_used_by_a_container_2">Find the volume(s) used by a container</h5>
<div class="paragraph">
<p>It works like volumes, check the previous section.</p>
</div>
</div>
<div class="sect4">
<h5 id="_mounting_on_an_existing_directory_2">Mounting on an existing directory</h5>
<div class="paragraph">
<p>If a container contains data in the mount point, then this data is kept, and the host directory content overlays the data in the container mount point.</p>
</div>
</div>
<div class="sect4">
<h5 id="_mounting_a_file">Mounting a file</h5>
<div class="paragraph">
<p>Host mount also works with files. The previous example could have been writen:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name host_mounting -v $PWD/hello:/data/hello debian:jessie bash</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_about_data_containers">About Data Containers</h4>
<div class="paragraph">
<p>I made a choice to present you only named volumes and to avoid data containers.
Here is why: when you use data container, Docker really creates unnamed containers behind your back.
If you destroy your data container, the volume it was using become dangling and its management is not easy.</p>
</div>
<div class="paragraph">
<p>Here is an example that will show you a bit the usage of data containers and the limitation I was mentionning.</p>
</div>
<div class="paragraph">
<p>We will first run interactively a container with an unnamed volume:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name unnamed_volume -v /data debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>And in the container:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">touch /data/hello
exit</code></pre>
</div>
</div>
<div class="paragraph">
<p>Let&#8217;s inspect the container for mounted volumes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker inspect --format "{{ .Mounts }}" unnamed_volume</code></pre>
</div>
</div>
<div class="paragraph">
<p>You end up with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>[{volume 6faa8a43f80bb550b11aa8f3f6093273782fbf5a5674fd5067ba7c7c27942314 /var/lib/docker/volumes/6faa8a43f80bb550b11aa8f3f6093273782fbf5a5674fd5067ba7c7c27942314/_data /data local  true }]</code></pre>
</div>
</div>
<div class="paragraph">
<p>So you really have a volume stored.
Now, usually, what you would do to be able to reuse this data container from another container is to use the <code>--volumes-from</code> command:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run -it --name unnamed_volume2 --volumes-from unnamed_volume debian:jessie bash</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here unnamed_volume2 shares the same unnamed volume with unnamed_volume.
This volume is mounted under /data.
Let&#8217;s check this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ls /data
exit</code></pre>
</div>
</div>
<div class="paragraph">
<p>You should see our dummy hello file.
So far so good.
Why do I prefer avoiding this technic might you ask.
Well, let&#8217;s now delete both our containers after having taken care of remembering our unnamed volume id:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker inspect --format "{{ .Mounts }}" unnamed_volume
docker rm unnamed_volume
docker rm unnamed_volume2</code></pre>
</div>
</div>
<div class="paragraph">
<p>And let&#8217;s look for our unnamed volume:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker volume ls | grep &lt;volume_id&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It&#8217;s still there!
Of course, you could still mount it by its id, to check what it contains, but it&#8217;s far from perfect from a maintenance point of view.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>You could also have issued a `docker rm -v` command to delete any anonymous volumes.</pre>
</div>
</div>
<div class="paragraph">
<p>I&#8217;m not saying that unnamed volumes and data containers might not be useful sometimes, but most of the case you will probably end up using named containers.
So, unless you are stuck with a pre 1.9 version of Docker, I strongly suggest you consider named volumes first.
In any case, you might want to review the <a href="https://docs.docker.com/engine/tutorials/dockervolumes/">Manage data in containers</a> page of the official documentation.</p>
</div>
<div class="paragraph">
<p>That&#8217;s it for this post, next one will show you how you could back up and restore your data, how you can extend volumes with drivers, and  a practical usage of the mounting of a host directory.</p>
</div>
</div>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2016/12/11/Docker-starter-kit-part-3-Commits-Volumes-and-Data-Management.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2016/12/11/Docker-starter-kit-part-3-Commits-Volumes-and-Data-Management.html</guid><category><![CDATA[HowTo]]></category><category><![CDATA[ Docker]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Sun, 11 Dec 2016 00:00:00 GMT</pubDate></item><item><title><![CDATA[Docker starter kit part 2: Anatomy of an Image and a Container]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In the <a href="https://pierrebtz.github.io/2016/11/27/Docker-starter-kit-part-1-Getting-Started-Containers-lifecycle.html">first part</a> of this blog series on Docker, you had the opportunity to follow a Docker basic tutorial and to have an overview of the lifecycle of a Docker container.
In this post, we will have an overview on what a Docker Image and a Docker Container are made up.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_anatomy_of_an_image">Anatomy of an image</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_union_file_systems">Union File Systems</h3>
<div class="quoteblock">
<blockquote>
Union file systems, or UnionFS, are file systems that operate by creating layers, making them very lightweight and fast. Docker uses union file systems to provide the building blocks for containers.
</blockquote>
<div class="attribution">
&#8212; Docker Official Documentation
</div>
</div>
<div class="paragraph">
<p>We will see next how those UnionFS are exploited in Docker.</p>
</div>
</div>
<div class="sect2">
<h3 id="_unionfs_and_docker_images">UnionFS and Docker Images</h3>
<div class="paragraph">
<p>A Docker image is made of several layers.
Each layer represents a filesystem difference with the layer below it.
The stack of all the layers makes up the filesystem of an image.
Each layer is <strong>read only</strong> and shared between the images (and the containers, as we will see in the next part).</p>
</div>
<div class="paragraph">
<p>As you may have guessed, all of this means that you need to be very careful when building new images to maximize the reuse of layers.
We will discuss this in details in another blog post of this series.</p>
</div>
</div>
<div class="sect2">
<h3 id="_hands_on">Hands On!</h3>
<div class="paragraph">
<p>Let us take a concrete example.
Let us pull a maven image:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker pull maven:3.3.9-jdk-8</code></pre>
</div>
</div>
<div class="paragraph">
<p>Do not bother with the output of the command yet, we will get back to it later.
Instead, let&#8217;s now use the history commands that displays all the layers of an image:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker history maven:3.3.9-jdk-8</code></pre>
</div>
</div>
<div class="paragraph">
<p>And the output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight nowrap"><code>IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
b2ef69e414f5        3 weeks ago         /bin/sh -c #(nop)  CMD ["mvn"]                  0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/usr/local/bi   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  VOLUME [/root/.m2]           0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop) COPY file:b3fc14e8337e0079a   327 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop) COPY file:e3aa30a24fcf60a59   1.042 kB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV MAVEN_CONFIG=/root/.m2   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV MAVEN_HOME=/usr/share/   0 B
&lt;missing&gt;           3 weeks ago         |2 MAVEN_VERSION=3.3.9 USER_HOME_DIR=/root /b   10.03 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ARG USER_HOME_DIR=/root      0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ARG MAVEN_VERSION=3.3.9      0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c /var/lib/dpkg/info/ca-certificates   418.2 kB
&lt;missing&gt;           3 weeks ago         /bin/sh -c set -x  &amp;&amp; apt-get update  &amp;&amp; apt-   351.5 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV CA_CERTIFICATES_JAVA_V   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_DEBIAN_VERSION=8u   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=8u111       0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/lib/jvm   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c {   echo '#!/bin/sh';   echo 'set    87 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c echo 'deb http://deb.debian.org/de   55 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c apt-get update &amp;&amp; apt-get install    1.287 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c apt-get update &amp;&amp; apt-get install    122.6 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c apt-get update &amp;&amp; apt-get install    44.3 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop) ADD file:41ea5187c50116884c   123 MB</code></pre>
</div>
</div>
<div class="paragraph">
<p>That is a lot of layers right?
Well, let&#8217;s try to sort this out.
First let&#8217;s review the Dockerfile used to build this image, <a href="https://github.com/carlossg/docker-maven/blob/33eeccbb0ce15440f5ccebcd87040c6be2bf9e91/jdk-8/Dockerfile">available on github</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Dockerfile" data-lang="Dockerfile">FROM openjdk:8-jdk

ARG MAVEN_VERSION=3.3.9
ARG USER_HOME_DIR="/root"

RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
  &amp;&amp; curl -fsSL http://apache.osuosl.org/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz \
    | tar -xzC /usr/share/maven --strip-components=1 \
  &amp;&amp; ln -s /usr/share/maven/bin/mvn /usr/bin/mvn

ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"

COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh
COPY settings-docker.xml /usr/share/maven/ref/

VOLUME "$USER_HOME_DIR/.m2"

ENTRYPOINT ["/usr/local/bin/mvn-entrypoint.sh"]
CMD ["mvn"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>When you build an image, you obviously build the layers from bottom to top.
So if we read the Dockerfile from bottom to top, we should find the list of commands that the history command output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
b2ef69e414f5        3 weeks ago         /bin/sh -c #(nop)  CMD ["mvn"]                  0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/usr/local/bi   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  VOLUME [/root/.m2]           0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop) COPY file:b3fc14e8337e0079a   327 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop) COPY file:e3aa30a24fcf60a59   1.042 kB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV MAVEN_CONFIG=/root/.m2   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV MAVEN_HOME=/usr/share/   0 B
&lt;missing&gt;           3 weeks ago         |2 MAVEN_VERSION=3.3.9 USER_HOME_DIR=/root /b   10.03 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ARG USER_HOME_DIR=/root      0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ARG MAVEN_VERSION=3.3.9      0 B</code></pre>
</div>
</div>
<div class="paragraph">
<p>It seems correct, up until the <code>FROM openjdk:8-jdk</code> command of the Dockerfile.
So, what about those remaining layers?</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
&lt;missing&gt;           3 weeks ago         /bin/sh -c /var/lib/dpkg/info/ca-certificates   418.2 kB
&lt;missing&gt;           3 weeks ago         /bin/sh -c set -x  &amp;&amp; apt-get update  &amp;&amp; apt-   351.5 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV CA_CERTIFICATES_JAVA_V   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_DEBIAN_VERSION=8u   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=8u111       0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/lib/jvm   0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c {   echo '#!/bin/sh';   echo 'set    87 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c echo 'deb http://deb.debian.org/de   55 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c apt-get update &amp;&amp; apt-get install    1.287 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c apt-get update &amp;&amp; apt-get install    122.6 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c apt-get update &amp;&amp; apt-get install    44.3 MB
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0 B
&lt;missing&gt;           3 weeks ago         /bin/sh -c #(nop) ADD file:41ea5187c50116884c   123 MB</code></pre>
</div>
</div>
<div class="paragraph">
<p>Let&#8217;s have a look at the content of the <a href="https://github.com/docker-library/openjdk/blob/e6e9cf8b21516ba764189916d35be57486203c95/8-jdk/Dockerfile">Dockerfile used to build openjdk:8-jdk</a> (I removed the comments to the readibility):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Dockerfile" data-lang="Dockerfile">FROM buildpack-deps:jessie-scm
RUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends \
		bzip2 \
		unzip \
		xz-utils \
	&amp;&amp; rm -rf /var/lib/apt/lists/*

RUN echo 'deb http://deb.debian.org/debian jessie-backports main' &gt; /etc/apt/sources.list.d/jessie-backports.list
ENV LANG C.UTF-8
RUN { \
		echo '#!/bin/sh'; \
		echo 'set -e'; \
		echo; \
		echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
	} &gt; /usr/local/bin/docker-java-home \
	&amp;&amp; chmod +x /usr/local/bin/docker-java-home

ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64

ENV JAVA_VERSION 8u111
ENV JAVA_DEBIAN_VERSION 8u111-b14-2~bpo8+1
ENV CA_CERTIFICATES_JAVA_VERSION 20140324

RUN set -x \
	&amp;&amp; apt-get update \
	&amp;&amp; apt-get install -y \
		openjdk-8-jdk="$JAVA_DEBIAN_VERSION" \
		ca-certificates-java="$CA_CERTIFICATES_JAVA_VERSION" \
	&amp;&amp; rm -rf /var/lib/apt/lists/* \
	&amp;&amp; [ "$JAVA_HOME" = "$(docker-java-home)" ]

RUN /var/lib/dpkg/info/ca-certificates-java.postinst configure</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you read the Dockerfile from bottom to top, you&#8217;ll match every command with a layer.
We could continue again with the <a href="https://github.com/docker-library/buildpack-deps/blob/1845b3f918f69b4c97912b0d4d68a5658458e84f/jessie/scm/Dockerfile">Dockerfile of the next base image (buildpack-deps:jessie-scm)</a> but I guess you get the point.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>A Docker image is a stack of layers.
When you build on top of a base image, you are stacking layers on top of all the layers of the base image.</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_base_image">Base image</h3>
<div class="paragraph">
<p>At this point, you might wonder which is the mother of all images.
If you continue working up the base images from the previous example, you will get to the <a href="https://github.com/tianon/docker-brew-debian/blob/2c836bc53feb12f70a07dacaa6256d4d66624f38/jessie/Dockerfile">debian:jessie</a> image:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Dockerfile" data-lang="Dockerfile">FROM scratch
ADD rootfs.tar.xz /
CMD ["/bin/bash"]</code></pre>
</div>
</div>
<div class="paragraph">
<p>The scratch image is a <strong>reserved minimal image</strong> used to signal Docker that you are building an image from scratch.
The next command of your Dockerfile will be the first layer of your container.
The scratch image has a special status in Docker.
You cannot pull it, push it or tag an image with the name.
Building a base image is clearly out of the scope of this blog post.
If you wish to build a base image, you can have a look to <a href="https://docs.docker.com/engine/userguide/eng-image/baseimages/">the official documentation on base images</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_back_to_the_output_of_code_docker_pull_code">Back to the output of <code>docker pull</code></h3>
<div class="paragraph">
<p>Remember, I told you that we would get back to the output of <code>docker pull maven:3.3.9-jdk-8</code>.
My output looked like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>3.3.9-jdk-8: Pulling from library/maven

386a066cd84a: Already exists
75ea84187083: Already exists
88b459c9f665: Already exists
690dbea0e4ca: Already exists
7e401cdd6f18: Already exists
a58186ddf9a0: Already exists
49999ed55bc4: Already exists
eb40561aad8f: Already exists
4ce0e24588f2: Pull complete
35430242cb99: Pull complete
d4af041dcf95: Pull complete
Digest: sha256:a7fd540bc273b7c4f1193fbcd46127ad3912fd095a251382f4e4312b9ac85e9d
Status: Downloaded newer image for maven:3.3.9-jdk-8</code></pre>
</div>
</div>
<div class="paragraph">
<p>The output of the <code>docker pull</code> shows you all the <strong>image layers</strong> that the maven:3.3.9-jdk-8 image is made of.
If you compare this output to the output of the <code>docker history</code> command, you will notice that we have 11 image layers here for 24 layers in the image.
This is due to the fact that during the build process, docker is able to make optimizations by squashing layers into an image.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>The previous statement is right since Docker 1.10.
Before version 1.10, there was a 1:1 ratio between image layers and layers.</pre>
</div>
</div>
<div class="paragraph">
<p>Of course this does not change anything regarding what you download.
You won&#8217;t have to download any image if you wish to download the openjdk:8-jdk image for instance:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>openjdk:8-jdk: Pulling from library/openjdk

386a066cd84a: Already exists
75ea84187083: Already exists
88b459c9f665: Already exists
690dbea0e4ca: Already exists
7e401cdd6f18: Already exists
a58186ddf9a0: Already exists
49999ed55bc4: Already exists
eb40561aad8f: Already exists
Digest: sha256:dd0fc686a5584c0c7f3e50dd84ddc42fae400c27a21d8ca98dad190aff5e9d52
Status: Downloaded newer image for openjdk:8-jdk</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_anatomy_of_a_container">Anatomy of a container</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Now, how does the previous apply to containers?
Well, it is pretty simple actually.
When you create a container, Docker will add a new layer on top of the stack of layers that makes up the image.
Contrary to the image layers, this layer is writable.</p>
</div>
<div class="paragraph">
<p>If you decide to create another container from the same image, a new writable layer will be created, the rest of the layer stack will be shared.</p>
</div>
<div class="paragraph">
<p>This diagram from the official documentation pretty much sums it up:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://docs.docker.com/engine/userguide/storagedriver/images/sharing-layers.jpg" alt="Layers and Containers">
</div>
</div>
<div class="paragraph">
<p>If you wish to go further in your understanding of containers, particurlarly on the topic of the writing strategies in containers, I suggest <a href="https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/">this page</a> of the official documentation.</p>
</div>
<div class="paragraph">
<p>That is it for this post, you can jump to the next one <a href="https://pierrebtz.github.io/2016/12/11/Docker-starter-kit-part-3-Commits-Volumes-and-Data-Management.html">Docker starter kit part 3: Commits, Volumes and Data Management</a></p>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2016/12/04/Docker-starter-kit-part-2-Anatomy-of-an-Image-and-a-Container.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2016/12/04/Docker-starter-kit-part-2-Anatomy-of-an-Image-and-a-Container.html</guid><category><![CDATA[HowTo]]></category><category><![CDATA[ Docker]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Sun, 04 Dec 2016 00:00:00 GMT</pubDate></item><item><title><![CDATA[Docker starter kit part 1: Getting Started, Container's lifecycle]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Docker is a major change in the way we develop software.
But it can be a bit challenging for a developer to enter the container world.
This series of articles is an answer to a developer wanting to onboard the Docker train.
It is not designed to be a complete docker training (these already exist), but rather a starter kit to quickly get up to speed.</p>
</div>
<div class="paragraph">
<p>There is not a fixed number of parts for this series as I am not sure yet how far I want to go (for instance I am not sure yet if I will talk about Docker compose or Docker Swarm).
Of course, do not hesitate to point any mistake or omission.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_getting_started">Getting Started</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_os">OS</h3>
<div class="paragraph">
<p>OS does not matter anymore.
Docker is now available natively on Windows 10 (from 1511 November update, Build 10586), OSX and Linux.
I use it without any problem on OSX and a debian at work, and on Windows 10 and a Linux Mint at home.
What I recommand though is to avoid the non native solutions for older OSX and Windows 7/10.
My experience with them was just awful: a lot of issues, of instabilities as well as some strange behaviors.</p>
</div>
</div>
<div class="sect2">
<h3 id="_let_s_go">Let&#8217;s go!</h3>
<div class="paragraph">
<p>Well, this part will be pretty simple.
Go to the <a href="https://www.docker.com/products/docker#/windows">official page</a>, and follow the official tutorial:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.docker.com/engine/getstarted/step_one/">Installation</a></p>
</li>
<li>
<p><a href="https://docs.docker.com/engine/getstarted/step_two/">Images and Containers</a></p>
</li>
<li>
<p><a href="https://docs.docker.com/engine/getstarted/step_three/">Run your first image</a></p>
</li>
<li>
<p><a href="https://docs.docker.com/engine/getstarted/step_four/">Build your first image</a></p>
</li>
<li>
<p><a href="https://docs.docker.com/engine/getstarted/step_five/">Create your Docker Hub account</a> this part can be skipped if you have access to a private repository</p>
</li>
<li>
<p><a href="https://docs.docker.com/engine/getstarted/step_six/">Push and Pull an image</a> if you are using a private repository, the name of the image becomes $private_repo/docker-whale</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In the following, I will assume you have the basic understanding that the tutorial gave you.</p>
</div>
<div class="paragraph">
<p>EDIT (04/12/2016): You can also have a look to the blog of <a href="http://jmkhael.io/">Johnny Mkhael</a> who wrote a <a href="http://jmkhael.hopto.org/hello-docker/">Hello Docker</a> blog series.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_lifecycle_of_a_container">Lifecycle of a container</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_overview">Overview</h3>
<div class="paragraph">
<p>A Docker Container can have multiple states.
Here is a complete list of the different states as well as the associated commands from the official documentation:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://docs.docker.com/engine/reference/api/images/event_state.png" alt="Docker States">
</div>
</div>
<div class="paragraph">
<p>As you can see, there are multiple state possible.</p>
</div>
</div>
<div class="sect2">
<h3 id="_main_states">Main States</h3>
<div class="paragraph">
<p>The main states are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>created</strong>: your container has been created.
This means you have created an instance of an image.
You get to this state by running the command <a href="https://docs.docker.com/engine/reference/commandline/create/">docker create</a></p>
</li>
</ul>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Remember what you saw in the tutorial, an image does not have a state and never changes.
</td>
</tr>
</table>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>running</strong>: The container is running which means it has been created (<a href="https://docs.docker.com/engine/reference/commandline/create/">docker create</a>) and started (<a href="https://docs.docker.com/engine/reference/commandline/start/">docker start</a>).
You can also directly reach this state from an image by running <a href="https://docs.docker.com/engine/reference/run/">docker run</a>.</p>
</li>
<li>
<p><strong>paused</strong>: A paused container is in a frozen state but its main process <strong>has not been stopped</strong> (the SIGTERM signal was not sent from docker to PID1 process).
The container has not released its ressources (eg memory).
From a running container you reach this state with the <a href="https://docs.docker.com/engine/reference/commandline/pause/">docker pause</a>.
It can be unfrozen using the <a href="https://docs.docker.com/engine/reference/commandline/unpause/">docker unpause</a>.</p>
</li>
<li>
<p><strong>stopped</strong>: When stopping a container, the SIGTERM signal has been sent from docker to the main process.
After a waiting time (10 seconds by default), the main process will receive a SIGKILL signal.
You can stop a container using <a href="https://docs.docker.com/engine/reference/commandline/stop/">docker stop</a>.
NB: You can also directly send a SIGKILL signal using <a href="https://docs.docker.com/engine/reference/commandline/kill/">docker kill</a>.
It is probably not a good idea if you wish to restart your container.
You can move from a stopped container to a running container using the docker start command.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>That is it for this post, you can jump to the next one: <a href="https://pierrebtz.github.io/2016/12/04/Docker-starter-kit-part-2-Anatomy-of-an-Image-and-a-Container.html">Docker starter kit part 2: Anatomy of an Image and a Container</a></p>
</div>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2016/11/27/Docker-starter-kit-part-1-Getting-Started-Containers-lifecycle.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2016/11/27/Docker-starter-kit-part-1-Getting-Started-Containers-lifecycle.html</guid><category><![CDATA[HowTo]]></category><category><![CDATA[ Docker]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Sun, 27 Nov 2016 00:00:00 GMT</pubDate></item><item><title><![CDATA[Running fish on Windows 10]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>You may already know that the latest Windows 10 builds offer a bash in Windows. This is a great feature. But what is even more interesting is that Microsoft did not do an emulation of linux commands but they partnered up with Canonical instead and integrated a real Ubuntu image on top of Windows. This means that you can run commands like&#8230;&#8203;apt-get in this bash.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__any_shell_for_windows">'Any Shell for Windows'</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In other words, this means that potentially, the 'bash for Windows' feature, could be understood like an 'any shell for Windows' feature! I decided to give it a try with fish. <a href="https://fishshell.com/">Fish</a> is an amazing shell a colleague of mine showed me some time ago. I am now using it every day at work and I was missing it at home on my Windows machine. It is really simple to use, it has a powerful autosuggestion engine, and it is easily extensible with scripts&#8230;&#8203;and a lot of people distribute great scripts!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_how_to_install_fish">How to install fish?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I will assume here that you already know how to install bash for Windows. If not, there are plenty of blogs that have detailed step by step guides. Once you are running bash, it is simply a matter of following the <a href="https://github.com/fish-shell/fish-shell/wiki/Nightly-builds">fish installation instruction for Ubuntu</a>, that is to say:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">sudo add-apt-repository ppa:fish-shell/nightly-master
sudo apt-get update
sudo apt-get install fish</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now you can just type fish in your prompt, and you are done. Unfortunately, next time you will launch an Unix shell from Windows it will have to be a bash. Good news is that it is easy to customize bash startup with the <strong>.bashrc</strong> file. To do this, first go to your unix home. Be careful, when you launch a bash, Windows puts you under your Windows' home (/mnt/c/Users/YourUser) and not under Unix&#8217;s home (~). To be sure, type:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">cd
nano .bashrc</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then add a the beginning of your .bashrc file the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">if [ -t 1 ]; then
  exec fish
fi</code></pre>
</div>
</div>
<div class="paragraph">
<p>Be careful not to just <strong>exec fish</strong>, you could break some programs and scripts relying on bash.</p>
</div>
<div class="paragraph">
<p>EDIT(04/12/2016): Despite the following script in my .bashrc, I had issues with scripts relying on bash.
I found another strategy which was to simply make a shortcut to launch fish.
To do this, you just have to locate the shortcut you have to launch bash, make a copy of this shortcut and edit it:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>target</strong>: Replace <code>C:\Windows\System32\bash.exe ~</code> by <code>C:\Windows\System32\bash.exe -c /usr/bin/fish</code>.</p>
</li>
<li>
<p><strong>start in</strong>: Set your preferred starting point. For the windows home set <code>%userprofile%</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Be sure to remove the modifications of the .bashrc as they are now useless (and maybe not harmless&#8230;&#8203;).</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_scripting">Scripting</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I installed <a href="https://github.com/fisherman/fisherman">fisherman</a> to manage the scripts of my fish installation. No ad intended, I don&#8217;t have enough feedback on it to give advice yet. Just know that there are other plugin managers out there, like <a href="https://github.com/oh-my-fish/oh-my-fish">oh my fish</a>. Here again, no need to adapt the installation instructions, everything worked as it would on Unix. For instance, I decided to install a useful teleporter script that allows to set labels on directories and jump back and forth:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">#install fisherman
curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs git.io/fisher
#install the tp script
fisher install simonrelet/tp-fish-function</code></pre>
</div>
</div>
<div class="paragraph">
<p>And here is the result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="http://github.com/PierreBtz/pierrebtz.github.io/raw/master/images/fishWin10_1.png" alt="result">
</div>
</div>
<div class="paragraph">
<p>I hope this was helpful. I&#8217;m still new to fish, feel free to drop a comment with tips or useful scripts! Of course, the main focus of the article was on fish, but I&#8217;m pretty sure that you can run any Unix shell you like using the same method.</p>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2016/10/09/Running-fish-on-Windows-10.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2016/10/09/Running-fish-on-Windows-10.html</guid><category><![CDATA[HowTo]]></category><category><![CDATA[ Windows 10]]></category><category><![CDATA[ Fish]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Sun, 09 Oct 2016 00:00:00 GMT</pubDate></item><item><title><![CDATA[Welcome Blogging World!]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>The idea of blogging has been making its way in my head for some time now. I&#8217;m already used to blogging on the internal blog of my company, but the audience is really small&#8230;&#8203;and so is the feedback.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_who_am_i">Who am I?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>By day, I am a UI developer working on multiple stacks going from Swing to Angular 2.0. I am also in charge of the Continuous Integration in my team.</p>
</div>
<div class="paragraph">
<p>I am interested in a lot of development topics, especially the subjects of Continuous Integration, Continous Delivery, Testing and Clean Coding.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_to_expect_from_this_blog">What to expect from this blog?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The main idea behind this blog is to share some thoughts about my readings, or the conferences I saw. I would also like to share some of my experiment. As the title suggests, I see this blog as a notepad, for my future self&#8230;&#8203;and for anyone who might have some interest on what I have to say.</p>
</div>
</div>
</div>]]></description><link>https://PierreBtz.github.io/2016/10/02/Welcome-Blogging-World.html</link><guid isPermaLink="true">https://PierreBtz.github.io/2016/10/02/Welcome-Blogging-World.html</guid><category><![CDATA[welcome]]></category><dc:creator><![CDATA[Pierre Btz]]></dc:creator><pubDate>Sun, 02 Oct 2016 00:00:00 GMT</pubDate></item></channel></rss>