fix: shutdown logger after container process exits#2337
fix: shutdown logger after container process exits#2337mharwani wants to merge 6 commits intocontainerd:mainfrom
Conversation
97ef2b7 to
9d8a14f
Compare
|
Hi @mharwani, as mentioned here closing task io io.Close() after task exiting should close FIFOs => send EOF to stdio => logger will receive |
Hi @fahedouch, I tried closing the task IO upon container exit with
|
| base := testutil.NewBase(t) | ||
| containerName := testutil.Identifier(t) | ||
| defer base.Cmd("rm", containerName).Run() | ||
| expected := []string{} |
There was a problem hiding this comment.
| expected := []string{} | |
| expected := make([]string, 10) |
| } | ||
|
|
||
| base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage, | ||
| "sh", "-euc", "for i in `seq 1 10`; do echo $i; sleep 1; done").AssertOK() |
There was a problem hiding this comment.
| "sh", "-euc", "for i in `seq 1 10`; do echo $i; sleep 1; done").AssertOK() | |
| "sh", "-euc", "for i in `seq 1 10`; do echo $i; done").AssertOK() |
There was a problem hiding this comment.
I added sleep to have both the container task and the logs command running concurrently
| base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage, | ||
| "sh", "-euc", "for i in `seq 1 10`; do echo $i; sleep 1; done").AssertOK() | ||
| base.Cmd("logs", "-f", containerName).AssertOutContainsAll(expected...) | ||
| base.Cmd("rm", "-f", containerName).AssertOK() |
| t.Parallel() | ||
| base := testutil.NewBase(t) | ||
| containerName := testutil.Identifier(t) | ||
| defer base.Cmd("rm", containerName).Run() |
There was a problem hiding this comment.
| defer base.Cmd("rm", containerName).Run() | |
| defer base.Cmd("rm", "-f", containerName).Run() |
| base.Cmd("rm", "-f", containerName).AssertOK() | ||
| } | ||
|
|
||
| func TestLogsWithRunningContainer(t *testing.T) { |
There was a problem hiding this comment.
The other test runs the log command after the container has stopped running, but this one tests logs during the task execution and as logs are being sent to the logger with the --follow option.
| go func() { | ||
| <-waitCh | ||
| // wait 100ms to let logViewer process data sent after container exit, if any | ||
| time.Sleep(100 * time.Millisecond) |
There was a problem hiding this comment.
have you experienced data not processed by the LogViewer?
There was a problem hiding this comment.
Yes, the logger may process additional data after container exits and there is no newline or EOF. For example, the following will not print any logs without time.Sleep:
sudo nerdctl run -d --name test alpine sh -c 'sleep 10; echo -n hello'
sudo nerdctl logs -f test
Sleeping for 100ms is undeterministic. There should be a better way to do this. Maybe somehow wait for the logger to finish instead of container wait?
There was a problem hiding this comment.
Yes, waiting for the logger to complete instead of waiting for the container to exit gives a more accurate result
There was a problem hiding this comment.
Sorry, didn't get a chance to work on it. I will send a new revision later today.
There was a problem hiding this comment.
@fahedouch To wait for the logger to complete, it has to send a signal to the LogViewer. I'm using file locks to accomplish this IPC. PTAL here and here. Thanks!
| if err := driver.PreProcess(dataStore, config); err != nil { | ||
| return err | ||
| } | ||
| exit, err := getContainerWait(ctx, hostAddress, config) |
There was a problem hiding this comment.
| exit, err := getContainerWait(ctx, hostAddress, config) | |
| exitCh, err := getContainerWait(ctx, hostAddress, config) |
| stderrR, stderrW := io.Pipe() | ||
| copyStream := func(reader io.Reader, writer *io.PipeWriter) { | ||
| // copy using a buffer of size 16K | ||
| buf := make([]byte, 16*1024) |
There was a problem hiding this comment.
| buf := make([]byte, 16*1024) | |
| buf := make([]byte, 16<<10) |
There was a problem hiding this comment.
Changed it to 32k as that is what containerd uses to copy stdio:
https://github.com/containerd/containerd/blob/main/cio/io.go#L34
|
|
||
| // generateLogConfig creates a LogConfig for the current container store | ||
| func generateLogConfig(dataStore string, id string, logDriver string, logOpt []string, ns string) (logConfig logging.LogConfig, err error) { | ||
| func generateLogConfig(dataStore string, id string, logDriver string, logOpt []string, gOpt *types.GlobalCommandOptions) (logConfig logging.LogConfig, err error) { |
There was a problem hiding this comment.
why passing a pointer here gOpt *types.GlobalCommandOptions?
9d8a14f to
8763da2
Compare
|
It looks like the environment setup is failing for some of the checks because curl cannot reach the endpoint https://raw.githubusercontent.com/AkihiroSuda/containerized-systemd/v0.1.1/docker-entrypoint.sh: Any ideas? |
|
CI is now green for Linux. |
fahedouch
left a comment
There was a problem hiding this comment.
@mharwani PTAL #2337 (comment)
8763da2 to
23b24a6
Compare
|
Windows tests are failing |
| } | ||
| if err := ready(); err != nil { | ||
|
|
||
| lockFile := getLockPath(dataStore, config.Namespace, config.ID) |
There was a problem hiding this comment.
may be loggerLock is more explicit here
3ce8c17 to
1df7878
Compare
Signed-off-by: Mrudul Harwani <mharwani@amazon.com>
Signed-off-by: Mrudul Harwani <mharwani@amazon.com>
…ocessed data Signed-off-by: Mrudul Harwani <mharwani@amazon.com>
Signed-off-by: Mrudul Harwani <mharwani@amazon.com>
Signed-off-by: Mrudul Harwani <mharwani@amazon.com>
1df7878 to
39fe0b3
Compare
Signed-off-by: Mrudul Harwani <mharwani@amazon.com>
39fe0b3 to
0050ac5
Compare
| for { | ||
| select { | ||
| case <-ctx.Done(): | ||
| return nil, fmt.Errorf("timed out waiting for container task to start") |
There was a problem hiding this comment.
| return nil, fmt.Errorf("timed out waiting for container task to start") | |
| return nil, errors.New("timed out waiting for container task to start") |
|
|
||
| // If task was not found, it's possible that the container runtime is still being created. | ||
| // Retry every 100ms. | ||
| ticker := time.NewTicker(100 * time.Millisecond) |
There was a problem hiding this comment.
| ticker := time.NewTicker(100 * time.Millisecond) | |
| ticker := time.NewTicker(100 * time.Millisecond) | |
| defer ticker.Stop() |
|
@mharwani PTAL |
|
ping @mharwani |
|
Hey @mharwani Would you still be interested in carrying this PR through? |
|
Sorry for ping again @mharwani Would you still be interested in carrying this PR through? |
…xits [carry #2337] ensure logger completion after container exit
Addresses #2313
Container task is not deleted after it exits, leaving the runtime shim and logger open throughout the lifecycle of a container. This can be a problem when there is unprocessed
stdoutdata after a container task finishes and the logger is blocked forever waiting for a newline or EOF.This PR fixes the issue by piping container output instead of reading directly from
stdoutandstderrfile descriptors. Closing the io pipes upon task exit allows the logger to shutdown gracefully and process leftover data without a newline .