Skip to main content

Terraform

  • opentofu/opentofu
    • OpenTofu
  • functions
  • 模板语法 string-templates
  • Provisioners
    • 本地或远程服务器执行特定动作
    • 用于准备服务或其他基础设施对象
    • 不建议使用,作为最后的方式
  • 注意
    • Provider configurations can be defined only in a root Terraform module.
    • 被调用模块不能定义 provider
    • 0.10 旧的模块不支持 for_each, count, depends_on
    • 移除 provider 之前确保所有资源删除
    • 模块会集成默认 provider - 没有别名的 provider
    • 如果发现网络不通,确保本地可以打开 https://registry.terraform.io/.well-known/terraform.json
  • 转换函数
    • yamldecode
  • 后端
    • local - 本地存储 terraform.tfstate
    • remote - Terraform Enterprise
    • artifactory - 无锁
    • consul
    • etcdv3
    • http - 可选锁 - REST 接口
    • kubernetes - secret 限制了最大 1MB - 不建议使用
    • 阿里云 oss、腾讯云 cos
    • pg
    • s3 - DynamoDB 支持锁
# 日志
TF_LOG=1 terraform apply

配置

terraform {
backend "local" {}

# experiments = [example]

required_providers {
# aws = ">= 2.7.0"

aws = {
version = ">= 2.7.0"
}
}
}

terraformrc

cat <<HCL > ~/.terraformrc
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
disable_checkpoint = true

provider_installation {
filesystem_mirror {
path = "$HOME/.terraform.d/plugins"
include = ["terraform.wener.me/*/*","registry.terraform.io/*/*"]
}
}
HCL

mkdir -p $HOME/.terraform.d/plugin-cache $HOME/.terraform.d/plugins

# 在 tf 项目下运行
terraform providers mirror ~/.terraform.d/plugins

变量

  • 输入变量
    • 使用变量必须先定义变量
    • 读取顺序
      • 环境变量
      • 变量文件 terraform.tfvars terraform.tfvars.json
        • HCL 或 JSON
      • 变量文件 *.auto.tfvars *.auto.tfvars.json
      • 参数 -var, -var-file
    • 会检测环境变量,例如 name 则会使用 TF_VAR_name
  • 本地变量
    • 直接写在文件里的变量
    • 可重复使用
  • 输出变量
    • 类似于一个模块的返回值
    • 子模块可通过输出变量暴露信息给上级
    • root 模块可输出到命令行
    • 当使用远程状态时,root 模块输出变量能够被其他配置访问到, terraform_remote_state
terraform {
# 开启变量校验
experiments = [variable_validation]
}

variable "gitlab_token" {
# 简单类型
# string number bool
# 复杂类型
# list(<TYPE>) set(<TYPE>) map(<TYPE>) object({<ATTR NAME> = <TYPE>, ... }) tuple([<TYPE>, ...])
type = string
# default = ''
description = "token for gitlab access"

validation {
condition = length(var.gitlab_token) > 0 && substr(var.gitlab_token, 0, 4) == "ami-"
# condition = can(regex("^ami-", var.image_id))
error_message = "Invalid token"
}
}
provider "gitlab" {
token = var.gitlab_token
}

# 本地变量
locals {
service_name = "forum"
owner = "Community Team"

instance_ids = concat(aws_instance.blue.*.id, aws_instance.green.*.id)

# 通过 local.common_tags 方式引用
common_tags = {
Service = local.service_name
Owner = local.owner
}
}

# 输出变量
output "instance_ip_addr" {
value = aws_instance.server.private_ip
}

output "db_password" {
value = aws_db_instance.db.password
description = "The password for logging in to the database."
sensitive = true
}

output "instance_ip_addr" {
value = aws_instance.server.private_ip
description = "The private IP address of the main server instance."

depends_on = [
# Security group rule must be created before this IP address could
# actually be used, otherwise the services will be unreachable.
aws_security_group_rule.local_access,
]
}

后端

  • Enhanced - 可存储状态和执行操作
    • local, remote
  • Standard - 远程存储,依赖 local 执行
    • consul, etcd, etcdv3
    • artifactory, pg, swift, http
    • azurerm, gcs, cos, oss, manta
    • s3
      • 通过 Dynamo DB 可支持 locking 和 一致性检查
      • 建议开启版本
    • kubernetes - 存储为 secret, 最多 1MB 限制
      • tfstate-{workspace}-{secret_suffix}
  • 特性支持
    • State Locking
      • 避免并发操作
      • remote, sql, s3+dynamo, kubernetes
terraform {
backend "kubernetes" {
# tfstate-{workspace}-state
secret_suffix = "state"
# ~/.kube/config
load_config_file = true
config_context = "demo"
# 默认使用 context 关联 ns
namespace = "demo"
}

# remote
backend "http" {
}
}

GitLab Terraform State Backend

STATE_NAME=staging
PROJECT_ID=
USERNAME=
PTA=
terraform init \
-backend-config="address=https://gitlab.com/api/v4/projects/$PROJECT_ID/terraform/state/$STATE_NAME" \
-backend-config="lock_address=https://gitlab.com/api/v4/projects/$PROJECT_ID/terraform/state/$STATE_NAME/lock" \
-backend-config="unlock_address=https://gitlab.com/api/v4/projects/$PROJECT_ID/terraform/state/$STATE_NAME/lock" \
-backend-config="username=$USERNAME" \
-backend-config="password=$PTA" \
-backend-config="lock_method=POST" \
-backend-config="unlock_method=DELETE" \
-backend-config="retry_wait_min=5"
image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest

variables:
TF_ROOT: ${CI_PROJECT_DIR}/environments/example/production
TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/example-production

cache:
key: example-production
paths:
- ${TF_ROOT}/.terraform

before_script:
- cd ${TF_ROOT}

stages:
- prepare
- validate
- build
- deploy

init:
stage: prepare
script:
- gitlab-terraform init

validate:
stage: validate
script:
- gitlab-terraform validate

plan:
stage: build
script:
- gitlab-terraform plan
- gitlab-terraform plan-json
artifacts:
name: plan
paths:
- ${TF_ROOT}/plan.cache
reports:
terraform: ${TF_ROOT}/plan.json

apply:
stage: deploy
environment:
name: production
script:
- gitlab-terraform apply
dependencies:
- plan
when: manual
only:
- master