Create server package for containerd daemon

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby
2017-06-21 17:14:28 -07:00
parent c4da4ed393
commit a6e77432df
12 changed files with 398 additions and 325 deletions

86
server/config.go Normal file
View File

@@ -0,0 +1,86 @@
package server
import (
"bytes"
"io"
"github.com/BurntSushi/toml"
)
// Config provides containerd configuration data for the server
type Config struct {
// 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"`
// Snapshotter specifies which snapshot driver to use
Snapshotter string `toml:"snapshotter"`
// Differ specifies which differ to use. Differ is tightly coupled with the snapshotter
// so not all combinations may work.
Differ string `toml:"differ"`
// Plugins provides plugin specific configuration for the initialization of a plugin
Plugins map[string]toml.Primitive `toml:"plugins"`
// Enable containerd as a subreaper
Subreaper bool `toml:"subreaper"`
// OOMScore adjust the containerd's oom score
OOMScore int `toml:"oom_score"`
md toml.MetaData
}
type GRPCConfig struct {
Address string `toml:"address"`
Uid int `toml:"uid"`
Gid int `toml:"gid"`
}
type Debug struct {
Address string `toml:"address"`
Uid int `toml:"uid"`
Gid int `toml:"gid"`
Level string `toml:"level"`
}
type MetricsConfig struct {
Address string `toml:"address"`
}
// Decode unmarshals a plugin specific configuration by plugin id
func (c *Config) Decode(id string, v interface{}) (interface{}, error) {
data, ok := c.Plugins[id]
if !ok {
return v, nil
}
if err := c.md.PrimitiveDecode(data, v); err != nil {
return nil, err
}
return v, nil
}
// WriteTo marshals the config to the provided writer
func (c *Config) WriteTo(w io.Writer) (int64, error) {
buf := bytes.NewBuffer(nil)
e := toml.NewEncoder(buf)
if err := e.Encode(c); err != nil {
return 0, err
}
return io.Copy(w, buf)
}
// LoadConfig loads the containerd server config from the provided path
func LoadConfig(path string, v *Config) error {
if v == nil {
v = &Config{}
}
md, err := toml.DecodeFile(path, v)
if err != nil {
return err
}
v.md = md
return nil
}

175
server/server.go Normal file
View File

@@ -0,0 +1,175 @@
package server
import (
"errors"
"expvar"
"net"
"net/http"
"net/http/pprof"
"os"
containers "github.com/containerd/containerd/api/services/containers/v1"
content "github.com/containerd/containerd/api/services/content/v1"
diff "github.com/containerd/containerd/api/services/diff/v1"
images "github.com/containerd/containerd/api/services/images/v1"
namespaces "github.com/containerd/containerd/api/services/namespaces/v1"
snapshot "github.com/containerd/containerd/api/services/snapshot/v1"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
version "github.com/containerd/containerd/api/services/version/v1"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
metrics "github.com/docker/go-metrics"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/health/grpc_health_v1"
)
// New creates and initializes a new containerd server
func New(ctx context.Context, config *Config, plugins []*plugin.Registration) (*Server, error) {
if config.Root == "" {
return nil, errors.New("root must be specified")
}
if err := os.MkdirAll(config.Root, 0700); err != nil {
return nil, err
}
rpc := grpc.NewServer(
grpc.UnaryInterceptor(interceptor),
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
)
var (
services []plugin.Service
s = &Server{
rpc: rpc,
emitter: events.NewEmitter(),
}
initialized = make(map[plugin.PluginType][]interface{})
)
for _, p := range plugins {
id := p.URI()
if !shouldLoadPlugin(p, config) {
log.G(ctx).WithField("type", p.Type).Infof("skip loading plugin %q...", id)
continue
}
log.G(ctx).WithField("type", p.Type).Infof("loading plugin %q...", id)
initContext := plugin.NewContext(
events.WithPoster(ctx, s.emitter),
initialized,
config.Root,
id,
)
initContext.Emitter = s.emitter
// load the plugin specific configuration if it is provided
if p.Config != nil {
pluginConfig, err := config.Decode(p.ID, p.Config)
if err != nil {
return nil, err
}
initContext.Config = pluginConfig
}
instance, err := p.Init(initContext)
if err != nil {
return nil, err
}
initialized[p.Type] = append(initialized[p.Type], instance)
// check for grpc services that should be registered with the server
if service, ok := instance.(plugin.Service); ok {
services = append(services, service)
}
}
// register services after all plugins have been initialized
for _, service := range services {
if err := service.Register(rpc); err != nil {
return nil, err
}
}
return s, nil
}
// Server is the containerd main daemon
type Server struct {
rpc *grpc.Server
emitter *events.Emitter
}
// ServeGRPC provides the containerd grpc APIs on the provided listener
func (s *Server) ServeGRPC(l net.Listener) error {
// before we start serving the grpc API regster the grpc_prometheus metrics
// handler. This needs to be the last service registered so that it can collect
// metrics for every other service
grpc_prometheus.Register(s.rpc)
return s.rpc.Serve(l)
}
// ServeMetrics provides a prometheus endpoint for exposing metrics
func (s *Server) ServeMetrics(l net.Listener) error {
m := http.NewServeMux()
m.Handle("/metrics", metrics.Handler())
return http.Serve(l, m)
}
// ServeDebug provides a debug endpoint
func (s *Server) ServeDebug(l net.Listener) error {
// don't use the default http server mux to make sure nothing gets registered
// that we don't want to expose via containerd
m := http.NewServeMux()
m.Handle("/debug/vars", expvar.Handler())
m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
return http.Serve(l, m)
}
// Stop gracefully stops the containerd server
func (s *Server) Stop() {
s.rpc.GracefulStop()
}
func shouldLoadPlugin(p *plugin.Registration, config *Config) bool {
switch p.Type {
case plugin.SnapshotPlugin:
return p.URI() == config.Snapshotter
case plugin.DiffPlugin:
return p.URI() == config.Differ
default:
return true
}
}
func interceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
ctx = log.WithModule(ctx, "containerd")
switch info.Server.(type) {
case tasks.TasksServer:
ctx = log.WithModule(ctx, "execution")
case containers.ContainersServer:
ctx = log.WithModule(ctx, "containers")
case content.ContentServer:
ctx = log.WithModule(ctx, "content")
case images.ImagesServer:
ctx = log.WithModule(ctx, "images")
case grpc_health_v1.HealthServer:
// No need to change the context
case version.VersionServer:
ctx = log.WithModule(ctx, "version")
case snapshot.SnapshotsServer:
ctx = log.WithModule(ctx, "snapshot")
case diff.DiffServer:
ctx = log.WithModule(ctx, "diff")
case namespaces.NamespacesServer:
ctx = log.WithModule(ctx, "namespaces")
default:
log.G(ctx).Warnf("unknown GRPC server type: %#v\n", info.Server)
}
return grpc_prometheus.UnaryServerInterceptor(ctx, req, info, handler)
}