containerd学习-2

源码简要分析(镜像的创建,容器的创建,容器的启动)
接下来的目录都是相对于github.com/containerd/containerd的路径

程序入口
cmd/containerd/main.go中

func main() {
    app := command.App()
    ...
}

cmd/containerd/command/main.go中

func App() *cli.App {
...
server, err := server.New(ctx, config)
...
}

初始化服务
services/server/server.go中

func New(ctx context.Context, config *srvconfig.Config) (*Server, error) {
...
plugins, err := LoadPlugins(ctx, config)
...
if service, ok := instance.(plugin.Service); ok {
  services = append(services, service)
}
...
 for _, service := range services {
if err := service.Register(rpc); err != nil {
return nil, err
}
}
}

加载插件
services/server/server,go中

func LoadPlugins(ctx context.Context, config *srvconfig.Config) ([]*plugin.Registration, error) {
    plugin.Register(&plugin.Registration{
        Type: plugin.MetadataPlugin,
        ID:   "bolt",
        Requires: []plugin.Type{
            plugin.ContentPlugin,
            plugin.SnapshotPlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            if err := os.MkdirAll(ic.Root, 0711); err != nil {
                return nil, err
            }
            cs, err := ic.Get(plugin.ContentPlugin)
            if err != nil {
                return nil, err
            }

            snapshottersRaw, err := ic.GetByType(plugin.SnapshotPlugin)
            if err != nil {
                return nil, err
            }

            snapshotters := make(map[string]snapshots.Snapshotter)
            for name, sn := range snapshottersRaw {
                sn, err := sn.Instance()
                if err != nil {
                    if !plugin.IsSkipPlugin(err) {
                        log.G(ic.Context).WithError(err).
                            Warnf("could not use snapshotter %v in metadata plugin", name)
                    }
                    continue
                }
                snapshotters[name] = sn.(snapshots.Snapshotter)
            }

            path := filepath.Join(ic.Root, "meta.db")
            ic.Meta.Exports["path"] = path

            db, err := bolt.Open(path, 0644, nil)
            if err != nil {
                return nil, err
            }
            mdb := metadata.NewDB(db, cs.(content.Store), snapshotters)
            if err := mdb.Init(ic.Context); err != nil {
                return nil, err
            }
            return mdb, nil
        },
    })
  ...
return plugin.Graph(config.DisabledPlugins), nil
}

创建镜像入口
services/images/service.go中

func init(){
    plugin.Register(&plugin.Registration{
        Type: plugin.GRPCPlugin,
        ID:   "images",
        Requires: []plugin.Type{
            plugin.ServicePlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            plugins, err := ic.GetByType(plugin.ServicePlugin)
            if err != nil {
                return nil, err
            }
            p, ok := plugins[services.ImagesService]
            if !ok {
                return nil, errors.New("images service not found")
            }
            i, err := p.Instance()
            if err != nil {
                return nil, err
            }
            return &service{local: i.(imagesapi.ImagesClient)}, nil
        },
    })
}
...
func (s *service) Register(server *grpc.Server) error {
    imagesapi.RegisterImagesServer(server, s)
    return nil
}
...
func (s *service) Create(ctx context.Context, req *imagesapi.CreateImageRequest) (*imagesapi.CreateImageResponse, error) {
    return s.local.Create(ctx, req)
}
...

创建镜像实际入口
service/images/local.go中

func init() {
    plugin.Register(&plugin.Registration{
        Type: plugin.ServicePlugin,
        ID:   services.ImagesService,
        Requires: []plugin.Type{
            plugin.MetadataPlugin,
            plugin.GCPlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            m, err := ic.Get(plugin.MetadataPlugin)
            if err != nil {
                return nil, err
            }
            g, err := ic.Get(plugin.GCPlugin)
            if err != nil {
                return nil, err
            }

            return &local{
                store:     metadata.NewImageStore(m.(*metadata.DB)),
                publisher: ic.Events,
                gc:        g.(gcScheduler),
            }, nil
        },
    })
}
...

func (l *local) Create(ctx context.Context, req *imagesapi.CreateImageRequest, _ ...grpc.CallOption) (*imagesapi.CreateImageResponse, error) {
    log.G(ctx).WithField("name", req.Image.Name).WithField("target", req.Image.Target.Digest).Debugf("create image")
    if req.Image.Name == "" {
        return nil, status.Errorf(codes.InvalidArgument, "Image.Name required")
    }

    var (
        image = imageFromProto(&req.Image)
        resp  imagesapi.CreateImageResponse
    )
    created, err := l.store.Create(ctx, image)
    if err != nil {
        return nil, errdefs.ToGRPC(err)
    }

    resp.Image = imageToProto(&created)

    if err := l.publisher.Publish(ctx, "/images/create", &eventstypes.ImageCreate{
        Name:   resp.Image.Name,
        Labels: resp.Image.Labels,
    }); err != nil {
        return nil, err
    }

    return &resp, nil

}
...

创建容器入口
services/containers/service.go

func init() {
    plugin.Register(&plugin.Registration{
        Type: plugin.GRPCPlugin,
        ID:   "containers",
        Requires: []plugin.Type{
            plugin.ServicePlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            plugins, err := ic.GetByType(plugin.ServicePlugin)
            if err != nil {
                return nil, err
            }
            p, ok := plugins[services.ContainersService]
            if !ok {
                return nil, errors.New("containers service not found")
            }
            i, err := p.Instance()
            if err != nil {
                return nil, err
            }
            return &service{local: i.(api.ContainersClient)}, nil
        },
    })
}
...
func (s *service) Register(server *grpc.Server) error {
    api.RegisterContainersServer(server, s)
    return nil
}
...
func (s *service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error) {
    return s.local.Create(ctx, req)
}
...

创建容器实际入口

services/containers/local.go

func init() {
    plugin.Register(&plugin.Registration{
        Type: plugin.ServicePlugin,
        ID:   services.ContainersService,
        Requires: []plugin.Type{
            plugin.MetadataPlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            m, err := ic.Get(plugin.MetadataPlugin)
            if err != nil {
                return nil, err
            }
            return &local{
                db:        m.(*metadata.DB),
                publisher: ic.Events,
            }, nil
        },
    })
}

func (l *local) Create(ctx context.Context, req *api.CreateContainerRequest, _ ...grpc.CallOption) (*api.CreateContainerResponse, error) {
    var resp api.CreateContainerResponse

    if err := l.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
        container := containerFromProto(&req.Container)

        created, err := store.Create(ctx, container)
        if err != nil {
            return err
        }

        resp.Container = containerToProto(&created)

        return nil
    }); err != nil {
        return &resp, errdefs.ToGRPC(err)
    }
    if err := l.publisher.Publish(ctx, "/containers/create", &eventstypes.ContainerCreate{
        ID:    resp.Container.ID,
        Image: resp.Container.Image,
        Runtime: &eventstypes.ContainerCreate_Runtime{
            Name:    resp.Container.Runtime.Name,
            Options: resp.Container.Runtime.Options,
        },
    }); err != nil {
        return &resp, err
    }

    return &resp, nil
}

启动容器创建和启动任务入口(实际客户端在启动容器是先调用create然后是start)
services/tasks/service.go

func init() {
    plugin.Register(&plugin.Registration{
        Type: plugin.GRPCPlugin,
        ID:   "tasks",
        Requires: []plugin.Type{
            plugin.ServicePlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            plugins, err := ic.GetByType(plugin.ServicePlugin)
            if err != nil {
                return nil, err
            }
            p, ok := plugins[services.TasksService]
            if !ok {
                return nil, errors.New("tasks service not found")
            }
            i, err := p.Instance()
            if err != nil {
                return nil, err
            }
            return &service{local: i.(api.TasksClient)}, nil
        },
    })
}

func (s *service) Register(server *grpc.Server) error {
    api.RegisterTasksServer(server, s)
    return nil
}

func (s *service) Start(ctx context.Context, r *api.StartRequest) (*api.StartResponse, error) {
    return s.local.Start(ctx, r)
}
func (s *service) Create(ctx context.Context, r *api.CreateTaskRequest) (*api.CreateTaskResponse, error) {
    return s.local.Create(ctx, r)
}

启动容器实际入口
service/tasks/local.go

func init() {
    plugin.Register(&plugin.Registration{
        Type:     plugin.ServicePlugin,
        ID:       services.TasksService,
        Requires: tasksServiceRequires,
        InitFn:   initFunc,
    })
}
func initFunc(ic *plugin.InitContext) (interface{}, error) {
...
选取v2runtime分析
v2r, err := ic.Get(plugin.RuntimePluginV2)
...
}



func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.CallOption) (*api.CreateTaskResponse, error) {
    container, err := l.getContainer(ctx, r.ContainerID)
    if err != nil {
        return nil, errdefs.ToGRPC(err)
    }
    rtime, err := l.getRuntime(container.Runtime.Name)
    if err != nil {
        return nil, err
    }
    if _, err := rtime.Get(ctx, r.ContainerID); err != runtime.ErrTaskNotExists {
        return nil, errdefs.ToGRPC(fmt.Errorf("task %s already exists", r.ContainerID))
    }
    c, err := rtime.Create(ctx, r.ContainerID, opts)
}

func (l *local) Start(ctx context.Context, r *api.StartRequest, _ ...grpc.CallOption) (*api.StartResponse, error) {
    t, err := l.getTask(ctx, r.ContainerID)
    if err != nil {
        return nil, err
    }
    p := runtime.Process(t)
    if r.ExecID != "" {
        if p, err = t.Process(ctx, r.ExecID); err != nil {
            return nil, errdefs.ToGRPC(err)
        }
    }
        启动容器
    if err := p.Start(ctx); err != nil {
        return nil, errdefs.ToGRPC(err)
    }
}

func (l *local) getTask(ctx context.Context, id string) (runtime.Task, error) {
    container, err := l.getContainer(ctx, id)
    if err != nil {
        return nil, err
    }
    return l.getTaskFromContainer(ctx, container)
}

func (l *local) getTaskFromContainer(ctx context.Context, container *containers.Container) (runtime.Task, error) {
    runtime, err := l.getRuntime(container.Runtime.Name)
    if err != nil {
        return nil, errdefs.ToGRPCf(err, "runtime for task %s", container.Runtime.Name)
    }
    t, err := runtime.Get(ctx, container.ID)
    if err != nil {
        return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID)
    }
    return t, nil
}

func (l *local) getRuntime(name string) (runtime.PlatformRuntime, error) {
    runtime, ok := l.runtimes[name]
    if !ok {
        // one runtime to rule them all
        return l.v2Runtime, nil
    }
    return runtime, nil
}

v2runtime
runtime/v2/manager.go中

func init() {
    plugin.Register(&plugin.Registration{
        Type: plugin.RuntimePluginV2,
        ID:   "task",
        Requires: []plugin.Type{
            plugin.MetadataPlugin,
        },
        InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            ic.Meta.Platforms = supportedPlatforms()
            if err := os.MkdirAll(ic.Root, 0711); err != nil {
                return nil, err
            }
            if err := os.MkdirAll(ic.State, 0711); err != nil {
                return nil, err
            }
            m, err := ic.Get(plugin.MetadataPlugin)
            if err != nil {
                return nil, err
            }
            return New(ic.Context, ic.Root, ic.State, ic.Address, ic.Events, m.(*metadata.DB))
        },
    })
}

func (m *TaskManager) Create(ctx context.Context, id string, opts runtime.CreateOpts) (_ runtime.Task, err error) {
    
    b := shimBinary(ctx, bundle, opts.Runtime, m.containerdAddress, m.events, m.tasks)
      启动一个shim进程
    shim, err := b.Start(ctx)


发送创建任务请求给shim进程
    t, err := shim.Create(ctx, opts)
    if err != nil {
        return nil, err
    }
    m.tasks.Add(ctx, t)
    return t, nil
}

runtime/v2/binary.go中

func (b *binary) Start(ctx context.Context) (_ *shim, err error) {
...
    cmd, err := client.Command(ctx,
        b.runtime,
        b.containerdAddress,
        bundlePath,
        "-id", b.bundle.ID,
        "-bundle", b.bundle.Path,
        "delete")
...
out, err := cmd.CombinedOutput()
address := strings.TrimSpace(string(out))
conn, err := client.Connect(address, client.AnonDialer)
    return &shim{
        bundle:  b.bundle,
        client:  client,
        task:    task.NewTaskClient(client),
        events:  b.events,
        rtTasks: b.rtTasks,
    }, nil
}
...

runtime/v2/shim.go中

func (s *shim) Create(ctx context.Context, opts runtime.CreateOpts) (runtime.Task, error) {
request := &task.ExecProcessRequest{
        ID:       s.ID(),
        ExecID:   id,
        Stdin:    opts.IO.Stdin,
        Stdout:   opts.IO.Stdout,
        Stderr:   opts.IO.Stderr,
        Terminal: opts.IO.Terminal,
        Spec:     opts.Spec,
    }
    response, err := s.task.Create(ctx, request)
}
func (s *shim) Start(ctx context.Context) error {
    response, err := s.task.Start(ctx, &task.StartRequest{
        ID: s.ID(),
    })
}

推荐阅读更多精彩内容