Working with Golang and Docker SDK: A Practical Approach
If you enjoy reading medium articles and are interested in becoming a member, I would be happy to share my referral link with you!
Docker is an essential tool for developing, deploying, and running applications in containers. Its SDK for Go offers a way for developers to manage Docker services programmatically from their Go applications. This article will guide you through the basics of using the Docker SDK for Go, featuring examples of how to manage Docker images and containers.
Before we begin, ensure that Docker is installed and running on your machine. You’ll also need a working Go environment to follow along with the code examples.
Setting Up Docker SDK for Go
First, install the Docker SDK for Go by running the following command in your terminal:
go get github.com/docker/docker/client
Now, you’re ready to use the Docker SDK in your Go application. Let’s start by initializing a Docker client.
package main
import (
"context"
"fmt"
"github.com/docker/docker/client"
)
func main() {
cli, err := client.NewClientWithOpts(client.WithVersion("1.40"))
if err != nil {
panic(err)
}
fmt.Println("Successfully initialized Docker client")
}
In the above snippet, we create a new Docker client using client.NewClientWithOpts()
. We specify Docker's API version using client.WithVersion()
. Make sure to use an API version that's compatible with your Docker Engine version.
Listing Docker Images
Now that we have a Docker client, we can start interacting with Docker services. Let’s start by listing the Docker images on our system:
package main
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func main() {
cli, err := client.NewClientWithOpts(client.WithVersion("1.40"))
if err != nil {
panic(err)
}
images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
if err != nil {
panic(err)
}
for _, image := range images {
fmt.Println(image.ID)
}
}
In the above code, we call cli.ImageList()
, passing in a context.Background()
and an empty types.ImageListOptions{}
to get all images. The method returns a list of types.ImageSummary
objects, and we iterate over them to print each image's ID.
Creating a Docker Container
Let’s move forward to create a Docker container using the SDK:
package main
import (
"context"
"fmt"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)
func main() {
cli, err := client.NewClientWithOpts(client.WithVersion("1.40"))
if err != nil {
panic(err)
}
ctx := context.Background()
containerConfig := &container.Config{
Image: "alpine",
Cmd: []string{"echo", "Hello from Docker!"},
}
resp, err := cli.ContainerCreate(ctx, containerConfig, nil, nil, nil, "")
if err != nil {
panic(err)
}
fmt.Printf("Container created with ID: %s\n", resp.ID)
}
In this example, we call cli.ContainerCreate()
, passing in a container.Config
object that specifies the image and command to run. The ContainerCreate()
method returns a container.ContainerCreateCreatedBody
object containing the ID of the newly created container, among other things.
Running a Docker Container
Creating a container is only part of the story. In this example, we will create and run a Docker container:
package main
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)
func main() {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
}
ctx := context.Background()
// Pulling an image
reader, err := cli.ImagePull(ctx, "docker.io/library/alpine", types.ImagePullOptions{})
if err != nil {
panic(err)
}
defer reader.Close()
// Creating a container
resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: "alpine",
Cmd: []string{"echo", "hello world"},
Tty: true,
}, nil, nil, nil, "")
if err != nil {
panic(err)
}
// Starting the container
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
panic(err)
}
fmt.Printf("Container %s has started\n", resp.ID)
}
In this code:
- We use
cli.ImagePull
to pull thealpine
image from Docker Hub. This function returns a reader for the output of thedocker pull
command. We defer closing the reader to prevent a resource leak. - We use
cli.ContainerCreate
to create a new container from thealpine
image. The container's command isecho hello world
. - Finally, we start the container using
cli.ContainerStart
.
When you run this Go program, it will download the alpine
Docker image (if it's not already present), create a container that runs the echo
command, and then start that container. The output will be the ID of the newly started container.
Conclusion
Throughout this article, we have explored the functionality provided by Docker’s Go SDK to manage Docker services programmatically. The simplicity and power of Go combined with Docker’s flexibility makes this a potent combination, enabling us to perform complex tasks with a few lines of Go code.
From initializing the Docker client to listing Docker images, creating, and running containers, we’ve gone through a series of operations that lay the foundation for more advanced Docker management tasks. The client.ContainerCreate()
function and its accompanying container.Config
struct offer a powerful and flexible way to create new Docker containers, specifying everything from the image to use, to the commands to run, environment variables, and more.
However, as we mentioned, creating a Docker container is just half the journey. Running the created container brings it to life, enabling it to fulfill its purpose, whether that’s running a web server, a database, a microservice, or any other type of application.
As you continue to explore the Docker SDK for Go, you’ll find that it supports all the Docker operations you’re familiar with from the Docker CLI. It’s a powerful tool that can simplify your Go applications’ interaction with Docker, making your code cleaner, more manageable, and more efficient.