使用Terraform管理k8s集群

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.

1
2
3
4
5
6
7
8
9
10
terraform {
backend "s3" {
bucket = "bucket"
key = "k8s/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "k8s"
profile = "profile"
}
}

配置 k8s provider

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

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

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

1
2
3
4
5
provider "helm" {
kubernetes {
config_path = "~/.kube/xxx"
}
}

管理资源

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

Secret

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

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

1
2
3
variable "AWS_SECRET_KEY" {
description = "AWS_SECRET_KEY"
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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 并包含如下信息:

1
AWS_SECRET_KEY="secret_key"

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

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

Config Map 中的文件引用

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

1
2
3
4
5
6
7
8
9
10
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 资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 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 还有一段时间吧~