The author | Sun Jianbo (tianyuan) alibaba technical experts

After nearly 3 months of iteration, OAM Spec v1alpha2 has finally been released. While adhering to the OAM Spec platform independence, the new version is more kubernetes-friendly overall, largely balancing standards and extensibility with better CRD support. If you have already written a CRD Operator, you can plug smoothly into the OAM architecture and enjoy the benefits of the OAM model.

At present, OAM has become the core architecture of cloud products built by many companies, including Alibaba, Microsoft, Upbond, Harmonic Cloud and so on. They built the app-centric, user-friendly Kubernetes PaaS through OAM; Give full play to the standardization and expansibility of OAM, realize the core Controller of OAM, and quickly access the existing Operator capability; Through OAM, multiple modules are opened horizontally, which breaks the dilemma that the original Operator is isolated from each other and cannot be reused.

  • To understand the background and origin of OAM, you can refer to “In-depth Interpretation! Lessons and Practice of Ali unified Application Management Architecture Upgrade”.

  • What value does OAM bring to the end user? See OAM in Depth: What value DOES OAM Bring to Cloud Native Apps?

Without further ado, what changes have been made to V1Alpha2?

Description of Major Changes

For your convenience, only the most important changes are listed here. For details, please refer to the upstream OAM Spec Github repository.

The term that

  • CRD (Custom Resource Definition) : IN OAM, CRD refers to a Custom Resource description Definition. In the OAM implementation of K8s, it can completely correspond to THE CRD of K8s. In the non-K8S implementation, THE CRD of OAM needs to contain APIVersion/Kind and can describe fields for verification.

  • CR (Custom Resource), CR in OAM is an instance of CRD, is in line with the FIELD format definition of a Resource description in CRD. In the OAM implementation of K8s it can correspond exactly to THE CR of K8s, and in the non-K8S implementation it may be necessary to align APIVersion/Kind and field format definitions.

Major change 1 Uses the Reference model to define Workload, traits, and Scope

V1alpha1 originally worked like this:

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: WorkloadType
metadata:
  name: OpenFaaS
  annotations:
    version: v1.0.0
    description: "OpenFaaS a Workload which can serve workload running as functions"
spec:
  group: openfaas.com
  version: v1alpha2
  names:
    kind: Function
    singular: function
    plural: functions
  workloadSettings: | { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [ "name", "image" ], "properties": { "name": { "type": "string", "description": "the name to the function" }, "image": { "type": "string", "description": "the docker image of the function" } } }Copy the code

In the original schema, group/version/kind were fields respectively, and the validation of the spec was expressed by jsonschema. The overall format is actually similar to CRD, but not identical.

In the new version of V1Alpha2, the reference model was completely changed to describe a reference relationship in the form of WorkloadDefinition TraitDefinition ScopeDefinition. You can reference a CRD directly, and name is the name of the CRD. For non-K8S OAM implementations, the name is an index to find crD-like validation files containing apiVersion and KIND, along with the corresponding schema validation.

  • Workload
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: containerisedworkload.core.oam.dev
spec:
  definitionRef:
    # Name of CRD. 
    name: containerisedworkload.core.oam.dev
Copy the code
  • Trait
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
  name: manualscalertrait.core.oam.dev
spec:
  appliesToWorkloads:
    - containerizedworkload.core.oam.dev
  definitionRef:
    name: manualscalertrait.core.oam.dev
Copy the code
  • Scope
apiVersion: core.oam.dev/v1alpha2
kind: ScopeDefinition
metadata:
  name: networkscope.core.oam.dev
spec:
  allowComponentOverlap: true
  definitionRef:
    name: networkscope.core.oam.dev
Copy the code

Note:

  1. Name (plural-kind>.

    ) for the OAM implementation of K8s, name is the name of the CRD inside K8s. The community best practice is that only one version of a CRD runs in the cluster, and generally the new version is forward compatible and replaced with the latest version at one time when upgrading. Kubectl get CRD

    if two versions exist at the same time, the user can further select them by kubectl get CRD

    .


  2. Definition layer is not for end user and is mainly used by platform implementation. For non-K8S implementation, if there are multiple versions of the scenario, the implementation platform of OAM can show the end user the choice of different versions.

Major change 2 is directly embedded in K8s CR as Component and Trait instances

In the original way, we only took out the SPEC part of CR at the Workload and Trait levels and put it in the workloadSettings and Properties fields, respectively.

Although such a method can already be “derived” from K8s CR, it is not conducive to CRD access in THE K8s ecosystem, so we need to redefine the spec in another format.

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: rediscluster
spec:
  workloadType: cache.crossplane.io/v1alpha1.RedisCluster
  workloadSettings:
    engineVersion: 1.0
    region: cn
Copy the code
// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: custom-single-app
  annotations:
    version: v1.0.0
    description: "Customized version of single-app"
spec:
  variables:
  components:
    - componentName: frontend
      instanceName: web-front-end
      parameterValues:
      traits:
        - name: manual-scaler
          properties:
            replicaCount: 5
Copy the code

The current approach is to embed CR directly, and you can see the complete CR description under the Workload and Trait fields.

apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
  name: example-server
spec:
  prameters:
    - name: xxx
      fieldPaths: 
        - "spec.osType"
  workload:
    apiVersion: core.oam.dev/v1alpha2
    kind: Server
    spec:
      osType: linux
      containers:
      - name: my-cool-server
        image:
          name: Example/very - cool - server: 1.0.0
        ports:
        - name: http
          value: 8080
        env:
        - name: CACHE_SECRET
Copy the code
apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
  name: cool-example
spec:
  components:
  - componentName: example-server
    traits:
    - trait:
        apiVersion: core.oam.dev/v1alpha2
        kind: ManualScalerTrait
        spec:
          replicaCount: 3
Copy the code

The benefits are obvious:

  1. It can easily interconnect with CRDS in existing K8s systems, even K8s native onesDeployment(Access as custom workload) and other resources;
  2. The field definition of K8s CR level is mature, and the parsing and verification are completely handed over to CRD system.

Traits is a []trait{CR} instead of a []CR} structure, adding a layer of seemingly useless trait fields for two reasons:

  • Leave room for further extensions to traits, such as possible arrangements (ordering), etc.
  • The non-K8S system can not be written in strict accordance with CR in this layer, completely customized, without binding K8s description format.

Major change 3: Parameter passing replaces fromParam with jsonPath

Research and development can set aside fields for operation and maintenance coverage, has always been an important function of OAM.

ParameterValue is overridden by parameterValue in AppConfig. ParameterValue is defined in Component.

The initial parameter passing was to place a fromParam field after each field. With custom schema support, this method obviously cannot cover all scenarios:

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: rediscluster
spec:
  workloadType: cache.crossplane.io/v1alpha1.RedisCluster
  parameters:
  - name: engineVersion
    type: string
  workloadSettings:
    - name: engineVersion
      type: string
      fromParam: engineVersion
Copy the code

Later we proposed something like this:

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: rediscluster
spec:
  workloadType: cache.crossplane.io/v1alpha1.RedisCluster
  parameters:
  - name: engineVersion
    type: string
  workloadSettings:
    engineVersion: "[fromParam(engineVersion)]"
Copy the code

The biggest problem with this solution is that dynamic functions are added to the static IaD (Infrastructure as Data), which makes it complicated to understand and use the IaD.

After much discussion, in the new scheme we describe the location of parameters to be injected in the form of JsonPath, which ensures that AppConfig is static in the user’s understanding.

apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
  name: example-server
spec:
  workload:
    apiVersion: core.oam.dev/v1alpha2
    kind: Server
    spec:
      containers:
      - name: my-cool-server
        image:
          name: Example/very - cool - server: 1.0.0
        ports:
        - name: http
          value: 8080
        env:
        - name: CACHE_SECRET
          value: cache
  parameters:
  - name: instanceName
    required: true
    fieldPaths:
    - ".metadata.name"
  - name: cacheSecret
    required: true
    fieldPaths:
    - ".workload.spec.containers[0].env[0].value"
Copy the code

FieldPaths is an array where each element defines parameters and corresponding Workload fields.

apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
  name: my-app-deployment
spec:
  components:
    - componentName: example-server
      parameterValues:
        - name: cacheSecret
          value: new-cache
Copy the code

ParameterValues in AppConfig override the parameter in Component.

Major change 4 ComponentSchematic name to Component

The original concept of a component was called ComponentSchematic, which was named primarily because it mixed up syntax descriptions and choices, such as for Core Workload (Container) and for extension Workload (workloadSettings), Container defines specific parameters. WorkloadSettings is more like a schema. The V1Alpha1 version of WorkloadSetting also incorporates type/description, which is more ambiguous.

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: rediscluster
spec:
  containers:
     .
  workloadSettings:
    - name: engineVersion
      type: string
      description: engine version
      fromParam: engineVersion
     .
Copy the code

In v1Alpha2, the concept of Component is changed to Component, explicitly an instance of Workload, and all syntactic definitions are defined by the actual CRD referenced in the WorkloadDefinition.

In the K8s implementation, WorkloadDefinition refers to CRD, and component.spec. workload refers to CRD instance CR.

apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
  name: example-server
spec:
  workload:
    apiVersion: core.oam.dev/v1alpha2
    kind: Server
    spec:
   .
Copy the code

Major changes 5 Scope was created separately by CR and no longer by AppConfig

The Scope in V1alpha1 is created by AppConfig, and as you can see from the example, it is also essentially a CR, which can be “derived” from it. However, since Scope is positioned to accommodate different Components in AppConfig, and Scope itself is not an App, it is not appropriate to use AppConfig to create Scope.

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: my-vpc-network
spec:
  variables:
    - name: networkName
      value: "my-vpc"
  scopes:
    - name: network
      type: core.oam.dev/v1alpha1.Network
      properties:
        network-id: "[fromVariable(networkName)]"
        subnet-ids: "my-subnet1, my-subnet2"
Copy the code

The new version of V1Alpha2 fully uses CR to correspond to instances. In order to make the concept of Scope clearer and more convenient to correspond to different types of Scope, Scope is taken out and directly created by CR corresponding to CRD defined by ScopeDefinition. Here is an example:

apiVersion: core.oam.dev/v1alpha2
kind: ScopeDefinition
metadata:
  name: networkscope.core.oam.dev
spec:
  allowComponentOverlap: true
  definitionRef:
    name: networkscope.core.oam.dev
Copy the code
apiVersion: core.oam.dev/v1alpha2
kind: NetworkScope
metadata:
  name: example-vpc-network
  labels:
    region: us-west
    environment: production
spec:
  networkId: cool-vpc-network
  subnetIds:
  - cool-subnetwork
  - cooler-subnetwork
  - coolest-subnetwork
  internetGatewayType: nat
Copy the code

Using scope references in AppConfig looks like this:

apiVersion: core.oam.dev/v1alpha2
kind: ApplicationConfiguration
metadata:
  name: custom-single-app
  annotations:
    version: v1.0.0
    description: "Customized version of single-app"
spec:
  components:
    - componentName: frontend
      scopes:
        - scopeRef:
            apiVersion: core.oam.dev/v1alpha2
            kind: NetworkScope
            name: my-vpc-network
    - componentName: backend
      scopes:
        - scopeRef:
            apiVersion: core.oam.dev/v1alpha2
            kind: NetworkScope
            name: my-vpc-network
Copy the code

Major change 6 removed the Variable list and the [fromVariable()] dynamic function

Variable is included in v1Alpha1 version in order to reduce redundancy by quoting some public variables in AppConfig, so Variable is added to the list. In practice, however, the reduced redundancy does not significantly reduce the complexity of OAM specs; on the contrary, adding dynamic functions significantly increases the complexity.

On the other hand, capabilities such as fromVariable can be done by tools such as Helm Template/Kustomiz, which render complete OAM specs and then use them.

Therefore, the variables list and the related fromVariable are removed without affecting any functions.

// Old version, used for comparison only
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: my-app-deployment
spec:
  variables:
    - name: VAR_NAME
      value: SUPPLIED_VALUE
  components:
    - componentName: my-web-app-component
      instanceName: my-app-frontent
      parameterValues:
        - name: ANOTHER_PARAMETER
          value: "[fromVariable(VAR_NAME)]"
      traits:
        - name: ingress
          properties:
            DATA: "[fromVariable(VAR_NAME)]"
Copy the code

Major change 7: ContainerizedWorkload replaces the original six core Workload types

Because WorkloadDefinition is now uniformly used to define Workload, Component becomes an instance, so the original six core Workload definitions actually become the same WorkloadDefinition with the same field description. The only difference is that traits are bound and appealed differently. Therefore, the original six types of core Workload specs were changed into a Workload type named ContainerizedWorkload.

At the same time, there is a plan to allow r&d to express the appeal of operations strategy by adding concepts such as policy, that is, which traits can be added to components.

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: containerizedworkloads.core.oam.dev
spec:
  definitionRef:
    name: containerizedworkloads.core.oam.dev
Copy the code

An example of using ContainerizedWorkload:

apiVersion: core.oam.dev/v1alpha2
kind: Component
metadata:
  name: frontend
  annotations:
    version: v1.0.0
    description: "A simple webserver"
spec:
  workload:
    apiVersion: core.oam.dev/v1alpha2
    kind: ContainerizedWorkload
    metadata:
      name: sample-workload
    spec:
      osType: linux
      containers:
      - name: web
        image: example/charybdis-single:latest@@sha256:verytrustworthyhash
        resources:
          cpu:
            required: 1.0
          memory:
            required: 100MB
        env:
        - name: MESSAGE
          value: default
  parameters:
  - name: message
    description: The message to display in the web app.  
    required: true
    type: string
    fieldPaths:
    - ".spec.containers[0].env[0].value"
Copy the code

Next step

  1. Parameter passing and dependencies between components at the application level (Workflow)
  2. The Policy scheme facilitates the development of traits in Component;
  3. Component adds the concept of versioning and how OAM addresses application versioning.

Common FAQ

  1. What do we need to do to transform the original platform into OAM model?

For the application management platform originally on K8s, the access transformation into OAM implementation can be divided into two stages:

  • OAM ApplicationConfiguration Controller (AppConfig Controller) This Controller contains the Component of OAM, WorkloadDefinition, TraitDefinition, ScopeDefinition, and other CRDS. AppConfig Controller Pulls up the CRD Operator of the original platform as described in OAM AppConfig.
  • The original CRD Operator is gradually divided into Workload and traits based on the idea of separation of concerns. At the same time, more Workload and traits in OAM community can be accessed and reused to enrich functions in more scenarios.
  1. What changes do existing CRD operators need to make to access OAM?

The existing CRD Operator** functions can be smoothly plugged into the OAM architecture, such as as a standalone extension Workload. However, in order to better enable end users to experience the benefits of OAM separation of concerns, we strongly suggest that CRD operators separate into different CRDS according to different concerns of R&D and operation and maintenance, and the CRDS concerned by R&D are connected to OAM as Workload. The CRD that operations care about is plugged into OAM as traits.

Currently, the OAM specification and model have actually addressed many of the existing problems, but its journey has only just begun. OAM is a neutral open source project and we welcome more people to join us in defining the future of cloud native application delivery.

Participation:

  • Nail scan code into OAM Project Chinese discussion group

  • Participate directly in the discussion via Gitter
  • OAM open source implementation address
  • I’m gonna hit star

Author’s brief introduction

Sun Jianbo (name: Tianyuan) is a technical expert of Alibaba. He is one of the main makers of THE OAM specification and is committed to promoting the standardization of cloud native applications. At the same time, I also participated in large-scale cloud native application delivery and application management in Alibaba. The team invites experts in application delivery, Serverless, and PaaS to join. Please contact Jianbo.sjb AT alibaba-inc.com

Hiring!

Cloud Native Application Platform invites Kubernetes/Serverless/PaaS/Application Delivery experts (P6-P8) to join:

  • Working years: p6-7 from 3 years, P8 from 5 years, depending on actual ability;
  • Location: China (Beijing/Hangzhou/Shenzhen); Overseas (San Francisco Bay Area/Seattle);
  • Positions include: architect, technical expert, full stack engineer, etc.

Resume reply immediately, 2~3 weeks to get the result, resume send: jianbo. SJB AT alibaba-inc.com.

“Alibaba Cloud originators pay close attention to technical fields such as microservice, Serverless, container and Service Mesh, focus on cloud native popular technology trends and large-scale implementation of cloud native, and become the technical circle that knows most about cloud native developers.”