溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

KubeVela是如何將appfile轉換為K8s特定資源對象的

發(fā)布時間:2021-12-16 09:49:30 來源:億速云 閱讀:169 作者:柒染 欄目:云計算

這期內容當中小編將會給大家?guī)碛嘘PKubeVela是如何將appfile轉換為K8s特定資源對象的,文章內容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

KubeVela 是一個簡單易用又高度可擴展的云原生應用管理引擎,是基于 Kubernetes 及阿里云與微軟云共同發(fā)布的云原生應用開發(fā)模型 OAM 構建。

KubeVela 基于 OAM 模型構建了一套具體的實現(xiàn),通過 Golang 編寫,可以端到端地為用戶構建云原生應用的平臺,提供一個相對完整的解決方案。

下面主要目的是探索 KubeVela 如何將一個 appfile 文件轉換為 K8s 中特定的資源對象。

該過程總的來說分為兩個階段:

  1. appfile 轉為 K8s 中的 application

  2. application 轉換為對應的 K8s 資源對象

# vela.yaml
name: test
services:
  nginx:
    type: webservice
    image: nginx
    env:
    - name: NAME
      value: kubevela

    # svc trait
    svc:
      type: NodePort
      ports:
      - port: 80
        nodePort: 32017

利用 vela up 命令可以完成部署。

vela up 命令

建議:在看 vela 命令行工具代碼之前,先去簡單了解一下 cobra 框架。

// references/cli/up.go
// NewUpCommand will create command for applying an AppFile
func NewUpCommand(c types.Args, ioStream cmdutil.IOStreams) *cobra.Command {
  cmd := &cobra.Command{
    Use:                   "up",
    DisableFlagsInUseLine: true,
    Short:                 "Apply an appfile",
    Long:                  "Apply an appfile",
    Annotations: map[string]string{
      types.TagCommandType: types.TypeStart,
    },
    PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
      return c.SetConfig()
    },
    RunE: func(cmd *cobra.Command, args []string) error {
      velaEnv, err := GetEnv(cmd)
      if err != nil {
        return err
      }
      kubecli, err := c.GetClient()
      if err != nil {
        return err
      }

      o := &common.AppfileOptions{
        Kubecli: kubecli,
        IO:      ioStream,
        Env:     velaEnv,
      }
      filePath, err := cmd.Flags().GetString(appFilePath)
      if err != nil {
        return err
      }
      return o.Run(filePath, velaEnv.Namespace, c)
    },
  }
  cmd.SetOut(ioStream.Out)

  cmd.Flags().StringP(appFilePath, "f", "", "specify file path for appfile")
  return cmd
}

上面源碼展示的是 vela up 命令的入口。

在 PresistentPreRunE 函數(shù)中,通過調用 c.SetConfig() 完成 Kuberentes 配置信息 kubeconfig 的注入。

在 RunE 函數(shù)中:

  • 首先,獲取 vela 的 env 變量,velaEnv.Namespace 對應 Kubernetes 的命名空間。

  • 其次,獲取 Kubernetes 的客戶端,kubectl。

  • 接著,利用 Kubernetes 客戶端和 vleaEnv 來構建渲染 Appfile 需要的 AppfileOptions。

  • 最后,調用 o.Run(filePath, velaEnv.Namespace, c)。

    • filePath: appfile 的路徑

    • velaEnv.Namespace:對應 K8s 的 namespace

    • c:K8s 客戶端

    • 該函數(shù)需要三個參數(shù),其中 filePath 用于指定 appfile 的位置,velaEnv.Namespace 和 c 用來將渲染后的 Application 創(chuàng)建到指定命名空間。

如何將一個 appfile 轉為 Kubernetes 中的 Application

  • 起點:appfile

  • 終點:applicatioin

  • 路徑:appfile -> application (services -> component)

    • comp[workload, traits]

1. 起點:AppFile

// references/appfile/api/appfile.go
// AppFile defines the spec of KubeVela Appfile
type AppFile struct {
  Name       string             `json:"name"`
  CreateTime time.Time          `json:"createTime,omitempty"`
  UpdateTime time.Time          `json:"updateTime,omitempty"`
  Services   map[string]Service `json:"services"`
  Secrets    map[string]string  `json:"secrets,omitempty"`

  configGetter config.Store
  initialized  bool
}

// NewAppFile init an empty AppFile struct
func NewAppFile() *AppFile {
  return &AppFile{
    Services:     make(map[string]Service),
    Secrets:      make(map[string]string),
    configGetter: &config.Local{},
  }
}
// references/appfile/api/service.go
// Service defines the service spec for AppFile, it will contain all related information including OAM component, traits, source to image, etc...
type Service map[string]interface{}

上面兩段代碼是 AppFile 在客戶端的聲明,vela 會將指定路徑的 yaml 文件讀取后,賦值給一個 AppFile。

// references/appfile/api/appfile.go
// LoadFromFile will read the file and load the AppFile struct
func LoadFromFile(filename string) (*AppFile, error) {
  b, err := ioutil.ReadFile(filepath.Clean(filename))
  if err != nil {
    return nil, err
  }
  af := NewAppFile()
  // Add JSON format appfile support
  ext := filepath.Ext(filename)
  switch ext {
  case ".yaml", ".yml":
    err = yaml.Unmarshal(b, af)
  case ".json":
    af, err = JSONToYaml(b, af)
  default:
    if json.Valid(b) {
      af, err = JSONToYaml(b, af)
    } else {
      err = yaml.Unmarshal(b, af)
    }
  }
  if err != nil {
    return nil, err
  }
  return af, nil
}

下面為讀取 vela.yaml 文件后,加載到 AppFile 中的數(shù)據(jù):

# vela.yaml
name: test
services:
  nginx:
    type: webservice
    image: nginx
    env:
    - name: NAME
      value: kubevela

    # svc trait
    svc:
      type: NodePort
      ports:
      - port: 80
        nodePort: 32017
Name: test
CreateTime: 0001-01-01 00:00:00 +0000 UTC
UpdateTime: 0001-01-01 00:00:00 +0000 UTC
Services: map[
             nginx: map[
               env: [map[name: NAME value: kubevela]] 
               image: nginx 
               svc: map[ports: [map[nodePort: 32017 port: 80]] type: NodePort] 
               type: webservice
            ]
          ]
Secrets    map[]
configGetter: 0x447abd0 
initialized: false

2. 終點:application

// apis/core.oam.dev/application_types.go
type Application struct {
  metav1.TypeMeta   `json:",inline"`
  metav1.ObjectMeta `json:"metadata,omitempty"`

  Spec   ApplicationSpec `json:"spec,omitempty"`
  Status AppStatus       `json:"status,omitempty"`
}

// ApplicationSpec is the spec of Application
type ApplicationSpec struct {
  Components []ApplicationComponent `json:"components"`

  // TODO(wonderflow): we should have application level scopes supported here

  // RolloutPlan is the details on how to rollout the resources
  // The controller simply replace the old resources with the new one if there is no rollout plan involved
  // +optional
  RolloutPlan *v1alpha1.RolloutPlan `json:"rolloutPlan,omitempty"`
}

上面代碼,為 Application 的聲明,結合 .vela/deploy.yaml(見下面代碼),可以看出,要將一個 AppFile 渲染為 Application 主要就是將 AppFile 的 Services 轉化為 Application 的 Components。

# .vela/deploy.yaml
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
  creationTimestamp: null
  name: test
  namespace: default
spec:
  components:
  - name: nginx
    scopes:
      healthscopes.core.oam.dev: test-default-health
    settings:
      env:
      - name: NAME
        value: kubevela
      image: nginx
    traits:
    - name: svc
      properties:
        ports:
        - nodePort: 32017
          port: 80
        type: NodePort
    type: webservice
status: {}

3. 路徑:Services -> Components

結合以上內容可以看出,將 Appfile 轉化為 Application 主要是將 Services 渲染為 Components。

// references/appfile/api/appfile.go
// BuildOAMApplication renders Appfile into Application, Scopes and other K8s Resources.
func (app *AppFile) BuildOAMApplication(env *types.EnvMeta, io cmdutil.IOStreams, tm template.Manager, silence bool) (*v1alpha2.Application, []oam.Object, error) {
  ...
  servApp := new(v1alpha2.Application)
  servApp.SetNamespace(env.Namespace)
  servApp.SetName(app.Name)
  servApp.Spec.Components = []v1alpha2.ApplicationComponent{}
  for serviceName, svc := range app.GetServices() {
    ...
    // 完成 Service 到 Component 的轉化
    comp, err := svc.RenderServiceToApplicationComponent(tm, serviceName)
    if err != nil {
      return nil, nil, err
    }
    servApp.Spec.Components = append(servApp.Spec.Components, comp)
  }
  servApp.SetGroupVersionKind(v1alpha2.SchemeGroupVersion.WithKind("Application"))
  auxiliaryObjects = append(auxiliaryObjects, addDefaultHealthScopeToApplication(servApp))
  return servApp, auxiliaryObjects, nil
}

上面的代碼是 vela 將 Appfile 轉化為 Application 代碼實現(xiàn)的位置。其中 comp, err := svc.RenderServiceToApplicationComponent(tm, serviceName) 完成 Service 到 Component 的轉化。

// references/appfile/api/service.go
// RenderServiceToApplicationComponent render all capabilities of a service to CUE values to KubeVela Application.
func (s Service) RenderServiceToApplicationComponent(tm template.Manager, serviceName string) (v1alpha2.ApplicationComponent, error) {

  // sort out configs by workload/trait
  workloadKeys := map[string]interface{}{}
  var traits []v1alpha2.ApplicationTrait

  wtype := s.GetType()
  comp := v1alpha2.ApplicationComponent{
    Name:         serviceName,
    WorkloadType: wtype,
  }

  for k, v := range s.GetApplicationConfig() {
    // 判斷是否為 trait
    if tm.IsTrait(k) {
      trait := v1alpha2.ApplicationTrait{
        Name: k,
      }
      ....
      // 如果是 triat 加入 traits 中
      traits = append(traits, trait)
      continue
    }
    workloadKeys[k] = v
  }

  // Handle workloadKeys to settings
  settings := &runtime.RawExte nsion{}
  pt, err := json.Marshal(workloadKeys)
  if err != nil {
    return comp, err
  }
  if err := settings.UnmarshalJSON(pt); err != nil {
    return comp, err
  }
  comp.Settings = *settings

  if len(traits) > 0 {
    comp.Traits = traits
  }

  return comp, nil
}

4. 總結

執(zhí)行 vela up 命令,渲染 appfile 為 Application,將數(shù)據(jù)寫入到 .vela/deploy.yaml 中,并在 K8s 中創(chuàng)建。

KubeVela是如何將appfile轉換為K8s特定資源對象的

Application 是如何轉換為對應 K8s 資源對象

  • 起點:Application

  • 中點:ApplicationConfiguration, Component

  • 終點:Deployment, Service

  • 路徑:

    • application_controller

    • applicationconfiguration controller

【建議】> 了解一下內容:> - client-to

  • controller-runtime

  • operator

1. Application

# 獲取集群中的 Application
$ kubectl get application
NAMESPACE   NAME   AGE
default     test   24h

2. ApplicationConfiguration 和 Component

當 application controller 獲取到 Application 資源對象之后,會根據(jù)其內容創(chuàng)建出對應的 ApplicationConfiguration 和 Component。

# 獲取 ApplicationConfiguration 和 Component
$ kubectl get ApplicationConfiguration,Component
NAME                                         AGE
applicationconfiguration.core.oam.dev/test   24h

NAME                           WORKLOAD-KIND   AGE
component.core.oam.dev/nginx   Deployment      24h

ApplicationiConfiguration 中以名字的方式引入 Component:

KubeVela是如何將appfile轉換為K8s特定資源對象的

3. application controller

基本邏輯:
  • 獲取一個 Application 資源對象。

  • 將 Application 資源對象渲染為 ApplicationConfiguration 和 Component。

  • 創(chuàng)建 ApplicationConfiguration 和 Component 資源對象。

代碼:
// pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go

// Reconcile process app event
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
  ctx := context.Background()
  applog := r.Log.WithValues("application", req.NamespacedName)
  
  // 1. 獲取 Application
  app := new(v1alpha2.Application)
  if err := r.Get(ctx, client.ObjectKey{
    Name:      req.Name,
    Namespace: req.Namespace,
  }, app); err != nil {
    ...
  }

  ...

  // 2. 將 Application 轉換為 ApplicationConfiguration 和 Component
  handler := &appHandler{r, app, applog}
  ...
  appParser := appfile.NewApplicationParser(r.Client, r.dm)
  ...
  appfile, err := appParser.GenerateAppFile(ctx, app.Name, app)
  ...
  ac, comps, err := appParser.GenerateApplicationConfiguration(appfile, app.Namespace)
  ...
  
  // 3. 在集群中創(chuàng)建 ApplicationConfiguration 和 Component 
  // apply appConfig & component to the cluster
  if err := handler.apply(ctx, ac, comps); err != nil {
    applog.Error(err, "[Handle apply]")
    app.Status.SetConditions(errorCondition("Applied", err))
    return handler.handleErr(err)
  }

  ...
  return ctrl.Result{}, r.UpdateStatus(ctx, app)
}

4. applicationconfiguration controller

基本邏輯:
  • 獲取 ApplicationConfiguration 資源對象。

  • 循環(huán)遍歷,獲取每一個 Component 并將 workload 和 trait 渲染為對應的 K8s 資源對象。

  • 創(chuàng)建對應的 K8s 資源對象。

代碼:
// pkg/controller/core.oam.dev/v1alpha2/applicationcinfiguratioin/applicationconfiguratioin.go

// Reconcile an OAM ApplicationConfigurations by rendering and instantiating its
// Components and Traits.
func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) {
  ...
  ac := &v1alpha2.ApplicationConfiguration{}
  // 1. 獲取 ApplicationConfiguration
  if err := r.client.Get(ctx, req.NamespacedName, ac); err != nil {
    ...
  }
  return r.ACReconcile(ctx, ac, log)
}

// ACReconcile contains all the reconcile logic of an AC, it can be used by other controller
func (r *OAMApplicationReconciler) ACReconcile(ctx context.Context, ac *v1alpha2.ApplicationConfiguration,
  log logging.Logger) (result reconcile.Result, returnErr error) {
  
  ...
  // 2. 渲染
  // 此處 workloads 包含所有Component對應的的 workload 和 tratis 的 k8s 資源對象
  workloads, depStatus, err := r.components.Render(ctx, ac)
  ...
  
  applyOpts := []apply.ApplyOption{apply.MustBeControllableBy(ac.GetUID()), applyOnceOnly(ac, r.applyOnceOnlyMode, log)}
  
  // 3. 創(chuàng)建 workload 和 traits 對應的 k8s 資源對象
  if err := r.workloads.Apply(ctx, ac.Status.Workloads, workloads, applyOpts...); err != nil {
    ...
  }
  
  ...

  // the defer function will do the final status update
  return reconcile.Result{RequeueAfter: waitTime}, nil
}

5. 總結

當 vela up 將一個 AppFile 渲染為一個 Application 后,后續(xù)的流程由 application controller 和 applicationconfiguration controller 完成。

KubeVela是如何將appfile轉換為K8s特定資源對象的

上述就是小編為大家分享的KubeVela是如何將appfile轉換為K8s特定資源對象的了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI