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.
Mục lục
- GitLab CI
- Execute Gitlab CI
- Tổ chức các môi trường
- Terraform Workspaces
- Kết luận
- Bài viết cùng series “Chinh phục 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.ymlmain.tfvariables.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.
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
- applyPhầ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 initPlan
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:
- planfilePhầ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: manualVì 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.tfstateNote: 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.
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 proChạy lại câu lệnh workspace list:
terraform workspace list* default
dev
proKhi 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.tfHai 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 devSwitched 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-approveTiế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.tfSau đó ta cập nhật lại .gitlab-ci.yml như sau:
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
First AI Journey for DevOps:
- PromptOps: From YAML to AI
- The DevOps AI Advantage
- The AIOps Book