0

[Terraform] [Lab 01] Triển khai EC2 bằng Terraform


Cơ sở hạ tầng triển khai trên Cloud.

image.png

Xây dựng cấu trúc thư mục

  • Theo gợi ý từ trang chủ của Terraform, ta sẽ xây dựng cấu trúc thư mục như sau:

image.png

  • Theo bài lab này, ta sẽ xây dựng 2 modules là ec2networking

  • File main.tf của thư mục root ta dùng đoạn code sau:

    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 4.16"
        }
      }
    
      required_version = ">= 1.2.0"
    }
    
    provider "aws" {
      region     = var.aws.region
      access_key = var.aws.access_key
      secret_key = var.aws.secret_key
    
      default_tags {
        tags = {
          Name = var.tag.name
        }
      }
    }
    
    
  • Ta khai báo biến sử dụng và kiểu dữ liệu vào file variables.tf

    variable "aws" {
      sensitive = true
      type = object({
        region     = string
        access_key = string
        secret_key = string
      })
    }
    
    variable "tag" {
      type = object({
        name = string
      })
    
      default = {
        name = "learning"
      }
    }
    
  • Giá trị của variables.tf được lấy ở terraform.tfvars , biến môi trường sẽ có cùng kiểu dữ liệu được lưu ở variables.tf

    # AWS Provider Configuration
    aws = {
      region     = <AWS_region>
      access_key = <AWS_access_key>
      secret_key = <AWS_secret_key>
    }
    
    # Default Tag Name
    tag = {
      name = "learning"
    }
    
    # Networking Configuration
    networking = {
      vpc_cidr_block    = "10.0.0.0/16"
      subnet_cidr_block = "10.0.1.0/24"
    }
    
  • output.tf , sau khi triển khai hệ thống xong, sẽ xuất giá trị được khai báo trong file ra màn hình

    output "instance_public_ip" {
      description = "Public IP address of the EC2 instance"
      value       = module.ec2.instance_public_ip
    }
    

Khởi tạo workspace

  • Sau khi đã khai báo xong, ta khởi tạo workspace

    terraform init
    
    Initializing the backend...
    Initializing provider plugins...
    - Finding hashicorp/aws versions matching "~> 4.16"...
    - Installing hashicorp/aws v4.67.0...
    - Installed hashicorp/aws v4.67.0 (signed by HashiCorp)
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.
    
    Terraform has been successfully initialized!
    
    ...
    
  • Chạy lệnh trên xong, thư mục .terraform.terraform.lock.hcl sẽ được tạo

Xây dựng các module

Ta sẽ xây dựng các modules trong thư mục modules

Networking

  • Networking trong bài lab này cần có VPC, Public subnet , security group , Route tableInternet gateway .

  • Đầu tiên ta tạo thư mục networking theo đường dẫn modules/networking

  • Sau đó tạo các file main.tf , output.tf , variables.tf

    • main.tf
    // VPC
    resource "aws_vpc" "vpc-learning" {
      cidr_block = var.vpc_cidr_block
    }
    
    // Subnet
    resource "aws_subnet" "subnet-learning" {
      vpc_id     = aws_vpc.vpc-learning.id
      cidr_block = var.subnet_cidr_block
    }
    
    // Internet gateway
    resource "aws_internet_gateway" "gw-learning" {
      vpc_id = aws_vpc.vpc-learning.id
    }
    
    // Route table
    resource "aws_route_table" "route-table-learning" {
      vpc_id = aws_vpc.vpc-learning.id
    
      route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.gw-learning.id
      }
    }
    resource "aws_route_table_association" "a" {
      subnet_id      = aws_subnet.subnet-learning.id
      route_table_id = aws_route_table.route-table-learning.id
    }
    
    // Security group
    resource "aws_security_group" "allow_tls" {
      name        = "allow_tls"
      description = "Allow TLS inbound traffic and all outbound traffic"
      vpc_id      = aws_vpc.vpc-learning.id
    
      // Inbound rules
      ingress {
        from_port   = 22
        to_port     = 22
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"] # Allow SSH from anywhere (adjust for security)
      }
    
      ingress {
        from_port   = 80
        to_port     = 80
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"] # Allow HTTP from anywhere
      }
    
      // Outbound rules
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
    }
    
    
    • variables.tf, khai báo biến được sử dụng trong file main.tf , giá trị của những biến này sẽ được nhận khi khai báo trong module blocks (ta sẽ tìm hiểu thêm ở phần sau của bài lab này)
    variable "vpc_cidr_block" {
      type = string
    }
    
    variable "subnet_cidr_block" {
      type = string
    }
    
    • output.tf , xuất giá trị trong modules ra ngoài để bên ngoài có thể truy xuất sử dụng được
    output "vpc_id" {
      value = aws_vpc.vpc-learning.id
    }
    
    output "subnet_id" {
      value = aws_subnet.subnet-learning.id
    }
    
    output "security_group_id" {
      value = aws_security_group.allow_tls.id
    }
    
    

EC2

  • Tương tự như module netwoking ở phần trên, ta tiến hành tạo thư mục ec2 trong đường dẫn modules/ec2 và tiến hành tạo các file main.tf , output.tf , variables.tf

    • main.tf
    // EC2
    resource "aws_instance" "web" {
      ami                         = var.ami
      instance_type               = var.instance_type
      subnet_id                   = var.subnet_id
      vpc_security_group_ids      = [var.security_group_id]
      associate_public_ip_address = true
      key_name                    = var.key_name
    
    	// Chạy ở máy local sau khi EC2 được tạo
      provisioner "local-exec" {
        command = "echo IP address is ${self.private_ip}"
      }
    
    	// Remote vào EC2 và chạy lần lược các lệnh trong inline
      provisioner "remote-exec" {
        connection {
          type        = "ssh"
          user        = var.user
          private_key = var.key_private
          host        = self.public_ip
        }
    
        inline = [
          "sudo yum update -y",
          "sudo amazon-linux-extras enable nginx1",
          "sudo yum install -y nginx",
          "sudo systemctl start nginx",
          "sudo systemctl enable nginx"
        ]
      }
    }
    
    • output.tf
    output "instance_id" {
      description = "ID of the EC2 instance"
      value       = aws_instance.web.id
    }
    
    output "instance_public_ip" {
      description = "Public IP address of the EC2 instance"
      value       = aws_instance.web.public_ip
    }
    
    output "instance_public_dns" {
      description = "Public DNS of the EC2 instance"
      value       = aws_instance.web.public_dns
    }
    
    • variables.tf
    variable "ami" {
      type = string
    }
    
    variable "instance_type" {
      type = string
    }
    
    variable "key_pair" {
      type = string
    }
    
    variable "key_name" {
      type = string
    }
    
    variable "key_private" {
      type = string
    }
    
    variable "user" {
      type = string
    }
    
    variable "subnet_id" {
      description = "Subnet ID where EC2 will be launched"
      type        = string
    }
    
    variable "security_group_id" {
      description = "Security group ID for the EC2 instance"
      type        = string
    }
    
    

Kết nối các modules

  • Trong file variables.tf ta khai báo thêm các biến cho 2 modules networkingEC2

    • variables.tf
    variable "instance" {
      sensitive = true
      type = object({
        ami         = string
        type        = string
        name        = string
        key_name    = string
        key_pair    = string
        key_private = string
        user        = string
      })
    }
    
    variable "networking" {
      sensitive = true
      type = object({
        vpc_cidr_block    = string
        subnet_cidr_block = string
      })
    }
    
  • Khai báo thêm giá trị cho các variables vừa thêm vào vào file terraform.tfvars

    # Networking Configuration
    networking = {
      vpc_cidr_block    = "10.0.0.0/16"
      subnet_cidr_block = "10.0.1.0/24"
    }
    
    # EC2 Configuration
    instance = {
      ami         = "ami-053a45fff0a704a47"
      type        = "t2.micro"
      name        = "learning"
      key_name    = "learning"
      key_pair    = "~/.ssh/terraform_learning.pub"
      key_private = "~/.ssh/terraform_learning"
      user        = "ec2-user"
    }
    
  • Sau đó khai báo các modules và thông tin cần thiết cho chúng tại file main.tf

    resource "aws_key_pair" "deployer" {
      key_name   = var.instance.key_name
      public_key = file(var.instance.key_pair)
    }
    
    module "networking" {
      source            = "./modules/networking"
      
      // vpc_cidr_block, subnet_cidr_block đã được khai báo trong modules/networking/variables.tf
      vpc_cidr_block    = var.networking.vpc_cidr_block
      subnet_cidr_block = var.networking.subnet_cidr_block
    }
    
    module "ec2" {
      source            = "./modules/ec2"
      
      ami               = var.instance.ami
      instance_type     = var.instance.type
      subnet_id         = module.networking.subnet_id // từ output.tf của modules/networking
      security_group_id = module.networking.security_group_id // từ output.tf của modules/networking
      key_name          = var.instance.key_name
      key_pair          = file(var.instance.key_pair)
      key_private       = file(var.instance.key_private)
      user              = var.instance.user
    }
    

Triển khai hệ thống

  • Vì cấu trúc của bài lab đã thay đổi, nên ta cần phải init lai workspace

    terraform init
    
  • Sau đó ta chạy lệnh sau để format lại code và validate các file cấu hình của cơ sở hạ tần

    terraform fmt
    terraform validate
    
  • Ta kiểm tra các thay đổi trước khi triển khai hệ thống lên Cloud

    terraform plan
    
  • Kiểm tra lại các thay đổi đã được log trên console, nếu không có lỗi gì thì tiến hành tạo resource trên Cloud

    terraform apply
    
  • Trong quá trình tạo nếu bị lỗi sau, có nghĩa key pair đã tồn tại nên không thể tạo được nữa

    ╷
    │ Error: importing EC2 Key Pair (learning): InvalidKeyPair.Duplicate: The keypair already exists
    │       status code: 400, request id: 6d8e71ea-7cc1-465e-b9cd-69f29de44a6c
    │ 
    │   with aws_key_pair.deployer,
    │   on main.tf line 24, in resource "aws_key_pair" "deployer":
    │   24: resource "aws_key_pair" "deployer" {
    │ 
    
  • Ở đây ta có thể vào account AWS để xoá key tồn tại, sau đó chạy lại lệnh terraform apply. Cách thứ 2, ta có thể import key hiện có trên AWS vào terraform

    terraform import aws_key_pair.deployer learning
    
  • Lúc này trong file terraform.tfstate ta sẽ thấy resource aws_key_pair được tạo ra, chạy lại command terraform planđể kiểm tra thay đổi, sau đó chạy command terraform apply để triền khai hệ thống lên Cloud.

    Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
    
    Outputs:
    
    instance_public_ip = "3.85.54.84"
    
  • Truy cập vào IP ở trên ta được kết quả như sau

image.png

  • Sau khi hoàn thành ta tiến hành xoá hết các resource vừa tạo

    terraform destroy
    

Kết luận

  • Qua bài lab này, ta đã biết được cách xây dựng cấu trúc thư mục Terraform đơn giản.
  • Nắm được các bước để triển khai cơ sở hạ tầng bằng Terraform.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí