diff --git a/cgroups/cgroups.go b/cgroups/cgroups.go index 30de8d4d1e..fdcab8c3da 100644 --- a/cgroups/cgroups.go +++ b/cgroups/cgroups.go @@ -12,8 +12,13 @@ import ( "strings" ) -// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt +type Values struct { + Memory int64 `json:"memory"` + MemorySwap int64 `json:"memory_swap"` + CpuShares int64 `json:"cpu_shares"` +} +// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt func FindCgroupMountpoint(subsystem string) (string, error) { mounts, err := mount.GetMounts() if err != nil { diff --git a/container.go b/container.go index 23a3c05335..79d9511d7b 100644 --- a/container.go +++ b/container.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/cgroups" "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/mount" @@ -299,15 +300,6 @@ func (container *Container) generateEnvConfig(env []string) error { return nil } -func (container *Container) generateLXCConfig() error { - fo, err := os.Create(container.lxcConfigPath()) - if err != nil { - return err - } - defer fo.Close() - return LxcTemplateCompiled.Execute(fo, container) -} - func (container *Container) setupPty() error { ptyMaster, ptySlave, err := pty.Open() if err != nil { @@ -554,10 +546,6 @@ func (container *Container) Start() (err error) { return err } - if err := container.generateLXCConfig(); err != nil { - return err - } - // Setup environment env := []string{ "HOME=/", @@ -662,17 +650,33 @@ func (container *Container) Start() (err error) { } } - var en *execdriver.Network + var ( + en *execdriver.Network + driverConfig []string + ) + if !container.Config.NetworkDisabled { network := container.NetworkSettings en = &execdriver.Network{ Gateway: network.Gateway, + Bridge: network.Bridge, IPAddress: network.IPAddress, IPPrefixLen: network.IPPrefixLen, Mtu: container.runtime.config.Mtu, } } + if lxcConf := container.hostConfig.LxcConf; lxcConf != nil { + for _, pair := range lxcConf { + driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value)) + } + } + cgroupValues := &cgroups.Values{ + Memory: container.Config.Memory, + MemorySwap: container.Config.MemorySwap, + CpuShares: container.Config.CpuShares, + } + container.process = &execdriver.Process{ ID: container.ID, Privileged: container.hostConfig.Privileged, @@ -681,10 +685,11 @@ func (container *Container) Start() (err error) { Entrypoint: container.Path, Arguments: container.Args, WorkingDir: workingDir, - ConfigPath: container.lxcConfigPath(), Network: en, Tty: container.Config.Tty, User: container.Config.User, + Config: driverConfig, + Cgroups: cgroupValues, } container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} @@ -1381,10 +1386,6 @@ func (container *Container) EnvConfigPath() (string, error) { return p, nil } -func (container *Container) lxcConfigPath() string { - return path.Join(container.root, "config.lxc") -} - // This method must be exported to be used from the lxc template func (container *Container) RootfsPath() string { return container.rootfs diff --git a/execdriver/driver.go b/execdriver/driver.go index e4d512f7bc..0141eef18d 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -2,6 +2,7 @@ package execdriver import ( "errors" + "github.com/dotcloud/docker/cgroups" "os/exec" "syscall" ) @@ -71,6 +72,7 @@ type Driver interface { type Network struct { Gateway string `json:"gateway"` IPAddress string `json:"ip"` + Bridge string `json:"bridge"` IPPrefixLen int `json:"ip_prefix_len"` Mtu int `json:"mtu"` } @@ -79,17 +81,19 @@ type Network struct { type Process struct { exec.Cmd - ID string `json:"id"` - Privileged bool `json:"privileged"` - User string `json:"user"` - Rootfs string `json:"rootfs"` // root fs of the container - InitPath string `json:"initpath"` // dockerinit - Entrypoint string `json:"entrypoint"` - Arguments []string `json:"arguments"` - WorkingDir string `json:"working_dir"` - ConfigPath string `json:"config_path"` // This should be able to be removed when the lxc template is moved into the driver - Tty bool `json:"tty"` - Network *Network `json:"network"` // if network is nil then networking is disabled + ID string `json:"id"` + Privileged bool `json:"privileged"` + User string `json:"user"` + Rootfs string `json:"rootfs"` // root fs of the container + InitPath string `json:"initpath"` // dockerinit + Entrypoint string `json:"entrypoint"` + Arguments []string `json:"arguments"` + WorkingDir string `json:"working_dir"` + ConfigPath string `json:"config_path"` // This should be able to be removed when the lxc template is moved into the driver + Tty bool `json:"tty"` + Network *Network `json:"network"` // if network is nil then networking is disabled + Config []string `json:"config"` + Cgroups *cgroups.Values `json:"cgroups"` } // Return the pid of the process diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 3b4572af96..09203dfbf1 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -72,10 +72,14 @@ func (d *driver) Name() string { } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { + configPath, err := d.generateLXCConfig(c) + if err != nil { + return -1, err + } params := []string{ "lxc-start", "-n", c.ID, - "-f", c.ConfigPath, + "-f", configPath, "--", c.InitPath, "-driver", @@ -259,7 +263,6 @@ func (i *info) IsRunning() bool { } func (d *driver) Info(id string) execdriver.Info { - return &info{ ID: id, driver: d, @@ -297,3 +300,23 @@ func rootIsShared() bool { // No idea, probably safe to assume so return true } + +func (d *driver) generateLXCConfig(p *execdriver.Process) (string, error) { + root := path.Join(d.root, "containers", p.ID, "config.lxc") + fo, err := os.Create(root) + if err != nil { + return "", err + } + defer fo.Close() + + if err := LxcTemplateCompiled.Execute(fo, struct { + *execdriver.Process + AppArmor bool + }{ + Process: p, + AppArmor: d.apparmor, + }); err != nil { + return "", err + } + return root, nil +} diff --git a/lxc_template.go b/execdriver/lxc/lxc_template.go similarity index 75% rename from lxc_template.go rename to execdriver/lxc/lxc_template.go index 4e1aece94b..deaa7a66ae 100644 --- a/lxc_template.go +++ b/execdriver/lxc/lxc_template.go @@ -1,24 +1,24 @@ -package docker +package lxc import ( - "github.com/dotcloud/docker/pkg/sysinfo" + "github.com/dotcloud/docker/cgroups" "strings" "text/template" ) const LxcTemplate = ` -{{if .Config.NetworkDisabled}} -# network is disabled (-n=false) -lxc.network.type = empty -{{else}} +{{if .Network}} # network configuration lxc.network.type = veth -lxc.network.link = {{.NetworkSettings.Bridge}} +lxc.network.link = {{.Network.Bridge}} lxc.network.name = eth0 +{{else}} +# network is disabled (-n=false) +lxc.network.type = empty {{end}} # root filesystem -{{$ROOTFS := .RootfsPath}} +{{$ROOTFS := .Rootfs}} lxc.rootfs = {{$ROOTFS}} # use a dedicated pts for the container (and limit the number of pseudo terminal @@ -31,7 +31,7 @@ lxc.console = none # no controlling tty at all lxc.tty = 1 -{{if (getHostConfig .).Privileged}} +{{if .Privileged}} lxc.cgroup.devices.allow = a {{else}} # no implicit access to devices @@ -82,8 +82,8 @@ lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noe lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 -{{if (getHostConfig .).Privileged}} -{{if (getSysInfo .).AppArmor}} +{{if .Privileged}} +{{if .AppArmor}} lxc.aa_profile = unconfined {{else}} #lxc.aa_profile = unconfined @@ -91,20 +91,22 @@ lxc.aa_profile = unconfined {{end}} # limits -{{if .Config.Memory}} -lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}} -lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}} -{{with $memSwap := getMemorySwap .Config}} +{{if .Cgroups}} +{{if .Cgroups.Memory}} +lxc.cgroup.memory.limit_in_bytes = {{.Cgroups.Memory}} +lxc.cgroup.memory.soft_limit_in_bytes = {{.Cgroups.Memory}} +{{with $memSwap := getMemorySwap .Cgroups}} lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}} {{end}} {{end}} -{{if .Config.CpuShares}} -lxc.cgroup.cpu.shares = {{.Config.CpuShares}} +{{if .Cgroups.CpuShares}} +lxc.cgroup.cpu.shares = {{.Cgroups.CpuShares}} +{{end}} {{end}} -{{if (getHostConfig .).LxcConf}} -{{range $pair := (getHostConfig .).LxcConf}} -{{$pair.Key}} = {{$pair.Value}} +{{if .Config}} +{{range $value := .Config}} +{{$value}} {{end}} {{end}} ` @@ -117,29 +119,19 @@ func escapeFstabSpaces(field string) string { return strings.Replace(field, " ", "\\040", -1) } -func getMemorySwap(config *Config) int64 { +func getMemorySwap(v *cgroups.Values) int64 { // By default, MemorySwap is set to twice the size of RAM. // If you want to omit MemorySwap, set it to `-1'. - if config.MemorySwap < 0 { + if v.MemorySwap < 0 { return 0 } - return config.Memory * 2 -} - -func getHostConfig(container *Container) *HostConfig { - return container.hostConfig -} - -func getSysInfo(container *Container) *sysinfo.SysInfo { - return container.runtime.sysInfo + return v.Memory * 2 } func init() { var err error funcMap := template.FuncMap{ "getMemorySwap": getMemorySwap, - "getHostConfig": getHostConfig, - "getSysInfo": getSysInfo, "escapeFstabSpaces": escapeFstabSpaces, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) diff --git a/lxc_template_unit_test.go b/execdriver/lxc/lxc_template_unit_test.go similarity index 60% rename from lxc_template_unit_test.go rename to execdriver/lxc/lxc_template_unit_test.go index f71f1dd6f5..fe65fe5cbc 100644 --- a/lxc_template_unit_test.go +++ b/execdriver/lxc/lxc_template_unit_test.go @@ -1,11 +1,14 @@ -package docker +package lxc import ( "bufio" "fmt" + "github.com/dotcloud/docker/cgroups" + "github.com/dotcloud/docker/execdriver" "io/ioutil" "math/rand" "os" + "path" "strings" "testing" "time" @@ -17,32 +20,39 @@ func TestLXCConfig(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) + + os.MkdirAll(path.Join(root, "containers", "1"), 0777) + // Memory is allocated randomly for testing rand.Seed(time.Now().UTC().UnixNano()) - memMin := 33554432 - memMax := 536870912 - mem := memMin + rand.Intn(memMax-memMin) - // CPU shares as well - cpuMin := 100 - cpuMax := 10000 - cpu := cpuMin + rand.Intn(cpuMax-cpuMin) - container := &Container{ - root: root, - Config: &Config{ - Memory: int64(mem), - CpuShares: int64(cpu), - NetworkDisabled: true, - }, - hostConfig: &HostConfig{ - Privileged: false, - }, - } - if err := container.generateLXCConfig(); err != nil { + var ( + memMin = 33554432 + memMax = 536870912 + mem = memMin + rand.Intn(memMax-memMin) + cpuMin = 100 + cpuMax = 10000 + cpu = cpuMin + rand.Intn(cpuMax-cpuMin) + ) + + driver, err := NewDriver(root, false) + if err != nil { t.Fatal(err) } - grepFile(t, container.lxcConfigPath(), + process := &execdriver.Process{ + ID: "1", + Cgroups: &cgroups.Values{ + Memory: int64(mem), + CpuShares: int64(cpu), + }, + } + p, err := driver.generateLXCConfig(process) + if err != nil { + t.Fatal(err) + } + grepFile(t, p, fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem)) - grepFile(t, container.lxcConfigPath(), + + grepFile(t, p, fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2)) } @@ -52,31 +62,29 @@ func TestCustomLxcConfig(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) - container := &Container{ - root: root, - Config: &Config{ - Hostname: "foobar", - NetworkDisabled: true, - }, - hostConfig: &HostConfig{ - Privileged: false, - LxcConf: []KeyValuePair{ - { - Key: "lxc.utsname", - Value: "docker", - }, - { - Key: "lxc.cgroup.cpuset.cpus", - Value: "0,1", - }, - }, - }, - } - if err := container.generateLXCConfig(); err != nil { + + os.MkdirAll(path.Join(root, "containers", "1"), 0777) + + driver, err := NewDriver(root, false) + if err != nil { t.Fatal(err) } - grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker") - grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1") + process := &execdriver.Process{ + ID: "1", + Privileged: false, + Config: []string{ + "lxc.utsname = docker", + "lxc.cgroup.cpuset.cpus = 0,1", + }, + } + + p, err := driver.generateLXCConfig(process) + if err != nil { + t.Fatal(err) + } + + grepFile(t, p, "lxc.utsname = docker") + grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") } func grepFile(t *testing.T, path string, pattern string) {