Containerization with Docker

Hey! If you love Go and building Go apps as much as I do, let's connect on Twitter or LinkedIn. I talk about this stuff all the time!

Want to learn how to build better Go applications faster and easier? You can.

Check out my course on the Go Standard Library. You can check it out now for free.


This article explores the concept of containerization using Docker, a powerful tool that revolutionizes how software is built and deployed.

What are containers?

Think of containers like virtual boxes for your code. Instead of installing your code directly on a server (which can be messy and lead to conflicts), you package it up with all its dependencies into a container. This ensures your code runs the same way everywhere, regardless of the underlying environment.

Imagine you’re baking a cake. You need specific ingredients like flour, sugar, and eggs, but the recipe for a delicious cake doesn’t change depending on who’s baking it. You can share the “recipe” (your code) as a container with all the exact ingredients (libraries and dependencies) needed to bake that cake anywhere.

Why use containers?

Docker lets you create these “recipes” for your software. Think of it like this:

  • Consistency: Just like using a pre-defined recipe ensures everyone gets the same results, using Docker means your application will run the same way on any machine that has Docker installed.
  • Isolation: Containers isolate your code from the host system and other containers. This is like baking the cake in its own pan - it doesn’t interfere with what else you’re making in the kitchen.

How Docker achieves this:

Docker uses a special file called a “Dockerfile” to build these isolated environments. In essence, a container contains everything your code needs to run:

  • Dependencies: These are the ingredients your code needs to function properly. Dependencies can be libraries, frameworks, or other software components.

What is a Dockerfile?

A Dockerfile is a text file containing a series of instructions that tell Docker how to build a container. It’s like a detailed recipe for Docker, specifying the exact steps and ingredients needed for it to function correctly.

Imagine a Dockerfile as a blueprint for your code environment.

Here’s what this “blueprint” looks like:

FROM golang:1.20 # Use a Go image as the base
WORKDIR /app # Set the working directory for your project
RUN go install <package name> # Install any necessary libraries/tools within the container

COPY . . # Copy all files in the current directory to the root of the container

CMD ["go", "run"] # Execute this command when the container starts

Let’s break down a simple example:

Scenario: You have a Go program that depends on specific libraries and configurations.

Solution: We can use Docker to package the application with these dependencies without installing them directly on the host machine.

Example Dockerfile:

# Start with a standard Go image
FROM golang:1.20

# Set the working directory
WORKDIR /app

# Install the Go code for your project
RUN go install ./cmd/my-project

# Specify the port to expose (for web applications)
EXPOSE 8080

# Copy the Go Dockerfile into the container
COPY . /go/Dockerfile

# Build the application
COPY . .

# Set the command for running the app
CMD ["go", "run", "main.go"]

Explanation:

  • FROM golang:1.20: This specifies that we’re using a pre-built Docker image with the Go programming environment already configured.

  • RUN go install: This command installs the Go program and its dependencies in the container, ensuring they are available for execution.

Why use this approach?

  • Portability: Your code will run in the same way regardless of where it’s deployed (e.g., on a laptop, in a cloud environment) because everything needed is within your “container”
  • Consistency: Containers help avoid the “what if my code needs something different on another machine?” problem. They ensure that your application has the same environment and dependencies everywhere it runs.

Important note: This is just a basic example, and you’ll likely need additional commands in your Dockerfile to install libraries and set up your container properly.

StepDescriptionExplanation
FROM <image>Choose a base image (e.g., golang:1.20)This is the starting point.

You’ll need to choose a base image that has the necessary dependencies for your project.

| | You can use go command to install Go programs and its dependencies within the container, ensuring they run in a consistent environment.
| FROM <base_image> | Example: A Dockerfile could include instructions like “RUN go get github.com/gorilla/mux” for a web server using the popular “mux” library.

The “main” step is an example of a command that can be used to define the behavior of your container, and it’s crucial for making the environment self-contained |
| COPY . <destination> | Copy the entire project into the container. This assumes you have a directory structure like this:

Project Structure:

<project_directory>
├── Dockerfile
└── .../main.go

|
| RUN go install ./my-program | This would be your code’s “blueprint” for the specific version of Go you need (e.g., 1.20) and the location of the project.

RUN go install ./main

Important:

  • Consistent Dependencies: The RUN command ensures that the same dependencies are used when building the image for your container, avoiding conflicts and inconsistencies.
  • Environment Setup:

This approach is crucial for specifying a base environment for the container. For example:

RUN go install github.com/gorilla/mux

Important Considerations:

  • Base Image Choice: The version of Go used in the FROM instruction matters. You want to choose a base image that matches your project’s requirements and dependencies.
  • Dependency Management: Use a “requirements.txt” file (similar to Python’s Pipfile) for easier dependency management and to share it with others

Important note: The RUN command is used to define the instructions for building a Docker image. It’s important to understand that go install is a specific command used in a Go application.

  • Dependency Versions: Make sure your project’s “requirements.txt” file specifies the correct versions of the dependencies needed (e.g., a specific version of the “go-mysql-driver”).

Common Mistakes:

  • Using the wrong base image: Always use the FROM command to specify a base image that matches your project’s requirements and dependencies.
  • Not specifying a version for the go image: This can lead to issues with compatibility, as Dockerfiles need to be as specific as possible.

Best Practices:

  • Use a “requirements.txt” file (or equivalent) to define all dependencies early on.
  • Specify exact versions of your dependencies in the “requirements.txt” file.
  • Optimize for a minimal image: This helps reduce the size of your images and makes them easier to manage.

Important considerations when setting up a container:

  • Use a “base image”: Define a base language (like “go:1.18”).

  • Build Context: Understanding the “context” for your Go project is crucial. The “base image” defines the environment and packages needed, but you’ll want to define the Dockerfile in a way that allows it to be easily updated and reused.

  • Create a “Dockerfile.go” file:

This helps achieve a consistent and clean build process.

Example Dockerfile (updated):

# Use a base image with the Go build tools installed
FROM golang:1.20

# Copy your project into the container
COPY . .


Stay up to date on the latest in Coding for AI and Data Science

Intuit Mailchimp