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:
commit
70fef1460a
9 changed files with 209 additions and 30 deletions
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package runconfig
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"testing"
|
||||
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
func TestParseLxcConfOpt(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue