github.com/ehazlett/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 }