Terraform项目的.gitlab-ci.yml模板
你好。
我們在GitLab上管理Terraform的代碼,所以現在談談我們創建的.gitlab-ci.yml文件。
本篇文章的动机是因为我觉得如果能够在每个环境中使用terraform plan和terraform apply,就可以满足基本的持续集成功能,并且可能还可以实现模板化。
.gitlab-ci.yml是什么?
GitLab Runner使用的文件,用于管理项目的作业。
将其放置在项目的根目录中,即可设置CI。
项目结构
在这个简单的项目中,我们启动 VPC 并在其中创建两台 EC2 作为开发和生产环境。我们将使用 vpc/example.tf 文件定义 VPC,并使用 environment/example.tf 文件来管理 EC2 的 terraform 代码。
将 .gitlab-ci.yml 文件放置在项目的根目录中。
├── environment
│ ├── example.tf
├── vpc
│ └── example.tf
├──.gitlab-ci.yml
用于terraform的代码
vpc/例子.tf
这是用于 VPC 构建的 Terraform 代码。
locals {
name = "tf-for-gitlab"
}
terraform {
backend "s3" {
bucket = "t-horikoshi-bucket"
key = "example.tfstate"
region = "ap-northeast-1"
}
}
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
tags {
Name = "${local.name}"
}
}
resource "aws_security_group" "example" {
name = "${local.name}"
vpc_id = "${aws_vpc.example.id}"
tags {
Name = "${local.name}"
}
}
output "security-group-ids" {
value = ["${aws_security_group.example.id}"]
}
output "vpc_id" {
value = "${aws_vpc.example.id}"
}
在创建 EC2 实例时,需要 VPC 的相关信息,因此将 VPC 的 ID 和安全组 ID 作为输出,输出到 example.tfstate 文件中。
在backend上指定了tfstate文件保存在S3上的信息。(请先假设S3存储桶已经事先创建。)
环境/示例.tf
以下是使用 Terraform 的代码来在 VPC 中启动 EC2 实例。
locals {
stage = "${terraform.workspace == "prod" ? "prod" : "dev"}"
}
data "terraform_remote_state" "example" {
backend = "s3"
config {
bucket = "t-horikoshi-bucket"
key = "example.tfstate"
region = "ap-northeast-1"
}
}
resource "aws_subnet" "example-a" {
cidr_block = "10.0.1.0/24"
vpc_id = "${data.terraform_remote_state.example.vpc_id}"
tags {
Name = "example-a"
}
}
resource "aws_instance" "example" {
ami = "${var.ami}"
instance_type = "t2.micro"
subnet_id = "${aws_subnet.example-a.id}"
vpc_security_group_ids = ["${data.terraform_remote_state.example.security-group-ids}"]
tags {
Name = "tf-for-gitlab-${local.stage}"
}
}
variable "ami" {
type ="string"
default = "ami-0a2de1c3b415889d2"
}
这次我们将使用 terraform workspace 来实现 dev 和 prod 两个环境的划分。
我们将通过读取 locals 的 stage 变量来选择工作空间。
为了能够读取在之前创建 VPC 时设置的 S3 和 tfstate 文件的信息,将其配置。
将aws_subnet中的vpc_id和aws_instance中的security-group-ids分别进行配置。
.gitlab-ci.yml 的中文翻译如下:
image:
name: hashicorp/terraform:0.11.11
entrypoint: [""]
.artifacts: &artifacts
paths:
- 'vpc/.terraform'
- 'environment/.terraform'
stages:
- fmt
- init
- plan
- apply
fmt:
stage: fmt
script:
- cd vpc && terraform fmt -check=true
- cd ../environment && terraform fmt -check=true
init:
stage: init
script:
- cd vpc && terraform init
- cd ../environment && terraform init
artifacts: *artifacts
plan_vpc:
stage: plan
script:
- cd vpc && terraform plan
artifacts: *artifacts
.plan_env_job: &plan_env_job
stage: plan
script:
- cd environment
- terraform workspace new $ENV && terraform workspace select $ENV && terraform plan
artifacts: *artifacts
plan_env_dev:
<<: *plan_env_job
variables:
ENV: dev
plan_env_prod:
<<: *plan_env_job
variables:
ENV: prod
apply_vpc:
stage: apply
script:
- cd vpc && terraform apply -auto-approve
when: manual
artifacts: *artifacts
only:
- master
.apply_env_job: &apply_env_job
stage: apply
script:
- cd environment
- terraform workspace select $ENV && terraform apply -auto-approve
when: manual
artifacts: *artifacts
apply_env dev:
<<: *apply_env_job
variables:
ENV: dev
apply_env prod:
<<: *apply_env_job
variables:
ENV: prod
only:
- master
从下面开始,我们将详细查看。
图片
image:
name: hashicorp/terraform:0.11.11
entrypoint: [""]
指定 HashiCorp 管理的 Docker 镜像。
默认情况下,entrypoint 指定了 terraform,因此无法在容器内运行 Pipeline 的工作。因此,我们需要进行覆盖。
由于Terraform更新迅速,建议指定版本。
艺术品
.artifacts: &artifacts
paths:
- 'vpc/.terraform'
- 'environment/.terraform'
这是为了使 vpc 和 environment 的成果在 stage 之间可以被使用而进行的设置。我们指定了 terraform init 和 terraform apply 之后的成果物将被输出到 .terraform 目录中。
阶段
stages:
- fmt
- init
- plan
- apply
在Pipeline中设置每个阶段。
这是terraform项目,所以它的结构是fmt > init > plan > apply。
格式化
fmt:
stage: fmt
script:
- cd vpc && terraform fmt -check=true
- cd ../environment && terraform fmt -check=true
这个阶段是用来检查Terraform代码格式的。
我们通过使用-check=true来确保如果有格式问题,会使其失败。
开始
init:
stage: init
script:
- cd vpc && terraform init
- cd ../environment && terraform init
artifacts: *artifacts
这是一个用于初始化 VPC 和环境的 stage。
计划
plan_vpc:
stage: plan
script:
- cd vpc && terraform plan
artifacts: *artifacts
.plan_env_job: &plan_env_job
stage: plan
script:
- cd environment
- terraform workspace new $ENV || terraform workspace select $ENV && terraform plan
artifacts: *artifacts
plan_env_dev:
<<: *plan_env_job
variables:
ENV: dev
plan_env_prod:
<<: *plan_env_job
variables:
ENV: prod
此阶段正在执行terraform plan。
由于只有一个vpc,所以只有一个作业。但是在环境中,我们希望将开发和生产环境分开建立。因此,我们定义了plan_env_dev和plan_env_prod两个作业。
通过使用YAML锚点功能,创建了一个名为.plan_env_job的共享工作。
使用ENV使每个工作区能够执行工作。
申请
apply_vpc:
stage: apply
script:
- cd vpc && terraform apply -auto-approve
when: manual
artifacts: *artifacts
only:
- master
.apply_env_job: &apply_env_job
stage: apply
script:
- cd environment
- terraform workspace select $ENV && terraform apply -auto-approve
when: manual
artifacts: *artifacts
apply_env dev:
<<: *apply_env_job
variables:
ENV: dev
apply_env prod:
<<: *apply_env_job
variables:
ENV: prod
only:
- master
基本上,環境分為不同部分並且使用錨點定義作業等,與計劃的機制相同。
当运行 terraform apply 时,实际的配置将会开始部署,为了手动执行,我们使用了 when: manual 指令。
另外,我們限定了只能在主分支(master)上執行工作並指定了 “only”。
在Gitlab中设置环境变量。
因为不希望将 AWS 凭证等安全信息提交到代码中,所以将其配置到 Gitlab 的环境变量中。

已完成的工作流程
将此前的代码推送到 git 仓库后,会创建如下所示的 Pipeline。

总结
這次介紹的 Gitlab 的 CI/CD 功能還只是初步接觸而已。因為還有其他看起來很實用的功能,所以希望可以善用並掌握。
另外,手动管理基础设施的麻烦是众所周知的事实,但是如果有代码管理,就会感到放心。
那么,请享受基础设施即代码吧!