Giới thiệu
Trong bài này chúng ta sẽ tìm hiểu về cách sử dụng Gitlab CI để xây dựng luồng CI/CD cho Terraform.
Gitlab CI là một tính năng rất tuyệt vời của Gitlab, nó hỗ trợ ta rất nhiều trường hợp về CI/CD. Để làm được bài này thì yêu cầu mọi người cần có tài khoản Gitlab. Ta sẽ làm một ví dụ đơn giản là tạo EC2 trên AWS thông qua Gitlab CI.
GitLab CI
Bước đầu tiên ta tạo Gitlab Repository với 3 tệp tin sau:
.gitlab-ci.yml
main.tf
variables.tf
Code của tệp tin variables.tf
và main.tf
.
variable "region" {
default = "us-west-2"
}
variable "instance_type" {
default = "t3.micro"
}
provider "aws" {
region = var.region
}
data "aws_ami" "ami" {
most_recent = true
filter {
name = "name"
values = ["amzn2-ami-hvm-2.0.*-x86_64-gp2"]
}
owners = ["amazon"]
}
resource "aws_instance" "server" {
ami = data.aws_ami.ami.id
instance_type = var.instance_type
tags = {
Name = "Server"
}
}
Ở trên là đoạn code đơn giản dùng để tạo EC2 trên AWS. Tệp tin quan trọng mà ta cần tìm hiểu ở bài này là tệp tin .gitlab-ci.yml
, đây là tệp tin chứa các câu lệnh hướng dẫn cho luồng CI/CD.
stages:
- plan
- apply
image:
name: hashicorp/terraform
entrypoint:
- "/usr/bin/env"
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
cache:
paths:
- .terraform.lock.hcl
- terraform.tfstate
before_script:
- terraform init
plan:
stage: plan
script:
- terraform plan -out "planfile"
artifacts:
paths:
- planfile
apply:
stage: apply
script:
- terraform apply -input=false "planfile"
dependencies:
- plan
when: manual
Mình sẽ giải thích một số phần quan trọng của tệp tin .gitlab-ci.yml
ở trên. Các bạn tham khảo thêm ở đây Gitlab CI.
Gitlab Stages
stages:
- plan
- apply
Phần này định nghĩa cho Gitlab CI biết nó cần chạy bao nhiêu phân đoạn (tên ta đặt tùy ý), ở trên ta chỉ định nó cần chạy hai phân đoạn plan
và apply
. Trong từng phân đoạn ta sẽ định nghĩa các câu lệnh được thực thi bởi Gitlab CI. Ta gọi từng phân đoạn trên là Job.
Cấu hình Image mặc định
Tiếp theo ta cấu hình cho toàn bộ Job sẽ được chạy trong Image hashicorp/terraform
.
image:
name: hashicorp/terraform
entrypoint:
- "/usr/bin/env"
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Init
Sau đó ta dùng before_script
để init
các tài nguyên cần thiết cho việc chạy Terraform.
before_script:
- terraform init
Plan
Tiếp theo ta chạy câu lệnh plan
để xem trước các resource sẽ được tạo.
plan:
stage: plan
script:
- terraform plan -out "planfile"
artifacts:
paths:
- planfile
Phần artifacts
ta dùng để xuất các tệp tin ta cần truyền từ Job này qua Job khác.
Apply
Cuối cùng là phần apply
để ta tạo resource.
apply:
stage: apply
script:
- terraform apply -input=false "planfile"
dependencies:
- plan
when: manual
Vì apply
là phần quan trọng nên ta thêm cho nó thuộc tính when: manual
. Thuộc tính này báo cho Gitlab CI biết là phân đoạn này cần ta bấm thủ công.
Cache
Để lưu lại Terraform State ta dùng thuộc tính cache.paths
.
cache:
paths:
- .terraform.lock.hcl
- terraform.tfstate
Note: lưu ý là khi làm thực tế ta nên sử dụng S3 backend để lưu Terraform State. Các bạn xem bài S3 Backend để hiểu rõ hơn Bài 8 - Sử dụng S3 Standard Backend vào dự án.
Execute Gitlab CI
Ta commit
và push
code lên trên Gitlab. Truy cập Gitlab và mở mục CI/CD.
Ta sẽ thấy pipeline
của ta chạy không thành công, bấm qua mục Job để kiểm tra tại sao.
Đây là logs mà Gitlab CI in ra.
$ terraform plan -out "planfile"
╷
│ Error: error configuring Terraform AWS Provider: no valid credential sources for Terraform AWS Provider found.
│
│ Please see https://registry.terraform.io/providers/hashicorp/aws
│ for more information about providing credentials.
│
│ Error: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds: GetMetadata, http response error StatusCode: 404, request to EC2 IMDS failed
│
│
│ with provider["registry.terraform.io/hashicorp/aws"],
│ on main.tf line 1, in provider "aws":
│ 1: provider "aws" {
│
╵
Lỗi này là do ta chưa cấu hình AWS Credentials, các bạn tạo IAM User với quyền là Administrator
theo hướng dẫn ở đây IAM User Admin. Sau đó tạo Access Key và Secret Key, lấy giá trị đó cấu hình vào hai biến môi trường là AWS_ACCESS_KEY_ID
và AWS_SECRET_ACCESS_KEY
.
Bật qua mục Settings -> CI/CD, kéo xuống phần Variables và thêm vào hai biến trên.
Chạy lại pipeline
và ta sẽ thấy nó chạy thành công. Tiếp theo là bước apply
, hiện tại nó đang ở chế độ manual
và chờ ta tự bấm.
Ta kích hoạt Gitlab CI bằng tay để nó chạy phần apply
.
Vậy là ta đã thực hiện CI/CD cho Terraform với Gitlab CI thành công.
Tổ chức các môi trường
Thông thường khi làm thực tế ta sẽ chia một Repository ra làm nhiều nhánh khác nhau, và tương ứng với từng nhánh thì ta sẽ triển khai nó lên một môi trường cụ thể.
Ví dụ, ta có hai môi trường là dev và pro, nhánh dev ta sẽ triển khai cho môi trường dev, và nhánh pro ta sẽ triển khai cho môi trường pro. Ta làm việc đó với Terraform như thế nào?
Terraform Workspaces
Workspaces là một tính năng của Terraform cho phép ta lưu nhiều Terraform State khác nhau trên cùng một source code. Có nghĩa là ta chỉ cần dùng một source code để triển khai lên nhiều môi trường khác nhau, thay vì với mỗi môi trường ta lại phải tạo một source code cho nó.
Và mỗi Workspace sẽ sử dụng biến khác nhau để tạo nên các hạ tầng khác nhau, đó là lý do tại sao ta nên sử dụng biến cho các thuộc tính mà có thể thay đổi tùy thuộc vào môi trường.
Ví dụ ở trên.
resource "aws_instance" "server" {
ami = data.aws_ami.ami.id
instance_type = var.instance_type
tags = {
Name = "Server"
}
}
Thay vì để cứng giá trị instance_type
thì ta nên đặt nó vào trong biến.
Workspaces
Khi ta chạy câu lệnh terraform init
thì Terraform đã tạo cho ta một Workspace tên là default
, liệt kê toàn bộ workspace hiện tại:
terraform workspace list
* default
Để tạo một Workspace mới ta chạy câu lệnh terraform workspace new <name>
, ví dụ tạo workspace dev và pro:
terraform workspace new dev
terraform workspace new pro
Chạy lại câu lệnh workspace list
:
terraform workspace list
* default
dev
pro
Khi ta chạy câu lệnh new
thì terraform sẽ tạo ra cho ta một thư mục là terraform.tfstate.d
, trong đó sẽ có hai thư mục con là dev và pro.
.
├── main.tf
├── terraform.tfstate.d
│ ├── dev
│ └── pro
└── variables.tf
Hai thư mục con này là nơi Terraform chứa tệp tin State cho các Workspace khác nhau. Để chuyển qua lại giữa các Workspace, ta dùng câu lệnh select
:
terraform workspace select dev
Switched to workspace "dev".
Sau khi tổ chức Workspace xong thì làm sao để apply
được chính xác variable
cho từng môi trường cụ thể?
Multiple environments
Ta có thể làm việc đó bằng cách khi ta chạy apply thì ta truyền thêm option -var-file
vào, ví dụ.
terraform apply -var-file=dev.tfvars -auto-approve
Tiếp theo ta áp dụng Workspace vào Gitlab CI, trước tiên các bạn tạo thêm hai nhánh dev và pro.
Quan trọng: nhớ cấu hình cho hai nhánh dev và pro là protected
.
Sau đó ta tạo thêm một thư mục tên là env
và tạo hai tệp tin dev.tfvars
và pro.tfvars
.
region = "us-west-2"
instance_type = "t3.micro"
region = "ap-southeast-1"
instance_type = "t3.small"
Tiếp theo để có thể đẩy được hai thư mục con rỗng trong terraform.tfstate.d
lên Gitlab thì ta thêm tệp tin .gitkeep
vào, cấu trúc thư mục hiện tại.
.
├── .gitlab-ci.yml
├── env
│ ├── dev.tfvars
│ └── pro.tfvars
├── main.tf
├── terraform.tfstate.d
│ ├── dev
│ │ └── .gitkeep
│ └── pro
│ └── .gitkeep
└── variables.tf
Sau đó ta cập nhật lại .gitlab-ci.yml
như sau:
stages:
- plan
- apply
image:
name: hashicorp/terraform
entrypoint:
- "/usr/bin/env"
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
cache:
paths:
- .terraform.lock.hcl
- terraform.tfstate.d/*
before_script:
- terraform init
- terraform workspace select $CI_COMMIT_REF_NAME
plan:
stage: plan
script:
- terraform plan -var-file=env/$CI_COMMIT_REF_NAME.tfvars -out "planfile"
artifacts:
paths:
- planfile
only:
- dev
- pro
apply:
stage: apply
script:
- terraform apply -input=false "planfile"
dependencies:
- plan
when: manual
only:
- dev
- pro
Giờ thì khi bạn merge
code vào nhánh dev thì Gitlab CI sẽ chạy và triển khai hạ tầng cho môi trường dev, tương tự cho môi trường pro.
Nhắc lại: lưu ý là khi làm thực tế ta nên sử dụng S3 Backend để lưu Terraform State.
Mình đã chia hai phần ra thành hai Gitlab Repository khác nhau để các bạn có thể dễ dàng tham khảo.
- https://gitlab.com/hoalongnatsu/terraform-series
- https://gitlab.com/hoalongnatsu/terraform-series-workspace
Kết luận
Vậy là ta đã tìm hiểu xong về cách dùng Gitlab CI với Terraform. Gitlab CI là một công cụ CI/CD phổ biến và được sử dụng rất nhiều khi làm CI/CD cho Terraform.
Tác giả Quân Huỳnh
Nếu bài viết có gì sai hoặc cần cập nhật thì liên hệ Admin.
Tham gia nhóm chat của DevOps VN tại Telegram.
Kém tiếng Anh và cần nâng cao trình độ giao tiếp: Tại sao bạn học không hiệu quả?