Docker基础之Daemon的创建

一、daemon的创建过程

本文介绍如何创建一个daemon的创建的过程,下面我将从Daemon结构体对象和创建新的Daemon的文件结构两个方面来介绍daemon的创建过程。

二、Daemon结构体对象

//Daemon基本信息
type Daemon struct {
	ID                        string  //daemon的编号
	repository                string
	containers                container.Store //容器
	execCommands              *exec.Store //可执行文件仓库
	referenceStore            reference.Store 
	downloadManager           *xfer.LayerDownloadManager
	uploadManager             *xfer.LayerUploadManager
	distributionMetadataStore dmetadata.Store //数据仓库
	trustKey                  libtrust.PrivateKey
	idIndex                   *truncindex.TruncIndex
	configStore               *Config //配置信息
	statsCollector            *statsCollector 
	defaultLogConfig          containertypes.LogConfig //日志信息
	RegistryService           registry.Service //注册机
	EventsService             *events.Events //事件服务器
	netController             libnetwork.NetworkController//网关
	volumes                   *store.VolumeStore
	discoveryWatcher          discoveryReloader
	root                      string //根目录
	seccompEnabled            bool
	shutdown                  bool //关机
	uidMaps                   []idtools.IDMap
	gidMaps                   []idtools.IDMap
	layerStore                layer.Store //层级仓库
	imageStore                image.Store //镜像仓库
	PluginStore               *plugin.Store //扩展插件仓库
	pluginManager             *plugin.Manager //插件管理
	nameIndex                 *registrar.Registrar //注册
	linkIndex                 *linkIndex //拉链表
	containerd                libcontainerd.Client //客户端lib容器
	containerdRemote          libcontainerd.Remote 
	defaultIsolation          containertypes.Isolation // 默认镜像的Windows
	clusterProvider           cluster.Provider
	cluster                   Cluster
	seccompProfile      []byte
	seccompProfilePath  string
}

三、创建新的Daemon

NewDaemon函数主要负责真正创建一个Daemon结构体对象,检查一些参数设置,同时负责创建各个目录作为docker的工作目录。部分目录文件及其负责的功能如下:
1、 /var/lib/docker/tmp 临时目录
2、 /var/lib/docker/containers 用来记录的是容器相关的信息,每运行一个容器,就在这个目录下面生成一个容器id对应的子目录
3、/var/lib/docker/image/${graphDriverName}/layerdb 记录镜像layer的元数据,每个层级都是一个目录,其ID称之为ChainID
4、/var/lib/docker/image/${graphDriverName}/imagedb 记录镜像元数据
5、/var/lib/docker/volumes/metadata.db 记录volume的元数据
6、/var/lib/docker/trust 用来放一些证书文件
7、/var/lib/docker/image/${graphDriverName}/distribution 这个目录用来记录layer元数据与镜像元数据之间的关联关系
8、/var/lib/docker/image/${graphDriverName}/repositories.json 是用来记录镜像仓库元数据,是imagedb 目录的索引

列出其中的关键核心结构体的创建过程,后面再一一进行分析。

  1. type layerStore struct,记录主机上面所有的layer层信息,包括只读layer和读写layer。其中涉及到graph driver ,主要用来管理容器文件系统及镜像存储的组件,与宿主机对各文件系统的支持相关。
  2. ImageStore,根据所有layer来构建image,维护所有image的元数据
  3. type VolumeStore struct,记录着所有的volumes,同时跟踪它们的使用情况
  4. type FSMetadataStore struct,使用filesystem来把 layer和image IDs关联起来
  5. reference store是一个tag store,负责管理镜像的tag
// NewDaemon函数为Daemon设置所有内容,以便能够为来自 Web 服务器的请求提供服务。
func NewDaemon(config *Config, registryService registry.Service, containerdRemote libcontainerd.Remote) (daemon *Daemon, err error) {
	//配置Docker容器的MTU,容器网络的最大传输单元
	setDefaultMtu(config)

	// Ensure that we have a correct root key limit for launching containers.
	if err := ModifyRootKeyLimit(); err != nil {
		logrus.Warnf("unable to modify root key limit, number of containers could be limited by this quota: %v", err)
	}
	/*
		/daemon/daemon_unix.go
			==>func verifyDaemonSettings
		检查参数设置是否有效
	*/
	if err := verifyDaemonSettings(config); err != nil {
		return nil, err
	}
	//判断我们是否开启网络服务
	config.DisableBridge = isBridgeNetworkDisabled(config)
	// 确定平台是否支持daemon
	if !platformSupported {
		return nil, errSystemNotSupported
	}
	// 验证特殊平台的要求
	if err := checkSystem(); err != nil {
		return nil, err
	}
	/*
		User namespaces的隔离
		将容器内的用户映射为宿主机上的普通用户
	*/
	uidMaps, gidMaps, err := setupRemappedRoot(config)
	if err != nil {
		return nil, err
	}
	/*rootUID, rootGID 的值:0 0 */
	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
	if err != nil {
		return nil, err
	}
	/*
		设置daemon的oom
	*/
	if err := setupDaemonProcess(config); err != nil {
		return nil, err
	}
	/*创建临时目录*/
	tmp, err := tempDir(config.Root, rootUID, rootGID)
	if err != nil {
		return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err)
	}
	realTmp, err := fileutils.ReadSymlinkedDirectory(tmp)
	if err != nil {
		return nil, fmt.Errorf("Unable to get the full path to the TempDir (%s): %s", tmp, err)
	}
	os.Setenv("TMPDIR", realTmp)
	/*
		创建一个type Daemon struct对象
	*/
	d := &Daemon{configStore: config}
	// 确保daemon在初始化期间出现故障时正确关闭
	defer func() {
		if err != nil {
			if err := d.Shutdown(); err != nil {
				logrus.Error(err)
			}
		}
	}()

	if err := d.setupSeccompProfile(); err != nil {
		return nil, err
	}
	// 设置默认的镜像模式
	if err := d.setDefaultIsolation(); err != nil {
		return nil, fmt.Errorf("error setting default isolation mode: %v", err)
	}
	logrus.Debugf("Using default logging driver %s", config.LogConfig.Type)
	/*go线程数量限制*/
	if err := configureMaxThreads(config); err != nil {
		logrus.Warnf("Failed to configure golang's threads limit: %v", err)
	}

	if err := ensureDefaultAppArmorProfile(); err != nil {
		logrus.Errorf(err.Error())
	}
	/*
		初始化与镜像存储相关的目录及Store
		  /var/lib/docker/containers 这个目录是用来记录的是容器相关的信息,每运行一个容器,就在这个目录下面生成一个容器Id对应的子目录
	*/
	daemonRepo := filepath.Join(config.Root, "containers")
	if err := idtools.MkdirAllAs(daemonRepo, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
		return nil, err
	}

	if runtime.GOOS == "windows" {
		if err := system.MkdirAll(filepath.Join(config.Root, "credentialspecs"), 0); err != nil && !os.IsExist(err) {
			return nil, err
		}
	}
	/*
		graph driver 是主要用来管理容器文件系统及镜像存储的组件,与宿主机对各文件系统的支持相关。
			比如 ubuntu 上默认使用的是 AUFS , Centos 上是 devicemapper , Coreos 上则是 btrfs 。
		graph driver 定义了一个统一的、抽象的接口,以一种可扩展的方式对各文件系统提供了支持。
	*/
	driverName := os.Getenv("DOCKER_DRIVER")
	if driverName == "" {
		driverName = config.GraphDriver
	}

	d.RegistryService = registryService
	d.PluginStore = plugin.NewStore(config.Root) // todo: remove
	// 插件系统初始化应该在恢复之前进行
	d.pluginManager, err = plugin.NewManager(plugin.ManagerConfig{
		Root:               filepath.Join(config.Root, "plugins"),
		ExecRoot:           "/run/docker/plugins", // possibly needs fixing
		Store:              d.PluginStore,
		Executor:           containerdRemote,
		RegistryService:    registryService,
		LiveRestoreEnabled: config.LiveRestoreEnabled,
		LogPluginEvent:     d.LogPluginEvent, // todo: make private
	})
	if err != nil {
		return nil, errors.Wrap(err, "couldn't create plugin manager")
	}
	/*
		layerStore:记录一个hots主机上面存储着的所有的layer层信息,包括只读layer和读写layer
		/var/lib/docker/image/${graphDriverName}/layerdb
		==>/layer/layer_store.go
			==>func NewStoreFromOptions
	*/
	d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
		StorePath:                 config.Root,
		MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
		GraphDriver:               driverName,
		GraphDriverOptions:        config.GraphOptions,
		UIDMaps:                   uidMaps,
		GIDMaps:                   gidMaps,
		PluginGetter:              d.PluginStore,
		ExperimentalEnabled:       config.Experimental,
	})
	if err != nil {
		return nil, err
	}
	graphDriver := d.layerStore.DriverName()
	imageRoot := filepath.Join(config.Root, "image", graphDriver)
	// 验证内核的安全支持模式
	if err := configureKernelSecuritySupport(config, graphDriver); err != nil {
		return nil, err
	}
	logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads)
	d.downloadManager = xfer.NewLayerDownloadManager(d.layerStore, *config.MaxConcurrentDownloads)
	logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads)
	d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads)
	/*
		/var/lib/docker/image/${graphDriverName}/imagedb 这个目录是用来记录镜像元数据
			==>/image/fs.go
	*/
	ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
	if err != nil {
		return nil, err
	}
	/*
		imageStore:根据所有layer来构建image,维护所有image的元数据
		根据StoreBackend ifs和 layerStore来创建一个imageStore
		==>/image/store.go
	*/
	d.imageStore, err = image.NewImageStore(ifs, d.layerStore)
	if err != nil {
		return nil, err
	}
	/*
		VolumeStore:记录着所有的volumes,同时跟踪它们的使用情况,相当于volume的一个cache
		创建/var/lib/docker/volumes/metadata.db,记录volume的元数据
		Volumes 是一种特殊的目录,其数据可以被一个或多个 container 共享,
		它和创建它的 container 的生命周期分离开来,
		在 container 被删去之后能继续存在。
	*/
	volStore, err := d.configureVolumes(rootUID, rootGID)
	if err != nil {
		return nil, err
	}

	trustKey, err := api.LoadOrCreateTrustKey(config.TrustKeyPath)
	if err != nil {
		return nil, err
	}
	/*
		/var/lib/docker/trust 这个目录用来放一些证书文件
	*/
	trustDir := filepath.Join(config.Root, "trust")

	if err := system.MkdirAll(trustDir, 0700); err != nil {
		return nil, err
	}
	/*
		type FSMetadataStore struct,使用filesystem来把 layer和image IDs关联起来
		/var/lib/docker/image/${graphDriverName}/distribution
			==>/distribution/metadata/metadata.go
	*/
	distributionMetadataStore, err := dmetadata.NewFSMetadataStore(filepath.Join(imageRoot, "distribution"))
	if err != nil {
		return nil, err
	}
	eventsService := events.New()
	/*
		reference store是一个tag store,负责管理镜像的tag
		/var/lib/docker/image/${graphDriverName}/repositories.json 是用来记录镜像仓库元数据,是imagedb 目录的索引
			==>/reference/store.go
	*/
	referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
	if err != nil {
		return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
	}
	/*
		Migrate和迁移旧Graph数据相关
	*/
	migrationStart := time.Now()
	if err := v1.Migrate(config.Root, graphDriver, d.layerStore, d.imageStore, referenceStore, distributionMetadataStore); err != nil {
		logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err)
	}
	logrus.Infof("Graph migration to content-addressability took %.2f seconds", time.Since(migrationStart).Seconds())

	// 仅当使用要播发的地址启动守护程序时,才会启用发现。 初始化时,守护程序已注册,我们可以将发现后端存储为其只读
	/*
		和对外发布相关
	*/
	if err := d.initDiscovery(config); err != nil {
		return nil, err
	}

	sysInfo := sysinfo.New(false)
	// 检查设备cgroup是否已挂载,这是Linux上容器安全性的硬要求。
	if runtime.GOOS == "linux" && !sysInfo.CgroupDevicesEnabled {
		return nil, fmt.Errorf("Devices cgroup isn't mounted")
	}

	d.ID = trustKey.PublicKey().KeyID()
	d.repository = daemonRepo
	d.containers = container.NewMemoryStore()
	d.execCommands = exec.NewStore()
	d.referenceStore = referenceStore
	d.distributionMetadataStore = distributionMetadataStore
	d.trustKey = trustKey
	d.idIndex = truncindex.NewTruncIndex([]string{})
	d.statsCollector = d.newStatsCollector(1 * time.Second)
	d.defaultLogConfig = containertypes.LogConfig{
		Type:   config.LogConfig.Type,
		Config: config.LogConfig.Config,
	}
	d.EventsService = eventsService
	d.volumes = volStore
	d.root = config.Root
	d.uidMaps = uidMaps
	d.gidMaps = gidMaps
	d.seccompEnabled = sysInfo.Seccomp

	d.nameIndex = registrar.NewRegistrar()
	d.linkIndex = newLinkIndex()
	d.containerdRemote = containerdRemote

	go d.execCommandGC()

	d.containerd, err = containerdRemote.Client(d)
	if err != nil {
		return nil, err
	}
	/*
		根据/var/lib/docker/containers目录里容器目录还原部分容器、初始化容器依赖的网络环境,初始化容器之间的link关系等
	*/
	if err := d.restore(); err != nil {
		return nil, err
	}
	info, _ := d.SystemInfo()
	engineVersion.WithValues(
		dockerversion.Version,
		dockerversion.GitCommit,
		info.Architecture,
		info.Driver,
		info.KernelVersion,
		info.OperatingSystem,
	).Set(1)
	engineCpus.Set(float64(info.NCPU))
	engineMemory.Set(float64(info.MemTotal))
	// 在类Unix系统上设置SIGUSR1处理程序,或在Windows上设置Win32全局事件以转储Go例程堆栈
	stackDumpDir := config.Root
	if execRoot := config.GetExecRoot(); execRoot != "" {
		stackDumpDir = execRoot
	}
	d.setupDumpStackTrap(stackDumpDir)
	return d, nil
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝域时空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值