diff --git a/api/client/network.go b/api/client/network.go index aaa4b5c25a..d0422e4192 100644 --- a/api/client/network.go +++ b/api/client/network.go @@ -118,6 +118,8 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error { flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address") flLinks := opts.NewListOpts(runconfigopts.ValidateLink) cmd.Var(&flLinks, []string{"-link"}, "Add link to another container") + flAliases := opts.NewListOpts(nil) + cmd.Var(&flAliases, []string{"-alias"}, "Add network-scoped alias for the container") cmd.Require(flag.Min, 2) if err := cmd.ParseFlags(args, true); err != nil { return err @@ -127,7 +129,8 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error { IPv4Address: *flIPAddress, IPv6Address: *flIPv6Address, }, - Links: flLinks.GetAll(), + Links: flLinks.GetAll(), + Aliases: flAliases.GetAll(), } return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig) } diff --git a/container/container_unix.go b/container/container_unix.go index 1d755b2e5b..282d889304 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -284,6 +284,10 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([] createOptions = append(createOptions, libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil)) } + + for _, alias := range epConfig.Aliases { + createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias)) + } } if !container.HostConfig.NetworkMode.IsUserDefined() { diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 4ea3222fff..0a361a6924 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -761,6 +761,10 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName return runconfig.ErrUnsupportedNetworkAndIP } + if !containertypes.NetworkMode(idOrName).IsUserDefined() && len(endpointConfig.Aliases) > 0 { + return runconfig.ErrUnsupportedNetworkAndAlias + } + controller := daemon.netController if err := validateNetworkingConfig(n, endpointConfig); err != nil { diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index f4dd125acc..ad23995ac1 100644 --- a/docs/reference/commandline/create.md +++ b/docs/reference/commandline/create.md @@ -68,6 +68,7 @@ Creates a new container. 'container:': reuse another container's network stack 'host': use the Docker host network stack '|': connect to a user-defined network + --net-alias=[] Add network-scoped alias for the container --oom-kill-disable Whether to disable OOM Killer for the container or not --oom-score-adj=0 Tune the host's OOM preferences for containers (accepts -1000 to 1000) -P, --publish-all Publish all exposed ports to random ports diff --git a/docs/reference/commandline/network_connect.md b/docs/reference/commandline/network_connect.md index eba6a6830c..b08dec3225 100644 --- a/docs/reference/commandline/network_connect.md +++ b/docs/reference/commandline/network_connect.md @@ -14,6 +14,7 @@ parent = "smn_cli" Connects a container to a network + --alias=[] Add network-scoped alias for the container --help Print usage --ip IPv4 Address --ip6 IPv6 Address @@ -45,6 +46,13 @@ You can use `--link` option to link another container with a prefered alias $ docker network connect --link container1:c1 multi-host-network container2 ``` +`--alias` option can be used to resolve the container by another name in the network +being connected to. + +```bash +$ docker network connect --alias db --alias mysql multi-host-network container2 +``` + You can pause, restart, and stop containers that are connected to a network. Paused containers remain connected and can be revealed by a `network inspect`. When the container is stopped, it does not appear on the network until you restart diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index f6cff75ee2..a36bbfb96a 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -68,6 +68,7 @@ parent = "smn_cli" 'container:': reuse another container's network stack 'host': use the Docker host network stack '|': connect to a user-defined network + --net-alias=[] Add network-scoped alias for the container --oom-kill-disable Whether to disable OOM Killer for the container or not --oom-score-adj=0 Tune the host's OOM preferences for containers (accepts -1000 to 1000) -P, --publish-all Publish all exposed ports to random ports diff --git a/docs/reference/run.md b/docs/reference/run.md index 63d4efc9df..95e0e0a605 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -273,6 +273,7 @@ of the containers. 'container:': reuse another container's network stack 'host': use the Docker host network stack '|': connect to a user-defined network + --net-alias=[] : Add network-scoped alias for the container --add-host="" : Add a line to /etc/hosts (host:IP) --mac-address="" : Sets the container's Ethernet device's MAC address --ip="" : Sets the container's Ethernet device's IPv4 address diff --git a/docs/userguide/networking/work-with-networks.md b/docs/userguide/networking/work-with-networks.md index 87452c876a..b4dea7d768 100644 --- a/docs/userguide/networking/work-with-networks.md +++ b/docs/userguide/networking/work-with-networks.md @@ -502,6 +502,130 @@ environment variables into a running container without significant effort and he it is not compatible with `docker network` which provides a dynamic way to connect/ disconnect containers to/from a network. +### Network-scoped alias + +While `links` provide private name resolution that is localized within a container, +the network-scoped alias provides a way for a container to be discovered by an +alternate name by any other container within the scope of a particular network. +Unlike the `link` alias, which is defined by the consumer of a service, the +network-scoped alias is defined by the container that is offering the service +to the network. + +Continuing with the above example, create another container in `isolated_nw` with a +network alias. + +```bash +$ docker run --net=isolated_nw -itd --name=container6 --net-alias app busybox +8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17 +``` + +```bash +$ docker attach container4 +/ # ping -w 4 app +PING app (172.25.0.6): 56 data bytes +64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms +64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms +64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms +64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms + +--- app ping statistics --- +4 packets transmitted, 4 packets received, 0% packet loss +round-trip min/avg/max = 0.070/0.081/0.097 ms + +/ # ping -w 4 container6 +PING container5 (172.25.0.6): 56 data bytes +64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms +64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms +64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms +64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms + +--- container6 ping statistics --- +4 packets transmitted, 4 packets received, 0% packet loss +round-trip min/avg/max = 0.070/0.081/0.097 ms +``` + +Now let us connect `container6` to the `local_alias` network with a different network-scoped +alias. + +``` +$ docker network connect --alias scoped-app local_alias container6 +``` + +`container6` in this example now is aliased as `app` in network `isolated_nw` and +as `scoped-app` in network `local_alias`. + +Let's try to reach these aliases from `container4` (which is connected to both these networks) +and `container5` (which is connected only to `isolated_nw`). + +```bash +$ docker attach container4 + +/ # ping -w 4 scoped-app +PING foo (172.26.0.5): 56 data bytes +64 bytes from 172.26.0.5: seq=0 ttl=64 time=0.070 ms +64 bytes from 172.26.0.5: seq=1 ttl=64 time=0.080 ms +64 bytes from 172.26.0.5: seq=2 ttl=64 time=0.080 ms +64 bytes from 172.26.0.5: seq=3 ttl=64 time=0.097 ms + +--- foo ping statistics --- +4 packets transmitted, 4 packets received, 0% packet loss +round-trip min/avg/max = 0.070/0.081/0.097 ms + +$ docker attach container5 + +/ # ping -w 4 scoped-app +ping: bad address 'scoped-app' + +``` + +As you can see, the alias is scoped to the network it is defined on and hence only +those containers that are connected to that network can access the alias. + +In addition to the above features, multiple containers can share the same network-scoped +alias within the same network. For example, let's launch `container7` in `isolated_nw` with +the same alias as `container6` + +```bash +$ docker run --net=isolated_nw -itd --name=container7 --net-alias app busybox +3138c678c123b8799f4c7cc6a0cecc595acbdfa8bf81f621834103cd4f504554 +``` + +When multiple containers share the same alias, name resolution to that alias will happen +to one of the containers (typically the first container that is aliased). When the container +that backs the alias goes down or disconnected from the network, the next container that +backs the alias will be resolved. + +Let us ping the alias `app` from `container4` and bring down `container6` to verify that +`container7` is resolving the `app` alias. + +```bash +$ docker attach container4 +/ # ping -w 4 app +PING app (172.25.0.6): 56 data bytes +64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms +64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms +64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms +64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms + +--- app ping statistics --- +4 packets transmitted, 4 packets received, 0% packet loss +round-trip min/avg/max = 0.070/0.081/0.097 ms + +$ docker stop container6 + +$ docker attach container4 +/ # ping -w 4 app +PING app (172.25.0.7): 56 data bytes +64 bytes from 172.25.0.7: seq=0 ttl=64 time=0.095 ms +64 bytes from 172.25.0.7: seq=1 ttl=64 time=0.075 ms +64 bytes from 172.25.0.7: seq=2 ttl=64 time=0.072 ms +64 bytes from 172.25.0.7: seq=3 ttl=64 time=0.101 ms + +--- app ping statistics --- +4 packets transmitted, 4 packets received, 0% packet loss +round-trip min/avg/max = 0.072/0.085/0.101 ms + +``` ## Disconnecting containers diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 4c27aa593c..b6b41be389 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -1138,5 +1138,42 @@ func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *check.C) { c.Assert(networks, checker.Contains, netWorkName1, check.Commentf(fmt.Sprintf("Should contain '%s' network", netWorkName1))) c.Assert(networks, checker.Contains, netWorkName2, check.Commentf(fmt.Sprintf("Should contain '%s' network", netWorkName2))) c.Assert(networks, checker.Not(checker.Contains), "bridge", check.Commentf("Should not contain 'bridge' network")) - +} + +func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *check.C) { + testRequires(c, DaemonIsLinux, NotUserNamespace) + dockerCmd(c, "network", "create", "-d", "bridge", "net1") + dockerCmd(c, "network", "create", "-d", "bridge", "net2") + + dockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo", "busybox", "top") + c.Assert(waitRun("first"), check.IsNil) + + dockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox", "top") + c.Assert(waitRun("second"), check.IsNil) + + // ping first container and its alias + _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") + c.Assert(err, check.IsNil) + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") + c.Assert(err, check.IsNil) + + // connect first container to net2 network + dockerCmd(c, "network", "connect", "--alias=bar", "net2", "first") + // connect second container to foo2 network with a different alias for first container + dockerCmd(c, "network", "connect", "net2", "second") + + // ping the new alias in network foo2 + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") + c.Assert(err, check.IsNil) + + // disconnect first container from net1 network + dockerCmd(c, "network", "disconnect", "net1", "first") + + // ping to net1 scoped alias "foo" must fail + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") + c.Assert(err, check.NotNil) + + // ping to net2 scoped alias "bar" must still succeed + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") + c.Assert(err, check.IsNil) } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index aaef5315a9..0cf8a1c8a6 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -273,6 +273,37 @@ func (s *DockerSuite) TestUserDefinedNetworkLinksWithRestart(c *check.C) { c.Assert(err, check.IsNil) } +func (s *DockerSuite) TestUserDefinedNetworkAlias(c *check.C) { + testRequires(c, DaemonIsLinux, NotUserNamespace) + dockerCmd(c, "network", "create", "-d", "bridge", "net1") + + dockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo1", "--net-alias=foo2", "busybox", "top") + c.Assert(waitRun("first"), check.IsNil) + + dockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox", "top") + c.Assert(waitRun("second"), check.IsNil) + + // ping to first and its network-scoped aliases + _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") + c.Assert(err, check.IsNil) + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo1") + c.Assert(err, check.IsNil) + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo2") + c.Assert(err, check.IsNil) + + // Restart first container + dockerCmd(c, "restart", "first") + c.Assert(waitRun("first"), check.IsNil) + + // ping to first and its network-scoped aliases must succeed + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") + c.Assert(err, check.IsNil) + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo1") + c.Assert(err, check.IsNil) + _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo2") + c.Assert(err, check.IsNil) +} + // Issue 9677. func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) { out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true") diff --git a/man/docker-create.1.md b/man/docker-create.1.md index ca859bd66d..08074ac431 100644 --- a/man/docker-create.1.md +++ b/man/docker-create.1.md @@ -52,6 +52,7 @@ docker-create - Create a new container [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]] [**--name**[=*NAME*]] [**--net**[=*"bridge"*]] +[**--net-alias**[=*[]*]] [**--oom-kill-disable**] [**--oom-score-adj**[=*0*]] [**-P**|**--publish-all**] @@ -265,6 +266,9 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap. 'host': use the Docker host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. '|': connect to a user-defined network +**--net-alias**=[] + Add network-scoped alias for the container + **--oom-kill-disable**=*true*|*false* Whether to disable OOM Killer for the container or not. diff --git a/man/docker-run.1.md b/man/docker-run.1.md index c639b14380..ea9b0b8168 100644 --- a/man/docker-run.1.md +++ b/man/docker-run.1.md @@ -54,6 +54,7 @@ docker-run - Run a command in a new container [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]] [**--name**[=*NAME*]] [**--net**[=*"bridge"*]] +[**--net-alias**[=*[]*]] [**--oom-kill-disable**] [**--oom-score-adj**[=*0*]] [**-P**|**--publish-all**] @@ -383,6 +384,9 @@ and foreground Docker containers. 'host': use the Docker host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. '|': connect to a user-defined network +**--net-alias**=[] + Add network-scoped alias for the container + **--oom-kill-disable**=*true*|*false* Whether to disable OOM Killer for the container or not. diff --git a/runconfig/errors.go b/runconfig/errors.go index 34b8aecea5..9f0c6b84b5 100644 --- a/runconfig/errors.go +++ b/runconfig/errors.go @@ -33,4 +33,6 @@ var ( ErrUnsupportedNetworkAndIP = fmt.Errorf("User specified IP address is supported on user defined networks only") // ErrUnsupportedNetworkNoSubnetAndIP conflict between network with no configured subnet and preferred ip address ErrUnsupportedNetworkNoSubnetAndIP = fmt.Errorf("User specified IP address is supported only when connecting to networks with user configured subnets") + // ErrUnsupportedNetworkAndAlias conflict between network mode and alias + ErrUnsupportedNetworkAndAlias = fmt.Errorf("Network-scoped alias is supported only for containers in user defined networks") ) diff --git a/runconfig/opts/parse.go b/runconfig/opts/parse.go index 53b376934f..f716022620 100644 --- a/runconfig/opts/parse.go +++ b/runconfig/opts/parse.go @@ -33,6 +33,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host flDeviceReadBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice) flDeviceWriteBps = NewThrottledeviceOpt(ValidateThrottleBpsDevice) flLinks = opts.NewListOpts(ValidateLink) + flAliases = opts.NewListOpts(nil) flDeviceReadIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice) flDeviceWriteIOps = NewThrottledeviceOpt(ValidateThrottleIOpsDevice) flEnv = opts.NewListOpts(ValidateEnv) @@ -103,6 +104,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume") cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory") cmd.Var(&flLinks, []string{"-link"}, "Add link to another container") + cmd.Var(&flAliases, []string{"-net-alias"}, "Add network-scoped alias for the container") cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container") cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container") cmd.Var(&flLabelsFile, []string{"-label-file"}, "Read in a line delimited file of labels") @@ -440,6 +442,16 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig } + if hostConfig.NetworkMode.IsUserDefined() && flAliases.Len() > 0 { + epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] + if epConfig == nil { + epConfig = &networktypes.EndpointSettings{} + } + epConfig.Aliases = make([]string, flAliases.Len()) + copy(epConfig.Aliases, flAliases.GetAll()) + networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig + } + return config, hostConfig, networkingConfig, cmd, nil }