容器是特殊的进程,runc则是他们的直接管理工具,contanerd则是提供对runc的管理。contanerd通过shim(垫片代理)屏蔽底层runtime实现(如runc),管理本机所有的runc和他的容器进程实例。一个容器进程实例对应一个task。
contanerd 可以完成镜像下载unpack到容器启动的整个过程,并管理容器的整个生命周期。容器的元数据实际会存储在boltdb(golang版本地kv db)中。contanerd完成容器的信息存储和状态存储,是容器的虚拟世界管理映射。
Container为容器的数据结构标识,可被存储在boltdb中,task是执行容器的运行时对象,相当于类和对象的关系。task实际会调用绑定的runtime,Containerd默认采用runc作为runtime,其他容器运行时如(runtime)也可以通过对接shim接口来接入Containerd。
runc相当于一个命令行工具通过命令将container启动起来就不管了,containerd则是一个守护进程。
shim service负责直接和container接触,代理其IO并接收退出信号,提供统一运行接口(启动 删除 运行),在shim service 之上会有一个统一管理进程来对各个shim或者说各个task实例进行管理。
Shims实现了对bundle目录的rootfs系统的挂载,卸载,containerd的文件系统是由snapshoter管理的,containerd 为不同文件系统提供管理如ext4 overlay, 每种不同的文件系统对应各自的实现的Snapshotter, 用户可以在配置文件中指定Snapshotter。
containerd 中的service 都是通过plugin加grpc方式组织管理,实际就是将service封装成plugin形式,然后调用统一接口启动封装成plugin的所有服务。
// shim 启动入口 v2 "github.com/containerd/containerd/runtime/v2/runc/v2"
// v2 是一个实际的runc runtime shim service 管理runc的生命周期,提供实际的api 如创建 container 在容器中启动进程等
func main() {
shim.Run("io.containerd.runc.v2", v2.New)
}
// containerd/runtime/v2/shim/shim.go
// 初始化并启动shim server
func Run(id string, initFunc Init, opts ...BinaryOpts) {
run(id, initFunc, config)
}
type Init func(context.Context, string, Publisher, func()) (Shim, error)
func run(id string, initFunc Init, config Config) error {
service, err := initFunc(ctx, idFlag, publisher, cancel)
}
// containerd/client.go Client为客户调用containerd接口,为taskService,
// diffService等提供一个统一的接口,通过grpc方式来调用。
type Client struct {
services
connMu sync.Mutex
conn *grpc.ClientConn
runtime string
defaultns string
platform platforms.MatchComparer
connector func() (*grpc.ClientConn, error)
}
// 创建client
client, err := containerd.New("/run/containerd/containerd.sock")
// 实际通过unix socket来连接后端的grpc服务
func DialAddress(address string) string {
return fmt.Sprintf("unix://%s", address)
}
// 通过init.go initial进程调用runc创建容器(调用runc cmd来实现)
func NewContainer(ctx context.Context, platform stdio.Platform, r *task.CreateTaskRequest) (_ *Container, retErr error) {
p, err := newInit(
ctx,
r.Bundle,
filepath.Join(r.Bundle, "work"),
ns,
platform,
config,
&opts,
rootfs,
)
p.Create(ctx, config)
}
func (p *Init) Create(ctx context.Context, r *CreateConfig) error {
// runtime为*runc.Runc, 实际是调用runc命令创建container
p.runtime.Create(ctx, r.ID, r.Bundle, opts)
}