티스토리 뷰
[IaC] IaC/테라폼 이해, 기본 명령 사용법( command, HCL, block, resource)
Hayley Shim 2023. 10. 28. 17:58안녕하세요. 최근 도서 “테라폼으로 시작하는 IaC” 의 내용을 기준으로 스터디한 내용을 정리했습니다. 해당 글에서는 IaC와 테라폼에 대해 간단히 이해하고 기본 명령 사용법에 대해 알아보겠습니다.
1. IaC/테라폼 이해
IaC와 테라폼
- IaC : 코드형 인프라(Infrastructure as Code, IaC)는 코드를 통해 인프라를 관리하고 프로비저닝하는 것을 말합니다.
- 테라폼 : 하시코프사에서 공개한 IaC 도구로 “워크플로우에 집중, 코드형 인프라, 실용주의”라는 하시코프의 철학을 담은 도구입니다.
테라폼 제공 유형별 기능
- 테라폼 Core
- 테라폼 Cloud : Terraform Cloud는 팀이 Terraform을 사용하여 인프라를 프로비저닝하는 데 도움이 되는 애플리케이션
- 테라폼 Enterprise : 고급 보안 및 규정 준수 요구 사항이 있는 조직은 Terraform Cloud의 자체 호스팅 배포판인 Terraform Enterprise를 구입
테라폼 클라우드 가격정책 — [참고: Pricing, Feature]
- Free : 리소스 500개 까지 무료 → 커뮤니티 버전
- Standard : Free + 워크플로우 기능 추가 + 동시실행(Concurrency 개수 3개)
- Plus : 정책, 보안, 신뢰성, 확장성 등 기업형 고객에게 적합(대규모 사용자를 위한 비용모델)
- Enterprise : Plus와 대부분 유사하며 설치형 모델
테라폼 환경 구성
- 여러 실행 환경 구성 방법 중 OS 패키지 관리자 활용하여 테라폼을 설치합니다.
- OS에 맞게 테라폼 설치
# macOS 환경
# tfenv 설치
$ brew install tfenv
# 설치 가능 버전 리스트 확인
$ tfenv list-remote
# 테라폼 1.5.6 버전 설치
$ tfenv install 1.5.6
# tfenv로 설치한 버전 확인(변경 전)
$ tfenv list
1.5.6
* 1.5.1 (set by /usr/local/Cellar/tfenv/3.0.0/version)
# 테라폼 1.5.6 버전 사용 설정
$ tfenv use 1.5.6
# tfenv로 설치한 버전 확인(변경 후)
$ tfenv list
* 1.5.6 (set by /usr/local/Cellar/tfenv/3.0.0/version)
1.5.1
# 테라폼 버전 정보 확인
$ terraform version
# 자동완성
$ terraform -install-autocomplete
## 참고 .zshrc 에 아래 추가됨
$ cat ~/.zshrc
autoload -U +X bashcompinit && bashcompinit
complete -o nospace -C /usr/local/bin/terraform terraform
windowOS에서 WSL2 리눅스 환경 설치 후 사용하는 것이 가능합니다. 하지만 WSL의 경우 종종 이슈가 발생하여 개인적으로 macOS 환경을 권장합니다.
2. OS 환경에 맞게 VS Code 설치 후 Externtions 확장 설치
- VS Code — [Extentions]에서 HashiCorp HCL 을 검색 후 설치합니다.

- 설정(Command+,) → 일반적으로 사용되는 설정 ⇒ Files: Auto Save 값을 afterDelay 로 선택하여 자동저장 설정을 합니다.

3. AWS CLI 설치 및 자격증명 설정
# 방안1. macOS 설치 방법
$ brew install awscli
# 방안2. Linux 설치 방법
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
# aws cli 버전 확인
$ aws --version
aws-cli/2.13.13 Python/3.11.5 Darwin/22.6.0 source/x86_64 prompt/off
# aws cli 사용 시도(실패)
$ aws s3 ls
#(결과예시)
1.InvalidToken: An error occurred (InvalidToken) when calling the ListBuckets operation: The provided token is malformed or otherwise invalid.
2.configure 미설정 : Unable to locate credentials. You can configure credentials by running "aws configure".
---
# 방안1. aws configure 로 자격증명 설정
$ aws configure
... >> 입력
$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile <not set> None None
access_key ****************67BY shared-credentials-file
secret_key ****************67BY shared-credentials-file
region ap-northeast-2 config-file ~/.aws/config
# 방안2. 환경 변수로 자격증명 설정
# Linux or macOS
$ export AWS_ACCESS_KEY_ID=AKIXXXXXXXXXX
$ export AWS_SECRET_ACCESS_KEY=QkHsXXXXXXXXXXXXXXXXXXXX
$ export AWS_DEFAULT_REGION=ap-northeast-2
# 페이저 사용 비활성화[참고: AWS CLI 페이지 매김]
$ export AWS_PAGER=""
# aws cli 사용 시도(성공)
$ aws s3 ls
4. 실습에 편리한 Tool 설치
# macOS
$ brew install tree jq watch
# Linux
$ sudo apt install -y tree jq
5. AWS 서울 리전(ap-northeast-2)에 default VPC 존재 확인 : 없을 경우 아래 (옵션) default VPC 생성 해두기 참고 — 링크
$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq
{
...
$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq '.Vpcs[0].VpcId'
"vpc-0dc6bc54bbf096999"
#aws ec2 describe-subnets --filter 'Name=vpc-id,Values=vpc-3912a952' --output table
$ aws ec2 describe-subnets --filter 'Name=vpc-id,Values=vpc-<자신의VPC ID>' --output table
--------------------------------------------------------------------------------------------------------------
| DescribeSubnets |
+------------------------------------------------------------------------------------------------------------+
|| Subnets ||
|+------------------------------+---------------------------------------------------------------------------+|
|| AssignIpv6AddressOnCreation | False ||
|| AvailabilityZone | ap-northeast-2a ||
|| AvailabilityZoneId | apne2-az1 ||
|| AvailableIpAddressCount | 4091 ||
|| CidrBlock | 172.31.0.0/20 ||
|| DefaultForAz | True ||
|| EnableDns64 | False
# default vpc 없을 경우 생성
# default VPC를 생성
$ aws ec2 create-default-vpc
# default Subnet 생성
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2a
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2b
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2c
$ aws ec2 create-default-subnet --availability-zone ap-northeast-2d
6. EC2 1대 배포
배포전 준비
# 각자 편한 디렉터리를 생성해준다
$ mkdir t101-1week-ec2
$ cd t101-1week-ec2
# Amazon Linux 2 최신 ami id 찾기 : ami-09bae2fe0380843e0
#aws ec2 describe-images --owners self amazon
$ aws ec2 describe-images --owners self amazon --query 'Images[*].[ImageId]' --output text
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[].Name"
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[].Value"
# EC2 생성 모니터링 - VS Code 터미널1에서 실행
$ export AWS_PAGER=""
$ while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done
EC2 1대 배포 실행 — 터미널1 : HCL Hashicorp Configuration Language 코드 파일 생성 — VS Code 터미널2에서 실행
# VS Code 터미널2
$ cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-09bae2fe0380843e0"
instance_type = "t2.micro"
}
EOT
배포 실행 — VS Code 터미널2에서 실행
# 초기화
$ terraform init
$ ls -al
total 16
drwxr-xr-x 5 yhshim staff 160 Sep 2 21:03 .
drwxr-xr-x 3 yhshim staff 96 Sep 2 20:52 ..
drwxr-xr-x 3 yhshim staff 96 Sep 2 21:03 .terraform
-rw-r--r-- 1 yhshim staff 252 Sep 2 21:03 .terraform.lock.hcl
-rw-r--r-- 1 yhshim staff 157 Sep 2 21:03 main.tf
$ tree .terraform
.terraform
└── providers
└── registry.terraform.io
└── hashicorp
└── aws
└── 4.29.0
└── darwin_arm64
└── terraform-provider-aws_v4.29.0_x5
# plan 실행 시 아래와 같은 정보가 출력
$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-09bae2fe0380843e0"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ subnet_id = (known after apply)
+ tags_all = (known after apply)
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
# apply 실행
$ terraform apply
Enter a value: yes 입력
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-09bae2fe0380843e0"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ subnet_id = (known after apply)
+ tags_all = (known after apply)
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Still creating... [30s elapsed]
aws_instance.example: Creation complete after 31s [id=i-0330d4be110f21edc]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
# 모니터링 : [터미널1]에 Name 확인
EC2 태그 정보 수정 — 터미널2
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-09bae2fe0380843e0"
instance_type = "t2.micro"
tags = {
Name = "t101-study"
}
}
EOT
배포 실행
# plan 실행 시 아래와 같은 정보가 출력
$ terraform plan
aws_instance.example: Refreshing state... [id=i-0330d4be110f21edc]
Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated
with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_instance.example will be updated in-place
~ resource "aws_instance" "example" {
id = "i-0330d4be110f21edc"
~ tags = {
+ "Name" = "t101-study"
}
~ tags_all = {
+ "Name" = "t101-study"
}
# (29 unchanged attributes hidden)
# (7 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
# apply 실행
$ terraform apply
Enter a value: yes 입력
aws_instance.example: Refreshing state... [id=i-0330d4be110f21edc]
Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated
with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_instance.example will be updated in-place
~ resource "aws_instance" "example" {
id = "i-0330d4be110f21edc"
~ tags = {
+ "Name" = "t101-study"
}
~ tags_all = {
+ "Name" = "t101-study"
}
# (29 unchanged attributes hidden)
# (7 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.example: Modifying... [id=i-0330d4be110f21edc]
aws_instance.example: Modifications complete after 0s [id=i-0330d4be110f21edc]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
# 모니터링 : [터미널1]에 Name 확인


EC2 삭제 — 터미널2
# 리소스 삭제
$ terraform destroy
Enter a value: yes 입력
혹은
$ terraform destroy -auto-approve

2. 기본 사용법
- 주요 커맨드
실습 디렉터리 생성 (workspaces)
# 실습 디렉터리 생성 후 이동
$ mkdir workspaces
$ cd workspaces
# 테라폼 실행
$ terraform
Usage: terraform [-version] [-help] <command> [args]
...
VS Code 폴더 생성 (03.start) → 새파일 생성 (main.tf)
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}
help & init 초기화
# 서브커맨드 help 지원
$ terraform console -help
$ terraform init -help
# init 초기화
# 테라폼 실행을 위해 코드 파일이 있는 디렉터리로 이동
# (참고) 테라폼이 실행되는 디렉터리 = 모듈(테라폼 코드 파일과 변수 파일), 기본 작업디렉터리는 '루트 모듈', 호출 모듈은 '자식 모듈'
$ cd 03.start/
# plan 실행 시 에러 출력
$ terraform plan
│ Error: Inconsistent dependency lock file
│
│ The following dependency selections recorded in the lock file are inconsistent with the current configuration:
│ - provider registry.terraform.io/hashicorp/local: required by this configuration but no version is selected
│
│ To make the initial dependency selections that will initialize the dependency lock file, run:
│ terraform init
# 초기화 : 코드 사용 구문 기반으로 필요한 프로바이더 플러그인을 찾고 설치, 추가로 '프로바이더/모듈/백엔드' 구성 설정/변경 시 수행 필요
$ terraform init
$ ls -al
$ tree .terraform # VS Code에서 탐색기 확인
.terraform
└── providers
└── registry.terraform.io
└── hashicorp
└── local
└── 2.4.0
└── darwin_arm64
└── terraform-provider-local_v2.4.0_x5
7 directories, 1 file
- Error: Inconsistent dependency lock file : 참고
plan 계획 & apply 실행
# plan 실행 : 구성 내용을 바탕으로 어떤 리소스가 생성되는지 상세 내역 출력, 기본값 자동 입력 적용
$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.abc will be created
+ resource "local_file" "abc" {
+ content = "abc!"
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "./abc.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy. # 하나의 리소스가 추가되고, 변경되거나 삭제되는 것은 없을 예정
# plan 결과를 시스템 코드로 출력
$ terraform plan -detailed-exitcode
...
# 코드 확인 : 0(변경 사항이 없는 성공), 1(오류가 있음), 2(변경 사항이 있는 성공)
$ echo $?
2
# (참고) apply 결과에 적용 시도
$ terraform apply -auto-approve -detailed-exitcode
# apply 실행 : no 입력
$ terraform apply
...
Enter a value: no
...
# plan 결과를 지정된 파일(바이너리 형태) 이름으로 생성
$ terraform plan -out=tfplan
$ cat tfplan
+�"W tfplanUT�G�dd��J�@����O�tᢈHwv)4ͥi�K��])N&�fj� g��n}B�w+B �
y��
�.����)����;�3�C"�� τ�425AORb �
ٌ@E�Kʚ���O���Q�4*�����T�!໓e�(l����V8^�`�{�S0���$�
ƞW�"�
�&k�Z���4�\K�R"�2[�N�'b���3L!�� 7���a�؛�T -��/��o�Ǽ�9��?{jB��{��\�;���q:�_.$��
�9�[]��:��w��P�ŕ�,�+�"W tfstateUT�G�dD�= 8�����b���mzf���r,�mX�
B1
��P���rm�+�"W"�R�c+I���ݥqp�~��-�b��nr'�tk�Z��i9�w%)�1�ޑK�t�9�z����a��d��ru������Y�9��
tfstate-prevUT�G�dD�=
B1
��P���rm�+�"W"�Rtfconfig/m-/main.tfUT�G�d�A��i9�w%)�1�ޑK�t�9�z����a��d��ru������Y�9��
1
�����xIcD!m�� ��}h����罉�̅��������5��[/�q��E�^Ǘ㝊?��q���c���Pg�fV\+�"W tfconfig/modules.jsonUT�G�d��RP��RPPPP�N�T�RPRҁp]2�@\=%.�Z�X@��P��k))+�"W .terraform.lock.hclUT�G�dT�Mo�E
���������{�cW��p��'q�����M6�ݤI�$���������t��Kn�(۹��v��>����x����px)��/q:iOwe��.��ݛ��?ꡄo���KYQ��l�%/��(���8��v����q*�S���/����ɛ���VϷ�OoG�þ��+�)N��x_ޗ=����ߕ�z����+���m{���o~�xw�������K�����;���ϧ���|x��-:�.����S��ʳ�6�\9�@V�Qk�Ԯ��F
�Jj
jyEͪ��<��q{��-��tn�f���8u̺2�Ve�9�)���]�G�B#v`X�B�uT05h�Bh�Vg\xE1(WL���xM��C��!Ʋ���%�$3%�q&�-W���ڣW'��X�ј���
���iZ�*45V�K
��Eu�6xd�Q�a7Y.ցyu��<����f�tE��N�F�-#+����ci�@ʜ�;�R^}��Qk�l��:����yA��l�Jck,�82�����U��6V�+*j��s�
1�Ui!�3qWt���u��U-�*��̐��! |z�-b��2�Zu!�tlC��Ô>����0UpL��tZ���6a�lR[W'��}-�e]��՞=M������?v�� ��P�2 ���+�"W�ŕ�,� tfplanUT�G�d+�"W���rm� itfstateUT�G�d+�"W���rm�
tfstate-prevUT�G�d+�"Wg�fV\ �tfconfig/m-/main.tfUT�G�d+�"W��k)) dtfconfig/modules.jsonUT�G�+�"W�2 ��� �.terraform.lock.hclUT�G�dPK��%
$ file tfplan
tfplan: Zip archive data, at least v2.0 to extract, compression method=deflate
# apply 실행 : 실행계획이 있으므로 즉시 적용됨
$ terraform apply tfplan
$ ls -al abc.txt
-rwxr-xr-x 1 yhshim staff 4 Sep 2 23:34 abc.txt
# apply 실행을 다시 하게 되면
# 테라폼은 선언적 구성 관리를 제공하는 언어로 멱등성 idempotence을 갖고,
# 상태를 관리하기 때문에 동일한 구성에 대해서는 다시 실행하거나 변경하는 작업을
# 수행하지 않음
$ terraform apply
local_file.abc: Refreshing state... [id=5678fb68a642f3c6c8004c1bdc21e7142087287b]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are
needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
$ terraform state list
local_file.abc
코드 파일 수정
# 코드 파일 수정
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}
resource "local_file" "dev" {
content = "def!"
filename = "${path.module}/def.txt"
}
# 실행 > 어떻게 되나요?
$ terraform apply
...
Enter a value: yes
...
# 확인
$ terraform state list
$ tree
.
├── abc.txt
├── def.txt
├── main.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── tfplan
1 directory, 6 files
$ ls *.txt
abc.txt def.txt
# 현재 배포된 리소스 확인
$ terraform state list
local_file.abc
local_file.dev
# 변경 이전의 실행 계획 적용 시도하면 아래와 같은 에러 ㅏㄹ생
$ terraform apply tfplan
Error: Saved plan is stale
│
│ The given plan file can no longer be applied because the state was changed by another operation after the plan was created.
다시 코드 내용 삭제
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}
# 실행 > 어떻게 되나요?
$ terraform apply
...
Enter a value: yes
...
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
local_file.dev: Destroying... [id=15f946cb27f0730866cefea4f0923248d9366cb0]
local_file.dev: Destruction complete after 0s]
# 확인
$ terraform state list
local_file.abc
$ tree
.
├── abc.txt
├── main.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── tfplan
1 directory, 5 files
$ ls *.txt
abc.txt
destroy 제거 & fmt
#
$ terraform destroy
...
Enter a value: yes
...
# 확인
$ terraform state list
$ ls *.txt
zsh: no matches found: *.txt
# 적용 후 코드 파일 내용 확인 -> 들여쓰기 확인
$ terraform fmt
# fmt : format 또는 reformat 줄임 표시로 terraform tmt 명령어로 수행, 테라폼 구성 파일을 표준 형식과 표준 스타일로 적용. 코드 가독성 높임
추가실습 : EC2 1대 배포 & 웹 서버 설정
- 테라폼 방식 : immutable, 이뮤터블(인프라스트럭처의 상태를 변경할 때, 기존의 인프라스트럭처를 수정하거나 업데이트하는 것이 아니라 새로운 infrastructure를 생성하여 이전 상태의 인프라스트럭처를 교체하는 방식)
실습 목표 : EC2 1대를 배포하면서 userdata에 웹 서버 설정하여 간단한 애플리케이션 설정을 자동화합니다.
배포 : Ubuntu 22.04 LTS 사용 (ami-0c9c942bd7bf113a2)
# 각자 편한 디렉터리를 생성
$ cd ..
$ mkdir t101-1week-web
$ cd t101-1week-web
코드 파일 작성 : user_data 에 실행 명령어 작성
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-0c9c942bd7bf113a2"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
echo "Hello, T101 Study" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "terraform-Study-101"
}
}
EOT
배포 실행
# init
$ terraform init
# plan
$ terraform plan
+ user_data = "d91ca31904077f0b641b5dd5a783401396ffbf3f"
# apply 실행
$ terraform apply -auto-approve
웹 서버 접속 시도 : 터미널3에서 실행
# [터미널3] 변수 지정
$ PIP=<각자 자신의 EC2 Public IP>
$ PIP=3.35.10.132
while true; do curl --connect-timeout 1 http://$PIP:8080/ ; echo "------------------------------"; date; sleep 1; done
문제 해결 : 웹 서버 접속
- 코드 파일 수정 : 보안 그룹 생성 후 연동
$ cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-0c9c942bd7bf113a2"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, T101 Study" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "Single-WebSrv"
}
}
resource "aws_security_group" "instance" {
name = var.security_group_name
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
variable "security_group_name" {
description = "The name of the security group"
type = string
default = "terraform-example-instance"
}
output "public_ip" {
value = aws_instance.example.public_ip
description = "The public IP of the Instance"
}
EOT
Error: with the retirement of EC2-Classic no new Security Groups can be created without referencing a VPC
│
│ with aws_security_group.instance,
│ on main.tf line 21, in resource “aws_security_group” “instance”:
│ 21: resource “aws_security_group” “instance” {
│
위와 같은 에러가 발생할 경우 아래와 같이 수정합니다 : 참고
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_default_vpc" "default" {
tags = {
Name = "Default VPC"
}
}
resource "aws_instance" "example" {
ami = "ami-0c9c942bd7bf113a2"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, T101 Study" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "Single-WebSrv"
}
}
resource "aws_security_group" "instance" {
name = var.security_group_name
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
vpc_id = aws_default_vpc.default.id
}
variable "security_group_name" {
description = "The name of the security group"
type = string
default = "terraform-example-instance"
}
output "public_ip" {
value = aws_instance.example.public_ip
description = "The public IP of the Instance"
}
배포 실행
# plan/apply
$ terraform plan
$ terraform apply -auto-approve
# 모니터링 : EC2 정보와 curl 접속 확인
# (옵션) 리소스 생성 그래프 확인
$ terraform graph
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] aws_default_vpc.default (expand)" [label = "aws_default_vpc.default", shape = "box"]
"[root] aws_instance.example (expand)" [label = "aws_instance.example", shape = "box"]
"[root] aws_security_group.instance (expand)" [label = "aws_security_group.instance", shape = "box"]
"[root] provider[\"registry.terraform.io/hashicorp/aws\"]" [label = "provider[\"registry.terraform.io/hashicorp/aws\"]", shape = "diamond"]
"[root] var.security_group_name" [label = "var.security_group_name", shape = "note"]
"[root] aws_default_vpc.default (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"]"
"[root] aws_instance.example (expand)" -> "[root] aws_security_group.instance (expand)"
"[root] aws_security_group.instance (expand)" -> "[root] aws_default_vpc.default (expand)"
"[root] aws_security_group.instance (expand)" -> "[root] var.security_group_name"
"[root] output.public_ip (expand)" -> "[root] aws_instance.example (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/aws\"] (close)" -> "[root] aws_instance.example (expand)"
"[root] root" -> "[root] output.public_ip (expand)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"] (close)"
}
}
# graph 확인 > 파일 선택 후 오른쪽 상단 DOT 클릭
$ terraform graph > graph.dot
리소스 삭제
# 리소스 삭제
$ terraform destroy -auto-approve
2. HCL
- HCL HashiCorp Configuration Language은 하시코프사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구
HCL 표현식
// 한줄 주석 방법1
# 한줄 주석 방법2
/*
라인
주석
*/
locals {
key1 = "value1" # = 를 기준으로 키와 값이 구분되며
myStr = "TF ♡ UTF-8" # UTF-8 문자를 지원한다.
multiStr = <<EOF
Multi
Line
String
with anytext
EOF
boolean1 = true # boolean true
boolean2 = false # boolean false를 지원한다.
deciaml = 123 # 기본적으로 숫자는 10진수,
octal = 0123 # 0으로 시작하는 숫자는 8진수,
hexadecimal = "0xD5" # 0x 값을 포함하는 스트링은 16진수,
scientific = 1e10 # 과학표기 법도 지원한다.
# funtion 호출 예
myprojectname = format("%s is myproject name", var.project)
# 3항 연산자 조건문을 지원한다.
credentials = var.credentials == "" ? file(var.credentials_file) : var.credentials
}
- 테라폼으로 인프라를 구성하기 위한 선언 블록도 다수 존재 : terraform, resource, data, variable, local, output
3. 테라폼 블록
테라폼 블록 : 테라폼 구성을 명시하는 데 사용
- 테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만, 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화 할 것을 권장 [Docs] [Docs 예제]
- 버전 체계는 시맨틱 버전 관리 Semantic Versioning(SemVer) 방식을 따른다 [참고], [Kubernetes Release Versioning]
terraform {
required_version = "~> 1.3.0" # 테라폼 버전
required_providers { # 프로바이더 버전을 나열
random = {
version = ">= 3.0.0, < 3.1.0"
}
aws = {
version = "4.2.0"
}
}
cloud { # Cloud/Enterprise 같은 원격 실행을 위한 정보 [참고: Docs]
organization = "<MY_ORG_NAME>"
workspaces {
name = "my-first-workspace"
}
}
backend "local" { # state를 보관하는 위치를 지정 [참고: Docs, local, remote, s3]
path = "relative/path/to/terraform.tfstate"
}
}
테라폼 버전 : required_version
프로바이더 버전
- 테라폼 0.13 버전 이전에는 provider 블록에 함께 버전을 명시했지만 해당 버전 이후 프로바이더 버전은 terraform 블록에서 required_providers에 정의
- 코드 파일 수정 : 프로바이더 버전을 과하게 높게 설정 — 링크
terraform {
required_version = ">= 1.0.0"
required_providers {
local = {
source = "hashicorp/local"
version = ">=10000.0.0"
}
}
}
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
cloud 블록
- Terraform Cloud, Terraform Enterprise는 CLI, VCS, API 기반의 실행 방식을 지원하고 cloud 블록으로 선언
백엔드 블록
- 백엔드 블록의 구성은 테라폼 실행 시 저장되는 State(상태 파일)의 저장 위치를 선언 (기본: local)
- 주의할 점은 하나의 백엔드만 허용한다는 점
- State에는 외부로 노출되면 안 되는 패스워드 또는 인증서 정보 같은 민감한 데이터들이 포함될 수 있으므로 State의 접근 제어 및 안전한 관리방안 대책수립이 필요
4. 리소스
리소스 구성 : 리소스 블록은 선언된 항목을 생성하는 동작을 수행
종속성
- 테라폼 종속성은 resource, module 선언으로 프로비저닝되는 각 요소의 생성 순서를 구분함
- 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용함 — [참고: Docs]
- 리소스 참조값을 설정해 두 개의 리소스 간 암시적 종속성 부여
- 리소스의 속성을 주입하지 않아도 두 리소스 간에 종속성이 필요한 경우에, depends_on 선언으로 적용 가능(명시적 종속성 부여)
리소스 속성 참조
- 리소스 구성에서 참조 가능한 값은 인수와 속성
- 인수 : 리소스 생성 시 사용자가 선언하는 값
- 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
수명주기
- lifecycle은 리소스의 기본 수명주기를 작업자가 의도적으로 변경하는 메타인수다. 메타인수 내에는 아래 선언이 가능.
- create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
- prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
- ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
- precondition: 리소스 요소에 선언해 인수의 조건을 검증
- postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증
위 내용 외에 도서 ‘Terraform: Up & Running(By Yevgeniy Brikman)’ 를 기준으로 작성된 지난 블로그 글을 참고하여 테라폼 학습에 도움되시길 바랍니다.
[IaC] Terraform tips & tricks — Loops & Conditions
[IaC] Managing Secrets with Terraform
[IaC] Production-grade Terraform code
written in 2023.9.3
https://medium.com/techblog-hayleyshim/iac-iac-%ED%85%8C%EB%9D%BC%ED%8F%BC-%EC%9D%B4%ED%95%B4-%EA%B8%B0%EB%B3%B8-%EB%AA%85%EB%A0%B9-%EC%82%AC%EC%9A%A9%EB%B2%95-90bd4bd8bae9
'Programming > IaC' 카테고리의 다른 글
[IaC] 기본 사용법( condition, function, provisioner, null_resource와 terraform_data, moved block, cli를 위한 시스템 환경 변수) & 프로바이더 (0) | 2023.10.28 |
---|---|
[IaC] 기본 사용법( data source, input variables, local, output, loop) (0) | 2023.10.28 |
[IaC] Working with Multiple Providers (0) | 2023.10.28 |
[IaC] GKE configuration deep dive (0) | 2023.10.28 |
[IaC] Production-grade Terraform code (0) | 2023.10.28 |
- Total
- Today
- Yesterday
- AI
- security
- gcp serverless
- operator
- cloud
- terraform
- 혼공단
- cni
- NW
- k8s
- S3
- SDWAN
- AWS
- GCP
- 혼공챌린지
- k8s cni
- 혼공파
- 파이썬
- EKS
- IaC
- autoscaling
- k8s calico
- GKE
- handson
- VPN
- 도서
- controltower
- PYTHON
- OS
- NFT
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |