github.com/demonoid81/containerd@v1.3.4/cmd/containerd/command/main.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package command 18 19 import ( 20 gocontext "context" 21 "fmt" 22 "io/ioutil" 23 "net" 24 "os" 25 "os/signal" 26 "path/filepath" 27 "runtime" 28 "time" 29 30 "github.com/containerd/containerd/errdefs" 31 "github.com/containerd/containerd/log" 32 "github.com/containerd/containerd/mount" 33 "github.com/containerd/containerd/services/server" 34 srvconfig "github.com/containerd/containerd/services/server/config" 35 "github.com/containerd/containerd/sys" 36 "github.com/containerd/containerd/version" 37 "github.com/pkg/errors" 38 "github.com/sirupsen/logrus" 39 "github.com/urfave/cli" 40 "google.golang.org/grpc/grpclog" 41 ) 42 43 const usage = ` 44 __ _ __ 45 _________ ____ / /_____ _(_)___ ___ _________/ / 46 / ___/ __ \/ __ \/ __/ __ ` + "`" + `/ / __ \/ _ \/ ___/ __ / 47 / /__/ /_/ / / / / /_/ /_/ / / / / / __/ / / /_/ / 48 \___/\____/_/ /_/\__/\__,_/_/_/ /_/\___/_/ \__,_/ 49 50 high performance container runtime 51 ` 52 53 func init() { 54 logrus.SetFormatter(&logrus.TextFormatter{ 55 TimestampFormat: log.RFC3339NanoFixed, 56 FullTimestamp: true, 57 }) 58 59 // Discard grpc logs so that they don't mess with our stdio 60 grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard)) 61 62 cli.VersionPrinter = func(c *cli.Context) { 63 fmt.Println(c.App.Name, version.Package, c.App.Version, version.Revision) 64 } 65 } 66 67 // App returns a *cli.App instance. 68 func App() *cli.App { 69 app := cli.NewApp() 70 app.Name = "containerd" 71 app.Version = version.Version 72 app.Usage = usage 73 app.Description = ` 74 containerd is a high performance container runtime whose daemon can be started 75 by using this command. If none of the *config*, *publish*, or *help* commands 76 are specified, the default action of the **containerd** command is to start the 77 containerd daemon in the foreground. 78 79 80 A default configuration is used if no TOML configuration is specified or located 81 at the default file location. The *containerd config* command can be used to 82 generate the default configuration for containerd. The output of that command 83 can be used and modified as necessary as a custom configuration.` 84 app.Flags = []cli.Flag{ 85 cli.StringFlag{ 86 Name: "config,c", 87 Usage: "path to the configuration file", 88 Value: defaultConfigPath, 89 }, 90 cli.StringFlag{ 91 Name: "log-level,l", 92 Usage: "set the logging level [trace, debug, info, warn, error, fatal, panic]", 93 }, 94 cli.StringFlag{ 95 Name: "address,a", 96 Usage: "address for containerd's GRPC server", 97 }, 98 cli.StringFlag{ 99 Name: "root", 100 Usage: "containerd root directory", 101 }, 102 cli.StringFlag{ 103 Name: "state", 104 Usage: "containerd state directory", 105 }, 106 } 107 app.Flags = append(app.Flags, serviceFlags()...) 108 app.Commands = []cli.Command{ 109 configCommand, 110 publishCommand, 111 ociHook, 112 } 113 app.Action = func(context *cli.Context) error { 114 var ( 115 start = time.Now() 116 signals = make(chan os.Signal, 2048) 117 serverC = make(chan *server.Server, 1) 118 ctx = gocontext.Background() 119 config = defaultConfig() 120 ) 121 122 if err := srvconfig.LoadConfig(context.GlobalString("config"), config); err != nil && !os.IsNotExist(err) { 123 return err 124 } 125 126 // Apply flags to the config 127 if err := applyFlags(context, config); err != nil { 128 return err 129 } 130 131 // Make sure top-level directories are created early. 132 if err := server.CreateTopLevelDirectories(config); err != nil { 133 return err 134 } 135 136 // Stop if we are registering or unregistering against Windows SCM. 137 stop, err := registerUnregisterService(config.Root) 138 if err != nil { 139 logrus.Fatal(err) 140 } 141 if stop { 142 return nil 143 } 144 145 done := handleSignals(ctx, signals, serverC) 146 // start the signal handler as soon as we can to make sure that 147 // we don't miss any signals during boot 148 signal.Notify(signals, handledSignals...) 149 150 // cleanup temp mounts 151 if err := mount.SetTempMountLocation(filepath.Join(config.Root, "tmpmounts")); err != nil { 152 return errors.Wrap(err, "creating temp mount location") 153 } 154 // unmount all temp mounts on boot for the server 155 warnings, err := mount.CleanupTempMounts(0) 156 if err != nil { 157 log.G(ctx).WithError(err).Error("unmounting temp mounts") 158 } 159 for _, w := range warnings { 160 log.G(ctx).WithError(w).Warn("cleanup temp mount") 161 } 162 163 if config.GRPC.Address == "" { 164 return errors.Wrap(errdefs.ErrInvalidArgument, "grpc address cannot be empty") 165 } 166 if config.TTRPC.Address == "" { 167 // If TTRPC was not explicitly configured, use defaults based on GRPC. 168 config.TTRPC.Address = fmt.Sprintf("%s.ttrpc", config.GRPC.Address) 169 config.TTRPC.UID = config.GRPC.UID 170 config.TTRPC.GID = config.GRPC.GID 171 } 172 log.G(ctx).WithFields(logrus.Fields{ 173 "version": version.Version, 174 "revision": version.Revision, 175 }).Info("starting containerd") 176 177 server, err := server.New(ctx, config) 178 if err != nil { 179 return err 180 } 181 182 // Launch as a Windows Service if necessary 183 if err := launchService(server, done); err != nil { 184 logrus.Fatal(err) 185 } 186 187 serverC <- server 188 189 if config.Debug.Address != "" { 190 var l net.Listener 191 if filepath.IsAbs(config.Debug.Address) { 192 if l, err = sys.GetLocalListener(config.Debug.Address, config.Debug.UID, config.Debug.GID); err != nil { 193 return errors.Wrapf(err, "failed to get listener for debug endpoint") 194 } 195 } else { 196 if l, err = net.Listen("tcp", config.Debug.Address); err != nil { 197 return errors.Wrapf(err, "failed to get listener for debug endpoint") 198 } 199 } 200 serve(ctx, l, server.ServeDebug) 201 } 202 if config.Metrics.Address != "" { 203 l, err := net.Listen("tcp", config.Metrics.Address) 204 if err != nil { 205 return errors.Wrapf(err, "failed to get listener for metrics endpoint") 206 } 207 serve(ctx, l, server.ServeMetrics) 208 } 209 // setup the ttrpc endpoint 210 tl, err := sys.GetLocalListener(config.TTRPC.Address, config.TTRPC.UID, config.TTRPC.GID) 211 if err != nil { 212 return errors.Wrapf(err, "failed to get listener for main ttrpc endpoint") 213 } 214 serve(ctx, tl, server.ServeTTRPC) 215 216 if config.GRPC.TCPAddress != "" { 217 l, err := net.Listen("tcp", config.GRPC.TCPAddress) 218 if err != nil { 219 return errors.Wrapf(err, "failed to get listener for TCP grpc endpoint") 220 } 221 serve(ctx, l, server.ServeTCP) 222 } 223 // setup the main grpc endpoint 224 l, err := sys.GetLocalListener(config.GRPC.Address, config.GRPC.UID, config.GRPC.GID) 225 if err != nil { 226 return errors.Wrapf(err, "failed to get listener for main endpoint") 227 } 228 serve(ctx, l, server.ServeGRPC) 229 230 if err := notifyReady(ctx); err != nil { 231 log.G(ctx).WithError(err).Warn("notify ready failed") 232 } 233 234 log.G(ctx).Infof("containerd successfully booted in %fs", time.Since(start).Seconds()) 235 <-done 236 return nil 237 } 238 return app 239 } 240 241 func serve(ctx gocontext.Context, l net.Listener, serveFunc func(net.Listener) error) { 242 path := l.Addr().String() 243 log.G(ctx).WithField("address", path).Info("serving...") 244 go func() { 245 defer l.Close() 246 if err := serveFunc(l); err != nil { 247 log.G(ctx).WithError(err).WithField("address", path).Fatal("serve failure") 248 } 249 }() 250 } 251 252 func applyFlags(context *cli.Context, config *srvconfig.Config) error { 253 // the order for config vs flag values is that flags will always override 254 // the config values if they are set 255 if err := setLevel(context, config); err != nil { 256 return err 257 } 258 for _, v := range []struct { 259 name string 260 d *string 261 }{ 262 { 263 name: "root", 264 d: &config.Root, 265 }, 266 { 267 name: "state", 268 d: &config.State, 269 }, 270 { 271 name: "address", 272 d: &config.GRPC.Address, 273 }, 274 } { 275 if s := context.GlobalString(v.name); s != "" { 276 *v.d = s 277 } 278 } 279 280 applyPlatformFlags(context) 281 282 return nil 283 } 284 285 func setLevel(context *cli.Context, config *srvconfig.Config) error { 286 l := context.GlobalString("log-level") 287 if l == "" { 288 l = config.Debug.Level 289 } 290 if l != "" { 291 lvl, err := log.ParseLevel(l) 292 if err != nil { 293 return err 294 } 295 logrus.SetLevel(lvl) 296 } 297 return nil 298 } 299 300 func dumpStacks(writeToFile bool) { 301 var ( 302 buf []byte 303 stackSize int 304 ) 305 bufferLen := 16384 306 for stackSize == len(buf) { 307 buf = make([]byte, bufferLen) 308 stackSize = runtime.Stack(buf, true) 309 bufferLen *= 2 310 } 311 buf = buf[:stackSize] 312 logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 313 314 if writeToFile { 315 // Also write to file to aid gathering diagnostics 316 name := filepath.Join(os.TempDir(), fmt.Sprintf("containerd.%d.stacks.log", os.Getpid())) 317 f, err := os.Create(name) 318 if err != nil { 319 return 320 } 321 defer f.Close() 322 f.WriteString(string(buf)) 323 logrus.Infof("goroutine stack dump written to %s", name) 324 } 325 }