mirror of
https://github.com/apple/container.git
synced 2026-10-14 10:09:10 +00:00
LinuxPod: Wire up pid namespace sharing (#434)
In a prior change I'd added a way for vminitd to double as a simple pause container. This change wires this up by adding a new bool to the pod config to ask for pid ns sharing.
This commit is contained in:
1 parent
c45fef7278
commit
2d27ef6deb
5 files changed
+186
-7
No files matched your search
@@ -50,6 +50,9 @@ public final class LinuxPod: Sendable {
|
||||
public var virtualization: Bool = false
|
||||
/// Optional file path to store serial boot logs.
|
||||
public var bootLog: BootLog?
|
||||
/// Whether containers in the pod should share a PID namespace.
|
||||
/// When enabled, all containers can see each other's processes.
|
||||
public var shareProcessNamespace: Bool = false
|
||||
|
||||
public init() {}
|
||||
}
|
||||
@@ -103,6 +106,7 @@ public final class LinuxPod: Sendable {
|
||||
private struct State: Sendable {
|
||||
var phase: Phase
|
||||
var containers: [String: PodContainer]
|
||||
var pauseProcess: LinuxProcess?
|
||||
}
|
||||
|
||||
private enum Phase: Sendable {
|
||||
@@ -173,7 +177,7 @@ public final class LinuxPod: Sendable {
|
||||
try configuration(&config)
|
||||
|
||||
self.config = config
|
||||
self.state = AsyncMutex(State(phase: .initialized, containers: [:]))
|
||||
self.state = AsyncMutex(State(phase: .initialized, containers: [:], pauseProcess: nil))
|
||||
}
|
||||
|
||||
private static func createDefaultRuntimeSpec(_ containerID: String, podID: String) -> Spec {
|
||||
@@ -303,9 +307,64 @@ extension LinuxPod {
|
||||
|
||||
do {
|
||||
let containers = state.containers
|
||||
let shareProcessNamespace = self.config.shareProcessNamespace
|
||||
let pauseProcessHolder = Mutex<LinuxProcess?>(nil)
|
||||
|
||||
try await vm.withAgent { agent in
|
||||
try await agent.standardSetup()
|
||||
|
||||
// Create pause container if PID namespace sharing is enabled
|
||||
if shareProcessNamespace {
|
||||
let pauseID = "pause-\(self.id)"
|
||||
let pauseRootfsPath = "/run/container/\(pauseID)/rootfs"
|
||||
|
||||
// Bind mount /sbin into the pause container rootfs.
|
||||
// This is where the guest agent lives.
|
||||
try await agent.mount(
|
||||
ContainerizationOCI.Mount(
|
||||
type: "",
|
||||
source: "/sbin",
|
||||
destination: "\(pauseRootfsPath)/sbin",
|
||||
options: ["bind"]
|
||||
))
|
||||
|
||||
var pauseSpec = Self.createDefaultRuntimeSpec(pauseID, podID: self.id)
|
||||
pauseSpec.process?.args = ["/sbin/vminitd", "pause"]
|
||||
pauseSpec.hostname = ""
|
||||
pauseSpec.mounts = LinuxContainer.defaultMounts().map {
|
||||
ContainerizationOCI.Mount(
|
||||
type: $0.type,
|
||||
source: $0.source,
|
||||
destination: $0.destination,
|
||||
options: $0.options
|
||||
)
|
||||
}
|
||||
pauseSpec.linux?.namespaces = [
|
||||
LinuxNamespace(type: .cgroup),
|
||||
LinuxNamespace(type: .ipc),
|
||||
LinuxNamespace(type: .mount),
|
||||
LinuxNamespace(type: .pid),
|
||||
LinuxNamespace(type: .uts),
|
||||
]
|
||||
|
||||
// Create LinuxProcess for pause container
|
||||
let process = LinuxProcess(
|
||||
pauseID,
|
||||
containerID: pauseID,
|
||||
spec: pauseSpec,
|
||||
io: LinuxProcess.Stdio(stdin: nil, stdout: nil, stderr: nil),
|
||||
ociRuntimePath: nil,
|
||||
agent: agent,
|
||||
vm: vm,
|
||||
logger: self.logger
|
||||
)
|
||||
|
||||
try await process.start()
|
||||
pauseProcessHolder.withLock { $0 = process }
|
||||
|
||||
self.logger?.debug("Pause container started", metadata: ["pid": "\(process.pid)"])
|
||||
}
|
||||
|
||||
// Mount all container rootfs
|
||||
for (_, container) in containers {
|
||||
guard let attachments = vm.mounts[container.id], let rootfsAttachment = attachments.first else {
|
||||
@@ -353,6 +412,8 @@ extension LinuxPod {
|
||||
}
|
||||
}
|
||||
|
||||
state.pauseProcess = pauseProcessHolder.withLock { $0 }
|
||||
|
||||
// Transition all containers to created state
|
||||
for id in state.containers.keys {
|
||||
state.containers[id]?.state = .created
|
||||
@@ -394,6 +455,33 @@ extension LinuxPod {
|
||||
let containerMounts = createdState.vm.mounts[containerID] ?? []
|
||||
spec.mounts = containerMounts.dropFirst().map { $0.to }
|
||||
|
||||
// Configure namespaces for the container
|
||||
var namespaces: [LinuxNamespace] = [
|
||||
LinuxNamespace(type: .cgroup),
|
||||
LinuxNamespace(type: .ipc),
|
||||
LinuxNamespace(type: .mount),
|
||||
LinuxNamespace(type: .uts),
|
||||
]
|
||||
|
||||
// Either join pause container's pid ns or create a new one
|
||||
if self.config.shareProcessNamespace, let pausePID = state.pauseProcess?.pid {
|
||||
let nsPath = "/proc/\(pausePID)/ns/pid"
|
||||
|
||||
self.logger?.debug(
|
||||
"Container joining pause PID namespace",
|
||||
metadata: [
|
||||
"container": "\(containerID)",
|
||||
"pausePID": "\(pausePID)",
|
||||
"nsPath": "\(nsPath)",
|
||||
])
|
||||
|
||||
namespaces.append(LinuxNamespace(type: .pid, path: nsPath))
|
||||
} else {
|
||||
namespaces.append(LinuxNamespace(type: .pid))
|
||||
}
|
||||
|
||||
spec.linux?.namespaces = namespaces
|
||||
|
||||
let stdio = IOUtil.setup(
|
||||
portAllocator: self.hostVsockPorts,
|
||||
stdin: container.config.process.stdin,
|
||||
|
||||
Reference in new issue
Block a user