Add config file

This adds a config file for containerd configuration.  It is hard to
have structure data on cli flags and the config file should be used for
the majority of fields when configuring containerd.

There are still a few flags on the daemon that override config file
values but flags should take a back seat going forward and should be
kept at a minimum.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2017-02-21 11:07:54 -08:00
parent da8f4bb904
commit a9950aedcf
16 changed files with 3374 additions and 41 deletions

54
cmd/containerd/config.go Normal file
View file

@ -0,0 +1,54 @@
package main
import "github.com/BurntSushi/toml"
func defaultConfig() *config {
return &config{
Root: "/var/lib/containerd",
State: "/run/containerd",
GRPC: grpcConfig{
Socket: "/run/containerd/containerd.sock",
},
Debug: debug{
Level: "info",
Socket: "/run/containerd/debug.sock",
},
}
}
// loadConfig loads the config from the provided path
func loadConfig(path string) error {
_, err := toml.DecodeFile(path, conf)
if err != nil {
return err
}
return nil
}
// config specifies the containerd configuration file in the TOML format.
// It contains fields to configure various subsystems and containerd as a whole.
type config struct {
// State is the path to a directory where containerd will store runtime state
State string `toml:"state"`
// Root is the path to a directory where containerd will store persistent data
Root string `toml:"root"`
// GRPC configuration settings
GRPC grpcConfig `toml:"grpc"`
// Debug and profiling settings
Debug debug `toml:"debug"`
// Metrics and monitoring settings
Metrics metricsConfig `toml:"metrics"`
}
type grpcConfig struct {
Socket string `toml:"socket"`
}
type debug struct {
Socket string `toml:"socket"`
Level string `toml:"level"`
}
type metricsConfig struct {
Address string `toml:"address"`
}

View file

@ -35,7 +35,10 @@ const usage = `
high performance container runtime
`
var global = log.WithModule(gocontext.Background(), "containerd")
var (
conf = defaultConfig()
global = log.WithModule(gocontext.Background(), "containerd")
)
func main() {
app := cli.NewApp()
@ -44,29 +47,25 @@ func main() {
app.Usage = usage
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "log-level",
Usage: "Set the logging level [debug, info, warn, error, fatal, panic]",
Value: "info",
Name: "config,c",
Usage: "path to the configuration file",
Value: "/etc/containerd/config.toml",
},
cli.StringFlag{
Name: "root",
Name: "log-level,l",
Usage: "set the logging level [debug, info, warn, error, fatal, panic]",
},
cli.StringFlag{
Name: "root,r",
Usage: "containerd root directory",
},
cli.StringFlag{
Name: "state",
Usage: "containerd state directory",
Value: "/run/containerd",
},
cli.StringFlag{
Name: "socket, s",
Name: "socket,s",
Usage: "socket path for containerd's GRPC server",
Value: "/run/containerd/containerd.sock",
},
cli.StringFlag{
Name: "debug-socket, d",
Usage: "socket path for containerd's debug server",
Value: "/run/containerd/containerd-debug.sock",
},
cli.StringFlag{
Name: "metrics-address, m",
Usage: "tcp address to serve metrics on",
Value: "127.0.0.1:7897",
},
}
app.Before = before
@ -78,7 +77,7 @@ func main() {
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT, syscall.SIGUSR1)
log.G(global).Info("starting containerd boot...")
runtimes, err := loadRuntimes(context)
runtimes, err := loadRuntimes()
if err != nil {
return err
}
@ -87,13 +86,13 @@ func main() {
return err
}
// start debug and metrics APIs
if err := serveDebugAPI(context); err != nil {
if err := serveDebugAPI(); err != nil {
return err
}
serveMetricsAPI(context)
serveMetricsAPI()
// start the GRPC api with the execution service registered
server := newGRPCServer(execution.New(supervisor))
if err := serveGRPC(context, server); err != nil {
if err := serveGRPC(server); err != nil {
return err
}
log.G(global).Infof("containerd successfully booted in %fs", time.Now().Sub(start).Seconds())
@ -106,23 +105,60 @@ func main() {
}
func before(context *cli.Context) error {
if l := context.GlobalString("log-level"); l != "" {
if err := loadConfig(context.GlobalString("config")); err != nil &&
!os.IsNotExist(err) {
return err
}
// the order for config vs flag values is that flags will always override
// the config values if they are set
if err := setLevel(context); err != nil {
return err
}
for _, v := range []struct {
name string
d *string
}{
{
name: "root",
d: &conf.Root,
},
{
name: "state",
d: &conf.State,
},
{
name: "socket",
d: &conf.GRPC.Socket,
},
} {
if s := context.GlobalString(v.name); s != "" {
*v.d = s
}
}
return nil
}
func setLevel(context *cli.Context) error {
l := context.GlobalString("log-level")
if l == "" {
l = conf.Debug.Level
}
if l != "" {
lvl, err := logrus.ParseLevel(l)
if err != nil {
lvl = logrus.InfoLevel
fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n, and being defaulted to info", l)
return err
}
logrus.SetLevel(lvl)
}
return nil
}
func serveMetricsAPI(context *cli.Context) {
if addr := context.GlobalString("metrics-address"); addr != "" {
log.G(global).WithField("metrics", addr).Info("starting metrics API...")
func serveMetricsAPI() {
if conf.Metrics.Address != "" {
log.G(global).WithField("metrics", conf.Metrics.Address).Info("starting metrics API...")
h := newMetricsHandler()
go func() {
if err := http.ListenAndServe(addr, h); err != nil {
if err := http.ListenAndServe(conf.Metrics.Address, h); err != nil {
log.G(global).WithError(err).Fatal("serve metrics API")
}
}()
@ -135,10 +171,10 @@ func newMetricsHandler() http.Handler {
return m
}
func serveDebugAPI(context *cli.Context) error {
path := context.GlobalString("debug-socket")
func serveDebugAPI() error {
path := conf.Debug.Socket
if path == "" {
return errors.New("--debug-socket path cannot be empty")
return errors.New("debug socket path cannot be empty")
}
l, err := utils.CreateUnixSocket(path)
if err != nil {
@ -156,13 +192,10 @@ func serveDebugAPI(context *cli.Context) error {
return nil
}
func loadRuntimes(context *cli.Context) (map[string]containerd.Runtime, error) {
var (
root = context.GlobalString("root")
o = make(map[string]containerd.Runtime)
)
func loadRuntimes() (map[string]containerd.Runtime, error) {
o := make(map[string]containerd.Runtime)
for _, name := range containerd.Runtimes() {
r, err := containerd.NewRuntime(name, root)
r, err := containerd.NewRuntime(name, conf.State)
if err != nil {
return nil, err
}
@ -178,8 +211,8 @@ func newGRPCServer(service api.ContainerServiceServer) *grpc.Server {
return s
}
func serveGRPC(context *cli.Context, server *grpc.Server) error {
path := context.GlobalString("socket")
func serveGRPC(server *grpc.Server) error {
path := conf.GRPC.Socket
if path == "" {
return errors.New("--socket path cannot be empty")
}

View file

@ -23,7 +23,7 @@ var pprofCommand = cli.Command{
cli.StringFlag{
Name: "debug-socket, d",
Usage: "socket path for containerd's debug server",
Value: "/run/containerd/containerd-debug.sock",
Value: "/run/containerd/debug.sock",
},
},
Subcommands: []cli.Command{