Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/check_kernel_capabilities'

Solomon Hykes 12 anni fa
parent
commit
76b40ad6c9
8 ha cambiato i file con 210 aggiunte e 25 eliminazioni
  1. 1 4
      Makefile
  2. 10 7
      commands.go
  3. 16 5
      container.go
  4. 1 6
      docker/docker.go
  5. 37 1
      runtime.go
  6. 0 2
      runtime_test.go
  7. 112 0
      utils.go
  8. 33 0
      utils_test.go

+ 1 - 4
Makefile

@@ -13,10 +13,7 @@ endif
 GIT_COMMIT = $(shell git rev-parse --short HEAD)
 GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES")
 
-NO_MEMORY_LIMIT ?= 0
-export NO_MEMORY_LIMIT
-
-BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS) -X main.NO_MEMORY_LIMIT $(NO_MEMORY_LIMIT)"
+BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS)"
 
 SRC_DIR := $(GOPATH)/src
 

+ 10 - 7
commands.go

@@ -21,8 +21,7 @@ import (
 const VERSION = "0.1.7"
 
 var (
-	GIT_COMMIT      string
-	NO_MEMORY_LIMIT bool
+	GIT_COMMIT string
 )
 
 func (srv *Server) Name() string {
@@ -184,10 +183,14 @@ func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string
 
 // 'docker version': show version information
 func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
-	fmt.Fprintf(stdout, "Version:%s\n", VERSION)
-	fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT)
-	if NO_MEMORY_LIMIT {
-		fmt.Fprintf(stdout, "Memory limit disabled\n")
+	fmt.Fprintf(stdout, "Version: %s\n", VERSION)
+	fmt.Fprintf(stdout, "Git Commit: %s\n", GIT_COMMIT)
+	fmt.Fprintf(stdout, "Kernel: %s\n", srv.runtime.kernelVersion)
+	if !srv.runtime.capabilities.MemoryLimit {
+		fmt.Fprintf(stdout, "WARNING: No memory limit support\n")
+	}
+	if !srv.runtime.capabilities.SwapLimit {
+		fmt.Fprintf(stdout, "WARNING: No swap limit support\n")
 	}
 	return nil
 }
@@ -910,7 +913,7 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string)
 }
 
 func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
-	config, err := ParseRun(args, stdout)
+	config, err := ParseRun(args, stdout, srv.runtime.capabilities)
 	if err != nil {
 		return err
 	}

+ 16 - 5
container.go

@@ -68,7 +68,7 @@ type Config struct {
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
 }
 
-func ParseRun(args []string, stdout io.Writer) (*Config, error) {
+func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) {
 	cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
 	if len(args) > 0 && args[0] != "--help" {
 		cmd.SetOutput(ioutil.Discard)
@@ -83,8 +83,8 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
 	flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
 	flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
 
-	if *flMemory > 0 && NO_MEMORY_LIMIT {
-		fmt.Fprintf(stdout, "WARNING: This version of docker has been compiled without memory limit support. Discarding -m.")
+	if *flMemory > 0 && !capabilities.MemoryLimit {
+		fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
 		*flMemory = 0
 	}
 
@@ -137,6 +137,12 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
 		Dns:          flDns,
 		Image:        image,
 	}
+
+	if *flMemory > 0 && !capabilities.SwapLimit {
+		fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
+		config.MemorySwap = -1
+	}
+
 	// When allocating stdin in attached mode, close stdin at client disconnect
 	if config.OpenStdin && config.AttachStdin {
 		config.StdinOnce = true
@@ -379,10 +385,15 @@ func (container *Container) Start() error {
 		return err
 	}
 
-	if container.Config.Memory > 0 && NO_MEMORY_LIMIT {
-		log.Printf("WARNING: This version of docker has been compiled without memory limit support. Discarding the limit.")
+	// Make sure the config is compatible with the current kernel
+	if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
+		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
 		container.Config.Memory = 0
 	}
+	if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
+		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
+		container.Config.MemorySwap = -1
+	}
 
 	if err := container.generateLXCConfig(); err != nil {
 		return err

+ 1 - 6
docker/docker.go

@@ -14,8 +14,7 @@ import (
 )
 
 var (
-	GIT_COMMIT      string
-	NO_MEMORY_LIMIT string
+	GIT_COMMIT string
 )
 
 func main() {
@@ -39,15 +38,11 @@ func main() {
 		os.Setenv("DEBUG", "1")
 	}
 	docker.GIT_COMMIT = GIT_COMMIT
-	docker.NO_MEMORY_LIMIT = NO_MEMORY_LIMIT == "1"
 	if *flDaemon {
 		if flag.NArg() != 0 {
 			flag.Usage()
 			return
 		}
-		if NO_MEMORY_LIMIT == "1" {
-			log.Printf("WARNING: This version of docker has been compiled without memory limit support.")
-		}
 		if err := daemon(*pidfile); err != nil {
 			log.Fatal(err)
 		}

+ 37 - 1
runtime.go

@@ -6,6 +6,7 @@ import (
 	"github.com/dotcloud/docker/auth"
 	"io"
 	"io/ioutil"
+	"log"
 	"os"
 	"os/exec"
 	"path"
@@ -14,6 +15,11 @@ import (
 	"time"
 )
 
+type Capabilities struct {
+	MemoryLimit bool
+	SwapLimit   bool
+}
+
 type Runtime struct {
 	root           string
 	repository     string
@@ -23,6 +29,8 @@ type Runtime struct {
 	repositories   *TagStore
 	authConfig     *auth.AuthConfig
 	idIndex        *TruncIndex
+	capabilities   *Capabilities
+	kernelVersion  *KernelVersionInfo
 }
 
 var sysInitPath string
@@ -282,7 +290,34 @@ func (runtime *Runtime) restore() error {
 
 // FIXME: harmonize with NewGraph()
 func NewRuntime() (*Runtime, error) {
-	return NewRuntimeFromDirectory("/var/lib/docker")
+	runtime, err := NewRuntimeFromDirectory("/var/lib/docker")
+	if err != nil {
+		return nil, err
+	}
+
+	k, err := GetKernelVersion()
+	if err != nil {
+		return nil, err
+	}
+	runtime.kernelVersion = k
+
+	if CompareKernelVersion(k, &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
+		log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
+	}
+
+	cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory")
+	if err != nil {
+		return nil, err
+	}
+
+	_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "/memory.limit_in_bytes"))
+	_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
+	runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
+
+	_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memeory.memsw.limit_in_bytes"))
+	runtime.capabilities.SwapLimit = err == nil
+
+	return runtime, nil
 }
 
 func NewRuntimeFromDirectory(root string) (*Runtime, error) {
@@ -321,6 +356,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
 		repositories:   repositories,
 		authConfig:     authConfig,
 		idIndex:        NewTruncIndex(),
+		capabilities:   &Capabilities{},
 	}
 
 	if err := runtime.restore(); err != nil {

+ 0 - 2
runtime_test.go

@@ -48,8 +48,6 @@ func layerArchive(tarfile string) (io.Reader, error) {
 }
 
 func init() {
-	NO_MEMORY_LIMIT = os.Getenv("NO_MEMORY_LIMIT") == "1"
-
 	// Hack to run sys init during unit testing
 	if SelfPath() == "/sbin/init" {
 		SysInit()

+ 112 - 0
utils.go

@@ -12,9 +12,12 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"runtime"
+	"strconv"
 	"strings"
 	"sync"
+	"syscall"
 	"time"
 )
 
@@ -384,3 +387,112 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
 	}
 	return written, err
 }
+
+type KernelVersionInfo struct {
+	Kernel   int
+	Major    int
+	Minor    int
+	Specific int
+}
+
+func GetKernelVersion() (*KernelVersionInfo, error) {
+	var uts syscall.Utsname
+
+	if err := syscall.Uname(&uts); err != nil {
+		return nil, err
+	}
+
+	release := make([]byte, len(uts.Release))
+
+	i := 0
+	for _, c := range uts.Release {
+		release[i] = byte(c)
+		i++
+	}
+
+	tmp := strings.SplitN(string(release), "-", 2)
+	if len(tmp) != 2 {
+		return nil, fmt.Errorf("Unrecognized kernel version")
+	}
+	tmp2 := strings.SplitN(tmp[0], ".", 3)
+	if len(tmp2) != 3 {
+		return nil, fmt.Errorf("Unrecognized kernel version")
+	}
+
+	kernel, err := strconv.Atoi(tmp2[0])
+	if err != nil {
+		return nil, err
+	}
+
+	major, err := strconv.Atoi(tmp2[1])
+	if err != nil {
+		return nil, err
+	}
+
+	minor, err := strconv.Atoi(tmp2[2])
+	if err != nil {
+		return nil, err
+	}
+
+	specific, err := strconv.Atoi(strings.Split(tmp[1], "-")[0])
+	if err != nil {
+		return nil, err
+	}
+
+	return &KernelVersionInfo{
+		Kernel:   kernel,
+		Major:    major,
+		Minor:    minor,
+		Specific: specific,
+	}, nil
+}
+
+func (k *KernelVersionInfo) String() string {
+	return fmt.Sprintf("%d.%d.%d-%d", k.Kernel, k.Major, k.Minor, k.Specific)
+}
+
+// Compare two KernelVersionInfo struct.
+// Returns -1 if a < b, = if a == b, 1 it a > b
+func CompareKernelVersion(a, b *KernelVersionInfo) int {
+	if a.Kernel < b.Kernel {
+		return -1
+	} else if a.Kernel > b.Kernel {
+		return 1
+	}
+
+	if a.Major < b.Major {
+		return -1
+	} else if a.Major > b.Major {
+		return 1
+	}
+
+	if a.Minor < b.Minor {
+		return -1
+	} else if a.Minor > b.Minor {
+		return 1
+	}
+
+	if a.Specific < b.Specific {
+		return -1
+	} else if a.Specific > b.Specific {
+		return 1
+	}
+	return 0
+}
+
+func FindCgroupMountpoint(cgroupType string) (string, error) {
+	output, err := exec.Command("mount").CombinedOutput()
+	if err != nil {
+		return "", err
+	}
+
+	reg := regexp.MustCompile(`^cgroup on (.*) type cgroup \(.*` + cgroupType + `[,\)]`)
+	for _, line := range strings.Split(string(output), "\n") {
+		r := reg.FindStringSubmatch(line)
+		if len(r) == 2 {
+			return r[1], nil
+		}
+		fmt.Printf("line: %s (%d)\n", line, len(r))
+	}
+	return "", fmt.Errorf("cgroup mountpoint not found")
+}

+ 33 - 0
utils_test.go

@@ -228,3 +228,36 @@ func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult strin
 		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
 	}
 }
+
+func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
+	if r := CompareKernelVersion(a, b); r != result {
+		t.Fatalf("Unepected kernel version comparaison result. Found %d, expected %d", r, result)
+	}
+}
+
+func TestCompareKernelVersion(t *testing.T) {
+	assertKernelVersion(t,
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		0)
+	assertKernelVersion(t,
+		&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0, Specific: 0},
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		-1)
+	assertKernelVersion(t,
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0, Specific: 0},
+		1)
+	assertKernelVersion(t,
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 16},
+		-1)
+	assertKernelVersion(t,
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5, Specific: 0},
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		1)
+	assertKernelVersion(t,
+		&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20, Specific: 25},
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
+		-1)
+}