<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>g.raphaelli.com</title><link>http://g.raphaelli.com/</link><description>Recent content on g.raphaelli.com</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 02 Dec 2023 12:00:16 -0500</lastBuildDate><atom:link href="http://g.raphaelli.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Meeting Recording Sharing as a Service</title><link>http://g.raphaelli.com/posts/2023-12-02-meeting-recording-sharing-as-a-service/</link><pubDate>Sat, 02 Dec 2023 12:00:16 -0500</pubDate><guid>http://g.raphaelli.com/posts/2023-12-02-meeting-recording-sharing-as-a-service/</guid><description>&lt;p>I made an Elastic Advent Calendar post here: &lt;a href="https://discuss.elastic.co/t/dec-2nd-2023-meeting-recording-sharing-as-a-service/347282">https://discuss.elastic.co/t/dec-2nd-2023-meeting-recording-sharing-as-a-service/347282&lt;/a>&lt;/p></description></item><item><title>Guess the utility</title><link>http://g.raphaelli.com/posts/2018-03-19-guess-the-utility/</link><pubDate>Mon, 19 Mar 2018 22:22:22 -0500</pubDate><guid>http://g.raphaelli.com/posts/2018-03-19-guess-the-utility/</guid><description>&lt;style>.description { cursor: pointer; }.solution { cursor: pointer; }&lt;/style>&lt;script>desc1 = "run a program with a time limit";solution1 = "&lt;code>/usr/bin/timeout&lt;/code>. The real timeout will send term first, and only optionally send kill after -k duration. This sends kill from the start.";desc2 = "run a program and summarize system resource usage";solution2 = "&lt;code>/usr/bin/time --format='rss:%M user:%U sys:%S real:%E'&lt;/code>. Not the bash builtin, &lt;a href='http://git.savannah.gnu.org/cgit/time.git/tree/src/time.c'>time.c&lt;/a>. Similar to /usr/bin/time -l on mac os.";desc3 = "run a program with process address space resource limit";solution3 = "&lt;code>/usr/bin/prlimit -v&lt;/code>";&lt;/script>&lt;p>I invented a couple of utilities at work today. They&amp;rsquo;re really useful little go programs. So useful, in fact,that they have already existed for many years. I haven&amp;rsquo;t written in a while so I figured I&amp;rsquo;d post them here as a littleguessing game. See if you can figure out what they are from the code, just note that they are not faithfulreimplementations, as I didn&amp;rsquo;t originally set out to reimplement anything. Expand the description for a hint.&lt;/p>&lt;h3>#1&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">ctx&lt;/span>, &lt;span style="color:#a6e22e">_&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">context&lt;/span>.&lt;span style="color:#a6e22e">WithTimeout&lt;/span>(&lt;span style="color:#a6e22e">context&lt;/span>.&lt;span style="color:#a6e22e">Background&lt;/span>(), &lt;span style="color:#a6e22e">timeout&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">cmd&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">exec&lt;/span>.&lt;span style="color:#a6e22e">CommandContext&lt;/span>(&lt;span style="color:#a6e22e">ctx&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Args&lt;/span>[&lt;span style="color:#ae81ff">1&lt;/span>], &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Args&lt;/span>[&lt;span style="color:#ae81ff">2&lt;/span>:]&lt;span style="color:#f92672">...&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stdout&lt;/span>, &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stdin&lt;/span> = &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stdout&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stdin&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#66d9ef">error&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Start&lt;/span>(); &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Fprintln&lt;/span>(&lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#66d9ef">else&lt;/span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Wait&lt;/span>()&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Exit&lt;/span>(&lt;span style="color:#a6e22e">exitCode&lt;/span>(&lt;span style="color:#a6e22e">err&lt;/span>))&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="description" onClick="this.innerHTML=desc1">description&lt;/p>&lt;p class="solution" onClick="this.innerHTML=solution1">solution&lt;/p>&lt;h3>#2&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">cmd&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">exec&lt;/span>.&lt;span style="color:#a6e22e">Command&lt;/span>(&lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Args&lt;/span>[&lt;span style="color:#ae81ff">1&lt;/span>], &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Args&lt;/span>[&lt;span style="color:#ae81ff">2&lt;/span>:]&lt;span style="color:#f92672">...&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stdout&lt;/span>, &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stdin&lt;/span> = &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stdout&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stdin&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#66d9ef">error&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">start&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Now&lt;/span>()&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Start&lt;/span>(); &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Fprintln&lt;/span>(&lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>} &lt;span style="color:#66d9ef">else&lt;/span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Wait&lt;/span>()&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">end&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Now&lt;/span>()&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">rusage&lt;/span> &lt;span style="color:#a6e22e">syscall&lt;/span>.&lt;span style="color:#a6e22e">Rusage&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">syscall&lt;/span>.&lt;span style="color:#a6e22e">Getrusage&lt;/span>(&lt;span style="color:#a6e22e">syscall&lt;/span>.&lt;span style="color:#a6e22e">RUSAGE_CHILDREN&lt;/span>, &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">rusage&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;rss:%d user:%s sys:%s real:%s\n&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">rusage&lt;/span>.&lt;span style="color:#a6e22e">Maxrss&lt;/span>, &lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Duration&lt;/span>(&lt;span style="color:#a6e22e">rusage&lt;/span>.&lt;span style="color:#a6e22e">Utime&lt;/span>.&lt;span style="color:#a6e22e">Nano&lt;/span>())&lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Nanosecond&lt;/span>, &lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Duration&lt;/span>(&lt;span style="color:#a6e22e">rusage&lt;/span>.&lt;span style="color:#a6e22e">Stime&lt;/span>.&lt;span style="color:#a6e22e">Nano&lt;/span>())&lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Nanosecond&lt;/span>, &lt;span style="color:#a6e22e">end&lt;/span>.&lt;span style="color:#a6e22e">Sub&lt;/span>(&lt;span style="color:#a6e22e">start&lt;/span>))&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Exit&lt;/span>(&lt;span style="color:#a6e22e">exitCode&lt;/span>(&lt;span style="color:#a6e22e">err&lt;/span>))&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="description" onClick="this.innerHTML=desc2">description&lt;/p>&lt;p class="solution" onClick="this.innerHTML=solution2">solution&lt;/p>&lt;h3>#3&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">rlimit&lt;/span> &lt;span style="color:#a6e22e">syscall&lt;/span>.&lt;span style="color:#a6e22e">Rlimit&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">rlimit&lt;/span>.&lt;span style="color:#a6e22e">Cur&lt;/span> = &lt;span style="color:#ae81ff">134217728&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">rlimit&lt;/span>.&lt;span style="color:#a6e22e">Max&lt;/span> = &lt;span style="color:#ae81ff">134217728&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">syscall&lt;/span>.&lt;span style="color:#a6e22e">Setrlimit&lt;/span>(&lt;span style="color:#a6e22e">syscall&lt;/span>.&lt;span style="color:#a6e22e">RLIMIT_AS&lt;/span>, &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">rlimit&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">cmd&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">exec&lt;/span>.&lt;span style="color:#a6e22e">Command&lt;/span>(&lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Args&lt;/span>[&lt;span style="color:#ae81ff">1&lt;/span>], &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Args&lt;/span>[&lt;span style="color:#ae81ff">2&lt;/span>:]&lt;span style="color:#f92672">...&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stdout&lt;/span>, &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Stdin&lt;/span> = &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stdout&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stdin&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#66d9ef">error&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Start&lt;/span>(); &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Fprintln&lt;/span>(&lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Stderr&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#66d9ef">else&lt;/span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">cmd&lt;/span>.&lt;span style="color:#a6e22e">Wait&lt;/span>()&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Exit&lt;/span>(&lt;span style="color:#a6e22e">exitCode&lt;/span>(&lt;span style="color:#a6e22e">err&lt;/span>))&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="description" onClick="this.innerHTML=desc3">description&lt;/p>&lt;p class="solution" onClick="this.innerHTML=solution3">solution&lt;/p>&lt;p>If you like this kind of thing, many other utilities can be found in &lt;a href="https://github.com/ericlagergren/go-coreutils">go-coreutils&lt;/a>.&lt;/p></description></item><item><title>A tech job search in 2017</title><link>http://g.raphaelli.com/posts/2017-11-11-a-tech-job-search/</link><pubDate>Sat, 11 Nov 2017 22:22:22 -0500</pubDate><guid>http://g.raphaelli.com/posts/2017-11-11-a-tech-job-search/</guid><description>&lt;p>I started searching for a new role just over 2 months ago. While I had passively reviewed messages from recruiters for awhile, I finally decided to give the search a real go, getting in touch with my network, switching my status to searching on AngelList &amp;amp; StackOverflow jobs, and applying to a few promising positions I had found through Indeed, in #golang-jobs on gophers slack, and elsewhere. I was not interested in relocating so I targeted only Philadelphia-based, 1 day/week in NYC, or fully remote gigs.&lt;/p>&lt;p>Here&amp;rsquo;s part that might be helpful to others - turns out it&amp;rsquo;s kind of a mess out there.&lt;/p>&lt;p>Sometimes you won&amp;rsquo;t hear any news for a long time. That was the biggest surprise for me, especially when doing well during a round of interviews. When I&amp;rsquo;ve been in a position to hire and a good candidate comes in - someone I think would be a great addition to the team - I work to create a sense of urgency and enthusiasm do get that candidate fired up to join. That means feedback within 24 hours, end of day or first thing the following morning. During my experience in the last few weeks, I had no word for 2+ weeks after just an HR screen, twice! I was pretty sure I didn&amp;rsquo;t mess up quite that badly in either. In both cases I followed up with a &amp;ldquo;hey I like you so far, do you still want to figure out if you like me&amp;rdquo; email and both times had an initial phone screen with team set up shortly after, both times with an apology for the delay. I&amp;rsquo;m still waiting on a code challenge following the second round of talks with one of those.&lt;/p>&lt;p>Despite making it clear that relocation was not an option, some recruiters still wanted to chat and try to convince me to move San Francisco (it was always SF). While I&amp;rsquo;m flattered, it&amp;rsquo;s not a great use of either of our time. I learned this one pretty quickly and cleared it up early in the process with each company. One phone screen ended amicably after about 90 seconds but I could have avoided it altogether with an email.&lt;/p>&lt;p>Some companies, especially very small startups, require an extreme amount of time to complete the interview process. I went along with a few of these at first, investing many hours in video calls, coffees, and pairing sessions. After having some success without investing quite that much time, I was less enthusiastic about going back for yet another round at the other places and politely bowed out of their next rounds.&lt;/p>&lt;p>Getting in through the front door is pretty tough. I&amp;rsquo;m fortunate to have many years of experience, in various roles, and tales of scaling and hard problems at some notable companies - I was rejected from a number of companies without even an initial phone screen. Referrals are of course great for getting that initial interview, but most of us only have access to so many of those. I felt more successful when replying to posts in slack channels and hacker news and those sent through a mailing list than through companies&amp;rsquo; HR portals though my supporting evidence on this is a little thin. I considered but ultimately decided against services like Hired, TripleByte, and VolkScience this time.&lt;/p>&lt;p>The point of all of this is not really to get recruiters, HR, and hiring managers to improve their processes, though it would be great if that happened, it&amp;rsquo;s more of a note to my fellow job seekers that there is a lot of strangeness in [tech] recruiting out there and you&amp;rsquo;re not weird or alone if you&amp;rsquo;ve had similar experiences. I&amp;rsquo;ve confirmed with a bunch of other developers that what I&amp;rsquo;ve described here was not unique to my search. My best advice is to stick with it and really go all in on the search with some deadline in mind for making decision. Be flexible and understanding with possible future employers but don&amp;rsquo;t let them undervalue your time.&lt;/p>&lt;p>Good luck out there.&lt;/p></description></item><item><title>Routetable - transit scheduling with GTFS</title><link>http://g.raphaelli.com/posts/2016-07-21-routetable-transit-gtfs/</link><pubDate>Thu, 21 Jul 2016 22:22:22 -0500</pubDate><guid>http://g.raphaelli.com/posts/2016-07-21-routetable-transit-gtfs/</guid><description>&lt;p>&lt;img src="http://g.raphaelli.com/2016-07-21-routetable.png" alt="Routetable screenshot">&lt;/p>&lt;p>I put together as a little &lt;a href="https://github.com/graphaelli/routetable">project&lt;/a> to get to know the&lt;a href="https://developers.google.com/transit/gtfs/">General Transit Feed Specification&lt;/a> and address some of the issues I havewith Google&amp;rsquo;s otherwise excellent transit routing. First, I&amp;rsquo;m willing to walk pretty far to a transit stop. Second, Iwant to see more options at one time. Finally, I want a hard limit on transfers (Google does provide a less transferspreference). I also really wanted to show off my amazing UX skills.&lt;/p>&lt;p>If you share the pleasure of riding SEPTA, you may find &lt;a href="http://g.raphaelli.com/rt/">http://g.raphaelli.com/rt/&lt;/a> helpful for planning yournext commute.&lt;/p></description></item><item><title>EC2 Metadata service mocking with Docker</title><link>http://g.raphaelli.com/posts/2016-03-03-ec2-metadata-mock-docker/</link><pubDate>Thu, 03 Mar 2016 00:00:00 -0500</pubDate><guid>http://g.raphaelli.com/posts/2016-03-03-ec2-metadata-mock-docker/</guid><description>&lt;p>My former &lt;a href="http://monetate.com">employer&lt;/a> quietly open sourced a handy utility for mocking out the metadata service on AWS&amp;rsquo; EC2hosts a few weeks ago. As I&amp;rsquo;ve mentioned &lt;a href="http://g.raphaelli.com/2015/11/20/docker-s3-private-registry.html">previously&lt;/a>, there are a fewprojects in this space, but none quite as simple as this one to get going.&lt;/p>&lt;p>&lt;a href="https://github.com/monetate/ectou-metadata">ectou-metadata&lt;/a> includes an implementation of the IAM security credential API that, to applications, feels exactly likerunning on an EC2 instance launched with an IAM Role. This means no more injecting credentials into containers needingAWS API access. It also helps increase &lt;a href="http://12factor.net/dev-prod-parity">dev prod parity&lt;/a>.&lt;/p>&lt;p>Here&amp;rsquo;s how I&amp;rsquo;m using it out under Mac OSX with a stock &lt;code>docker-machine&lt;/code> virtualbox VM.&lt;/p>&lt;p>First, build the docker container image. I&amp;rsquo;ve contributed a &lt;a href="https://github.com/monetate/ectou-metadata/pull/1">Dockerfile&lt;/a>.&lt;/p>&lt;p>Next, plumb the well-known address in the docker-machine VM:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>docker-machine ssh &amp;lt;machine-label&amp;gt; sudo ip addr add 169.254.169.254 dev eth0&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Finally, launch a container bound to that IP. This works with &lt;code>--net&lt;/code> if using docker networking.&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>docker run &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --env&lt;span style="color:#f92672">=&lt;/span>MOCK_METADATA_ROLE_ARN&lt;span style="color:#f92672">=&lt;/span>arn:aws:iam::ACCOUNT:role/ROLE &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --publish&lt;span style="color:#f92672">=&lt;/span>169.254.169.254:80:5000 &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --volume&lt;span style="color:#f92672">=&lt;/span>$HOME/.aws/mock-metadata-credentials:/home/ec2-user/.aws/credentials:ro &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> ectou-metadata&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If everything is wired up correctly you&amp;rsquo;ll see 200s logged by the ectou-metadata service when making API calls, like:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>172.17.0.1 - - &lt;span style="color:#f92672">[&lt;/span>01/Feb/2016 18:54:29&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#e6db74">&amp;#34;GET /latest/meta-data/iam/security-credentials/ HTTP/1.1&amp;#34;&lt;/span> &lt;span style="color:#ae81ff">200&lt;/span> &lt;span style="color:#ae81ff">9&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>172.17.0.1 - - &lt;span style="color:#f92672">[&lt;/span>01/Feb/2016 18:54:29&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#e6db74">&amp;#34;GET /latest/meta-data/iam/security-credentials/role-name HTTP/1.1&amp;#34;&lt;/span> &lt;span style="color:#ae81ff">200&lt;/span> &lt;span style="color:#ae81ff">641&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Other containers running on this VM that have network access will be able to use these credentials.&lt;/p>&lt;p>I&amp;rsquo;d love to figure out how to run multiple instances of this within a single VM so different containers automaticallyhave access to different roles but for now I&amp;rsquo;ve settled on one instance per VM/set of containers. If there&amp;rsquo;s anyone outthere, please &lt;a href="http://g.raphaelli.com/about">get in touch&lt;/a> with thoughts.&lt;/p>&lt;p>There are someother &lt;a href="https://github.com/monetate/ectou-metadata/blob/2ab1894c619e19b6f5062101bad24cd37ab32910/ectou_metadata/service.py#L22-L26">goodies&lt;/a>baked in that look useful. Big thanks to Monetate for opening up this code!&lt;/p></description></item><item><title>Caffe with Python3 on Centos 7 Install Notes</title><link>http://g.raphaelli.com/posts/2016-01-04-centos-python3-caffe/</link><pubDate>Tue, 05 Jan 2016 01:00:16 -0500</pubDate><guid>http://g.raphaelli.com/posts/2016-01-04-centos-python3-caffe/</guid><description>&lt;p>I&amp;rsquo;ve posted a gist &lt;a href="https://gist.github.com/graphaelli/7a104545be9e288d94bc">here&lt;/a> with a Dockerfile for building aCentos 7 + Python 3 + caffe container image. I don&amp;rsquo;t expect many will use this directly but the steps and variouspatches might be useful for incorporation into your own Dockerfiles. As such, I&amp;rsquo;ve put no effort into minimizing thesize or cacheability of layers. &lt;code>docker build&lt;/code> as-is weighs in at 2.4+ GB.&lt;/p>&lt;p>The default docker run expects a &lt;code>/notebooks&lt;/code> volume and exposes jupyter-notebook on port 8888.&lt;/p></description></item><item><title>AWS EC2 Container Registry is Better</title><link>http://g.raphaelli.com/posts/2015-12-23-aws-ec2-container-registry-is-better/</link><pubDate>Wed, 23 Dec 2015 00:00:16 -0500</pubDate><guid>http://g.raphaelli.com/posts/2015-12-23-aws-ec2-container-registry-is-better/</guid><description>&lt;p>I spent some of today with EC2 Container Registry following it&amp;rsquo;s promotion to &lt;a href="https://aws.amazon.com/blogs/aws/ec2-container-registry-now-generally-available/">GA&lt;/a> status and it&amp;rsquo;s clearthat it&amp;rsquo;s far better than what I &lt;a href="http://g.raphaelli.com/2015/11/20/docker-s3-private-registry.html">was using&lt;/a>. For $0.07 more perGB-month, you get the same features as any other private registry service:&lt;/p>&lt;ul>&lt;li>no need to run a local registry on every host&lt;/li>&lt;li>global https access to a highly available registry&lt;/li>&lt;li>registry browser UI&lt;/li>&lt;/ul>&lt;p>But you also get &lt;strong>IAM permissions&lt;/strong> based access control!&lt;/p>&lt;p>The docs and AWS console walkthrough are great for getting started, the brief version is:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>pip install -U awscli&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>aws ecr create-repository --repository-name &amp;lt;name&amp;gt;&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">$(&lt;/span>aws ecr get-login --region us-east-1&lt;span style="color:#66d9ef">)&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># docker tag, push ...&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="docker-pull-permissions">docker pull permissions&lt;/h2>&lt;p>The policy looks like this for the role and user, using awacs and troposphere:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Permit AWS-hosted docker registry access&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">from&lt;/span> awacs.aws &lt;span style="color:#f92672">import&lt;/span> Allow, Everybody, Policy, Statement&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> awacs.iam &lt;span style="color:#66d9ef">as&lt;/span> iam&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> troposphere.iam&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>troposphere&lt;span style="color:#f92672">.&lt;/span>iam&lt;span style="color:#f92672">.&lt;/span>Policy(&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>PolicyName&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;DockerRegistryConsumerPolicy&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>PolicyDocument&lt;span style="color:#f92672">=&lt;/span>Policy(&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Statement&lt;span style="color:#f92672">=&lt;/span>[&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Statement(&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Effect&lt;span style="color:#f92672">=&lt;/span>Allow,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Action&lt;span style="color:#f92672">=&lt;/span>[&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;BatchCheckLayerAvailability&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;BatchGetImage&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;DescribeRepositories&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;GetAuthorizationToken&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;GetDownloadUrlForLayer&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;GetRepositoryPolicy&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>iam&lt;span style="color:#f92672">.&lt;/span>Action(&lt;span style="color:#e6db74">&amp;#34;ecr&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;ListImages&amp;#34;&lt;/span>),&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Resource&lt;span style="color:#f92672">=&lt;/span>[Everybody],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and generates:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;Policies&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;PolicyName&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">&amp;#34;DockerRegistryConsumerPolicy&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;PolicyDocument&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Statement&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Action&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:BatchCheckLayerAvailability&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:BatchGetImage&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:DescribeRepositories&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:GetAuthorizationToken&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:GetDownloadUrlForLayer&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:GetRepositoryPolicy&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;ecr:ListImages&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Effect&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Allow&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Resource&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;*&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This permits login, pulling images, and browsing the repositories. Since we&amp;rsquo;re using AWS, command line tools tosubstitute for &lt;code>docker search&lt;/code> are easily within reach&lt;/p>&lt;ul>&lt;li>&lt;code>aws ecr describe-repositories&lt;/code> to view the repos&lt;/li>&lt;li>&lt;code>aws ecr list-images --repository-name &amp;lt;name&amp;gt;&lt;/code> to see image:tag availability.&lt;/li>&lt;/ul>&lt;h2 id="docker-push-permissions">docker push permissions&lt;/h2>&lt;p>So far, I&amp;rsquo;ve simply logged in as a registry administrator (&lt;code>Allow&lt;/code>ed to &lt;code>ecr:*&lt;/code>) when needing to push images -eventually CI will be the only user allowed to assume that role. For now that is:&lt;/p>&lt;p>&lt;code>docker logout https://$acct.dkr.ecr.us-east-1.amazonaws.com&lt;/code>&lt;/p>&lt;p>followed by&lt;/p>&lt;p>&lt;code>$(aws ecr get-login --region us-east-1 --profile registry-admin)&lt;/code>.&lt;/p></description></item><item><title>Elasticsearch magic for IPython</title><link>http://g.raphaelli.com/posts/2015-12-14-ipython-elasticsearch/</link><pubDate>Mon, 14 Dec 2015 05:00:16 -0500</pubDate><guid>http://g.raphaelli.com/posts/2015-12-14-ipython-elasticsearch/</guid><description>&lt;p>Scratched an itch on Friday night while waiting for my flight back from HQ. It&amp;rsquo;s basically &lt;a href="https://www.elastic.co/blog/found-sense-a-cool-json-aware-interface-to-elasticsearch">sense&lt;/a> + &lt;a href="https://github.com/catherinedevlin/ipython-sql">ipython-sql&lt;/a> and looks like this in jupyter-notebook under python3:&lt;/p>&lt;iframe frameborder="0" border="0" cellspacing="0" style="border-style: none;width: 100%; height: 1000px;" src="http://g.raphaelli.com/2015-12-14-ipython-elasticsearch.ipynb.html">&lt;/iframe>&lt;p>I&amp;rsquo;m pleasantly surprised the name was available on &lt;a href="https://pypi.python.org/pypi/ipython-elasticsearch">pypi&lt;/a>. Check out the source on &lt;a href="https://github.com/graphaelli/ipython-elasticsearch">github&lt;/a>.&lt;/p></description></item><item><title>Docker S3-Backed Private Registry On Every Host</title><link>http://g.raphaelli.com/posts/2015-11-20-docker-s3-private-registry/</link><pubDate>Fri, 20 Nov 2015 02:04:18 -0500</pubDate><guid>http://g.raphaelli.com/posts/2015-11-20-docker-s3-private-registry/</guid><description>&lt;h1 id="update---aws-has-since-released-a-private-registry-service20151223aws-ec2-container-registry-is-betterhtml">&lt;strong>Update&lt;/strong> - AWS has since released a &lt;a href="http://g.raphaelli.com/2015/12/23/aws-ec2-container-registry-is-better.html">private registry service&lt;/a>&lt;/h1>&lt;p>I attended &lt;a href="http://peterjshan.com/speaking/2015/08/golangphilly-phillydevops-combined-meetup/">this talk&lt;/a> at a &lt;a href="http://www.meetup.com/GoLangPhilly">GoLangPhilly&lt;/a> meetup back in August where the speaker,&lt;a href="https://github.com/pietrojs">Peter Shannon&lt;/a>, detailed the setup his company has been using to run docker in production. Every host thereruns a local docker registry server backed by S3.&lt;/p>&lt;p>That was the first I&amp;rsquo;d heard of this setup. Usually posts discuss running a private registry in a dedicated spot(usually on AWS, behind an ELB) and funneling all docker pulls and pushes through it. Eliminating authentication issuesand a single point of failure (except s3 of course) seem like a big win with little downside. I decided to try it out.&lt;/p>&lt;p>I realize tutorials for creating an S3-backed private registry are easily found on the web and the&lt;a href="https://docs.docker.com/registry/configuration/">docs&lt;/a> are good, but I haven&amp;rsquo;t seen a single version I&amp;rsquo;d follow beginning to end yet.Here is the way we do it at &lt;a href="http://realscout.com">RealScout&lt;/a>. Briefly, the steps covered involve:&lt;/p>&lt;ol>&lt;li>Create a bucket&lt;/li>&lt;li>Grant bucket access&lt;/li>&lt;li>Run the registry container&lt;/li>&lt;li>Using the registry&lt;/li>&lt;/ol>&lt;p>In the end, we&amp;rsquo;ll be able to run docker containers on EC2 and outside of EC2 in pretty much the same way, both using ahighly available S3-backed private registry.&lt;/p>&lt;h2 id="create-the-bucket">Create the bucket&lt;/h2>&lt;p>I&amp;rsquo;ve given up on doing this with Cloudformation and just do this rare operation via the AWS console.&lt;/p>&lt;h2 id="configure-the-bucket">Configure the bucket&lt;/h2>&lt;p>Optional, but it&amp;rsquo;s probably a good idea to turn on &lt;a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html">server side encryption&lt;/a> for this bucket. We use&lt;a href="https://github.com/cloudtools/troposphere">troposphere&lt;/a> to generate templates. &lt;a href="https://gist.github.com/graphaelli/3a1e43cb94b3e7e36ce5">This gist&lt;/a> generates:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Resources&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;DockerRegistryBucket&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Properties&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Bucket&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;docker-registry&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;PolicyDocument&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Id&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;DockerRegistryBucketPolicy&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Statement&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Action&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;s3:PutObject&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Condition&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;StringNotEquals&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;s3:x-amz-server-side-encryption&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;AES256&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Effect&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Deny&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Principal&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;*&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Resource&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;arn:aws:s3:::docker-registry/*&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Sid&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;DenyUnEncryptedObjectUploads&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2012-10-17&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;AWS::S3::BucketPolicy&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="configure-bucket-access-permissions">Configure bucket access permissions&lt;/h2>&lt;p>Access to the bucket backing the registry currently works two different ways in our environment, depending on whetheryou&amp;rsquo;re accessing from within EC2 or outside. Within EC2, IAM roles can be utilized to control access. Outside of EC2,we use IAM user policies to manage access. I hope to spend some time with &lt;a href="https://github.com/dump247/aws-mock-metadata">aws-mock-metadata&lt;/a> or&lt;a href="https://github.com/AdRoll/hologram">hologram&lt;/a> to eliminate this disparity.&lt;/p>&lt;p>The policy looks like this for the role and user, using &lt;a href="https://gist.github.com/graphaelli/3a1e43cb94b3e7e36ce5">this gist&lt;/a>:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;Policies&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;PolicyName&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">&amp;#34;DockerRegistryConsumerPolicy&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;PolicyDocument&amp;#34;&lt;/span>: {&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Statement&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Action&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;s3:ListBucket&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;s3:GetBucketLocation&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Effect&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Allow&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Resource&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;arn:aws:s3:::docker-registry&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>},&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;Action&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;s3:GetObject&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>],&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Effect&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Allow&amp;#34;&lt;/span>,&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;#34;Resource&amp;#34;&lt;/span>: [&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;arn:aws:s3:::docker-registry/*&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This permits only pulls. Add &lt;code>s3:PutObject&lt;/code> for pushing.&lt;/p>&lt;h2 id="run-the-registry-container">Run the registry container&lt;/h2>&lt;p>Using this config, &lt;code>dev-config.yml&lt;/code> in my case:&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">+++&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">version&lt;/span>: &lt;span style="color:#ae81ff">0.1&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">log&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">level&lt;/span>: &lt;span style="color:#ae81ff">info&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">formatter&lt;/span>: &lt;span style="color:#ae81ff">text&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">fields&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">service&lt;/span>: &lt;span style="color:#ae81ff">registry&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">storage&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cache&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">blobdescriptor&lt;/span>: &lt;span style="color:#ae81ff">inmemory&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">s3&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">region&lt;/span>: &lt;span style="color:#ae81ff">us-east-1&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">bucket&lt;/span>: &lt;span style="color:#ae81ff">docker-registry&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">encrypt&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">secure&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">v4auth&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">rootdirectory&lt;/span>: &lt;span style="color:#ae81ff">/myroot&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">http&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">addr&lt;/span>: :&lt;span style="color:#ae81ff">5000&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">headers&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">X-Content-Type-Options&lt;/span>: [&lt;span style="color:#ae81ff">nosniff]&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">health&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">storagedriver&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">enabled&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">interval&lt;/span>: &lt;span style="color:#ae81ff">10s&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">threshold&lt;/span>: &lt;span style="color:#ae81ff">3&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;p>and this &lt;code>docker-compose.yml&lt;/code>:&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">+++&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">registry&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">container_name&lt;/span>: &lt;span style="color:#ae81ff">registry&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">image&lt;/span>: &lt;span style="color:#ae81ff">registry:2&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ports&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">5000&lt;/span>:&lt;span style="color:#ae81ff">5000&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">restart&lt;/span>: &lt;span style="color:#ae81ff">always&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">volumes&lt;/span>:&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">./dev-config.yml:/etc/docker/registry/config.yml:ro&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;p>You can &lt;code>docker-compose up -d&lt;/code> on an EC2 box with the above policy attached to its IAM Role.&lt;/p>&lt;p>Outside of EC2, use a similar &lt;code>docker-compose.yml&lt;/code> with credentials mounted in via &lt;code>volumes&lt;/code>:&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#ae81ff">${HOME}/.aws/docker-credentials:/${REGISTRY_USER}/.aws/credentials:ro&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;p>That credentials file follows the &lt;a href="https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs">standardized credentials&lt;/a> file format.&lt;/p>&lt;p>If all is well, you should be able to list the available repos:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-console" data-lang="console">&lt;span style="display:flex;">&lt;span>$ curl -s http://localhost:5000/v2/_catalog&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{&amp;#34;repositories&amp;#34;:[]}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The docker docs are really quite good, &lt;a href="https://docs.docker.com/registry/introduction/">https://docs.docker.com/registry/introduction/&lt;/a> is a great placeto continue on from here.&lt;/p>&lt;h2 id="notes">Notes&lt;/h2>&lt;p>We haven&amp;rsquo;t hit any issues with this configuration yet but do worry a bit about eventual consistency issues with s3 thata single registry server would address.&lt;/p></description></item><item><title>Docker Data Container Snapshots</title><link>http://g.raphaelli.com/posts/2015-11-13-docker-data-container-snapshots/</link><pubDate>Fri, 13 Nov 2015 17:10:18 -0500</pubDate><guid>http://g.raphaelli.com/posts/2015-11-13-docker-data-container-snapshots/</guid><description>&lt;p>It&amp;rsquo;s straightforward to create a data container with pre-baked archives included: &lt;code>COPY&lt;/code> them on during &lt;code>docker build&lt;/code> and off you go. But what if you can&amp;rsquo;t or don&amp;rsquo;t want to create the data ahead of time?&lt;/p>&lt;p>At &lt;a href="http://realscout.com">RealScout&lt;/a>, we recently started experimenting with the approach detailed below to create docker images from postgres snapshots for distribution to development environments. Briefly, it looks like:&lt;/p>&lt;ol>&lt;li>Create and initialize a data container&lt;/li>&lt;li>Start postgres using the volume from that data container&lt;/li>&lt;li>Restore data&lt;/li>&lt;li>Stop postgres&lt;/li>&lt;li>Create an image from the populated data container&lt;/li>&lt;/ol>&lt;h2 id="setup">Setup&lt;/h2>&lt;p>First, build a data container image, for example with this &lt;code>Dockerfile&lt;/code>. :&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-docker" data-lang="docker">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">FROM&lt;/span>&lt;span style="color:#e6db74"> debian:jessie&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">ENV&lt;/span> PGDATA /var/lib/postgresql/data&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># ids must match postgres container&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> groupadd -r -g &lt;span style="color:#ae81ff">999&lt;/span> postgres &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> useradd -u &lt;span style="color:#ae81ff">999&lt;/span> -r -g postgres postgres&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> mkdir -p -m &lt;span style="color:#ae81ff">0700&lt;/span> &lt;span style="color:#e6db74">${&lt;/span>PGDATA&lt;span style="color:#e6db74">}&lt;/span> &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> chown -R postgres:postgres &lt;span style="color:#e6db74">${&lt;/span>PGDATA&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">VOLUME&lt;/span>&lt;span style="color:#e6db74"> ${PGDATA}&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">CMD&lt;/span> /bin/true&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-console" data-lang="console">&lt;span style="display:flex;">&lt;span>$ docker build .&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Successfully built 221c07e4f85c&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now create a postgres data container using that image:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-console" data-lang="console">&lt;span style="display:flex;">&lt;span>$ docker run --name postgres-data 221c07e4f85c&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>$ docker ps -af ancestor&lt;span style="color:#f92672">=&lt;/span>221c07e4f85c&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>d13320582ff2 221c07e4f85c &amp;#34;/bin/sh -c /bin/true&amp;#34; 1 seconds ago Exited (0) Less than a second ago hopeful_boyd&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now start a postgres server (using an image from Docker Hub here) using the volume from that data container:&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-console" data-lang="console">&lt;span style="display:flex;">&lt;span>$ docker run -d --volumes-from postgres-data --name postgres postgres&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>146ad1bcbb45c74100f5345facff8be2ba9a532cbe0c5642a0c0bf840b4234e4&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;p>Now we can load up some data. At this point, you should probably scrub out anything from the database backup that you don&amp;rsquo;t want to include in the image.&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>pg_restore ...&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-clever-part">The clever part&lt;/h2>&lt;p>We&amp;rsquo;re going to &lt;code>docker build&lt;/code> inside of a third container that has the data volume mounted inside of it. It&amp;rsquo;s &lt;a href="http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/">not quite docker in docker&lt;/a>, but it&amp;rsquo;s related.&lt;/p>&lt;p>First, take the Dockerfile above and &lt;code>COPY data/ ${PGDATA}&lt;/code> to it so it looks like:&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-docker" data-lang="docker">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">FROM&lt;/span>&lt;span style="color:#e6db74"> debian:jessie&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">ENV&lt;/span> PGDATA /var/lib/postgresql/data&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># ids must match postgres container&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> groupadd -r -g &lt;span style="color:#ae81ff">999&lt;/span> postgres &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> useradd -u &lt;span style="color:#ae81ff">999&lt;/span> -r -g postgres postgres&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">RUN&lt;/span> mkdir -p -m &lt;span style="color:#ae81ff">0700&lt;/span> &lt;span style="color:#e6db74">${&lt;/span>PGDATA&lt;span style="color:#e6db74">}&lt;/span> &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> chown -R postgres:postgres &lt;span style="color:#e6db74">${&lt;/span>PGDATA&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">COPY&lt;/span> data/ &lt;span style="color:#e6db74">${&lt;/span>PDATA&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">VOLUME&lt;/span>&lt;span style="color:#e6db74"> ${PGDATA}&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#66d9ef">CMD&lt;/span> /bin/true&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;p>(We just have a single &lt;code>Dockerfile&lt;/code> used for initial and snapshot builds and an empty &lt;code>data/&lt;/code> sitting around for use by the initial build.)&lt;/p>&lt;p>Now stick this in &lt;code>snapshot.sh&lt;/code>:&lt;/p>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">{&lt;/span>%raw&amp;gt;&lt;span style="color:#f92672">}}&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#!/bin/bash -e&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>BUILD_WD&lt;span style="color:#f92672">=&lt;/span>/tmp/build&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>DATA_CONTAINER&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>1&lt;span style="color:#66d9ef">:-&lt;/span>postgres-data&lt;span style="color:#e6db74">}&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>DOCKERFILE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>2&lt;span style="color:#66d9ef">:-&lt;/span>Dockerfile&lt;span style="color:#e6db74">}&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># like --volumes-from, but allow for mounting in a different location&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>DATA_VOLUME&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>docker inspect --format &lt;span style="color:#e6db74">&amp;#39;{{range $mount := .Mounts}}{{if eq $mount.Destination &amp;#34;/var/lib/postgresql/data&amp;#34;}}{{$mount.Source}}{{end}}{{end}}&amp;#39;&lt;/span> $DATA_CONTAINER&lt;span style="color:#66d9ef">)&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># stop sends TERM, like postgres wants&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker stop -t &lt;span style="color:#ae81ff">60&lt;/span> postgres&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># create a docker container with data mounted at /data and run docker build inside it&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>docker run --rm &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -v &lt;span style="color:#e6db74">${&lt;/span>PWD&lt;span style="color:#e6db74">}&lt;/span>/&lt;span style="color:#e6db74">${&lt;/span>DOCKERFILE&lt;span style="color:#e6db74">}&lt;/span>:/tmp/build/Dockerfile &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -v &lt;span style="color:#e6db74">${&lt;/span>DATA_VOLUME&lt;span style="color:#e6db74">}&lt;/span>:&lt;span style="color:#e6db74">${&lt;/span>BUILD_WD&lt;span style="color:#e6db74">}&lt;/span>/data &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -v /var/run/docker.sock:/var/run/docker.sock &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -v &lt;span style="color:#66d9ef">$(&lt;/span>which docker&lt;span style="color:#66d9ef">)&lt;/span>:/bin/docker &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -w &lt;span style="color:#e6db74">${&lt;/span>BUILD_WD&lt;span style="color:#e6db74">}&lt;/span> &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> debian:jessie &lt;span style="color:#ae81ff">\&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> docker build .&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">{&lt;/span>%endraw&amp;gt;&lt;span style="color:#f92672">}}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>if you used the &lt;code>--name&lt;/code> flag on &lt;code>docker run&lt;/code> earlier, you can snapshot with:&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>./snapshot.sh&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>&lt;p>I hope that helps. Please let me know if I screwed anything up.&lt;/p></description></item><item><title>About</title><link>http://g.raphaelli.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://g.raphaelli.com/about/</guid><description>&lt;p>Hey, I&amp;rsquo;m Gil. I&amp;rsquo;m &lt;code>graphaelli&lt;/code> on most services including gmail, linkedin, github, and gitlab.&lt;/p></description></item></channel></rss>