New oom sync (#257)
* Vendor in runc afaa21f79ade3b2e99a68f3f15e7219155aa4662 This updates the Dockerfile to use go 1.6.2 and install pkg-config are both are now needed by runc. Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com> * Add support for runc create/start operation Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com> * Remove dependency on runc state directory for OOM handler Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com> * Add OOM test Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
cf554d59dd
commit
8040df4e89
214 changed files with 215 additions and 30135 deletions
|
@ -413,7 +413,7 @@ func (c *container) Start(checkpointPath string, s Stdio) (Process, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.startCmd(InitProcessID, cmd, p); err != nil {
|
||||
if err := c.createCmd(InitProcessID, cmd, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
|
@ -453,13 +453,13 @@ func (c *container) Exec(pid string, pspec specs.ProcessSpec, s Stdio) (pp Proce
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.startCmd(pid, cmd, p); err != nil {
|
||||
if err := c.createCmd(pid, cmd, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) error {
|
||||
func (c *container) createCmd(pid string, cmd *exec.Cmd, p *process) error {
|
||||
p.cmd = cmd
|
||||
if err := cmd.Start(); err != nil {
|
||||
if exErr, ok := err.(*exec.Error); ok {
|
||||
|
@ -469,7 +469,7 @@ func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
if err := c.waitForStart(p, cmd); err != nil {
|
||||
if err := c.waitForCreate(p, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
c.processes[pid] = p
|
||||
|
@ -552,7 +552,7 @@ type waitArgs struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (c *container) waitForStart(p *process, cmd *exec.Cmd) error {
|
||||
func (c *container) waitForCreate(p *process, cmd *exec.Cmd) error {
|
||||
wc := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -11,46 +12,84 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func (c *container) getLibctContainer() (libcontainer.Container, error) {
|
||||
runtimeRoot := "/run/runc"
|
||||
func findCgroupMountpointAndRoot(pid int, subsystem string) (string, string, error) {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Check that the root wasn't changed
|
||||
for _, opt := range c.runtimeArgs {
|
||||
if strings.HasPrefix(opt, "--root=") {
|
||||
runtimeRoot = strings.TrimPrefix(opt, "--root=")
|
||||
break
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
fields := strings.Split(txt, " ")
|
||||
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||
if opt == subsystem {
|
||||
return fields[4], fields[3], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
f, err := libcontainer.New(runtimeRoot, libcontainer.Cgroupfs)
|
||||
return "", "", fmt.Errorf("cgroup path for %s not found", subsystem)
|
||||
}
|
||||
|
||||
func parseCgroupFile(path string) (map[string]string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.Load(c.id)
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
cgroups := make(map[string]string)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := s.Text()
|
||||
parts := strings.Split(text, ":")
|
||||
|
||||
for _, subs := range strings.Split(parts[1], ",") {
|
||||
cgroups[subs] = parts[2]
|
||||
}
|
||||
}
|
||||
return cgroups, nil
|
||||
}
|
||||
|
||||
func (c *container) OOM() (OOM, error) {
|
||||
container, err := c.getLibctContainer()
|
||||
if err != nil {
|
||||
if lerr, ok := err.(libcontainer.Error); ok {
|
||||
// with oom registration sometimes the container can run, exit, and be destroyed
|
||||
// faster than we can get the state back so we can just ignore this
|
||||
if lerr.Code() == libcontainer.ContainerNotExists {
|
||||
return nil, ErrContainerExited
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
p := c.processes[InitProcessID]
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("no init process found")
|
||||
}
|
||||
state, err := container.State()
|
||||
|
||||
mountpoint, hostRoot, err := findCgroupMountpointAndRoot(os.Getpid(), "memory")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memoryPath := state.CgroupPaths["memory"]
|
||||
return c.getMemeoryEventFD(memoryPath)
|
||||
|
||||
cgroups, err := parseCgroupFile(fmt.Sprintf("/proc/%d/cgroup", p.pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root, ok := cgroups["memory"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no memory cgroup for container %s", c.ID())
|
||||
}
|
||||
|
||||
// Take care of the case were we're running inside a container
|
||||
// ourself
|
||||
root = strings.TrimPrefix(root, hostRoot)
|
||||
|
||||
return c.getMemeoryEventFD(filepath.Join(mountpoint, root))
|
||||
}
|
||||
|
||||
func u64Ptr(i uint64) *uint64 { return &i }
|
||||
|
|
|
@ -23,6 +23,9 @@ type Process interface {
|
|||
// This is either "init" when it is the container's init process or
|
||||
// it is a user provided id for the process similar to the container id
|
||||
ID() string
|
||||
// Start unblocks the associated container init process.
|
||||
// This should only be called on the process with ID "init"
|
||||
Start() error
|
||||
CloseStdin() error
|
||||
Resize(int, int) error
|
||||
// ExitFD returns the fd the provides an event when the process exits
|
||||
|
@ -252,3 +255,13 @@ func getControlPipe(path string) (*os.File, error) {
|
|||
func (p *process) Signal(s os.Signal) error {
|
||||
return syscall.Kill(p.pid, s.(syscall.Signal))
|
||||
}
|
||||
|
||||
// Start unblocks the associated container init process.
|
||||
// This should only be called on the process with ID "init"
|
||||
func (p *process) Start() error {
|
||||
if p.ID() == InitProcessID {
|
||||
_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 2, 0, 0)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue