github.com/jzwlqx/containerd@v0.2.5/containerd/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net"
     7  	"os"
     8  	"os/signal"
     9  	"runtime"
    10  	"strings"
    11  	"sync"
    12  	"syscall"
    13  	"time"
    14  
    15  	"google.golang.org/grpc"
    16  	"google.golang.org/grpc/health"
    17  	"google.golang.org/grpc/health/grpc_health_v1"
    18  
    19  	"github.com/Sirupsen/logrus"
    20  	"github.com/codegangsta/cli"
    21  	"github.com/cyberdelia/go-metrics-graphite"
    22  	"github.com/docker/containerd"
    23  	"github.com/docker/containerd/api/grpc/server"
    24  	"github.com/docker/containerd/api/grpc/types"
    25  	"github.com/docker/containerd/api/http/pprof"
    26  	"github.com/docker/containerd/supervisor"
    27  	"github.com/docker/docker/pkg/listeners"
    28  	"github.com/rcrowley/go-metrics"
    29  )
    30  
    31  const (
    32  	usage               = `High performance container daemon`
    33  	minRlimit           = 1024
    34  	defaultStateDir     = "/run/containerd"
    35  	defaultGRPCEndpoint = "unix:///run/containerd/containerd.sock"
    36  )
    37  
    38  var daemonFlags = []cli.Flag{
    39  	cli.BoolFlag{
    40  		Name:  "debug",
    41  		Usage: "enable debug output in the logs",
    42  	},
    43  	cli.StringFlag{
    44  		Name:  "state-dir",
    45  		Value: defaultStateDir,
    46  		Usage: "runtime state directory",
    47  	},
    48  	cli.DurationFlag{
    49  		Name:  "metrics-interval",
    50  		Value: 5 * time.Minute,
    51  		Usage: "interval for flushing metrics to the store",
    52  	},
    53  	cli.StringFlag{
    54  		Name:  "listen,l",
    55  		Value: defaultGRPCEndpoint,
    56  		Usage: "proto://address on which the GRPC API will listen",
    57  	},
    58  	cli.StringFlag{
    59  		Name:  "runtime,r",
    60  		Value: "runc",
    61  		Usage: "name or path of the OCI compliant runtime to use when executing containers",
    62  	},
    63  	cli.StringSliceFlag{
    64  		Name:  "runtime-args",
    65  		Value: &cli.StringSlice{},
    66  		Usage: "specify additional runtime args",
    67  	},
    68  	cli.StringFlag{
    69  		Name:  "shim",
    70  		Value: "containerd-shim",
    71  		Usage: "Name or path of shim",
    72  	},
    73  	cli.StringFlag{
    74  		Name:  "pprof-address",
    75  		Usage: "http address to listen for pprof events",
    76  	},
    77  	cli.DurationFlag{
    78  		Name:  "start-timeout",
    79  		Value: 15 * time.Second,
    80  		Usage: "timeout duration for waiting on a container to start before it is killed",
    81  	},
    82  	cli.IntFlag{
    83  		Name:  "retain-count",
    84  		Value: 500,
    85  		Usage: "number of past events to keep in the event log",
    86  	},
    87  	cli.StringFlag{
    88  		Name:  "graphite-address",
    89  		Usage: "Address of graphite server",
    90  	},
    91  }
    92  
    93  // DumpStacks dumps the runtime stack.
    94  func dumpStacks() {
    95  	var (
    96  		buf       []byte
    97  		stackSize int
    98  	)
    99  	bufferLen := 16384
   100  	for stackSize == len(buf) {
   101  		buf = make([]byte, bufferLen)
   102  		stackSize = runtime.Stack(buf, true)
   103  		bufferLen *= 2
   104  	}
   105  	buf = buf[:stackSize]
   106  	logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)
   107  }
   108  
   109  func setupDumpStacksTrap() {
   110  	c := make(chan os.Signal, 1)
   111  	signal.Notify(c, syscall.SIGUSR1)
   112  	go func() {
   113  		for range c {
   114  			dumpStacks()
   115  		}
   116  	}()
   117  }
   118  
   119  func main() {
   120  	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: time.RFC3339Nano})
   121  	app := cli.NewApp()
   122  	app.Name = "containerd"
   123  	if containerd.GitCommit != "" {
   124  		app.Version = fmt.Sprintf("%s commit: %s", containerd.Version, containerd.GitCommit)
   125  	} else {
   126  		app.Version = containerd.Version
   127  	}
   128  	app.Usage = usage
   129  	app.Flags = daemonFlags
   130  	app.Before = func(context *cli.Context) error {
   131  		setupDumpStacksTrap()
   132  		if context.GlobalBool("debug") {
   133  			logrus.SetLevel(logrus.DebugLevel)
   134  			if context.GlobalDuration("metrics-interval") > 0 {
   135  				if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil {
   136  					return err
   137  				}
   138  			}
   139  		}
   140  		if p := context.GlobalString("pprof-address"); len(p) > 0 {
   141  			pprof.Enable(p)
   142  		}
   143  		if err := checkLimits(); err != nil {
   144  			return err
   145  		}
   146  		return nil
   147  	}
   148  
   149  	app.Action = func(context *cli.Context) {
   150  		if err := daemon(context); err != nil {
   151  			logrus.Fatal(err)
   152  		}
   153  	}
   154  	if err := app.Run(os.Args); err != nil {
   155  		logrus.Fatal(err)
   156  	}
   157  }
   158  
   159  func daemon(context *cli.Context) error {
   160  	s := make(chan os.Signal, 2048)
   161  	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
   162  	sv, err := supervisor.New(
   163  		context.String("state-dir"),
   164  		context.String("runtime"),
   165  		context.String("shim"),
   166  		context.StringSlice("runtime-args"),
   167  		context.Duration("start-timeout"),
   168  		context.Int("retain-count"))
   169  	if err != nil {
   170  		return err
   171  	}
   172  	wg := &sync.WaitGroup{}
   173  	for i := 0; i < 10; i++ {
   174  		wg.Add(1)
   175  		w := supervisor.NewWorker(sv, wg)
   176  		go w.Start()
   177  	}
   178  	if err := sv.Start(); err != nil {
   179  		return err
   180  	}
   181  	// Split the listen string of the form proto://addr
   182  	listenSpec := context.String("listen")
   183  	listenParts := strings.SplitN(listenSpec, "://", 2)
   184  	if len(listenParts) != 2 {
   185  		return fmt.Errorf("bad listen address format %s, expected proto://address", listenSpec)
   186  	}
   187  	server, err := startServer(listenParts[0], listenParts[1], sv)
   188  	if err != nil {
   189  		return err
   190  	}
   191  	for ss := range s {
   192  		switch ss {
   193  		default:
   194  			logrus.Infof("stopping containerd after receiving %s", ss)
   195  			server.Stop()
   196  			os.Exit(0)
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  func startServer(protocol, address string, sv *supervisor.Supervisor) (*grpc.Server, error) {
   203  	// TODO: We should use TLS.
   204  	// TODO: Add an option for the SocketGroup.
   205  	sockets, err := listeners.Init(protocol, address, "", nil)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	if len(sockets) != 1 {
   210  		return nil, fmt.Errorf("incorrect number of listeners")
   211  	}
   212  	l := sockets[0]
   213  	s := grpc.NewServer()
   214  	types.RegisterAPIServer(s, server.NewServer(sv))
   215  	healthServer := health.NewHealthServer()
   216  	grpc_health_v1.RegisterHealthServer(s, healthServer)
   217  
   218  	go func() {
   219  		logrus.Debugf("containerd: grpc api on %s", address)
   220  		if err := s.Serve(l); err != nil {
   221  			logrus.WithField("error", err).Fatal("containerd: serve grpc")
   222  		}
   223  	}()
   224  	return s, nil
   225  }
   226  
   227  // getDefaultID returns the hostname for the instance host
   228  func getDefaultID() string {
   229  	hostname, err := os.Hostname()
   230  	if err != nil {
   231  		panic(err)
   232  	}
   233  	return hostname
   234  }
   235  
   236  func checkLimits() error {
   237  	var l syscall.Rlimit
   238  	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l); err != nil {
   239  		return err
   240  	}
   241  	if l.Cur <= minRlimit {
   242  		logrus.WithFields(logrus.Fields{
   243  			"current": l.Cur,
   244  			"max":     l.Max,
   245  		}).Warn("containerd: low RLIMIT_NOFILE changing to max")
   246  		l.Cur = l.Max
   247  		return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l)
   248  	}
   249  	return nil
   250  }
   251  
   252  func debugMetrics(interval time.Duration, graphiteAddr string) error {
   253  	for name, m := range supervisor.Metrics() {
   254  		if err := metrics.DefaultRegistry.Register(name, m); err != nil {
   255  			return err
   256  		}
   257  	}
   258  	processMetrics()
   259  	if graphiteAddr != "" {
   260  		addr, err := net.ResolveTCPAddr("tcp", graphiteAddr)
   261  		if err != nil {
   262  			return err
   263  		}
   264  		go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr)
   265  	} else {
   266  		l := log.New(os.Stdout, "[containerd] ", log.LstdFlags)
   267  		go metrics.Log(metrics.DefaultRegistry, interval, l)
   268  	}
   269  	return nil
   270  }