iaC
ChatGPT가 알려주는 IaC
IaC (Infrastructure as Code)는 IT 인프라의 구성과 관리를 코드 형식으로 처리하는 접근 방식입니다. 이 방식은 프로그래밍 기법을 사용하여 인프라를 설정하고 유지보수함으로써, 전통적인 수동 프로세스의 복잡성과 오류 가능성을 줄입니다. IaC는 특히 클라우드 환경에서 자원을 효율적으로 관리하고 배포하는 데 매우 유용합니다.
즉, IaC는 인프라를 code로서 관리하는 것을 의미합니다.
장점
속도 및 효율성
코드를 통해 인프라를 자동으로 배포할 수 있어 시간이 크게 단축됩니다. 또한 필요할 때마다 즉시 환경을 재생성하거나 복제할 수 있습니다.
오류 감소
인프라를 코드로 관리함으로써 수동으로 관리하면서 발생할 수 있는 실수를 줄일 수 있습니다. 또한 코드를 통한 자동화는 설정의 일관성을 보장하며, 테스트와 검증을 통해 안정성을 높일 수 있습니다.
문서화 및 버전 관리
인프라의 설정을 코드 형태로 저장하고 관리함으로써, 소프트웨어 개발에서 git을 활용한 버전관리 인프라도 버전 관리가 가능해집니다.
비용 효율
적절한 리소스만 코드의 설정값으로 사용함으로써 클라우드 리소스의 활용을 최적화하여 전체적인 운영 비용을 절감할 수 있습니다.
보안 강화
보안 설정을 일관되게 적용할 수 있고, 필요에 따라 쉽게 업데이트하거나 조정할 수 있습니다.
iaC의 단점
러닝커브
초기 설정 비용
인프라를 코드로 전환하는 과정에서 기존의 프로세스와 환경을 재구성해야 할 수도 있습니다.
복잡성 증가
간단한 프로젝트나 작은 규모의 환경에서 IaC를 도입하는 것은 오히려 관리가 더 어려워질 수 있습니다.
코드 의존성
인프라의 모든 측면을 코드로 관리하게 되면, 코드의 오류가 전체적인 인프라에 영향을 미칠 수 있습니다. 즉, 잘못된 코드가 배포되면 심각한 문제를 일으킬 수 있습니다.
iaC 도구
Terraform
개발사: HashiCorp
HCL (HashiCorp Configuration Language)을 사용하여 정의합니다. 특히, Terraform은 '선언적' 접근 방식을 사용하여, 사용자가 원하는 최종 상태를 정의하고, 이 상태의 인프라를 구성합니다.
AWS CloudFormation
개발사: Amazon Web Services
AWS 전용 IaC 도구로, JSON 또는 YAML 형식으로 AWS 리소스를 정의할 수 있습니다. 특히, CloudFormation은 AWS 환경에서 인프라의 생성, 연결 및 관리를 자동화하며, Stack이라는 단위로 모든 리소스를 관리합니다.
Ansible
개발사: Red Hat
Ansible은 에이전트리스(agentless) 설계로, SSH를 통해 작업을 수행합니다. YAML을 사용하여 '플레이북'이라고 하는 구성 파일을 작성하며, 이를 통해 서버 구성, 프로그램 설치 등을 자동화합니다.
Puppet
개발사: Puppet Labs
Puppet은 '상태 기반'의 구성 관리 도구로, 서버의 원하는 상태를 정의하고 자동으로 이 상태를 유지할 수 있도록 돕습니다. 독자적인 언어를 사용하며, 마스터-슬레이브 아키텍처를 사용합니다.
Chef
개발사: Chef Software
Chef는 Ruby 기반의 IaC 도구로, '레시피'와 '쿡북'을 통해 인프라를 자동으로 구성하고 관리합니다. Chef는 코드를 사용하여 서버의 설정과 정책을 자동으로 적용합니다.
Terraform
Terraform은 HashiCorp에서 개발한 오픈 소스 도구로, Infrastructure as Code (IaC) 방식을 통해 인프라를 관리하고 조정하는 데 사용됩니다. 특히 여러 클라우드 제공자를 포함한 다양한 플랫폼에서 사용할 수 있으며, API를 통해 자동으로 인프라를 설정하고 관리할 수 있게 해줍니다.
동작
Init
terraform init
Terraform이 사용할 클라우드 프로바이더의 플러그인(프로바이더)을 다운로드하고 초기화합니다.
Plan
terraform plan
실행할 코드가 적용되어 변경될 인프라를 미리 확인합니다.
Apply
terraform apply
Plan에서 변경사항을 확인하고 사용자는 apply 명령어를 통해 승인하여 Terraform이 자동으로 인프라를 생성이나 수정, 삭제를 진행합니다.
Destroy
terraform destroy
Terraform을 통해 생성된 모든 리소스를 제거합니다.
구조
terraform 코드의 구조에는 provider와 resource로 나눌 수 있습니다.
provider
provider는 Terraform이 관리할 수 있는 각각의 서비스나 API와 연결하는 방법에 대해 작성합니다.
예를 들어 AWS, Azure, Google Cloud 등의 클라우드 제공자를 포함하여 다양한 기술 스택과 서비스가 각각의 Provider를 가집니다.
즉, Terraform으로 정의할 Infrastructure Provider를 의미합니다.
resource
resource는 Terraform을 사용하여 관리하려는 실제 클라우드 인프라의 구성 요소를 나타냅니다.
예를 들어 가상 머신, 네트워크 스위치, 로드 밸런서 등이 있으며 각 리소스는 특정 타입에 속하고, Provider가 관리하는 리소스 타입에 따라 다르게 정의됩니다.
즉, 실제로 생성할 인프라 자원을 의미합니다.
depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의합니다.
count : 선언된 개수에 따라 여러 리소스를 생성합니다.
for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성합니다.
provider : 동일한 프로바이더가 다수 정의되어 있는 경우 지정합니다.
lifecycle : 리소스의 수명주기 관리합니다.
provisioner : 리소스 생성 후 추가 작업 정의합니다.
timeouts : 프로바이더에서 정의한 일부 리소스 유형에서 create, update, delete에 대한 허용 시간 정의합니다.
data
data source는 리소스를 생성하거나 수정하지 않고, 데이터를 읽기만 합니다. 이를 통해 Terraform 코드 내에서 외부 리소스의 상세 정보를 가져올 수 있으며, 이 정보를 사용하여 다른 리소스의 설정에 활용할 수 있습니다.
즉, 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용합니다.
data "aws_ami" "example" {
most_recent = true
owners = ["self"]
filter {
name = "name"
values = ["my-image*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.example.id
instance_type = "t2.micro"
}
위의 코드에서 aws_ami 데이터 소스는 사용자의 계정에서 "my-image"로 시작하는 가장 최근의 AMI를 찾습니다. 그 후 aws_instance 리소스는 이 AMI를 사용하여 새로운 인스턴스를 생성합니다. 여기서 중요한 점은 data.aws_ami.example.id를 통해 조회된 AMI ID를 참조하고 있다는 것입니다.
depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의합니다.
count : 선언된 개수에 따라 여러 리소스를 생성합니다.
for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성합니다.
lifecycle : 리소스의 수명주기 관리합니다.
variables
변수는 설정을 매개 변수화하여 코드의 재사용성과 유연성을 높이는 데 사용됩니다. 변수를 사용하면 다양한 환경(예: 개발, 스테이징, 프로덕션)에 따라 다른 값을 쉽게 할당할 수 있고, 코드를 변경하지 않고도 구성을 조정할 수 있습니다.
즉, 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성할 수 있게 하기 위해 사용합니다.
default : 변수에 대한 기본 값으로 설정되어있지 않으면 대화식으로 사용자에게 변수에 대한 정보를 입력받게 합니다.
type : 변수에 대한 유형을 정의합니다. 이 값에 허용되는 값 유형에는 string, number, bool, list, map, set, object, tuple 과 같은 유형이 있으며 유형을 지정하지 않으면 any 유형으로 간주합니다.
description : 입력 변수의 설명을 입력합니다.
validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의합니다.
sensitive : 민감한 변수 값임을 알리고 terraform의 출력문에서 값 노출을 제한합니다.
nullable : 변수에 값이 없어도 됨을 지정합니다.
또한 입력변수는 선언되는 방식에 따라 변수의 우선순위가 있으므로, 이를 적절히 사용해 로컬 환경과 빌드 서버 환경에서의 정의를 다르게 하거나, 프로비저닝 파이프라인을 구성하는 경우 외부 값을 변수에 지정할 수 있습니다.
우선 순위 높은 순서대로 나열하자면 아래와 같습니다.
실행 후 입력 > variable 블록의 default 값 > 환경 변수 (TF_VAR 변수 이름) > terraform.tfvars에 정의된 변수 선언 > *.auto.tfvars에 정의된 변수 선언 > *.auto.tfvars.json에 정의된 변수 선언 > CLI 실행 시 -var 인수에 지정 또는 -var-file로 파일 지정
output
output은 Terraform 모듈이나 구성에서 생성된 리소스의 정보를 출력하는 용도로 활용되며, 특히 다른 모듈, 스택, 또는 외부 시스템과 정보를 공유할 때 사용됩니다.
즉, 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용합니다.
주의할 점은 output 결과에서 리소스 생성 후 결정되는 속성 값은 프로비저닝이 완료되어야 최종적으로 결과를 확인할 수 있고 terraform plan 단계에서는 적용될 값이 출력하지 않는다는 것입니다.
실습 준비
실습 준비는 Mac 기준으로 작성하였습니다.
terraform 설치
# tfenv 설치
brew install tfenv
# 설치 가능 버전 리스트 확인
tfenv list-remote
# 테라폼 1.8.1 버전 설치
tfenv install 1.8.1
# 테라폼 1.8.1 버전 사용 설정
tfenv use 1.8.1
# tfenv로 설치한 버전 확인
tfenv list
# 테라폼 버전 정보 확인
terraform version
# 자동완성
terraform -install-autocomplete
AWSCLI 설치
brew install awscli
설치 이후 aws configure 로 자격증명을 설정합니다.
EKSCTL 설치
brew install eksctl
KUBECTL 설치
brew install kubernetes-cli
HELM 설치
brew install helm
AWS 서울 리전(ap-northeast-2)에 default VPC 확인
aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq
이번 실습은 AWS 계정을 생성하면 기본적으로 생성되어 있는 vpc 내에서 진행을 할 것이기 때문에 defaultVPC를 확인합니다.
Amazon Linux 2 최신 AMI ID찾기
aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId]' --output text
위의 명령어를 토대로 현재 사용가능한 Amazon Linux2의 최신 AMI ID를 찾습니다.
ami-0217b147346e48e84
실습 - terraform 리소스 생성
테라폼 코드 작성
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-0217b147346e48e84"
instance_type = "t2.micro"
}
위의 형태로 ami에 위에서 출력한 AMI ID 값을 입력한 terraform 코드를 작성합니다.
Init
terraform init
init 명령어를 통해 초기화를 하게 되면 왼쪽에 .terraform이라는 환경 설정 폴더가 생성되며 위의 코드로 생성한 AMI 환경 설정이 들어가 있습니다.
Plan
terraform plan
terraform plan 명령어를 통해 해당 코드가 적합한 리소스를 생성하는지 먼저 확인합니다.
위의 사진과 같이 문제없이 리소스가 생성될 것이라는 것을 확인하실 수 있습니다.
Apply
terraform apply
apply 명령어를 통해 코드로 리소스를 생성합니다.
생성할 건지에 대해 한번 더 의견을 물으니 'yes'를 입력해서 진행합니다.
위의 사진처럼 aws_instance.example이 생성중인 것을 확인하실 수 있습니다.
그러고 AWS managed console에 접근하여 EC2 서비스에서 인스턴스 목록을 확인해보시면 위의 사진과 같이 EC2 Instance가 생성된 것을 확인하실 수 있습니다.
테라폼 정보 확인
terraform state list
위의 명령어를 통해 terraform으로 배포한 리소스들의 목록을 확인합니다.
terraform show
terraform show 명령어를 통해 배포된 리소스의 상세 정보를 확인합니다.
실습 - terraform 리소스 수정
테라폼 소스 수정
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-0217b147346e48e84"
instance_type = "t2.micro"
tags = {
Name = "aews-study"
}
}
tags라는 설정값이 추가된 코드를 입력합니다.
Plan
terraform plan
terraform plan 명령어를 통해 반영될 리소스를 확인합니다.
아까 생성할 때는 1 to add 였는데, 이번엔 수정이다보니 1 to change로 보여집니다.
Apply
terraform apply
apply 후 변경사항에 대해 승인하기 위해 yes를 입력하면 아래의 사진과 같이 정상적으로 리소스가 수정되는 것을 확인하실 수 있습니다.
AWS Managed Console에서 확인해보시면 위의 사진과 같이 tag가 설정되어 Name에 값이 보여지는 것을 확인하실 수 있습니다.
실습 - 테라폼 리소스
resource는 위에서 살펴본 바와 같이 선언된 항목을 생성하는 동작을 수행합니다. 또한 리소스에서 사용되는 유형들은 프로바이더에 종속성을 갖습니다. 특정 프로바이더의 유형만 추가해도 init 수행 시 해당 프로바이더를 설치합니다.
테라폼 코드 작성
resource "local_file" "abc" {
content = "123"
filename = "${path.module}/abc.txt"
}
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
Init
terraform init
Init 명령을 실행할 시 프로바이더 선언 없이도 리소스에 명시된 것을 토대로 자동으로 인식하여 명령을 실행합니다.
즉, 위의 코드에서 provider block이 존재하지 않는데 위의 사진처럼 aws와 local이라는 provider가 생성된 것이 자동으로 인식되었기 때문입니다.
실습 - 테라폼 종속성
Terraform에서 종속성은 리소스 간의 관계를 정의하고 순서를 관리할 수 있는 기능입니다. 즉, Terraform이 리소스를 생성, 업데이트, 삭제를 진행할 시에 순서를 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용한다.
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
resource "local_file" "def" {
content = "456!"
filename = "${path.module}/def.txt"
}
local_file이라는 리소스는 local에 file을 생성하는 resource입니다.
apply 명령어를 통해 terraform을 실행하면 abc라는 리소스는 123! 이라는 내용의 abc.txt file을 생성하고, def라는 리소스는 456!라는 def.txt file을 생성합니다.
종속성 부여
리소스 참조값 설정
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
resource "local_file" "def" {
content = local_file.abc.content
filename = "${path.module}/def.txt"
}
abc는 위와 같지만 def는 content의 내용이 local_file.abc의 content를 참조하게 종속성이 부여된 것을 확인하실 수 있습니다.
종속성이 리소스의 참조값으로 부여되었기 때문에 local_file.abc를 먼저 생성한 후 local_file.def를 생성합니다.
결과적으로 위의 사진처럼 같은 내용을 저장한 것을 확인하실 수 있습니다.
depends_on
리소스의 속성을 주입하지 않아도 두 리소스 간에 종속성이 필요한 경우에, depends_on 선언으로 적용 가능합니다.
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
resource "local_file" "def" {
depends_on = [
local_file.abc
]
content = "456!"
filename = "${path.module}/def.txt"
}
실습 - 리소스 속성 참조
테라폼 코드
resource "kubernetes_namespace" "example" {
metadata {
annotations = {
name = "example-annotation"
}
name = "terraform-example-namespace"
}
}
resource "kubernetes_secret" "example" {
metadata {
namespace = kubernetes_namespace.example.metadata.0.name # namespace 리소스 인수 참조
name = "terraform-example"
}
data = {
password = "P4ssw0rd"
}
}
위의 테라폼 코드는 쿠버네티스 프로바이더의 Namespace 리소스를 생성하고 이후 Secret을 해당 Namespace에 생성하는 종속성을 리소스 인수 값 값으로 생성하는 예시 코드입니다.
즉, Namespace의 이름만 변경해도, 해당 Namespace를 참조하는 모든 리소스가 업데이트되어 영향을 받습니다.
실습 - 데이터 소스
테라폼 코드 작성
data "aws_vpc" "default" {
default = true
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "primary" {
vpc_id = data.aws_vpc.default.id
availability_zone = data.aws_availability_zones.available.names[0]
cidr_block = "172.31.64.0/21"
tags = {
Name = "primary"
}
}
resource "aws_subnet" "secondary" {
vpc_id = data.aws_vpc.default.id
availability_zone = data.aws_availability_zones.available.names[1]
cidr_block = "172.31.72.0/21"
tags = {
Name = "secondary"
}
}
데이터 소스를 활용해 AWS region 내에서 사용 가능한 가용영역 목록을 출력하여 availability_zone에 인수로 정의하는 코드입니다.
aws_vpc.default data source에 의해 default vpc를 가져오게 됩니다. 하지만 이 값은 JSON형태로 출력되기 때문에 값을 참조할 때 하위의 값을 설정해주셔야 합니다. (resource에서 vpc_id를 data.aws_vpc.default.id로 참조한 것)
그리고 aws_availability_zones.available data source로는 현재 사용가능한 az를 가져옵니다.
resource로 선언된 aws_subnet.primary와 aws_subnet.secondary는 각각 해당 VPC내에 있는 0번째, 1번째 가용 영역을 가져옵니다. 추가로 aws_subnet으로 subnet을 생성하기 위해선 cidr_block이 필수로 필요한 변수인데, 본인의 vpc내에서 생성되어있는 기존의 subnet을 제외하고 CIDR Block 범위를 설정해주셔야 합니다.
init & plan & apply
terraform init && terraform plan && terraform apply -auto-approve
위의 명령어로 terraform을 한번에 동작시키겠습니다.
상단에 보이시는 것처럼 먼저 data source를 읽고 해당 data source의 값을 토대로 resource를 생성합니다.
terraform 리소스 확인
terraform state list
state list 명령어로 terraform으로 생성한 리소스 목록을 확인합니다.
AWS Managed Console에서 확인해보시면 해당 tag name으로 생성된 subnet을 확인하실 수 있습니다.
실습 - terrform console
terraform으로 생성한 data source나 resource들에 대해 어떤 값이 할당되었는지, 무슨 값을 참조하는건지 모를 수 있습니다.
terraform console
그럴 때 위의 명령어를 통해 terraform으로 구성한 파일을 로드하고 쿼리하거나 조회할 수 있습니다.
보통 terraform state list 명령어로 출력된 terraform로 생성된 리소스들을 위의 사진과 같이 조회하여 어떤 값들이 구성되어있는지 확인하실 수 있습니다.
echo "data.aws_availability_zones.available" | terraform console
위의 명령어처럼 echo를 통해 바로 확인하실 수도 있습니다.
실습 - terraform 입력 변수
입력 변수를 받아 resoucre 생성하기
terraform 코드 작성
variable "image_id" {
type = string
description = "The ID of the machine image (AMI) to use for the server."
default = "ami-0217b147346e48e84"
}
resource "aws_instance" "example" {
ami = var.image_id
instance_type = "t2.micro"
}
variable을 통해 image_id라는 입력 변수를 선언하였고, type은 string으로 default로 설정된 기본값은 ami-0217b147346e48e84으로 설정하였습니다.
또한, aws_instance.example라는 resource를 통해 ami 값에서 var.image_id로 위에서 생성한 입력 변수의 값을 참조하고 있습니다.
init & plan & apply
terraform init && terraform plan && terraform apply -auto-approve
위의 명령어로 init과 plan, apply를 동시에 진행합니다.
위의 사진과 같이 resource가 생성되고 있음을 확인하실 수 있습니다.
terraform 리소스 확인
terraform state list
state list 명령어로 terraform으로 생성한 리소스 목록을 확인합니다.
AWS Managed Console에서 확인해보시면 terraform에서 입력 변수로 설정한 AMI ID로 생성된 EC2가 존재하는 것을 확인하실 수 있습니다.
입력 변수의 유효성 검사
변수 블록 내에 validation 블록에서 condition을 통해 유효성 검사 규칙을 지정하고, error_message를 통해 condition 값의 결과가 false 인 경우 출력되는 메시지를 지정합니다. 또한 입력되는 변수 타입 지정 이외, 사용자 지정 유효성 검사가 가능합니다.
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 {
condition = can(regex("^ami-", var.image_id))
error_message = "The image_id value must starting with \"ami-\"."
}
}
init & plan & apply
terraform init && terraform plan && terraform apply -auto-approve
위의 명령어로 init과 plan, apply를 동시에 진행합니다.
위의 사진을 보시면 apply 한 후 프롬프트 창이 활성화되게 되는데, Enter a value에 5를 입력하였더니 길이가 4개 이상의 값을 입력하라는 error_message와 ami-i로 시작하라는 error_message가 출력되는 것을 확인하실 수 있습니다.
실습 - output
terraform 코드 작성
variable "image_id" {
type = string
description = "The ID of the machine image (AMI) to use for the server."
default = "ami-0217b147346e48e84"
}
resource "aws_instance" "example" {
ami = var.image_id
instance_type = "t2.micro"
}
output "instance_ip" {
value = aws_instance.example.public_ip
description = "The public IP address of the instance."
}
바로 위의 실습에서 진행했던 terraform 코드에서 output 값을 지정해보겠습니다.
instance_ip라는 이름으로 생성하고 value를 aws_instance.example.public_ip로 지정하여 생성된 EC2의 pulibc ip를 output으로 출력하는 코드입니다.
init & plan & apply
terraform init && terraform plan && terraform apply -auto-approve
위의 명령어로 init과 plan, apply를 동시에 진행합니다.
그럼 위의 사진과 같이 Outputs란에 instance_up의 값이 출력되는 것을 확인하실 수 있습니다.
AWS Managed Console에서 확인해보시면 terraform에서 output으로 출력된 public ip의 값이 실제로 EC2의 public ip의 값인 것을 확인하실 수 있습니다.
Reference
'스터디 이야기 > AWS EKS' 카테고리의 다른 글
[AEWS] 8-3. Amazon EKS - Iac (Terraform으로 AWS 리소스 배포 ) (0) | 2024.04.26 |
---|---|
[AEWS] 8-2. Amazon EKS - IaC (Terraform 심화) (1) | 2024.04.26 |
[AEWS] 7-2. Amazon EKS - CI/CD (ArgoCD/ArgoRollouts) (1) | 2024.04.16 |
[AEWS] 7-1. Amazon EKS - CI/CD (Jenkins) (0) | 2024.04.16 |