banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

[Reprint] How to Operate Docker Elegantly with Golang

title: '【转载】How to Operate Docker Elegantly with Golang'
date: 2021-08-09 10:33:00
comment: false
toc: true
category:

  • Golang
    tags:
  • Reprint
  • Go
  • golang
  • Docker

This article is reprinted from: How to Operate Docker Elegantly with Golang - Zhihu


Docker is currently the most popular Linux container solution, solving the problem of environment configuration in software development, and thus has been widely used in daily development. Typically, we download Docker and install it according to the steps on the official website, and then execute docker run hello-world in the terminal to get the output below, which indicates that the installation is successful.

klew@primary:~$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

What Happens When Executing docker run#

Docker is a typical C-S architecture, where the terminal executing docker run acts as the client, while Docker containers are actually controlled by the Docker Daemon. The Docker Daemon runs on the machine specified by the DOCKER_HOST environment variable. When we execute docker run hello-world, the default value of DOCKER_HOST is unix:///var/run/docker.sock, at which point the client will find the local Docker Daemon through this socket and send it the command run hello-world, which is then executed by the Docker Daemon to pull the image and start the container. Refer to the image below:

image

As can be seen from the image, the Docker client and Docker Daemon do not necessarily have to be on the same machine. By specifying the DOCKER_HOST environment variable at runtime, we can also send commands to the Docker Daemon on other machines. For example, to run run hello-world on the Docker Daemon at 192.168.64.1, we only need to execute:

$ DOCKER_HOST=tcp://192.168.64.1:2375 docker run hello-world

It is important to note that the successful execution of this command requires that port 2375 is open on 192.168.64.1. Executing Docker commands in the terminal is generally sufficient, but it is still inconvenient to integrate into applications. For example, if I want to create a Docker container monitoring project using Golang, how can I obtain container information in a Go program? The Docker SDK happens to provide such functionality.

Docker SDK#

The Docker SDK provides an interface for interacting with the Docker daemon, officially supporting Python and Go, with corresponding third-party libraries available for other popular languages.

Environment Preparation#

  1. Configure Docker Environment

Using the Docker Daemon requires a version of 18.09 or higher, and the local Docker client must also be version 19.03 or higher; otherwise, an error will occur when attempting to connect later:

[] error during connect: Get "http://docker/v1.24/images/json": command [] has exited with exit status 255, please make sure the URL is valid, and Docker 18.09 or
 later is installed on the remote host: stderr=ssh: connect to host: Connection refused
  1. Install Go SDK

Follow the steps in Official Website - Install the SDKs to download the dependencies:

$ go get github.com/docker/docker/client

Connect to Local Docker Daemon#

Initialize Client Object#

Here we directly connect to the local Docker Daemon without excessive configuration, simply initializing the client with environment variable parameters.

// NewEnvClient uses the DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH, DOCKER_API_VERSION from environment variables
cl, err := client.NewEnvClient()

Execute Commands#

The Docker SDK encapsulates commands for pulling images, running containers, checking statuses, etc. For specifics, refer to the documentation. For example, to view the list of images, simply execute the following command:

cl.ImageList(context.Background(), types.ImageListOptions{})

Complete Code#

// main.go
package main

import (
    "context"
    "fmt"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
)

func main() {
    cl, err := client.NewEnvClient()
    if err != nil {
        fmt.Println("Unable to create docker client")
        panic(err)
    }

    fmt.Println(cl.ImageList(context.Background(), types.ImageListOptions{}))

}

Running Result#

$ go run main.go
[{-1 1614986725 sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726 map[]  [hello-world@sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24] [hello-world:latest] -1 13336 13336}] <ni
l>

You can see that the hello-world image is displayed here.

Connect to Remote Docker Daemon#

Connecting to a remote Docker Daemon is similar to connecting to a local one, except that you need to specify the method of connecting to the remote server when initializing the client object. For example, if you want to use TCP to connect to the Docker Daemon at 192.168.64.1:2375, simply replace the client initialization statement with the following:

cl, err := client.NewClient("tcp://192.168.64.1:2375", "", nil, nil)

Connect to Servers Requiring Authentication#

The method of connecting to a remote Docker Daemon mentioned above assumes that the target machine has port 2375 open. However, in most cases, for security reasons, servers have strict restrictions on open ports, and developers usually need to log in to the server via SSH before they can operate Docker on the server. For this situation, we can use the ```github.com/docker/cli/cli/connhelper` library to help us complete the SSH login authentication.

Create Connection Client#

This step is similar to creating a client when sending HTTP requests in Go, but you need to replace the Transport's DialContext with the Dialer provided by connhelper.

helper, _ := connhelper.GetConnectionHelper("ssh://[email protected]:22")
httpClient := &http.Client{
    Transport: &http.Transport{
        DialContext: helper.Dialer,
    },
}

Initialize Docker Client#

Here we no longer use the default configuration from environment variables, but instead initialize the Docker client through configuration parameters, specifying the HTTP client and context information. This way, when we attempt to connect to the remote server's Docker Daemon, connhelper will automatically help us complete the SSH key authentication.

cl, err := client.NewClientWithOpts(
    client.WithHTTPClient(httpClient),
    client.WithHost(helper.Host),
    client.WithDialContext(helper.Dialer),
)

Execute Commands#

We still use the method of viewing the image list to verify the code.

fmt.Println(cl.ImageList(context.Background(), types.ImageListOptions{}))

Complete Code#

package main

import (
    "context"
    "fmt"
    "net/http"

    "github.com/docker/cli/cli/connhelper"
    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
)

func main() {
    helper, err := connhelper.GetConnectionHelper("ssh://[email protected]:22")
    if err != nil {
        return
    }
    httpClient := &http.Client{
        Transport: &http.Transport{
            DialContext: helper.Dialer,
        },
    }
    cl, err := client.NewClientWithOpts(
        client.WithHTTPClient(httpClient),
        client.WithHost(helper.Host),
        client.WithDialContext(helper.Dialer),
    )
    if err != nil {
        fmt.Println("Unable to create docker client")
        panic(err)
    }

    fmt.Println(cl.ImageList(context.Background(), types.ImageListOptions{}))
}

Running Result#

$ go run main.go
[{-1 1614986725 sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726 map[]  [hello-world@sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24] [hello-world:latest] -1 13336 13336}] <ni
l>

Summary#

The Docker SDK encapsulates the commands that the Docker client will use. The Go client for the Docker Engine API provides a detailed introduction to the usage of each command, fully meeting our needs for using programs with the Docker Daemon.

References#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.