Skip to content

fix: ImagePull should close http connection if there is no available data to read.#9369

Merged
samuelkarp merged 3 commits intocontainerd:mainfrom
fuweid:fix-pull-progress
Nov 20, 2023
Merged

fix: ImagePull should close http connection if there is no available data to read.#9369
samuelkarp merged 3 commits intocontainerd:mainfrom
fuweid:fix-pull-progress

Conversation

@fuweid
Copy link
Copy Markdown
Member

@fuweid fuweid commented Nov 15, 2023

Close connection if no more data. It's to fix false alert filed by image
pull progress.

dst = OpenWriter (--> Content Store)

src = Fetch
        Open (--> Registry)
        Mark it as active request

Copy(dst, src) (--> Keep updating total received bytes)

   ^
   |  (Active Request > 0, but total received bytes won't be updated)
   v

defer src.Close()
content.Commit(dst)

Before migrating to transfer service, CRI plugin doesn't limit global
concurrent downloads for ImagePulls. Each ImagePull requests have 3 concurrent
goroutines to download blob and 1 goroutine to unpack blob. Like ext4
filesystem 1, the fsync from content.Commit may sync unrelated dirty pages
into disk. The host is running under IO pressure, and then the content.Commit
will take long time and block other goroutines. If httpreadseeker
doesn't close the connection after io.EOF, this connection will be
considered as active. The pull progress reporter reports there is no
bytes transfered and cancels the ImagePull.

The original 1-minute timeout2 is from kubelet settting. Since CRI-plugin
can't limit the total concurrent downloads, this patch is to update 1-minute
to 5-minutes to prevent from unexpected cancel.

Fixes: #8024
Alternative: #9347

Signed-off-by: Wei Fu [email protected]

@k8s-ci-robot
Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@fuweid fuweid marked this pull request as ready for review November 15, 2023 13:25
@fuweid
Copy link
Copy Markdown
Member Author

fuweid commented Nov 15, 2023

ping @samuelkarp @ruiwen-zhao @thaJeztah please help to review

5-minutes is from https://github.com/kubernetes/kubernetes/blob/1635c380b26a1d8cc25d36e9feace9797f4bae3c/cluster/gce/util.sh#L882 and the information provided by andyzhangx

cc @dmcgowan @ktock since I change the remote/dockers code path. Please take a look. thanks

@thaJeztah
Copy link
Copy Markdown
Member

I'll try to have a look at this one, but not deeply familiar, so don't let that keep others from beating me to it 😂

Alternative: #9347

It's fine to make that Alternative, closes https://github.com/containerd/containerd/pull/9347. That way the other PR gets automatically closed if we decide for this one 👍

Comment thread pkg/cri/config/config_unix.go Outdated
EnableCDI: false,
CDISpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
ImagePullProgressTimeout: time.Minute.String(),
ImagePullProgressTimeout: (5 * time.Minute).String(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick blurb, per my comment on the other PR, it may be good to document where this timeout came from (why we chose this value); which could be a const somewhere that has the documentation on it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot the original comment. Updated. Please take a look.

Comment thread remotes/docker/httpreadseeker.go Outdated
return n, nil
}
}
// close the connection if there is no more available data
Copy link
Copy Markdown
Member

@ktock ktock Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have a note about the reason why we need to immediately release the resource (i.e. caller can rely on the status of the reader for monitoring purpose, etc.).

I'm also a bit curious about whether it's possible to run src.Close() before content.Commit(dst) to make sure releasing resources before the long task.

defer src.Close()
content.Commit(dst)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have a note about the reason why we need to immediately release the resource (i.e. caller can rely on the status of the reader for monitoring purpose, etc.).

Thanks! Updated.

I'm also a bit curious about whether it's possible to run src.Close() before content.Commit(dst) to make sure releasing resources before the long task.

I was thinking about this. But the httpReadSeeker can reconnect if there is unexpected EOF. And the pusher has ErrReset case and the content.Copy helper supports the retry. So, we can't just close the src here.

assert.Equal(t, content.ErrReset, err)

if errors.Is(err, ErrReset) {

@fuweid fuweid force-pushed the fix-pull-progress branch 2 times, most recently from eea52b3 to 2f33a9a Compare November 15, 2023 15:49
Comment thread pkg/cri/config/config.go Outdated
//
// NOTE:
//
// This function is ported from kubelet/dockershim's --image-pull-progress-deadline.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confused about this line of comment. which function is it referring?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Please take a look.

Comment thread remotes/docker/httpreadseeker.go Outdated
// The CRI's imagePullProgressTimeout relies on responseBody.Close to
// update the process monitor's status. If the err is io.EOF, close
// the connection since there is no more available data.
if err == io.EOF {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels like it's better to be a else if here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. PTAL

@fuweid
Copy link
Copy Markdown
Member Author

fuweid commented Nov 17, 2023

CI fails and it needs #9392

Copy link
Copy Markdown
Member

@henry118 henry118 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@samuelkarp samuelkarp added this pull request to the merge queue Nov 17, 2023
@samuelkarp samuelkarp added area/cri Container Runtime Interface (CRI) cherry-pick/1.7.x Change to be cherry picked to release/1.7 branch labels Nov 17, 2023
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 17, 2023
@samuelkarp samuelkarp added this pull request to the merge queue Nov 17, 2023
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 17, 2023
The new active request is filed and there is no bytes read yet when the
progress reporter just wakes up. If the timeout / 2 is less than the
minPullProgressReportInternal, it's easy to file false alert.

We should remove the minPullProgressReportInternal limit.

Fixes: containerd#8024

Signed-off-by: Wei Fu <[email protected]>
Close connection if no more data. It's to fix false alert filed by image
pull progress.

```
dst = OpenWriter (--> Content Store)

src = Fetch
        Open (--> Registry)
        Mark it as active request

Copy(dst, src) (--> Keep updating total received bytes)

   ^
   |  (Active Request > 0, but total received bytes won't be updated)
   v

defer src.Close()
content.Commit(dst)
```

Before migrating to transfer service, CRI plugin doesn't limit global
concurrent downloads for ImagePulls. Each ImagePull requests have 3 concurrent
goroutines to download blob and 1 goroutine to unpack blob. Like ext4
filesystem [1][1], the fsync from content.Commit may sync unrelated dirty pages
into disk. The host is running under IO pressure, and then the content.Commit
will take long time and block other goroutines. If httpreadseeker
doesn't close the connection after io.EOF, this connection will be
considered as active. The pull progress reporter reports there is no
bytes transfered and cancels the ImagePull.

The original 1-minute timeout[2][2] is from kubelet settting. Since CRI-plugin
can't limit the total concurrent downloads, this patch is to update 1-minute
to 5-minutes to prevent from unexpected cancel.

[1]: https://lwn.net/Articles/842385/
[2]: https://github.com/kubernetes/kubernetes/blob/release-1.23/pkg/kubelet/config/flags.go#L45-L48

Signed-off-by: Wei Fu <[email protected]>
@samuelkarp samuelkarp added this pull request to the merge queue Nov 20, 2023
Merged via the queue into containerd:main with commit f12e84b Nov 20, 2023
@thaJeztah thaJeztah added cherry-picked/1.7.x PR commits are cherry-picked into release/1.7 branch and removed cherry-pick/1.7.x Change to be cherry picked to release/1.7 branch labels Nov 27, 2023
@fuweid fuweid deleted the fix-pull-progress branch November 28, 2023 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cri Container Runtime Interface (CRI) cherry-picked/1.7.x PR commits are cherry-picked into release/1.7 branch ok-to-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[test] flaky testcase TestCRIImagePullTimeout/HoldingContentOpenWriter

6 participants