Remove dep on larger packages
This removes most of the deps on the larger packages for the shim and reduces the binary size and memory footprint from a 7.1mb binary to a 2.6mb binary. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
96034177f9
commit
6f18acda73
5 changed files with 122 additions and 41 deletions
2
Makefile
2
Makefile
|
@ -50,7 +50,7 @@ daemon-static:
|
||||||
cd containerd && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd
|
cd containerd && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd
|
||||||
|
|
||||||
shim: bin
|
shim: bin
|
||||||
cd containerd-shim && go build -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
cd containerd-shim && go build -tags "$(BUILDTAGS)" -ldflags "-w" -o ../bin/containerd-shim
|
||||||
|
|
||||||
shim-static:
|
shim-static:
|
||||||
cd containerd-shim && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
cd containerd-shim && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
||||||
|
|
54
containerd-shim/console.go
Normal file
54
containerd-shim/console.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewConsole returns an initalized console that can be used within a container by copying bytes
|
||||||
|
// from the master side to the slave that is attached as the tty for the container's init process.
|
||||||
|
func newConsole(uid, gid int) (*os.File, string, error) {
|
||||||
|
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
console, err := ptsname(master)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if err := unlockpt(master); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if err := os.Chmod(console, 0600); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if err := os.Chown(console, uid, gid); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return master, console, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ioctl(fd uintptr, flag, data uintptr) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||||
|
// unlockpt should be called before opening the slave side of a pty.
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
var u int32
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptsname retrieves the name of the first available pts for the given master.
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
var n int32
|
||||||
|
if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("/dev/pts/%d", n), nil
|
||||||
|
}
|
|
@ -8,11 +8,14 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/containerd/osutils"
|
"github.com/docker/containerd/osutils"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func writeMessage(f *os.File, level string, err error) {
|
||||||
|
fmt.Fprintf(f, `{"level": "%s","msg": "%s"}`, level, err)
|
||||||
|
}
|
||||||
|
|
||||||
// containerd-shim is a small shim that sits in front of a runtime implementation
|
// containerd-shim is a small shim that sits in front of a runtime implementation
|
||||||
// that allows it to be repartented to init and handle reattach from the caller.
|
// that allows it to be repartented to init and handle reattach from the caller.
|
||||||
//
|
//
|
||||||
|
@ -28,9 +31,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
logrus.SetOutput(f)
|
if err := start(f); err != nil {
|
||||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
|
||||||
if err := start(); err != nil {
|
|
||||||
// this means that the runtime failed starting the container and will have the
|
// this means that the runtime failed starting the container and will have the
|
||||||
// proper error messages in the runtime log so we should to treat this as a
|
// proper error messages in the runtime log so we should to treat this as a
|
||||||
// shim failure because the sim executed properly
|
// shim failure because the sim executed properly
|
||||||
|
@ -41,13 +42,13 @@ func main() {
|
||||||
// log the error instead of writing to stderr because the shim will have
|
// log the error instead of writing to stderr because the shim will have
|
||||||
// /dev/null as it's stdio because it is supposed to be reparented to system
|
// /dev/null as it's stdio because it is supposed to be reparented to system
|
||||||
// init and will not have anyone to read from it
|
// init and will not have anyone to read from it
|
||||||
logrus.Error(err)
|
writeMessage(f, "error", err)
|
||||||
f.Close()
|
f.Close()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() error {
|
func start(log *os.File) error {
|
||||||
// start handling signals as soon as possible so that things are properly reaped
|
// start handling signals as soon as possible so that things are properly reaped
|
||||||
// or if runtime exits before we hit the handler
|
// or if runtime exits before we hit the handler
|
||||||
signals := make(chan os.Signal, 2048)
|
signals := make(chan os.Signal, 2048)
|
||||||
|
@ -73,7 +74,7 @@ func start() error {
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := p.Close(); err != nil {
|
if err := p.Close(); err != nil {
|
||||||
logrus.Warn(err)
|
writeMessage(log, "warn", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := p.start(); err != nil {
|
if err := p.start(); err != nil {
|
||||||
|
@ -84,7 +85,7 @@ func start() error {
|
||||||
for {
|
for {
|
||||||
var msg, w, h int
|
var msg, w, h int
|
||||||
if _, err := fmt.Fscanf(control, "%d %d %d\n", &msg, &w, &h); err != nil {
|
if _, err := fmt.Fscanf(control, "%d %d %d\n", &msg, &w, &h); err != nil {
|
||||||
logrus.Warn(err)
|
continue
|
||||||
}
|
}
|
||||||
switch msg {
|
switch msg {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -108,21 +109,12 @@ func start() error {
|
||||||
for s := range signals {
|
for s := range signals {
|
||||||
switch s {
|
switch s {
|
||||||
case syscall.SIGCHLD:
|
case syscall.SIGCHLD:
|
||||||
exits, err := osutils.Reap()
|
exits, _ := osutils.Reap()
|
||||||
if err != nil {
|
|
||||||
logrus.Warn(err)
|
|
||||||
}
|
|
||||||
for _, e := range exits {
|
for _, e := range exits {
|
||||||
// check to see if runtime is one of the processes that has exited
|
// check to see if runtime is one of the processes that has exited
|
||||||
if e.Pid == p.pid() {
|
if e.Pid == p.pid() {
|
||||||
exitShim = true
|
exitShim = true
|
||||||
logrus.WithFields(logrus.Fields{
|
writeInt("exitStatus", e.Status)
|
||||||
"pid": e.Pid,
|
|
||||||
"status": e.Status,
|
|
||||||
}).Info("shim: runtime exited")
|
|
||||||
if err := writeInt("exitStatus", e.Status); err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{"status": e.Status}).Warn(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,41 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/containerd/runtime"
|
"github.com/docker/containerd/specs"
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errRuntime = errors.New("shim: runtime execution error")
|
var errRuntime = errors.New("shim: runtime execution error")
|
||||||
|
|
||||||
|
type checkpoint struct {
|
||||||
|
// Timestamp is the time that checkpoint happened
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Name is the name of the checkpoint
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Tcp checkpoints open tcp connections
|
||||||
|
Tcp bool `json:"tcp"`
|
||||||
|
// UnixSockets persists unix sockets in the checkpoint
|
||||||
|
UnixSockets bool `json:"unixSockets"`
|
||||||
|
// Shell persists tty sessions in the checkpoint
|
||||||
|
Shell bool `json:"shell"`
|
||||||
|
// Exit exits the container after the checkpoint is finished
|
||||||
|
Exit bool `json:"exit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type processState struct {
|
||||||
|
specs.ProcessSpec
|
||||||
|
Exec bool `json:"exec"`
|
||||||
|
Stdin string `json:"containerdStdin"`
|
||||||
|
Stdout string `json:"containerdStdout"`
|
||||||
|
Stderr string `json:"containerdStderr"`
|
||||||
|
RuntimeArgs []string `json:"runtimeArgs"`
|
||||||
|
NoPivotRoot bool `json:"noPivotRoot"`
|
||||||
|
Checkpoint string `json:"checkpoint"`
|
||||||
|
RootUID int `json:"rootUID"`
|
||||||
|
RootGID int `json:"rootGID"`
|
||||||
|
}
|
||||||
|
|
||||||
type process struct {
|
type process struct {
|
||||||
sync.WaitGroup
|
sync.WaitGroup
|
||||||
id string
|
id string
|
||||||
|
@ -26,12 +54,12 @@ type process struct {
|
||||||
stdio *stdio
|
stdio *stdio
|
||||||
exec bool
|
exec bool
|
||||||
containerPid int
|
containerPid int
|
||||||
checkpoint *runtime.Checkpoint
|
checkpoint *checkpoint
|
||||||
shimIO *IO
|
shimIO *IO
|
||||||
stdinCloser io.Closer
|
stdinCloser io.Closer
|
||||||
console libcontainer.Console
|
console *os.File
|
||||||
consolePath string
|
consolePath string
|
||||||
state *runtime.ProcessState
|
state *processState
|
||||||
runtime string
|
runtime string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,26 +87,26 @@ func newProcess(id, bundle, runtimeName string) (*process, error) {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadProcess() (*runtime.ProcessState, error) {
|
func loadProcess() (*processState, error) {
|
||||||
f, err := os.Open("process.json")
|
f, err := os.Open("process.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
var s runtime.ProcessState
|
var s processState
|
||||||
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadCheckpoint(bundle, name string) (*runtime.Checkpoint, error) {
|
func loadCheckpoint(bundle, name string) (*checkpoint, error) {
|
||||||
f, err := os.Open(filepath.Join(bundle, "checkpoints", name, "config.json"))
|
f, err := os.Open(filepath.Join(bundle, "checkpoints", name, "config.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
var cpt runtime.Checkpoint
|
var cpt checkpoint
|
||||||
if err := json.NewDecoder(f).Decode(&cpt); err != nil {
|
if err := json.NewDecoder(f).Decode(&cpt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -200,25 +228,25 @@ func (p *process) openIO() error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if p.state.Terminal {
|
if p.state.Terminal {
|
||||||
console, err := libcontainer.NewConsole(uid, gid)
|
master, console, err := newConsole(uid, gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.console = console
|
p.console = master
|
||||||
p.consolePath = console.Path()
|
p.consolePath = console
|
||||||
stdin, err := os.OpenFile(p.state.Stdin, syscall.O_RDONLY, 0)
|
stdin, err := os.OpenFile(p.state.Stdin, syscall.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go io.Copy(console, stdin)
|
go io.Copy(master, stdin)
|
||||||
stdout, err := os.OpenFile(p.state.Stdout, syscall.O_RDWR, 0)
|
stdout, err := os.OpenFile(p.state.Stdout, syscall.O_RDWR, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Add(1)
|
p.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(stdout, console)
|
io.Copy(stdout, master)
|
||||||
console.Close()
|
master.Close()
|
||||||
p.Done()
|
p.Done()
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,11 +2,7 @@
|
||||||
|
|
||||||
package osutils
|
package osutils
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exit is the wait4 information from an exited process
|
// Exit is the wait4 information from an exited process
|
||||||
type Exit struct {
|
type Exit struct {
|
||||||
|
@ -34,7 +30,18 @@ func Reap() (exits []Exit, err error) {
|
||||||
}
|
}
|
||||||
exits = append(exits, Exit{
|
exits = append(exits, Exit{
|
||||||
Pid: pid,
|
Pid: pid,
|
||||||
Status: utils.ExitStatus(ws),
|
Status: exitStatus(ws),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exitSignalOffset = 128
|
||||||
|
|
||||||
|
// exitStatus returns the correct exit status for a process based on if it
|
||||||
|
// was signaled or exited cleanly
|
||||||
|
func exitStatus(status syscall.WaitStatus) int {
|
||||||
|
if status.Signaled() {
|
||||||
|
return exitSignalOffset + int(status.Signal())
|
||||||
|
}
|
||||||
|
return status.ExitStatus()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue