KubeVela篇10:KubeVela 私有云Terraform Provider Addon插件开发指南

原创 吴就业 202 0 2023-07-23

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

本文链接:https://wujiuye.com/article/042fd776438b4264a0533092d880c667

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

如何Debug Terraform Controller

摸索期,肯定会遇到很多问题,有些问题可以通过日记查看,而有些问题可能需要修改源码,添加一些用于排查问题的代码。

例如,由于job中的容器执行完命令后就退出了,没办法通过exec -it命令进入容器查看当时的/data目录下执行的main.tf代码文件,也看不到当时的环境变量,可以修改源码给job加一条命令,用来查问题。

将源码克隆下来后,卸载kubevela已经安装的terraform controller,然后本地debug启动terraform controller。

vela addon disable terraform 

如何修改kubevela使用的Terraform版本

由于terraform的每个版本几乎都会修改tf协议,使用高版本特性编写的tf文件,需要高版本的terraform去解析执行,所以我们可能需要修改Terraform controller使用的oamdev/docker-terraform镜像的版本。

从Terraform controller工作原理我们了解到,Terraform controller是以启动一个Job容器的方式去执行terraform命令申请/删除资源,Job的容器镜像里面安装了terraform引擎,这个镜像就是oamdev/docker-terraform,镜像的不同版本安装的是不同的terraform版本。

假设我们现在需要修改Terraform controller使用latest版本的oamdev/docker-terraform镜像,应该怎么修改。

从Terraform controller源码ConfigurationReconciler类可以找到,terraform镜像可以从环境变量“TERRAFORM_IMAGE”取,而Terraform controller会打包成helm chart,通过查看helm chart的terraform_controller.yaml文件,发现controller pod配置了“TERRAFORM_IMAGE”环境变量,值会通过“ .Values.terraformImage”取。现在我们知道可以通过在安装chart的时候指定terraformImage变量值为:“oamdev/docker-terraform:latest”,就可以修改terraform镜像版本为latest。

而kubevela是以addon的方式安装Terraform controller的,所以我们可以在执行vela addon enable terraform命令的时候指定terraformImage变量值为“oamdev/docker-terraform:latest”

但是通过命令指定很容易忘记,如何将“oamdev/docker-terraform:latest”配置为terraformImage的默认值呢。

以从本地安装terraform插件为例。

我们需要下载catalog仓库,修改addons/terraform下的resources/parameter.cue文件,在这个文件中配置terraformImage。 私有云terraform kubevela addon

parameter: {
        values: #values
}

#values: {
    terraformImage: *"oamdev/docker-terraform:latest" | string 
}

修改完保存后,再从本地重新安装Terraform controller。

先禁用已经安装的Terraform controller。

vela addon disable terraform

再从本地安装Terraform controller。

vela addon enable ~/kubevela/catalog/addons/terraform --override-definitions

要删除已有的Job,让terraform controller重新拉起Job才会生效。

如何让Configuration可以指向私有仓库

Configuration是terraform-controller提供的一种自定义资源,用于配置terraform代码。支持通过hcl字段直接配置terraform代码,也支持通过remote字段配置为通过git协议从远程仓库拉取。

Configuration不会直接暴露给kubevela的用户,而是kubevela terraform provider插件的开发者,然后由kubevela terraform provider插件开发者定义为ComponentDefinition提供给kubevela用户使用。

因此,通过hcl方式使用会导致一但terraform有变更,就需要插件重新生成ComponentDefinition,这很麻烦。

apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  annotations:
    definition.oam.dev/description: Terraform configuration for AWS S3
  creationTimestamp: null
  labels:
    type: terraform
  name: aws-s3
  namespace: vela-system
spec:
  schematic:
    terraform:
      configuration: |
        resource "aws_s3_bucket" "bucket-acl" {
          bucket = var.bucket
          acl = var.acl
        }

        output "BUCKET_NAME" {
          value = aws_s3_bucket.bucket-acl.bucket_domain_name
        }

        variable "bucket" {
          description = "S3 bucket name"
          default = "vela-website"
          type = string
        }

        variable "acl" {
          description = "S3 bucket ACL"
          default = "private"
          type = string
        }
  workload:
    definition:
      apiVersion: terraform.core.oam.dev/v1beta1
      kind: Configuration
status: {}

因此我们更倾向于通过使用git仓库的方式,而官方文档只介绍了如何使用公开的github仓库,我们想要的是指向gitlab私有仓库。

apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  annotations:
    definition.oam.dev/description: Terraform configuration for Alibaba Cloud Elastic
      IP
  creationTimestamp: null
  labels:
    type: terraform
  name: alibaba-eip
  namespace: vela-system
spec:
  schematic:
    terraform:
      configuration: https://github.com/oam-dev/terraform-alibaba-eip.git
      type: remote
  workload:
    definition:
      apiVersion: terraform.core.oam.dev/v1beta1
      kind: Configuration
status: {}

这就要研究terraform-controller是否支持配置ssh从私有仓库拉取代码。

从controllers/configuration_controller.go:811 assembleTerraformJob方法中找到了答案。

terraform-controller启动一个Job执行terraform代码,一共会启动四个容器执行四条命令:

一、初始化容器1

初始化容器1负责将ConfigMap挂盘的文件(/opt/tf-configuration/*)拷贝到/data目录下。

当使用hcl,ConfigMap的内容如下。(/opt/tf-configuration/目录下有kubeconfig和main.tf两个文件)

    ```yaml
        apiVersion: v1
        kind: ConfigMap
        data:
          kubeconfig: ''
          main.tf: |
             ${tf代码}
             terraform {
               backend "kubernetes" {
                 secret_suffix     = "${component_name}"
                 in_cluster_config = true
                 namespace         = "${namespace}"
               }
             }
        ```

当使用remote,ConfigMap的内容如下。(/opt/tf-configuration/目录下有kubeconfig和terraform-backend.tf两个文件)

```yaml
    apiVersion: v1
    kind: ConfigMap
    data:
      kubeconfig: ''
      terraform-backend.tf: |
         terraform {
           backend "kubernetes" {
             secret_suffix     = "${component_name}"
             in_cluster_config = true
             namespace         = "${namespace}"
           }
         }
    ```

二、初始化容器2(如果配置了remote)

如果配置了remote,则controller会创建初始化容器2用来则执行git clone命令拉取代码。

流程: 1. 如果配置了Git Secret引用,会将Secret挂盘到/root/.ssh目录下,挂盘后会生成两个文件(两个key),即ssh-privatekey和known_hosts。 2. 执行eval ssh-agent && ssh-add命令,将ssh-privatekey添加到ssh-agent。 3. 执行git clone命令拉取代码。

三、初始化容器3:执行terraform init命令,初始化拉取代码中指定的terraform provider插件。

四、执行容器:负责执行terraform apply -auto-approve命令。

第三、四条命令都会给容器注入一些环境变量:

  1. 将Configuration.Spec.Variable配置的变量转为Terraform环境变量(加上TF_VAR_前缀)。
  2. 根据Configuration.Spec.ProviderReference,获取Provider资源,再根据Provider.Spec.Credentials.SecretRef,获取Secret资源,根据Provider.Spec.Provider和Secret生成Credentials。
  3. 将Terraform环境变量、Credentials(map),注入容器的环境变量,当然,不是直接注入值,值是存到一个名以variable-开头的Secret,容器环境变量注入secretKey,引用这个Secret资源。

因此,使用私有仓库,只需要配置Configuration的remote、gitCredentialsSecretReference、path(可选),但是,因为我们不是直接用terraform-controller的,不能直接创建一个Configuration资源,那么kubevela的ComponentDefinition是否支持?

kubevela源码的charts/vela-core/crds目录下的core.oam.dev_componentdefinitions.yaml文件,就是kubevela的ComponentDefinition资源的定义文件,我找到了关于terraform部分的properties定义:

terraform:
  description: >-
    Terraform is the struct to describe cloud resources managed by Hashicorp
    Terraform
  properties:
    configuration:
      description: Configuration is Terraform Configuration
      type: string
    customRegion:
      description: >-
        Region is cloud provider's region. It will override the region in the
        region field of ProviderReference
      type: string
    deleteResource:
      default: true
      description: >-
        DeleteResource will determine whether provisioned cloud resources will
        be deleted when CR is deleted
      type: boolean
    gitCredentialsSecretReference:
      description: >-
        GitCredentialsSecretReference specifies the reference to the secret
        containing the git credentials
      properties:
        name:
          description: name is unique within a namespace to reference a secret resource.
          type: string
        namespace:
          description: >-
            namespace defines the space within which the secret name must be
            unique.
          type: string
      type: object
      x-kubernetes-map-type: atomic
    path:
      description: >-
        Path is the sub-directory of remote git repository. It's valid when
        remote is set
      type: string
    providerRef:
      description: ProviderReference specifies the reference to Provider
      properties:
        name:
          description: Name of the referenced object.
          type: string
        namespace:
          default: default
          description: Namespace of the referenced object.
          type: string
      required:
        - name
      type: object
    type:
      default: hcl
      description: 'Type specifies which Terraform configuration it is, HCL or JSON syntax'
      enum:
        - hcl
        - json
        - remote
      type: string
    writeConnectionSecretToRef:
      description: >-
        WriteConnectionSecretToReference specifies the namespace and name of a
        Secret to which any connection details for this managed resource should
        be written. Connection details frequently include the endpoint,
        username, and password required to connect to the managed resource.
      properties:
        name:
          description: Name of the secret.
          type: string
        namespace:
          description: Namespace of the secret.
          type: string
      required:
        - name
      type: object
  required:
    - configuration
  type: object

从源码得到结论:是支持使用gitCredentialsSecretReference的,只是kubevela提供的vela def init命令不支持指定gitCredentialsSecretReference,所以我们用命令生成云资源的ComponentDefinition后,还需要手动加上gitCredentialsSecretReference。

vela def init

为云资源编写ComponentDefinition

制作ssh访问git的Secret资源

首先我们在k8s集群中创建包含ssh-privatekey和known_hosts的Secret资源。

这里使用个人账号举例:

使用“kubectl create secret generic”命令创建:

kubectl create secret generic wjy-github-ssh-key-secret --from-file=ssh-privatekey=/Users/wujiuye/.ssh/id_rsa --from-file=known_hosts=/Users/wujiuye/.ssh/known_hosts -n vela-system

然后我们就可以使用remote生成资源的ComponentDefinition了。

为云资源生成ComponentDefinition

我们可以用vela def命令来生成一个模版,也可以直接copy现有一个模版来修改:

vela def init vpc --type component --provider mycloud --desc "Terraform configuration for Mycloud VPC" --git [email protected]:wujiuye/terraform-mycloud-modules.git --path=vpc

修改生成的vpc ComponentDefinition,添加gitCredentialsSecretReference配置:

apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  annotations:
    definition.oam.dev/description: Terraform configuration for Mycloud VPC
  creationTimestamp: null
  labels:
    type: terraform
  name: mycloud-vpc
  namespace: vela-system
spec:
  schematic:
    terraform:
      configuration: https://github.com/wujiuye/terraform-mycloud-modules.git
      path: vpc
      type: remote
      gitCredentialsSecretReference: 
        name: wjy-github-ssh-key-secret
        namespace: vela-system
  workload:
    definition:
      apiVersion: terraform.core.oam.dev/v1beta2
      kind: Configuration
status: {}

重新覆盖安装一下自定义的terraform-mycloud插件。

vela addon enable ~/kubevela/catalog/addons/terraform-mycloud --override-definitions

~/kubevela/catalog/addons/terraform-mycloud为本地terraform-mycloud插件的目录

使用vela show mycloud-vpc --web命令验证组件是否apply成功。

这种方式并不高效,如果组件非常多的话,可以写一个组件生成器,通过模板生成组件。

可能会遇到invalid auth method问题,此报错是kubevela验证这个ComponentDefinition报的错,无法从远程拉取代码。原因是URL不能以https开头,需要将https://github.com/wujiuye/terraform-mycloud-modules.git改成[email protected]:wujiuye/terraform-mycloud-modules.git

验证流程是否跑通

先为mycloud创建一个Provider资源,其实就是创建访问mycloud terraform provider的Secret资源以及terraform controller的Provider资源。

需要准备: - ENDPOINT - TOKEN

替换下面命令中的参数,然后secret的name和provider的name可以自己指定,用于创建kubevela terraform-controller的Provider资源:

vela config create mycloud-office-admin-secret -t terraform-mycloud ENDPOINT="http://127.0.0.1:8080" TOKEN="xxxx" name=mycloud-office-admin-provider

输出“the config mycloud-office-admin-secret applied successfully”表示配置创建成功。

输出的“mycloud-office-admin-secret”为Secret资源的名称,由名为“mycloud-office-admin-provider”的Provider引用。

验证:

kubectl get provider  

输出:

NAME                            STATE   AGE
mycloud-office-admin-provider   ready   9s

现在我们编写一个application验证一下组件。

[test-app-vpc.yaml]

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: test-app
spec:
  components:
    - name: test-app-vpc
      type: mycloud-vpc
      properties:
        name: test-app-vpc-123456789
        ip_capacity: 32
        providerRef:
          name: mycloud-office-admin-provider
          namespace: default
        writeConnectionSecretToRef:
          name: test-app-vpc-output-secret 
          namespace: default

执行命令apply验证创建:

vela up -f test-app-vpc.yaml

可能会遇到问题:failed for volume “git-auth-configuration”: secret “xxx” not found

MountVolume.SetUp faild for volume "git-auto-configuration" : secret "wjy-github-ssh-key-secret" not found

分析源码后发现,k8s约束了给pod挂盘ConfigMap或者Secret必须是跟pod在同一个namespace下。也就是我们创建的Secret,必须跟terraform controller创建的Job资源在同一个namespace。以及组件的providerRef、writeConnectionSecretToRef引用的资源,也必须跟terraform controller创建的Job资源在同一个namespace。

解决方案一:在default namespace创建git Secret资源

terraform controller创建的Job资源使用的默认namespace是default,可以将git Secret资源创建在default这个namespace下。

kubectl create secret generic wjy-github-ssh-key-secret --from-file=ssh-privatekey=/Users/wujiuye/.ssh/id_rsa --from-file=known_hosts=/Users/wujiuye/.ssh/known_hosts -n default

解决方案二:修改指定terraform controller创建的Job资源使用的namespace

实际是修改terraform controller的部署namespace。

假设我们需要将terraform controller部署在vela-system。

如果是本地debug,启动参数配置controller-namespace,指定namespace为vela-system。

如果是通过vela addon安装terraform插件,可通过addon命令指定controllerNamespace=vela-system。

一个正确的示例

ComponentDefinition:

 apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  annotations:
    definition.oam.dev/description: Terraform configuration for Mycloud VPC
  creationTimestamp: null
  labels:
    type: terraform
  name: mycloud-vpc
  namespace: vela-system
spec:
  schematic:
    terraform:
      configuration: [email protected]:wujiuye/terraform-mycloud-modules.git
      path: vpc
      type: remote
      gitCredentialsSecretReference: 
        name: wjy-github-ssh-key-secret
        namespace: default
  workload:
    definition:
      apiVersion: terraform.core.oam.dev/v1beta2
      kind: Configuration
status: {}

使用:

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: test-app
spec:
  components:
    - name: test-app-vpc
      type: mycloud-vpc
      properties:
        name: test-app-vpc-123456789
        ip_capacity: 32
        providerRef:
          name: mycloud-office-admin-provider
          namespace: default
        writeConnectionSecretToRef:
          name: test-app-vpc-output-secret 
          namespace: default
#云原生

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

文章推荐

KubeVela篇13:跨地域的多集群管理方案

随着公司全球化战略的布局,业务呈点状分布在亚太、美东、欧洲等多个地域,云原生kubevela在跨地域多集群管控方面也遇到网络上的互通问题。

KubeVela篇12:自定义工作流步骤以及踩坑经验

官方提供的工作流步骤有限,另外,对于自研的PaaS平台,我们需要借助工作流步骤实现一些例如存量项目基础设施导入、项目环境初始化、平台组件共享基础设施需要解决的差异对比审核、基础设施漂移等。

KubeVela篇11:可持续测试应用部署之Mock基础设施

我们基于KubeVela开发的云原生应用交付平台,提供如初始化基础设施导入、中间件部署共用基础设施等相关能力的测试,需要依赖基础设施。虽然terraform是面向公司内部的混合云平台,但是测试都要跨部门配置效率太低了,而且这种模式无法支持持续测试。

KubeVela篇09:KubeVela工作流步骤CUE模版和Go代码是怎么关联的

terraformProvider、multiclusterProvider、oamProvider、configprovider、kube这些provider的Install方法注册了很多操作处理方法。这些方法就是提供给CUE中调用的方法。

KubeVela篇08:一个组件的输出作为另一个组件的输入案例及传递原理

kubevela安装一个Application的过程,就是执行工作流上的每个步骤的过程,并且当我们未配置工作流,kubevela会自动为组件的部署生成一个工作流步骤。

KubeVela篇07:terraform controller实现原理

erraform-controller是一个专门负责terraform一类的组件"安装"的Operator,通过打包成helm,再封装成kubevela的Addon,由kubevela安装到管控集群,为其它terraform provider插件提供模块定义支持。