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 还有一段时间吧~