<?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: James D.</title>
    <description>The latest articles on Forem by James D. (@jamesatintegratnio).</description>
    <link>https://forem.com/jamesatintegratnio</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%2F671786%2F54663674-00b3-4b0c-9fe8-ebd23409e292.jpeg</url>
      <title>Forem: James D.</title>
      <link>https://forem.com/jamesatintegratnio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jamesatintegratnio"/>
    <language>en</language>
    <item>
      <title>Multi Stage Builds in Docker</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Tue, 03 Aug 2021 02:12:26 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/multi-stage-builds-in-docker-2265</link>
      <guid>https://forem.com/jamesatintegratnio/multi-stage-builds-in-docker-2265</guid>
      <description>&lt;h1&gt;
  
  
  Multi Stage Builds
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Back Story
&lt;/h2&gt;

&lt;p&gt;So way back in the beginning of this we briefly discussed layers. Each line in a dockerfile adds a layer. Which means it adds size to the dockerfile.&lt;/p&gt;

&lt;p&gt;Lets look at when we added git to our first image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its just git. No big deal right? It can't take that much space. But lets look at all the extra things that happened to add git. We ran &lt;code&gt;apk update&lt;/code&gt;. This meas we reached out to the Alpine repos and fetched the basic details for every package in the apk repositories and saved them into our image. Then we did &lt;code&gt;apk add --no-cache git&lt;/code&gt;. So we fetched the git package and any of its dependent packages and stashed them in our image so that we can install them. Then the installer unpacks those packages and installs the files where they are needed. Thats a lot, and thats only adding git.&lt;/p&gt;

&lt;p&gt;So you have all these dependencies that you need to build your base image. Git, the dependencies to build your application, the compiler itself, and a bunch of other junk. We don't need all that in production. We really don't want all of that in production. Your dev dependencies are extra attack vectors. You aren't going to be pushing to git from your production container. So how do we get rid of all of that?&lt;/p&gt;

&lt;h2&gt;
  
  
  About Multi Stage Builds
&lt;/h2&gt;

&lt;p&gt;This is where &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/"&gt;Multi Stage builds&lt;/a&gt; come in. Before Multi Stage Builds, you would have to do a bunch of funny shell tricks to clean up every layer as you used it just to keep the artifacts that you needed and get rid of everything else. Now with Multi Stage Builds you can declare the artifacts you want in each image. So lets start with an example&lt;br&gt;
&lt;/p&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:alpine as builder&lt;/span&gt;
&lt;span class="c"&gt;# Install git.&lt;/span&gt;
&lt;span class="c"&gt;# Git is required for fetching the dependencies.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $GOPATH/src/integratnio/go-rest-api/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go-rest-api .&lt;/span&gt;
&lt;span class="c"&gt;# Fetch dependencies using go get.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# Build the binary.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go/bin/hello

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:alpine&lt;/span&gt;
&lt;span class="c"&gt;# Copy our static executable from the builder.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /go/bin/hello /go/bin/hello&lt;/span&gt;
&lt;span class="c"&gt;# Run the binary.&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/go/bin/hello"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright. Looks good right? So far this is pretty simple. We used the same base image but we only copied our binary from the first image into the second. We could also use a different base image if we wanted. This is a real simple app with no external dependencies. So the fat trimmed is minimal. But pattern still helped us save some space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY          TAG    IMAGE ID       CREATED          SIZE
helloFromIntegratnIO    dev    285c3af572b0   5 seconds ago    321MB
helloFromIntegratnIO    prod   0c2fb67c8edc   14 seconds ago   306MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just look at those savings. But we can go deeper, and we will in lesson 7. &lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQj764Ae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Developing Inside A Docker Container</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Thu, 29 Jul 2021 23:28:54 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/developing-inside-a-docker-container-3ofd</link>
      <guid>https://forem.com/jamesatintegratnio/developing-inside-a-docker-container-3ofd</guid>
      <description>&lt;p&gt;Now we are going to start getting into the weeds of leveraging docker. I think an easy way to start this one would be with a user story. So here we go.&lt;/p&gt;

&lt;p&gt;As a developer I would like to have an environment to develop that alleviates the "Works on my box" problem. It should work just the same on my box as it does in production. I shouldn't be able to install something in my environment that doesn't exist when I deploy my app to production. Any other developer should be able to pick up my app and develop the exact same way without a huge time sink spent getting their environment to look just like mine.&lt;/p&gt;

&lt;p&gt;Pipe dreams, am i right?&lt;/p&gt;

&lt;p&gt;Lets see how we can pull this off with docker.&lt;/p&gt;

&lt;p&gt;Most languages these days support some sort of live reload to allow developers to instantly make a change and have it running live for them to play with. So lets start out by adding that functionality to our little API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running The Container
&lt;/h2&gt;

&lt;p&gt;Running this container so that we can actually work inside it with our editor is going to take a bit more than we've done before. We are actually going to leverage an image we haven't built ourselves this time. Lets take look at the image we are going to use. &lt;a href="https://hub.docker.com/r/cosmtrek/air" rel="noopener noreferrer"&gt;cosmtrek/air&lt;/a&gt;. Please take a minute to explore the image link if you haven't explored Dockerhub before. The &lt;code&gt;Overview&lt;/code&gt; tab will give you a rundown of how to use the image. The &lt;code&gt;Tags&lt;/code&gt; tab will show you different tags you can consume. So if we scroll down on the overview you will see how to run it &lt;code&gt;The Docker Way&lt;/code&gt;. So lets do that. &lt;/p&gt;

&lt;p&gt;First we need to pull the image down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull cosmtrek/air
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we wanted to consume a specific tag then we could do that as well. Just make sure you also use that tag in the following docker run command.&lt;/p&gt;

&lt;p&gt;Lets add a little config for air so that it will cleanup for us when its done. In &lt;code&gt;$projectRoot/go-rest-api/&lt;/code&gt;, lets add &lt;code&gt;.air.conf&lt;/code&gt; and fill it with the following then save.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .air.conf&lt;/span&gt;
&lt;span class="c"&gt;# Config file for [Air](https://github.com/cosmtrek/air) in TOML format&lt;/span&gt;

&lt;span class="c"&gt;# Working directory&lt;/span&gt;
&lt;span class="c"&gt;# . or absolute path, please note that the directories following must be under root.&lt;/span&gt;
root &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt; 
tmp_dir &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tmp"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;build]
&lt;span class="c"&gt;# It's not necessary to trigger build each time file changes if it's too frequent.&lt;/span&gt;
delay &lt;span class="o"&gt;=&lt;/span&gt; 1000 &lt;span class="c"&gt;# ms&lt;/span&gt;
&lt;span class="c"&gt;# Stop to run old binary when build errors occur.&lt;/span&gt;
stop_on_error &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;color]
&lt;span class="c"&gt;# Customize each part's color. If no color found, use the raw app log.&lt;/span&gt;
main &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"magenta"&lt;/span&gt;
watcher &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cyan"&lt;/span&gt;
build &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"yellow"&lt;/span&gt;
runner &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"green"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;misc]
&lt;span class="c"&gt;# Delete tmp directory on exit&lt;/span&gt;
clean_on_exit &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets run this image and load our code into on the fly&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-w&lt;/span&gt; /app/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/go-rest-api:/app/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:10000 &lt;span class="se"&gt;\&lt;/span&gt;
    cosmtrek/air &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-c&lt;/span&gt; .air.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Theres a lot going on there. So lets break it down.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker run&lt;/code&gt;: We've seen this before. Just the base command to run a container&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-it&lt;/code&gt;: This is the shorthand way to declare &lt;code&gt;--interactive --tty&lt;/code&gt; that we discussed earlier&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--rm&lt;/code&gt;: This will cleanup the filesystem that our container creates. Normally this persists. But we really don't want that happening. Whenever we start this we want make sure its fresh with just the code we are working with.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-w&lt;/code&gt;: Just like in our Dockerfile. This lets us declare &lt;code&gt;WORKDIR&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-v&lt;/code&gt;: This is short for &lt;code&gt;--volume&lt;/code&gt; This will mount our &lt;code&gt;$projectRoot/go-rest-api&lt;/code&gt; at &lt;code&gt;/app&lt;/code&gt; inside the container. the &lt;code&gt;pwd\&lt;/code&gt; is just a little shorthand to get our present working directory. Otherwise we would have to declare the full path of what we want to mount inside the container. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;-p 8080:10000&lt;/code&gt; Port mapping just like before.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cosmtrek/air&lt;/code&gt; Declaring the image we want to use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-c .air.conf&lt;/code&gt; Because we declared &lt;code&gt;-it&lt;/code&gt; we are able to pass parameters to &lt;code&gt;ENTRYPOINT&lt;/code&gt; of the container. We are leveraging this to pass in the configuration file we created.&lt;/p&gt;

&lt;p&gt;You should have gotten an output like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-w&lt;/span&gt; /app/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/go-rest-api:/app/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:10000 &lt;span class="se"&gt;\&lt;/span&gt;
    cosmtrek/air &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-c&lt;/span&gt; .air.conf

  __    _   ___  
 / /&lt;span class="se"&gt;\ &lt;/span&gt; | | | |_&lt;span class="o"&gt;)&lt;/span&gt; 
/_/--&lt;span class="se"&gt;\ &lt;/span&gt;|_| |_| &lt;span class="se"&gt;\_&lt;/span&gt; // live reload &lt;span class="k"&gt;for &lt;/span&gt;Go apps &lt;span class="o"&gt;[&lt;/span&gt;v1.11.1]

&lt;span class="nb"&gt;mkdir&lt;/span&gt; /app/tmp
watching &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;!&lt;/span&gt;exclude tmp
building...
running...
Starting Web Server
Preparing to handle requests
Ready &lt;span class="k"&gt;for &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So looking at this right off the bat. We see that it spun up &lt;code&gt;v1.11.1&lt;/code&gt;. Thats old. Lets do this again using a version tag to run a newer version. (If you are doing this later. You may have gotten a different version. But at the time of writing. It looks like new images are being pushed but &lt;code&gt;latest&lt;/code&gt; isn't being updated)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it --rm\
    -e GO111MODULE=off
    -w /app/ \
    -v `pwd`/go-rest-api:/app/ \
    -p 8080:10000 \
    cosmtrek/air:v1.15.1 \
    -c .air.conf

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

&lt;/div&gt;



&lt;p&gt;You'll notice this time. We skipped the pull step. Yay Shortcuts. There was one other little thing we added.&lt;br&gt;
&lt;code&gt;-e GO111MODULE=off&lt;/code&gt;: GO111MODULE in its most simplest terms tells go whether to look for and require go modules. Getting into all of that really isn't the point of this. But if you are curious, this &lt;a href="https://dev.to/maelvls/why-is-go111module-everywhere-and-everything-about-go-modules-24k"&gt;blog&lt;/a&gt; was a great read.&lt;/p&gt;

&lt;p&gt;But we are running again. And look. Our go version is more recent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -it --rm\
    -e GO111MODULE=off \
    -w /app/ \
    -v `pwd`/go-rest-api:/app/ \
    -p 8080:10000 \
    cosmtrek/air:v1.15.1 \
    -c .air.conf

  __    _   ___  
 / /\  | | | |_) 
/_/--\ |_| |_| \_ v1.15.1 // live reload for Go apps, with Go 1.15.5

mkdir /app/tmp
watching .
!exclude tmp
building...
running...
Starting Web Server
Preparing to handle requests
Ready for requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So lets see if our curl still works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
Hello From integratnio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great the app is still working. Now lets make a code change and watch the live reload do its thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a change
&lt;/h2&gt;

&lt;p&gt;Open up &lt;code&gt;$projectRoot/go-rest-api/main.go&lt;/code&gt;. On line 11 you will see &lt;code&gt;message&lt;/code&gt; declared. Lets change the value of that line and save the file. Feel free to change it to anything you want. But I'm going to change it to &lt;code&gt;Yay we did it&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Watch the terminal running your dev container when you hit save.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;main.go has changed
building...
running...
Starting Web Server
Preparing to handle requests
Ready &lt;span class="k"&gt;for &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Magic.&lt;/p&gt;

&lt;p&gt;Lets test that curl request again.&lt;br&gt;
Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
Yay We Did It
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at that. This is just one example of how to do this. For a node app you could use &lt;code&gt;npm run dev:watch&lt;/code&gt; as your entrypoint and get a similar effect. Pick your code flavor and I bet you could figure out a way to do this.&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>go</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker Training 4 - Managing The Build Context</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Wed, 28 Jul 2021 13:22:57 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/docker-training-4-managing-the-build-context-58pa</link>
      <guid>https://forem.com/jamesatintegratnio/docker-training-4-managing-the-build-context-58pa</guid>
      <description>&lt;p&gt;So far we have created a small api that runs in a container. We've tested that its working. Worked with environment variables inside the container on the &lt;code&gt;docker run&lt;/code&gt; command. Accessed a shell inside the container with the &lt;code&gt;docker exec&lt;/code&gt; command. Now I want to talk about the docker build context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Build Context
&lt;/h2&gt;

&lt;p&gt;The most common way to work with the build context is to build an image based on our current path. How we do &lt;code&gt;docker build . -t repository:version&lt;/code&gt; We are doing this with a relatively small app. So we haven't gone over a few things that you will see in a larger project. One of the first things you will see in a larger project is a &lt;a href="https://docs.docker.com/engine/reference/builder/#dockerignore-file"&gt;.dockerignore&lt;/a&gt; file. This is a lot like your &lt;code&gt;.gitignore&lt;/code&gt;. We don't need every file in our repo to build our image. So why are we going to bring them into the build context. If you are doing a huge data science project with large sample model files. Or a financial model project with 100s of .csv files to test against. Now, I won't get into the argument about if that data really belongs in the repo. Thats not the purpose of this series. But if it is we don't need it to build our base image to run our app.&lt;/p&gt;

&lt;p&gt;So take this structure for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myApp/
    .git/
    .github/
    docs/
        getting_started.md
        install.md
    src/
       myApp/
    scripts/
       install.sh
    dockerfile
    .dockerignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty simple app structure. What part of this do we really need to build this app? the &lt;code&gt;src/myApp&lt;/code&gt; for sure. But the rest would all just be bloat to our build context. &lt;/p&gt;

&lt;p&gt;Remember the output we had from our &lt;code&gt;docker build&lt;/code&gt; command?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; helloFromIntegratnIO
Sending build context to Docker daemon  144.4kB
Step 1/7 : FROM golang:alpine
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6af5835b113c
Step 2/7 : RUN apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 80212a0a3d3b
Step 3/7 : WORKDIR &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/integratnio/go-rest-api/
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 75eb0766eb86
Step 4/7 : COPY go-rest-api &lt;span class="nb"&gt;.&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 103a86626a1d
Step 5/7 : RUN go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;88aa3cef85b2
Removing intermediate container 88aa3cef85b2
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 22e8e7d430e6
Step 6/7 : RUN go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go/bin/hello
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;e6c5ada5af3e
Removing intermediate container e6c5ada5af3e
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ff7cb854f70f
Step 7/7 : ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/go/bin/hello"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;1a4d5925b331
Removing intermediate container 1a4d5925b331
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; e8d948afae99
Successfully built e8d948afae99
Successfully tagged helloFromIntegratnIO:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at line 2 of that output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sending build context to Docker daemon  144.4kB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That happened so quick we didn't even talk about it before. When building your container. Everything in your local context will go into the build context unless you declaratively exclude it. Not a big deal when your repo is only a couple megabytes. But when you get into the Gigabytes and you are trying to speed up the build you will come back here and find a way to trim a couple minutes right up front.&lt;/p&gt;

&lt;p&gt;So lets create a .dockerignore for the file structure example I posted earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.git/
.github/
docs/
scripts/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are other ways to pass a build context. You could pass a URL, a tar file and more. Docker has built great documentation around all of it &lt;a href="https://docs.docker.com/engine/reference/commandline/build/"&gt;here&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQj764Ae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker Training 3 - My First Docker Deployment</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Mon, 26 Jul 2021 22:18:21 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/docker-training-3-my-first-docker-deployment-4m92</link>
      <guid>https://forem.com/jamesatintegratnio/docker-training-3-my-first-docker-deployment-4m92</guid>
      <description>&lt;p&gt;Lets recap where we are so far. We've gone over some of the basics of a Dockerfile. Talked a bit about some basic commands that will come in handy as we continue. Wrote a Dockerfile to build our little Golang API. Finally we have built an image and discussed tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Run
&lt;/h2&gt;

&lt;p&gt;So lets deploy this image and play with it a little bit. Open up a terminal and type this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:8080:10000/tcp helloFromIntegratnIO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets step through this command 1 piece at a time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker run&lt;/code&gt;: Here we are telling the Docker cli that we want to &lt;a href="https://docs.docker.com/engine/reference/commandline/run/"&gt;run&lt;/a&gt; an image as a container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-publish&lt;/code&gt;: &lt;a href="https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p---expose"&gt;Publish&lt;/a&gt; will bind a local port to a port in the container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;127.0.0.1&lt;/code&gt;: Keeps us only exposing localhost, so our container won't be available outside of our machine.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:8080&lt;/code&gt;: is the local port on our host machine that we are binding to&lt;br&gt;
&lt;code&gt;:10000&lt;/code&gt;: is the port inside the container that we want &lt;code&gt;8080&lt;/code&gt; to impersonate.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/tcp&lt;/code&gt;: is the protocol we want to use. You could also use &lt;code&gt;udp&lt;/code&gt; or &lt;code&gt;sctp&lt;/code&gt; if needed. You can learn more about that in the &lt;a href="https://docs.docker.com/network/links/#connect-using-network-port-mapping"&gt;User Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you haven't hit enter yet. Go ahead and hit it now and kick off that Docker run command.&lt;/p&gt;

&lt;p&gt;You should see the following output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:10000/tcp helloFromIntegratnIO
Starting Web Server
Preparing to handle requests
Ready &lt;span class="k"&gt;for &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets see if it works. &lt;/p&gt;

&lt;p&gt;Open up a second terminal and enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
Hello from Integratn.IO!!!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are cooking. But what else can we do with this little container. &lt;/p&gt;

&lt;p&gt;Lets change the response from the curl request. We can do this by passing an environment variable into the container. We can do this one of two ways. We can edit the docker file. Add the &lt;a href="https://docs.docker.com/engine/reference/builder/#env"&gt;ENV&lt;/a&gt; instruction. Tell it it we want &lt;code&gt;MESSAGE&lt;/code&gt; to equal something else. Rebuild our image. Then finally run it again. This is a great method for establishing default values for environment variables. Or we can add the parameter &lt;a href="https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file"&gt;--env&lt;/a&gt; with a value to our &lt;code&gt;docker run&lt;/code&gt; command and change it on the fly. Environment Variables passed during a &lt;code&gt;docker run&lt;/code&gt; command will always trump what is in your Dockerfile. If you haven't exited the container already hit &lt;code&gt;ctrl +c&lt;/code&gt; to exit the container from the terminal.&lt;/p&gt;

&lt;p&gt;Lets run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Hello from outerspace"&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:10000/tcp helloFromIntegratnIO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in another terminal lets use that curl statement again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
Hello from outerspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at that. We changed the response with the environment variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker exec
&lt;/h2&gt;

&lt;p&gt;Lets look at the &lt;a href="https://docs.docker.com/engine/reference/commandline/exec/"&gt;docker exec&lt;/a&gt; command while our container is running. Docker exec allows us to run commands inside our container while its already running. This can be very handy for debugging a container. But first we have to figure out what our container is named.&lt;/p&gt;

&lt;p&gt;Lets look at our running container with a command we covered in lesson 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get an output similar to this but your &lt;code&gt;NAMES&lt;/code&gt; field will not be the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps                     
CONTAINER ID   IMAGE              COMMAND           CREATED         STATUS         PORTS                       NAMES
56290825f889   helloFromIntegratnIO   &lt;span class="s2"&gt;"/go/bin/hello"&lt;/span&gt;   2 minutes ago   Up 2 minutes   127.0.0.1:8080-&amp;gt;10000/tcp   hungry_bohr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have found our running container. Lets use the container name and execute a shell to get into our container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--interactive&lt;/span&gt; &lt;span class="nt"&gt;--tty&lt;/span&gt; hungry_bohr /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--interactive&lt;/span&gt; &lt;span class="nt"&gt;--tty&lt;/span&gt; hungry_bohr /bin/sh
/go/src/integratnio/go-rest-api &lt;span class="c"&gt;# &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Does that path look familiar? Its the same path we set for &lt;code&gt;WORKDIR&lt;/code&gt; in the Dockerfile. We are in the shell of the container. Type &lt;code&gt;exit&lt;/code&gt; and hit &lt;code&gt;enter&lt;/code&gt; to leave the container.&lt;/p&gt;

&lt;p&gt;Lets look at the command real quick. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker exec&lt;/code&gt; is the base command. This is declaring that we want to execute something against a running container.&lt;br&gt;
&lt;code&gt;--interactive&lt;/code&gt; will keep STDIN open even if we aren't attached.&lt;br&gt;
&lt;code&gt;--tty&lt;/code&gt; gives us a pseudo tty.&lt;br&gt;
&lt;code&gt;hungry_bohr&lt;/code&gt; is the name of my running container. Yours will be different.&lt;br&gt;
&lt;code&gt;/bin/sh&lt;/code&gt; This is the command we want to run in the container. One caveat to this. Whatever you run against the container has to actually be part of the container. In an effort to trim containers down a lot of container images don't have a shell command available.&lt;/p&gt;

&lt;p&gt;So now, we've ran our container. We've exec'd into our container so that we can explore it. Thats it for this time. Get ready for next time when we have a brief exploration into the build context.&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQj764Ae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Git Worktree Workflow</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Sun, 25 Jul 2021 21:01:34 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/my-git-worktree-workflow-186f</link>
      <guid>https://forem.com/jamesatintegratnio/my-git-worktree-workflow-186f</guid>
      <description>&lt;p&gt;I recently made a change to how I work on repositories when I have to work with others. It has been a life saver. &lt;/p&gt;

&lt;p&gt;There is a TLDR example at the bottom to get you interested in the rest of the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pain Points
&lt;/h2&gt;

&lt;p&gt;Lets start with the normal git workflow and the pain points it causes. First you clone the repo. You create your branch and you start coding away. Then your buddy hits you up on Slack with a huge PR and scrolling the diff in Github isn't enough for you to approve it. Great. Now you have to stash the code you are working on, update your base so that you have their branch, and pull it all down. Now you can finally review the PR. This is nuts. Its annoying and you completely lose track of whatever you were just working on. I didn't even cover getting back to your branch and getting going again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Painting a Picture With a Dev Story
&lt;/h2&gt;

&lt;p&gt;As a developer I want to be able to review my teammates code without having to stash my work or maintain a second copy of the entire repository. I also want to be able to pause my work and work on a different branch again without having to stash my work or maintain a second copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Git Worktree
&lt;/h2&gt;

&lt;p&gt;First off, here is a link to the git worktree &lt;a href="https://git-scm.com/docs/git-worktree"&gt;documentation&lt;/a&gt;. I won't cover every caveat. So check it out for a lot more info.&lt;/p&gt;

&lt;p&gt;Git is capable of maintaining multiple working trees of a single repo. You heard that right. With git worktree you can check out more than one branch at a time and sensibly maintain each of those branches.&lt;/p&gt;

&lt;p&gt;I had to rethink my folder folder structure when I started using worktree. It was weird at first. But its been amazing after I settled in.&lt;/p&gt;

&lt;p&gt;When I clone a repository with worktree in mind I will do it a little differently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone git@github.com:googleapis/python-tasks.git python-tasks/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the main branch lives. I really don't touch it except to fetch/pull and create new branches with worktree. Its my clean source of truth and I like to keep it that way.&lt;/p&gt;

&lt;p&gt;From here I will create a new branch to for whatever I'm working on But I won't use &lt;code&gt;git branch ${branchName}&lt;/code&gt; or &lt;code&gt;git checkout -b ${branchName}&lt;/code&gt;. I'll do it with worktree while i'm in the main branch folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; my-awesome-branch ../my-awesome-branch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pay attention to that &lt;code&gt;../&lt;/code&gt; If you forget it you will end up putting the branch in the folder where main lives and that can get real ugly, real fast. &lt;/p&gt;

&lt;p&gt;So now we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/Projects
  /python-tasks
    /main
    /my-awesome-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now list all of your working trees with the list argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree list
~/Projects/python-tasks/main                63df2ef &lt;span class="o"&gt;[&lt;/span&gt;master]
~/Projects/python-tasks/my-awesome-branch   63df2ef &lt;span class="o"&gt;[&lt;/span&gt;my-awesome-branch]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I change to that directory. I do my work. Next thing I know I get a message from a teammate and he needs me to do that PR review we've been talking about. With git worktree its too easy. I just go back to main and add his branch to my worktree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree add &lt;span class="nt"&gt;--track&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; add-appengine-flexible-tasks-samples ../ppr-review origin/add-appengine-flexible-tasks-samples

Preparing worktree &lt;span class="o"&gt;(&lt;/span&gt;new branch &lt;span class="s1"&gt;'add-appengine-flexible-tasks-samples'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Branch &lt;span class="s1"&gt;'add-appengine-flexible-tasks-samples'&lt;/span&gt; &lt;span class="nb"&gt;set &lt;/span&gt;up to track remote branch &lt;span class="s1"&gt;'add-appengine-flexible-tasks-samples'&lt;/span&gt;  from &lt;span class="s1"&gt;'origin'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
HEAD is now at e2c8eee chore: generate noxfile.py &lt;span class="k"&gt;for &lt;/span&gt;samples
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a lot going on in that command. So check out the &lt;a href="https://git-scm.com/docs/git-worktree#Documentation/git-worktree.txt-addltpathgtltcommit-ishgt"&gt;doc&lt;/a&gt; for the full explanation, but I'll try to break it down.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git worktree add&lt;/code&gt;: We've seen this. Its the base command to create a branch and a working tree&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--track&lt;/code&gt;: This sets up tracking mode. We need it to track a remote branch&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-b&lt;/code&gt;: This will create a new branch and we give it the same name as the remote branch because its the right thing to do. But really we could call it anything.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;../pr-review&lt;/code&gt; The path we want it checked out to. Don't forget that &lt;code&gt;../&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;code&gt;origin/add-appengine-flexible-tasks-samples&lt;/code&gt; Finally the &lt;code&gt;&amp;lt;remote&amp;gt;/&amp;lt;branch&amp;gt;&lt;/code&gt; that we want check out.&lt;/p&gt;

&lt;p&gt;Now if we do &lt;code&gt;git worktree list&lt;/code&gt; We'll see our friends branch in our working tree ready for review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree list
~/Projects/python-tasks/main                63df2ef &lt;span class="o"&gt;[&lt;/span&gt;master]
~/Projects/python-tasks/my-awesome-branch   63df2ef &lt;span class="o"&gt;[&lt;/span&gt;my-awesome-branch]
~/Projects/python-tasks/pr-review           e2c8eee &lt;span class="o"&gt;[&lt;/span&gt;add-appengine-flexible-tasks-samples]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty sweet.&lt;/p&gt;

&lt;p&gt;Now I've finished my PR review and I don't need the code anymore. so all we have to do from &lt;code&gt;main&lt;/code&gt; is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree remove pr-review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Poof, The folder is gone and we don't have to clog our working tree up with it anymore. Sometimes we get lazy right? maybe I did &lt;code&gt;rm -rf ./pr-review&lt;/code&gt;. This is bad. I just broke my working tree. The folders gone but git still knows about it when I do &lt;code&gt;git worktree list&lt;/code&gt; How can we fix this? Is it time to just throw the whole repo away and clone again? What do we do?&lt;/p&gt;

&lt;p&gt;From main as usual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now our mistake has been removed from the git and we can continue on about our day.&lt;/p&gt;

&lt;p&gt;There are a few other worktree commands out there. Checkout &lt;a href="https://git-scm.com/docs/git-worktree#Documentation/git-worktree.txt-lock"&gt;lock&lt;/a&gt; and &lt;a href="https://git-scm.com/docs/git-worktree#Documentation/git-worktree.txt-unlock"&gt;unlock&lt;/a&gt; if you need to put a working tree on a thumb drive or a network share thats not always mounted. That will protect it from &lt;a href="https://git-scm.com/docs/git-worktree#Documentation/git-worktree.txt-prune"&gt;prune&lt;/a&gt;. This was a long one. If you stuck around and found it useful, feel free to hit me up on my socials to tell me about it or teach me a new trick you found thats even better.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; emergency-fix ../temp main
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; ../temp
&lt;span class="c"&gt;# ... hack hack hack ...&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'emergency fix for boss'&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git worktree remove ../temp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/integratnio"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQj764Ae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Hugo+Netlify Setup</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Sat, 24 Jul 2021 17:40:16 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/my-hugo-netlify-setup-1b7p</link>
      <guid>https://forem.com/jamesatintegratnio/my-hugo-netlify-setup-1b7p</guid>
      <description>&lt;p&gt;So here is how I got my blog all set up. It was a lot of back and forth on how I set this up. So this post is for those that read it. But also for me. It was a lot of work and I want to capture what I did so I can reproduce it later.&lt;/p&gt;

&lt;p&gt;This is by no means a complete set of instructions that you can iterate through. But more of a flow. I try to call out the things that were pain points for me directly though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Hugo
&lt;/h2&gt;

&lt;p&gt;First thing I did was install hugo. You can find instructions for it &lt;a href="https://gohugo.io/getting-started/installing/"&gt;here&lt;/a&gt;. After I had it installed I used &lt;code&gt;hugo new site ${siteName}&lt;/code&gt; to create my site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version Control is Key
&lt;/h2&gt;

&lt;p&gt;After that I wanted to have git setup so that I had solid version control incase I broke anything. &lt;code&gt;git init&lt;/code&gt; solved that really quickly. Then I created my repo on &lt;a href="https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/creating-a-new-repository"&gt;Github&lt;/a&gt;. After you do that it will give you instructions on how to create your first commit and change your origin for the remote and push your code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Gotta Make it Pretty
&lt;/h2&gt;

&lt;p&gt;Next up is adding the theme. You can find a lot of them on the &lt;a href="https://themes.gohugo.io/"&gt;Hugo site&lt;/a&gt;. Use &lt;code&gt;git submodule add ${url to theme repo} themes/{theme-name}&lt;/code&gt; to put it in place.&lt;/p&gt;

&lt;p&gt;After you get your theme in place you will need to update your config.yaml to actually use it. You would do this by adding a line like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anubis"&lt;/span&gt; &lt;span class="c1"&gt;# This is the name of the folder in the themes directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy It
&lt;/h2&gt;

&lt;p&gt;Now I have everything installed. Lets see it run. To run it locally to make sure everything works you can use &lt;code&gt;hugo serve&lt;/code&gt;. But if you really want to get going then you will need to deploy it somewhere online. I put mine on netlify as I knew the name already and the process was simple enough. &lt;/p&gt;

&lt;p&gt;Create your account on &lt;a href="https://app.netlify.com/signup"&gt;netlify&lt;/a&gt;. After your account is created you can create a new site with this magic button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kVINQb9x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hp5ipj1nl7gd0sr4r4rk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kVINQb9x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hp5ipj1nl7gd0sr4r4rk.png" alt="new site button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this you will go through some steps to connect it to your github repository and a deploy should be triggered. When its all said and done you should have your new site up and ready to go.&lt;/p&gt;

&lt;p&gt;This includes setting up your &lt;code&gt;netlify.toml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[build]&lt;/span&gt;
&lt;span class="py"&gt;publish&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"public"&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hugo --gc --minify"&lt;/span&gt;

&lt;span class="nn"&gt;[context.production.environment]&lt;/span&gt;
&lt;span class="py"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.85.0"&lt;/span&gt;
&lt;span class="py"&gt;HUGO_ENV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"production"&lt;/span&gt;
&lt;span class="py"&gt;HUGO_ENABLEGITINFO&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;

&lt;span class="nn"&gt;[context.split1]&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hugo --gc --minify --enableGitInfo"&lt;/span&gt;

&lt;span class="nn"&gt;[context.split1.environment]&lt;/span&gt;
&lt;span class="py"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.85.0"&lt;/span&gt;
&lt;span class="py"&gt;HUGO_ENV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"production"&lt;/span&gt;

&lt;span class="nn"&gt;[context.deploy-preview]&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hugo --gc --minify --buildDrafts --buildFuture -b $DEPLOY_PRIME_URL"&lt;/span&gt;

&lt;span class="nn"&gt;[context.deploy-preview.environment]&lt;/span&gt;
&lt;span class="py"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.85.0"&lt;/span&gt;

&lt;span class="nn"&gt;[context.branch-deploy]&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hugo --gc --minify --buildDrafts --buildFuture -b $DEPLOY_PRIME_URL"&lt;/span&gt;

&lt;span class="nn"&gt;[context.branch-deploy.environment]&lt;/span&gt;
&lt;span class="py"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.85.0"&lt;/span&gt;

&lt;span class="nn"&gt;[context.next.environment]&lt;/span&gt;
&lt;span class="py"&gt;HUGO_ENABLEGITINFO&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding Netlify CMS
&lt;/h2&gt;

&lt;p&gt;You can find the getting started documentation &lt;a href="https://www.netlifycms.org/docs/intro/"&gt;here&lt;/a&gt;. You'll notice we didn't use the Hugo Template that they offer. That thing is outdated and I was unable to get it to work well with a theme for local development. It was much easier to add it to an &lt;a href="https://www.netlifycms.org/docs/add-to-your-site/"&gt;existing site&lt;/a&gt;. Read that doc. It will answer most of your questions. But here is my config.yml (make sure you use &lt;code&gt;.yml&lt;/code&gt; and not &lt;code&gt;.yaml&lt;/code&gt;. It only looks for the one).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git-gateway&lt;/span&gt;
  &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jamesattensure/integratn-io&lt;/span&gt;

&lt;span class="na"&gt;publish_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;editorial_workflow&lt;/span&gt;
&lt;span class="na"&gt;media_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static/images"&lt;/span&gt;
&lt;span class="na"&gt;public_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/images"&lt;/span&gt;

&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;posts"&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Posts"&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content/posts"&lt;/span&gt;
    &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{year}}-{{month}}-{{day}}-{{slug}}"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Draft"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;draft&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boolean"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Layout"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;layout"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hidden"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Date"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;datetime"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Categories"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;categories"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tags"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Series"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;series&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Comment"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nocomment"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boolean"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;markdown"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your collections are built based on your hugo &lt;a href="https://gohugo.io/content-management/archetypes/"&gt;archetypes&lt;/a&gt;. There are a couple extra widgets that I don't use. Those can be found in the &lt;a href="https://www.netlifycms.org/docs/widgets"&gt;widget doc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you get through the whole doc and complete the authentication steps or anyone will be able to access the cms features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Things to Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Analytics
&lt;/h3&gt;

&lt;p&gt;Pay for netlify analytics or add &lt;a href="https://analytics.google.com/"&gt;Google analytics&lt;/a&gt;. Most themes have Google analytics baked in and you just have to add the params to your &lt;code&gt;config.yaml&lt;/code&gt; to connect it. Here are the two posts I followed to get it setup. &lt;a href="http://cloudywithachanceofdevops.com/posts/2018/05/17/setting-up-google-analytics-on-hugo/"&gt;cloudywithachanceofdevops.com&lt;/a&gt; and &lt;a href="https://gideonwolfe.com/posts/sysadmin/hugo/hugogoogleanalytics/"&gt;gideonwolfe.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure you filter out your connections to your site so that you don't skew your own analytics. This blog post from &lt;a href="https://www.daniloaz.com/en/5-ways-to-exclude-your-own-visits-from-google-analytics/"&gt;daniloaz.com&lt;/a&gt; really helped get me through it. I went with the second method of filtering out my public IP as internal traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search
&lt;/h3&gt;

&lt;p&gt;After you have Google analytics setup. You can setup &lt;a href="https://search.google.com/search-console"&gt;Google Search&lt;/a&gt; to the same property that you setup in google analytics. It helps to have a sitemap.xml to add. The easiest way I found to do that was to use the &lt;a href="https://github.com/netlify-labs/netlify-plugin-sitemap#readme"&gt;sitemap netlify plugin&lt;/a&gt; I recommend just installing it from the plugins tab on your Netlify site. Much easier to manage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Plugins
&lt;/h3&gt;

&lt;p&gt;Here are a couple other plugins I installed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cdeleeuwe/netlify-plugin-hugo-cache-resources#readme"&gt;Hugo cache resources&lt;/a&gt;:&lt;br&gt;
This plugin caches the resources folder after build. If you are processing many images, this would improve build duration significantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cdeleeuwe/netlify-plugin-submit-sitemap#readme"&gt;Submit sitemap&lt;/a&gt;:&lt;br&gt;
This plugin will notify the search engines after every production build about your latest sitemap. The plugin can be used without any configuration if using the defaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;There is a lot to learn when getting started with blogging. At the time of posting this I've only been at it a couple weeks. I spent most of that time bouncing through various starters and trying different things out to figure out what worked and what didn't. Hell, a week of that was probably just trying to pick a theme that I could live with until I understand enough to build my own theme. I hope this helped get you up and running. Enjoy and happy blogging.&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQj764Ae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>netlify</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Docker Training - My First Docker Build</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Sat, 24 Jul 2021 17:37:25 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/docker-training-2-my-first-docker-build-3d6d</link>
      <guid>https://forem.com/jamesatintegratnio/docker-training-2-my-first-docker-build-3d6d</guid>
      <description>&lt;p&gt;The goal of this project is not to learn how to build APIs in Golang or anything extra. If you want to learn more about the snippet below feel free to dig through the &lt;a href="https://golang.org/pkg/net/http/"&gt;Go Docs&lt;/a&gt;. Now lets create files and get started.&lt;/p&gt;

&lt;p&gt;To get started we need to create a project to store our app we are going to work with. Create the following structure. We'll keep filling more in as we go later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;DockerTraining/
    go-rest-api/
        main.go
    dockerfile      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize the folder as a go project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ GO111MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on go mod init helloFromIntegratnIO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code to main.go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;helloFromIntegratnIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Hello From Integratn.IO"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MESSAGE"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Endpoint Hit: helloFromIntegratnIO"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleRequests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;helloFromIntegratnIO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":10000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting Web Server"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Preparing to handle requests"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ready for requests"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;handleRequests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a whole lot going on up there. We create a ResponseWriter that prints some text whenever you hit the app on port 10000.&lt;/p&gt;

&lt;p&gt;Lets get our dockerfile filled in.&lt;br&gt;
&lt;/p&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:alpine&lt;/span&gt;
&lt;span class="c"&gt;# Keep older versions of Go behaving the same as the latest&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; GO111MODULE=on \&lt;/span&gt;
    CGO_ENABLED=1
&lt;span class="c"&gt;# Install git.&lt;/span&gt;
&lt;span class="c"&gt;# Git is required for fetching the dependencies.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $GOPATH/src/integratnio/go-rest-api/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go-rest-api .&lt;/span&gt;
&lt;span class="c"&gt;# Fetch dependencies.&lt;/span&gt;
&lt;span class="c"&gt;# Using go get.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# Build the binary.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go/bin/hello
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/go/bin/hello"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks familiar, right?&lt;/p&gt;

&lt;p&gt;Lets step through this one more time. We declared our base image with the &lt;strong&gt;FROM&lt;/strong&gt; instruction. Then we use the &lt;strong&gt;RUN&lt;/strong&gt; instruction to install git. Used &lt;strong&gt;WORKDIR&lt;/strong&gt; to change the path we are working from. Copied in our code with the &lt;strong&gt;COPY&lt;/strong&gt; instruction. Used the &lt;strong&gt;RUN&lt;/strong&gt; Command again to get any possible dependencies that were needed for our program. One more final use of &lt;strong&gt;RUN&lt;/strong&gt; to build our app into an executable. Then finally we declared our &lt;strong&gt;ENTRYPOINT&lt;/strong&gt; with the final instruction pointing to where our app is located.&lt;/p&gt;

&lt;p&gt;Now we are ready to finally step through a build. Lets issue our first docker command.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;$ docker build . -t helloFromIntegratnIO&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Lets look at the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✭ ᐅ docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; helloFromIntegratnIO
Sending build context to Docker daemon  84.48kB
Step 1/7 : FROM golang:alpine
alpine: Pulling from library/golang
596ba82af5aa: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;344f2904b0c6: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;d3bda26d9fa1: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;24e1a14bb4a2: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;f0b175b107d5: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;Digest: sha256:07ec52ea1063aa6ca02034af5805aaae77d3d4144cced4e95f09d62a6d8ddf0a
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;golang:alpine
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6af5835b113c
Step 2/7 : RUN apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;db684e4aab79
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
v3.13.0-114-g822507f819 &lt;span class="o"&gt;[&lt;/span&gt;https://dl-cdn.alpinelinux.org/alpine/v3.13/main]
v3.13.0-115-g41ee0c8f55 &lt;span class="o"&gt;[&lt;/span&gt;https://dl-cdn.alpinelinux.org/alpine/v3.13/community]
OK: 13880 distinct packages available
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
&lt;span class="o"&gt;(&lt;/span&gt;1/6&lt;span class="o"&gt;)&lt;/span&gt; Installing brotli-libs &lt;span class="o"&gt;(&lt;/span&gt;1.0.9-r3&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;2/6&lt;span class="o"&gt;)&lt;/span&gt; Installing nghttp2-libs &lt;span class="o"&gt;(&lt;/span&gt;1.42.0-r1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;3/6&lt;span class="o"&gt;)&lt;/span&gt; Installing libcurl &lt;span class="o"&gt;(&lt;/span&gt;7.74.0-r0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;4/6&lt;span class="o"&gt;)&lt;/span&gt; Installing expat &lt;span class="o"&gt;(&lt;/span&gt;2.2.10-r1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;5/6&lt;span class="o"&gt;)&lt;/span&gt; Installing pcre2 &lt;span class="o"&gt;(&lt;/span&gt;10.36-r0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;6/6&lt;span class="o"&gt;)&lt;/span&gt; Installing git &lt;span class="o"&gt;(&lt;/span&gt;2.30.0-r0&lt;span class="o"&gt;)&lt;/span&gt;
Executing busybox-1.32.1-r0.trigger
OK: 19 MiB &lt;span class="k"&gt;in &lt;/span&gt;21 packages
Removing intermediate container db684e4aab79
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 80212a0a3d3b
Step 3/7 : WORKDIR &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/mypackage/myapp/
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;60c0c79d0d5c
Removing intermediate container 60c0c79d0d5c
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 29ab2f51aa11
Step 4/7 : COPY go-rest-api &lt;span class="nb"&gt;.&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 7ded2d7874e0
Step 5/7 : RUN go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;da6e8b285779
Removing intermediate container da6e8b285779
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; f7eb2d97bd6f
Step 6/7 : RUN go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go/bin/hello
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;0e503ced7243
Removing intermediate container 0e503ced7243
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 21bbd2acaf3c
Step 7/7 : ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/go/bin/hello"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;42165a2c670f
Removing intermediate container 42165a2c670f
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 86fd2d2eb271
Successfully built 86fd2d2eb271
Successfully tagged helloFromIntegratnIO:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So whats happening here?&lt;/p&gt;

&lt;p&gt;Step 1/7: Our &lt;strong&gt;FROM&lt;/strong&gt; instruction is consumed and we see it fetch all the layers needed to build our base image.&lt;/p&gt;

&lt;p&gt;Step 2/7: &lt;strong&gt;RUN&lt;/strong&gt; executes and there is some normal &lt;code&gt;apt&lt;/code&gt; output while it installs git and the required dependencies.&lt;/p&gt;

&lt;p&gt;Step 3/7: We see &lt;strong&gt;WORKDIR&lt;/strong&gt; set our new path that we are "working in".&lt;/p&gt;

&lt;p&gt;Step 4/7: &lt;strong&gt;COPY&lt;/strong&gt; pulls our source code into the image.&lt;/p&gt;

&lt;p&gt;Step 5/7: &lt;strong&gt;RUN&lt;/strong&gt; uses the go executable to fetches dependencies for our app. Something to notice here. We never installed Go locally. All of this runs in the build container. As long as the base image you work from has the binary you need or you add it to the container some how. You can run whatever you need in the container without worrying about something locally on your machine getting in the way.&lt;/p&gt;

&lt;p&gt;Step 6/7 &lt;strong&gt;RUN&lt;/strong&gt; uses the go executable again to build the app.&lt;/p&gt;

&lt;p&gt;Step 7/7 &lt;strong&gt;ENTRYPOINT&lt;/strong&gt; declares the what to start with the container. &lt;/p&gt;

&lt;p&gt;The image is built. But where did it go? Lets use &lt;code&gt;docker image ls&lt;/code&gt; to find it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ᐅ docker image &lt;span class="nb"&gt;ls
&lt;/span&gt;REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
helloFromIntegratnIO    latest    dee090a053a3        About an hour ago   321MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Heres how this output breaks down:&lt;/p&gt;

&lt;p&gt;REPOSITORY: The name we gave our image.&lt;/p&gt;

&lt;p&gt;TAG: &lt;code&gt;latest&lt;/code&gt; is always the most recently built version of the image. &lt;/p&gt;

&lt;p&gt;IMAGE ID: This is a unique hash that identifies our image&lt;/p&gt;

&lt;p&gt;CREATED: How long ago the image was created&lt;/p&gt;

&lt;p&gt;SIZE: The size of our image.&lt;/p&gt;

&lt;p&gt;Lets take a quick second and talk about tags. Tags are important. They are a great way to reference what version of our image is running. They help us be declarative in what we run in any environment. So, lets create an app with an actual tag instead of latest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ᐅ docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; helloFromIntegratnIO:dev
Sending build context to Docker daemon  100.9kB
Step 1/7 : FROM golang:alpine
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6af5835b113c
Step 2/7 : RUN apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 80212a0a3d3b
Step 3/7 : WORKDIR &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/integratnio/go-rest-api/
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 75eb0766eb86
Step 4/7 : COPY go-rest-api &lt;span class="nb"&gt;.&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 9ea13e96b401
Step 5/7 : RUN go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cf8a5cf85856
Step 6/7 : RUN go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go/bin/hello
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; e4e107828679
Step 7/7 : ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/go/bin/hello"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dee090a053a3
Successfully built dee090a053a3
Successfully tagged helloFromIntegratnIO:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a second. This output looks different. What happened?&lt;/p&gt;

&lt;p&gt;Here is one of the advantages of Docker. Your Docker cache maintains a history of your images and the layers used to build those images. If your dockerfile and you code haven't changed it will use a cached image to build that layer.  Since all we did was retag the image it just consumed the cache all the way down.&lt;/p&gt;

&lt;p&gt;Lets look at our images again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
/Volumes/CaseSensitive/docker-training &lt;span class="o"&gt;(&lt;/span&gt;main ✘&lt;span class="o"&gt;)&lt;/span&gt;✹ ᐅ docker image &lt;span class="nb"&gt;ls                              
&lt;/span&gt;REPOSITORY            TAG        IMAGE ID            CREATED             SIZE
helloFromIntegratnIO  dev        dee090a053a3        About an hour ago   321MB
helloFromIntegratnIO  latest     dee090a053a3        About an hour ago   321MB
&amp;lt;none&amp;gt;                &amp;lt;none&amp;gt;     86fd2d2eb271        About an hour ago   321MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we have the &lt;code&gt;dev&lt;/code&gt; tag we just created. But lets look at the &lt;code&gt;IMAGE ID&lt;/code&gt;. The &lt;code&gt;dev&lt;/code&gt; tag and the &lt;code&gt;latest&lt;/code&gt; tag have the same &lt;code&gt;IMAGE ID&lt;/code&gt;. This is what we want because it is the most recently created image. &lt;/p&gt;

&lt;p&gt;So what is this &lt;code&gt;&amp;lt;none&amp;gt;&lt;/code&gt; image? If you scroll up. You will see that the &lt;code&gt;&amp;lt;none&amp;gt; IMAGE ID&lt;/code&gt; matches the &lt;code&gt;IMAGE ID&lt;/code&gt; from our first build. This shows you what happens when you don't properly tag your images. If you built another image using the &lt;code&gt;dev&lt;/code&gt; tag. The same thing would happen to that image. But this is local testing and not a production release. So no big deal. &lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQj764Ae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Docker Training - Getting Started</title>
      <dc:creator>James D.</dc:creator>
      <pubDate>Thu, 22 Jul 2021 00:27:55 +0000</pubDate>
      <link>https://forem.com/jamesatintegratnio/docker-training-getting-started-3p65</link>
      <guid>https://forem.com/jamesatintegratnio/docker-training-getting-started-3p65</guid>
      <description>&lt;p&gt;Before we can really dive into this project we need take care of a few things. First we need to get our environment setup to run docker. Then we need to go over a couple things about Dockerfiles and the Docker CLI &lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Docker
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mac
&lt;/h3&gt;

&lt;p&gt;Mac install is pretty straight forward. Follow the instructions below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/docker-for-mac/install/" rel="noopener noreferrer"&gt;Instructions&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;There are 2 ways to install docker on windows. 1 is supported with a Hyper-V Backend and the second is supported with a WSL2 backend. Windows home users have to use the WSL2 backend method. Pro, Education, and Enterpise can use either.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/docker-for-windows/install-windows-home/" rel="noopener noreferrer"&gt;WSL2 Instructions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/docker-for-windows/install/" rel="noopener noreferrer"&gt;Hyper V Instructions&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux
&lt;/h3&gt;

&lt;p&gt;Like all things linux. Docker setup is a bit more manual. You will have to install both Docker and Docker Compose to get through these lessons. Please find the directions for your preferred distro here. You will also have to run the Post-Installation steps or run all your docker commands as the root user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;Docker Install&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;Docker Compose Install&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.docker.com/engine/install/linux-postinstall/" rel="noopener noreferrer"&gt;Post Installation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is a dockerfile
&lt;/h2&gt;

&lt;p&gt;Lets get started on understanding a dockerfile. A dockerfile is the instruction set that is used to build a docker image. Each instruction within the dockerfile will create a new layer or intermediate image. These layers are like commits in a git repository. Any of the layers is a fully built image that could be ran as a Docker container. Here is a basic example of a dockerfile for Go.&lt;br&gt;
&lt;/p&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:alpine&lt;/span&gt;
&lt;span class="c"&gt;# Install git.&lt;/span&gt;
&lt;span class="c"&gt;# Git is required for fetching the dependencies.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; git
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $GOPATH/src/mypackage/myapp/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="c"&gt;# Fetch dependencies.&lt;/span&gt;
&lt;span class="c"&gt;# Using go get.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go get &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# Build the binary.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go/bin/hello
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/go/bin/hello"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets step through the various instructions and see what they do. I have linked all of the instruction identifiers below. There are many others and can be found in the &lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;Dockerfile Builder Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/reference/builder/#from" rel="noopener noreferrer"&gt;&lt;strong&gt;FROM&lt;/strong&gt;&lt;/a&gt; is an instruction that initializes a new build stage sets a base image. This is the container that will run while we build our image. Right now we are consuming the Golang image from Alpine. We chose this image because it is lighter and faster than the official Golang image. You can find more about Apline Linux and their images from &lt;a href="https://www.alpinelinux.org/about/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. There are many other images to choose from and they can be found at &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/reference/builder/#run" rel="noopener noreferrer"&gt;&lt;strong&gt;RUN&lt;/strong&gt;&lt;/a&gt; is how we execute a shell command inside the container that we are building from. This instruction will execute any commands that are passed to it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/reference/builder/#workdir" rel="noopener noreferrer"&gt;&lt;strong&gt;WORKDIR&lt;/strong&gt;&lt;/a&gt; instruction is used to change the working directory within the project. Much like how the command &lt;code&gt;cd&lt;/code&gt; is used in a shell. If the directory doesn't exist it will be created even if it is never used in any other Dockerfile instructions.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/reference/builder/#copy" rel="noopener noreferrer"&gt;&lt;strong&gt;COPY&lt;/strong&gt;&lt;/a&gt; instruction will copy everything from the declared path relative to your project to the destination of where it will live inside your image. In this case we are copying everything from the base of the project directory to &lt;code&gt;$GOPATH/src/mypackage/myapp/&lt;/code&gt;. The first &lt;code&gt;.&lt;/code&gt; represents your project directory. The second &lt;code&gt;.&lt;/code&gt; represents the &lt;code&gt;WORKDIR&lt;/code&gt; we set earlier.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint" rel="noopener noreferrer"&gt;&lt;strong&gt;ENTRYPOINT&lt;/strong&gt;&lt;/a&gt; instruction is executable that will be ran when the container is started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with the Docker CLI
&lt;/h2&gt;

&lt;p&gt;The Docker CLI is the main way we will interact with Docker. We will use it to build Docker images, run Docker containers and see whats going on in our Docker environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  See the help menu:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;help&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For any docker command, use &lt;code&gt;docker help $command&lt;/code&gt; to learn more about the command and the options that are available for it. I highly encourage you run this on ever command below. There are many options that you can pass to commands and it will list the aliases that can be used to make the commands shorter and save save some typing&lt;/p&gt;

&lt;h3&gt;
  
  
  Building an image:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$MYIMAGENAME&lt;/span&gt;:&lt;span class="nv"&gt;$MYIMAGETAG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage: &lt;code&gt;docker build [OPTIONS] PATH | URL | -&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Generally when building an image you are in the root directory of your project. That directory also contains the Dockerfile that will be used to build the project. It is also important to tag your images so that you can find them in the future. This is the purpose of the &lt;code&gt;-t&lt;/code&gt; in the command above. If you don't when you search through your images you will only see &lt;code&gt;None&lt;/code&gt; as the image name. &lt;/p&gt;

&lt;h3&gt;
  
  
  Listing images in your local registry:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List running containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List all containers (Including exited)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get logs from container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs &lt;span class="nv"&gt;$CONTAINERNAME&lt;/span&gt; | &lt;span class="nv"&gt;$CONTAINERID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very helpful if you have a container that keeps crashing on startup.&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/integratnio" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
