KubeVela篇01:部署即代码-编写yaml在KubeVela上交付第一个应用

原创 吴就业 177 0 2023-07-22

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://wujiuye.com/article/21bb03edef084e37ac801bb4646da783

作者:吴就业
链接:https://wujiuye.com/article/21bb03edef084e37ac801bb4646da783
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。

带你体验什么叫“yaml工程师”

云原生企业级实战笔记专栏

初识OAM模型,交付一个简单的应用

“部署即代码”即用代码描述一个应用的部署计划。KubeVela就是实现这一目标的平台,让我们可以编写一个符合OAM模型的yaml来描述应用的部署。这些概念我们先不急着理解。

现实场景中,我们部署一个应用可能会涉及到申请基础设施,然后要知道部署到哪个集群上(环境),以及需要先部署某些需要依赖的中间件。

例如部署一个简单的web服务,我们需要依赖redis中间件。假设现在我们用kubeVela来部署这样一个应用,我们需要用OAM模型来描述它,编写一个application.yaml文件。

一、OAM模型的结构

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: oam-app
spec:
  components:
  workflow:

OAM模型将基础设施、中间件、微服务等抽象成组件,一个应用由一到多个组件组成,而工作流则负责编排组件的部署。默认情况下,即便我们不声明工作流步骤,kubeVela也会提供默认的工作流步骤来部署组件。

二、描述web服务的部署

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: web-demo-app
spec:
  components:
    - name: web-demo-service
      type: webservice
      properties:
        image: xxx.xxx.xx/web-demo-service:1.0
        ports:
         - port: 8080
           expose: true
        redis: 
          endpoints: 
           - {}
  workflow:
   - type: apply-component     
     name: deploy-web-demo-service       
     properties:               
       component: web-demo-service

这段代码描述了web-demo-app这个应用由一个组件组成,并且声明了一个工作流步骤来部署这个组件:

三、描述依赖的中间件部署

假设部署redis我们使用kubevela提供redis-operator addon插件封装的redis-failover组件,实际部署应用的话需要先安装这个addon插件。

现在我们需要给app添加一个redis-failover组件,并且拿到redis组件部署后输出的Endpoints(访问redis集群的ip和端口)传递给web服务(web-demo-service)。

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: web-demo-app
spec:
  components:
    - name: web-demo-service
      type: webservice
      properties:
        image: xxx.xxx.xx/web-demo-service:1.0
        ports:
         - port: 8080
           expose: true
        redis: 
          endpoints: 
            - {} # 待部署后生成
    - name: sample-redis
      type: redis-failover
      properties:
        replicas: 1
  workflow:
  - type: apply-component 
    name: deploy-sample-redis
    properties:
      component: sample-redis
  - name: read-redis-output
    type: read-object
    dependsOn:
      - deploy-sample-redis
    outputs:
      - name: redisoutput
        valueFrom: output.value.subsets
    properties:
      apiVersion: v1
      kind: Endpoints
      name: rfs-sample-redis
   - type: apply-component     
     name: deploy-web-demo-service       
     properties:               
       component: web-demo-service
     dependsOn:
       - read-redis-output
     inputs:
      - from: redisoutput
        parameterKey: values.redis.endpoints

我们给描述文件添加了sample-redis组件,然后增加了deploy-sample-redis和read-redis-output这两个工作流步骤,而deploy-web-demo-service工作流步骤也有所修改。

现在我们来理解一下这三个工作流步骤:

四、指定部署在哪个k8s集群

kubevela通常以独立的k8s集群部署,而预发、线上环境都是独立的k8s集群,现在假设我们需要让kubevela将应用部署到线上环境的k8s集群,线上k8s集群名为”prod-cluster”。

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: web-demo-app
spec:
  components:
    - name: web-demo-service
      type: webservice
      properties:
        image: xxx.xxx.xx/web-demo-service:1.0
        ports:
         - port: 8080
           expose: true
        redis: 
          endpoints: 
            - {} # 待部署后生成
    - name: sample-redis
      type: redis-failover
      properties:
        replicas: 1
  workflow:
  - type: apply-component 
    name: deploy-sample-redis
    properties:
      component: sample-redis
      cluster: prod-cluster
  - name: read-redis-output
    type: read-object
    dependsOn:
      - deploy-sample-redis
    outputs:
      - name: redisoutput
        valueFrom: output.value.subsets
    properties:
      apiVersion: v1
      kind: Endpoints
      name: rfs-sample-redis
      cluster: prod-cluster
   - type: apply-component     
     name: deploy-web-demo-service       
     properties:               
       component: web-demo-service
       cluster: prod-cluster
     dependsOn:
       - read-redis-output
     inputs:
      - from: redisoutput
        parameterKey: values.redis.endpoints

当然,除了在工作流步骤中指定工作流的依赖关系实现组件按依赖顺序部署,还可以直接在组件中声明这种关系,而参数的传递也同样可以在组件中声明。

除了指定部署到prod-cluster集群外,相同的目标,描述文件可以改成:

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: web-demo-app
spec:
  components:
    - name: web-demo-service
      type: webservice
      dependsOn:
       - sample-redis
      properties:
        image: xxx.xxx.xx/web-demo-service:1.0
        ports:
         - port: 8080
           expose: true
        redis: 
          endpoints: 
            - {} # 待部署后生成
      inputs:
       - from: redisoutput
         parameterKey: properties.values.redis.endpoints
    - name: sample-redis
      type: redis-failover
      properties:
        replicas: 1
      output:
        - name: redisoutput
          valueFrom: |
            import "vela/op"
            #Endpoints: op.#Read & {
               value: {
                  kind: "Endpoints"
                  apiVersion: "v1"
                  metadata: {
                    name: "rfs-sample-redis"
                    namespace: "default"
                  }
               }
            }
            #Endpoints.value.subsets
  workflow:
   - type: deploy                    
     name: deploy-to-prod-cluster      

由于没有一个叫read-object的组件,所以这里用到了valueFrom支持的另一种写法:CUE表达式。通过CUE代码读取Endpoints资源,并将Endpoints资源的subsets输出。

现在我们通过OAM模型中的应用策略来指定部署集群。

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: web-demo-app
spec:
  components:
    - name: web-demo-service
      type: webservice
      dependsOn:
       - sample-redis
      properties:
        image: xxx.xxx.xx/web-demo-service:1.0
        ports:
         - port: 8080
           expose: true
        redis: 
          endpoints: 
            - {} # 待部署后生成
      inputs:
       - from: redisoutput
         parameterKey: properties.values.redis.endpoints
    - name: sample-redis
      type: redis-failover
      properties:
        replicas: 1
      output:
        - name: redisoutput
          valueFrom: |
            import "vela/op"
            #Endpoints: op.#Read & {
               cluster: "prod-cluster",
               value: {
                  kind: "Endpoints"
                  apiVersion: "v1"
                  metadata: {
                    name: "rfs-sample-redis"
                    namespace: "default"
                  }
               }
            }
            #Endpoints.value.subsets
 policies:
   - name: prod-topology
     type: topology
     properties:
       clusters: ["prod-cluster"]
  workflow:
   - type: deploy                    
     name: deploy-to-prod-cluster 
     properties:                     
       policies: ["prod-topology"]       

我们描述了类型为topology的应用策略“prod-topology”,topology策略是kubevela内置的策略,描述组件部署到哪些k8s集群,这里指定为只部署到“prod-cluster”集群。然后还需要将该策略配置给deploy-to-prod-cluster工作流步骤。

五、以NodePort暴露web服务给集群外部访问

OAM模型中,组件可以有运维特征,我们给组件配置水平自动扩缩容需要用到运维特征,给组件注入环境变量也可以用到运维特征。

现在,为了验证我们部署的web-demo-service服务能够正常访问,需要用到kubevela内置的expose特征,以NodePort类型暴露8080端口给k8s集群外部访问。

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: web-demo-app
spec:
  components:
    - name: web-demo-service
      type: webservice
      dependsOn:
       - sample-redis
      properties:
        image: xxx.xxx.xx/web-demo-service:1.0
        ports:
         - port: 8080
           expose: true
        redis: 
          endpoints: 
            - {} # 待部署后生成
      inputs:
       - from: redisoutput
         parameterKey: properties.values.redis.endpoints
      traits:
        - type: expose
          properties:
            port: [8080]
            type: NodePort
    - name: sample-redis
      type: redis-failover
      properties:
        replicas: 1
      output:
        - name: redisoutput
          valueFrom: |
            import "vela/op"
            #Endpoints: op.#Read & {
               value: {
                  kind: "Endpoints"
                  apiVersion: "v1"
                  metadata: {
                    name: "rfs-sample-redis"
                    namespace: "default"
                  }
               }
            }
            #Endpoints.value.subsets
 policies:
   - name: prod-topology
     type: topology
     properties:
       clusters: ["prod-cluster"]
  workflow:
   - type: deploy                    
     name: deploy-to-prod-cluster 
     properties:                     
       policies: ["prod-topology"]       

总结

此web服务举例还不完整,还缺少申请基础设施的部分:要暴露给浏览器访问,首先还得要有个域名,然后还涉及到ALB(应用层负载均衡)等,因为过于复杂,特别是ALB,不好理解,所以案例把基础设施部分砍掉了。不过通过后面小节内容的学习,我们也能了解到如何通过OAM来描述申请基础设施。

另外,在此web案例中,我们将Redis作为中间件部署,实际上,我们也可以将Redis作为基础设施申请,例如使用阿里云的Redis服务,阿里云的Terraform Provider提供了Redis资源。


参考文献:

#云原生

声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

文章推荐

KubeVela篇04:KubeVela打通Terraform申请云资源

以使用阿里云的基础设施为例,理解KubeVela打通Terraform申请云资源。

KubeVela篇03:了解KubeVela安装一个应用的过程

以一个简单的first-vela-app应用在kubevela上部署为例,介绍应用安装流程。

KubeVela篇02:初识KubeVela,进一步理解OAM模型

KubeVela是面向混合云环境的应用交付控制面,不与任何云产商绑定。KubeVela通过提供插件扩展机制,打通了应用交付涉及的基础设施即代码-terraform等能力。编写一个符合OAM模型的application.yaml就能实现将应用部署起来,包括申请基础设施。实现了声明式部署,且一次编写,到处部署。

terraform篇03:terraform provider开发避坑指南

Go sdk本地开发调试sdk依赖问题;关于复杂嵌套结构体的schema声明;状态死循环监听,以及terraform命令终止时如何终止死循环;资源创建接口的默认可选字段不填遇到的坑;HCL代码输入变量的复杂校验。

terraform篇02:为私有云开发一个terraform provider

很多企业内部为了不与云厂商绑定,避免上云容易下云难的尴尬,以及企业内部可能也会做私有云,或者封装一个混合云平台,因此不能直接用云厂商提供的provider。

terraform篇01:基础设施即代码,初识terraform,用代码减少沟通成本

通常申请基础设施,我们需要向运维描述我们需要什么基础设施、什么规格,运维根据我们的描述去检查是否已经申请过这样的资源,有就会直接给我们使用基础设施的信息,没有再帮我们申请,然后告诉我们使用基础设施的信息,例如mysql的jdbc和用户名、密码。如果将描述代码化,基础设施的申请自动化,就能实现“基础设施即代码”。而terraform就是实现“将描述代码化”的工具软件。