Skip to content

Commit

Permalink
client: enable HTTP(S) keep-alive
Browse files Browse the repository at this point in the history
Enable HTTP(S) keep-alive to improve network performance and reduce
latency.  We need several HTTP(S) requests before we get to request the
blob and each of them requires a new HTTP(S) connection and that slows
down significantly pulling images, especially on networks with a
higher latency (e.g. wifi).
This will allow multiple requests to be sent over a single connection,
reducing the overhead of establishing new connections for each
request.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
  • Loading branch information
giuseppe committed Feb 27, 2023
1 parent b350ee3 commit c3691cd
Show file tree
Hide file tree
Showing 9 changed files with 27 additions and 4 deletions.
5 changes: 5 additions & 0 deletions docker/daemon/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func TestDockerClientFromNilSystemContext(t *testing.T) {

assert.Equal(t, dockerclient.DefaultDockerHost, client.DaemonHost(), "The default docker host should have been used")
assert.Equal(t, defaultAPIVersion, client.ClientVersion(), "The default api version should have been used")

assert.NoError(t, client.Close())
}

func TestDockerClientFromCertContext(t *testing.T) {
Expand All @@ -38,6 +40,8 @@ func TestDockerClientFromCertContext(t *testing.T) {

assert.Equal(t, host, client.DaemonHost())
assert.Equal(t, "1.22", client.ClientVersion())

assert.NoError(t, client.Close())
}

func TestTlsConfigFromInvalidCertPath(t *testing.T) {
Expand Down Expand Up @@ -94,6 +98,7 @@ func TestSpecifyPlainHTTPViaHostScheme(t *testing.T) {
assert.NotNil(t, client, "A Docker client reference should have been returned")

assert.Equal(t, host, client.DaemonHost())
assert.NoError(t, client.Close())
}

func testDir(t *testing.T) string {
Expand Down
1 change: 1 addition & 0 deletions docker/daemon/daemon_dest.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func newImageDestination(ctx context.Context, sys *types.SystemContext, ref daem

// imageLoadGoroutine accepts tar stream on reader, sends it to c, and reports error or success by writing to statusChannel
func imageLoadGoroutine(ctx context.Context, c *client.Client, reader *io.PipeReader, statusChannel chan<- error) {
defer c.Close()
err := errors.New("Internal error: unexpected panic in imageLoadGoroutine")
defer func() {
logrus.Debugf("docker-daemon: sending done, status %v", err)
Expand Down
2 changes: 2 additions & 0 deletions docker/daemon/daemon_src.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref daemonRef
if err != nil {
return nil, fmt.Errorf("initializing docker engine client: %w", err)
}
defer c.Close()

// Per NewReference(), ref.StringWithinTransport() is either an image ID (config digest), or a !reference.NameOnly() reference.
// Either way ImageSave should create a tarball with exactly one image.
inputStream, err := c.ImageSave(ctx, []string{ref.StringWithinTransport()})
Expand Down
10 changes: 10 additions & 0 deletions docker/docker_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
// signatureBase is always set in the return value
// The caller must call .Close() on the returned client when done.
func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, registryConfig *registryConfiguration, write bool, actions string) (*dockerClient, error) {
auth, err := config.GetCredentialsForRef(sys, ref.ref)
if err != nil {
Expand Down Expand Up @@ -247,6 +248,7 @@ func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, regis
// (e.g., "registry.com[:5000][/some/namespace]/repo").
// Please note that newDockerClient does not set all members of dockerClient
// (e.g., username and password); those must be set by callers if necessary.
// The caller must call .Close() on the returned client when done.
func newDockerClient(sys *types.SystemContext, registry, reference string) (*dockerClient, error) {
hostName := registry
if registry == dockerHostname {
Expand Down Expand Up @@ -302,6 +304,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
if err != nil {
return fmt.Errorf("creating new docker client: %w", err)
}
defer client.Close()
client.auth = types.DockerAuthConfig{
Username: username,
Password: password,
Expand Down Expand Up @@ -371,6 +374,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
if err != nil {
return nil, fmt.Errorf("creating new docker client: %w", err)
}
defer client.Close()
client.auth = auth
if sys != nil {
client.registryToken = sys.DockerBearerRegistryToken
Expand Down Expand Up @@ -1084,3 +1088,9 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe
func sigstoreAttachmentTag(d digest.Digest) string {
return strings.Replace(d.String(), ":", "-", 1) + ".sig"
}

// Close removes resources associated with an initialized dockerClient, if any.
func (c *dockerClient) Close() error {
c.client.CloseIdleConnections()
return nil
}
2 changes: 2 additions & 0 deletions docker/docker_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
if err != nil {
return nil, fmt.Errorf("failed to create client: %w", err)
}
defer client.Close()

tags := make([]string, 0)

Expand Down Expand Up @@ -136,6 +137,7 @@ func GetDigest(ctx context.Context, sys *types.SystemContext, ref types.ImageRef
if err != nil {
return "", fmt.Errorf("failed to create client: %w", err)
}
defer client.Close()

path := fmt.Sprintf(manifestPath, reference.Path(dr.ref), tagOrDigest)
headers := map[string][]string{
Expand Down
2 changes: 1 addition & 1 deletion docker/docker_image_dest.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (d *dockerImageDestination) Reference() types.ImageReference {

// Close removes resources associated with an initialized ImageDestination, if any.
func (d *dockerImageDestination) Close() error {
return nil
return d.c.Close()
}

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
Expand Down
4 changes: 3 additions & 1 deletion docker/docker_image_src.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logica
s.Compat = impl.AddCompat(s)

if err := s.ensureManifestIsLoaded(ctx); err != nil {
client.Close()
return nil, err
}
return s, nil
Expand All @@ -166,7 +167,7 @@ func (s *dockerImageSource) Reference() types.ImageReference {

// Close removes resources associated with an initialized ImageSource, if any.
func (s *dockerImageSource) Close() error {
return nil
return s.c.Close()
}

// simplifyContentType drops parameters from a HTTP media type (see https://tools.ietf.org/html/rfc7231#section-3.1.1.1)
Expand Down Expand Up @@ -605,6 +606,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
if err != nil {
return err
}
defer c.Close()

headers := map[string][]string{
"Accept": manifest.DefaultRequestedManifestMIMETypes,
Expand Down
1 change: 1 addition & 0 deletions oci/layout/oci_src.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func (s *ociImageSource) Reference() types.ImageReference {

// Close removes resources associated with an initialized ImageSource, if any.
func (s *ociImageSource) Close() error {
s.client.CloseIdleConnections()
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/tlsclientconfig/tlsclientconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ func NewTransport() *http.Transport {
Proxy: http.ProxyFromEnvironment,
DialContext: direct.DialContext,
TLSHandshakeTimeout: 10 * time.Second,
// TODO(dmcgowan): Call close idle connections when complete and use keep alive
DisableKeepAlives: true,
IdleConnTimeout: 90 * time.Second,
MaxIdleConns: 100,
}
return tr
}

0 comments on commit c3691cd

Please sign in to comment.