Giới thiệu
Ở bài trước chúng ta đã tìm hiểu về vòng đời của một resource trong terraform. Ở bài này chúng ta sẽ tìm hiểu về cách làm sao để lập trình trong Terraform.
Mục lục
- Tạo EC2
- Khai báo biến đầu vào
- Gán giá trị cho biến
- Kiểm tra lỗi của biến
- Giá trị đầu ra
- Thuộc tính count
- Biểu thức For
- Hàm Format
- Kết luận
- Bài viết cùng series “Chinh phục Terraform”
Terraform hỗ trợ ta lập trình theo kiểu Functional Programming.
Tạo EC2
Ta sẽ làm ví dụ về EC2 để tìm hiểu về các khái niệm lập trình trong Terraform. Tạo một file tên là main.tf
Chạy terraform init và terraform apply, sau đó lên AWS ta sẽ thấy EC2 của ta. Với đoạn code trên thì EC2 của ta luôn luôn có instance_type là t2.micro, nếu ta muốn tạo lại EC2 mà có instance_type khác thì sao? Ta sẽ sửa lại code trong tệp tin Terraform? Vậy thì không linh hoạt cho lắm, thay vào đó ta sẽ sử dụng biến (được gọi là variable trong lập trình) để làm việc này.
Khai báo biến đầu vào
Ta có thể định nghĩa biến cho Terraform với cú pháp như bên dưới:
Đây là cú pháp variable block để khai báo biến. Ở ví dụ trên, ta tạo thêm một tệp tin nữa với tên là variable.tf (này bạn đặt tên gì cũng được nha) để khai báo biến của ta.
variable "instance_type" {
type = string
description = "Instance type of the EC2"
}Thuộc tính là type để chỉ định kiểu dữ liệu của biến đó, thuộc tính description dùng để ghi lại mô tả cho người đọc biến đó biết nó có ý nghĩa gì. Chỉ có thuộc tính type là bắt buộc phải khai báo. Trong Terraform thì một biến sẽ có các kiểu dữ liệu sau:
- Basic Type: string, number, bool
- Complex Type: list(), set(), map(), object(), tuple()
Trong Terraform, kiểu dữ liệu number và bool sẽ được chuyển thành kiểu dữ liệu string khi cần thiết. Nghĩa là 1 sẽ thành "1", true sẽ thành "true".
Ta dùng cú pháp var.<VARIABLE_NAME> để truy cập được giá trị của biến, cập nhật lại tệp tin main.tf.
Ở thuộc tính instance_type thay vì gán cứng thì bây giờ ta sẽ dùng biến var.instance_type.
Gán giá trị cho biến
Để gán giá trị cho biến, ta sẽ tạo một tệp tin tên là terraform.tfvars.
instance_type = "t2.micro"Khi ta chạy terraform apply thì tệp tin terraform.tfvars sẽ được Terraform sử dụng để tải giá trị mặc định cho biến, nếu ta không muốn dùng mặc định, thì khi chạy câu lệnh apply ta thêm vào thuộc tính là -var-file. Tạo một tệp tin tên là production.tfvars.
instance_type = "t3.small"Khi chạy CI/CD cho production, ta chỉ định tệp tin như sau:
terraform apply -var-file="production.tfvars"Bây giờ thì giá trị instance_type của ta linh hoạt hơn nhiều.
Kiểm tra lỗi của biến
Ta cũng có thể định nghĩa biến này chỉ có thể được gán những giá trị mà ta cho phép bằng cách sử dùng thuộc tính validating, như sau:
variable "instance_type" {
type = string
description = "Instance type of the EC2"
validation {
condition = contains(["t2.micro", "t3.small"], var.instance_type)
error_message = "Value not allow."
}
}Ở tệp tin trên ta sẽ dùng hàm contains để kiểm tra giá trị của biến instance_type này chỉ được nằm trong mảng ta cho phép, nếu không thì khi ta chạy câu lệnh apply ta sẽ thấy lỗi ở trường error_message. Sửa lại tệp tin terraform.tfvars.
instance_type = "t3.micro"Chạy apply.
terraform apply╷
│ Error: Invalid value for variable
│
│ on variable.tf line 1:
│ 1: variable "instance_type" {
│
│ Value not allow.
│
│ This was checked by the validation rule at variable.tf:5,3-13.
╵Sử dụng validating để kiểm soát giá trị của biến mà bạn muốn. Sửa lại tệp tin terraform.tfvars như cũ nhé. Thông thường khi tạo EC2 xong, ta sẽ muốn xem IP của nó, để làm được việc đó thì ta sử dụng output block.
Giá trị đầu ra
Giá trị của output block sẽ được in ra Terminal, cú pháp như sau:
Để in được giá trị public_ip của EC2, ta thêm vào tệp tin main.tf đoạn code sau:
...
output "ec2" {
value = {
public_ip = aws_instance.hello.public_ip
}
}Bạn chạy lại câu lệnh apply, ta sẽ thấy giá trị IP của EC2 được in ra Terminal.
terraform apply -auto-approve...
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
ec2 = {
"public_ip" = "52.36.124.230"
}Bây giờ thì ta đã biết cách sử dụng biến và output. Tiếp theo, nếu ta muốn thêm một EC2 nữa thì sao? Trong tệp tin main.tf ta sẽ sao chép ra thêm một EC2 nữa.
Ta sẽ thêm một resource block cho EC2 thứ hai, và ở phần output, ta cập nhật lại để nó có thể in ra được IP của hai con EC2. Mọi thứ đều không có gì phức tạp hết, nhưng nếu giờ ta muốn tạo 100 con EC2 thì sao? Ta có thể sao chép ra 100 resource block, nhưng không ai làm vậy cả 😂, mà ta sẽ sử dụng thuộc tính count.
Thuộc tính count
Thuộc tính count là một Meta Argument, là một thuộc tính trong Terraform chứ không phải của resource type thuộc Provider, ở bài 1 ta đã nói resource type chỉ có chứa các thuộc tính mà Provider cung cấp, còn Meta Argument là thuộc tính của Terraform => nghĩa là ta có thể sử dụng nó ở bất kì resource block nào. Cập nhật lại tệp tin main.tf mà sẽ tạo ra 5 EC2.
Bây giờ thì khi ta chạy apply, Terraform sẽ tạo ra cho ta 5 con EC2. Bạn để ý là ở phần output, để truy cập được resource thì ta sẽ dùng thêm dấu [] và giá trị Index của resource. Bình thường để truy cập được resource ta dùng theo cú pháp <RESOURCE TYPE>.<NAME>, nhưng khi ta dùng count thì ta sẽ truy cập resource theo cú pháp <RESOURCE TYPE>.<NAME>[index].
Bây giờ ta đã giải quyết được vấn đề sao chép resource ra khi cần tạo nó với số lượng lớn, nhưng ở phần output ta vẫn phải ghi ra từng resource riêng lẻ. Ta sẽ giải quyết nó bằng cách sử dụng biểu thức for.
Biểu thức For
For cho phép ta duyệt qua một danh sách, cú pháp của lệnh for:
for <value> in <list> : <return value>Ví dụ dùng for:
- Tạo ra một mảng mới với giá trị của mảng mới sẽ được viết hoa:
[for s in var.words : upper(s)] - Tạo ra một đối tượng mới với giá trị của đối tượng mới được viết hoa:
{ for k, v in var.words : k => upper(s) }
Ta sẽ dùng for để rút gọn phần output của EC2. Cập nhật lại tệp tin main.tf
...
resource "aws_instance" "hello" {
count = 5
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
}
output "ec2" {
value = {
public_ip = [ for v in aws_instance.hello : v.public_ip ]
}
}Phần output trên sẽ in ra giá trị public_ip là một mảng IP của tất cả EC2 được tạo ra. Còn nếu bạn muốn in output ra theo kiểu { public_ip1: <value>, public_ip2: <value> } thì ta có thể dùng hàm format.
Hàm Format
Hàm format sẽ giúp ta nối chuỗi, cập nhật output lại như sau:
...
resource "aws_instance" "hello" {
count = 5
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
}
output "ec2" {
value = { for i, v in aws_instance.hello : format("public_ip%d", i + 1) => v.public_ip }
}Chạy terraform plan để kiểm tra ta sẽ thấy output lúc này sẽ là dạng { public_ip1: <value>, public_ip2: <value> }.
terraform planBây giờ thì định đạng của giá trị đầu ra dễ đọc hơn nhiều.
Kết luận
Vậy là ta đã tìm hiểu xong về một số cách đơn giản để lập trình trong Terraform. Sử dụng varibale để chứa biến, sử dụng output để hiển thị giá trị đầu ra, sử dụng for để duyệt qua mảng. Ở bài tiếp theo ta sẽ tìm hiểu thêm một vài hàm thông qua ví dụ dùng Terraform để triển khai một trang Web lên S3.
Tác giả @Quân Huỳnh
First AI Journey for DevOps:
- PromptOps: From YAML to AI
- The DevOps AI Advantage
- The AIOps Book