Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add docs for terminals #1730

Merged
merged 1 commit into from
Jun 25, 2018
Merged

Conversation

deitch
Copy link
Contributor

@deitch deitch commented Feb 22, 2018

As discussed with @cyphar , a first attempt to document how to use terminals with runc. See discussion here particularly this comment

I am sure there are some errors here, so happy to take updates.

@cyphar
Copy link
Member

cyphar commented Feb 23, 2018

I will take a thorough look over this when I get back from vacation.

@cyphar cyphar self-requested a review February 23, 2018 07:29
@deitch
Copy link
Contributor Author

deitch commented Mar 14, 2018

Hey @cyphar . How was vacation/holiday? Did I capture it well? Happy to make the next person to walk down my path have an easier walk. :-)

Copy link
Member

@cyphar cyphar left a comment

Choose a reason for hiding this comment

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

My main concern with the current state of this is that there are some assumptions you've made (see inline comments for details) that aren't quite right.

You've also exclusively focused on the shell usecase -- which is important -- but feels like the wrong way of explaining this to me. Really, shells are the exception (especially the part where you talk about interspersed output and indeterminate input race conditions) and make it hard to understand what is meant to be happening.

The rest of it looks good though.


Here is what I'd recommend doing:

  • Move the parts that talk about shell-specific details to a warning section at the bottom of the document (or in a block quote with a NOTE).

  • I would introduce "new terminal" first and then discuss pass-through as being "not new terminal". In particular, with non-detached mode runc's pass-through is more like a pipe than a pass-through (so the description you give first confuses the later clarification you have).

  • I'm not sure if you saw the security worries about using pass-through (detached) in a shell, but please include those (if you need clarification on them feel free to ping me). This is actually the most significant issue with using detached pass-through in a shell.

* new terminal

### Pass-Through
In pass-through mode, the `stdio` passed to the `runc` call will be passed as is to the primary container. This means that if you do the following:
Copy link
Member

Choose a reason for hiding this comment

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

This isn't totally accurate. In the case of non-detached, the stdio passed to the container are actually pipes (which are then read from by the still-running runc process). The reason this might be important to someone is so they know that fd=2 in your example isn't actually the file /tmp/log.out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure I knew that, although it makes sense. Updating.


Then the stdout of `some_container` will appear in the file `/tmp/log.out`, the stderr will appear in `/tmp/log.err`, and the process will receive input of `input`. `runc`'s stdout was redirected to `/tmp/log.out`, and `runc`, in turn, passed that handle to the container itself.

To invoke pass-through mode, configure your container's `config.json` with `terminal: false` (which is the default).
Copy link
Member

Choose a reason for hiding this comment

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

It's not the default if you use runc spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it still is the default if no terminal: setting is specific. runc spec just happens to actually specify it in the config.json. But I don't mind updating it.

You can use either terminal mode with either runc mode. However, there are considerations that may indicate preference for one mode over another.

### Foreground
When running in foreground, `runc` and the container you invoke are most like running a regular foreground process. This is the scenario most likely to be useful for running in pass-through terminal mode. As the example above showed:
Copy link
Member

Choose a reason for hiding this comment

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

This is the scenario most likely to be useful for running in pass-through terminal mode.

Unfortunately it's not as simple as this. It really depends on how you're using pass-through. If you want to set up your own stdio then actually detached makes more sense in a lot of cases (unless you don't want to bother with writing the exit-code handling).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My inclination would be to leave this part. It doesn't say "only", but rather "most likely". I am coming at this as someone who hadn't messed around with runc before, banged my head on the wall to understand, and am trying to save others the headache. Simplifications, along with options to get deeper, are the best way to explain it to people IMHO.

Copy link
Member

Choose a reason for hiding this comment

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

The problem is that in this case (foreground and "pass-through", runc creates a pipe that is used for stdio). So the description isn't quite right. However, this is quite ... nuanced and if you like I can reword it after it's merged so you don't have to keep doing drafts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please do. I think I need to see how you rewrite it once it is in.

When running runc detached with terminal Pass-Through, the stdin/stdout/stderr of the calling process will be passed to `runc` and from there to the container's process. However, since `runc` detaches, there are several side effects to take into account:

* The stdout/stderr of the process can become intermixed with the stdout/stderr of the calling process.
* Any input into the calling process can cause indeterminate issues with which process actually receives the input.
Copy link
Member

Choose a reason for hiding this comment

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

These notes appear to only be meaningful for shell users (and quite a few other points also seem quite shell-focused). Really this should just be a caution to not use pass-through detached in a shell -- rather than a warning about pass-through detached in general.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is that true? I could have a process with its own stdout/stderr/stdin that is not a shell, and if it calls runc detached with pass-through, then the intermixing will happen, and anything fed into stdin will be indeterminate. Granted, stdin is used most often in shells, but it is not exclusive. I think the issue you described to me (slack? wherever it was...) actually is a general one.

Copy link
Member

Choose a reason for hiding this comment

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

That's fair enough.


Further, a third, possibly temporary problem exists. Pass-Through Detached only works correctly when stdout/stderr are either an actual terminal or a file. If you use _anything_ else, e.g. a pipe, it will hang, for reasons unknown. There is an [open issue](https://github.com/opencontainers/runc/issues/1721) on this.

For this reason, we **strongly recommend** using New Terminal mode when running `runc` detached.
Copy link
Member

Choose a reason for hiding this comment

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

... unless you set up your own stdio outside the container.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Fixing.

3. Listen on the socket to get the master file descriptor
4. Use that file descriptor to control stdio

Note the one shortcoming: the single descriptor means that you have mixed stdout/stderr. There is no way to separate them. Essentially, this is a _console_, which has no separate stdout and stderr.
Copy link
Member

Choose a reason for hiding this comment

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

(Though this shortcoming is identical to regular terminal emulators -- take a look at /proc/self/fd/[12] in your shell right now.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course. It really is only something you can separate when you start a new process with separate descriptors. Happy to add a comment here.


* `0`: standard-in (`stdin`), the input stream into the process
* `1`: standard-out (`stdout`), the output stream from the process
* `2`: standard-error (`stderr`), the error stream from the process
Copy link
Member

Choose a reason for hiding this comment

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

I would recommend at least mentioning --preserve-fds here, with the note that it is separate to all of this handling for stdio and that those file descriptors are passed verbatim in all cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK. I just added something at the end.

@deitch
Copy link
Contributor Author

deitch commented Mar 19, 2018

@cyphar I updated with most of the changes requested; have another look.

I'm not sure if you saw the security worries about using pass-through (detached) in a shell, but please include those (if you need clarification on them feel free to ping me). This is actually the most significant issue with using detached pass-through in a shell.

Please do clarify?

@deitch
Copy link
Contributor Author

deitch commented Apr 10, 2018

@cyphar any update?

@deitch
Copy link
Contributor Author

deitch commented May 6, 2018

Hi @cyphar ; this is almost 2 months old. Can we review the changes and get it in?

```

## Other File Descriptors
Completely separately from stdio - file descriptors `0`,`1`,`2` - you also can pass other file descriptors to the container process "as is" using `--preserve-fds <descriptor>`. Thus, for example, if your calling process has an open handle to the file `/var/foo` as file descriptor `5`, and you want to pass it to your container process, you can call `runc`:
Copy link
Member

Choose a reason for hiding this comment

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

This description is not quite correct -- --preserve-fds 5 means that all file descriptors from 3 to 5 (inclusive) are preserved. The way this is currently written it looks like you're saying that only 5 is preserved.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixing now.

$ echo input | runc run some_container > /tmp/log.out 2>& /tmp/log.err
```

Nonetheless, you still have the option, when running in foreground runc mode, of using New Terminal mode. However, if the process is short-lived, you will not have much time to retrieve the console's fds from the socket and doing anything with it.
Copy link
Member

Choose a reason for hiding this comment

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

... for instance this last sentence is inaccurate because you cannot use --console-socket with foreground mode (and so from the host management perspective there's no difference between the two terminal modes in foreground -- though there is a difference in the container). But as I said above, I can fix this up separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, please do.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to remove the inaccurate part if we like to merge this now.

@cyphar
Copy link
Member

cyphar commented May 6, 2018

Sorry for the long wait. I've added some comments. However, I have a feeling at this point we might as well merge it (with the --preserve-fds section fixed) and I can reword the relevant parts in my own time (since I'm sure it's annoying to re-send drafts).

@cyphar cyphar closed this May 6, 2018
@cyphar
Copy link
Member

cyphar commented May 6, 2018

Eugh, sorry. Wrong button.

@cyphar cyphar reopened this May 6, 2018
@deitch
Copy link
Contributor Author

deitch commented May 6, 2018

However, I have a feeling at this point we might as well merge it (with the --preserve-fds section fixed) and I can reword the relevant parts in my own time (since I'm sure it's annoying to re-send drafts).

Sure. I will do an immediate update to the best of my knowledge and send it back to you.

@deitch
Copy link
Contributor Author

deitch commented May 6, 2018

My one request, please add your patches soon after merging? I finally understand most of it, want to grasp the entire thing! :-)

@deitch
Copy link
Contributor Author

deitch commented May 6, 2018

Updated the --preserve-fds comment. Back at you @cyphar .

@cyphar
Copy link
Member

cyphar commented Jun 15, 2018

LGTM. I will do a rewrite of the relevant parts after it's merged.

/cc @opencontainers/runc-maintainers

@cyphar
Copy link
Member

cyphar commented Jun 15, 2018

PullApprove appears to be broken? /cc @caniszczyk

@caniszczyk
Copy link
Contributor

caniszczyk commented Jun 15, 2018

LGTM

Approved with PullApprove

@caniszczyk
Copy link
Contributor

@cyphar weird try to LGTM again

@dqminh
Copy link
Contributor

dqminh commented Jun 15, 2018

@cyphar does it make more sense to just commit directly into the branch instead of us having to merge with some incompleted / incorrect parts ?

Copy link
Contributor

@dqminh dqminh left a comment

Choose a reason for hiding this comment

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

Thanks for doing this ! I just have a few nits.

lrwx------ 1 root root 64 Mar 19 07:09 /proc/self/fd/2 -> /0
```

The following sample code, partially taken from the reference implementation [recvtty](https://github.com/opencontainers/runc/blob/master/contrib/cmd/recvtty/recvtty.go), shows how to accept a file descriptor for the socket. It does not contain the usual `err` handling and `defer`red closing.
Copy link
Contributor

Choose a reason for hiding this comment

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

i would prefer to just point directly to recvtty.go rather than attaching sample code here so there are one less place to keep change.

$ echo input | runc run some_container > /tmp/log.out 2>& /tmp/log.err
```

Nonetheless, you still have the option, when running in foreground runc mode, of using New Terminal mode. However, if the process is short-lived, you will not have much time to retrieve the console's fds from the socket and doing anything with it.
Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to remove the inaccurate part if we like to merge this now.

@cyphar
Copy link
Member

cyphar commented Jun 15, 2018

@dqminh Ah, I didn't even check if @deitch has given us push access. I'll send some corrections to his branch then re-LGTM it.

@deitch
Copy link
Contributor Author

deitch commented Jun 15, 2018

I did give push access IIRC. I always open PRs that way.

@cyphar
Copy link
Member

cyphar commented Jun 24, 2018

@deitch I have done a fairly extensive rewrite of the document, and now I'm pretty happy with how it stands. Since it still has your name on it, I'd like your blessing before we merge it -- does the current document help explain how things work?

/cc @opencontainers/runc-maintainers

@cyphar
Copy link
Member

cyphar commented Jun 24, 2018

LGTM (obviously) -- but wait for @deitch's response before merging.

Approved with PullApprove

@deitch
Copy link
Contributor Author

deitch commented Jun 24, 2018

I'd like your blessing before we merge it

Aleksa is the expert here! Not sure asking my opinion makes sense... :-)

I will do a quick review.

In addition to `--preserve-fds`, `LISTEN_FDS` file descriptors are passed
automatically to allow for `systemd`-style socket activation. They always take
up the first file descriptors after `stdio` (and `--preserve-fds` always follow).
To disable this, simply unset `LISTEN_FDS`.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. what happens if LISTEN_FDS are set and --preserve-fds? An example like the above might help?
  2. By default, isn't LISTEN_FDS unset anyways?

Copy link
Member

Choose a reason for hiding this comment

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

  1. What happens if LISTEN_FDS are set and --preserve-fds? An example like the above might help?

The first LISTEN_FDS file descriptors come first, then the --preserve-fds ones (the --help for runc run explains it a bit). I could try to add an example, but LISTEN_FDS is a bit difficult to given an example of without having a systemd service and all the rest of that fun stuff.

  1. By default, isn't LISTEN_FDS unset anyways?

Yup, but it is important to mention (because it's quite related to --preserve-fds).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That helps, thanks.

passing of file descriptors -- [details below](#runc-modes)). As an example:

```
% echo input | runc run some_container > /tmp/log.out 2>& /tmp/log.err
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm... isn't runc's default mode terminal: true?

Copy link
Member

Choose a reason for hiding this comment

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

Yup, though in this section I thought it was implied. I can add a one-liner about it.

to the container's `stdio`. The purpose of this option is to allow a user to
set up `stdio` for a container themselves and then force `runc` to just use
their pre-prepared `stdio` (without any pseudo-terminal funny business). *If
you don't see why this would be useful, don't use this option.*
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I love this line!

> path name.

In order to help users make use of detached new terminal mode, we have provided
a [Go implementation in the `go-runc` bindings][containerd/go-runc.Socket].
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This reference is very helpful.

to a Unix domain socket which `runc` will connect to and send the
pseudo-terminal master file descriptor down (after which `runc` will exit and
the only holder of the pseudo-terminal master file descriptor will be whatever
process read the file descriptor from the socket).
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe add a paragraph that says something like the following?

So to use runc in "Detached New Terminal" mode:

  1. Set up a unix domain socket
  2. Run runc in detached new terminal mode, passing it the path to the socket as the argument to --console-socket
  3. Retrieve the file descriptor for the new pseudo-terminal from the socket
  4. Use the file descriptor to communicate with stdio of the container, now running detached

Copy link
Member

Choose a reason for hiding this comment

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

Sure, I'll explain what the binding does (roughly).

@deitch
Copy link
Contributor Author

deitch commented Jun 24, 2018

Thanks @cyphar , really better. I made one small suggestion, and 1-2 places I was a bit confused.

In the end, this is complex, so it will be confusing. But this is a really good starting point.

@cyphar
Copy link
Member

cyphar commented Jun 24, 2018

@deitch Fixed, PTAL.

Copy link
Contributor Author

@deitch deitch left a comment

Choose a reason for hiding this comment

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

Just that one typo. Other than that, really great.

1. Create a unix domain socket at some path, `$socket_path`.
2. Call `runc run` or `runc create` with the argument `--console-socket
$socket_path`.
3. Using `recvmsg(2)` retreive the file descriptor sent using `SCM_RIGHTS` by
Copy link
Contributor Author

Choose a reason for hiding this comment

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

One nit: retrieve instead of retreive on the above line.

Users can get very confused by how terminals work with runc, and the
quite confusing "terminal: ..." option. Add a document which goes
through all of the important parts of terminal handling in runc, in the
hopes that we can just point people to this as an explanation.

Signed-off-by: Avi Deitcher <[email protected]>
[cyphar: quite a large rewrite to fix factual errors and structure]
Co-authored-by: Avi Deitcher <[email protected]>
Signed-off-by: Aleksa Sarai <[email protected]>
@cyphar
Copy link
Member

cyphar commented Jun 24, 2018

LGTM.

/cc @opencontainers/runc-maintainers

Approved with PullApprove

@crosbymichael
Copy link
Member

LGTM

@crosbymichael crosbymichael merged commit 0d05939 into opencontainers:master Jun 25, 2018
@deitch deitch deleted the document-terminal branch June 25, 2018 15:27
@deitch
Copy link
Contributor Author

deitch commented Jun 25, 2018

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants