<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sergey Ponomarev</title>
    <description>The latest articles on DEV Community by Sergey Ponomarev (@sponomarev).</description>
    <link>https://dev.to/sponomarev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F73949%2Fd29d1746-dbd8-4f80-893b-6c1e6cb92e44.jpeg</url>
      <title>DEV Community: Sergey Ponomarev</title>
      <link>https://dev.to/sponomarev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sponomarev"/>
    <language>en</language>
    <item>
      <title>Speeding up Go Modules for Docker and CI</title>
      <dc:creator>Sergey Ponomarev</dc:creator>
      <pubDate>Mon, 05 Aug 2019 15:19:53 +0000</pubDate>
      <link>https://dev.to/evilmartians/speeding-up-go-modules-for-docker-and-ci-4ebe</link>
      <guid>https://dev.to/evilmartians/speeding-up-go-modules-for-docker-and-ci-4ebe</guid>
      <description>&lt;p&gt;Finally, the Golang world has a built-in, conventional dependency manager in the ecosystem: Go Modules. What began in Go 1.11 as an opt-in feature has become widely adopted by the community, and we are so close to Go 1.13 when Go Modules will be enabled by default. The delightful dilemma of choosing the “best” tool can be finally resolved.&lt;/p&gt;

&lt;p&gt;I can’t help but mention two features which are very close to my heart:&lt;/p&gt;

&lt;p&gt;– No more &lt;code&gt;$GOPATH&lt;/code&gt; imprisonment! In my years of experience, I’d gotten used to storing everything I work on in &lt;code&gt;~/Projects/&lt;/code&gt; and its subfolders somewhere in the home directory, no matter the programming language. So, being forced to keep my Golang stuff in another specific place and respect SCM url in the path was a real pain, and made routine &lt;code&gt;cd&lt;/code&gt; operations feel like such a chore. No longer an issue!&lt;br&gt;
– No more vendoring! Dependency updates don’t produce enormous PR diffs to read, and repositories are lighter. I can just remove the &lt;code&gt;vendor&lt;/code&gt; folder from my source code and forget about it.&lt;/p&gt;

&lt;p&gt;The migration to Go Modules is &lt;a href="https://github.com/golang/go/wiki/Modules#how-to-use-modules"&gt;pretty simple&lt;/a&gt; and won’t take more than a couple of minutes, especially if you use &lt;a href="https://tip.golang.org/pkg/cmd/go/internal/modconv/?m=all#pkg-variables"&gt;any of the supported package managers&lt;/a&gt; to migrate from.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go mod init
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;vendor/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; ./...
&lt;span class="nv"&gt;$ &lt;/span&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s pretty much it!&lt;/p&gt;

&lt;p&gt;With Go Modules your dependencies are not a part of your source code anymore. The toolchain downloads them on its own, keeps modules up-to-date, and caches them locally in &lt;code&gt;$GOPATH/pkg/mod&lt;/code&gt; for future use. That sounds perfect for when all of your processes occur in a stateful environment like a laptop, but what about stateless builds in your CI pipeline or Docker? Every now and again Go will download every item in your dependencies and waste your priceless time. Let’s fix that with some caching!&lt;/p&gt;

&lt;h1&gt;
  
  
  Caching on CI
&lt;/h1&gt;

&lt;p&gt;It’s such a common situation to cache dependencies between builds on CI that some of the services provide a simplified, ecosystem-specific syntax to make it easier. Alas, I haven’t found specific Go Modules caching on popular CIs yet, so let’s do it manually.&lt;/p&gt;

&lt;p&gt;If you use TravisCI, it’s very straightforward. Just add those lines to your &lt;code&gt;.travis.yml&lt;/code&gt; config and you’re all set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;directories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$GOPATH/pkg/mod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Setting up dependency caching on my favorite CircleCI is a little more verbose. Wrap &lt;code&gt;go mod download&lt;/code&gt; or your build step in the code below. Golang will take care of the missing dependencies and CircleCI will cache them between builds relying on the content of the &lt;code&gt;go.sum&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;restore_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go-modules-v1-{{ checksum "go.sum" }}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go-modules-v1&lt;/span&gt;
      &lt;span class="c1"&gt;# get dependencies here with `go mod download` or implicitly &lt;/span&gt;
      &lt;span class="c1"&gt;# with `go build` or `go test`&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;save_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-modules-v1-{{ checksum "go.sum" }}&lt;/span&gt;
          &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/go/pkg/mod"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here are the results of boosting of my little project on CircleCI:&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`go test ./...` =&amp;gt; 00:20s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After cache warm-up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Restoring Cache =&amp;gt; 00:03s
`go test ./...` =&amp;gt; 00:06s
Saving Cache    =&amp;gt; 00:00s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Not bad at all: 2x faster CI build, and for free.&lt;/p&gt;

&lt;h1&gt;
  
  
  Caching in Docker
&lt;/h1&gt;

&lt;p&gt;There are two completely different use cases for how we use Docker: for the development process to isolate an application and its environment, and for packing production builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Development
&lt;/h2&gt;

&lt;p&gt;If you follow Test Driven Development (TDD) caching, Go Modules can significantly increase your development productivity. You definitely know how crucial it is to have as fast a test suite as possible.&lt;/p&gt;

&lt;p&gt;Using Docker Compose, cache your modules in a separate volume, and see the performance boost. I saved 20 seconds. Not that bad for a small change!&lt;/p&gt;

&lt;p&gt;Here is a minimal &lt;code&gt;docker-compose.yml&lt;/code&gt;, simplified for the sake of brevity, with highlighted volumes changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application:0.0.1-development&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/development/Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go-modules:/go/pkg/mod&lt;/span&gt; &lt;span class="c1"&gt;# Put modules cache into a separate volume&lt;/span&gt;
  &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*app&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go test ./...&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;go-modules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Define the volume&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  In production
&lt;/h2&gt;

&lt;p&gt;For production builds, we can take advantage of the &lt;a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache"&gt;layer caching&lt;/a&gt; power. Dependencies change less often than the code itself—let’s make it a separate step in your &lt;code&gt;Dockerfile&lt;/code&gt; before the build phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# `FROM` and other prerequisites here skipped for the sake of brevity&lt;/span&gt;

&lt;span class="c"&gt;# Copy `go.mod` for definitions and `go.sum` to invalidate the next layer&lt;/span&gt;
&lt;span class="c"&gt;# in case of a change in the dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="c"&gt;# Download dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download

&lt;span class="c"&gt;# `RUN go build ...` and further steps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  In a nutshell
&lt;/h1&gt;

&lt;p&gt;Introducing Go Modules was an exciting moment and a significant relief to the Golang community. It brought us a lot of excellent features we had been waiting a long time for. Don’t hesitate to try Modules if you haven’t yet. It’s pretty easy to migrate to it, but don’t forget to change your CI or Docker settings to avoid downloading overhead and keep your builds blazing fast.&lt;/p&gt;

</description>
      <category>go</category>
      <category>modules</category>
      <category>docker</category>
      <category>ci</category>
    </item>
  </channel>
</rss>
