Terraform AWS 클라우드: 온전한 인프라 관리

게시 됨: 2022-03-11

지원서를 작성하는 것은 이야기의 일부일 뿐입니다. 가치가 있으려면 확장 가능한 위치에 배포해야 하고, 고가용성으로 실행해야 하고, 백업이 필요합니다.

적어도 이 배포 프로세스를 이해하려면 점점 더 많은 개발자가 필요합니다. 이는 예를 들어 시스템 복잡성이 증가함에 따라 DevOps가 오늘날 자주 요청되는 역할이 되는 것으로 나타납니다. 우리는 이러한 변화를 무시할 수 없으며 쉽게 배포할 수 있도록 애플리케이션을 설계하는 방법을 알고 있어야 합니다.

이것은 또한 우리 고객의 이익입니다. 그들은 우리를 우리 분야의 전문가로 고용하고 종종 처음부터 끝까지 전체 제품을 제공하기를 기대합니다. 그들은 요구 사항이 있으며 종종 비즈니스 솔루션이 실행되는 스택을 잊어 버립니다. 결국 중요한 것은 제품의 비즈니스 가치입니다.

Terraform 소개

배포 및 인프라 관리는 간단한 프로세스가 아닙니다. 끊임없이 변화하는 많은 도메인 전문 지식 외에도 또 다른 도구 또는 새로운 워크플로를 배워야 합니다. 미루고 있다면 이 기사를 통해 인프라 관리에 대한 한 가지 접근 방식에 대해 알아볼 수 있습니다. 나는 결국 당신이 Terraform을 사용하는 데 더 자신감을 갖고 가능한 접근 방식과 도전 과제에 대해 더 많이 알게 되기를 바랍니다. 이 도구를 사용하여 최소한 클라우드 인프라의 일부 관리를 시작할 수 있어야 합니다.

Terraform은 추상화 계층입니다. 예, 추상화가 새는 데 동의합니다. 그러나 결국 우리는 문제를 해결하고 추상화를 관리하는 비즈니스에 있습니다. 이것은 우리의 일상 업무에서 더 많은 온전함을 제공하는 것을 목표로 합니다.

목표

Terraform이 무엇인지, 전체 생태계에 어떻게 적합하며, 다른 유사한 도구와 어떻게 비교되는지 설명하겠습니다. 그런 다음 팀을 위해 다중 환경 및 프로덕션 준비 Terraform 설정을 구성하는 데 필요한 단계를 보여 드리겠습니다. Terraform 구성 작성의 기본 사항인 공유 가능한 모듈 로 복잡성을 관리하고 코드를 복제하는 방법을 설명하겠습니다.

예제는 모두 하나의 클라우드 제공업체인 Amazon Web Services(AWS)에 초점을 맞춥니다. 이것은 내가 가장 경험이 많은 클라우드일 뿐이지만 모든 정보는 다른 클라우드에도 적용되어야 합니다.

시작했을 때 알았더라면 좋았을 몇 가지 메모로 마무리하겠습니다. 몇 가지 구문 문제, 단점 및 Terraform이 내가 선택한 도구가 아닌 경우입니다.

나는 구문의 핵심적인 세부 사항에 초점을 맞추지 않을 것입니다. HashiCorp가 Terraform에 대해 제공하는 환상적인 문서와 가이드를 읽으면 빠르게 배울 수 있습니다. 저는 처음부터 명확하지 않을 수도 있고 Terraform 작업을 시작하기 전에 알았더라면 좋았을 것들에 초점을 맞추고 싶습니다. 바라건대, 이것이 올바른 방향을 제시하고 Terraform을 향후 프로젝트에서 사용할 도구로 고려할 수 있게 해주기를 바랍니다.

인프라 관리가 어렵다

클라우드에서 앱을 위한 환경을 설정하는 데에는 여러 단계가 포함됩니다. 세세한 체크리스트로 일일이 적어두고 철저히 지키지 않으면 항상 실수를 하게 됩니다. 우리는 결국 인간입니다. 그 단계는 공유하기 어렵습니다. 많은 수동 절차를 문서화해야 하며 문서가 빨리 구식이 될 수 있습니다. 이제 dev , test/qa , stageprod 단일 앱에 대한 총 환경 수로 이 모든 값을 곱합니다. 또한 각각의 보안에 대해서도 생각해야 합니다.

모든 도구에 사용하고 복잡성을 잊어버릴 수 있는 UI가 있지 않습니까?

UI가 있습니다. AWS Management Console이 대표적인 예입니다. 그러나 이러한 도구는 내부적으로 많은 작업을 수행합니다. UI를 한 번 클릭하면 실제로 파악하기 어려운 일련의 변경 사항이 호출될 수 있습니다. 일반적으로 UI에서 수행한 작업을 실행 취소할 수 있는 방법은 없습니다(일반적인 "확실합니까?" 프롬프트로는 충분하지 않은 경우가 많습니다). 또한 변경 사항을 확인하기 위해 두 번째 눈을 갖는 것이 항상 좋은 생각이지만 UI를 사용할 때 이 사람과 함께 앉아 있거나 변경 사항이 적용된 변경 사항을 확인해야 합니다. 이는 변경 사항보다 감사에 가깝습니다. 검토. 각 클라우드 제공업체에는 마스터해야 하는 고유한 UI가 있습니다. UI는 사용하기 쉽도록 설계되었으며(반드시 단순하지는 않습니다!) "이것은 단지 작은 조정일 뿐입니다" 또는 48시간 내에 잊어버릴 빠른 프로덕션 핫픽스와 같은 망상적 접근 방식에 취약합니다. 이러한 수동 클릭도 자동화하기가 매우 어렵습니다.

CLI 도구는 어떻습니까?

우리의 사용 사례에서는 UI 도구보다 낫습니다. 그러나 여전히 손으로 변경하거나 쉽게 처리할 수 없는 bash 스크립트를 작성하는 경향이 있습니다. 또한 각 공급자에는 고유한 CLI 도구가 있습니다. 이러한 도구를 사용하면 커밋하기 전에 변경 사항을 볼 수 없습니다. 고맙게도 이것은 새로운 문제가 아니며 이를 도와주는 도구가 있습니다.

오케스트레이션 대 구성 관리

인프라를 관리하는 데 사용되는 도구 및 사례의 몇 가지 범주를 정의하겠습니다. 오케스트레이션 및 구성 관리입니다. 대략적으로 선언적 및 명령형 프로그래밍 모델로 생각할 수 있습니다(Prolog 대 Python을 생각하십시오). 각각의 장단점이 있지만 모두 알고 주어진 작업에 가장 적합한 도구를 적용하는 것이 가장 좋습니다. 또한 오케스트레이션에는 일반적으로 구성 관리보다 높은 추상화 수준이 포함됩니다.

관현악법

오케스트레이션은 선언적 프로그래밍 패러다임에 가깝습니다. 오케스트라의 지휘자라고 생각하고 싶습니다. Wikipedia(링크)에서는 지휘자의 작업을 "제스쳐를 사용하여 여러 연주자 또는 가수의 동시 연주를 지휘하는 기술"로 멋지게 요약하고 있습니다. 지휘자는 비트와 템포, 역동성, 음악의 신호를 전달하지만 실제 작업은 악기 연주에 능숙한 개별 음악가가 수행합니다. 그런 조화가 없었다면 완벽한 작품을 완성할 수 없었을 것입니다.

여기에 Terraform이 적합합니다. 이를 사용하여 IT 인프라의 일부를 수행합니다. 배포할 대상을 알려주면 Terraform이 이를 모두 함께 연결하고 필요한 모든 API 호출을 수행합니다. 이 공간에는 유사한 도구가 있습니다. 가장 잘 알려진 것 중 하나는 AWS Cloud Formation입니다. Terraform보다 오류 발생 시 복구 및 롤백을 더 잘 지원하지만 제 생각에는 학습 곡선이 더 가파르다고 생각합니다. 또한 클라우드에 구애받지 않습니다. AWS에서만 작동합니다.

구성 관리

이러한 관행의 보완적인 측면은 구성 관리 접근 방식입니다. 이 패러다임에서는 도구가 주어진 원하는 구성에 도달하기 위해 수행해야 하는 정확한 단계를 지정합니다. 단계 자체는 작고 여러 운영 체제를 지원할 수 있지만 실행 순서에 대해 적극적으로 생각해야 합니다. 그들은 현재 상태와 주변 환경에 대한 인식이 없으며(당신이 프로그래밍하지 않는 한), 따라서 당신이 그들에게 제공하는 모든 단계를 맹목적으로 실행할 것입니다. 이것은 구성 드리프트( configuration drift )로 알려진 문제로 이어질 수 있습니다. 여기서 리소스는 특히 수동으로 변경한 경우 초기에 나타내려고 했던 것과 천천히 비동기화됩니다. 개별 인스턴스에서 서비스를 관리하고 프로비저닝하는 데 탁월합니다. 이 워크플로에서 탁월한 도구의 예로 Chef, Puppet, Ansible 및 Salt가 있습니다.

오케스트레이션은 리소스를 애완동물이 아닌 가축으로 취급하는 인프라에 대한 접근 방식을 시행합니다. 각 VPS를 수동으로 "양성"하는 대신 문제가 발생하면 정확한 사본으로 교체할 수 있습니다. 나는 당신이 단순히 신경 쓰지 않고 최선을 바라는 것을 다시 시작한다는 것을 의미하지 않습니다.

IT Crowd TV 쇼의 상징적인 슬로건: 껐다가 다시 켜본 적이 있습니까?

대신 코드에서 문제를 조사하고 수정한 다음 배포해야 합니다.

Ansible(및 기타 CM 도구)을 사용하여 AWS 인프라를 관리할 수 있지만, 이는 특히 인프라가 자주 변경되고 복잡성이 증가하는 경우 많은 작업이 필요하고 오류가 발생하기 쉽습니다.

기억해야 할 한 가지 중요한 사항은 오케스트레이션 및 구성 관리 접근 방식이 서로 충돌하지 않는다는 것입니다. 그들은 호환됩니다. Terraform에서 관리하는 AutoScaling 그룹에 EC2(VPS) 인스턴스 그룹이 있지만 디스크의 스냅샷인 AWS Application Image(AMI)를 실행하는 것은 완벽하게 괜찮습니다. . Terraform에는 시스템이 부팅되면 외부 프로비저닝 도구를 실행할 수 있는 "제공자" 개념도 있습니다.

Terraform 문서는 이를 더 자세히 설명하고 전체 생태계에 Terraform을 배치하는 데 도움이 되는 훌륭한 작업입니다.

테라폼이란?

HashiCorp에서 만든 오픈 소스 도구로 인프라를 버전이 지정되고 공유되며 검토할 수 있는 선언적 구성 파일로 코드화할 수 있습니다.

HashiCorp 이름은 종을 울려야 합니다. 그들은 또한 Nomad, Vault, Packer, Vagrant 및 Consul을 만듭니다. 이러한 도구 중 하나를 사용해 본 적이 있다면 문서의 품질, 활기찬 커뮤니티 및 솔루션에서 기대할 수 있는 유용성을 이미 알고 있는 도구입니다.

코드로서의 인프라

Terraform은 플랫폼에 구애받지 않습니다. AWS, Google Cloud Platform, OpenStack 및 Azure와 같은 베어메탈 서버 또는 클라우드 서버를 관리하는 데 사용할 수 있습니다. Terraform 용어에서는 이를 제공자 라고 합니다. 지원되는 제공자의 전체 목록을 읽으면 규모를 알 수 있습니다. 예를 들어 공급자 A가 VM을 구성하지만 공급자 B가 DNS 레코드를 구성하고 위임하는 경우와 같이 여러 공급자를 동시에 사용할 수 있습니다.

구성 파일을 한 번만 변경하면 클라우드 공급자를 변경할 수 있다는 뜻인가요? 아니요, 적어도 자동화된 방식은 아니더라도 당신이 그것을 원할 것이라고는 생각하지 않습니다. 문제는 제공자가 다른 기능, 오퍼링, 흐름, 아이디어 등을 가질 수 있다는 것입니다. 이는 동일한 개념을 표현하기 위해 다른 제공자가 다른 리소스를 사용해야 한다는 것을 의미합니다. 그러나 이 모든 작업은 여전히 ​​친숙한 단일 구성 구문으로 수행할 수 있으며 응집력 있는 워크플로의 일부가 될 수 있습니다.

Terraform 설정의 필수 부분

  1. 설치해야 하는 Terraform 바이너리 자체
  2. 소스 코드 파일, 즉 구성
  3. Terraform이 관리하는 리소스를 나타내는 상태(로컬 또는 원격)(나중에 자세히 설명)

Terraform 구성 작성

HCL 언어를 사용하여 *.tf 파일에 Terraform 구성 코드를 작성합니다. JSON 형식( *.tf.json )을 사용하는 옵션이 있지만 사람이 아닌 기계 및 자동 생성을 대상으로 합니다. HCL을 고수하는 것이 좋습니다. HCL 언어의 구문에 대해 자세히 설명하지 않겠습니다. 공식 문서는 HCL을 작성하는 방법과 변수 및 보간법을 사용하는 방법을 설명하는 환상적인 작업을 수행합니다. 예제를 이해하는 데 필요한 최소한의 것만 언급하겠습니다.

Terraform 파일 내에서 대부분 리소스데이터 소스 를 다루고 있습니다. 리소스는 인프라의 구성 요소(예: AWS EC2 인스턴스, RDS 인스턴스, Route53 DNS 레코드 또는 보안 그룹의 규칙)를 나타냅니다. 이를 통해 클라우드 아키텍처 내에서 프로비저닝하고 변경할 수 있습니다.

Terraform을 설정했다고 가정하고 terraform apply 를 실행하면 아래 코드는 완전한 기능의 EC2 인스턴스를 생성합니다(특정 속성만 표시됨).

 resource "aws_instance" "bastion" { ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2 instance_type = "t2.nano" key_name = "${var.key_name}" subnet_ vpc_security_group_ids = ["${aws_security_group.bastion.id}"] monitoring = "false" associate_public_ip_address = "true" disable_api_termination = "true" tags = { Name = "${var.project_tag}-bastion-${var.env}" Env = "${var.env}" Application ApplicationRole = "Bastion Host" Project = "${var.project_tag}" } }

반면에 주어진 구성 요소에 대한 데이터를 변경하지 않고 읽을 수 있는 데이터 소스가 있습니다. ACM 발급 인증서의 AWS ID(ARN)를 얻으시겠습니까? 데이터 소스를 사용합니다. 차이점은 구성 파일에서 데이터 소스를 참조할 때 데이터 소스 앞에 data_ 가 붙는다는 것입니다.

 data "aws_acm_certificate" "ssl_cert" { domain = "*.example.com" statuses = ["ISSUED"] }

위의 내용은 AWS ALB와 함께 사용할 수 있는 발급된 ACM SSL 인증서를 참조합니다. 모든 작업을 수행하기 전에 환경을 설정해야 합니다.

폴더 구조

Terraform 환경(및 해당 상태)은 디렉토리로 구분됩니다. Terraform은 디렉토리의 모든 *.tf 파일을 하나의 네임스페이스로 로드하므로 순서는 중요하지 않습니다. 다음 디렉토리 구조를 권장합니다.

 /terraform/ |---> default_variables.tf (1) /stage/ (2) |---> terraform.tfvars (3) |---> default_variables.tf (4) |---> terraform.tf (5) |---> env_variables.tf (6) /prod/ /<env_name>/
  1. default_variables.tf – 모든 최상위 변수와 선택적으로 기본값을 정의합니다. 심볼릭 링크를 사용하여 각 환경(중첩 디렉토리)에서 재사용할 수 있습니다.
  2. /stage/ – 전체 별도 환경에 대한 구성을 보유하는 디렉토리입니다(여기서는 stage 라고 명명되지만 아무 것도 될 수 있음). 이 폴더 내에서 이루어진 모든 변경 사항은 다른 환경(예: prod )과 완전히 독립적입니다. 이는 stage 에 대한 변경 사항으로 프로덕션 환경을 엉망으로 만드는 것을 피하기 위해 원하는 것입니다!
  3. terraform.tfvars – 변수 값을 정의합니다. .tfvars 파일은 정의된 변수에 대한 key=val 쌍을 보유한다는 점에서 .env 파일과 유사합니다. 예를 들어, 이것은 내가 사용하는 AWS profile , AWS key_name 및 AWS key_path 를 지정합니다. Git에서는 무시할 수 있습니다.
  4. default_variables.tf – 이것은 파일(2)에 대한 심볼릭 링크이며, 이를 통해 반복하지 않고 환경 독립 변수를 공유할 수 있습니다.
  5. terraform.tf – 이것은 각 환경의 기본 구성입니다. 백엔드를 구성하는 terraform {} 블록을 보유합니다. 여기에서 공급자 도 구성합니다.
  6. env_variables.tf – 이 파일에는 환경별 변수가 있습니다. AWS에서 모든 리소스에 Env=<env_name> 태그를 지정하므로 이 파일은 일반적으로 env 라는 하나의 변수만 정의합니다.

물론 이것이 환경을 구성하는 유일한 방법은 아닙니다. 이것은 관심사의 명확한 분리를 가능하게 함으로써 저에게 잘 작동한 것입니다.

백엔드 구성

이미 Terraform 상태에 대해 언급했습니다. 이것은 Terraform 워크플로의 필수적인 부분입니다. state가 실제로 필요한지 궁금할 수 있습니다. Terraform은 인프라의 실제 상태를 얻기 위해 항상 AWS API를 쿼리할 수 없었습니까? 생각해보면 Terraform은 선언적 구성 파일에서 관리하는 것과 해당 파일이 실제로 해당하는 것(클라우드 공급자의 환경에서) 간의 매핑을 유지해야 합니다. Terraform 구성 파일을 작성하는 동안 게시하는 보안 그룹에 대해 생성될 개별 EC2 인스턴스 또는 ARN과 같은 ID는 신경 쓰지 않습니다. 그러나 내부적으로 Terraform은 주어진 리소스 블록이 ID/ARN이 있는 구체적인 리소스를 나타낸다는 것을 알아야 합니다. 이것은 변경 사항을 감지하는 데 필요합니다. 또한 상태는 리소스 간의 종속성을 추적하는 데 사용됩니다(일반적으로 생각할 필요가 없는 항목이기도 합니다!). 그것들은 (일반적으로) 병렬화되고 실행될 수 있는 그래프를 구성하는 데 사용됩니다. 항상 그렇듯이 Terraform 상태와 그 목적에 대한 훌륭한 문서를 읽는 것이 좋습니다.

상태는 아키텍처에 대한 단일 정보 소스이므로 귀하와 귀하의 팀이 항상 최신 버전에서 작업하고 상태에 대한 비동기식 액세스로 인해 충돌을 일으키지 않도록 해야 합니다. 상태 파일에서 병합 충돌을 해결하고 싶지는 않습니다. 저를 믿으십시오.

기본적으로 Terraform은 현재 작업 디렉토리(각 환경의)에 있는 디스크의 파일에 terraform.tfstate 파일로 상태를 저장합니다. 이 작업에서 유일한 개발자가 될 것이라는 것을 알고 있거나 Terraform을 배우고 실험하는 중이라면 괜찮습니다. 기술적으로 VCS 저장소에 상태를 커밋할 수 있기 때문에 팀에서 작동하도록 만들 수 있습니다. 그러나 모든 사람이 항상 최신 버전의 상태에서 작업하고 아무도 동시에 변경하지 않도록 해야 합니다! 이것은 일반적으로 주요 두통이며 이에 대해 강력히 권고합니다. 게다가 누군가가 단일 개발 작업에 참여하는 경우에도 상태에 대한 대체 위치를 구성해야 합니다.

다행히 이것은 Terraform에 빌드된 좋은 솔루션의 문제입니다. 이른바 Remote State 입니다. 원격 상태가 작동하려면 사용 가능한 백엔드 공급자 중 하나를 사용하여 백엔드를 구성해야 합니다. 다음 백엔드 예제는 AWS S3 및 AWS DynamoDB(AWS NoSQL 데이터베이스)를 기반으로 합니다. S3만 사용할 수 있지만 상태 잠금 및 일관성 검사 메커니즘을 잃게 됩니다(권장하지 않음). 이전에 로컬 상태만 사용한 경우 원격 백엔드를 구성하면 상태를 처음으로 마이그레이션하는 옵션이 제공되므로 아무 것도 잃지 않습니다. 백엔드 구성에 대한 자세한 내용은 여기에서 읽을 수 있습니다.

불행히도 닭고기와 계란 문제가 있습니다. S3 버킷과 DynamoDB 테이블을 수동으로 생성해야 합니다. 아직 상태가 없기 때문에 Terraform은 자동으로 생성할 수 없습니다! AWS CLI를 사용하여 이를 자동화하는 https://github.com/gruntwork-io/terrarunt와 같은 몇 가지 솔루션이 있지만 이 블로그 게시물의 주요 주제에서 벗어나고 싶지 않습니다.

S3 및 DynamoDB 백엔드 구성에 대해 알아야 할 중요한 사항은 다음과 같습니다.

  1. 인적 오류 및 머피의 법칙으로부터 안전하도록 S3 버킷에서 버전 관리를 활성화합니다.
  2. DynamoDB 테이블에는 읽기 및 쓰기에 대한 속도 제한(용량이라고 함)이 있습니다. 원격 상태를 많이 변경하는 경우 해당 테이블에 대해 DynamoDB AutoScaling을 활성화하거나 충분히 높은 R/W 제한을 구성해야 합니다. 그렇지 않으면 Terraform은 많은 호출을 실행할 때 AWS API에서 HTTP 400 오류를 받습니다.

요약하자면 다음 백엔드 구성을 terraform.tf 에 배치하여 S3 및 DynamoDB에서 원격 상태를 구성할 수 있습니다.

 terraform { # Sometimes you may want to require a certain version of Terraform required_version = ">= 0.11.7" # Stores remote state, required for distributed teams # Bucket & dynamoDB table have to be created manually if they do not exist # See: https://github.com/hashicorp/terraform/issues/12780 backend "s3" { bucket = "my-company-terraform-state" key = "app-name/stage" region = "eu-west-1" # 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table. # See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html dynamodb_table = "terraform-state-lock-table" } }

이것은 한 번에 처리해야 할 것이 많지만 각 환경에 대해 한 번만 수행하면 잊어버릴 수 있습니다. 상태 잠금에 대한 더 많은 제어가 필요한 경우 HashiCorp Terraform Enterprise가 있지만 여기서는 다루지 않겠습니다.

공급자

이 백엔드에 액세스할 수 있고 클라우드 공급자와 전혀 통신할 수 있도록 하려면 소위 공급자 를 구성해야 합니다. 다음 블록은 terraform.tf 파일에 배치할 수 있습니다(각 환경에 대해).

 provider "aws" { profile = "${var.profile}" region = "${var.region}" version = "~> 1.23.0" }

profileregion 에 대한 변수는 무시될 수 있는 terraform.tfvars 파일에 저장됩니다. profile 변수는 표준 자격 증명 파일을 사용하여 AWS 클라우드에 대한 보안 자격 증명을 보유하는 명명된 프로필을 나타냅니다. 일부 버전 제약 조건도 설정하고 있습니다. 모든 백엔드 초기화에 대한 지식 없이 Terraform이 제공자 플러그인을 업그레이드하는 것을 원하지 않습니다. 특히 주의 깊게 업그레이드해야 하는 AWS 공급자의 버전 2.x가 있다는 점을 감안할 때.

백엔드 초기화

각 백엔드 구성에는 처음과 변경 사항이 있을 때마다 초기화 단계가 필요합니다. 초기화는 Terraform 모듈(나중에 자세히 설명)도 구성하므로 추가할 때 초기화 단계도 다시 실행해야 합니다. 이 작업은 여러 번 실행해도 안전합니다. 백엔드 초기화 중에 Terraform이 상태를 구성하기 위해 모든 변수를 읽을 수 있는 것은 아니며 보안상의 이유(예: 비밀 키)도 아닙니다. 이를 극복하기 위해 그리고 우리의 경우 기본값이 아닌 다른 AWS 프로필을 사용하기 위해 accept k=v 변수 쌍과 함께 -backend-config 옵션을 사용할 수 있습니다. 이것을 부분 구성이라고 합니다.

 terraform init -backend-config=profile=<aws_profile_name>

Terraform 파일은 지정된 디렉토리로 제한된 범위를 공유합니다. 이는 하위 폴더가 상위 디렉토리 코드에 직접 연결되지 않음을 의미합니다. 그러나 코드 재사용, 복잡성 관리 및 공유를 허용하는 모듈을 정의하는 것입니다.

워크플로

Terraform 코드로 작업할 때의 일반적인 워크플로는 다음과 같습니다.

  1. 인프라에 대한 구성을 작성합니다.
  2. 실제 변경 사항을 확인하십시오( terraform plan ).
  3. 선택적으로 2단계에서 본 정확한 변경 사항을 실행합니다 ( terraform apply ).
  4. 고토 1

테라폼 플랜

Terraform plan 명령은 apply 명령을 실행할 때 인프라에 수행될 변경 목록을 표시합니다. 계획 자체는 아무 것도 변경하지 않으므로 plan 여러 번 발행하는 것이 안전합니다.

계획을 읽는 방법

Terraform의 개체(리소스 및 데이터 소스)는 정규화된 이름으로 쉽게 식별할 수 있습니다.

  1. 리소스의 경우 ID는 다음과 같을 수 있습니다. <resource_type>.<resource_name> —예: aws_ecs_service.this .
  2. 모듈 내부 리소스의 경우 모듈 이름이 추가되었습니다. module.<module_name>.<resource_type>.<resource_name> —예: module.my_service_ecs_service_task.aws_ecs_service.this .
  3. 데이터 소스(모듈 내부 및 외부): (module.<module_name>).data.<resource_type>.<resource_name> —예: module.my_service_ecs_service_task.data.aws_ecs_task_definition.this .

리소스 유형은 특정 제공자에 따라 다르며 일반적으로 해당 이름( aws_… )을 포함합니다. AWS에 사용 가능한 리소스의 전체 목록은 문서에서 찾을 수 있습니다.

계획이 지정된 리소스에 대해 표시할 5가지 작업이 있습니다.

  1. [+] 추가 – 새 리소스가 생성됩니다.
  2. [-] Destroy – 자원이 완전히 파괴됩니다.
  3. [~] 현재 위치 수정 – 리소스가 수정되고 하나 이상의 매개변수가 변경됩니다. 이것은 일반적으로 안전합니다. Terraform은 수정될 매개변수와 수정 방법(가능한 경우)을 표시합니다.
  4. [- / +] – 리소스가 제거된 다음 새 매개변수로 다시 생성 됩니다. 이것은 내부에서 변경할 수 없는 매개변수가 변경된 경우에 발생합니다. Terraform은 빨간색 주석으로 리소스를 강제로 재생성하는 변경 사항을 보여줍니다. (forces new resource) . 자원이 전혀 존재하지 않는 기간이 있기 때문에 이는 잠재적으로 위험합니다. 또한 연결된 다른 종속성을 깨뜨릴 수도 있습니다. 결과에 대해 알지 못하거나 가동 중지 시간에 신경 쓰지 않는 경우가 아니면 이러한 변경 사항을 해결하는 것이 좋습니다.
  5. [<=] – 데이터 소스를 읽습니다. 이것은 읽기 전용 작업입니다.

Terraform AWS — 샘플 "계획"

위는 예시 계획입니다. 내가 변경한 사항은 다음과 같습니다.

  • 첫 번째 요새 인스턴스의 instance_type 변경
  • 두 번째 요새 인스턴스를 추가했습니다.
  • 보안 그룹의 이름을 변경했습니다.

마지막 변경 사항은 상위 그룹 이름의 변경 사항을 자동으로 감지하는 보안 그룹 규칙입니다. 제 생각에는 전체 계획이 매우 읽기 쉽습니다. 일부 매개변수 값은 <computed> 로 표시됩니다. 이는 변경될 것이라는 의미가 아니라 이 단계에서 검색 및 표시할 수 없음을 의미합니다(예: 아직 생성되지 않은 SG 이름). plan 명령 중에는 변경된 리소스만 표시된다는 점을 기억하십시오. 생략된 것은 건드리지 않습니다.

일반적으로 Terraform 명령은 추가 매개변수를 허용합니다. plan 명령의 가장 중요한 매개변수는 디스크에 계획을 저장하는 -out 옵션입니다. 그런 다음 이 저장된 계획을 적용 명령으로 실행할 수 있습니다(저장된 그대로). 이것은 매우 중요합니다. 그렇지 않으면 예를 들어 귀하의 plan 발행과 apply 발행 사이에 동료에 의해 변경될 수 있기 때문입니다. 예시:

  1. plan 을 발행하고 좋아 보이는지 확인하십시오.
  2. 당신도 모르는 사이에 누군가가 상태를 변경합니다.
  3. 문제가 apply 되었고—죄송합니다. 계획된 것과 다른 작업을 수행했습니다!

고맙게도 이 워크플로는 Terraform v0.11.0에서 개선되었습니다. 이 버전부터 apply 명령은 승인해야 하는 계획을 자동으로 제시합니다(명시적으로 yes 를 입력하여). 이 계획을 적용하면 제시된 대로 정확하게 실행된다는 장점이 있습니다.

또 다른 유용한 옵션은 -destroy 입니다. 이는 Terraform이 관리하는 모든 리소스를 파괴할 계획을 보여줍니다. -target 옵션을 사용하여 특정 리소스를 폐기 대상으로 지정할 수도 있습니다.

테라폼 적용

주어진 계획을 적용하면 Terraform이 나가서 독점성을 보장하기 위해 상태를 잠급니다. 그런 다음 리소스 변경을 진행하고 결국 업데이트된 상태를 푸시합니다. 한 가지 주의할 점은 일부 리소스는 다른 리소스보다 완료하는 데 시간이 더 오래 걸린다는 것입니다. 예를 들어 AWS RDS 인스턴스 생성은 12분 이상 걸릴 수 있으며 Terraform은 이 작업이 완료될 때까지 기다립니다. 분명히 Terraform은 이것으로 다른 모든 작업을 차단하지 않을 만큼 충분히 똑똑합니다. 요청된 변경 사항의 방향성 그래프를 생성하고 상호 종속성이 없는 경우 병렬 처리를 사용하여 실행 속도를 높입니다.

리소스 가져오기

종종 Terraform 및 기타 "코드로서의 구성" 솔루션은 이미 존재하는 환경에 점진적으로 도입됩니다. 전환은 정말 쉽습니다. 기본적으로 Terraform 내부에 정의되지 않은 모든 것은 관리되지 않고 변경되지 않은 상태로 남습니다. Terraform은 관리 대상에만 관심이 있습니다. 물론 이로 인해 문제가 발생할 수 있습니다. 예를 들어 Terraform이 구성 외부에 존재하는 일부 끝점에 의존하고 수동으로 파괴되는 경우입니다. Terraform은 이에 대해 알지 못하므로 상태가 변경되는 동안 이 리소스를 다시 생성할 수 없으며 계획 실행 중에 API에서 오류가 발생합니다. 이것은 내가 걱정할 일이 아닙니다. Terraform 도입의 이점은 단점보다 훨씬 큽니다.

도입 과정을 조금 더 쉽게 하기 위해 Terraform에는 import 명령어가 포함되어 있습니다. 이미 존재하는 외부 리소스를 Terraform 상태로 도입하고 해당 리소스를 관리할 수 있도록 하는 데 사용됩니다. 불행히도 Terraform은 적어도 작성 시점에서 가져온 모듈에 대한 구성을 자동 생성할 수 없습니다. 이 기능에 대한 계획이 있지만 지금은 먼저 Terraform에 리소스 정의를 작성한 다음 이 리소스를 가져와 Terraform에 관리를 시작하도록 지시해야 합니다. 상태로 가져오면(그리고 실행 계획 중에) .tf 파일에 작성한 것과 클라우드에 실제로 존재하는 것 사이의 모든 차이점을 볼 수 있습니다. 이런 식으로 구성을 추가로 조정할 수 있습니다. 이상적으로는 변경 사항이 표시되지 않아야 합니다. 즉, Terraform 구성이 클라우드에 이미 있는 것을 1:1로 반영합니다.

모듈

모듈은 Terraform 구성의 필수적인 부분이며 모듈을 수용하고 자주 사용하는 것이 좋습니다. 특정 구성 요소를 재사용하는 방법을 제공합니다. Docker 컨테이너를 실행하는 데 사용되는 AWS ECS 클러스터를 예로 들 수 있습니다. 이러한 클러스터가 작동하려면 클러스터 자체, 별도의 EC2 인스턴스를 관리하는 시작 구성, 이미지용 컨테이너 리포지토리, Autoscaling 그룹 및 정책 등 많은 별도의 리소스를 구성해야 합니다. 일반적으로 별도의 환경 및/또는 애플리케이션에 대해 별도의 클러스터가 필요합니다.

이를 극복하는 한 가지 방법은 구성을 복사하여 붙여넣는 것이지만 이것은 분명히 근시안적인 솔루션입니다. 가장 간단한 업데이트를 수행하면 오류가 발생합니다.

모듈을 사용하면 이러한 모든 개별 리소스를 하나의 구성 블록(모듈이라고 함) 아래에 캡슐화할 수 있습니다. 모듈은 "외부 세계"(또는 호출 코드)와 통신하는 인터페이스인 입력출력 을 정의합니다. 또한 모듈을 다른 모듈 내부에 중첩할 수 있으므로 전체 별도 환경을 빠르게 실행할 수 있습니다.

로컬 및 원격 모듈

상태와 유사하게 로컬 모듈 또는 원격 모듈을 가질 수 있습니다. 로컬 모듈은 Terraform 구성과 함께 저장됩니다(별도의 디렉토리, 각 환경 외부에 있지만 동일한 저장소에 있음). 간단한 아키텍처가 있고 해당 모듈을 공유하지 않는 경우 괜찮습니다.

그러나 이것은 한계가 있습니다. 해당 모듈의 버전을 지정하고 공유하기가 어렵습니다. 프로덕션 환경에서는 ECS 모듈의 v1.0.0을 사용하고 싶지만 스테이징 환경에서는 v1.1.0으로 실험하고 싶기 때문에 버전 관리가 중요합니다. 모듈이 코드와 함께 저장된 경우 모듈 코드에 대한 모든 변경 사항은 일반적으로 바람직하지 않은 모든 환경( apply 이 실행된 후)에 반영됩니다.

모듈 버전 관리에 대한 한 가지 편리한 접근 방식은 이들을 모두 별도의 저장소(예: your-company/terraform-modules)에 넣는 것입니다. 그런 다음 Terraform 구성 내에서 해당 모듈을 참조할 때 VCS 링크를 소스로 사용할 수 있습니다.

 module "my-module" { source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0" ... }

여기에서는 다른 환경에서 동일한 모듈의 다른 버전과 독립적으로 테스트할 수 있는 my-module(특정 경로)의 v1.1.0을 참조합니다.

그 외에 모듈의 검색 가능성 및 공유 가능성 문제가 있습니다. 잘 문서화되고 재사용 가능한 모듈을 작성하기 위해 노력해야 합니다. 일반적으로 앱마다 Terraform 구성이 다르며 앱 간에 동일한 모듈을 공유하려고 할 수 있습니다. 그것들을 별도의 리포지토리로 추출하지 않으면 매우 어려울 것입니다.

모듈 사용

모듈은 특수 모듈 블록을 정의하여 Terraform 환경에서 쉽게 참조할 수 있습니다. 다음은 가상의 ECS 모듈에 대한 블록의 예입니다.

 module "my_service_ecs_cluster" { source = "../modules/ecs_cluster" cluster_name = "my-ecs-service-${var.env}" repository_names = [ "my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ] service_name = "my-ecs-service-${var.env}" ecs_instance_type = "t2.nano" min_size = "1" max_size = "1" use_autoscaling = false alb_target_group_arn = "${module.my_alb.target_group_arn}" subnets = "${local.my_private_subnets}" security_groups = "${aws_security_group.my_ecs.id}" key_name = "${var.key_name}" env_tag = "${var.env}" project_tag = "${var.project_tag}" application_tag = "${var.api_app_tag}" asg_tag = "${var.api_app_tag}-asg" }

전달된 모든 옵션( source 와 같은 일부 전역 옵션 제외)은 이 모듈의 구성 내에서 입력(변수)으로 정의되었습니다.

모듈 레지스트리

최근 HashiCorp는 공식 Terraform 모듈 레지스트리를 출시했습니다. 이미 전투 테스트를 거친 모듈을 개발한 커뮤니티의 지식을 활용할 수 있기 때문에 이것은 좋은 소식입니다. 또한 일부 제품에는 "HashiCorp Verified Module" 배지가 있습니다. 이는 해당 제품이 검사를 받고 적극적으로 유지 관리되어 더 큰 자신감을 준다는 의미입니다.

이전에는 자신의 모듈을 처음부터 작성해야 했습니다(그리고 실수에서 배워야 함). 또는 동작에 대한 보장 없이(코드 읽기 제외!) GitHub 및 기타 장소에 게시된 모듈을 사용해야 했습니다.

환경 간 데이터 공유

이상적으로는 다른 AWS 계정을 사용하더라도 환경이 완전히 분리되어야 합니다. 실제로 한 Terraform 환경이 다른 환경의 일부 정보를 사용할 수 있는 경우가 있습니다. Terraform을 사용하도록 아키텍처를 점진적으로 변환하는 경우 특히 그렇습니다. One example might be that you have a global env that provides certain resources to other envs.

Let's say env global shares data with stage . For this to work, you can define outputs at the main level of the environment like so:

 output "vpc_id" { value = "${module.network.vpc_id}" }

Then, in the stage environment, you define a datasource that points to the remote state of global :

 data "terraform_remote_state" "global" { backend = "s3" config { bucket = "my-app-terraform-state" key = "terraform/global" region = "${var.region}" dynamodb_table = "terraform-state-lock-table" profile = "${var.profile}" } }

Now, you can use this datasource as any other and access all the values that were defined in global 's outputs:

 vpc_

Words of Caution

Terraform has a lot of pros. I use it daily in production environments and consider it stable enough for such work. Having said that, Terraform is still under active development. Thus, you will stumble on bugs and quirks.

Where to Report Issues and Monitor Changes

First of all, remember: Terraform has a separate core repo and repositories for each provider (eg, AWS). If you encounter issues, make sure to check both the core repo and the separate provider repositories for issues and/or opened pull requests with fixes. GitHub is really the best place to search for bugs and fixes as it is very active and welcoming.

This also means that provider plugins are versioned separately, so make sure you follow their changelogs as well as the core one. Most of the bugs I have encountered were resolved by upgrading the AWS provider which already had a fix.

Can't Cheat Your Way out of Cloud Knowledge

You cannot use Terraform to configure and manage infrastructure if you have no knowledge of how a given provider works. I would say this is a misconception and not a downside, since Terraform has been designed to augment and improve the workflow of configuration management and not to be some magic dust that you randomly sprinkle around and—poof! Environments grow! You still need a solid knowledge of a security model of each cloud, how to write, eg, AWS policies, what resources are available, and how they interact.

Prefer Separate Resources That Are Explicitly linked

There are certain resources—for example, the AWS security group or AWS route table—that allow you to configure the security rules and routes respectively, directly inside their own block. This is tempting, as it looks like less work but in fact will cause you trouble. The problems start when you are changing those rules on subsequent passes. The whole resource will be marked as being changed even if only one route/security rule is being introduced. It also gives implicit ordering to those rules and makes it harder to follow the changes. Thankfully, mixing those both approaches is not allowed now (see the note).

Best-practice example, with explicitly linked resources:

 resource "aws_security_group" "my_sg" { name = "${var.app_tag}-my-sg" ... } resource "aws_security_group_rule" "rule_one" { security_group_ ... } resource "aws_security_group_rule" "rule_two" { security_group_ ... }

Terraform plan Doesn't Always Detect Issues and Conflicts

I already mentioned this in the case where you were managing resources with Terraform that were relying on other, unmanaged infrastructure. But there are more trivial examples—for example, you will get an error if your EC2 instance has Termination Protection enabled, even though plan would show you it's OK to destroy it. You can argue that this is what Termination Protection has been designed for, and I agree, but there are more examples of things you can do in theory/on plan but when executed will deadlock or error out. For example, you cannot remove a network interface if something is using it—you get a deadlock without an option to gracefully recover.

Syntax Quirks

There are also quirks related to how HCLv1 (the syntax language Terraform uses) has been designed. It has a couple of frustrating quirks. There is work underway to provide an improved version of the parser for HCLv2. The best way to read on the current limitations and the plan to overcome them is this fantastic blog series. In the meantime, there are workarounds for most of those issues. They are not pretty and they will fail once v0.12 comes out, but hey, it is what it is.

When State Update Fails

It sometimes happens that Terraform is not able to correctly push an updated state. This is usually due to underlying network issues. The solution is to retry the state update instead of running apply again, which will fork the state .

Another issue might happen when state lock (the synchronization primitive that prevents multiple users to update the same state) fails to be taken down by Terraform. This involves running terraform force-unlock with the lock ID to take it down manually.

Thankfully, in case of such problems, Terraform provides you with a good description and steps you need to make to fix it.

Not Everything Is Fun to Manage Through Terraform

There are certain cases where Terraform is not my tool of choice. For example, configuring AWS CodePipeline and CodeBuild projects (AWS equivalent of CI/CD pipeline) is cumbersome when done through Terraform. You need to define each step through very verbose configuration blocks and things like “Login via GitHub” are a lot more complicated than using the UI. Of course, it's still possible if you prefer to have it codified. Well, I guess it's a good candidate for a well-written module!

Same thing goes for managing AWS API Gateway endpoints. In this case, using a dedicated serverless framework would be a better option.

When configuring AWS resources with Terraform, you will find yourself writing a lot of policies. Policies that would otherwise often be auto-generated for you (when using the UI). For those, I'd recommend the AWS Visual Editor and then copying the resulting policy JSON into Terraform.

결론

Using Terraform has been fun and I'll continue doing so. Initial steps can be a bumpy ride, but there are more and more resources that help to ease you in.

I'd definitely recommend taking Terraform for a spin and simply playing with it. Remember, though—be safe and test it out on a non-essential account. If you are eligible for AWS Free Tier, use it as a 12-month free trial. Just be aware it has limitations as to what you can provision. Otherwise, just make sure you spin the cheapest resources, like t3.nano instances.

I highly recommend extensions for Terraform support in various code editors. For Visual Studio Code, there is one with syntax highlighting, formatting, validation and linting support.

It's always valuable to learn new things and evaluate new tools. I found that Terraform helped me immensely in managing my infrastructure. I think working with Terraform will only get easier and more fun, especially once v0.12.0 ships with a major upgrade to the HCL syntax and solve most of the quirks. The traction and community around Terraform are active and vibrant. You can find a lot of great resources on things I didn't manage to cover in a single blogs post, eg, a detailed guide on how to write modules.

Related: Terraform vs. CloudFormation: The Definitive Guide