Merge pull request #19229 from mavenugo/udlinks

Local alias support
This commit is contained in:
Sebastiaan van Stijn 2016-01-12 16:47:32 -08:00
Родитель 1e1a3c9508 2aa5f33b20
Коммит 47d87d3b92
12 изменённых файлов: 364 добавлений и 25 удалений

Просмотреть файл

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringid"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/network"
@ -112,6 +113,8 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
flLinks := opts.NewListOpts(runconfigopts.ValidateLink)
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
cmd.Require(flag.Min, 2)
if err := cmd.ParseFlags(args, true); err != nil {
return err
@ -121,6 +124,7 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
IPv4Address: *flIPAddress,
IPv6Address: *flIPv6Address,
},
Links: flLinks.GetAll(),
}
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig)
}

Просмотреть файл

@ -18,6 +18,7 @@ import (
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/docker/utils"
"github.com/docker/docker/volume"
"github.com/docker/engine-api/types/container"
@ -247,6 +248,21 @@ func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox)
return nil
}
// BuildJoinOptions builds endpoint Join options from a given network.
func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
var joinOptions []libnetwork.EndpointOption
if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
for _, str := range epConfig.Links {
name, alias, err := runconfigopts.ParseLink(str)
if err != nil {
return nil, err
}
joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias))
}
}
return joinOptions, nil
}
// BuildCreateEndpointOptions builds endpoint options from a given network.
func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
var (

Просмотреть файл

@ -586,7 +586,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
if container.NetworkSettings == nil {
container.NetworkSettings = &network.Settings{}
}
if endpointsConfig != nil {
if len(endpointsConfig) > 0 {
container.NetworkSettings.Networks = endpointsConfig
}
if container.NetworkSettings.Networks == nil {
@ -816,7 +816,12 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
container.UpdateSandboxNetworkSettings(sb)
}
if err := ep.Join(sb); err != nil {
joinOptions, err := container.BuildJoinOptions(n)
if err != nil {
return err
}
if err := ep.Join(sb, joinOptions...); err != nil {
return err
}

Просмотреть файл

@ -868,7 +868,7 @@ func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error
// registerLinks writes the links to a file.
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
if hostConfig == nil {
if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() {
return nil
}

Просмотреть файл

@ -15,6 +15,9 @@ parent = "smn_cli"
Connects a container to a network
--help Print usage
--ip IP Address
--ip6 IPv6 Address
--link=[] Add a link to another container
Connects a container to a network. You can connect a container by name
or by ID. Once connected, the container can communicate with other containers in
@ -33,7 +36,13 @@ $ docker run -itd --net=multi-host-network busybox
You can specify the IP address you want to be assigned to the container's interface.
```bash
$ docker network connect multi-host-network --ip 10.10.36.122 container2
$ docker network connect --ip 10.10.36.122 multi-host-network container2
```
You can use `--link` option to link another container with a prefered alias
```bash
$ docker network connect --link container1:c1 multi-host-network container2
```
You can pause, restart, and stop containers that are connected to a network.
@ -60,3 +69,4 @@ You can connect a container to one or more networks. The networks need not be th
* [network ls](network_ls.md)
* [network rm](network_rm.md)
* [Understand Docker container networks](../../userguide/networking/dockernetworks.md)
* [Work with networks](../../userguide/networking/work-with-networks.md)

Просмотреть файл

@ -1301,12 +1301,12 @@ specifies `EXPOSE 80` in the Dockerfile). At runtime, the port might be
bound to 42800 on the host. To find the mapping between the host ports
and the exposed ports, use `docker port`.
If the operator uses `--link` when starting a new client container, then the
client container can access the exposed port via a private networking interface.
Linking is a legacy feature that is only supported on the default bridge
network. You should prefer the Docker networks feature instead. For more
information on this feature, see the [*Docker network
overview*""](../userguide/networking/index.md)).
If the operator uses `--link` when starting a new client container in the
default bridge network, then the client container can access the exposed
port via a private networking interface.
If `--link` is used when starting a container in a user-defined network as
described in [*Docker network overview*""](../userguide/networking/index.md)),
it will provide a named alias for the container being linked to.
### ENV (environment variables)

Просмотреть файл

@ -17,14 +17,11 @@ Before the [Docker networks feature](../dockernetworks.md), you could use the
Docker link feature to allow containers to discover each other and securely
transfer information about one container to another container. With the
introduction of the Docker networks feature, you can still create links but they
are only supported on the default `bridge` network named `bridge` and appearing
in your network stack as `docker0`.
behave differently between default `bridge` network and
[user defined networks](../work-with-networks.md#linking-containers-in-user-defined-networks)
This section briefly discusses connecting via a network port and then goes into
detail on container linking. While links are still supported on Docker's default
network (`bridge`), you should avoid them in preference of the Docker
networks feature. Linking is expected to be deprecated and removed in a future
release.
detail on container linking in default `bridge` network.
## Connect using network port mapping

Просмотреть файл

@ -268,7 +268,7 @@ PING container3 (172.25.3.3): 56 data bytes
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
This isn't the case for the default bridge network. Both `container2` and `container1` are connected to the default bridge network. Docker does not support automatic service discovery on this network. For this reason, pinging `container1` by name fails as you would expect based on the `/etc/hosts` file:
This isn't the case for the default `bridge` network. Both `container2` and `container1` are connected to the default bridge network. Docker does not support automatic service discovery on this network. For this reason, pinging `container1` by name fails as you would expect based on the `/etc/hosts` file:
```bash
/ # ping -w 4 container1
@ -314,6 +314,192 @@ PING 172.17.0.2 (172.17.0.2): 56 data bytes
You can connect both running and non-running containers to a network. However,
`docker network inspect` only displays information on running containers.
### Linking containers in user-defined networks
In the above example, container_2 was able to resolve container_3's name automatically
in the user defined network `isolated_nw`, but the name resolution did not succeed
automatically in the default `bridge` network. This is expected in order to maintain
backward compatibility with [legacy link](default_network/dockerlinks.md).
The `legacy link` provided 4 major functionalities to the default `bridge` network.
* name resolution
* name alias for the linked container using `--link=CONTAINER-NAME:ALIAS`
* secured container connectivity (in isolation via `--icc=false`)
* environment variable injection
Comparing the above 4 functionalities with the non-default user-defined networks such as
`isolated_nw` in this example, without any additional config, `docker network` provides
* automatic name resolution using DNS
* automatic secured isolated environment for the containers in a network
* ability to dynamically attach and detach to multiple networks
* supports the `--link` option to provide name alias for the linked container
Continuing with the above example, create another container `container_4` in `isolated_nw`
with `--link` to provide additional name resolution using alias for other containers in
the same network.
```bash
$ docker run --net=isolated_nw -itd --name=container4 --link container5:c5 busybox
01b5df970834b77a9eadbaff39051f237957bd35c4c56f11193e0594cfd5117c
```
With the help of `--link` container4 will be able to reach container5 using the
aliased name `c5` as well.
Please note that while creating container4, we linked to a container named `container5`
which is not created yet. That is one of the differences in behavior between the
`legacy link` in default `bridge` network and the new `link` functionality in user defined
networks. The `legacy link` is static in nature and it hard-binds the container with the
alias and it doesnt tolerate linked container restarts. While the new `link` functionality
in user defined networks are dynamic in nature and supports linked container restarts
including tolerating ip-address changes on the linked container.
Now let us launch another container named `container5` linking container4 to c4.
```bash
$ docker run --net=isolated_nw -itd --name=container5 --link container4:c4 busybox
72eccf2208336f31e9e33ba327734125af00d1e1d2657878e2ee8154fbb23c7a
```
As expected, container4 will be able to reach container5 by both its container name and
its alias c5 and container5 will be able to reach container4 by its container name and
its alias c4.
```bash
$ docker attach container4
/ # ping -w 4 c5
PING c5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- c5 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 container5
PING container5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- container5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
```bash
$ docker attach container5
/ # ping -w 4 c4
PING c4 (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
--- c4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.065/0.070/0.082 ms
/ # ping -w 4 container4
PING container4 (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
--- container4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.065/0.070/0.082 ms
```
Similar to the legacy link functionality the new link alias is localized to a container
and the aliased name has no meaning outside of the container using the `--link`.
Also, it is important to note that if a container belongs to multiple networks, the
linked alias is scoped within a given network. Hence the containers can be linked to
different aliases in different networks.
Extending the example, let us create another network named `local_alias`
```bash
$ docker network create -d bridge --subnet 172.26.0.0/24 local_alias
76b7dc932e037589e6553f59f76008e5b76fa069638cd39776b890607f567aaa
```
let us connect container4 and container5 to the new network `local_alias`
```
$ docker network connect --link container5:foo local_alias container4
$ docker network connect --link container4:bar local_alias container5
```
```bash
$ docker attach container4
/ # ping -w 4 foo
PING foo (172.26.0.3): 56 data bytes
64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.26.0.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: 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
/ # ping -w 4 c5
PING c5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- c5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
Note that the ping succeeds for both the aliases but on different networks.
Let us conclude this section by disconnecting container5 from the `isolated_nw`
and observe the results
```
$ docker network disconnect isolated_nw container5
$ docker attach container4
/ # ping -w 4 c5
ping: bad address 'c5'
/ # ping -w 4 foo
PING foo (172.26.0.3): 56 data bytes
64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.26.0.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: 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
```
In conclusion, the new link functionality in user defined networks provides all the
benefits of legacy links while avoiding most of the well-known issues with `legacy links`.
One notable missing functionality compared to `legacy links` is the injection of
environment variables. Though very useful, environment variable injection is static
in nature and must be injected when the container is started. One cannot inject
environment variables into a running container without significant effort and hence
it is not compatible with `docker network` which provides a dynamic way to connect/
disconnect containers to/from a network.
## Disconnecting containers
You can disconnect a container from a network using the `docker network

Просмотреть файл

@ -1040,3 +1040,44 @@ func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
out, _ = dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.GlobalIPv6Address }}'", nwname), cName)
c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
}
func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "foo1")
dockerCmd(c, "network", "create", "-d", "bridge", "foo2")
dockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
// run a container in user-defined network udlinkNet with a link for an existing container
// and a link for a container that doesnt exist
dockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1",
"--link=third:bar", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias FirstInFoo1 must succeed
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
c.Assert(err, check.IsNil)
// connect first container to foo2 network
dockerCmd(c, "network", "connect", "foo2", "first")
// connect second container to foo2 network with a different alias for first container
dockerCmd(c, "network", "connect", "--link=first:FirstInFoo2", "foo2", "second")
// ping the new alias in network foo2
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
c.Assert(err, check.IsNil)
// disconnect first container from foo1 network
dockerCmd(c, "network", "disconnect", "foo1", "first")
// link in foo1 network must fail
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
c.Assert(err, check.NotNil)
// link in foo2 network must succeed
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
c.Assert(err, check.IsNil)
}

Просмотреть файл

@ -199,6 +199,80 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) {
}
}
func (s *DockerSuite) TestUserDefinedNetworkLinks(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet")
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
// run a container in user-defined network udlinkNet with a link for an existing container
// and a link for a container that doesnt exist
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo",
"--link=third:bar", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias foo must succeed
_, _, 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)
// ping to third and its alias must fail
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
c.Assert(err, check.NotNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
c.Assert(err, check.NotNil)
// start third container now
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=third", "busybox", "top")
c.Assert(waitRun("third"), check.IsNil)
// ping to third and its alias must succeed now
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
c.Assert(err, check.IsNil)
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
c.Assert(err, check.IsNil)
}
func (s *DockerSuite) TestUserDefinedNetworkLinksWithRestart(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet")
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo",
"busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias foo must succeed
_, _, 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)
// Restart first container
dockerCmd(c, "restart", "first")
c.Assert(waitRun("first"), check.IsNil)
// ping to first and its alias foo must still succeed
_, _, 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)
// Restart second container
dockerCmd(c, "restart", "second")
c.Assert(waitRun("second"), check.IsNil)
// ping to first and its alias foo must still succeed
_, _, 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)
}
// Issue 9677.
func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true")

Просмотреть файл

@ -48,10 +48,6 @@ func ValidateNetMode(c *container.Config, hc *container.HostConfig) error {
return ErrConflictContainerNetworkAndLinks
}
if hc.NetworkMode.IsUserDefined() && len(hc.Links) > 0 {
return ErrConflictUserDefinedNetworkAndLinks
}
if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 {
return ErrConflictNetworkAndDNS
}

Просмотреть файл

@ -417,11 +417,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
config.StdinOnce = true
}
var networkingConfig *networktypes.NetworkingConfig
networkingConfig := &networktypes.NetworkingConfig{
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
}
if *flIPv4Address != "" || *flIPv6Address != "" {
networkingConfig = &networktypes.NetworkingConfig{
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
}
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: *flIPv4Address,
@ -430,6 +430,16 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
}
}
if hostConfig.NetworkMode.IsUserDefined() && len(hostConfig.Links) > 0 {
epConfig := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
if epConfig == nil {
epConfig = &networktypes.EndpointSettings{}
}
epConfig.Links = make([]string, len(hostConfig.Links))
copy(epConfig.Links, hostConfig.Links)
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
}
return config, hostConfig, networkingConfig, cmd, nil
}