native

native即用原生的linux文件系统比如ext4等完成snapshottor的工作,虽然在应用环境中很少使用,但对于实现自己的snapshottor有很好的借鉴意义,因为其细节比较简单,对于理解snapshottor的主要流程有很好的帮助。

我们来学习snapshottor主要放在prepare和commit两个方法上。

// 这里的key应该是chaninID
func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
    return o.createSnapshot(ctx, snapshots.KindActive, key, parent, opts)
}

func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) ([]mount.Mount, error) {
    var (
        err      error
        path, td string
    )

    if kind == snapshots.KindActive || parent == "" {
        // 如果是一个active的snapshot或者是no parent需要搞一个目录来存储这一层的内容(相当于moby的)RW层
        td, err = ioutil.TempDir(filepath.Join(o.root, "snapshots"), "new-")
        if err != nil {
            return nil, errors.Wrap(err, "failed to create temp dir")
        }
        if err := os.Chmod(td, 0755); err != nil {
            return nil, errors.Wrapf(err, "failed to chmod %s to 0755", td)
        }
        defer func() {
            if err != nil {
                if td != "" {
                    if err1 := os.RemoveAll(td); err1 != nil {
                        err = errors.Wrapf(err, "remove failed: %v", err1)
                    }
                }
                if path != "" {
                    if err1 := os.RemoveAll(path); err1 != nil {
                        err = errors.Wrapf(err, "failed to remove path: %v", err1)
                    }
                }
            }
        }()
    }

    // MetaStore开启一个事务
    ctx, t, err := o.ms.TransactionContext(ctx, true)
    if err != nil {
        return nil, err
    }
    //完成存储层元数据的管理
    s, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
    if err != nil {
        if rerr := t.Rollback(); rerr != nil {
            log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
        }
        return nil, errors.Wrap(err, "failed to create snapshot")
    }

    // 如果是需要创建新的目录的话
    if td != "" {
        // layer have parent
        if len(s.ParentIDs) > 0 {
            // 直接拿直系parent的即可,不支持多层的parent没有意义
            parent := o.getSnapshotDir(s.ParentIDs[0])
            // 因为native filesystem没有提供COW的能力,因此必须全量拷贝parent的目录
            if err := fs.CopyDir(td, parent); err != nil {
                return nil, errors.Wrap(err, "copying of parent failed")
            }
        }
        // s.ID应该每个snapshot唯一,不能冲突
        path = o.getSnapshotDir(s.ID)
        // 重命名临时目录,因此path对于snapshot应该是唯一的,和s.ID一一对应
        if err := os.Rename(td, path); err != nil {
            if rerr := t.Rollback(); rerr != nil {
                log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
            }
            return nil, errors.Wrap(err, "failed to rename")
        }
        td = ""
    }

    if err := t.Commit(); err != nil {
        return nil, errors.Wrap(err, "commit failed")
    }

    return o.mounts(s), nil
}

// 其实就是在bolt的元数据的存储中登记一下
func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
    var (
        roFlag string
        source string
    )

    // 区分是否可读写
    if s.Kind == snapshots.KindView {
        roFlag = "ro"
    } else {
        roFlag = "rw"
    }

    if len(s.ParentIDs) == 0 || s.Kind == snapshots.KindActive {
        source = o.getSnapshotDir(s.ID)
    } else {
        // 只读的话直接拿parent的即可
        source = o.getSnapshotDir(s.ParentIDs[0])
    }

    return []mount.Mount{
        {
            Source: source,
            // 联合挂载,可在本机上实验一下
            Type:   "bind",
            Options: []string{
                roFlag,
                // rbind表示会把原来目录下的挂载点也会一起挂载过去
                "rbind",
            },
        },
    }
}

// 在bolt元数据存储中标记一下,表示snapshot为commited状态
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
    ctx, t, err := o.ms.TransactionContext(ctx, true)
    if err != nil {
        return err
    }

    id, _, _, err := storage.GetInfo(ctx, key)
    if err != nil {
        return err
    }

    usage, err := fs.DiskUsage(ctx, o.getSnapshotDir(id))
    if err != nil {
        return err
    }

    if _, err := storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
        if rerr := t.Rollback(); rerr != nil {
            log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
        }
        return errors.Wrap(err, "failed to commit snapshot")
    }
    return t.Commit()
}

results matching ""

    No results matching ""