[Terraform] 프로비저너

728x90

출력값

공식 문서: https://learn.hashicorp.com/tutorials/terraform/aws-outputs?in=terraform/aws-get-started

how 🐳  ~/terraform/01 $ cat output.tf
output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.app_server.id
}
how 🐳  ~/terraform/01 $ terraform output             
app_server_ip = "3.38.88.206"

로컬값

테라폼 내부에서만 사용하는 변수, 스코프가 테라폼 내부에만 잡혀있다고 생각하면 된다.

input variable 이나 output 과는 다르다.

함수 test(a, b, c) 로 예를 들어보면 input variable 은 함수의 알규먼트인 a,b,c 에 해당하고 output 은 test(a, b, c) 의 리턴값이다. 로컬값은 이제 함수 내부에 정의된 변수들이라고 생각하면 된다.

주로 태그를 붙일 때 사용한다.

locals {
  common_tags = {
    Name        = "My Terraform"
    Environment = var.env
    AZ          = var.aws_availability_zone[var.aws_region]
  }
}
resource "aws_instance" "app_server" {
  ami               = var.aws_amazon_linux_ami[var.aws_region]
  instance_type     = var.instance.type
  availability_zone = var.aws_availability_zone[var.aws_region]

  tags = local.common_tags

  # 명시적 의존성
  depends_on = [
    aws_s3_bucket.b
  ]
}

resource "aws_s3_bucket" "b" {
  bucket = "csw-test-bucker"

  tags = local.common_tags
}

사용자 데이터(User Data)

user data 는 base64 로 인코딩 해준다.

resource "aws_instance" "app_server" {
  ami               = var.aws_amazon_linux_ami[var.aws_region]
  instance_type     = var.instance.type
  availability_zone = var.aws_availability_zone[var.aws_region]

  user_data = <<-EOF
    #!/bin/bash
    yum -y install httpd
    systemctl enable --now httpd
    echo "hello world" > /var/www/html/index.html
    EOF

  tags = local.common_tags

  vpc_security_group_ids = [aws_security_group.app_server_sg.id]

  # 명시적 의존성
  depends_on = [
    aws_s3_bucket.b
  ]
}

위와 같은 방식으로 user_data 를 명시적으로 작성할 수 있거나

resource "aws_instance" "app_server" {
  ami               = var.aws_amazon_linux_ami[var.aws_region]
  instance_type     = var.instance.type
  availability_zone = var.aws_availability_zone[var.aws_region]

  user_data = file("userdata.sh")

  tags = local.common_tags

  vpc_security_group_ids = [aws_security_group.app_server_sg.id]

  # 명시적 의존성
  depends_on = [
    aws_s3_bucket.b
  ]
}

file 함수를 사용하여 특정 파일에서 userdata를 읽을 수 있다.

프로비저너

공식 문서: https://www.terraform.io/language/resources/provisioners/syntax

프로비저너 종류

  • file: 파일 복사
resource "aws_instance" "web" {

  # Copies the myapp.conf file to /etc/myapp.conf
  provisioner "file" {
    source      = "conf/myapp.conf"
    destination = "/etc/myapp.conf"
  }

}
  • local-exec: 로컬 머신에서 명령어 실행
resource "aws_instance" "web" {
  # ...

  provisioner "local-exec" {
    command = "echo ${self.private_ip} >> private_ips.txt"
  }
}
  • remote-exec: 원격 머신에서 명령어 실행
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 "remote-exec" {
    inline = [
      "puppet apply",
      "consul join ${aws_instance.web.private_ip}",
    ]
  }
}

프로비저너 연결

프로비저너가 연결되기 위해서는 SSH 연결이 필수이다.(https://www.terraform.io/language/resources/provisioners/syntax#the-self-object)

  • file
  • remote_exec
# Copies the file as the root user using SSH
provisioner "file" {
  source      = "conf/myapp.conf"
  destination = "/etc/myapp.conf"

  connection {
    type     = "ssh"
    user     = "root"
    password = "${var.root_password}"
    host     = "${var.host}"
  }
}

Tainted

오염되다, 문제있다. 오류

# aws_instance.app_server: (tainted)
resource "aws_instance" "app_server" {
    ami                                  = "ami-02de72c5dc79358c9"
    arn                                  = "arn:aws:ec2:ap-northeast-2:007442965030:instance/i-01778fddefa71face"
    associate_public_ip_address          = true
    availability_zone                    = "ap-northeast-2c"

리소스를 생성/변경 하다 오류가 발생하면, 해당 리소스를 Taint 처리한다.

terrform taint <RESOURCE> : 해당 리소스에 Taint 를 걸어준다.

terraform untaint <RESOURCE> : Taint 된 리소스를 untaint 처리를 해준다.

Taint 처리된 리소스는 다음 작업 시 무조건 재생성된다.

Ansible 실행 방법

  1. AMI 이미지 내에 ansible 을 미리 설치
    1. file 로 플레이북 및 파일 복사
    2. remote-exec 로 실행
    3. ansible-playbook a.yml -c local (자기 자신한테 접속할 때)
  2. 로컬에서 실행
    1. 로컬에 ansible 이 설치되어 있어야 함
    2. local-exec 로 인벤토리 생성
    3. local-exec 로 ansible-playbook 실행

local-exec 를 사용하여 ansible 사용

resource "aws_key_pair" "app_server_key" {
  key_name   = "app_server_key"
  public_key = file("/Users/csw/.ssh/id_rsa.pub")
}

resource "aws_instance" "app_server" {
  ami               = var.aws_amazon_linux_ami[var.aws_region]
  instance_type     = var.instance.type
  availability_zone = var.aws_availability_zone[var.aws_region]

  key_name = aws_key_pair.app_server_key.key_name

  connection {
    type        = "ssh"
    user        = "ec2-user"
    host        = self.public_ip
    private_key = file("/Users/csw/.ssh/id_rsa")
    timeout     = "1m"
  }

  provisioner "local-exec" {
    command = "echo ${self.public_ip} ansible_user=ec2-user > inven.ini"
  }

  provisioner "local-exec" {
    command = "ansible-playbook -i inven.ini web_install.yml -b"
  }

  tags = local.common_tags

  vpc_security_group_ids = [aws_security_group.app_server_sg.id]

  # 명시적 의존성
  depends_on = [
    aws_s3_bucket.b
  ]
}

resource "aws_eip" "app_server_eip" {
  # 암시적 의존성
  instance = aws_instance.app_server.id
  vpc      = true
  tags     = local.common_tags
}

resource "aws_s3_bucket" "b" {
  bucket = "csw-test-bucker"

  tags = local.common_tags
}

resource "aws_s3_bucket_acl" "example" {
  bucket = aws_s3_bucket.b.id
  acl    = "private"
}

playbook 은 아래와 같다. 단순히 httpd 패키지 다운로드하고 서비스 실행한 뒤에 index.html 파일을 만든다.

- hosts: all

  tasks:
    - yum:
        name: httpd
    - service:
        name: httpd
        state: started
        enabled: yes
    - copy:
        content: "<h1>hello teraform</h1>"
        dest: "/var/www/html/index.html"
728x90