[번역] Infrastructure-As-Code with Terraform, VMware and VMware Cloud on AWS

출처 : https://cloud.vmware.com/community/2019/11/19/infrastructure-code-terraform-vmware-vmware-cloud-aws/

이 글은 VMware Cloud on AWS를 포함한 VMware를 통해 Terraform을 설정하는 방법과 Terraform을 설정하는 방법을 소개한다.

terraform_primarylogo_fullcolor.png

Terraform은 HashiCorp이 제공하는 또 다른 툴이다(이전의 글에서 Packer에 대해서 알아보았다).

패커는 코드로 작성된 템플릿(JSON 형식)에서 가상 시스템을 생성하고 관리할 수 있는 기능을 제공했다.

Terraform은 가상 머신뿐만 아니라 여러 인프라, 클라우드 및 벤더에 걸쳐 스토리지, 네트워킹 및 보안 엔티티를 구축함으로써 이를 확장한다.

몇 년 전에 테라폼을 소개해 준 Gilles와 Prabhu 덕분이다. 또한 우리 팀의 라이언은 AWS에서 VMC의 Terraform을 사용하여 그의 블로그에 문서화했다.

Terraform에 대한 몇 가지 사항은 다음과 같다.

  • Hashicorp DevOps 도구 제품군의 하나다. 다른 것으로는 Vagrant, Packer, Vault Consul, Nomad등이 있다.
  • 오픈소스다.
  • AWS, Azure, Google Cloud, vSphere, OpenStack 등 여러 공급업체에서 인프라를 구축, 변경, 버전, 구축하도록 설계되었다.
  • JSON 또는 HCL(HCL은 HashiCorp 구성 언어)로 작성된 Infrastructure as Code이다.
  • 구축하고자 하는 인프라의 최종 상태를 높은-수준(high-level)의 구문으로 설명하면 Terraform이 이를 구축해 줄 것이다.
  • 배포하기 전에 명령을 실행해서 코드 유효성 검사(terraform validate)를 할 수 있다.
  • 명령을 실행하여 배치할 대상(terraform plan)을 파악할 수 있다.
  • 단일 명령으로 전체 인프라를 쉽게 배포할 수 있다(terraform apply).
  • Terraform은 이미 배치된 상태를 유지하고 코드를 업데이트한 후 terraform apply 명령을 다시 실행하면 '델타'(다시 생성하는 대신)만 적용된다.

이제 몇 가지 예를 보자.

Terraform in Action

Terraform을 사용하면 비밀, 암호 및 변수를 별도의 파일에 저장하는 것이 가장 좋다.

  • terraform.tfvars에는 변수 <- 변수값을 정의
  • vars.tf는 변수가 되려고하는(would be) 것을 정의 <- 변수를 정의

예를 들어, vars.tf은 다음과 같이 보일 것이다.

variable "vsphere_user" {}
variable "vsphere_password" {}
variable "vsphere_server" {}

terraform.tfvars는 다음과 같이 보일 것이다.

vsphere_user= "cloudadmin@vmc.local"
vsphere_password= "XXXXXXXXXXXXXXXXX"
vsphere_server= "vcenter.sddc-A-B-C-D.vmwarevmc.com"

이제 main.tf라는 이름의 주 파일로 이동한다. 여기서 생성할 리소스를 정의하십시오. 우리가 무엇을 만들 계획인지 이해하기 위해 읽는 것은 인간 친화적이고 직설적이다.

우리는 Terraform을 사용하여 vCenter에 VM을 배포할 것이다. 내 vCenter는 클라우드에 있지만 사용자의 vCenter는 어디에나 있을 수 있다.

main.tf에서는 먼저 다음과 같은 데이터 소스를 확보했다.

data "vsphere_datacenter" "dc" {
  name = "SDDC-Datacenter"
}
data "vsphere_datastore" "datastore" {
  name          = "WorkloadDatastore"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_compute_cluster" "cluster" {
    name          = "Cluster-1"
    datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_resource_pool" "pool" {
  name          = "Compute-ResourcePool"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_network" "network" {
  name          = "segment"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_virtual_machine" "template" {
  name          = "Windows Template"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

데이터 블록은 Terraform이 지정된 데이터 소스("vsphere_datacenter")에서 읽고 지정된 로컬 이름("SDDC-Datacenter")으로 결과를 내보내도록 요청한다. 이름은 동일한 Terraform 모듈의 다른 곳에서 이 자원을 가리키는 데 사용되지만 모듈의 범위 밖에서는 의미가 없다.

데이터 소스와 이름이 함께 지정된 리소스의 식별자 역할을 한다.

데이터 센터, 데이터스토어, 리소스 풀 및 네트워크는 각각 vsphere_datacenter, vsphere_datastore, vsphere_resource_pool 및 vsphere_network 데이터 소스를 통해 검색된다.

다음으로, 자원으로 무엇을 만들 것인가를 정의할 것이다.

resource "vsphere_virtual_machine" "vm" {
    name             = "terraform-test"
    folder           = "Workloads"
    resource_pool_id = "${data.vsphere_compute_cluster.cluster.resource_pool_id}"
    datastore_id     = "${data.vsphere_datastore.datastore.id}"
    firmware         = "${data.vsphere_virtual_machine.template.firmware}"
    num_cpus = 2
    memory   = 4096
    guest_id = "${data.vsphere_virtual_machine.template.guest_id}"
    network_interface {
        network_id   = "${data.vsphere_network.network.id}"
        adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}"
    }
    disk {
        label            = "disk0"
        size             = "${data.vsphere_virtual_machine.template.disks.0.size}"
        eagerly_scrub    = "${data.vsphere_virtual_machine.template.disks.0.eagerly_scrub}"
        thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}"
    }
    scsi_type = "${data.vsphere_virtual_machine.template.scsi_type}"

다시 한 번 말씀드리지만, 우리는 리소스를 생성하고 있다("vsphere_virtual_machine" 유형). VM 이름은 "terraform-test"로, Workloads 폴더에 배치되고 이전에 발견한 리소스 풀과 데이터스토어에 배포될 것이다. 네트워크 및 디스크도 동일하다.

템플릿 파일이 완료되면, terraform apply를 통해 리소스를 배포할 수 있다. 앞에서 언급한 바와 같이, terraform validate(코드를 올바르게 작성했는지 확인하기 위해), terraform plan(테라폼이 무엇을 생성, 업데이트 또는 삭제할 것인지 이해하기 위해) 및 terraform destory(이 명령이 어떻게 할 것인지 분명히 알아낼 수 있을 것이다)를 실행할 수 있다.

윈도우에서 Terraform Deploy

Terraform 출력:

bash-3.2$ terraform apply
data.vsphere_datacenter.dc: Refreshing state...
data.vsphere_compute_cluster.cluster: Refreshing state...
data.vsphere_network.network: Refreshing state...
data.vsphere_virtual_machine.template: Refreshing state...
data.vsphere_datastore.datastore: Refreshing state...
data.vsphere_resource_pool.pool: Refreshing state...
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
Terraform will perform the following actions:
  # vsphere_virtual_machine.vm will be created
  + resource "vsphere_virtual_machine" "vm" {
      + boot_retry_delay                        = 10000
      + change_version                          = (known after apply)
      + cpu_limit                               = -1
      + cpu_share_count                         = (known after apply)
      + cpu_share_level                         = "normal"
      + datastore_id                            = "datastore-43"
      + default_ip_address                      = (known after apply)
      + ept_rvi_mode                            = "automatic"
      + firmware                                = "bios"
      + folder                                  = "Workloads"
      + force_power_off                         = true
      + guest_id                                = "windows9_64Guest"
      + guest_ip_addresses                      = (known after apply)
      + host_system_id                          = (known after apply)
      + hv_mode                                 = "hvAuto"
      + id                                      = (known after apply)
      + imported                                = (known after apply)
      + latency_sensitivity                     = "normal"
      + memory                                  = 4096
      + memory_limit                            = -1
      + memory_share_count                      = (known after apply)
      + memory_share_level                      = "normal"
      + migrate_wait_timeout                    = 30
      + moid                                    = (known after apply)
      + name                                    = "terraform-test"
      + num_cores_per_socket                    = 1
      + num_cpus                                = 2
      + reboot_required                         = (known after apply)
      + resource_pool_id                        = "resgroup-9"
      + run_tools_scripts_after_power_on        = true
      + run_tools_scripts_after_resume          = true
      + run_tools_scripts_before_guest_shutdown = true
      + run_tools_scripts_before_guest_standby  = true
      + scsi_bus_sharing                        = "noSharing"
      + scsi_controller_count                   = 1
      + scsi_type                               = "lsilogic-sas"
      + shutdown_wait_timeout                   = 3
      + swap_placement_policy                   = "inherit"
      + uuid                                    = (known after apply)
      + vapp_transport                          = (known after apply)
      + vmware_tools_status                     = (known after apply)
      + vmx_path                                = (known after apply)
      + wait_for_guest_ip_timeout               = 0
      + wait_for_guest_net_routable             = true
      + wait_for_guest_net_timeout              = 5
      + clone {
          + template_uuid = "42065f41-1191-b400-b9bf-44b787a38842"
          + timeout       = 30
        }
      + disk {
          + attach           = false
          + datastore_id     = "<computed>"
          + device_address   = (known after apply)
          + disk_mode        = "persistent"
          + disk_sharing     = "sharingNone"
          + eagerly_scrub    = false
          + io_limit         = -1
          + io_reservation   = 0
          + io_share_count   = 0
          + io_share_level   = "normal"
          + keep_on_remove   = false
          + key              = 0
          + label            = "disk0"
          + path             = (known after apply)
          + size             = 32
          + thin_provisioned = true
          + unit_number      = 0
          + uuid             = (known after apply)
          + write_through    = false
        }
      + network_interface {
          + adapter_type          = "e1000e"
          + bandwidth_limit       = -1
          + bandwidth_reservation = 0
          + bandwidth_share_count = (known after apply)
          + bandwidth_share_level = "normal"
          + device_address        = (known after apply)
          + key                   = (known after apply)
          + mac_address           = (known after apply)
          + network_id            = "network-o28"
        }
    }
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
vsphere_virtual_machine.vm: Creating...
vsphere_virtual_machine.vm: Still creating... [10s elapsed]
vsphere_virtual_machine.vm: Still creating... [20s elapsed]
vsphere_virtual_machine.vm: Still creating... [30s elapsed]
vsphere_virtual_machine.vm: Still creating... [40s elapsed]
vsphere_virtual_machine.vm: Still creating... [50s elapsed]
vsphere_virtual_machine.vm: Still creating... [1m0s elapsed]
vsphere_virtual_machine.vm: Still creating... [1m10s elapsed]
vsphere_virtual_machine.vm: Still creating... [1m20s elapsed]
vsphere_virtual_machine.vm: Still creating... [1m30s elapsed]
vsphere_virtual_machine.vm: Still creating... [1m40s elapsed]
vsphere_virtual_machine.vm: Creation complete after 1m41s [id=42060cb8-442a-98bc-1ca6-9ade030a24c0]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Customization

구축 중에 사용자 지정 적용을 시작하면 더욱 흥미로워진다.

아래 요약에서 우리는 기존 템플릿에서 VM을 복제하고 있으며 이를 'terraform-test'라고 부를 것임을 명시한다.

clone {
  template_uuid = "${data.vsphere_virtual_machine.template.id}"
  customize {
    linux_options {
      host_name = "terraform-test"
      domain    = "test.internal"
    }
    network_interface {}
  }
}

빈 network_interface는 VM이 DHCP를 통해 IP를 선택함을 의미한다. 아래 보시다시피 VM을 DHCP 지원 네트워크에 연결했으며 VM이 IP를 선택했다. 정적 IP를 할당하려는 경우 {} 내에서 해당 IP를 지정한다.

아래에서 볼 수 있듯이 VM에는 우리가 테라폼 파일("terraform-test")에서 지정한 이름이 있다.

screenshot-2019-07-16-at-140754.png

Idempotence

특징적인 선언적 기반구조를 코드로 사용하는 것의 장점은 Terraform이 해야 할 변화를 해결할 것이라는 것이다. 상태를 파악함에 따라, 어떤 변화를 만들어야 하는지, 예를 들어 동일한 인프라를 두 번 구축하지 않고 델타를 적용하는 것이 아니라 변화를 일으킬 수 있을 만큼 충분히 영리하다.

예를 들어 방금 Terraform을 사용하여 생성한 VM에 vSphere 태그를 적용하려면 어떻게 해야 하는가?

main.tf 계획을 다음과 같이 업데이트한다.

resource "vsphere_tag_category" "environment" {
    name        = "environment"
    cardinality = "SINGLE"
    associable_types = [
        "VirtualMachine"
    ]
}
resource "vsphere_tag_category" "region" {
    name        = "region"
    cardinality = "SINGLE"
    associable_types = [
        "VirtualMachine"
    ]
}
resource "vsphere_tag" "environment" {
    name        = "test-dev"
    category_id = "${vsphere_tag_category.environment.id}"
}
resource "vsphere_tag" "region" {
    name         = "UK"
    category_id = "${vsphere_tag_category.region.id}"
}
resource "vsphere_virtual_machine" "vm" {
### cut for brevity
tags = [
        "${vsphere_tag.environment.id}",
        "${vsphere_tag.region.id}",
   ]

우리는 이 각각의 범주에서 두 개의 태그 범주(environment 및 region)와 두 개의 태그(test-dev 및 UK)를 만들었다.

고객들 중 한 명이 하는 것이다. 그들은 자동화를 위해 Terraform과 이러한 vSphere 태그를 사용한다. 이 태그를 기반으로 Puppet은 새로 배포된 VM에 적용할 구성을 결정할 것이다.

Terraform Plan을 실행할 때 Terraform은 4개의 vSphere 구성 요소(2개의 태그 범주 및 2개의 태그)를 생성하고 1을 업데이트(새로 구성된 태그로 VM)해야 함을 해결한다.

bash-3.2$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.
data.vsphere_datacenter.dc: Refreshing state...
data.vsphere_resource_pool.pool: Refreshing state...
data.vsphere_datastore.datastore: Refreshing state...
data.vsphere_network.network: Refreshing state...
data.vsphere_compute_cluster.cluster: Refreshing state...
data.vsphere_virtual_machine.template: Refreshing state...
vsphere_virtual_machine.vm: Refreshing state... [id=42060cb8-442a-98bc-1ca6-9ade030a24c0]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
Terraform will perform the following actions:
  # vsphere_tag.environment will be created
  + resource "vsphere_tag" "environment" {
      + category_id = (known after apply)
      + id          = (known after apply)
      + name        = "test-dev"
    }
  # vsphere_tag.region will be created
  + resource "vsphere_tag" "region" {
      + category_id = (known after apply)
      + id          = (known after apply)
      + name        = "UK"
    }
  # vsphere_tag_category.environment will be created
  + resource "vsphere_tag_category" "environment" {
      + associable_types = [
          + "VirtualMachine",
        ]
      + cardinality      = "SINGLE"
      + id               = (known after apply)
      + name             = "environment"
    }
  # vsphere_tag_category.region will be created
  + resource "vsphere_tag_category" "region" {
      + associable_types = [
          + "VirtualMachine",
        ]
      + cardinality      = "SINGLE"
      + id               = (known after apply)
      + name             = "region"
    }
  # vsphere_virtual_machine.vm will be updated in-place
  ~ resource "vsphere_virtual_machine" "vm" {
 
## cut for brevity
Plan: 4 to add, 1 to change, 0 to destroy.
---------------------------------------------------------------------

terraform apply를 실행하면, 테라폼은 모든 것을 아주 부드럽게 업데이트 한다.

새 VM이 아니라 올바른 vSphere 태그가 적용된 업데이트된 VM이 있다.

screenshot-2019-07-16-at-153223.png

세부사항 및 의견

  • VMware Cloud on AWS 경우 WorkloadDatastore 및 Compute-ResourcePool에 배포한다. 그렇지 않으면 VM이 배포되지 않는다.
  • NSX-T Terraform Provider가 VMware Cloud on AWS와 호환되지 않음 따라서 아직 VMware Cloud on AWS에서 Terraform을 통해 네트워크 세그먼트 또는 방화벽 규칙을 만들 수 없다. 이것은 현재 진행 중인 작업이다.
  • VMtools가 VM에서 최신 상태로 실행 중인지 확인하십시오. 그렇지 않으면 Terraform이 만족스럽지 않고 시간 초과될 수 있습니다.
  • 나는 직접 윈도우 머신을 주문 제작하는 데 애를 먹었다. 당신은 운이 더 좋을지도 몰라.
  • Terraform 문제 해결이 과제가 될 수 있다. 이것은 아마도 Terraform에 대한 나의 가장 큰 불만일 것이다.

시작하는 방법

Mac에서 가장 쉬운 방법은 다음과 같다.

  • buw 설치: https://brew.sh/
  • brew를 사용하여 Terraform 설치: buw install terraform

Windows 또는 다른 플랫폼에서는 언제든지 직접 다운로드할 수 있다: https://www.terraform.io/downloads.html

  • GitHub에서 파일 다운로드
  • 올바른 변수(클러스터, 리소스 풀, 템플릿, 네트워크, 암호 등)로 파일 업데이트
  • 다음 명령을 실행한다(Windows를 사용하는 경우 Terraform이 PATH에 있는지 또는 파일이 Terraform exe와 동일한 위치에 있는지 확인한다).
    • terraform init
    • terraform plan
    • terraform apply
    • terraform apply
    • [terraform destory]
  • 그리고 바로 그거야!

읽어줘서 감사합니다.