Terraform Study #3

Dongmin Hanยท2023๋…„ 9์›” 13์ผ
0

Terraform-Study

๋ชฉ๋ก ๋ณด๊ธฐ
3/5

3์ฃผ์ฐจ

๐Ÿ’ก โ€˜ํ…Œ๋ผํผ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” IaCโ€™ ์ฑ…์œผ๋กœ ์ง„ํ–‰ํ•˜๋Š” Terraform ์Šคํ„ฐ๋””[T101] 3์ฃผ์ฐจ ์ •๋ฆฌ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ์‹œ๊ฐ„์€ ํ…Œ๋ผํผ ๊ธฐ๋ณธ์‚ฌ์šฉ ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„(3/3)์ด๋‹ค. ์ด๋ฒˆ์ฃผ์ฐจ์—์„œ๋Š” ์กฐ๊ฑด๋ฌธ, ํ•จ์ˆ˜, ํ”„๋กœ๋น„์ €๋„ˆ, data block์— ๋Œ€ํ•ด ๋ฐฐ์šด ๋’ค ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๊ฒฝํ—˜ํ•ด๋ณด๊ณ  ๋งˆ๋ฌด๋ฆฌ๋œ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ null_resource์— ๋Œ€ํ•ด ์ž˜๋ชฐ๋ž๋‹ค. ๋งŽ์ด ์‚ฌ์šฉํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ค‘ ํ•˜๋‚˜๋ผ๊ณ  ํ•œ๋‹ค..! ์ด๋ฒˆ ๊ธฐํšŒ์— ์ž˜ ๋ฐฐ์›Œ๋‘๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. (์ง€๊ธˆ์€ terraform_data๊ฐ€ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.)

Conditional(์กฐ๊ฑด๋ฌธ)

์กฐ๊ฑด ๋ฌธ์˜ ๊ฒฝ์šฐ C์–ธ์–ด์˜ ์‚ผํ•ญ์—ฐ์‚ฐ์ž์™€ ์œ ์‚ฌํ•˜๋‹ค. ๊ทธ ์™ธ์—๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋ชจ์–‘์ด๋‹ค.

ํ˜•์‹: condition ? true_val : false_val

์‹ค์Šต

  • main.tf
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 : ""
}

์œ„์˜ ์ฝ”๋“œ์˜ ๋‚ด์šฉ์€ var.enable_file์˜ ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, true๋กœ ์„ค์ •ํ•˜๋ฉด foo.bar๋ผ๋Š” local file์„ ์ƒ์„ฑํ•œ๋‹ค. ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ๋ผ๋ฉด ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • false๋ฅผ ์ง€์ •ํ•œ ๊ฒฝ์šฐ
$ export TF_VAR_enable_file**=false**
$ export | grep TF_VAR_enable_file
TF_VAR_enable_file=false
$ terraform init && terraform plan && terraform apply -auto-approve
...
Changes to Outputs:
  + content = ""

You can apply this plan to save these new output values to the Terraform state, without changing any real
infrastructure.

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

content = ""
  • true๋ฅผ ์ง€์ •ํ•œ๊ฒฝ์šฐ(=์•„๋ฌด๊ฒƒ๋„ ์ž…๋ ฅํ•˜์ง€ ์•Š์Œ)
# ์•„๋ฌด๊ฒƒ๋„ ์ž…๋ ฅํ•˜์ง€ ์•Š์•˜์„ ๋–„
$ terraform init && terraform plan && terraform apply -auto-approve
...
+ content = "foo!"
local_file.foo[0]: Creating...
local_file.foo[0]: Creation complete after 0s [id=4bf3e335199107182c6f7638efaad377acc7f452]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:
content = "foo!"

$ terraform state list
local_file.foo[0]

$ echo "local_file.foo[0].content" | terraform console
โ•ท
โ”‚ Warning: Value for undeclared variable
โ”‚ 
โ”‚ The root module does not declare a variable named "ec2_instance_type" but a value was found in file
โ”‚ "terraform.tfvars". If you meant to use this value, add a "variable" block to the configuration.
โ”‚ 
โ”‚ To silence these warnings, use TF_VAR_... environment variables to provide certain "global" settings to
โ”‚ all configurations in your organization. To reduce the verbosity of these warnings, use the
โ”‚ -compact-warnings option.
โ•ต
"foo!"

์œ„์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ, ์กฐ๊ฑด๋ฌธ์ด ์•„์ฃผ ์ž˜ ์ ์šฉ๋œ๋‹ค.

ํ•จ์ˆ˜

ํ•จ์ˆ˜๋Š” ๋‚ด์žฅํ•จ์ˆ˜๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ํ•จ์ˆ˜์™€ ๊ฐ™์ด ์ง์ ‘ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค. **๊ณต์‹๋ฌธ์„œ** ์—์„œ ํ™•์ธํ•˜๋ฉด์„œ ํ™•์ธํ•œ ํ•จ์ˆ˜๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ดค๋‹ค.

  • toset : ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์ง‘ํ•ฉ๊ณผ ๊ฐ™์ด ์ค‘๋ณต๋œ ์›์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์ •๋ ฌ์‹œํ‚จ๋‹ค.

    • toset(["b", "a","b"]) =[โ€aโ€, โ€œbโ€]
  • Slice : ๋ชฉ๋ก ๋‚ด์—์„œ ์ผ๋ถ€ ์—ฐ์† ์š”์†Œ(elements)๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. ์‹œ์ž‘ ์ธ๋ฑ์Šค(startindex
    )๋Š” ํฌํ•จ๋˜์ง€๋งŒ ๋ ์ธ๋ฑ์Šค(endindex)๋Š” ์ œ์™ธ

  • [**length](https://developer.hashicorp.com/terraform/language/functions/length) :** list, map ๋˜๋Š” string์˜ ๊ธธ์ด๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์ŠคํŠธ ๋˜๋Š” ๋งต์ด๋ฉด ์ปฌ๋ ‰์…˜(collection)์˜ ์š”์†Œ ์ˆ˜, ๋ฌธ์ž์—ด์ด๋ฉด ๋ฌธ์ž ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  • ์ˆซ์ž ๊ด€๋ จ ํ•จ์ˆ˜

    • min, max, ceil, floor ํ•จ์ˆ˜๋„ ์กด์žฌํ•œ๋‹ค.
    • min(-1,2,var.temp) = -1, ceil(10.1) = 11
  • ๋ฌธ์ž์—ด ๊ด€๋ จ ํ•จ์ˆ˜

    • split(",", "ami-xyz,AMI-ABC,ami-efg") = [ "ami-xyz","AMI-ABC","ami-efg" ]
    • lower, upper : lower(var.ami)= [ "ami-xyz","ami-abc","ami-efg" ]
    • substr(var.ami.0,7) = ami-xyz
    • join(โ€,โ€ , [ "ami-xyz","AMI-ABC","ami-efg" ]) : "ami-xyz,AMI-ABC,ami-efg"
  • Collection ํ•จ์ˆ˜

    • length(var.ami) = 3
    • index(var.ami, โ€œAMI-ABCโ€) = 1
    • element(var.ami,2) = ami-efg
    • contains(var.ami, โ€œAMI-ABCโ€) = true (์š”์†Œ๊ฐ€ ์žˆ๋Š” ์ง€ ์—†๋Š” ์ง€)
  • MAP ๊ด€๋ จ ํ•จ์ˆ˜ โ†’ map ํ•จ์ˆ˜๋Š” ์ง€์›ํ•˜์ง€ ์•Š๊ณ , tomap ํ•จ์ˆ˜๋ฅผ ์ง€์›

    variable "ami" {
      type = map
      default = { 
    		"us-east-1" = "ami-xyz",
        "ca-central-1" = "ami-efg",
        "ap-south-1" = "ami-ABC"
    	}
    	description = "A map of AMI ID's for specific regions" 
    }
    • lookup (var.ami, "us-east-1") : ami-xyz

ํ”„๋กœ๋น„์ €๋„ˆ

ํ”„๋กœ๋น„์ €๋„ˆ๋Š” ํ”„๋กœ๋ฐ”์ด๋”๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ์ปค๋งจ๋“œ์™€ ํŒŒ์ผ ๋ณต์‚ฌ ๊ฐ™์€ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

ํ”„๋กœ๋น„์ €๋„ˆ๋กœ ์‹คํ–‰๋œ ๊ฒฐ๊ณผ๋Š” ํ…Œ๋ผํผ์˜ ์ƒํƒœ ํŒŒ์ผ๊ณผ ๋™๊ธฐํ™”๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ”„๋กœ๋น„์ €๋‹์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ํ•ญ์ƒ ๊ฐ™๋‹ค๊ณ  ๋ณด์žฅํ•  ์ˆ˜ ์—†๋‹ค โ‡’ ์„ ์–ธ์  ๋ณด์žฅ ์•ˆ๋จ

๊ทธ๋ ‡๊ธฐ์—, ํ”„๋กœ๋น„์ €๋„ˆ๋ณด๋‹จ userdata ๋“ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

ํ”„๋กœ๋น„์ €๋„ˆ๋Š” ์ƒ์„ฑํ•  ๋•Œ๋งŒ ์‹คํ–‰๋˜๊ณ  ์ถ”ํ›„ ์ž‘์—…์€ ์—†๋‹ค. ๊ทธ๋ž˜์„œย provisioner๊ฐ€ ์‹คํŒจํ•˜๋ฉด ๋ฆฌ์†Œ์Šค๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ณ  ๋‹ค์Œย terraform applyย ํ•  ๋•Œ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ๋‹ค์‹œ ์ƒ์„ฑํ•œ๋‹ค.ย provisioner์—์„œย when = "destroy"๋ฅผ ์ง€์ •ํ•˜๋ฉด ํ•ด๋‹น ํ”„๋กœ๋น„์ €๋„ˆ๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๊ณ  ํ”„๋กœ๋น„์ €๋„ˆ๊ฐ€ ์‹คํŒจํ•œ๋‹ค๋ฉด ๋‹ค์Œย terraform applyย ํ•  ๋•Œ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค. ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด ์ด ๋•Œ๋ฌธ์— ์ œ๊ฑฐ ํ”„๋กœ๋น„์ €๋„ˆ๋Š” ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰ํ•ด๋„ ๊ดœ์ฐฎ๋„๋ก ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.


์ฐธ๊ณ  ์ž๋ฃŒ

์•ค์„œ๋ธ”๊ณผ ์—ฐ๋™ํ•ด์„œ ์“ธ๊ฑฐ๋ฉด, ์•„๋ž˜์˜ ๋งํฌ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

https://github.com/ansible/terraform-provider-ansible/tree/main/examples


์•„๋ž˜์™€ ๊ฐ™์ด ์›๊ฒฉ์— ๋‚ด์šฉ์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฐ€๊ธ‰์  user_data๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

user_data = base64encode(templatefile("${path.module}/ubuntu_docker.tftpl", {}))

connection: remote-exec์™€ file ํ”„๋กœ๋น„์ €๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, ์›๊ฒฉ์— ์—ฐ๊ฒฐํ•  ์ •๋ณด๋ฅผ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค. ์ฃผ๋กœ SSH/WinRM๋งŒ ์กด์žฌํ•œ๋‹ค.

resource "aws_instance" "web" {
	...
  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",
    ]
  }
}

null resource

์•„๋ฌด์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค์ด๋‹ค.

์ด๋Ÿฐ ๋ฆฌ์†Œ์Šค๊ฐ€ ํ•„์š”ํ•œ ์ด์œ ๋Š” ํ…Œ๋ผํผ ํ”„๋กœ๋น„์ €๋‹ ๋™์ž‘์„ ์„ค๊ณ„ํ•˜๋ฉด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์˜๋„์ ์œผ๋กœ ํ”„๋กœ๋น„์ €๋‹ํ•˜๋Š” ๋™์ž‘์„ ์กฐ์œจํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜์—ฌ, ํ”„๋กœ๋ฐ”์ด๋”๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์†Œ์Šค ์ˆ˜๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ๋งŒ์œผ๋กœ๋Š” ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค

  • ํ”„๋กœ๋น„์ €๋‹ ์ˆ˜ํ–‰ ๊ณผ์ •์—์„œ ๋ช…๋ น์–ด ์‹คํ–‰
  • ํ”„๋กœ๋น„์ €๋„ˆ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ
  • ๋ชจ๋“ˆ, ๋ฐ˜๋ณต๋ฌธ, ๋ฐ์ดํ„ฐ ์†Œ์Šค, ๋กœ์ปฌ ๋ณ€์ˆ˜์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ
  • ์ถœ๋ ฅ์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต

์˜ˆ์‹œ ์ƒํ™ฉ

EC2์˜ ์ธ์Šคํ„ด์Šค๋กœ ์›น์„œ๋น„์Šค๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. ์›น์„œ๋น„์Šค ์„ค์ •์— ๊ณ ์ •๋œ IP(EIP)๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ์ˆœํ™˜์ฐธ์กฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์—์„œ, null_resource๋ฅผ ์ถ”๊ฐ€ํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ

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"
  }
	# (1) ์—ฌ๊ธฐ์—์„œ eip ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ํ•˜๋ฉด ์ˆœํ™˜์ฐธ์กฐ๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•จ. 
  provisioner "remote-exec" {
    inline = [
      "echo ${aws_eip.myeip.public_ip}"
     ]
  }
}
# (1)๋ฒˆ์˜ ๋‚ด์šฉ์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š” Null_resource
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}"
      ]
  }
}

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"
}

terraform_data

์ด ๋ฆฌ์†Œ์Šค ๋˜ํ•œ, null_resource์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•˜๋‚˜, ํ…Œ๋ผํผ ์ž์ฒด์— ํฌํ•จ๋œ ๊ธฐ๋ณธ ์ˆ˜๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ์ž๊ฐ€ ์ œ๊ณต๋œ๋‹ค.

triggers_replace: ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋ฉฐ, ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์•„๋ž˜์˜ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•˜๋ฉด,

resource "terraform_data" "foo" {
  triggers_replace = [
    local_file.foo
  ]
  provisioner "local-exec" {
    command = "echo 'terraform_data test'"
  }
}
output "terraform_data_output" {
  value = terraform_data.foo.output # ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” "world"
}

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 : ""
}
  1. terraform apply ์ดํ›„, foo.bar์˜ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ–ˆ์„ ๋•Œ
$ terraform apply -auto-approve
local_file.foo[0]: Refreshing state... [id=4bf3e335199107182c6f7638efaad377acc7f452]
terraform_data.foo: Refreshing state... [id=bdfbfd37-fccc-4f02-6542-08f1bbb3d2a1]
...
terraform_data.foo: Destroying... [id=bdfbfd37-fccc-4f02-6542-08f1bbb3d2a1]
terraform_data.foo: Destruction complete after 0s
local_file.foo[0]: Creating...
local_file.foo[0]: Creation complete after 0s [id=4bf3e335199107182c6f7638efaad377acc7f452]
terraform_data.foo: Creating...
terraform_data.foo: Provisioning with 'local-exec'...
terraform_data.foo (local-exec): Executing: ["/bin/sh" "-c" "echo 'terraform_data test'"]
terraform_data.foo (local-exec): terraform_data test
terraform_data.foo: Creation complete after 0s [id=4301eef8-bbc6-58f0-6948-a6f6ac176b6c]

Apply complete! Resources: 2 added, 0 changed, 1 destroyed.

Outputs:

content = "foo!"
  1. (triggers_replace๋ฅผ ์ฃผ์„์œผ๋กœ ์ œ๊ฑฐํ•œ ๋’ค)terraform apply ์ดํ›„, foo.bar์˜ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ–ˆ์„ ๋•Œ
$ terraform apply
terraform_data.foo: Refreshing state... [id=3396b6e8-ffca-dac3-f0d5-c41455864c42]
local_file.foo[0]: Refreshing state... [id=4bf3e335199107182c6f7638efaad377acc7f452]

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.foo[0] will be created
  + resource "local_file" "foo" {
      + content              = "foo!"
      + 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             = "./foo.bar"
      + id                   = (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

local_file.foo[0]: Creating...
local_file.foo[0]: Creation complete after 0s [id=4bf3e335199107182c6f7638efaad377acc7f452]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

content = "foo!"

moved

state์— ๊ธฐ๋ก๋˜๋Š” ๋ฆฌ์†Œ์Šค์˜ ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๊ธฐ์กด ๋ฆฌ์†Œ์Šค ์‚ญ์ œ ํ›„ ์žฌ์ƒ์„ฑํ•œ๋‹ค. ์ด๋ฆ„์€ ๋ณ€๊ฒฝํ•˜์ง€๋งŒ, ์ธํ”„๋ผ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์„ ๋•Œ moved block์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ์›๋ณธ
resource "local_file" "a" {
  content  = "foo!"
  filename = "${path.module}/foo.bar"
}

output "file_content" {
  value = local_file.a.content
}
  • ์ด๋ฆ„ ์ˆ˜์ • ํ›„
resource "local_file" "b" {
  content  = "foo!"
  filename = "${path.module}/foo.bar"
}

moved {
  from = local_file.a
  to   = local_file.b
}

output "file_content" {
  value = local_file.b.content
}

์ด์™€ ๊ฐ™์ด moved block์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ธํ”„๋ผ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋กœ๋ฐ”์ด๋”

ํ”„๋กœ๋ฐ”์ด๋”๋ž€ ์ธํ”„๋ผ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ์—…์ฒด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. Terraform์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๋ฐ”์ด๋”๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ํด๋ผ์šฐ๋“œ, SaaS, ๋‹ค๋ฅธ API์™€ ์ƒํ˜ธ์ž‘์šฉํ•œ๋‹ค.

Terraform ์ด ์–ด๋–ค ๊ณต๊ธ‰์ž์™€ ์‚ฌ์šฉํ•  ์ง€ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด, provider.tf ์— ๋ณ„๋„๋กœ ์ •์˜ํ•œ๋‹ค.

ํ”„๋กœ๋ฐ”์ด๋”๋Š” terraform init ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด, ํ•„์š”ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒ€์ƒ‰ ๋ฐ ๋‹ค์šด๋กœ๋“œํ•˜๋ฉฐ lock.hcl ํŒŒ์ผ์— ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋ช…์‹œํ•˜์—ฌ ์•ž์œผ๋กœ์˜ ์ฝ”๋“œ ์ˆ˜ํ–‰์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ œํ•œํ•œ๋‹ค. (์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ, ๋™์ž‘์„ ๋ฐฉ์ง€ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.) terraform init ๋ช…๋ น์–ด๋Š” ๋ฐฑ์—”๋“œ ์„ค์ • ํ˜น์€ ํ”„๋กœ์ ํŠธ ์‹œ์ž‘์‹œ ์ˆ˜ํ–‰ํ•˜๊ธฐ์— ์—ฌ๋Ÿฌ ์ž‘์—…์ด ์ผ์–ด๋‚œ๋‹ค. ํ”„๋กœ๋ฐ”์ด๋”๋งŒ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ณ  ์‹ถ์œผ๋ฉด, terraform init -upgrade ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์•„๋ž˜์˜ ๊ทธ๋ฆผ์œผ๋กœ ํ•œ๋ฒˆ์— ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.


์ถœ์ฒ˜:https://malwareanalysis.tistory.com/619

์•„๋ž˜์™€ ๊ฐ™์ด, ํŒŒํŠธ๋„ˆ์‚ฌ ํ˜น์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ œ๊ณตํ•˜๋Š” ์—…์ฒด๋ผ๋ฉด ํ…Œ๋ผํผ์„ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
Terraform๊ณผ ํŒŒํŠธ๋„ˆ ๋ชฉ๋ก์€ ์•„๋ž˜์˜ ์ด๋ฏธ์ง€ ์ฐธ๊ณ 

  • kubernetes ํ™˜๊ฒฝ ์ธํ”„๋ผ ๊ตฌ์ถ•ํ•˜๊ธฐ
    • provider.tf
      terraform {
        required_providers {
          kubernetes = {
            source = "hashicorp/kubernetes"
          }
        }
      }
      
      provider "kubernetes" {
        config_path    = "~/.kube/config"
      }
    • kubernetes.tf
      resource "kubernetes_deployment" "nginx" {
        metadata {
          name = "nginx-example"
          labels = {
            App = "t101-nginx"
          }
        }
        spec {
          replicas = 2
          selector {
            match_labels = {
              App = "t101-nginx"
            }
          }
          template {
            metadata {
              labels = {
                App = "t101-nginx"
              }
            }
            spec {
              container {
                image = "nginx:1.7.8"
                name  = "example"
      
                port {
                  container_port = 80
                }
              }
            }
          }
        }
      }
      
      resource "kubernetes_service" "nginx" {
        metadata {
          name = "nginx-example"
        }
        spec {
          selector = {
            App = kubernetes_deployment.nginx.spec.0.template.0.metadata[0].labels.App
          }
          port {
            node_port   = 30080
            port        = 80
            target_port = 80
          }
      
          type = "NodePort"
        }
      }
  • ์‹คํ–‰๊ฒฐ๊ณผ(๋ฏธ๋‹ˆํ๋ธŒ๋กœ ํ…Œ์ŠคํŠธ)
$ terraform init && terraform plan && terraform apply -auto-approve
...
Plan: 2 to add, 0 to change, 0 to destroy.
kubernetes_deployment.nginx: Creating...
kubernetes_deployment.nginx: Still creating... [10s elapsed]
kubernetes_deployment.nginx: Creation complete after 16s [id=default/nginx-example]
kubernetes_service.nginx: Creating...
kubernetes_service.nginx: Creation complete after 0s [id=default/nginx-example]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
kubernetes_deployment.nginx
kubernetes_service.nginx
Every 1.0s: kubectl get pods,svc                                 MacBook-Pro.local: Wed Sep 13 21:30:54 2023

NAME                                 READY   STATUS    RESTARTS   AGE
pod/nginx-example-868fbd6dcc-8r9bv   1/1     Running   0          89s
pod/nginx-example-868fbd6dcc-xp4rg   1/1     Running   0          89s

NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP        114d
service/nginx-example   NodePort    10.103.116.226   <none>        80:30080/TCP   74s

์ด์ฒ˜๋Ÿผ ์ •์ƒ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

profile
์ดˆ๋ณด ๋ฐ๋ธŒ์˜ต์Šค ์—”์ง€๋‹ˆ์–ด, ํ”ผ๋“œ๋ฐฑ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€