Giới thiệu
Ở bài trước chúng ta đã nói về lý thuyết của Terraform Backend. Ở bài này chúng ta sẽ thực hành sử dụng Terraform Standard Backend, cụ thể là S3 Standard Backend. Ta sẽ tìm hiểu Terraform S3 Backend sẽ bao gồm các thành phần gì, tạo nó ra sao và ứng dụng nó vào dự án của ta thế nào.
Hình minh họa S3 Standard Backend.
Triển khai S3 Backend
Kiến trúc
Trước khi sử dụng S3 Backend thì ta cần phải tạo nó trước, cấu trúc của một S3 Backend gồm những thành phần:
- IAM
- DynamoDB
- S3 bucket - KMS
Từng thành phần trên được sử dụng như sau:
- IAM được sử dụng để Terraform Assume Role, cấp cho Terraform có quyền ghi vào Dynamodb Table và
fetch/store
State vào bên trong S3. - Dynamodb được Terraform dùng để ghi
Lock Key
của một Process. - S3 Bucket dùng để lưu trữ State khi Terraform chạy xong, KMS được S3 sử dụng để mã hóa dữ liệu State khi nó được lưu vào bên trong S3.
Triển khai
Giờ thì ta sẽ tiến hành tạo S3 Backend, các resource mà ta sẽ sử dụng để tạo S3 Backend như hình bên dưới.
Tạo một một thư mục và tệp tin main.tf
+ variables.tf
+ versions.tf
với nội dung.
provider "aws" {
region = var.region
}
variable "region" {
type = string
default = "us-west-2"
}
variable "project" {
description = "The project name to use for unique resource naming"
default = "terraform-series"
type = string
}
variable "principal_arns" {
description = "A list of principal arns allowed to assume the IAM role"
default = null
type = list(string)
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
Sau đó chạy câu lệnh terraform init
. Oke, vậy là bước chuẩn bị đã xong, tiếp theo ta tạo tệp tin dynamodb.tf
.
resource "aws_dynamodb_table" "dynamodb_table" {
name = "${var.project}-s3-backend"
hash_key = "LockID"
billing_mode = "PAY_PER_REQUEST"
attribute {
name = "LockID"
type = "S"
}
tags = local.tags
}
Đây là DynamoDB Table mà Terraform dùng để lưu Lock State, ta định nghĩa Table này sẽ có một trường là LockID với kiểu dữ liệu là String, đây là cấu hình bắt buộc mà Terraform quy định cho Table mà dùng để lưu Lock State.
Tiếp theo ta tạo tệp tin iam.tf
chứa các IAM resource.
data "aws_caller_identity" "current" {}
locals {
principal_arns = var.principal_arns != null ? var.principal_arns : [data.aws_caller_identity.current.arn]
}
data "aws_iam_policy_document" "policy_doc" {
statement {
actions = ["s3:ListBucket"]
resources = [aws_s3_bucket.s3_bucket.arn]
}
statement {
actions = ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"]
resources = ["${aws_s3_bucket.s3_bucket.arn}/*"]
}
statement {
actions = ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem"]
resources = [aws_dynamodb_table.dynamodb_table.arn]
}
}
Data Source aws_caller_identity
dùng để lấy thông tin về AWS Account mà ta đang chạy. Biến principal_arns
sẽ chứa tất cả đối tượng mà ta cho phép nó Assume Role với AWS Account.
Từ biểu thức so sánh var.principal_arns != null ? var.principal_arns : [data.aws_caller_identity.current.arn]
ở trên => nếu ta không truyền biến này vào khi chạy Terraform thì nó sẽ chỉ cho phép Account mà ta dùng để chạy Terraform có quyền Assume Role.
aws_iam_policy_document
resource dùng để định nghĩa các Policy của ta, Policy Document ở trên sẽ định nghĩa quyền cần thiết để ta có thể thực hiện hành động lên trên DynamoDB, S3, KSM. Tiếp theo ta sẽ gắn Policy Document này vào Policy và Role.
...
resource "aws_iam_policy" "policy" {
name = "${title(var.project)}S3BackendPolicy"
path = "/"
policy = data.aws_iam_policy_document.policy_doc.json
}
resource "aws_iam_role" "iam_role" {
name = "${title(var.project)}S3BackendRole"
assume_role_policy = <<-EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"AWS": ${jsonencode(local.principal_arns)}
},
"Effect": "Allow"
}
]
}
EOF
tags = local.tags
}
resource "aws_iam_role_policy_attachment" "policy_attach" {
role = aws_iam_role.iam_role.name
policy_arn = aws_iam_policy.policy.arn
}
Sau đó ta tạo tệp tin s3.tf
.
resource "aws_s3_bucket" "s3_bucket" {
bucket = "${var.project}-s3-backend"
force_destroy = false
tags = local.tags
}
resource "aws_s3_bucket_acl" "s3_bucket" {
bucket = aws_s3_bucket.s3_bucket.id
acl = "private"
}
resource "aws_s3_bucket_versioning" "s3_bucket" {
bucket = aws_s3_bucket.s3_bucket.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_kms_key" "kms_key" {
tags = local.tags
}
resource "aws_s3_bucket_server_side_encryption_configuration" "s3_bucket" {
bucket = aws_s3_bucket.s3_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.kms_key.arn
}
}
}
aws_s3_bucket
ta dùng để định nghĩa S3 Bucket, aws_s3_bucket_acl
được sử dụng để định nghĩa Access Control List của S3, ta nên gán giá trị là private
.
Tiếp theo và quan trọng là để S3 có thể dùng để lưu trữ State, ta phải bật versioning
cho nó, ta làm bằng aws_s3_bucket_versioning
resource. Cuối cùng là ta bật SSE (Server Side Encryption) cho Bucket của ta bằng aws_s3_bucket_server_side_encryption_configuration
resource.
Ta đã chuẩn bị đủ các resource cho S3 backend, tiếp theo ta cập nhật lại tệp tin main.tf
để nó output ra giá trị của S3 Backend mà ta sẽ cần, để sử dụng cho các Terraform Project khác.
...
locals {
tags = {
project = var.project
}
}
data "aws_region" "current" {}
resource "aws_resourcegroups_group" "resourcegroups_group" {
name = "${var.project}-s3-backend"
resource_query {
query = <<-JSON
{
"ResourceTypeFilters": [
"AWS::AllSupported"
],
"TagFilters": [
{
"Key": "project",
"Values": ["${var.project}"]
}
]
}
JSON
}
}
output "config" {
value = {
bucket = aws_s3_bucket.s3_bucket.bucket
region = data.aws_region.current.name
role_arn = aws_iam_role.iam_role.arn
dynamodb_table = aws_dynamodb_table.dynamodb_table.name
}
}
Bạn để ý thấy có resource tên là aws_resourcegroups_group
, đây là resource dùng để nhóm các resource lại cho dễ quản lý.
Chạy câu lệnh terraform plan
để tạo S3 Backend, sau khi chạy xong ta sẽ thấy giá trị đầu ra như dưới, đây là các giá trị ta sẽ cần.
config = {
"bucket" = "terraform-series-s3-backend"
"dynamodb_table" = "terraform-series-s3-backend"
"region" = "us-west-2"
"role_arn" = "arn:aws:iam::<ACCOUNT_ID>:role/HpiS3BackendRole"
}
Để kiểm tra các resource của S3 Backend, ta truy cập AWS Console Resouce Group.
Bấm vào nó ta sẽ thấy chi tiết của từng resource của S3 Backend. Tiếp theo ta sẽ tiến hành sử dụng S3 Backend này vào trong dự án 😁.
Sử dụng S3 Backend
Để sử dụng S3 Backend cho một dự án, ta cấu hình như sau.
terraform {
backend "s3" {
bucket = <bucket-name>
key = <path>
region = <region>
encrypt = true
role_arn = <arn-role>
dynamodb_table = <dynamodb-table-name>
}
}
Ta khai báo một block
tên là terraform
với Backend là S3 với các giá trị sau:
bucket
: tên của S3 Bucket.key
: đường dẫn ta lưu State trong Bucket.role_arn
: IAM Role mà có quyền cần thiết.dynamodb_table
: Table dùng để lưu Lock State.
Giờ ta sẽ làm ví dụ tạo một EC2 mà sử dụng S3 Backend. Tạo một thư mục và tệp tin main.tf
.
terraform {
backend "s3" {
bucket = "terraform-series-s3-backend"
key = "test-project"
region = "us-west-2"
encrypt = true
role_arn = "arn:aws:iam::<ACCOUNT_ID>:role/HpiS3BackendRole"
dynamodb_table = "terraform-series-s3-backend"
}
}
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ami" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
owners = ["099720109477"]
}
resource "aws_instance" "server" {
ami = data.aws_ami.ami.id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
}
tags = {
Name = "Server"
}
}
output "public_ip" {
value = aws_instance.server.public_ip
}
Chạy terraform init
sau đó ta chạy terraform plan
, sau khi nó chạy xong ta sẽ thấy terraform.tfstate
sẽ không còn nằm ở local
nữa. Mà ta sẽ cần lên trên S3 Bucket để xem tệp tin State của ta.
Truy cập AWS S3 Console.
Bấm vào terraform-series-s3-backend
ta sẽ thấy tệp tin State của ta.
Ta đã sử dụng S3 Backend thành công 😁.
Kết luận
Vậy là ta đã tìm hiểu xong về S3 Backend, cách tạo và sử dụng nó. Khi ta làm việc với đội thì ta nên sử dụng S3 Backend cho dự án, vừa tập trung được tệp tin State, vừa giải quyết được vấn để xung đột khi nhiều người chạy Terraform cùng một lúc. Ở bài tiếp theo ta nói về cách cấu hình và triển khai Terraform dùng Remote Backend.
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ả?