이번 포스팅에서는 Terraform의 data source와 variable에 대해 알아보겠다. Terraform을 사용하다 보면 꼭 필요한 기능들이니 잘 익혀두면 도움이 된다.
일반적으로 terraform을 통해서 리소스를 생성할 때는 resource를 사용하게 된다. 그럼 data source는 어디에 쓰일까?
# Create Azure Storage Account required for Function App
resource "azurerm_storage_account" "primary" {
name = "b59storage"
resource_group_name = azurerm_resource_group.primary.name
location = azurerm_resource_group.primary.location
account_kine = "StorageV2"
account_tier = "Standard"
account_replication_type = "LRS"
}
바로, 외부 리소스 또는 이미 저장된 정보를 terraform 내에서 참조할 때 사용하게 된다.
쉽게 예를 들면, terraform을 통해 aks를 만든다고 가정해보자.
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_kubernetes_cluster" "example" {
name = "example-aks1"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dns_prefix = "exampleaks1"
default_node_pool {
name = "default"
node_count = 1
vm_size = "Standard_D2_v2"
}
identity {
type = "SystemAssigned"
}
tags = {
Environment = "Production"
}
}
output "client_certificate" {
value = azurerm_kubernetes_cluster.example.kube_config.0.client_certificate
}
output "kube_config" {
value = azurerm_kubernetes_cluster.example.kube_config_raw
sensitive = true
}
이렇게 만들어진 aks를 만약 다른 terrform에서 참조해야하는 상황이라고 한다면,
참고로, output을 통해 만들어진 결과 중 중요한 값들을 콘솔에서 확인도 가능하다.
data "azurerm_kubernetes_cluster" "example" {
name = "myakscluster"
resource_group_name = "my-example-resource-group"
}
이런식으로 data resource를 선언해 주고,
data.azurerm_kubernetes_cluster.example.name
으로 참조가 가능하다.
Variable를 통해 인프라 구성 시 필요한 값을 미리 지정해두고, 코드에 하드코딩이나 코드변경이 없이 사용할 수 있게 하는 장점이있다.
예제코드를 보자.
# variable 블록 선언의 예
variable "<이름>" {
<인수> = <값>
}
variable "image_id" {
type = string
}
간단한 코드이지만, Variable에는 default, type, descrption, validation, sensitive, nullable 과 같은 메타인수 사용이 가능하다.
variable "ingress_rules" { # optional ( >= terraform 1.3.0)
type = list(object({
port = number,
description = optional(string),
protocol = optional(string, "tcp"),
}))
default = [
{ port = 80, description = "web" },
{ port = 53, protocol = "udp" }]
}
위와 같이 ingress rule 세팅 시 list type의 변수로 지정해서 사용도 가능하다.
validation check
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4
error_message = "The image_id value must exceed 4."
}
validation {
# regex(...) fails if it cannot find a match
condition = can(regex("^ami-", var.image_id))
error_message = "The image_id value must starting with \"ami-\"."
}
}
variable 사용 시 validation check를 통해 사용할 변수의 유효성 검증하는 방법도 있다. 위의 예제는 AMI type을 변수로 설정하는 건데, 변수 길이와 정규표현식을 통해 검증을 하게 된다.
사용자가 잘못된 값을 입력하면 아래 그림과 같이 미리 오류가 나기때문에 미연에 잘못된 세팅방지가 가능하다.
default VPC 대신 직접 VPC를 만들고, 해당 VPC내에 EC2 1대를 배포
Network - main.tf
1. VPC를 배포
2. Subnet 2개 IGW, Route Table 배포
3. Route Table에 Subnet 연결 및 Routing 정보 추가
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "kimchivpc" {
cidr_block = "10.10.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "t101-study"
}
}
resource "aws_subnet" "kimchisubnet1" {
vpc_id = aws_vpc.kimchivpc.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "t101-subnet1"
}
}
resource "aws_subnet" "kimchisubnet2" {
vpc_id = aws_vpc.kimchivpc.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "t101-subnet2"
}
}
resource "aws_internet_gateway" "kimchiigw" {
vpc_id = aws_vpc.kimchivpc.id
tags = {
Name = "t101-igw"
}
}
resource "aws_route_table" "kimchirt" {
vpc_id = aws_vpc.kimchivpc.id
tags = {
Name = "t101-rt"
}
}
resource "aws_route_table_association" "kimchirtassociation1" {
subnet_id = aws_subnet.kimchisubnet1.id
route_table_id = aws_route_table.kimchirt.id
}
resource "aws_route_table_association" "kimchirtassociation2" {
subnet_id = aws_subnet.kimchisubnet2.id
route_table_id = aws_route_table.kimchirt.id
}
resource "aws_route" "kimchidefaultroute" {
route_table_id = aws_route_table.kimchirt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.kimchiigw.id
}
output "aws_vpc_id" {
value = aws_vpc.kimchivpc.id
}
Security Group - sg.tf
Security Group 생성 후 inbound/outbound 규칙 설정
resource "aws_security_group" "kimchisg" {
vpc_id = aws_vpc.kimchivpc.id
name = "T101 SG"
description = "T101 Study SG"
}
resource "aws_security_group_rule" "kimchisginbound" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.kimchisg.id
}
resource "aws_security_group_rule" "kimchisgoutbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.kimchisg.id
}
EC2 - ec2.tf
data "aws_ami" "kimchi_amazonlinux2" {
most_recent = true
filter {
name = "owner-alias"
values = ["amazon"]
}
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-ebs"]
}
owners = ["amazon"]
}
resource "aws_instance" "kimchiec2" {
depends_on = [
aws_internet_gateway.kimchiigw
]
ami = data.aws_ami.kimchi_amazonlinux2.id
associate_public_ip_address = true
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.kimchisg.id}"]
subnet_id = aws_subnet.kimchisubnet1.id
user_data = <<-EOF
#!/bin/bash
wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
mv busybox-x86_64 busybox
chmod +x busybox
RZAZ=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone-id)
IID=$(curl 169.254.169.254/latest/meta-data/instance-id)
LIP=$(curl 169.254.169.254/latest/meta-data/local-ipv4)
echo "<h1>RegionAz($RZAZ) : Instance ID($IID) : Private IP($LIP) : Web Server</h1>" > index.html
nohup ./busybox httpd -f -p 80 &
EOF
user_data_replace_on_change = true
tags = {
Name = "t101-kimchiec2"
}
}
output "kimchiec2_public_ip" {
value = aws_instance.kimchiec2.public_ip
description = "The public IP of the Instance"
}
결과
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ myec2_public_ip = (known after apply)
aws_instance.kimchiec2: Creating...
aws_instance.kimchiec2: Still creating... [10s elapsed]
aws_instance.kimchiec2: Still creating... [20s elapsed]
aws_instance.kimchiec2: Still creating... [30s elapsed]
aws_instance.kimchiec2: Still creating... [40s elapsed]
aws_instance.kimchiec2: Creation complete after 41s [id=i-06c6d0321c172df7f]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
aws_vpc_id = "vpc-0bb4099e0c894f0ff"
myec2_public_ip = "X.XX.XX.XX"
확인
MYIP=$(terraform output -raw kimchiec2_public_ip)
while true; do curl --connect-timeout 1 http://$MYIP/ ; echo "------------------------------"; date; sleep 1; done
EC2가 잘 생성되었고, 조금 기다렸다가 curl을 날리면 위와 같은 결과를 얻을 수 있다.
vpc_security_group_ids = ["${aws_security_group.kimchisg.id}"]
원래 module을 사용해서 참조하는 방법은 알고 있었는데, 이런 방법으로 같은 레벨에 있는 파일의 리소스 값을 참조도 가능하다!
이번 스터디에서는 terraform data resource, variable, for 문 등에 대해 배웠다. 실무에서 사용할 수 있는 기능들이니 하나씩 익혀가면 좋을 것 같다.