Prechádzať zdrojové kódy

Merge pull request #8390 from MalteJ/set-macaddress

Adding docker-cli run param to set MAC address
Alexandr Morozov 10 rokov pred
rodič
commit
9eb8fcd58a

+ 1 - 0
daemon/container.go

@@ -457,6 +457,7 @@ func (container *Container) AllocateNetwork() error {
 	)
 
 	job := eng.Job("allocate_interface", container.ID)
+	job.Setenv("RequestedMac", container.Config.MacAddress)
 	if env, err = job.Stdout.AddEnv(); err != nil {
 		return err
 	}

+ 9 - 0
docs/man/docker-run.1.md

@@ -29,6 +29,7 @@ docker-run - Run a command in a new container
 [**-m**|**--memory**[=*MEMORY*]]
 [**--name**[=*NAME*]]
 [**--net**[=*"bridge"*]]
+[**--mac-address**[=*MACADDRESS*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-p**|**--publish**[=*[]*]]
 [**--privileged**[=*false*]]
@@ -187,6 +188,14 @@ and foreground Docker containers.
                                'container:<name|id>': reuses another container network stack
                                'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
 
+**--mac-address**=*macaddress*
+   Set the MAC address for the container's Ethernet device:
+    --mac-address=12:34:56:78:9a:bc
+
+Remember that the MAC address in an Ethernet network must be unique.
+The IPv6 link-local address will be based on the device's MAC address
+according to RFC4862.
+
 **-P**, **--publish-all**=*true*|*false*
    When set to true publish all exposed ports to the host interfaces. The
 default is false. If the operator uses -P (or -p) then Docker will make the

+ 12 - 2
docs/sources/articles/networking.md

@@ -104,6 +104,9 @@ Finally, several networking options can only be provided when calling
  *  `--net=bridge|none|container:NAME_or_ID|host` — see
     [How Docker networks a container](#container-networking)
 
+ *  `--mac-address=MACADDRESS...` — see
+    [How Docker networks a container](#container-networking)
+
  *  `-p SPEC` or `--publish=SPEC` — see
     [Binding container ports](#binding-ports)
 
@@ -537,9 +540,15 @@ The steps with which Docker configures a container are:
     separate and unique network interface namespace, there are no
     physical interfaces with which this name could collide.
 
-4.  Give the container's `eth0` a new IP address from within the
+4.  Set the interface's MAC address according to the `--mac-address`
+    parameter or generate a random one.
+
+5.  Give the container's `eth0` a new IP address from within the
     bridge's range of network addresses, and set its default route to
-    the IP address that the Docker host owns on the bridge.
+    the IP address that the Docker host owns on the bridge. If available
+    the IP address is generated from the MAC address. This prevents ARP
+    cache invalidation problems, when a new container comes up with an
+    IP used in the past by another container with another MAC.
 
 With these steps complete, the container now possesses an `eth0`
 (virtual) network card and will find itself able to communicate with
@@ -621,6 +630,7 @@ Docker do all of the configuration:
 
     $ sudo ip link set B netns $pid
     $ sudo ip netns exec $pid ip link set dev B name eth0
+    $ sudo ip netns exec $pid ip link set eth0 address 12:34:56:78:9a:bc
     $ sudo ip netns exec $pid ip link set eth0 up
     $ sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0
     $ sudo ip netns exec $pid ip route add default via 172.17.42.1

+ 4 - 0
docs/sources/reference/api/docker_remote_api.md

@@ -52,6 +52,10 @@ You can still call an old version of the API using
 `info` now returns the number of CPUs available on the machine (`NCPU`) and
 total memory available (`MemTotal`).
 
+`POST /containers/create`
+**New!**
+You can set the new container's MAC address explicitly.
+
 ## v1.15
 
 ### Full Documentation

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.15.md

@@ -131,6 +131,7 @@ Create a container
              },
              "WorkingDir":"",
              "NetworkDisabled": false,
+             "MacAddress":"12:34:56:78:9a:bc",
              "ExposedPorts":{
                      "22/tcp": {}
              },

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.16.md

@@ -131,6 +131,7 @@ Create a container
              },
              "WorkingDir":"",
              "NetworkDisabled": false,
+             "MacAddress":"12:34:56:78:9a:bc",
              "ExposedPorts":{
                      "22/tcp": {}
              },

+ 8 - 0
docs/sources/reference/commandline/cli.md

@@ -516,6 +516,7 @@ Creates a new container.
       --lxc-conf=[]              (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
       -m, --memory=""            Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
       --name=""                  Assign a name to the container
+      --mac-address=""           Set the container's MAC address
       --net="bridge"             Set the Network mode for the container
                                    'bridge': creates a new network stack for the container on the docker bridge
                                    'none': no networking for this container
@@ -867,6 +868,13 @@ straightforward manner.
 
     $ sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $INSTANCE_ID
 
+**Get an instance's MAC Address:**
+
+For the most part, you can pick out any field from the JSON in a fairly
+straightforward manner.
+
+    $ sudo docker inspect --format='{{.NetworkSettings.MacAddress}}' $INSTANCE_ID
+
 **List All Port Bindings:**
 
 One can loop over arrays and maps in the results to produce simple text

+ 12 - 7
docs/sources/reference/run.md

@@ -133,13 +133,14 @@ example, `docker run ubuntu:14.04`.
 
 ## Network settings
 
-    --dns=[]        : Set custom dns servers for the container
-    --net="bridge"  : Set the Network mode for the container
-                                 'bridge': creates a new network stack for the container on the docker bridge
-                                 'none': no networking for this container
-                                 'container:<name|id>': reuses another container network stack
-                                 'host': use the host network stack inside the container
-    --add-host=""   : Add a line to /etc/hosts (host:IP)
+    --dns=[]         : Set custom dns servers for the container
+    --net="bridge"   : Set the Network mode for the container
+                                  'bridge': creates a new network stack for the container on the docker bridge
+                                  'none': no networking for this container
+                                  'container:<name|id>': reuses another container network stack
+                                  'host': use the host network stack inside the container
+    --add-host=""    : Add a line to /etc/hosts (host:IP)
+    --mac-address="" : Sets the container's Ethernet device's MAC address
 
 By default, all containers have networking enabled and they can make any
 outgoing connections. The operator can completely disable networking
@@ -150,6 +151,10 @@ networking. In cases like this, you would perform I/O through files or
 Your container will use the same DNS servers as the host by default, but
 you can override this with `--dns`.
 
+By default a random MAC is generated. You can set the container's MAC address
+explicitly by providing a MAC via the `--mac-address` parameter (format:
+`12:34:56:78:9a:bc`).
+
 Supported networking modes are:
 
 * none - no networking in the container

+ 35 - 0
integration-cli/docker_cli_run_test.go

@@ -2018,6 +2018,41 @@ func TestRunNetworkNotInitializedNoneMode(t *testing.T) {
 	logDone("run - network must not be initialized in 'none' mode")
 }
 
+func TestRunSetMacAddress(t *testing.T) {
+	mac := "12:34:56:78:9a:bc"
+	cmd := exec.Command("/bin/bash", "-c", dockerBinary+` run -i --rm --mac-address=`+mac+` busybox /bin/sh -c "ip link show eth0 | tail -1 | awk '{ print \$2 }'"`)
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err)
+	}
+	actualMac := strings.TrimSpace(out)
+	if actualMac != mac {
+		t.Fatalf("Set MAC address with --mac-address failed. The container has an incorrect MAC address: %q, expected: %q", actualMac, mac)
+	}
+
+	deleteAllContainers()
+	logDone("run - setting MAC address with --mac-address")
+}
+
+func TestRunInspectMacAddress(t *testing.T) {
+	mac := "12:34:56:78:9a:bc"
+	cmd := exec.Command(dockerBinary, "run", "-d", "--mac-address="+mac, "busybox", "top")
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err)
+	}
+	id := strings.TrimSpace(out)
+	inspectedMac, err := inspectField(id, "NetworkSettings.MacAddress")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if inspectedMac != mac {
+		t.Fatalf("docker inspect outputs wrong MAC address: %q, should be: %q", inspectedMac, mac)
+	}
+	deleteAllContainers()
+	logDone("run - inspecting MAC address")
+}
+
 func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
 	cmd := exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top")
 	out, _, err := runCommandWithOutput(cmd)

+ 2 - 0
runconfig/config.go

@@ -31,6 +31,7 @@ type Config struct {
 	WorkingDir      string
 	Entrypoint      []string
 	NetworkDisabled bool
+	MacAddress      string
 	OnBuild         []string
 	SecurityOpt     []string
 }
@@ -53,6 +54,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
 		Image:           job.Getenv("Image"),
 		WorkingDir:      job.Getenv("WorkingDir"),
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
+		MacAddress:      job.Getenv("MacAddress"),
 	}
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("Volumes", &config.Volumes)

+ 2 - 0
runconfig/parse.go

@@ -59,6 +59,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
 		flCpuset          = cmd.String([]string{"-cpuset"}, "", "CPUs in which to allow execution (0-3, 0,1)")
 		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container\n'bridge': creates a new network stack for the container on the docker bridge\n'none': no networking for this container\n'container:<name|id>': reuses another container network stack\n'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.")
+		flMacAddress      = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
 		flRestartPolicy   = cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits (no, on-failure[:max-retry], always)")
 	)
 
@@ -269,6 +270,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
 		Cmd:             runCmd,
 		Image:           image,
 		Volumes:         flVolumes.GetMap(),
+		MacAddress:      *flMacAddress,
 		Entrypoint:      entrypoint,
 		WorkingDir:      *flWorkingDir,
 		SecurityOpt:     flSecurityOpt.GetAll(),