目录

    1. Terraform Vs Kubernetes

    基础架构即代码(Iac) 基于不可变的基础架构,使用编排工具将基础架构文本化,允许像管理代码一样管理基础设施。

    2018 年,我在从事 SaaS 开发,使用 Kubernetes 平台进行部署,这一年 Terraform 很火。2019 年,我开始从事 Kubernetes 的二次开发,才听说 Terraform 。现在网上 Terraform 相关的文档增量已经很少,更多是 Kubernetes 。

    为什么我开始关注 Terraform ?因为测试 Kubernetes 经常需要创建大量集群。手工在 IaaS 的 GUI 创建 VM ,登陆部署是最低效的方式。我也尝试过使用 Jenkins 流水线部署 Kubernetes ,这得维护一台可靠的服务器。最后就关注到了 Terraform 。

    Terraform 承载的是平台,而 Kubernetes 承载的是应用。

    存储平台、监控平台、PaaS 平台、依赖于 Kubernetes 的平台、DevOps 平台等,需要考虑使用 VM 进行部署,减少架构上的复杂度,而用户服务的负载可以直接使用 Kubernetes 进行部署。目前,使用和运维 Kubernetes 的门槛并没有很低,强行 All in Kubernetes 而运维能力没有跟上,会导致更棘手的问题。以前服务只是慢,现在服务打不开 。

    2. Terraform 的运行机制

    编排工具的核心是定义一种 DSL 语言,用户使用 Outer DSL 语言描述流程,而编排工具实现 Inner DSL 对其解析,转换为具体执行动作。如下图:

    Terraform 的 Outer DSL 是提供给用户编写的:

    provider "cloud" {                                                                                                                                                      
        secret_id  = ""
        secret_key = ""
        region     = ""
    }
    
    data "cloud_image" "myimage" {
      os_name = "centos"
      ...
    }
    
    resource "cloud_instance" "my_app" {
      instance_name = "app1"
      ...
    }
    

    Terraform 的 Inner DSL 是开发者使用 Golang 实现的,由两部分组成,Core 和 Plugins 。Core 通过 RPC 与 Plugins 进行通信。

    Plugins 插件负责领域实现,提供 Provider 。Core 负责解析 Outer DSL 、管理资源、管理构建、执行 plan。如下图:

    3. 如何发布 Provider

    Provider 主要是对领域的封装,直白点就是实现对 IaaS API 的封装。基于 Terraform 提供的 schema.Provider 实现鉴权、基于 schema.Resource 实现对 IaaS 资源的 CRUD 即可。

    https://registry.terraform.io/ 提供了类似 DockerHub 的托管功能,在页面上可以找到相关基础设施的 Provider 和 Modules 。Provider 通常就是 IaaS ,Modules 就是基于 Provider 的一个组件、应用等。

    • 首先需要将 Provider 发布到 Github Release 。

    主要分为如下几个步骤:

    1. 安装 goreleaser ,配置 .goreleaser.yml

    直接拷贝 terraform-provider-scaffolding 中的 .goreleaser.yml 文件到项目根目录下。goreleaser 用于项目的发布,可以同时编译多个系统版本,并发布到 GitHub 上。

    1. 配置 GPG_FINGERPRINT

    没有配置 GPG 的可以参考这篇文档,GPG 验证提交 。查看环境中的 GPG 全部秘钥:

    gpg --list-keys
    
    xxx(YOUR_GPG_ID)
    

    设置环境变量:

    export GPG_FINGERPRINT=xxx(YOUR_GPG_ID)
    

    GPG_FINGERPRINT 指向了你所使用的某条 GPG 秘钥,也是下面需要在注册页面上输入的内容。

    1. 设置 GITHUB_TOKEN

    打开 Github 的个人 配置页面 页面,勾选 public_repo 权限,生成 Token 之后:

    export GITHUB_TOKEN=xxx
    
    1. 仓库打标签
    git tag v1.2.6
    
    1. 发布 Release 版本
    goreleaser release --rm-dist
    

    最后在 Github 页面上看到是这样的效果:

    如果使用 Github Action 自动进行发布,可以增加一个文件 :

    name: goreleaser
    
    on:
      push:
        tags:
          - 'v*'
    
    jobs:
      goreleaser:
        runs-on: ubuntu-latest
        steps:
          -
            name: Checkout
            uses: actions/[email protected]
            with:
              fetch-depth: 0
          -
            name: Set up Go
            uses: actions/[email protected]
            with:
              go-version: 1.14
          -
            name: Import GPG key
            id: import_gpg
            uses: crazy-max/[email protected]
            env:
              GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
          -
            name: Run GoReleaser
            uses: goreleaser/[email protected]
            with:
              version: latest
              args: release --rm-dist
            env:
              GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
              GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
    

    RELEASE_TOKEN 就是上面的 GITHUB_TOKEN 值,而 GPG_PRIVATE_KEY 为下面命令的输出值:

    gpg --list-keys
    
    xxx(YOUR_GPG_ID)
    
    gpg --export-secret-keys --armor  xxx(YOUR_GPG_ID)
    
    • 然后准备好 GPG 秘钥,在 https://registry.terraform.io/ 页面上,使用 Github 账户登陆,选择 Provider 仓库,发布一个 Provider 。发布之后的效果如下:

    4. 如何使用 Provider Iac 应用

    在目录下新增三个文件,var.tf 、platform.tf 、install.sh 。内容大致如下:

    var.tf , 定义 provider 和全局变量。

    terraform {
      required_providers {
        qingcloud = {
          source = "shaowenchen/qingcloud"
          version = "1.2.6"
        }
      }
    }
    variable "access_key" {
      default = "yourID"
    }
    
    variable "secret_key" {
      default = "yourSecret"
    }
    
    variable "zone" {
      default = "pek3a"
    }
    
    provider "qingcloud" {
      access_key = "${var.access_key}"
      secret_key = "${var.secret_key}"
      zone = "${var.zone}"
    }
    

    platform.tf ,定义 IaaS 相关的资源。

    resource "qingcloud_eip" ...
    resource "qingcloud_security_group" ...
    resource "qingcloud_security_group_rule" ...
    resource "qingcloud_keypair" ...
    resource "qingcloud_instance" ...
    resource "null_resource" "install_platform" {
      provisioner "file" {
        destination = "./install.sh"
        source      = "./install.sh"
    
        connection {
          type        = "ssh"
          user        = "root"
          host        = "${qingcloud_eip.init.addr}"
          private_key = "${file("~/.ssh/id_rsa")}"
          port        = "22"
        }
      }
      provisioner "remote-exec" {
        inline = [
          "sh install.sh"
        ]
        connection {
          type        = "ssh"
          user        = "root"
          host        = "${qingcloud_eip.init.addr}"
          private_key = "${file("~/.ssh/id_rsa")}"
          port        = "22"
        }
      }
    

    install.sh ,在指定的 IaaS 上安装平台应用。也可以将其封装成 module ,会更加清晰。

    #!/usr/bin/env bash
    # install your application
    

    这些基础设施相关的配置文件,都需要使用 Git 进行存储和管理。每次需要创建时,只需要克隆下来,进入目录:

    terraform init
    terraform apply
    

    即可创建对应的平台应用。而执行 terraform destroy 即可销毁创建的全部资源。

    5. 参考