Merge remote-tracking branch 'origin/check_kernel_capabilities'

This commit is contained in:
Solomon Hykes 2013-04-20 17:40:25 -07:00
Родитель 3b6c540fe8 f3e89fae28
Коммит 76b40ad6c9
8 изменённых файлов: 210 добавлений и 25 удалений

Просмотреть файл

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

Просмотреть файл

@ -21,8 +21,7 @@ import (
const VERSION = "0.1.7" const VERSION = "0.1.7"
var ( var (
GIT_COMMIT string GIT_COMMIT string
NO_MEMORY_LIMIT bool
) )
func (srv *Server) Name() 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 // 'docker version': show version information
func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
fmt.Fprintf(stdout, "Version:%s\n", VERSION) fmt.Fprintf(stdout, "Version: %s\n", VERSION)
fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT) fmt.Fprintf(stdout, "Git Commit: %s\n", GIT_COMMIT)
if NO_MEMORY_LIMIT { fmt.Fprintf(stdout, "Kernel: %s\n", srv.runtime.kernelVersion)
fmt.Fprintf(stdout, "Memory limit disabled\n") 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 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 { 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 { if err != nil {
return err return err
} }

Просмотреть файл

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

Просмотреть файл

@ -14,8 +14,7 @@ import (
) )
var ( var (
GIT_COMMIT string GIT_COMMIT string
NO_MEMORY_LIMIT string
) )
func main() { func main() {
@ -39,15 +38,11 @@ func main() {
os.Setenv("DEBUG", "1") os.Setenv("DEBUG", "1")
} }
docker.GIT_COMMIT = GIT_COMMIT docker.GIT_COMMIT = GIT_COMMIT
docker.NO_MEMORY_LIMIT = NO_MEMORY_LIMIT == "1"
if *flDaemon { if *flDaemon {
if flag.NArg() != 0 { if flag.NArg() != 0 {
flag.Usage() flag.Usage()
return 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 { if err := daemon(*pidfile); err != nil {
log.Fatal(err) log.Fatal(err)
} }

Просмотреть файл

@ -6,6 +6,7 @@ import (
"github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/auth"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@ -14,6 +15,11 @@ import (
"time" "time"
) )
type Capabilities struct {
MemoryLimit bool
SwapLimit bool
}
type Runtime struct { type Runtime struct {
root string root string
repository string repository string
@ -23,6 +29,8 @@ type Runtime struct {
repositories *TagStore repositories *TagStore
authConfig *auth.AuthConfig authConfig *auth.AuthConfig
idIndex *TruncIndex idIndex *TruncIndex
capabilities *Capabilities
kernelVersion *KernelVersionInfo
} }
var sysInitPath string var sysInitPath string
@ -282,7 +290,34 @@ func (runtime *Runtime) restore() error {
// FIXME: harmonize with NewGraph() // FIXME: harmonize with NewGraph()
func NewRuntime() (*Runtime, error) { 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) { func NewRuntimeFromDirectory(root string) (*Runtime, error) {
@ -321,6 +356,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
repositories: repositories, repositories: repositories,
authConfig: authConfig, authConfig: authConfig,
idIndex: NewTruncIndex(), idIndex: NewTruncIndex(),
capabilities: &Capabilities{},
} }
if err := runtime.restore(); err != nil { if err := runtime.restore(); err != nil {

Просмотреть файл

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

112
utils.go
Просмотреть файл

@ -12,9 +12,12 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
) )
@ -384,3 +387,112 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
} }
return written, err 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")
}

Просмотреть файл

@ -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) 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)
}