docker containerd简要分析(v0.2.5)

由于最新分支在命令方面做了改变,与我研究的docker engine分支(1.13.0)不匹配,决定下个老点的分支来研究.具体方法是直接git下源码,然后:

root@os:/opt/goHome/workspace/src/github.com/docker/containerd_bak# git tag
0.0.2
0.0.3
0.0.4
0.0.5
v0.1.0
v0.2.0
v0.2.1
v0.2.2
v0.2.3
v0.2.4
v0.2.5
root@os:/opt/goHome/workspace/src/github.com/docker/containerd_bak# git reset --hard v0.2.5
HEAD is now at 2a5e70c Use golang.org/x/net/context for now

好了回到我们的正题.docker engine是通过gRPC与containerd通信的.网上摘了段gRPC的定义:
gRPC是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。

还有一个golang使用gRPC的例子(不管message,直接看服务端代码和客户端代码,因为我们是要研究结果代码):

服务端代码

package main

import (
    "log"
    "net"

    pb "your_path_to_gen_pb_dir/helloworld"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)

const (
    port = ":50051"
)

// server is used to implement helloworld.GreeterServer.
type server struct{}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    s.Serve(lis)
}

客户端代码

package main

import (
    "log"
    "os"

    pb "your_path_to_gen_pb_dir/helloworld"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)

const (
    address     = "localhost:50051"
    defaultName = "world"
)

func main() {
    // Set up a connection to the server.
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}

然后我们参照上面服务端代码例子的代码研究containerd.
入口函数在docker\containerd\containerd\main.go

func main() {
    logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: time.RFC3339Nano})
    app := cli.NewApp()
    app.Name = "containerd"
    if containerd.GitCommit != "" {
        app.Version = fmt.Sprintf("%s commit: %s", containerd.Version, containerd.GitCommit)
    } else {
        app.Version = containerd.Version
    }
    app.Usage = usage
    app.Flags = daemonFlags
    app.Before = func(context *cli.Context) error {
        setupDumpStacksTrap()
        if context.GlobalBool("debug") {
            logrus.SetLevel(logrus.DebugLevel)
            if context.GlobalDuration("metrics-interval") > 0 {
                if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil {
                    return err
                }
            }
        }
        if p := context.GlobalString("pprof-address"); len(p) > 0 {
            pprof.Enable(p)
        }
        if err := checkLimits(); err != nil {
            return err
        }
        return nil
    }
        //最终会用来执行命令
    app.Action = func(context *cli.Context) {
        if err := daemon(context); err != nil {
            logrus.Fatal(err)
        }
    }
    if err := app.Run(os.Args); err != nil {
        logrus.Fatal(err)
    }
}

可以看到最后执行Run函数,Run函数里面主要做的是执行Before,解析参数到context里面等工作,最后执行app.Action,进而执行daemon:

func daemon(context *cli.Context) error {

    s := make(chan os.Signal, 2048)
    signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
    // Supervisor represents a container supervisor
    //容器管理器
    sv, err := supervisor.New(
        context.String("state-dir"),
        context.String("runtime"),
        context.String("shim"),
        context.StringSlice("runtime-args"),
        context.Duration("start-timeout"),
        context.Int("retain-count"))
    if err != nil {
        return err
    }
    wg := &sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        //专门用于启动容器的工作队列
        w := supervisor.NewWorker(sv, wg)
        go w.Start()
    }
    //开启内部消息处理loop
    if err := sv.Start(); err != nil {
        return err
    }
    // Split the listen string of the form proto://addr
    listenSpec := context.String("listen")
    listenParts := strings.SplitN(listenSpec, "://", 2)
    if len(listenParts) != 2 {
        return fmt.Errorf("bad listen address format %s, expected proto://address", listenSpec)
    }
    //包含启动GRPC服务
    server, err := startServer(listenParts[0], listenParts[1], sv)
    if err != nil {
        return err
    }
    for ss := range s {
        switch ss {
        //收到Ctr+C等中断信息,其他都不反应
        default:
            logrus.Infof("stopping containerd after receiving %s", ss)
            server.Stop()
            os.Exit(0)
        }
    }
    return nil
}

在deamon中主要干了三件事:
第一:开启容器启动工作队列

w := supervisor.NewWorker(sv, wg)

第二:开启内部消息loop

    if err := sv.Start(); err != nil {
        return err
    }

第三:启用gRPC服务

server, err := startServer(listenParts[0], listenParts[1], sv)

我们先了解第三件事具体做了什么:

func startServer(protocol, address string, sv *supervisor.Supervisor) (*grpc.Server, error) {
    // TODO: We should use TLS.
    // TODO: Add an option for the SocketGroup.
    sockets, err := listeners.Init(protocol, address, "", nil)
    if err != nil {
        return nil, err
    }
    if len(sockets) != 1 {
        return nil, fmt.Errorf("incorrect number of listeners")
    }
    l := sockets[0]
    s := grpc.NewServer()
    //注册gRPC服务
    types.RegisterAPIServer(s, server.NewServer(sv))
    healthServer := health.NewHealthServer()
    grpc_health_v1.RegisterHealthServer(s, healthServer)

    go func() {
        logrus.Debugf("containerd: grpc api on %s", address)
        if err := s.Serve(l); err != nil {
            logrus.WithField("error", err).Fatal("containerd: serve grpc")
        }
    }()
    return s, nil
}

参照gRPC服务端的例子,我们需要三件东西:
1.监听对象lis:

   lis, err := net.Listen("tcp", port)

2.gRPC服务对象s:

   s := grpc.NewServer()

3.封装的服务函数对象&server{}:

    pb.RegisterGreeterServer(s, &server{})
    s.Serve(lis)

这三个我们在startServer函数中都能发现,分别是:

    l := sockets[0]
    s := grpc.NewServer()
    //注册gRPC服务
    types.RegisterAPIServer(s, server.NewServer(sv))

l和s都是一样的,不同只有封装的服务函数对象server.NewServer(sv):

// NewServer returns grpc server instance
func NewServer(sv *supervisor.Supervisor) types.APIServer {
    return &apiServer{
        //初始化在docker\containerd\containerd\main.go的main函数
        sv: sv,
    }
}

可以看到实际返回的是apiServer,是一些API的封装,docker engine可以方便得调用:
这里写图片描述
后面我们分析docker run启动容器命令的时候,将详细分析其中一些接口.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值