mirror of
https://github.com/apple/container.git
synced 2026-08-24 10:05:43 -05:00
Adds network IPv6 configuration. (#975)
- Part of work for #460. - Enable set/get of IPv6 network prefix in ReservedVmnetNetwork. - Show IPv6 prefix in `network list` full output. - Option for setting IPv6 prefix when creating a network. - System property for default IPv6 prefix. ## Type of Change - [ ] Bug fix - [x] New feature - [ ] Breaking change - [x] Documentation update ## Motivation and Context See #460. ## Testing - [x] Tested locally - [ ] Added/updated tests - [x] Added/updated docs
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "64cb422fa3914611343af4301e317573002890fea7d174e550cc937a76571515",
|
||||
"originHash" : "928d12d151bf6f1a66dad38525ddf6aecbc8390889e72345fe353b89fc304830",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
@@ -15,8 +15,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/containerization.git",
|
||||
"state" : {
|
||||
"revision" : "9ba8267afbdff66e5ddce180312abdb41395292f",
|
||||
"version" : "0.17.0"
|
||||
"revision" : "dcbc7bf71da8fe993f6d19214adb8179eec7d02a",
|
||||
"version" : "0.18.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import PackageDescription
|
||||
let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0"
|
||||
let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified"
|
||||
let builderShimVersion = "0.7.0"
|
||||
let scVersion = "0.17.0"
|
||||
let scVersion = "0.18.0"
|
||||
|
||||
let package = Package(
|
||||
name: "container",
|
||||
|
||||
@@ -32,7 +32,10 @@ extension Application {
|
||||
var labels: [String] = []
|
||||
|
||||
@Option(name: .customLong("subnet"), help: "Set subnet for a network")
|
||||
var subnet: String? = nil
|
||||
var ipv4Subnet: String? = nil
|
||||
|
||||
@Option(name: .customLong("subnet-v6"), help: "Set the IPv6 prefix for a network")
|
||||
var ipv6Subnet: String? = nil
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
@@ -44,8 +47,9 @@ extension Application {
|
||||
|
||||
public func run() async throws {
|
||||
let parsedLabels = Utility.parseKeyValuePairs(labels)
|
||||
let ipv4Subnet = try subnet.map { try CIDRv4($0) }
|
||||
let config = try NetworkConfiguration(id: self.name, mode: .nat, ipv4Subnet: ipv4Subnet, labels: parsedLabels)
|
||||
let ipv4Subnet = try ipv4Subnet.map { try CIDRv4($0) }
|
||||
let ipv6Subnet = try ipv6Subnet.map { try CIDRv6($0) }
|
||||
let config = try NetworkConfiguration(id: self.name, mode: .nat, ipv4Subnet: ipv4Subnet, ipv6Subnet: ipv6Subnet, labels: parsedLabels)
|
||||
let state = try await ClientNetwork.create(configuration: config)
|
||||
print(state.id)
|
||||
}
|
||||
|
||||
@@ -74,6 +74,11 @@ extension Application {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid CIDRv4 address: \(value)")
|
||||
}
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
case .defaultIPv6Subnet:
|
||||
guard (try? CIDRv6(value)) != nil else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid CIDRv6 address: \(value)")
|
||||
}
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public enum DefaultsStore {
|
||||
case defaultKernelBinaryPath = "kernel.binaryPath"
|
||||
case defaultKernelURL = "kernel.url"
|
||||
case defaultSubnet = "network.subnet"
|
||||
case defaultIPv6Subnet = "network.subnetv6"
|
||||
case defaultRegistryDomain = "registry.domain"
|
||||
}
|
||||
|
||||
@@ -73,6 +74,7 @@ public enum DefaultsStore {
|
||||
(.defaultKernelBinaryPath, { Self.get(key: $0) }),
|
||||
(.defaultKernelURL, { Self.get(key: $0) }),
|
||||
(.defaultSubnet, { Self.getOptional(key: $0) }),
|
||||
(.defaultIPv6Subnet, { Self.getOptional(key: $0) }),
|
||||
(.defaultDNSDomain, { Self.getOptional(key: $0) }),
|
||||
(.defaultRegistryDomain, { Self.get(key: $0) }),
|
||||
]
|
||||
@@ -131,7 +133,9 @@ extension DefaultsStore.Keys {
|
||||
case .defaultKernelURL:
|
||||
return "The URL for the kernel file to install, or the URL for an archive containing the kernel file."
|
||||
case .defaultSubnet:
|
||||
return "Default subnet for IP allocation (used on macOS 15 only)."
|
||||
return "Default subnet for IPv4 allocation."
|
||||
case .defaultIPv6Subnet:
|
||||
return "Default IPv6 network prefix."
|
||||
case .defaultRegistryDomain:
|
||||
return "The default registry to use for image references that do not specify a registry."
|
||||
}
|
||||
@@ -153,6 +157,8 @@ extension DefaultsStore.Keys {
|
||||
return String.self
|
||||
case .defaultSubnet:
|
||||
return String.self
|
||||
case .defaultIPv6Subnet:
|
||||
return String.self
|
||||
case .defaultRegistryDomain:
|
||||
return String.self
|
||||
}
|
||||
@@ -180,6 +186,8 @@ extension DefaultsStore.Keys {
|
||||
return "https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz"
|
||||
case .defaultSubnet:
|
||||
return "192.168.64.1/24"
|
||||
case .defaultIPv6Subnet:
|
||||
return "fd00::/64"
|
||||
case .defaultRegistryDomain:
|
||||
return "docker.io"
|
||||
}
|
||||
|
||||
@@ -37,8 +37,11 @@ extension NetworkVmnetHelper {
|
||||
@Option(name: .shortAndLong, help: "Network identifier")
|
||||
var id: String
|
||||
|
||||
@Option(name: .shortAndLong, help: "CIDR address for the subnet")
|
||||
var subnet: String?
|
||||
@Option(name: .customLong("subnet"), help: "CIDR address for the IPv4 subnet")
|
||||
var ipv4Subnet: String?
|
||||
|
||||
@Option(name: .customLong("subnet-v6"), help: "CIDR address for the IPv6 prefix")
|
||||
var ipv6Subnet: String?
|
||||
|
||||
func run() async throws {
|
||||
let commandName = NetworkVmnetHelper._commandName
|
||||
@@ -50,8 +53,14 @@ extension NetworkVmnetHelper {
|
||||
|
||||
do {
|
||||
log.info("configuring XPC server")
|
||||
let ipv4Subnet = try self.subnet.map { try CIDRv4($0) }
|
||||
let configuration = try NetworkConfiguration(id: id, mode: .nat, ipv4Subnet: ipv4Subnet)
|
||||
let ipv4Subnet = try self.ipv4Subnet.map { try CIDRv4($0) }
|
||||
let ipv6Subnet = try self.ipv6Subnet.map { try CIDRv6($0) }
|
||||
let configuration = try NetworkConfiguration(
|
||||
id: id,
|
||||
mode: .nat,
|
||||
ipv4Subnet: ipv4Subnet,
|
||||
ipv6Subnet: ipv6Subnet,
|
||||
)
|
||||
let network = try Self.createNetwork(configuration: configuration, log: log)
|
||||
try await network.start()
|
||||
let server = try await NetworkService(network: network, log: log)
|
||||
|
||||
@@ -283,7 +283,7 @@ public actor NetworksService {
|
||||
serviceIdentifier,
|
||||
]
|
||||
|
||||
if let ipv4Subnet = (configuration.ipv4Subnet.map { $0 }) {
|
||||
if let ipv4Subnet = configuration.ipv4Subnet {
|
||||
var existingCidrs: [CIDRv4] = []
|
||||
for networkState in networkStates.values {
|
||||
if case .running(_, let status) = networkState {
|
||||
@@ -303,6 +303,26 @@ public actor NetworksService {
|
||||
args += ["--subnet", ipv4Subnet.description]
|
||||
}
|
||||
|
||||
if let ipv6Subnet = configuration.ipv6Subnet {
|
||||
var existingCidrs: [CIDRv6] = []
|
||||
for networkState in networkStates.values {
|
||||
if case .running(_, let status) = networkState, let otherIPv6Subnet = status.ipv6Subnet {
|
||||
existingCidrs.append(otherIPv6Subnet)
|
||||
}
|
||||
}
|
||||
let overlap = existingCidrs.first {
|
||||
$0.contains(ipv6Subnet.lower)
|
||||
|| $0.contains(ipv6Subnet.upper)
|
||||
|| ipv6Subnet.contains($0.lower)
|
||||
|| ipv6Subnet.contains($0.upper)
|
||||
}
|
||||
if let overlap {
|
||||
throw ContainerizationError(.exists, message: "IPv6 subnet \(ipv6Subnet) overlaps an existing network with subnet \(overlap)")
|
||||
}
|
||||
|
||||
args += ["--subnet-v6", ipv6Subnet.description]
|
||||
}
|
||||
|
||||
try await pluginLoader.registerWithLaunchd(
|
||||
plugin: networkPlugin,
|
||||
pluginStateRoot: store.entityUrl(configuration.id),
|
||||
|
||||
@@ -67,7 +67,12 @@ public actor AllocationOnlyVmnetNetwork: Network {
|
||||
let subnet = DefaultsStore.get(key: .defaultSubnet)
|
||||
let subnetCIDR = try CIDRv4(subnet)
|
||||
let gateway = IPv4Address(subnetCIDR.lower.value + 1)
|
||||
self._state = .running(configuration, NetworkStatus(ipv4Subnet: subnetCIDR, ipv4Gateway: gateway))
|
||||
let status = NetworkStatus(
|
||||
ipv4Subnet: subnetCIDR,
|
||||
ipv4Gateway: gateway,
|
||||
ipv6Subnet: nil,
|
||||
)
|
||||
self._state = .running(configuration, status)
|
||||
log.info(
|
||||
"started allocation-only network",
|
||||
metadata: [
|
||||
|
||||
@@ -32,6 +32,9 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
|
||||
/// The preferred CIDR address for the IPv4 subnet, if specified
|
||||
public let ipv4Subnet: CIDRv4?
|
||||
|
||||
/// The preferred CIDR address for the IPv6 subnet, if specified
|
||||
public let ipv6Subnet: CIDRv6?
|
||||
|
||||
/// Key-value labels for the network.
|
||||
public var labels: [String: String] = [:]
|
||||
|
||||
@@ -40,12 +43,14 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
|
||||
id: String,
|
||||
mode: NetworkMode,
|
||||
ipv4Subnet: CIDRv4? = nil,
|
||||
ipv6Subnet: CIDRv6? = nil,
|
||||
labels: [String: String] = [:]
|
||||
) throws {
|
||||
self.id = id
|
||||
self.creationDate = Date()
|
||||
self.mode = mode
|
||||
self.ipv4Subnet = ipv4Subnet
|
||||
self.ipv6Subnet = ipv6Subnet
|
||||
self.labels = labels
|
||||
try validate()
|
||||
}
|
||||
@@ -55,6 +60,7 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
|
||||
case creationDate
|
||||
case mode
|
||||
case ipv4Subnet
|
||||
case ipv6Subnet
|
||||
case labels
|
||||
// TODO: retain for deserialization compatability for now, remove later
|
||||
case subnet
|
||||
@@ -72,6 +78,8 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
|
||||
try container.decodeIfPresent(String.self, forKey: .ipv4Subnet)
|
||||
?? container.decodeIfPresent(String.self, forKey: .subnet)
|
||||
ipv4Subnet = try subnetText.map { try CIDRv4($0) }
|
||||
ipv6Subnet = try container.decodeIfPresent(String.self, forKey: .ipv6Subnet)
|
||||
.map { try CIDRv6($0) }
|
||||
labels = try container.decodeIfPresent([String: String].self, forKey: .labels) ?? [:]
|
||||
try validate()
|
||||
}
|
||||
@@ -83,7 +91,8 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(creationDate, forKey: .creationDate)
|
||||
try container.encode(mode, forKey: .mode)
|
||||
try container.encodeIfPresent(ipv4Subnet?.description, forKey: .ipv4Subnet)
|
||||
try container.encodeIfPresent(ipv4Subnet, forKey: .ipv4Subnet)
|
||||
try container.encodeIfPresent(ipv6Subnet, forKey: .ipv6Subnet)
|
||||
try container.encode(labels, forKey: .labels)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,40 +21,23 @@ public struct NetworkStatus: Codable, Sendable {
|
||||
/// The address allocated for the network if no subnet was specified at
|
||||
/// creation time; otherwise, the subnet from the configuration.
|
||||
public let ipv4Subnet: CIDRv4
|
||||
|
||||
/// The gateway IPv4 address.
|
||||
public let ipv4Gateway: IPv4Address
|
||||
|
||||
/// The address allocated for the IPv6 network if no subnet was specified at
|
||||
/// creation time; otherwise, the IPv6 subnet from the configuration.
|
||||
public let ipv6Subnet: CIDRv6?
|
||||
|
||||
public init(
|
||||
ipv4Subnet: CIDRv4,
|
||||
ipv4Gateway: IPv4Address
|
||||
ipv4Gateway: IPv4Address,
|
||||
ipv6Subnet: CIDRv6?,
|
||||
) {
|
||||
self.ipv4Subnet = ipv4Subnet
|
||||
self.ipv4Gateway = ipv4Gateway
|
||||
self.ipv6Subnet = ipv6Subnet
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case ipv4Subnet
|
||||
case ipv4Gateway
|
||||
}
|
||||
|
||||
/// Create a network status from the supplied Decoder.
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let addressText = try container.decode(String.self, forKey: .ipv4Subnet)
|
||||
ipv4Subnet = try CIDRv4(addressText)
|
||||
let gatewayText = try container.decode(String.self, forKey: .ipv4Gateway)
|
||||
ipv4Gateway = try IPv4Address(gatewayText)
|
||||
}
|
||||
|
||||
/// Encode the network status to the supplied Encoder.
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(ipv4Subnet.description, forKey: .ipv4Subnet)
|
||||
try container.encode(ipv4Gateway.description, forKey: .ipv4Gateway)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// The configuration and runtime attributes for a network.
|
||||
|
||||
@@ -39,6 +39,7 @@ public final class ReservedVmnetNetwork: Network {
|
||||
let network: vmnet_network_ref
|
||||
let ipv4Subnet: CIDRv4
|
||||
let ipv4Gateway: IPv4Address
|
||||
let ipv6Subnet: CIDRv6
|
||||
}
|
||||
|
||||
private let stateMutex: Mutex<State>
|
||||
@@ -79,7 +80,11 @@ public final class ReservedVmnetNetwork: Network {
|
||||
|
||||
let networkInfo = try startNetwork(configuration: configuration, log: log)
|
||||
|
||||
let networkStatus = NetworkStatus(ipv4Subnet: networkInfo.ipv4Subnet, ipv4Gateway: networkInfo.ipv4Gateway)
|
||||
let networkStatus = NetworkStatus(
|
||||
ipv4Subnet: networkInfo.ipv4Subnet,
|
||||
ipv4Gateway: networkInfo.ipv4Gateway,
|
||||
ipv6Subnet: networkInfo.ipv6Subnet,
|
||||
)
|
||||
state.networkState = NetworkState.running(configuration, networkStatus)
|
||||
state.network = networkInfo.network
|
||||
}
|
||||
@@ -102,10 +107,6 @@ public final class ReservedVmnetNetwork: Network {
|
||||
]
|
||||
)
|
||||
|
||||
// with the reservation API, subnet priority is CLI argument, UserDefault, auto
|
||||
let defaultSubnet = try DefaultsStore.getOptional(key: .defaultSubnet).map { try CIDRv4($0) }
|
||||
let subnet = configuration.ipv4Subnet ?? defaultSubnet
|
||||
|
||||
// set up the vmnet configuration
|
||||
var status: vmnet_return_t = .VMNET_SUCCESS
|
||||
guard let vmnetConfiguration = vmnet_network_configuration_create(vmnet.operating_modes_t.VMNET_SHARED_MODE, &status), status == .VMNET_SUCCESS else {
|
||||
@@ -114,21 +115,42 @@ public final class ReservedVmnetNetwork: Network {
|
||||
|
||||
vmnet_network_configuration_disable_dhcp(vmnetConfiguration)
|
||||
|
||||
// set the subnet if the caller provided one
|
||||
if let subnet {
|
||||
let gateway = IPv4Address(subnet.lower.value + 1)
|
||||
// subnet priority is CLI argument, UserDefault, auto
|
||||
let defaultIpv4Subnet = try DefaultsStore.getOptional(key: .defaultSubnet).map { try CIDRv4($0) }
|
||||
let ipv4Subnet = configuration.ipv4Subnet ?? defaultIpv4Subnet
|
||||
let defaultIpv6Subnet = try DefaultsStore.getOptional(key: .defaultIPv6Subnet).map { try CIDRv6($0) }
|
||||
let ipv6Subnet = configuration.ipv6Subnet ?? defaultIpv6Subnet
|
||||
|
||||
// set the IPv4 subnet if the caller provided one
|
||||
if let ipv4Subnet {
|
||||
let gateway = IPv4Address(ipv4Subnet.lower.value + 1)
|
||||
var gatewayAddr = in_addr()
|
||||
inet_pton(AF_INET, gateway.description, &gatewayAddr)
|
||||
let mask = IPv4Address(subnet.prefix.prefixMask32)
|
||||
let mask = IPv4Address(ipv4Subnet.prefix.prefixMask32)
|
||||
var maskAddr = in_addr()
|
||||
inet_pton(AF_INET, mask.description, &maskAddr)
|
||||
log.info(
|
||||
"configuring vmnet subnet",
|
||||
metadata: ["cidr": "\(subnet)"]
|
||||
"configuring vmnet IPv4 subnet",
|
||||
metadata: ["cidr": "\(ipv4Subnet)"]
|
||||
)
|
||||
let status = vmnet_network_configuration_set_ipv4_subnet(vmnetConfiguration, &gatewayAddr, &maskAddr)
|
||||
guard status == .VMNET_SUCCESS else {
|
||||
throw ContainerizationError(.internalError, message: "failed to set subnet \(subnet) for network \(configuration.id)")
|
||||
throw ContainerizationError(.internalError, message: "failed to set subnet \(ipv4Subnet) for IPv4 network \(configuration.id)")
|
||||
}
|
||||
}
|
||||
|
||||
// set the IPv6 network prefix if the caller provided one
|
||||
if let ipv6Subnet {
|
||||
let gateway = IPv6Address(ipv6Subnet.lower.value + 1)
|
||||
var gatewayAddr = in6_addr()
|
||||
inet_pton(AF_INET6, gateway.description, &gatewayAddr)
|
||||
log.info(
|
||||
"configuring vmnet IPv6 prefix",
|
||||
metadata: ["cidr": "\(ipv6Subnet)"]
|
||||
)
|
||||
let status = vmnet_network_configuration_set_ipv6_prefix(vmnetConfiguration, &gatewayAddr, ipv6Subnet.prefix.length)
|
||||
guard status == .VMNET_SUCCESS else {
|
||||
throw ContainerizationError(.internalError, message: "failed to set prefix \(ipv6Subnet) for IPv6 network \(configuration.id)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,15 +170,33 @@ public final class ReservedVmnetNetwork: Network {
|
||||
let runningSubnet = try CIDRv4(lower: lower, upper: upper)
|
||||
let runningGateway = IPv4Address(runningSubnet.lower.value + 1)
|
||||
|
||||
var prefixAddr = in6_addr()
|
||||
var prefixLength = UInt8(0)
|
||||
vmnet_network_get_ipv6_prefix(network, &prefixAddr, &prefixLength)
|
||||
guard let prefix = Prefix(length: prefixLength) else {
|
||||
throw ContainerizationError(.internalError, message: "invalid IPv6 prefix length \(prefixLength) for network \(configuration.id)")
|
||||
}
|
||||
let prefixIpv6Bytes = withUnsafeBytes(of: prefixAddr.__u6_addr.__u6_addr8) {
|
||||
Array($0)
|
||||
}
|
||||
let prefixIpv6Addr = IPv6Address(prefixIpv6Bytes)
|
||||
let runningV6Subnet = try CIDRv6(prefixIpv6Addr, prefix: prefix)
|
||||
|
||||
log.info(
|
||||
"started vmnet network",
|
||||
metadata: [
|
||||
"id": "\(configuration.id)",
|
||||
"mode": "\(configuration.mode)",
|
||||
"cidr": "\(runningSubnet)",
|
||||
"cidrv6": "\(runningV6Subnet)",
|
||||
]
|
||||
)
|
||||
|
||||
return NetworkInfo(network: network, ipv4Subnet: runningSubnet, ipv4Gateway: runningGateway)
|
||||
return NetworkInfo(
|
||||
network: network,
|
||||
ipv4Subnet: runningSubnet,
|
||||
ipv4Gateway: runningGateway,
|
||||
ipv6Subnet: runningV6Subnet,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +131,10 @@ public actor SandboxService {
|
||||
|
||||
// Dynamically configure the DNS nameserver from a network if no explicit configuration
|
||||
if let dns = config.dns, dns.nameservers.isEmpty {
|
||||
if let nameserver = try await self.getDefaultNameserver(attachmentConfigurations: config.networks) {
|
||||
let defaultNameservers = try await self.getDefaultNameservers(attachmentConfigurations: config.networks)
|
||||
if !defaultNameservers.isEmpty {
|
||||
config.dns = ContainerConfiguration.DNSConfiguration(
|
||||
nameservers: [nameserver],
|
||||
nameservers: defaultNameservers,
|
||||
domain: dns.domain,
|
||||
searchDomains: dns.searchDomains,
|
||||
options: dns.options
|
||||
@@ -859,17 +860,17 @@ public actor SandboxService {
|
||||
Self.configureInitialProcess(czConfig: &czConfig, config: config)
|
||||
}
|
||||
|
||||
private func getDefaultNameserver(attachmentConfigurations: [AttachmentConfiguration]) async throws -> String? {
|
||||
private func getDefaultNameservers(attachmentConfigurations: [AttachmentConfiguration]) async throws -> [String] {
|
||||
for attachmentConfiguration in attachmentConfigurations {
|
||||
let client = NetworkClient(id: attachmentConfiguration.network)
|
||||
let state = try await client.state()
|
||||
guard case .running(_, let status) = state else {
|
||||
continue
|
||||
}
|
||||
return status.ipv4Gateway.description
|
||||
return [status.ipv4Gateway.description]
|
||||
}
|
||||
|
||||
return nil
|
||||
return []
|
||||
}
|
||||
|
||||
private static func configureInitialProcess(
|
||||
|
||||
@@ -653,7 +653,7 @@ Creates a new network with the given name.
|
||||
**Usage**
|
||||
|
||||
```bash
|
||||
container network create [--label <label> ...] [--debug] <name>
|
||||
container network create [--label <label> ...] [--subnet <subnet>] [--subnet-v6 <subnet-v6>] [--debug] <name>
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
@@ -663,6 +663,8 @@ container network create [--label <label> ...] [--debug] <name>
|
||||
**Options**
|
||||
|
||||
* `--label <label>`: Set metadata for a network
|
||||
* `--subnet <subnet>`: Set the IPv4 subnet for a network (CIDR format, e.g., 192.168.100.0/24)
|
||||
* `--subnet-v6 <subnet-v6>`: Set the IPv6 prefix for a network (CIDR format, e.g., fd00:1234::/64)
|
||||
|
||||
### `container network delete (rm)`
|
||||
|
||||
|
||||
+28
-1
@@ -286,6 +286,12 @@ This command creates a network named `foo`:
|
||||
container network create foo
|
||||
```
|
||||
|
||||
You can also specify custom IPv4 and IPv6 subnets when creating a network:
|
||||
|
||||
```bash
|
||||
container network create foo --subnet 192.168.100.0/24 --subnet-v6 fd00:1234::/64
|
||||
```
|
||||
|
||||
The `foo` network, the default network, and any other networks you create are isolated from one another. A container on one network has no connectivity to containers on other networks.
|
||||
|
||||
Run `container network list` to see the networks that exist:
|
||||
@@ -319,6 +325,26 @@ container stop my-web-server
|
||||
container network delete foo
|
||||
```
|
||||
|
||||
Networks support both IPv4 and IPv6. When creating a network without explicit subnet options, the system uses default values if configured via system properties (see below), or automatically allocates subnets. The system validates that custom subnets don't overlap with existing networks.
|
||||
|
||||
## Configure default network subnets
|
||||
|
||||
You can customize the default IPv4 and IPv6 subnets used for new networks using system properties.
|
||||
|
||||
### Set default IPv4 subnet
|
||||
|
||||
```bash
|
||||
container system property set network.subnet 192.168.100.1/24
|
||||
```
|
||||
|
||||
### Set default IPv6 prefix
|
||||
|
||||
```bash
|
||||
container system property set network.subnetv6 fd00:abcd::/64
|
||||
```
|
||||
|
||||
These settings apply to networks created without explicit `--subnet` or `--subnet-v6` options.
|
||||
|
||||
## View container logs
|
||||
|
||||
The `container logs` command displays the output from your containerized application:
|
||||
@@ -460,7 +486,8 @@ image.builder String ghcr.io/apple/container-builder-shim/... The image r
|
||||
image.init String ghcr.io/apple/containerization/vminit... The image reference for the default initial filesystem image.
|
||||
kernel.binaryPath String opt/kata/share/kata-containers/vmlinu... If the kernel URL is for an archive, the archive member pathname for the kernel file.
|
||||
kernel.url String https://github.com/kata-containers/ka... The URL for the kernel file to install, or the URL for an archive containing the kernel file.
|
||||
network.subnet String *undefined* Default subnet for IP allocation (used on macOS 15 only).
|
||||
network.subnet String *undefined* Default subnet for IPv4 allocation.
|
||||
network.subnetv6 String *undefined* Default IPv6 network prefix.
|
||||
```
|
||||
|
||||
### Example: Disable Rosetta for builds
|
||||
|
||||
Reference in New Issue
Block a user