-
Notifications
You must be signed in to change notification settings - Fork 18.9k
Implement private and shareable ipc modes #34087
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
Conversation
42827a6 to
ea5b4eb
Compare
This builds (and depends) on moby/moby#34087 Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
thaJeztah
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @kolyshkin - I left some comments (but interested in other reviews as well)
Wondering would this also reduce the chance of /shm "device in use" problems?
cmd/dockerd/config_unix.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the default be set here? ("shareable" - perhaps a constant defaultIPCMode)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps mention the possible options as well, given that they're limited;
`Default mode for containers ipc ("debug" | "info")`
daemon/container_operations_unix.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, there's already a couple of these, but we should try to avoid the API being imported into the daemon package. The daemon should not be aware of the API.
daemon/daemon_unix.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd change the wording a bit here.. something like;
"IPC mode (%v) is not supported as default value. Select either 'shareable' or 'private')".
daemon/container_operations_unix.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look "right". The IpcMode should be stored in the container's hostconfig, not requested from the daemon. When creating the container, we should;
- check if
docker run --ipc=..is set - if not, get the daemon's default, and set it to that value
Doing so prevents the container's IPC-mode to be updated if the daemon defaults are changed and the container restarted. Possibly have a similar function as runconfig.SetDefaultNetModeIfBlank(), and apply the defaults in daemon.create()
Unrelated to this PR, but looks like we should really evaluate where defaults should be set. I noticed the SetDefaultNetModeIfBlank() method is called in three different locations(!);
daemon.create()daemon.setHostConfig()(which is called bydaemon.create()), andContainerConfigWrapper.getHostConfig(), which is ultimately called throughpostContainersCreate()andpostContainersStart()(last one for legacy reasons))
daemon/container_operations_unix.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here as above; at this point, c.HostConfig.IpcMode should already be set to a value
daemon/daemon_unix.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the IpcMode is set during create, this will have to be updated to (e.g.) containertypes.IpcMode(config.IpcMode).Valid(). At this point we should not be checking the daemon defaults, but the container's setting.
verifyDefaultIpcMode() could still be needed, but only when verifying the daemon configuration (i.e. when starting the daemon and/or (re)loading the daemon configuration
daemon/oci_linux.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same remarks apply here as I made in container_operations_unix.go. The container should already have this configuration set at this point
daemon/oci_linux.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should also be set earlier, so that it's baked into the container's config
daemon/oci_linux.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c.HostConfig.IpcMode.IsPrivate() (related to earlier comments)
runconfig/hostconfig_test.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would it be more readable to use a struct for these? The second loop for parsing container names could be combined in that case;
func TestIpcModeTest(t *testing.T) {
ipcModes := map[container.IpcMode]struct {
private bool
host bool
container bool
shareable bool
valid bool
ctrName string
}{
"": {valid: true},
"private": {private: true, valid: true},
"something": {},
"something:weird": {ctrName: "weird"},
":weird": {ctrName: "weird"},
"host": {host: true, valid: true},
"container": {},
"container:name": {container: true, ctrName: "name", valid: true},
"container:name1:name2": {container: true, ctrName: "name1:name2", valid: true},
"container:": {container: true, valid: true},
"shareable": {shareable: true, valid: true},
}
for ipcMode, state := range ipcModes {
assert.Equal(t, state.private, ipcMode.IsPrivate(), "IpcMode.IsPrivate() parsing failed for %q", ipcMode)
assert.Equal(t, state.host, ipcMode.IsHost(), "IpcMode.IsHost() parsing failed for %q", ipcMode)
assert.Equal(t, state.container, ipcMode.IsContainer(), "IpcMode.IsContainer() parsing failed for %q", ipcMode)
assert.Equal(t, state.shareable, ipcMode.IsShareable(), "IpcMode.IsShareable() parsing failed for %q", ipcMode)
assert.Equal(t, state.valid, ipcMode.Valid(), "IpcMode.Valid() parsing failed for %q", ipcMode)
assert.Equal(t, state.ctrName, ipcMode.Container(), "IpcMode.Container() parsing failed for %q", ipcMode)
}
}|
The way we have gone after this is to add a RUNC hook oci-umount which cleans up leaked mount points into a container. |
1db6766 to
aa438d4
Compare
ab5cac0 to
e3be977
Compare
|
@thaJeztah thank you for review! I think I addressed all your comments in the updated patches, can you please take another look? |
|
Thank you @kolyshkin ! I gave it a quick glance, and definitely looks better; I'll do a full review soon. |
Done. Thank you for reviewing! |
|
Windows test failure seems to be caused by some tests (or execution environment) race, not related to the patchset. Will restart |
|
All green now; merging 👍 |
It was noted[1] that container's HostConfig.ShmSize, if not set, should be initialized to daemon default value during container creation. In fact, it is already done in daemon.adaptContainerSettings, so we can use value from container.HostConfig directly. [1] moby/moby#34087 (comment) Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Upstream-commit: 0fb1fb1 Component: engine
Since the commit d88fe44 ("Add support for sharing /dev/shm/ and /dev/mqueue between containers") container's /dev/shm is mounted on the host first, then bind-mounted inside the container. This is done that way in order to be able to share this container's IPC namespace (and the /dev/shm mount point) with another container. Unfortunately, this functionality breaks container checkpoint/restore (even if IPC is not shared). Since /dev/shm is an external mount, its contents is not saved by `criu checkpoint`, and so upon restore any application that tries to access data under /dev/shm is severily disappointed (which usually results in a fatal crash). This commit solves the issue by introducing new IPC modes for containers (in addition to 'host' and 'container:ID'). The new modes are: - 'shareable': enables sharing this container's IPC with others (this used to be the implicit default); - 'private': disables sharing this container's IPC. In 'private' mode, container's /dev/shm is truly mounted inside the container, without any bind-mounting from the host, which solves the issue. While at it, let's also implement 'none' mode. The motivation, as eloquently put by Justin Cormack, is: > I wondered a while back about having a none shm mode, as currently it is > not possible to have a totally unwriteable container as there is always > a /dev/shm writeable mount. It is a bit of a niche case (and clearly > should never be allowed to be daemon default) but it would be trivial to > add now so maybe we should... ...so here's yet yet another mode: - 'none': no /dev/shm mount inside the container (though it still has its own private IPC namespace). Now, to ultimately solve the abovementioned checkpoint/restore issue, we'd need to make 'private' the default mode, but unfortunately it breaks the backward compatibility. So, let's make the default container IPC mode per-daemon configurable (with the built-in default set to 'shareable' for now). The default can be changed either via a daemon CLI option (--default-shm-mode) or a daemon.json configuration file parameter of the same name. Note one can only set either 'shareable' or 'private' IPC modes as a daemon default (i.e. in this context 'host', 'container', or 'none' do not make much sense). Some other changes this patch introduces are: 1. A mount for /dev/shm is added to default OCI Linux spec. 2. IpcMode.Valid() is simplified to remove duplicated code that parsed 'container:ID' form. Note the old version used to check that ID does not contain a semicolon -- this is no longer the case (tests are modified accordingly). The motivation is we should either do a proper check for container ID validity, or don't check it at all (since it is checked in other places anyway). I chose the latter. 3. IpcMode.Container() is modified to not return container ID if the mode value does not start with "container:", unifying the check to be the same as in IpcMode.IsContainer(). 3. IPC mode unit tests (runconfig/hostconfig_test.go) are modified to add checks for newly added values. [v2: addressed review at moby/moby#34087 (review)] [v3: addressed review at moby/moby#34087 (review)] [v4: addressed the case of upgrading from older daemon, in this case container.HostConfig.IpcMode is unset and this is valid] [v5: document old and new IpcMode values in api/swagger.yaml] [v6: add the 'none' mode, changelog entry to docs/api/version-history.md] Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Upstream-commit: 7120976 Component: engine
This builds (and depends) on moby/moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Upstream-commit: 9285db67525c4dce4308903dcf80627f693388dc Component: cli
This builds (and depends) on moby/moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This builds (and depends) on moby/moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This builds (and depends) on moby/moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Currently, Docker make IPC of every container shareable by default, which means other containers can join it's IPC namespace. This is implemented by creating a tmpfs mount on the host, and then bind-mounting it to a container's /dev/shm. Other containers that want to share the same IPC (and the same /dev/shm) can also bind-mount the very same host's mount. Now, since moby/moby@7120976d7 (moby/moby#34087) there is a possiblity to have per-daemon default of having "private" IPC mode, meaning all the containers created will have non-shareable /dev/shm. For shared IPC to work in the above scenario, we need to explicitly make the "pause" container's IPC mode as "shareable", which is what this commit does. To test: add "default-ipc-mode: private" to /etc/docker/daemon.json, try using kube as usual, there should be no errors. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
It was noted[1] that container's HostConfig.ShmSize, if not set, should be initialized to daemon default value during container creation. In fact, it is already done in daemon.adaptContainerSettings, so we can use value from container.HostConfig directly. [1] moby/moby#34087 (comment) Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Upstream-commit: 0fb1fb1 Component: engine
Since the commit d88fe44 ("Add support for sharing /dev/shm/ and /dev/mqueue between containers") container's /dev/shm is mounted on the host first, then bind-mounted inside the container. This is done that way in order to be able to share this container's IPC namespace (and the /dev/shm mount point) with another container. Unfortunately, this functionality breaks container checkpoint/restore (even if IPC is not shared). Since /dev/shm is an external mount, its contents is not saved by `criu checkpoint`, and so upon restore any application that tries to access data under /dev/shm is severily disappointed (which usually results in a fatal crash). This commit solves the issue by introducing new IPC modes for containers (in addition to 'host' and 'container:ID'). The new modes are: - 'shareable': enables sharing this container's IPC with others (this used to be the implicit default); - 'private': disables sharing this container's IPC. In 'private' mode, container's /dev/shm is truly mounted inside the container, without any bind-mounting from the host, which solves the issue. While at it, let's also implement 'none' mode. The motivation, as eloquently put by Justin Cormack, is: > I wondered a while back about having a none shm mode, as currently it is > not possible to have a totally unwriteable container as there is always > a /dev/shm writeable mount. It is a bit of a niche case (and clearly > should never be allowed to be daemon default) but it would be trivial to > add now so maybe we should... ...so here's yet yet another mode: - 'none': no /dev/shm mount inside the container (though it still has its own private IPC namespace). Now, to ultimately solve the abovementioned checkpoint/restore issue, we'd need to make 'private' the default mode, but unfortunately it breaks the backward compatibility. So, let's make the default container IPC mode per-daemon configurable (with the built-in default set to 'shareable' for now). The default can be changed either via a daemon CLI option (--default-shm-mode) or a daemon.json configuration file parameter of the same name. Note one can only set either 'shareable' or 'private' IPC modes as a daemon default (i.e. in this context 'host', 'container', or 'none' do not make much sense). Some other changes this patch introduces are: 1. A mount for /dev/shm is added to default OCI Linux spec. 2. IpcMode.Valid() is simplified to remove duplicated code that parsed 'container:ID' form. Note the old version used to check that ID does not contain a semicolon -- this is no longer the case (tests are modified accordingly). The motivation is we should either do a proper check for container ID validity, or don't check it at all (since it is checked in other places anyway). I chose the latter. 3. IpcMode.Container() is modified to not return container ID if the mode value does not start with "container:", unifying the check to be the same as in IpcMode.IsContainer(). 3. IPC mode unit tests (runconfig/hostconfig_test.go) are modified to add checks for newly added values. [v2: addressed review at moby/moby#34087 (review)] [v3: addressed review at moby/moby#34087 (review)] [v4: addressed the case of upgrading from older daemon, in this case container.HostConfig.IpcMode is unset and this is valid] [v5: document old and new IpcMode values in api/swagger.yaml] [v6: add the 'none' mode, changelog entry to docs/api/version-history.md] Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Upstream-commit: 7120976 Component: engine
This builds (and depends) on moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This builds (and depends) on moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> (cherry picked from commit c23d4b0) Signed-off-by: Cory Snider <csnider@mirantis.com>
This builds (and depends) on moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> (cherry picked from commit c23d4b0) Signed-off-by: Cory Snider <csnider@mirantis.com>
This builds (and depends) on moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> (cherry picked from commit c23d4b0) Signed-off-by: Cory Snider <csnider@mirantis.com>
This builds (and depends) on moby#34087 Version 2: - remove --ipc argument validation (it is now done by daemon) - add/document 'none' value - docs/reference/run.md: add a table with better modes description - dockerd(8) typesetting fixes Version 3: - remove ipc mode tests from cli/command/container/opts_test.go Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Since the commit d88fe44 ("Add support for sharing /dev/shm/ and
/dev/mqueue between containers") container's /dev/shm is mounted on the
host first, then bind-mounted inside the container. This is done that
way in order to be able to share this container's IPC namespace
(and the /dev/shm mount point) with another container.
Unfortunately, this functionality breaks container checkpoint/restore
(even if IPC is not shared). Since /dev/shm is an external mount, its
contents is not saved by
criu checkpoint, and so upon restore anyapplication that tries to access data under /dev/shm is severily
disappointed as the data is not there (which usually results in a fatal crash). For example, see
https://github.com/xemul/criu/issues/300 and https://github.com/xemul/criu/issues/320.
This commit solves the issue by introducing new IPC modes for containers
(in addition to
hostandcontainer:ID). The new modes are:shareable: enables sharing this container's IPC with others(this used to be a default behavior)
private: disables sharing this container's IPC.In
privatemode, container's /dev/shm is truly mounted inside thecontainer, without any bind-mounting from the host, which solves the
issue for checkpoint/restore.
Surely, to ultimately solve it, we'd need to make
privatethe defaultmode, but it breaks the backward compatibility. So, let's make the
default container IPC mode per-daemon configurable (with built-in
default set to
shareablefor now). The default can be changedeither via a daemon CLI option (
--default-shm-mode) or configurationfile parameter of the same name.
Surely, one can only set either
shareableorprivateIPC modes as adaemon default. Other modes (like 'host' or 'container') do not make
sense in this context and are therefore not allowed.
Some other changes this patch introduces are:
A mount for /dev/shm is added to default OCI Linux spec.
IpcMode.Valid()is simplified to remove duplicated code that parsed'container:ID' form. Note the old version used to check that ID does
not contain a semicolon -- this is no longer the case (tests are
modified accordingly). The motivation is we should either do a
proper check for container ID validity, or don't check it at all
(since it is checked in other places anyway). I chose the latter.
IPC mode tests are modified to add checks for newly added values.
I did some manual testing, and it appears to be working.
TODO: write integration testsTest cases come in the second patch, and they cover the scenarios I can think of.