Dawn's Blogs

分享技术 记录成长

0%

阅读dubbo-kubernetes的启发 (2) dubboctl客户端Client与Repository Runtime Template

Client

dubbo-kubernetes 的命令行工具 dubboctl 用于构建项目、打包成镜像、推送至 registry、部署 kubernetes 等。

而 dubboctl 的核心在于 Client,表示 dubboctl 客户端,执行 dubboctl 的各种操作。Client 定义在 app/dubboctl/internal/dubbo/client.go 文件中,其结构如下。其中,Client 包含了 builder、pusher、deployer 等组件,用于打包镜像、推送、部署工作。

1
2
3
4
5
6
7
8
9
10
type Client struct {
repositoriesPath string // path to repositories
repositoriesURI string // repo URI (overrides repositories path)
templates *Templates // Templates management
repositories *Repositories // Repositories management
builder Builder // Builds a runnable image source
pusher Pusher // Pushes function image to a remote
deployer Deployer // Deploys or Updates a function}
KubeCtl *kube.CtlClient // Kube Client
}

初始化函数

New 用于初始化一个 Client,该函数根据配置 options 配置 Client,并且初始化 repository 和 template 管理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// New client for function management.
func New(options ...Option) *Client {
// Instantiate client with static defaults.
c := &Client{}
for _, o := range options {
o(c)
}

// Initialize sub-managers using now-fully-initialized client.
c.repositories = newRepositories(c)
c.templates = newTemplates(c)

return c
}

dubboctl 使用的默认初始化函数

在 dubboctl 使用 Client 时,又封装了一个默认的初始化函数 NewClient,用于提供完整功能的 Client,包括配置:

  • 默认的本地 repository 地址,dubbo.WithRepositoriesPath。
  • Builder 镜像构造器,dubbo.WithBuilder。
  • Pusher 用于将镜像推送至 registry,dubbo.WithPusher。
  • Deployer 用于部署应用至 kubernetes,dubbo.WithDeployer。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func NewClient(options ...dubbo.Option) (*dubbo.Client, func()) {
var (
t = newTransport(false)
c = newCredentialsProvider(config.Dir(), t)
d = newDubboDeployer()
o = []dubbo.Option{
dubbo.WithRepositoriesPath(config.RepositoriesPath()),
dubbo.WithBuilder(pack.NewBuilder()),
dubbo.WithPusher(docker.NewPusher(
docker.WithCredentialsProvider(c),
docker.WithTransport(t))),
dubbo.WithDeployer(d),
}
)
// Client is constructed with standard options plus any additional options
// which either augment or override the defaults.
client := dubbo.New(append(o, options...)...)

cleanup := func() {}
return client, cleanup
}

三层结构

在 dubboctl 创建应用时,支持自定义应用模版。而在 dubboctl 中有三层结构的概念,分别是 repository,runtime,template。

其中,repository 指的是一个仓库(可以是本地的或是远程的 git 仓库),而 runtime 指的是使用的编程语言,template 为创建应用的具体模版。

如默认仓库 default 的结构如下,有两种 runtime 分别是 go 和 java,而每一种 runtime 中又指定了一个名为 common 的模版。

image-20230920175155155

Repository Runtime Template 之间都是一对多的关系,一个 Repository 下有多个 Runtime,一个 Runtime 下有多个 Template。

Repository

Repository 被定义为存放 template 的仓库,这个仓库可以是本地的,也可以是远程的。至于 repository 是本地的还是远程的,取决于 uri,根据 uri 构造不同类型的文件系统,如上一篇文章所述,dubbo-kubernetes 抽象出了多种不同的文件系统。

若 uri 以 file:// 为协议头,则表示这是一个本地仓库。

若 uri 为 git 远程地址,则表示这是一个远程 git repository。

在 repository 的根目录下可以保存一个 manifes.yaml,用于保存 repository 的配置信息 repositoryConfig,包括名字、版本号(这个目前没有用到)、template 存放地址(默认在仓库的根目录下)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Repository represents a collection of runtimes, each containing templates.
type Repository struct {
// Name of the repository
// This can be for instance:
// directory name on FS or last part of git URL or arbitrary value defined by the Template author.
Name string
// Runtimes containing Templates loaded from the repo
Runtimes []Runtime
fs filesystem.Filesystem
uri string // URI which was used when initially creating
}

type repositoryConfig struct {
// DefaultName is the name indicated by the repository author.
// Stored in the yaml attribute "name", it is only consulted during initial
// addition of the repo as the default option.
DefaultName string `yaml:"name,omitempty"`
// Version of the repository.
Version string `yaml:"version,omitempty"`
// TemplatesPath defines an optional path within the repository at which
// templates are stored. By default this is the repository root.
TemplatesPath string `yaml:"templates,omitempty"`
}

Client 不直接操作 Repository,而是通过 Repository 管理器 Repositories 操作仓库。

Runtime

Runtime 表示不同的语言,在 Repository 中维护了 Runtime 切片,在 Runtime 中维护了 Template 切片。

1
2
3
4
5
6
7
8
9
// Runtime is a division of templates within a repository of templates for a
// given runtime (source language plus environmentally available services
// and libraries)
type Runtime struct {
// Name of the runtime
Name string
// Templates defined for the runtime
Templates []Template
}

在 Repository 中,读取 repositoryConfig.TempatesPath(默认为 Repository 根目录)下的所有子文件夹,每一个子文件夹的名称就是一个 Runtime:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// repositoryRuntimes returns runtimes defined in this repository's filesystem.
// The views are denormalized, using the parent repository's values
// for inherited fields BuildConfig and HealthEndpoints as the default values
// for the runtimes and templates. The runtimes and templates themselves can
// override these values by specifying new values in thir config files.
func repositoryRuntimes(fs filesystem.Filesystem, repoName string, repoConfig repositoryConfig) (runtimes []Runtime, err error) {
runtimes = []Runtime{}

// Load runtimes
fis, err := fs.ReadDir(repoConfig.TemplatesPath)
if err != nil {
return
}
for _, fi := range fis {
// ignore files and hidden dirs
if !fi.IsDir() || strings.HasPrefix(fi.Name(), ".") {
continue
}
// Runtime, defaulted to values inherited from the repository
runtime := Runtime{
Name: fi.Name(),
}

// Runtime Templates
// Load from repo filesystem for runtime. Will inherit values from the
// runtime such as BuildConfig, HealthEndpoints etc.
runtime.Templates, err = runtimeTemplates(fs, repoConfig.TemplatesPath, repoName, runtime.Name)
if err != nil {
return
}
runtimes = append(runtimes, runtime)
}
return
}

Template

Template 表示某个应用模版,在创建应用时,最终的操作就是将模版复制到指定的文件夹下。template 维护了一个 filesystem.Filesystem,表示以当前模版为根目录的文件系统。

在创建模版时,遍历 repositoryConfig.TemplatesPath/RuntimeName 下的每一个文件夹,文件夹的名字作为模版名字,使用 subFS 作为模版文件系统的实现

1
2
3
4
5
6
7
// Template, defaulted to values inherited from the runtime
t := template{
name: fi.Name(),
repository: repoName,
runtime: runtimeName,
fs: filesystem.NewSubFS(path.Join(runtimePath, fi.Name()), fs),
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Template is a function project template.
// It can be used to instantiate new function project.
type Template interface {
// Name of this template.
Name() string
// Runtime for which this template applies.
Runtime() string
// Repository within which this template is contained. Value is set to the
// currently effective name of the repository, which may vary. It is user-
// defined when the repository is added, and can be set to "default" when
// the client is loaded in single repo mode. I.e. not canonical.
Repository() string
// Fullname is a calculated field of [repo]/[name] used
// to uniquely reference a template which may share a name
// with one in another repository.
Fullname() string
// Write updates fields of function f and writes project files to path pointed by f.Root.
Write(ctx context.Context, f *Dubbo) error
}

// template default implementation
type template struct {
name string
runtime string
repository string
fs filesystem.Filesystem
}

写入

在写入时,使用了 maskingFS,用于屏蔽当前模版下的 manifest 文件(虽然现在 manifest 没有什么用,并没有起到配置 template 的作用)。

1
2
3
4
5
6
7
8
func (t template) Write(ctx context.Context, f *Dubbo) error {
mask := func(p string) bool {
_, f := path.Split(p)
return f == templateManifest
}

return filesystem.CopyFromFS(".", f.Root, filesystem.NewMaskingFS(mask, t.fs)) // copy everything but manifest.yaml
}