1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #4441 from crosbymichael/add-net-flag

Add --net flag to docker run and allow host network stack
This commit is contained in:
Guillaume J. Charmes 2014-05-05 13:54:55 -07:00
commit 70fef1460a
9 changed files with 209 additions and 30 deletions

View file

@ -325,7 +325,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
})
}
func populateCommand(c *Container, env []string) {
func populateCommand(c *Container, env []string) error {
var (
en *execdriver.Network
context = make(map[string][]string)
@ -338,14 +338,29 @@ func populateCommand(c *Container, env []string) {
Interface: nil,
}
if !c.Config.NetworkDisabled {
network := c.NetworkSettings
en.Interface = &execdriver.NetworkInterface{
Gateway: network.Gateway,
Bridge: network.Bridge,
IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen,
parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
switch parts[0] {
case "none":
case "host":
en.HostNetworking = true
case "bridge", "": // empty string to support existing containers
if !c.Config.NetworkDisabled {
network := c.NetworkSettings
en.Interface = &execdriver.NetworkInterface{
Gateway: network.Gateway,
Bridge: network.Bridge,
IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen,
}
}
case "container":
nc, err := c.getNetworkedContainer()
if err != nil {
return err
}
en.ContainerID = nc.ID
default:
return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
}
// TODO: this can be removed after lxc-conf is fully deprecated
@ -372,6 +387,7 @@ func populateCommand(c *Container, env []string) {
}
c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
c.command.Env = env
return nil
}
func (container *Container) Start() (err error) {
@ -415,7 +431,9 @@ func (container *Container) Start() (err error) {
if err := container.setupWorkingDirectory(); err != nil {
return err
}
populateCommand(container, env)
if err := populateCommand(container, env); err != nil {
return err
}
if err := setupMountsForContainer(container); err != nil {
return err
}
@ -485,9 +503,18 @@ func (container *Container) StderrLogPipe() io.ReadCloser {
return utils.NewBufReader(reader)
}
func (container *Container) buildHostnameAndHostsFiles(IP string) {
func (container *Container) buildHostname() {
container.HostnamePath = path.Join(container.root, "hostname")
ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
if container.Config.Domainname != "" {
ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644)
} else {
ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
}
}
func (container *Container) buildHostnameAndHostsFiles(IP string) {
container.buildHostname()
hostsContent := []byte(`
127.0.0.1 localhost
@ -505,12 +532,12 @@ ff02::2 ip6-allrouters
} else if !container.Config.NetworkDisabled {
hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
}
ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
}
func (container *Container) allocateNetwork() error {
if container.Config.NetworkDisabled {
mode := container.hostConfig.NetworkMode
if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() {
return nil
}
@ -963,14 +990,22 @@ func (container *Container) setupContainerDns() error {
if container.ResolvConfPath != "" {
return nil
}
var (
config = container.hostConfig
daemon = container.daemon
)
if config.NetworkMode == "host" {
container.ResolvConfPath = "/etc/resolv.conf"
return nil
}
resolvConf, err := utils.GetResolvConf()
if err != nil {
return err
}
// If custom dns exists, then create a resolv.conf for the container
if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
var (
@ -1010,7 +1045,32 @@ func (container *Container) setupContainerDns() error {
}
func (container *Container) initializeNetworking() error {
if container.daemon.config.DisableNetwork {
var err error
if container.hostConfig.NetworkMode.IsHost() {
container.Config.Hostname, err = os.Hostname()
if err != nil {
return err
}
parts := strings.SplitN(container.Config.Hostname, ".", 2)
if len(parts) > 1 {
container.Config.Hostname = parts[0]
container.Config.Domainname = parts[1]
}
container.HostsPath = "/etc/hosts"
container.buildHostname()
} else if container.hostConfig.NetworkMode.IsContainer() {
// we need to get the hosts files from the container to join
nc, err := container.getNetworkedContainer()
if err != nil {
return err
}
container.HostsPath = nc.HostsPath
container.ResolvConfPath = nc.ResolvConfPath
container.Config.Hostname = nc.Config.Hostname
container.Config.Domainname = nc.Config.Domainname
} else if container.daemon.config.DisableNetwork {
container.Config.NetworkDisabled = true
container.buildHostnameAndHostsFiles("127.0.1.1")
} else {
@ -1219,3 +1279,20 @@ func (container *Container) GetMountLabel() string {
}
return container.MountLabel
}
func (container *Container) getNetworkedContainer() (*Container, error) {
parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2)
switch parts[0] {
case "container":
nc := container.daemon.Get(parts[1])
if nc == nil {
return nil, fmt.Errorf("no such container to join network: %s", parts[1])
}
if !nc.State.IsRunning() {
return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])
}
return nc, nil
default:
return nil, fmt.Errorf("network mode not set to container")
}
}

View file

@ -89,8 +89,10 @@ type Driver interface {
// Network settings of the container
type Network struct {
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
Mtu int `json:"mtu"`
Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
Mtu int `json:"mtu"`
ContainerID string `json:"container_id"` // id of the container to join network.
HostNetworking bool `json:"host_networking"`
}
type NetworkInterface struct {

View file

@ -3,15 +3,16 @@ package lxc
import (
"encoding/json"
"fmt"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/netlink"
"github.com/dotcloud/docker/pkg/user"
"github.com/syndtr/gocapability/capability"
"io/ioutil"
"net"
"os"
"strings"
"syscall"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/netlink"
"github.com/dotcloud/docker/pkg/user"
"github.com/syndtr/gocapability/capability"
)
// Clear environment pollution introduced by lxc-start

View file

@ -14,12 +14,13 @@ const LxcTemplate = `
lxc.network.type = veth
lxc.network.link = {{.Network.Interface.Bridge}}
lxc.network.name = eth0
{{else}}
lxc.network.mtu = {{.Network.Mtu}}
{{else if not .Network.HostNetworking}}
# network is disabled (-n=false)
lxc.network.type = empty
lxc.network.flags = up
{{end}}
lxc.network.mtu = {{.Network.Mtu}}
{{end}}
# root filesystem
{{$ROOTFS := .Rootfs}}

View file

@ -3,6 +3,7 @@ package native
import (
"fmt"
"os"
"path/filepath"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
@ -52,6 +53,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
}
func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
if c.Network.HostNetworking {
container.Namespaces.Get("NEWNET").Enabled = false
return nil
}
container.Networks = []*libcontainer.Network{
{
Mtu: c.Network.Mtu,
@ -75,6 +80,20 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
}
container.Networks = append(container.Networks, &vethNetwork)
}
if c.Network.ContainerID != "" {
cmd := d.activeContainers[c.Network.ContainerID]
if cmd == nil || cmd.Process == nil {
return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID)
}
nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
container.Networks = append(container.Networks, &libcontainer.Network{
Type: "netns",
Context: libcontainer.Context{
"nspath": nspath,
},
})
}
return nil
}

View file

@ -136,8 +136,8 @@ PID files):
## Network Settings
-n=true : Enable networking for this container
--dns=[] : Set custom dns servers for the container
--dns=[] : Set custom dns servers for the container
--net=bridge : Set the network mode
By default, all containers have networking enabled and they can make any
outgoing connections. The operator can completely disable networking
@ -148,6 +148,48 @@ files or STDIN/STDOUT only.
Your container will use the same DNS servers as the host by default, but
you can override this with `--dns`.
Supported networking modes are:
* none - no networking in the container
* bridge - (default) connect the container to the bridge via veth interfaces
* host - use the host's network stack inside the container
* container - use another container's network stack
#### Mode: none
With the networking mode set to `none` a container will not have a access to
any external routes. The container will still have a `loopback` interface
enabled in the container but it does not have any routes to external traffic.
#### Mode: bridge
With the networking mode set to `bridge` a container will use docker's default
networking setup. A bridge is setup on the host, commonly named `docker0`,
and a pair of veth interfaces will be created for the container. 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.
#### Mode: host
With the networking mode set to `host` a container will share the host's
network stack and all interfaces from the host will be available to the
container. The container's hostname will match the hostname on the host
system. Publishing ports and linking to other containers will not work
when sharing the host's network stack.
#### Mode: container
With the networking mode set to `container` a container will share the
network stack of another container. The other container's name must be
provided in the format of `--net container:<name|id>`.
Example running a redis container with redis binding to localhost then
running the redis-cli and connecting to the redis server over the
localhost interface.
$ docker run -d --name redis example/redis --bind 127.0.0.1
$ # use the redis container's network stack to access localhost
$ docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1
## Clean Up (rm)
By default a container's file system persists even after the container

View file

@ -1,11 +1,24 @@
package runconfig
import (
"strings"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/utils"
)
type NetworkMode string
func (n NetworkMode) IsHost() bool {
return n == "host"
}
func (n NetworkMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container"
}
type HostConfig struct {
Binds []string
ContainerIDFile string
@ -17,6 +30,7 @@ type HostConfig struct {
Dns []string
DnsSearch []string
VolumesFrom []string
NetworkMode NetworkMode
}
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
@ -24,6 +38,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
ContainerIDFile: job.Getenv("ContainerIDFile"),
Privileged: job.GetenvBool("Privileged"),
PublishAllPorts: job.GetenvBool("PublishAllPorts"),
NetworkMode: NetworkMode(job.Getenv("NetworkMode")),
}
job.GetenvJson("LxcConf", &hostConfig.LxcConf)
job.GetenvJson("PortBindings", &hostConfig.PortBindings)

View file

@ -2,14 +2,15 @@ package runconfig
import (
"fmt"
"io/ioutil"
"path"
"strings"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/opts"
flag "github.com/dotcloud/docker/pkg/mflag"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"path"
"strings"
)
var (
@ -49,7 +50,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
flNetwork = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
@ -61,7 +62,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID")
flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
flNetMode = cmd.String([]string{"-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)")
// For documentation purpose
_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
@ -197,6 +198,11 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
// boo, there's no debug output for docker run
//utils.Debugf("Environment variables for the container: %#v", envVariables)
netMode, err := parseNetMode(*flNetMode)
if err != nil {
return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err)
}
config := &Config{
Hostname: hostname,
Domainname: domainname,
@ -230,6 +236,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
Dns: flDns.GetAll(),
DnsSearch: flDnsSearch.GetAll(),
VolumesFrom: flVolumesFrom.GetAll(),
NetworkMode: netMode,
}
if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
@ -274,3 +281,17 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) {
}
return out, nil
}
func parseNetMode(netMode string) (NetworkMode, error) {
parts := strings.Split(netMode, ":")
switch mode := parts[0]; mode {
case "bridge", "none", "host":
case "container":
if len(parts) < 2 || parts[1] == "" {
return "", fmt.Errorf("invalid container format container:<name|id>")
}
default:
return "", fmt.Errorf("invalid --net: %s", netMode)
}
return NetworkMode(netMode), nil
}

View file

@ -1,8 +1,9 @@
package runconfig
import (
"github.com/dotcloud/docker/utils"
"testing"
"github.com/dotcloud/docker/utils"
)
func TestParseLxcConfOpt(t *testing.T) {