Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding custom bridge support to docker run #6704

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions daemon/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,10 @@ func (container *Container) allocateNetwork() error {
)

job := eng.Job("allocate_interface", container.ID)
if bridgeToUse := mode.GetNonDefaultBridge(); bridgeToUse != "" {
job.Setenv("RequestedBridge", bridgeToUse)
}

if env, err = job.Stdout.AddEnv(); err != nil {
return err
}
Expand Down Expand Up @@ -995,9 +999,13 @@ func (container *Container) setupLinkedContainers() ([]string, error) {
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
}

if container.NetworkSettings.Bridge != child.NetworkSettings.Bridge {
return nil, fmt.Errorf("Cannot link to a container (%s) running in a different bridge (%s)", child.Name, child.NetworkSettings.Bridge)
}
link, err := links.NewLink(
container.NetworkSettings.IPAddress,
child.NetworkSettings.IPAddress,
container.NetworkSettings.Bridge,
linkAlias,
child.Config.Env,
child.Config.ExposedPorts,
Expand Down
44 changes: 29 additions & 15 deletions daemon/networkdriver/bridge/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
// Network interface represents the networking stack of a container
type networkInterface struct {
IP net.IP
Bridge net.IPNet
PortMappings []net.Addr // there are mappings to the host interfaces
}

Expand Down Expand Up @@ -317,32 +318,44 @@ func createBridgeIface(name string) error {
// Allocate a network interface
func Allocate(job *engine.Job) engine.Status {
var (
ip *net.IP
err error
id = job.Args[0]
requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
ip *net.IP
err error
id = job.Args[0]
requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
requestedBridge = job.Getenv("RequestedBridge")
bridgeNetworkInUse = bridgeNetwork
bridgeIfaceInUse = bridgeIface
)

if requestedBridge != "" && requestedBridge != bridgeIfaceInUse {
addr, err := networkdriver.GetIfaceAddr(requestedBridge)
if err != nil {
return job.Error(fmt.Errorf("Could not obtain details about bridge %s. Error: %s",
requestedBridge, err))
}
bridgeNetworkInUse = addr.(*net.IPNet)
bridgeIfaceInUse = requestedBridge
}
if requestedIP != nil {
ip, err = ipallocator.RequestIP(bridgeNetwork, &requestedIP)
ip, err = ipallocator.RequestIP(bridgeNetworkInUse, &requestedIP)
} else {
ip, err = ipallocator.RequestIP(bridgeNetwork, nil)
ip, err = ipallocator.RequestIP(bridgeNetworkInUse, nil)
}
if err != nil {
return job.Error(err)
}

out := engine.Env{}
out.Set("IP", ip.String())
out.Set("Mask", bridgeNetwork.Mask.String())
out.Set("Gateway", bridgeNetwork.IP.String())
out.Set("Bridge", bridgeIface)
out.Set("Mask", bridgeNetworkInUse.Mask.String())
out.Set("Gateway", bridgeNetworkInUse.IP.String())
out.Set("Bridge", bridgeIfaceInUse)

size, _ := bridgeNetwork.Mask.Size()
size, _ := bridgeNetworkInUse.Mask.Size()
out.SetInt("IPPrefixLen", size)

currentInterfaces.Set(id, &networkInterface{
IP: *ip,
IP: *ip,
Bridge: *bridgeNetworkInUse,
})

out.WriteTo(job.Stdout)
Expand All @@ -367,7 +380,7 @@ func Release(job *engine.Job) engine.Status {
}
}

if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil {
if err := ipallocator.ReleaseIP(&containerInterface.Bridge, &containerInterface.IP); err != nil {
log.Printf("Unable to release ip %s\n", err)
}
return engine.StatusOK
Expand Down Expand Up @@ -462,6 +475,7 @@ func LinkContainers(job *engine.Job) engine.Status {
parentIP = job.Getenv("ParentIP")
ignoreErrors = job.GetenvBool("IgnoreErrors")
ports = job.GetenvList("Ports")
bridge = job.Getenv("Bridge")
)
split := func(p string) (string, string) {
parts := strings.Split(p, "/")
Expand All @@ -471,7 +485,7 @@ func LinkContainers(job *engine.Job) engine.Status {
for _, p := range ports {
port, proto := split(p)
if output, err := iptables.Raw(action, "FORWARD",
"-i", bridgeIface, "-o", bridgeIface,
"-i", bridge, "-o", bridge,
"-p", proto,
"-s", parentIP,
"--dport", port,
Expand All @@ -483,7 +497,7 @@ func LinkContainers(job *engine.Job) engine.Status {
}

if output, err := iptables.Raw(action, "FORWARD",
"-i", bridgeIface, "-o", bridgeIface,
"-i", bridge, "-o", bridge,
"-p", proto,
"-s", childIP,
"--sport", port,
Expand Down
1 change: 1 addition & 0 deletions docs/man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ and foreground Docker containers.
**--net**="bridge"
Set the Network mode for the container
'bridge': creates a new network stack for the container on the docker bridge
'bridge:<name>: creates a new network stack for the container on the specified pre-existing bridge'
'none': no networking for this container
'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.
Expand Down
3 changes: 2 additions & 1 deletion docs/sources/reference/commandline/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,8 @@ removed before the image is removed.
-m, --memory="" Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
--name="" Assign a name to the container
--net="bridge" Set the Network mode for the container
'bridge': creates a new network stack for the container on the docker bridge
'bridge': creates a new network stack for the container on the default docker bridge
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know in the generated docs, but the output looks weird (tabs/spaces)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

'bridge:<name>': creates a new network stack for the container on the specified pre-existing bridge
'none': no networking for this container
'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.
Expand Down
19 changes: 15 additions & 4 deletions docs/sources/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ PID files):

--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
'bridge': creates a new network stack for the container on the default docker bridge
'bridge:<name>': creates a new network stack for the container on the specified pre-existing bridge
'none': no networking for this container
'container:<name|id>': reuses another container network stack
'host': use the host network stack inside the container

By default, all containers have networking enabled and they can make any
outgoing connections. The operator can completely disable networking
Expand Down Expand Up @@ -170,6 +171,16 @@ container's namespaces in addition to the `loopback` interface. An IP
address will be allocated for containers on the bridge's network and
traffic will be routed though this bridge to the container.

#### Mode: bridge:<name>
With the networking mode set to `bridge:<name>` a container will use a
pre-existing bridge referred to by `name` for its networking setup.
A pair of veth interfaces will be created in bridge `name` for the container.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be vEth instead of veth, or even "virtual ethernet (vEth)" for example?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I think "virtual ethernet (vEth)" is clearest.

One side of the veth pair will remain on the host attached to the bridge while
the other side of the pair will be placed inside the container's namespaces in
addition to the `loopback` interface. An IP address will be allocated for
containers on the bridge's network and trafic will be routed though this bridge
to the container.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an fully worked example for someone to follow in the networking.md article?

mind you Docs LGTM @jamtur01 @fredlf @ostezer


#### Mode: host

With the networking mode set to `host` a container will share the host's
Expand Down
5 changes: 4 additions & 1 deletion links/links.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (
type Link struct {
ParentIP string
ChildIP string
Bridge string
Name string
ChildEnvironment []string
Ports []nat.Port
IsEnabled bool
eng *engine.Engine
}

func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}, eng *engine.Engine) (*Link, error) {
func NewLink(parentIP, childIP, bridge, name string, env []string, exposedPorts map[nat.Port]struct{}, eng *engine.Engine) (*Link, error) {

var (
i int
Expand All @@ -34,6 +35,7 @@ func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.
Name: name,
ChildIP: childIP,
ParentIP: parentIP,
Bridge: bridge,
ChildEnvironment: env,
Ports: ports,
eng: eng,
Expand Down Expand Up @@ -121,6 +123,7 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {

job.Setenv("ParentIP", l.ParentIP)
job.Setenv("ChildIP", l.ChildIP)
job.Setenv("Bridge", l.Bridge)
job.SetenvBool("IgnoreErrors", ignoreErrors)

out := make([]string, len(l.Ports))
Expand Down
7 changes: 5 additions & 2 deletions links/links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestLinkNew(t *testing.T) {
ports := make(nat.PortSet)
ports[nat.Port("6379/tcp")] = struct{}{}

link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", nil, ports, nil)
link, err := NewLink("172.0.17.3", "172.0.17.2", "docker0", "/db/docker", nil, ports, nil)
if err != nil {
t.Fatal(err)
}
Expand All @@ -65,13 +65,16 @@ func TestLinkNew(t *testing.T) {
t.Fail()
}
}
if link.Bridge != "docker0" {
t.Fail()
}
}

func TestLinkEnv(t *testing.T) {
ports := make(nat.PortSet)
ports[nat.Port("6379/tcp")] = struct{}{}

link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports, nil)
link, err := NewLink("172.0.17.3", "172.0.17.2", "docker0", "/db/docker", []string{"PASSWORD=gordon"}, ports, nil)
if err != nil {
t.Fatal(err)
}
Expand Down
13 changes: 13 additions & 0 deletions runconfig/hostconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ type DeviceMapping struct {
CgroupPermissions string
}

func (n NetworkMode) IsNonDefaultBridge() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "bridge"
}

func (n NetworkMode) GetNonDefaultBridge() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == "bridge" {
return parts[1]
}
return ""
}

type HostConfig struct {
Binds []string
ContainerIDFile string
Expand Down
2 changes: 1 addition & 1 deletion runconfig/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
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.")
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'bridge:<custom bridge>': creates a new network stack for the container on the user specified custom bridge\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.")
// For documentation purpose
_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify received signals to the process (even in non-TTY mode). SIGCHLD is not proxied.")
_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
Expand Down