Terraform

Terraform 是 HashiCorp 公司旗下的 Provision Infrastructure 产品, 是 AWS APN Technology Partner 与 AWS DevOps Competency Partner。Terraform 是一个 IT 基础架构自动化编排工具,它的口号是“Write, Plan, and Create Infrastructure as Code”, 是一个“基础设施即代码”工具。

Terraform VS Yaml

相比于直接使用 Yaml 文件管理 k8s 集群,虽然 yaml 文件也能版本控制,但是可能会存在如下问题:

资源删除/重命名 后,该如何自行执行更新?

以删除资源为例,可能需要通过执行 kubectl delete -f xxx.yaml 来执行删除,而使用 terraform 管理的话,就很简单: terraform apply.

初始化

配置 Terraform backend

为了方便多人协作以及 CI,这里推荐使用 S3 作为 terraform backend,其他 backend 可以查看官方文档 .

以下配置包含了使用 aws s3 作为 state 的存储以及使用 dynamodb 作为 lock.

terraform {
  backend "s3" {
    bucket         = "bucket"
    key            = "k8s/terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "k8s"
    profile        = "profile"
  }
}

配置 k8s provider

k8s provider 配置比较简单,只需要配置 kube config 的路径:

provider "kubernetes" {
    config_path = "~/.kube/xxx"
}

如果需要同时管理 Helm,可以同时添加配置:

provider "helm" {
  kubernetes {
    config_path = "~/.kube/xxx"
  }
}

管理资源

通过 Terraform 管理 k8s 资源,在官方文档 已经很详细了,这里我列举几个常见用法.

Secret

以 secret 资源为例,常常需要配置不少不适合提交到代码仓库中的敏感信息,通过 Terraform 我们可以通过参数的形式传入变量,将敏感信息保存在执行跳板机/运维人员本地,来避免提交敏感信息到代码仓库。

以下为配置案例,首先,定义变量:

variable "AWS_SECRET_KEY" {
  description = "AWS_SECRET_KEY"
}

在使用 secret 的地方引用变量:

resource "kubernetes_secret" "aws" {
  metadata {
    name      = "aws"
    namespace = "prod"
  }

  data = {
    AWS_ACCESS_KEY        = var.AWS_ACCESS_KEY
    AWS_SECRET_KEY        = var.AWS_SECRET_KEY
  }

  type = "Opaque"
}

通过文件存储变量,创建文件/etc/terraform/prod.tfars 并包含如下信息:

AWS_SECRET_KEY="secret_key"

执行的时候,可以通过以下命令传入变量:

terraform apply -var-file=/etc/terraform/prod.tfars

Config Map 中的文件引用

Config Map 可以用来存储文件信息,并用于 Pod 的 Volume,在 Terraform 我们可以通过直接引用文件的形势,方便的管理 Config Map:

resource "kubernetes_config_map" "xxxsrv-conf" {
  metadata {
    name      = "xxxsesrv"
    namespace = "prod"
  }

  data = {
    "service.conf" = "${file("data/configmap/xxxsrv.conf")}"
  }
}

Helm

在 Terraform 管理 helm 也是很方便,以安装 cert-manager 为例,我们能通过在 Terraform 中声明好 Helm Chart 的版本,value.yaml 等信息,来方便的管理 helm 资源。

resource "helm_release" "cert-manager" {
  repository = "https://charts.jetstack.io"
  name       = "cert-manager"
  chart      = "cert-manager"
  namespace  = "cert-manager"

  version = "v1.0.0-alpha.1"

  values = [
    "${file("data/cert-manager.yaml")}"
  ]

}

CI/CD 中使用 Terraform

得利于我们将 Terraform 的 state 信息存放于 s3 等 remote backend, 我们可以方便的在本地以及 CI 中执行 terraform 命令。

由于 secret 存放于执行跳板机,以 Gitlab CI Runner 为例,我们推荐在专门的跳板机上配置好相关 tfvars 文件,并将相关 runner 用于对应的 Terraform 仓库。

完整的.gitlab-ci.yml:

# Official image for Hashicorp's Terraform. It uses light image which is Alpine
# based as it is much lighter.
#
# Entrypoint is also needed as image by default set `terraform` binary as an
# entrypoint.
image:
  name: registry.gitlab.com/gitlab-org/gitlab-build-images:terraform
  entrypoint:
    - "/usr/bin/env"
    - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Default output file for Terraform plan
variables:
  PLAN: plan.tfplan
  JSON_PLAN_FILE: tfplan.json
  ARGS: -var-file=/etc/terraform/prod.tfvars

cache:
  paths:
    - .terraform

before_script:
  - shopt -s expand_aliases
  - alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
  - terraform --version
  - terraform init

stages:
  - validate
  - build
  - test
  - deploy

validate:
  stage: validate
  tags:
    - ops
    - terraform
  script:
    - terraform validate

plan:
  stage: build
  script:
    - terraform plan $ARGS -out=$PLAN
    - terraform show --json $PLAN | convert_report > $JSON_PLAN_FILE
  tags:
    - ops
    - terraform
  artifacts:
    name: plan
    paths:
      - $PLAN
    reports:
      terraform: $JSON_PLAN_FILE

# Separate apply job for manual launching Terraform as it can be destructive
# action.
apply:
  stage: deploy
  environment:
    name: production
  script:
    - terraform apply -input=false $PLAN
  tags:
    - ops
    - terraform
  dependencies:
    - plan
  when: manual
  only:
    - master

缺陷

Terraform 目前还不支持 CRD,虽然官方 Blog 有提到在 alpha 测试,但是距离 GA 还有一段时间吧~


comments powered by Disqus