3.10 조건문

  • 테라폼의 조건식은 3학 연산자 형태
  • true, false로 확인된는 모든 표현식 사용 가능
  • 비교대상의 형태가 다르면 자동 변환해서 비교 -> 명시적 형태로 해야 오류가 없다
variable "enable_file" {
  default = true
}

resource "local_file" "foo" {
  count    = var.enable_file ? 1 : 0
  content  = "foo!"
  filename = "${path.module}/foo.bar"
}

output "content" {
  value = var.enable_file ? local_file.foo[0].content : ""
}

3.11 함수

  • 테라폼은 프로그래밍 언어적 특성을 지닌다
    -> 값의 유형을 변경하거나 조합할 수 있는 내장 함수 사용 가능 : Built-in Functions
  • 별도의 사용자 정의 함수를 지원하지는 않는다
  • 함수 종류에는 숫자, 문자열, 컬렉션, 인코딩, 파일 시스템, 날짜/시간, 해시/암호화, IP 네트워크, 유형 변환이 있다.
  • 테라폼 코드에 함수를 적용하면 변수, 리소스 속성, 데이터 소스 속성, 출력 값 표현 시 작업을 동적이고 효과적으로 수행할 수 있다
resource "local_file" "foo" {
  content  = upper("foo! bar!")
  filename = "${path.module}/foo.bar"
}

실행

#
terraform init && terraform plan && terraform apply -auto-approve
cat foo.bar ; echo

# 내장 함수 간단 사용
terraform console
>
-----------------
upper("foo!")
max(5, 12, 9)
lower(local_file.foo.content)

cidrnetmask("172.16.0.0/12")

exit
-----------------

3.12 프로비저너

  • 프로바이더와 비슷하게 '제공자'로 해석 되나 다름
  • 프로바이더로 실행되지 않는 커맨드, 파일 복사와 같은 역할 수행 : Provisioners
  • local-exec Provisioner : 테라폼이 실행되는 환경에서 수행할 커맨드를 정의 
  • 리눅스나 윈도우 등 테라폼을 실행하는 환경에 맞게 커맨드를 정의
  • 사용 인수
    • command(req)
      • 실행할 명령어 라인을 입력한다
      • `<<` 연산자를 통해 여러 줄의 커맨드를 입력 가능하다.
    • working_dir(option)
      • command의 명령을 실행할 디렉터리를 지정
      • 상대/절대 경로 설정
    • intgerpreter(option)
      • command의 명령을 실행하는데 필요한 인터프리터를 지정
      • 첫번째 인수는 인터프리터 이름, 그 이후부터는 인터프리터 인수 값
    • environment(option)
      • 실행시 환경 변수는 실행 환경의 값을 상속 받는다
      • 추가 재할당시 해당 인수에 key=value 형태로 설정한다
resource "null_resource" "example1" {
  
  provisioner "local-exec" {
    command = <<EOF
      echo Hello!! > file.txt
      echo $ENV >> file.txt
      EOF
    
    interpreter = [ "bash" , "-c" ]

    working_dir = "/tmp"

    environment = {
      ENV = "world!!"
    }

  }
}

실행

# 
terraform init -upgrade

# 
terraform plan && terraform apply -auto-approve
...
null_resource.example1: Creating...
null_resource.example1: Provisioning with 'local-exec'...
null_resource.example1 (local-exec): Executing: ["bash" "-c" "      echo Hello!! > file.txt\n      echo $ENV >> file.txt\n"]
...

# 
terraform state list
terraform state show null_resource.example1
cat /tmp/file.txt

remote-exec Provisioner(원격지 연결)

  • remote-exec, file 프로비저너를 사용하기 위해서는 원격지에 연결한 SSH, WinRM 연결 정의 필요
  • connection 블록 리소스 선언 시, 해당 리소스 내에 구성된 프로비저너에 대해 공통으로 선언되고
    프로비저너 내 에 선언되는 경우에는 해당 프로비저너에만 적용된다.
# connection 블록으로 원격지 연결 정의
resource "null_resource" "example1" {
  
  connection {
    type     = "ssh"
    user     = "root"
    password = var.root_password
    host     = var.host
  }

  provisioner "file" {
    source      = "conf/myapp.conf"
    destination = "/etc/myapp.conf"
  }

  provisioner "file" {
    source      = "conf/myapp.conf"
    destination = "C:/App/myapp.conf"

    connection {
        type     = "winrm"
        user     = "Administrator"
        password = var.admin_password
        host     = var.host
    }
  }
}

Provisioner Connection Settings(커넥션 적용 세팅)

Conbnection 적용 인수와 설명

 

원격 연결이 요구되는 프로비저너의 경우
->스크립트 파일을 원격 시스템에 업로드 해 해당 시스템의 기본 쉘에서 실행하도록 한다.
-> script_path 경우 적절한 위치를 지정하도록 한다.
-> 경로는 난수인 `%RAND%` 경로가 포함되어 생성된다.

Unix/Linux/macOS : /tmp/terraform_%RAND%.sh
Windows(cmd) : C:/windows/temp/terraform_%RAND%.cmd
Windows(PowerShell) : C:/windows/temp/terraform_%RAND%.ps1

Connecting through a Bastion Host with SSH(배스천 호스트를 통해 연결)

connection 적용 시 Bastion 호스트를 설정하는 인수와 설명3.1

3.12.4 file 프로비저너

  • 테라폼을 실행하는 시스템에서 연결 대상으로 파일 또는 디렉터리를 복사하는데 사용
  • 사용되는 인수
    • source : 소스 파일 또는 디렉터리로, 현재 작업 중인 디렉터리에 대한 상대 경로 또는 절대 경로로 지정할 수 있다. content와 함께 사용할 수 없다.
    • content : 연결 대상에 복사할 내용을 정의하며 대상이 디렉터리인 경우 tf-file-content 파일이 생성되고, 파일인 경우 해당 파일에 내용이 기록된다. source와 함께 사용할 수 없다.
    • destination : 필수 항목으로 항상 절대 경로로 지정되어야 하며, 파일 또는 디렉터리다.
  • destination 지정 시 주의해야 할 점은 winrm 연결은 디렉터리가 없는 경우 자동으로 생성하지만, ssh 연결의 경우 대상 디렉터리가 존재해야 한다.
  • 디렉터리를 대상으로 하는 경우에는 source 경로 형태에 따라 동작에 차이가 생긴다.
  • destination이 /tmp인 경우 source가 디렉터리로 /foo 처럼 마지막에 /가 없는 경우 대상 디렉터리에 지정한 디렉터리가 업로드되어 연결된 시스템에 /tmp/foo 디렉터리가 업로드된다.
  • source가 디렉터리로 /foo/ 처럼 마지막에 /가 포함되는 경우 source 디렉터리 내의 파일만 /tmp 디렉터리에 업로드된다.

예시 - 파일 프로비저너 구성 예시

resource "null_resource" "foo" {
  
  # myapp.conf 파일이 /etc/myapp.conf 로 업로드
  provisioner "file" {
    source      = "conf/myapp.conf"
    destination = "/etc/myapp.conf"
  }
  
  # content의 내용이 /tmp/file.log 파일로 생성
  provisioner "file" {
    content     = "ami used: ${self.ami}"
    destination = "/tmp/file.log"
  }
  
  # configs.d 디렉터리가 /etc/configs.d 로 업로드
  provisioner "file" {
    source      = "conf/configs.d"
    destination = "/etc"
  }
  
  # apps/app1 디렉터리 내의 파일들만 D:/IIS/webapp1 디렉터리 내에 업로드
  provisioner "file" {
    source      = "apps/app1/"
    destination = "D:/IIS/webapp1"
  }

}

3.12.5 remote - exec Provisioner

  • 예 - AWS의 EC2 인스턴스를 생성 해당 VM에서 명령을 실행하고 패키지를 설치하는 등의 동작을 의미한다.
  • 사용하는 인수는 다음과 같고 각 인수는 서로 배타적이다.
    • inline : 명령에 대한 목록으로 [ ] 블록 내에 “ “로 묶인 다수의 명령을 , 로 구분해 구성한다.
    • script : 로컬의 스크립트 경로를 넣고 원격에 복사해 실행한다.
    • scripts : 로컬의 스크립트 경로의 목록으로 [ ] 블록 내에 “ “로 묶인 다수의 스크립트 경로를 , 로 구분해 구성한다
  • script 또는 scripts의 대상 스크립트 실행에 필요한 인수는 관련 구성에서 선언할 수 없으므로 필요할 때 file 프로바이더로 해당 스크립트를 업로드하고 inline 인수를 활용해 스크립트에 인수를 추가한다.

구성예시

resource "aws_instance" "web" {
  # ...

  # Establishes connection to be used by all
  # generic remote provisioners (i.e. file/remote-exec)
  connection {
    type     = "ssh"
    user     = "root"
    password = var.root_password
    host     = self.public_ip
  }

  provisioner "file" {
    source      = "script.sh"
    destination = "/tmp/script.sh"
  }

  provisioner "remote-exec" {
    inline = [
      "chmod +x /tmp/script.sh",
      "/tmp/script.sh args",
    ]
  }
}

3.13 null_resource와 terraform_data

테라폼 1.4가 릴리즈 되면서 기존 null_resource를 대체하는 terraform_data리소스가 추가 되었다.
두 리소스의 의미와 사용 방식 확인 필요
  • null_resource - 아무 작업도 수행하지 않는 리소스 구현
    • 이런 리소스가 필요한 이유??
      • 테라폼 프로비저닝 동작을 설계하면서 사용자가 의도적으로 프로비저닝하는 동작을 조율해야 하는 상황이 발생함
      • 프로바이더가 제공하는 리소스 수명주기 관리만으로는 이를 해결하기 어렵기 때문.
    • 주로 사용되는 시나리오
      • 프로비저닝 수행 과정에서 명령어 실행
      • 프로비저너와 함께 사용
      • 모듈, 반복문, 데이터 소스, 로컬 변수와 함께 사용
      • 출력을 위한 데이터 가공
    • 예를 들어 다음의 상황을 가정
      • AWS EC2 인스턴스를 프로비저닝하면서 웹서비스를 실행시키고 싶다
      • 웹서비스 설정에는 노출되어야 하는 고정된 외부 IP가 포함된 구성이 필요하다. 따라서 aws_eip 리소스를 생성해야 한다.
    • AWS EC2 인스턴스를 프로비저닝하기 위해 aws_instance 리소스 구성 시 앞서 확인한 프로비저너를 활용하여 웹서비스를 실행하고자 한다

예시

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_security_group" "instance" {
  name = "t101sg"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

}

resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  subnet_id              = "subnet-dbc571b0" 
  private_ip             = "172.31.1.100"
  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 80 &
              EOF

  tags = {
    Name = "Single-WebSrv"
  }

  provisioner "remote-exec" {
    inline = [
      "echo ${aws_eip.myeip.public_ip}"
     ]
  }
}

resource "aws_eip" "myeip" {
  #vpc = true
  instance = aws_instance.example.id
  associate_with_private_ip = "172.31.1.100"
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}
  • aws_eip가 생성되는 고정된 IP를 할당하기 위해서는 대상인 aws_instance의 id값이 필요하다
  • aws_instance의 프로비저너 동작에서는 aws_eip가 생성하는 속성 값인 public_ip가 필요하다
  • 상호 참조 발생
    • 테라폼 구성 정의에서 상호 참조가 발생하는 상황
    • 실제 코드에서 plan 을 수행하면 에러가 발생한다.

수정 - 둘 중 하나의 실행 시점을 한 단계 뒤로 미룬다 -> main.tf 수정

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_security_group" "instance" {
  name = "t101sg"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

}

resource "aws_instance" "example" {
  ami                    = "ami-0c9c942bd7bf113a2"
  instance_type          = "t2.micro"
  subnet_id              = "subnet-0b92d8356a0cbca38"
  private_ip             = "172.31.0.100"
  key_name               = "kp-kaje" # 각자 자신의 EC2 SSH Keypair 이름 지정
  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 80 &
              EOF

  tags = {
    Name = "Single-WebSrv"
  }

}

resource "aws_eip" "myeip" {
  #vpc = true
  instance = aws_instance.example.id
  associate_with_private_ip = "172.31.0.100"
}

resource "null_resource" "echomyeip" {
  provisioner "remote-exec" {
    connection {
      host = aws_eip.myeip.public_ip
      type = "ssh"
      user = "ubuntu"
      private_key =  file("/home/kaje/kp-kaje.pem") # 각자 자신의 EC2 SSH Keypair 파일 위치 지정
      #password = "qwe123"
    }
    inline = [
      "echo ${aws_eip.myeip.public_ip}"
      ]
  }
}

output "public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP of the Instance"
}

output "eip" {
  value       = aws_eip.myeip.public_ip
  description = "The EIP of the Instance"
}

수정 실행

# 프로비저너 필요로 설치
terraform init -upgrade

# 실행 : EIP 할당 전 (임시) 유동 공인 IP 출력
terraform plan
terraform apply -auto-approve
...
null_resource.echomyeip (remote-exec): Connected!
null_resource.echomyeip (remote-exec): 13.125.25.238
...
Outputs:
eip = "13.125.25.238"
public_ip = "43.201.63.58"

#
terraform state list
terraform state show aws_eip.myeip
terraform state show aws_instance.example
terraform state show null_resource.echomyeip

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 데이터소스 값 확인
echo "aws_instance.example.public_ip" | terraform console
echo "aws_eip.myeip.public_ip" | terraform console

# 출력된 EC2 퍼블릭IP로 curl 접속 확인
MYIP=$(terraform output -raw eip)
while true; do curl --connect-timeout 1  http://$MYIP/ ; echo "------------------------------"; date; sleep 1; done
  • 삭제: `terraform destroy -auto-approve`
  • null_resource는 정의된 속성이 ‘id’가 전부이므로, 선언된 내부의 구성이 변경되더라도 새로운 Plan 과정에서 실행 계획에 포함되지 못한다.
  • 따라서 사용자가 null_resource에 정의된 내용을 강제로 다시 실행하기 위한 인수로 trigger가 제공된다.
  • trigger는 임의의 string 형태의 map 데이터를 정의하는데, 정의된 값이 변경되면 null_resource 내부에 정의된 행위를 다시 실행한다.]
  • trigger 예시
resource "null_resource" "foo" {
  triggers = {
    ec2_id = aws_instance.bar.id # instance의 id가 변경되는 경우 재실행
  }
  ...생략...
}

resource "null_resource" "bar" {
  triggers = {
    ec2_id = time() # 테라폼으로 실행 계획을 생성할 떄마다 재실행
  }
  ...생략...
}

terraform_data (번역)

  • 이 리소스 또한 자체적으로 아무것도 수행하지 않는다.
  • null_resource는 별도의 프로바이더 구성이 필요하다는 점과 비교하여
    추가 프로바이더 없이 테라폼 자체에 포함된 기본 수명주기 관리자가 제공된다는 것이 장점이다.
  • 사용 시나리오는 기본 null_resource와 동일하며 강제 재실행을 위한 trigger_replace와 상태 저장을 위한 input 인수와 input에 저장된 값을 출력하는 output 속성이 제공된다.
  • triggers_replace에 정의되는 값이 기존 map 형태에서 tuple로 변경되어 쓰임이 더 간단해졌다
  • terraform_data 리소스의 trigger_replace 정의와 동작 예제
resource "terraform_data" "foo" {
  triggers_replace = [
    aws_instance.foo.id,
    aws_instance.bar.id
  ]

  input = "world"
}

output "terraform_data_output" {
  value = terraform_data.foo.output  # 출력 결과는 "world"
}

3.14 moved block

  • 테라폼의 State에 기록되는 리소스 주소의 이름이 변경되면 기존 리소스는 삭제되고 새로운 리소스가 생성됨을
    앞의 설명에서 확인.
  • 하지만 테라폼 리소스를 선언하다 보면 이름을 변경해야 하는 상황이 발생하기도 하는데, 예를 들면 다음과 같다.
    • 리소스 이름을 변경
    • count로 처리하던 반복문을 for_each로 변경
    • 리소스가 모듈로 이동하여 참조되는 주소가 변경
  • 리소스의 이름은 변경되지만 이미 테라폼으로 프로비저닝된 환경을 그대로 유지하고자 하는 경우 테라폼 1.1 버전부터 moved 블록을 사용할 수 있다.
  • ‘moved’라는 단어가 의미하는 것처럼 테라폼 State에서 옮겨진 대상의 이전 주소와 새 주소를 알리는 역할을 수행한다.
  • moved 블록 이전에는 State를 직접 편집하는 terraform state mv 명령을 사용하여 State를 건드려야 하는 부담이 있었다면, moved 블록은 State에 접근 권한이 없는 사용자라도 변경되는 주소를 리소스 영향 없이 반영할 수 있다.

 

3.15 System Environment Variables for CLI

  • 테라폼은 환경 변수를 통해 실행 방식과 출력 내용에 대한 옵션을 조절 가능
    • 시스템 환경 변수 설정으로, 영구적으로 로컬 환경에 적용되는 옵션이나 별도 서버 환경에서 실행하기 위한 옵션을 부여 가능.
    • 이를 통한 로컬 작업 환경과 다른 환경 구성에서만 사용될 특정 옵션을 적용.
Mac/리눅스/유닉스: export <환경 변수 이름>=<값>
Windows CMD: set <환경 변수 이름>=<값> 
Windows PowerShell: $Env:<환경 변수 이름>='<값>'

3.15.1 TF_LOG

  • 테라폼의 stderr 로그에 대한 레벨을 정의
    • trace, debug, info, warn, error, off를 설정할 수 있고 관련 환경 변수가 없는 경우 off와 동일하다
    • 디버깅을 위한 로그 관련 환경 변수 설명은 다음과 같다
      • TF_LOG: 로깅 레벨 지정 또는 해제
      • TF_LOG_PATH: 로그 출력 파일 위치 지정
      • TF_LOG_CORE: TF_LOG와 별도로 테라폼 자체 코어에 대한 로깅 레벨 지정 또는 해제
      • TF_LOG_PROVIDER: TF_LOG와 별도로 테라폼에서 사용하는 프로바이더에 대한 로깅 레벨 지정 또는 해제
    • 환경에 맞게 TF_LOG를 info로 설정하고, terraform plan 동작을 실행하면 테라폼 출력에 관련 로그가 출력된다
      (TF_LOG=info terraform plan ...)

3.15.2 TF_INPUT

  •  값을 false 또는 0으로 설정하면 테라폼 실행 시 인수에 -input=false 를 추가한 것과 동일한 수행 결과를 확인
    • 환경에 맞게 TF_INPUT을 0으로 설정하고 terraform plan 동작 실행하면 입력받는 동작을 수행하지 않으므로 입력 변수를 입력해야 하는 경우 에러가 출력된다
    • TF_INPUT=0 terraform plan Error : No value for required variable

3.15.3 TF_VAR_name 

TF_VAR_<변수 이름>을 사용하면 입력 시 또는 default로 선언된 변수 값을 대체한다 ← 3.6절에서 확인!

# 3.15.4
# TF_CLI_ARGS="-input=false" terraform apply -auto-approve 는 terraform apply -input=false -auto-approve 와 같다
TF_CLI_ARGS="-input=false" terraform apply -auto-approve
Error: No value for required variable

# TF_CLI_ARGS_apply로 인수를 정의하면 terraform apply 커맨드 수행 시에만 동작한다
export TF_CLI_ARGS_apply="-input=false"
terraform apply -auto-approve
<에러>

terraform plan
<정상 계획 예측 출력>

3.15.5 TF_DATA_DIR

State 저장 백엔드 설정과 같은 작업 디렉터리별 데이터를 보관하는 위치를 지정

  • 데이터는 .terraform 디렉터리 위치에 기록되지만 TF_DATA_DIR에 경로가 정의되면 기본 경로를 대체하여 사용된다.
  • 일관된 테라폼 사용을 위해서 해당 변수는 실행 시마다 일관되게 적용될 수 있도록 설정하는 것이 중요하다.
  • 설정 값이 이전 실행 시에만 적용되는 경우 init 명령으로 수행된 모듈, 아티팩트 등의 파일을 찾지 못한다.
  • 이미 terraform init이 수행된 상태에서 TF_DATA_DIR로 경로를 재지정하고 실행하는 경우 플러그인 설치가 필요하다는 메시지 출력을 확인할 수 있다.
TF_DATA_DIR=./.terraform_tmp terraform plan
Error: Required plugins anr not installed
블로그 이미지

감동맨

rkaehdaos의 블로그

,

3.5 데이터 소스

  • 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용
  • 데이터 소스 블록은 data로 시작, 이후 데이터 소스 유형 정의
data "<리소스 유형>"	"<이름>" {
	<인수> = <값>
}
  •  
    • 첫번째 _를 기준으로 앞은 프로바이더 이름,
      뒤는 프로바이더에서 제공하는 리소스 유형
      ex)local_file - local이라는 이름의 프로바이더에서 제공하는 file이라는 리소스 유형
    • 데이터 소스 유형 선언 뒤에는 고유한 이름을 붙인다
      • 리소스 이름과 마찬가지로 중복 될 수 없다 -> 식별자 역할
    • 이름 뒤 데이터 소스 유형에 대한 구성 인수들은 {} 안에 선언한다
data "local_file" "abc" {
  filename = "${path.module}/abc.txt"
}
  • 데이터 소스  정의시 사용가능한 메타인수
    • depends_on
      • 종속성 선언
      • 선언 구성요소와 생성 시점에 대해 정의
    • count
      • 선언된 개수에 따라 여러 리소스 생성
    • for_each
      • map 또는 set타입의 데이터 배열의 값을 기준으로 여러 리소스 생성
    • lifecycle
      • 리소스의 수명 주기 관리
# 실습 확인을 위해서 abc.txt 파일 생성
echo "t101 study - 2week" > abc.txt

# -auto-approve가 있으면 yes를 묻지 않음
terraform init && terraform plan && terraform apply -auto-approve
terraform state list

# 테라폼 콘솔 : 데이터 소스 참조 확인
echo "data.local_file.abc" | terraform console
  • 데이터 소스 속성 참조

3.6 입력 변수 Variable

  • Input Variable로 테라폼에서는 정의
    • 인프라를 구성하는데 필요한 속성 값을 정의
    • 코드 변경 없이 여러 인프라 생성 가능하게 하는데 목적
  • 변수 선언 방식
    • 변수는 variable로 시작되는 블록으로 구성된다.
    • 변수 블록 뒤의 이름 값은 동일 모듈 내 모든 변수 선언에서 고유해야 하며, 이 이름으로 다른 코드 내에서 참조된다.
    • 테라폼 예약변수 이름으로는 사용이 불가능하다.
# variable 블록 선언의 예
variable "<이름>" {
 <인수> = <값>
}

variable "image_id" {
 type = string
}
안되는 것들 : source, version, providers, count, for_each, lifecycle, depends_on, locals
  • 변수 정의 시 사용 가능한 메타인수
    • default
      • 변수 값을 전달하는 여러 가지 방법을 지정하지 않으면 기본값 전달
      • 기본값이 없으면 대화식으로 사용자에게 변수에 대한 정보를 물어봄
    • type
      • 변수에 허용되는 값 타입 정의
      • string, number, bool, list, map, set, object, tuple
      • 타입을 지정하지 않으면 any 유형으로 간주
    • description : 입력 변수의 설명
    • validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의
    • sensitive : 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한 (암호 등 민감 데이터의 경우)
    • nullable : 변수에 값이 없어도 됨을 지정
  • 변수 유형
    • 기본 유형
      • string, number, bool, any(명시적으로 모든 유형이 허용됨을 선언)
    • 집합 유형
      • list (<유형>): 인덱스 기반 집합
      • map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬
      • set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬
      • object ({<인수 이름>=<유형>, …})
      • tuple ([<유형>, …])
    • list와 set : 선언하는 형태가 비슷하지만 참조 방식이 인덱스와 키로 각각 차이가 있다
    • map와 set : 선언된 값이 정렬되는 특징
  • 입력 변수 사용 예시
# number
variable "number_example" {
  description = "An example of a number variable in Terraform"
  type        = number
  default     = 42
}


# list
variable "list_example" {
  description = "An example of a list in Terraform"
  type        = list
  default     = ["a", "b", "c"]
}


# 조건 결합1 : 모든 항목이 number인 list
variable "list_numeric_example" {
  description = "An example of a numeric list in Terraform"
  type        = list(number)
  default     = [1, 2, 3]
}




# 조건 결합2 : 모든 값이 String 인 mapvariable "map_example" {
  description = "An example of a map in Terraform"
  type        = map(string)

  default = {
    key1 = "value1"
    key2 = "value2"
    key3 = "value3"
  }
}

# 구조적 유형 (Structural type)
# object나 tuple 제약 조건을 사용해서 만든 복잡 유형
variable "object_example" {
  description = "An example of a structural type in Terraform"
  type        = object({
    name    = string
    age     = number
    tags    = list(string)
    enabled = bool
  })

  default = {
    name    = "value1"
    age     = 42
    tags    = ["a", "b", "c"]
    enabled = true
  }
}

 

3.7 local 지역 값

variable "prefix" {
  default = "hello"
}

locals {
  name    = "terraform"
  content = "${var.prefix} ${local.name}"
  my_info = {
    age    = 20
    region = "KR"
  }
  my_nums = [1, 2, 3, 4, 5]
}

locals {
  content = "content2" # 중복 선언되었으므로 오류가 발생한다.
}
  • 코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local (지역 값)
  • 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작
  • 선언된 모듈 내에서만 접근 가능 (입력변수와 차이점)
  • 변수처럼 실행시 입력받을 수 없음
  • 장단점
    • 장점 : 값이나 표현식을 반복적으로 사용할 수 있는 편의 제공
    • 단점
      • 빈번하게 여러 곳에서 사용되는 경우 실제 값에 대한 추적이 어려움
      • 유지 관리 부담 커짐
      • 주의할 것      

3.8 출력 output

output "instance_ip_addr" {
  value = "http://${aws_instance.server.private_ip}"
}
  • 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용된다.
  • 프로그래밍 언어에서 코드 내 요소 간에 제한된 노출을 지원하듯,
    테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용 가능
  • 자바의 getter와 비슷한 역할?
  • output의 용도
    • 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력
    • 자식 모듈의 특정 값을 정의하고 루트 모듈에서 결과를 참조
    • 서로 다른 루트 모듈의 결과를 원격으로 읽기 위한 접근 요소
  • 정의시 사용 가능 메타 인수
    • description : 출력 값 설명
    • sensitive : 민감한 출력 값임을 알리고 테라폼의 출력문에서 값 노출을 제한
    • depends_on : value에 담길 값이 특정 구성에 종속성이 있는 경우 생성되는 순서를 임의로 조정
    • precondition : 출력 전에 지정된 조건을 검증
  • 예시
resource "local_file" "abc" {
  content  = "abc123"
  filename = "${path.module}/abc.txt"
}

output "file_id" {
  value = local_file.abc.id
}

output "file_abspath" {
# abspath : 파일 시스템 경로를 포함하는 문자열을 가져와 절대 경로로 변환하는 함수
  value = abspath(local_file.abc.filename)
}
  • 실행
# plan 실행 시, 이미 정해진 속성은 출력을 예측하지만
# file_id는 예측할수 없으므로 known after apply로 표시
terraform init && terraform plan
...
Changes to Outputs:
  + file_abspath = "/Users/gasida/Downloads/workspaces/3.8/abc.txt"
  + file_id      = (known after apply)


# 
terraform apply -auto-approve
...
Outputs:
file_abspath = "/Users/gasida/Downloads/workspaces/3.8/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"

#
terraform state list
terraform output

3.9 반복문

list 형태의 값 목록이나 Key-Value 형태의 문자열 집합인 데이터가 있는 경우
동일한 내용에 대해 테라폼 구성 정의를 반복적으로 하지 않고 관리할 수 있다.

count : 반복문, 정수 값만큼 리소스나 모듈을 생성

resource "local_file" "abc" {
  count    = 5
  content  = "abc${count.index}"
  filename = "${path.module}/abc${count.index}.txt"
}

output "fileid" {
  value = local_file.abc.*.id
}

output "filename" {
  value = local_file.abc.*.filename
}

output "filecontent" {
  value = local_file.abc.*.content
}

for_each : 반복문, 선언된 key 값 개수만큼 리소스를 생성

  • each.key , each.value :  인스턴스에 해당하는 map의 key, value
resource "local_file" "abc" {
  for_each = {
    a = "content a"
    b = "content b"
  }
  content  = each.value
  filename = "${path.module}/${each.key}.txt"
}

for : 복합 형식 값의 형태를 변환하는 데 사용 (for_each와 다름에 유의)

variable "names" {
  default = ["a", "b", "c"]
}

resource "local_file" "abc" {
  content  = jsonencode(var.names) # 결과 : ["a", "b", "c"]
  filename = "${path.module}/abc.txt"
}

dynamic : 리소스 내부 속성 블록을 동적인 블록으로 생성

variable "names" {
  default = {
    a = "hello a"
    b = "hello b"
    c = "hello c"
  }
}

data "archive_file" "dotfiles" {
  type        = "zip"
  output_path = "${path.module}/dotfiles.zip"

  dynamic "source" {
    for_each = var.names
    content {
      content  = source.value
      filename = "${path.module}/${source.key}.txt"
    }
  }
}
블로그 이미지

감동맨

rkaehdaos의 블로그

,

CHAPTER 1 IaC와 테라폼

1.1 인프라 자동화의 성숙도 변화

인프라 방식

  1. Manual
    1. 문서로 관리
      1. 인프라 관련 모든 정보
      2. 인프라 구성 방법
      3. 인프라 변경 방법
      4. 기존 아키텍처에 대한 내용
    2. 모든 변경을 문서로 남겨야 한다
      그렇지 않으면 기억에 의존한다
    3. 장점?
      1. 정보와 구성을 곧바로 눈으로
        매뉴얼을 보고 확인할 수 있다
      2. 물리적으로 가까이 있는 사람에게
        즉각 정보를 전달 할 수 있다
    4. 단점?
      1. 매뉴얼은 사람이 아닌 인프라가 이해할 수 없다
      2. 결국 사람이 실제 작업을 위한 명령어와 구성 파일을 별도로 준비해야 한다
      3. 변경 사항 반영 어려움 → 재사용성 낮음
      4. 버전 관리 불가능 → 업데이트 위해서는 새로 작성 필요
  2. Script
    1. 반복되는 작업 → 스크립트를 작성해서 자동화(작업자 노하우)
    2. 신규 서버 설정, 특정 기능 수행
    3. 반복 지루한 작업을 줄이고, 미리 정의한 동작 한번에 실행 → 오류를 줄여준다
    4. 단점
      1. 동작하는 코드 증가
      2. 시스템의 응답이나 상태를 고려해야하는 케이스 발생
      3. 스크립트 특성상 상태와 상관없이 실행
      4. 최종 상태가 스크립트 결과와 일치 하지 않는 경우가 종종 발생
  3. VM
    1. 가상화 솔루션 → 가상 머신 → 인프라 운영 관리 편해짐
    2. 가상 머신 이미지 템플릿 → 저장, 반복적 사용 가능
    3. 필요 패키지 설치 + 필요설정 추가한 템플릿 가능
    4. 스냅샷 : 수동적이지만 state를 보관 가능하다
    5. 단점?
      1. 가상화 솔루션 범위에 포함이 되지 않거나
        서로 다른 툴 사용? → 일관된 관리 불가능
      2. 하이퍼 바이저에 의존하게 된다
      3. 이미지 변경 → 수작업
  4. Cloud Infra
    1. 인프라를 더 이상 소유하지 않고
      상품화된 인프라와 데이터 센터를 사용하는 단계
      → 기본적으로 클라우드 리소스를 원격 관리 가능
    2. 빠른 인프라 시작 구성 가능, 뛰어난 확장성
    3. 단점
      1. 클라우드 제공자마다 서로 다른 API
      2. 멀티 클라우드 자동화 위해서는 ?
        → 개별적인 작업이 추가로 필요
  5. Container
    1. 물리적머신 → VM→OS를 가상한 환경 제공
    2. 최근 데브 옵스 흐름 → 더 빠른 성장
    3. k8s같은 오케스트레이션 사용
      → 특정 서버가 아닌 적절한 리소스의 어떤 서버에 배포
      → 리소스 활용율 ↑, 이전보다 더 자동화
    4. 가용한 모든 리소스 확장 가능
    5. 매우 빠른 배포 전략 도입
    6. 역설적인 단점 : 자동화의 역설
      1. 기존 : 물리적 하드웨어가 VM으로 가상화
        → 이를 제어하기 위한 SW 관리 작업이 추가 됨
      2. 컨테이너도 관리와 배포를 위한 제어 시스템 구축
        → 모니터링 노력이 상대적 커짐 → 자동화의 역설
    7. 가장 최신 기술 → 컨테이너 개발/운영 전문 인력이 부족

_1.2 프로세스로서의 자동화

  • 자동화: 각 프로세스 작업을 통합하고 재활용성을 높이는 것이 중요하다
  • 이전 자동화 방향성 : “기술 주도형 접근”
    • 강력한 엔지니어적 협조
    • 빅뱅방식 적용
      • 개발 기간을 정하고
      • 모든 시스템을 일괄적으로
      • 구축 또는 업그레이드 하는 개발 방식
    • 느리고 매뉴얼화된 절차적단계를 거쳐 프로세스 진행
    • 단계별로 파편화된 자동화 → 특정 환경에서만 동작
    • 따라서 내부에서 구축된 리소스 자동화에 적합
  • 새로운 자동화 필요 : CI/CD
    • 고정적이지 않고 추상화된 리소스 → 기존 자동화 재적용 어려움
    • 동적 인프라(like 클라우드)+ 다른 플랫폼 기술 융합 자동화 → 프로세스적인 접근법 필요
    • 작은 규모로 독립성을 유지 관리 → 주기적인 변경과 적용 방식 지향 → 빠른 시장 적응과 장애 극복 능력 획득 필요
    • 코드 표현 방식에 기반한 자동화 설계 → 개별 서비스를 릴리즈 하기 위한 반복 작업 효율화
  • 자동화의 역설 어게인
    • 기존 하드웨어 기반에 비해 시간과 인력 소모 → 자동화 할수 있는 API인터페이스 제공
    • 자동화(가상화↑, 컨테이너화) → 일반적으로 업무량이 줄고 더 나은 워라밸이 가능해야 한다
    • 자동화의 역설 → 무수히 많은 반복과 검증 작업 필요 → 이전보다 더 많은 시간과 노력을 요구하기도 한다
    • 더 아이러니 한 것
      • 인프라 관리, 운영을 위한 단계 방식이 어느 것 하나 없어지지 않음
      • VM, 베어메탈 같은 서버 환경이 현재도 존재
      • 기존 서버 환경보다 더 작은 Edge, IOT 인프라도 관리대상 추가

1.3 IaC의 이해

  • 코드로 인프라를 관리 한다는 것
    • “자유롭게 변경”하고
    • “환경을 이해”하고
    • “반복적으로 동일한 상태”를 만들 수 있다
    • 이에 대한 명세를 별도의 문서로 정리하지 않아도
      인프라가 명확하게 정의되어 남게 된다
  • 좋은 코드의 특징을 먼저 살펴보면
    • 잘 작동함
    • 관리가 쉬움
    • 읽기 쉬움
    • 변경이 쉬움
    • 모듈화 됨
    • 간결하고 명확함
    • 테스트 가능함
    • 효율적임
    • 보기 좋음 -우아함
  • 좋은 코드의 특징과 IaC의 관계
    • 인프라도 좋은 코드처럼 관리가 가능
      • 자동화를 위해 → 문서화
      • 인프라 종속성 분석해서 관리
      • 인프라 자원 있을때마다 변경
      • 결과물이나 결과물 만드는 도구를 관리
    • 인프라를 위한 좋은 코드 : 연습이 필요하다
> IaC (Infrastructure as Code)는 코드로 인프라를 관리하는 것으로,
> 좋은 코드의 특징과 비슷합니다.
> 인프라도 코드처럼 잘 작동하고, 관리가 쉽고, 읽기 쉽고, 변경이 쉽고,
> 모듈화되며, 간결하고 명확하며, 테스트 가능하고, 효율적이며, 우아합니다.
> IaC는 이러한 좋은 코드의 특징을 활용하여 인프라를 자동화하고,
> 문서화하고, 인프라 종속성을 분석하여 관리하며, 인프라 자원이 있을 때마다
> 변경하며, 결과물이나 결과물을 만드는 도구를 관리합니다.
> 이러한 과정 없이, 좋은 코드가 좋은 인프라 자동화로 이어지도록 만들어줍니다.
  • IaC 장점
    • 속도와 효율
    • 버전 관리
    • 협업
    • 재사용성
    • 기술의 자산화
  • IaC 우려 측면
    • 코드 문법 학습 - 새로운 도구를 위한 학습 필요
    • 파이프라인 통합 - 기존 워크플로에 자동화를 위한 수고가 추가로 필요
    • 대상 인프라 이해 필요 - 관리 대상이 되는 인프라의 지식이 필요

1.4 테라폼의 특성

  • 테라폼 : 하시코프사
  • 3가지 철학 - 워크플로에 집중, IaC, 실용주의
    • workflow -Workflows, not technologies
      • solution - 기술, 기능 구현을 위해 만들어진다
      • 어떤 기능 하나가 모든 것을 해결해주지 않음
      • 테라폼 : 개발자/관리자가 일하는 방식과 유사한
        workflow를 만들기 위한 도구로 설계
      • workflow대상: 인프라 구성과 배포, 보안구성, 모니터링 도구 설정 등
      • 어떤 기술이 사용되더라도 workflow를 통해
        환경 변화에 크게 구애 받지 않는다
      • 인프라 환경이 바뀌어도 workflow는 유지될 수 있음
    • IaC
      • 의미: 구현/구성되는 모든 것이 코드로 표현되어야 한다
      • ‘문서화된 실행 가능한 텍스트’
      • 기록 뿐 아니라 버전 관리 대상 → 많은 장점
    • 실용주의(pragmatism)
      • 현실적인 해결을 위해 이상을 재평가 해야하는 상황이 많음
      • 실용주의:
        • 새로운 아이디어와 접근 방식, 기술을 다시 평가
        • 타협이 아닌 이전의 것이 틀릴 수 있다는 사실을 받아들이는 적응 능력
        • 혁신에 있어 매우 중요

책에 나오지 않은데 스터디에서 설명해준 내용

- 페이저 비활성화

export AWS_PAGER=""
 

AWS CLI 페이지 매김 옵션 사용 - AWS Command Line Interface

--starting-token 파라미터는 null이거나 비어있을 수 없습니다. 이전 명령이 NextToken 값을 반환하지 않으면 반환할 더 이상의 항목이 없는 것이기 때문에 명령을 다시 호출할 필요가 없습니다.

docs.aws.amazon.com

  • 명령어 사용시 바로 프롬프트가 떨어지도록 한다.

- Amazon Linux 2 최신 ami id 찾기

  • 자주 업데이트가 된다
  • (참고)이미지 타입 : /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2
# amazon owner 이미지들 : 어마어마한 양임.. page마다 나오지 중간에 나오면 된다
#aws ec2 describe-images --owners self amazon

# 위의 내용에서 이미지 ID만 필터링 : 내가 직접 세본 것만 200페이지가 넘어간다. 중간에 나오자.
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"

- AWS Default VPC 생성

  • 조회방법
# vpc 확인
aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq
{
...

# vpc id만 확인
aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq '.Vpcs[0].VpcId'
"vpc-3912a952"

# 서브넷 확인
#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

 

 


도전과제1

Ubuntu 에 apache(httpd) 를 설치하고 index.html 생성(닉네임 출력)하는 userdata 를
작성해서 설정 배포 후 웹 접속
 - 해당 테라폼 코드(파일)를 작성

 

 

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
              apt-get update
              apt-get install -y apache2
              service apache2 start
              echo "My Nickname : rkaehdaos" > /var/www/html/index.html
              EOF
  tags = {
    Name = "Single-WebSrv"
  }
}
resource "aws_security_group" "instance" {
  name = var.security_group_name

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    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"
}
첫 주 스터디를 하기 전에 예습을 했어야 했는데...

정말 내가 이상한지 모르겠는데..
IaC는 그냥 프로그램하고는 다르다..
분명히 스터디 하고 영상 다시 보기 할 떄는 맞아 맞아.. 그거지..했는데.. 막상 도전 과제 하나 하려니..
정말 실습에서 아주 조금 비튼 건데.. 지금 몇 시간을 날린건지..새벽 2시가 되서야.. ㅠ
과제는 과제로 치고.. 진짜로 빡집중하자 진짜로.

자고 일어나서
동영상 2번 다시 볼것!!

아냐.
1번 보고 다시  따라 해볼 것!

 

블로그 이미지

감동맨

rkaehdaos의 블로그

,