<?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>Forem: Thao Nguyen</title>
    <description>The latest articles on Forem by Thao Nguyen (@thaonx).</description>
    <link>https://forem.com/thaonx</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%2F1146246%2F62344e04-0c87-4dd8-a651-eb9e4b168dc8.png</url>
      <title>Forem: Thao Nguyen</title>
      <link>https://forem.com/thaonx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thaonx"/>
    <language>en</language>
    <item>
      <title>[Go Tour ] 3. Hot-reload</title>
      <dc:creator>Thao Nguyen</dc:creator>
      <pubDate>Tue, 19 Dec 2023 13:57:29 +0000</pubDate>
      <link>https://forem.com/thaonx/go-tour-3-setup-hot-reload-for-a-golang-application-53c7</link>
      <guid>https://forem.com/thaonx/go-tour-3-setup-hot-reload-for-a-golang-application-53c7</guid>
      <description>&lt;h2&gt;
  
  
  1. What is Hot-reload?
&lt;/h2&gt;

&lt;p&gt;Hot-reload or also known as Auto-reload, It’s like when you make a change to the source code and it automatically takes effect without  a manual rebuild or restart. It helps to eliminate those repetitive and tedious tasks.. It saves and increase productivity.&lt;br&gt;
The traditional technique to achieve hot-reload in Golang is to install a third-party tools or libraries libraries such as Air, Fresh, …etc. However, in this section, we will explore how to configure hot-reload without any third-party tools neither nor libraries. That will use the built-in &lt;code&gt;compose watch&lt;/code&gt; a new feature of Docker Compose v2.22.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Docker Compose Watch (v2.22)
&lt;/h2&gt;

&lt;p&gt;Docker Compose Watch is a new feature of Docker Compose v2.22. It allows us to watch the changes of the source code and automatically rebuild the image. It’s like a hot-reload feature for Docker Compose. It’s very useful for the local development environment. It helps to eliminate the repetitive and tedious tasks of rebuilding the image and restarting the container. It saves and increase productivity. &lt;a href="https://docs.docker.com/compose/file-watch/"&gt;https://docs.docker.com/compose/file-watch/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compose Watch versus bind mounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compose supports sharing a host directory inside service containers. Watch mode does not replace this functionality but exists as a companion specifically suited to developing in containers.

More importantly, watch allows for greater granularity than is practical with a bind mount. Watch rules let you ignore specific files or entire directories within the watched tree.

For example, in a JavaScript project, ignoring the node_modules/ directory has two benefits:

Performance. File trees with many small files can cause high I/O load in some configurations
Multi-platform. Compiled artifacts cannot be shared if the host OS or architecture is different to the container
For example, in a Node.js project, it's not recommended to sync the node_modules/ directory. Even though JavaScript is interpreted, npm packages can contain native code that is not portable across platforms.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Auto-reload using Docker Compose Watch
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;--- a/gotour/3.setup-hot-reload/compose.yaml
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/gotour/3.setup-hot-reload/compose.yaml
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,7 +1,6 @@&lt;/span&gt;
 version: '3.7'
&lt;span class="err"&gt;
&lt;/span&gt; services:
&lt;span class="gd"&gt;-
&lt;/span&gt;   basic-svc:
     build:
       context: ./basic-svc
&lt;span class="p"&gt;@@ -9,6 +8,10 @@&lt;/span&gt; services:
       target: ${DOCKER_BUILD_TARGET:-dev}
     ports:
     - 8080:8080
&lt;span class="gi"&gt;+    develop:
+      watch:
+        - action: rebuild
+          path: ./basic-svc
&lt;/span&gt;   basic-ui:
     build: 
       context: ./basic-ui
&lt;span class="p"&gt;@@ -16,3 +19,12 @@&lt;/span&gt; services:
       target: ${DOCKER_BUILD_TARGET:-dev}
     ports:
       - 3000:3000
&lt;span class="gi"&gt;+    develop:
+      watch:
+        - action: sync
+          path: ./basic-ui
+          target: /src
+          exclude:
+            - node_modules
+        - action: rebuild
+          path: ./basic-ui/Dockerfile
&lt;/span&gt;\ No newline at end of file
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For the &lt;code&gt;basic-svc&lt;/code&gt; service, we use the &lt;code&gt;action: rebuild&lt;/code&gt; to rebuild the image when the source code changes. As outlined in the prior article, If the Dockerfile is optimized, the rebuild process should be fast. &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/thaonx" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9v9nlEzK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--iNtk0jpQ--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/1146246/62344e04-0c87-4dd8-a651-eb9e4b168dc8.png" alt="thaonx"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/thaonx/go-tour-2-optimize-dockerfile-1noc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;[Go Tour ] 2. Optimize Dockerfile&lt;/h2&gt;
      &lt;h3&gt;Thao Nguyen ・ Dec 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#go&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dockerfile&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#optimize&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
For the &lt;code&gt;basic-ui&lt;/code&gt; service, we use the &lt;code&gt;action: sync&lt;/code&gt; to sync the source code to the container when the source code changes (the auto-reload frontend application will be handled by the frontend framework).&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Conclusion
&lt;/h2&gt;

&lt;p&gt;There are several ways to do hot-reload in Golang. In this article, we delved into to configuring hot-reload without relying on any third-party tools neither nor libraries. This approach will keep the Dockerfile simple and clean, facilitating a streamlined development environment.&lt;/p&gt;

</description>
      <category>go</category>
      <category>docker</category>
      <category>development</category>
    </item>
    <item>
      <title>[Go Tour ] 2. Optimize Dockerfile</title>
      <dc:creator>Thao Nguyen</dc:creator>
      <pubDate>Sat, 16 Dec 2023 05:27:31 +0000</pubDate>
      <link>https://forem.com/thaonx/go-tour-2-optimize-dockerfile-1noc</link>
      <guid>https://forem.com/thaonx/go-tour-2-optimize-dockerfile-1noc</guid>
      <description>&lt;h2&gt;
  
  
  Simple Dockerfile
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:1.21.3-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/myapp

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["myapp"]&lt;/span&gt;

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

&lt;/div&gt;

&lt;h2&gt;
  
  
  1. Identified Issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.1. Image size:&lt;/strong&gt;&lt;br&gt;
    The &lt;code&gt;golang:1.21.3-alpine&lt;/code&gt; base image carries a lot of stuff that make the image unnecessarily big. Since Golang is a compiled language, having the compiler and its dependencies in the final image is redundant.&lt;br&gt;
&lt;u&gt;Taking a simple Hello World sample for a instance, the image size amounts to &lt;b&gt; 327MB. &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2. Security Concerns:&lt;/strong&gt;&lt;br&gt;
    We cannot not entirely ensure what is up in the &lt;code&gt;golang:1.21.3-alpine&lt;/code&gt; image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.3. Inefficiency:&lt;/strong&gt;&lt;br&gt;
    Despite Docker's layer caching, the &lt;code&gt;COPY . .&lt;/code&gt; instruction invalidates the cache for all the subsequent changes. Consequently, any modification will result in downloading all the dependencies and full recompilation. the simple Hello World sample image would approximately take 10 seconds to build.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
#9 [5/5] RUN go build -v -o /usr/local/bin/myapp
#9 0.503 go: downloading gorm.io/gorm v1.25.5
#9 0.505 go: downloading gorm.io/driver/sqlite v1.5.4
#9 0.723 go: downloading github.com/mattn/go-sqlite3 v1.14.17
#9 0.913 go: downloading github.com/jinzhu/now v1.1.5
#9 0.913 go: downloading github.com/jinzhu/inflection v1.0.0
#9 1.182 internal/goos
#9 1.182 internal/itoa
#9 1.182 internal/godebugs
...
#9 8.840 gotour/example
#9 DONE 9.6s
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  2. Optimizations
&lt;/h2&gt;
&lt;h3&gt;
  
  
  2.1. Use a multi-stage build to reduce the image size and improve security.
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;#----- Builder stage -----#&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.21.3-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# download dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;go.mod,target&lt;span class="o"&gt;=&lt;/span&gt;go.mod &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;go.sum,target&lt;span class="o"&gt;=&lt;/span&gt;go.sum &lt;span class="se"&gt;\
&lt;/span&gt;    go mod download &lt;span class="nt"&gt;-x&lt;/span&gt;

&lt;span class="c"&gt;# compile go binary&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;.,target&lt;span class="o"&gt;=&lt;/span&gt;.&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\
&lt;/span&gt;    go build &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/myapp ./

&lt;span class="c"&gt;#----- Runtime stage -----#&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;gcr.io/distroless/static-debian11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /usr/local/bin/myapp /usr/local/bin/myapp&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["myapp"]&lt;/span&gt;

&lt;span class="c"&gt;#----- Dev stage -----#&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;

&lt;span class="c"&gt;#----- Prod stage -----#&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, we split the Dockerfile into two main stages: &lt;code&gt;builder&lt;/code&gt; and &lt;code&gt;runner&lt;/code&gt;. The &lt;code&gt;builder&lt;/code&gt; is responsible for compiling the Go binary and the &lt;code&gt;runner&lt;/code&gt; is responsible for running the binary. The &lt;code&gt;runner&lt;/code&gt; uses the &lt;code&gt;distroless/static-debian11&lt;/code&gt; image as its base image which is a minimal image that contains only the necessary dependencies to run a statically compiled binary.&lt;br&gt;
&lt;u&gt;The image size is now reduced to &lt;b&gt; 13.64MB. &lt;/b&gt;&lt;br&gt;
&lt;/u&gt;&lt;br&gt;
&lt;strong&gt;Second&lt;/strong&gt;, we seperate the download of dependencies and the compilation of the binary into two separate steps. Most of the time, the dependencies do not change as often as the source code. Therefore, we can only invalidate the docker layer cache when the dependencies change. This will save us a lot of time when building the image.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# download dependencies
RUN --mount=type=bind,source=go.mod,target=go.mod \
    --mount=type=bind,source=go.sum,target=go.sum \
    go mod download -x

# compile go binary
RUN --mount=type=bind,source=.,target=.\
    CGO_ENABLED=0 \
    go build -o /usr/local/bin/myapp ./
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2.2. Use docker dedicated RUN cache
&lt;/h3&gt;

&lt;p&gt;However, a drawback arises when changing dependencies (go.mod), as Docker tends to redownload all dependencies and recompile the binary, leading to inefficiency.&lt;br&gt;
In prior article: &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/thaonx" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1146246%2F62344e04-0c87-4dd8-a651-eb9e4b168dc8.png" alt="thaonx"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/thaonx/go-tours-1-exploring-the-go-build-command-omd" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;[Go Tour] 1. Exploring the `go build` Command&lt;/h2&gt;
      &lt;h3&gt;Thao Nguyen ・ Dec 12 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#go&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#explore&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
I introduced how go buildkit caches the downloaded dependencies and compiled binaries. To capitalize on this feature, we can utilize Docker's dedicated RUN cache. The Dockerfile is as follows:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# download dependencies
&lt;span class="gd"&gt;-RUN --mount=type=bind,source=go.mod,target=go.mod \
&lt;/span&gt;&lt;span class="gi"&gt;+RUN \
+   --mount=type=cache,target=/go/pkg/mod \
+   --mount=type=bind,source=go.mod,target=go.mod \
&lt;/span&gt;    --mount=type=bind,source=go.sum,target=go.sum \
    go mod download -x
&lt;span class="err"&gt;
&lt;/span&gt;# compile go binary
&lt;span class="gd"&gt;-RUN --mount=type=bind,source=.,target=.\
&lt;/span&gt;&lt;span class="gi"&gt;+RUN \
+   --mount=type=cache,target=/go/pkg/mod \
+   --mount=type=cache,target=/root/.cache/go-build \
+   --mount=type=bind,source=.,target=.\
&lt;/span&gt;    CGO_ENABLED=0 \
    go build -v -o /usr/local/bin/myapp ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;code&gt;--mount=type=cache,target=/go/pkg/mod&lt;/code&gt; and &lt;code&gt;--mount=type=cache,target=/root/.cache/go-build&lt;/code&gt; to the &lt;code&gt;RUN&lt;/code&gt; command, we can use docker dedicated RUN cache to cache the dependencies and the compiled binary. Everytimes docker &lt;code&gt;BuilderKit&lt;/code&gt; run the &lt;code&gt;RUN&lt;/code&gt; command, it will mount the cache to the specified target. This will leverage the artifacts from the previous build and save us a lot of time.&lt;br&gt;
For instance, I made a change into &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;repository&lt;/code&gt; package and re-build the image. The output is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;9 &lt;span class="o"&gt;[&lt;/span&gt;builder 3/4] RUN     &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/go/pkg/mod     &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;go.mod,target&lt;span class="o"&gt;=&lt;/span&gt;go.mod     &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;go.sum,target&lt;span class="o"&gt;=&lt;/span&gt;go.sum     go mod download &lt;span class="nt"&gt;-x&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;9 DONE 0.3s
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;10 &lt;span class="o"&gt;[&lt;/span&gt;builder 4/4] RUN     &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/go/pkg/mod     &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.cache/go-build     &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;.,target&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;     &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0     go build  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/myapp ./
&lt;span class="gp"&gt;#&lt;/span&gt;10 0.556 gotour/example/internal/repository
&lt;span class="gp"&gt;#&lt;/span&gt;10 0.587 gotour/example/internal/service
&lt;span class="gp"&gt;#&lt;/span&gt;10 0.597 gotour/example
&lt;span class="gp"&gt;#&lt;/span&gt;10 DONE 1.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This log discloses that the docker &lt;code&gt;BuilderKit&lt;/code&gt; re-executed &lt;code&gt;go mod download&lt;/code&gt; command but it was exceptionally fast as the go mod reused the downloaded dependencies from the cache. Similarly, the &lt;code&gt;go build&lt;/code&gt; command was also re-executed and remarkably swift, as it only recompiled three packages (&lt;code&gt;repository&lt;/code&gt;, &lt;code&gt;service&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt;) instead of the whole project along with all Go standard libraries.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3. Multi platforms build (Bonus)
&lt;/h4&gt;

&lt;p&gt;We can also use docker multi platform build to build the image for different platforms. For instance, we can build the image for &lt;code&gt;linux/amd64&lt;/code&gt; and &lt;code&gt;linux/arm64&lt;/code&gt; platforms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;#----- Builder stage -----#
&lt;span class="gd"&gt;-FROM golang:1.21.3-alpine as builder
&lt;/span&gt;&lt;span class="gi"&gt;+FROM --platform=${BUILDPLATFORM} golang:1.21.3-alpine as builder
+ARG TARGETOS
+ARG TARGETARCH
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;RUN \
&lt;/span&gt;   --mount=type=cache,target=/go/pkg/mod \
   --mount=type=cache,target=/root/.cache/go-build \
   --mount=type=bind,source=.,target=.\
    CGO_ENABLED=0 \
&lt;span class="gi"&gt;+   GOOS=${TARGETOS} \
+   GOARCH=${TARGETARCH} \
&lt;/span&gt;    go build -v -o /app/bin/main .
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Conclusion
&lt;/h2&gt;

&lt;p&gt;In this section, we have explored how to optimize the Dockerfile for a golang application. Key takeaways include leveraging Docker's multi-stage build to diminish image size (from &amp;gt;300Mb to 13Mb) and enhance security. Additionally, We delved into utilizing Docker's dedicated RUN cache to speed up the build process. However, this technique is primarily effective on the Local Development Environment. For the CI/CD pipeline, we need a more advanced technique to enable the cache across separate builds.&lt;/p&gt;

&lt;p&gt;In the upcoming section, we will delve into configuring hot-reload in local development environment for a Golang application.&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;

</description>
      <category>go</category>
      <category>docker</category>
      <category>dockerfile</category>
      <category>optimize</category>
    </item>
    <item>
      <title>[Go Tour] 1. Exploring the `go build` Command</title>
      <dc:creator>Thao Nguyen</dc:creator>
      <pubDate>Tue, 12 Dec 2023 08:59:17 +0000</pubDate>
      <link>https://forem.com/thaonx/go-tours-1-exploring-the-go-build-command-omd</link>
      <guid>https://forem.com/thaonx/go-tours-1-exploring-the-go-build-command-omd</guid>
      <description>&lt;h2&gt;
  
  
  First execution of &lt;code&gt;go build&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's start with a basic Go application&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R6AKD6Fm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62rfnpms4qjghoplyol0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R6AKD6Fm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62rfnpms4qjghoplyol0.png" alt="basic golang application" width="800" height="715"&gt;&lt;/a&gt;&lt;br&gt;
in the &lt;code&gt;go build&lt;/code&gt; command, you can use the &lt;code&gt;-v&lt;/code&gt; option to display the names of packages as they are compiled:&lt;br&gt;
&lt;code&gt;CGO_ENABLED=0 go build -v -o /tmp/basic-svc ./&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ CGO_ENABLED=0 go build -v -o /tmp/basic-svc ./
go: downloading gorm.io/gorm v1.25.5
go: downloading gorm.io/driver/sqlite v1.5.4
go: downloading github.com/mattn/go-sqlite3 v1.14.18
go: downloading github.com/jinzhu/now v1.1.5
go: downloading github.com/jinzhu/inflection v1.0.0
internal/goexperiment
internal/godebugs
internal/unsafeheader
internal/coverage/rtcov
internal/goarch
internal/goos
unicode/utf8
math/bits
internal/cpu
runtime/internal/atomic
internal/itoa
runtime/internal/syscall
internal/race
sync/atomic
unicode
encoding
unicode/utf16
internal/abi
runtime/internal/math
runtime/internal/sys
crypto/internal/alias
crypto/subtle
crypto/internal/boring/sig
strings
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon the first execution of &lt;code&gt;go build&lt;/code&gt;, some packages are downloaded from internet. And a bunch of package, including many Go standard libraries are compiled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching in &lt;code&gt;go build&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The downloaded packages are stored in &lt;code&gt;~/go/pkg/mod/*&lt;/code&gt;, and the compiled binaries generated by &lt;code&gt;go build&lt;/code&gt; reside in &lt;code&gt;~/.cache/go-build/*&lt;/code&gt;. Keeping the source code unchanged, and execute the go build command again, no packages are recompiled as it efficiently utilizes the cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ CGO_ENABLED=0 go build -v ./
$ 
$ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compilation of Dependencies
&lt;/h2&gt;

&lt;p&gt;In this example, the dependency hierarchy: the &lt;code&gt;main&lt;/code&gt; imports the &lt;code&gt;service&lt;/code&gt;, the &lt;code&gt;service&lt;/code&gt; imports the &lt;code&gt;repository&lt;/code&gt;.&lt;br&gt;
When changes are made to the repository package, executing &lt;code&gt;go build&lt;/code&gt; subsequently triggers recompilation of three packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ CGO_ENABLED=0 go build -v -o /tmp/basic-svc ./
openlab1/basic-svc/internal/repository
openlab1/basic-svc/internal/service
openlab1/basic-svc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go builder detects which packages need to be re-compiled. It avoid unnecessary recompilation.&lt;/li&gt;
&lt;li&gt;During the first execution of go build, all the necessary packages are downloaded (if they do not exist) and subsequently compiled.&lt;/li&gt;
&lt;li&gt;This feature can be leveraged to optimize a Dockerfile specifically for a Golang application (details in the next post)&lt;/li&gt;
&lt;li&gt;Maintaining small and independent Go packages helps prevent unnecessary recompilations. (details in the next post)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>explore</category>
    </item>
  </channel>
</rss>
