-------------------------------------------------------------------------------- # TERRAFORM-STUDY # -------------------------------------------------------------------------------- ######################################################## ### Terraform https://www.terraform.io/ https://developer.hashicorp.com/terraform/docs https://developer.hashicorp.com/terraform/docs/terraform-tools https://registry.terraform.io/ https://github.com/shuaibiyy/awesome-terraform https://t1.daumcdn.net/cfile/tistory/99938D445C9DADAC25?original ######################################################## ### Reference - Iac https://www.redhat.com/ko/topics/automation/what-is-infrastructure-as-code-iac https://hvho.github.io/2021-07-25/terraform-up-and-running-1 - Terraform https://t1.daumcdn.net/cfile/tistory/99938D445C9DADAC25?original https://gurumee92.tistory.com/category/IaC/Terraform https://j-dev.tistory.com/search/Terraform?page=1 https://www.44bits.io/ko/keyword/terraform https://www.44bits.io/ko/post/terraform_introduction_infrastrucute_as_code https://j-dev.tistory.com/search/Terraform?page=1 https://hvho.github.io/2021-08-29/terraform-up-and-running-2 - Terraform AWS https://developer.hashicorp.com/terraform/tutorials/aws-get-started https://terraform101.inflearn.devopsart.dev/cont/ - Terraform Azure https://developer.hashicorp.com/terraform/tutorials/azure-get-started https://learn.microsoft.com/ko-kr/azure/developer/terraform/ - Terraform GCP https://developer.hashicorp.com/terraform/tutorials/gcp-get-started https://cloud.google.com/docs/terraform/get-started-with-terraform - Terraform Naver Cloud https://docs.toast.com/ko/Compute/Instance/ko/terraform-guide/ - Terraform Etc https://kim-dragon.tistory.com/249 - Github source https://github.com/hashicorp/terraform-guides https://github.com/hashicorp/tfc-guide-example https://github.com/futurice/terraform-examples https://github.com/ContainerSolutions/terraform-examples https://github.com/gruntwork-io/intro-to-terraform https://github.com/alfonsof/terraform-aws-examples https://github.com/PARKINHYO/WordPress-Terraform https://github.com/gurumee92/gurumee-book-terraform https://github.com/rampart81/terraform-examples ######################################################## ### Terraform Guide # IaC(Infrastruction as Code) 코드를 이용해 인프라를 자동으로 구축, 관리, 프로비저닝하는 접근 방식 프로비저닝, 시스템 변경 및 구성에 대해 일관되게 반복되는 과정을 코드를 통해 자동화 하면 빠르게 변경/구성할 수 있으며, 수동으로 구성 시 발생하는 누락 및 잘못 설정하는 등의 인적 실수를 방지 인프라 구성을 코드로 처리하므로 애플리케이션 구성과의 경계가 좁아지면서 개발자와 운영자의 경계가 모호해진 DevOps에서 많이 사용 * Iac 종류 - 애드혹 스크립트 bash 스크립트 등을 서버에 직접 실행하는 방식 - Provisioning Tool 컴퓨터나 가상호스트를 사용해 라이브러리나 서비스등을 설치하는것을 의미 ex) Terraform, Cloudformation - SCM Tool (구성 관리 도구) 성능부터 H/W 속성과 라이프사이클 전반에 걸친 요구사항 설계 및 운영정보의 일관선을 유지하기 위한 시스템 프로세스 ex) Chef Puppet Ansible - 서버 템플릿 도구 도커 등으로 서버 환경에 필요한 운영체제, 소프트웨어, 파일등을 모두 포함하고 있는 이미지(스냅숏)으로 관리하는 도구 - 오케스트레이션 도구 쿠버네티스 등으로 위 서버 템플릿 도구로 생성한 서버 스냅숏을 실제 인프라에 띄워 관리하는 도구 * Iac 장점 - 자급식 배포 자동 배포 파이프라인을 구성해 놓음으로써, 일부의 관리자 말고도 모든 개발자가 원할때 직접 배포 가능 - 속도와 안정성 사람 대신 컴퓨터가 자동으로 배포하면 당연히 속도가 빨라지고 human error 가 발생할 소지가 적음 - 문서화 소스파일 자체가 인프라에 대한 문서 역할 - 버전관리 인프라의 변경 내역을 git 과 같은 버전 관리 도구로 관리할 수 있음 - 유효성 검증 인프라가 변경을 적용 전 정적 분석을 통해 오류를 미리 확인 가능 - 재사용성 인프라 코드를 재사용 함으로써 비슷한 인프라에 대해서 재사용 가능 # Terraform https://www.terraform.io/ https://developer.hashicorp.com/terraform/docs Terraform은 Hashicorp에서 오픈소스로 개발중인 클라우드 인프라스트럭처 자동화를 지향하는 Infrastructure as Code, IaC 도구 IaC는 코드로 인프라스트럭처를 관리한다는 개념으로 테라폼에서는 하시코프 설정 언어HCL, Hashicorp Configuration Language을 사용해 클라우드 리소스를 선언 아마존 웹 서비스Amazon Web Service가 자체적으로 만든 AWS CloudFormation의 경우 AWS만 지원하는 것과 달리 테라폼의 경우 Amazon Web Service, Google Cloud Platform, Microsoft Azure와 같은 주요 클라우드 서비스를 비롯한 다양한 클라우드 서비스들을 프로바이더 방식으로 제공 이를 통해 테라폼만으로 멀티 클라우드의 리소스들을 선언하고 코드로 관리하는 것도 가능 테라폼은 고Go 프로그래밍 언어로 개발 테라폼 사용자는 HCL 언어로 클라우드 리소스를 정의하고 이 내용을 테라폼 CLI 애플리케이션(terraform)으로 자신의 클라우드 계정에 실제로 반영 API를 호출해 명령을 실행하는 절차적인 방법과 달리 HCL은 선언적으로 리소스를 정의하기 때문에 리소스를 정의하고 여러번 테라폼을 실행한다고 여러 개의 리소스가 만들어지지는 않음(멱등성) 테라폼을 사용하면 이 과정을 계획(plan)과 적용(apply) 단계로 나누어 진행 plan 서브 명령어를 사용하면 클라우드에 적용될 변화 사항을 보여주며 apply 서브 명령어는 이를 자신의 클라우드 계정에 실제로 적용 # Terraform 설치 https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli Terraform을 메뉴얼하게 바로 설치해도 좋지만, 실제 개발 환경에 따라서 여러 버전을 사용할 가능성이 있다. 때문에 Terraform 버전 관리 도구인 tfenv를 먼저 설치하고, 이를 이용해서 Terraform을 설치한다. # tfenv 설치 $ brew install tfenv # tfenv 설치 확인 $ tfenv --version tfenv 2.2.0 # terraform 최신 버전 설치 $ tfenv install # terraform 특정 버전 설치를 원한다면 다음과 같은 형식으로 할 수 있다. $ tfenv install 0.14.6 # terraform 버전 사용 $ tfenv use 0.14.6 # terraform 버전 확인 $ terraform version Terraform v0.14.6 # 테라폼 기본 개념 * 프로비저닝 어떤 프로세스나 서비스를 실행하기 위한 준비 단계 프로바이더로는 aws, 구글 클라우드 플랫폼, 마이크로소프트 애저와 같은 범용 클라우드 서비스를 비롯해 깃허브, 데이터 도그와 같은 특정 기능을 제공하는 서비스, mysql, 도커와 같은 로컬 서비스를 지원 * Provider Terraform 은 AWS, GCP, AZURE 등 여러 클라우드 인프라 환경을 지원하는데, 이렇게 클라우드 인프라 공급자 들이 Provider * Resource 리소스란 특정 프로바이더가 제공해주는 조작 가능한 대상의 최소 단위 예를 들어 EC2 instance, LB( Load Balencer ), DNS( Domain Name Server ), RDS 등의 각 요소들 * Plan 테라폼 프로젝트 디렉터리 아래의 모든 .tf 파일의 내용을 실제로 적용 가능한지 확인 하는 작업 테라폼은 이를 terraform plan 명령어로 제공하며, 이를 명령어로 실행하면 어떤 리소스가 생성되고, 수정되고, 삭제될지 보여줌 * Apply 테라폼 프로젝트 디렉터리 아래의 모든 .tf 파일의 내용대로 리소스를 생성, 수정, 삭제하는 일을 적용 * HCL HCL ( Hashicorp Configuration Languate ) 는 Terraform 을 만든 제작사인 hashicorp 에서 만든 Terraform 전용 DSL ( Domain Specific Language ) Terraform 의 설정 파일들은 HCL 로 기술되며, .tf 의 파일 확장자를 가짐 * state 테라폼을 통해 생성된 리소스들의 상태를 의미 (= 테라폼 apply 명령어를 실행한 결과물) * output 테라폼으로 만든 리소스를 변수 형태로 state에 저장하는 것을 의미 * module 공통적으로 활용할 수 있는 모듈을 정의하는 것을 의미 * remote 다른 경로의 state를 참조하는 것을 의미하며, output 변수를 불러올 때 주로 사용 # Terraform command ⚡ root@localhost ~ terraform -help Usage: terraform [global options] <subcommand> [args] The available commands for execution are listed below. The primary workflow commands are given first, followed by less common or more advanced commands. Main commands: init Prepare your working directory for other commands validate Check whether the configuration is valid plan Show changes required by the current configuration apply Create or update infrastructure destroy Destroy previously-created infrastructure All other commands: console Try Terraform expressions at an interactive command prompt fmt Reformat your configuration in the standard style force-unlock Release a stuck lock on the current workspace get Install or upgrade remote Terraform modules graph Generate a Graphviz graph of the steps in an operation import Associate existing infrastructure with a Terraform resource login Obtain and save credentials for a remote host logout Remove locally-stored credentials for a remote host output Show output values from your root module providers Show the providers required for this configuration refresh Update the state to match remote systems show Show the current state or a saved plan state Advanced state management taint Mark a resource instance as not fully functional test Experimental support for module integration testing untaint Remove the 'tainted' state from a resource instance version Show the current Terraform version workspace Workspace management Global options (use these before the subcommand, if any): -chdir=DIR Switch to a different working directory before executing the given subcommand. -help Show this help output, or the help for a specified subcommand. -version An alias for the "version" subcommand. # Terraform LifeCycle Terraform 으로 인프라를 프로비저닝 하는 일련의 작업은 계획(Plan)-적용(Apply)-삭제(Destroy) 의 과정 ------------------ * 계획(Plan) 테라폼을 통해 인프라 작업을 하면, 직접 콘솔을 통해 인프라를 변경시키는 것에 비해서 코드를 실제 환경하기 적용하기 전에 “검증” 단계를 거칠 수 있는 장점 콘솔로 작업하면 인프라가 바로 변경되지만, 코드로 작업하면 해당 코드를 환경에 적용하기 전 static validation 할 수 있음 해당 단계를 테라폼은 계획 단계라고 하며, 해당 단계는 현재 적용되어 있는 리소스들의 상태와 코드를 적용 시켰을 때 변경될 리소스의 상태의 차이를 보여줌 코드를 실제 환경에 적용하기 전, 코드의 변경점이 내가 의도한 변경사항이 맞는지 확인해 보는 단계이고, 해당 명령어는 실제 환경에 영향을 주지 않기 때문에 내가 정확히 원하는 결과를 얻을 때 까지 반복해서 확인 가능 * 적용(Apply) 코드의 변경 사항을 실제 환경에 적용시키는 단계 적용이 성공한다면 변경 ( 추가 / 변경 / 삭제 ) 된 리소스의 정보가 출력되고, 만약 실패한다면 실패한 이유 출력 적용 명령은 멱등성(idempotent) 을 가지기 때문에 여러번 적용하여도 작성한 코드와 동일한 상태를 보장 리소스 여러개를 생성하다 중간에 실패했을 경우에도 코드를 고쳐 다시 적용하면 이전에 만들어졋던 리소스는 알아서 처리 * 삭제(Destory) 모든 인프라를 삭제하는 명령어 ------------------ * terraform import 운영중인 리소스를 참조해서 code화 시킬 수 있음 terraform(0.12.16)에서는 import 명령어를 사용하기 전에 빈 tf 파일이 존재해야 함 terraform import aws_instance.<resource name> <instance ID> import가 되었다면 .tfstate 파일이 업데이트 됨 (실행 폴더에 .tfstate 파일이 미존재시 새로 생성) import는 사용에 매우 유의 테라폼은 apply시에 .tf 파일에는 없고 .tfstate 에만 있는 인프라 정보를 삭제 만약 운영환경에서 모든 ec2를 import한 후 .tf파일을 생성하지 않은채 apply 할 경우 모든 운영환경이 destroy 됨 * terraform graph 하나의 리소스에서 다른 리소스로 참조를 추가하면 내재된 종속성이 작성 테라폼은 이러한 종속성 구문을 분석하여 그래프를 작성하고 이를 사용하여 리소스를 생성하는 순서를 자동으로 결정 결과값으로는 DOT라는 언어로 되어 있으며, 그래프비즈 혹은 그래프비즈온라인(https://dreampuf.github.io/GraphvizOnline/) 같은 앱을 사용하면 그래프 이미지로 확인 가능 ------------------ # Terraform work process 0. provider 정보 생성 - AWS 계정, API 키 설정 1. *.tf 작성 - HCL 언어로 필요한 리소스 선언(*.tf 생성) 2. terraform init - 선언된 리소스들이 생성 가능한지 계획 확인 3. terraform validate - 유효성 검사 4. terraform plan - 선언된 리소스들이 생성 가능한지 계획 확인 5. terraform appply - 선언된 리소스들을 적용r 6. terraform destory - 선언된 리소스 한번에 제거 # Terraform 변수 선언 예시 * 입력 변수 variable "NAME" { description = "<description>" default = <default> type = <type> } - description(Option) : 변수를 설명하는 변수 - default(Option) : 변수에 값을 전달하는 여러가지 방법이 있는데 -var(명령줄), -var-file(파일), ‘TF_VAR_<variable_name>’(환경변수)를 통해 값을 전달 만약 값이 전달되지 않으면 기본값을 할당 기본값이 없는 경우 테라폼은 사용자에게 변수에 대한 정보를 물음 - type(Option) : 변수의 유형을 지정 string, number, bool, list, map, set, object, tuple등의 유형이 있음 유형을 지정하지 않으면 any로 간주 - ex) 변수에 값 전달 예시 #명령줄 $ terraform plan -var "server_port=8080" #환경변수 $ export TF_VAR_server_port=8080 $ terraform plan * 출력 변수 output "NAME" { value = "<value>" description = <description> senstive = <senstive> } - value : 출력하려는 값 - description(Option) : 출력 변수를 설명하는 변수 - senstive(Option) : 변수 출력 실행이 끝날때 기록하지 않도록 하려면 senstive를 true로 설정해야함 출력 변수에 개인정보 등 민감한 데이터가 있을 시 유용 # Terraform 상태 / Backend 해당 파일에는 테라폼을 실행 시 매핑되는 리소스들의 상태값을 가지고 있음 기본적으로 /terraform/test 폴더에서 테라폼을 실행하면 JSON 형태의 /terraform/testterraform.tfstate 파일을 생성 상태파일은 프라이빗이며 직접 편집하거나 수정해서는 안됨 파일 상태를 조작해야 하는 경우 terraform import, terraform state 명령을 통해 조작 terraform init과 terraform apply 명령어를 실행하면 실제로 다음의 파일이 생성 .terraform .terraform.lock.hcl terraform.tfstate .terraform.* 형태의 파일들은 terraform init 명령어를 실행할 때 생성되며 terraform.tfstate는 terraform apply 명령어 실행 후 생성 terraform.tfstate는 JSON 형태로 되어 있는데, 이는 terraform으로 구성된 인프라스터럭처의 현재 상태를 보여준다. .terraform.lock.hcl은 잠금 파일이며 경쟁 상태에서 생길 수 있는 문제들을 피할 수 있게 해준다. 하지만 개인 프로젝트처럼 개인 혹은 머신 한대에서 terraform으로 인프라를 운영한다면 딱히 도움되는 것은 없다. 하지만 팀으로써 운영한다면 얘기가 달라진다. 이 장에서는 terraform을 팀으로써 운영할 때 상태 파일을 공유하고, 이 상태 파일을 이용해서 다른 인프라스트럭처 구성 시 어떻게 사용할 수 있는지 확인 가능 테라폼을 사용하여 인프라를 업데이트하려면 각 팀원이 동일한 테라폼 상태 파일에 엑세스해야 하므로, 상태 파일을 공유 위치에 저장해야 합니다. 두 팀원이 동시에 테라폼을 실행하는 경우 여러 테라폼 프로세스가 상태 파일을 동시에 업데이트하여 충돌을 일으킬 수 있습니다. 이러한 상태가 되면 데이터가 손실되거나 상태 파일이 손상될 수 있습니다. 인프라를 변경할 때는 다른 환경을 격리하는 것이 가장 좋습니다. 예를 들어 테스트 환경을 변경할 경우 실수로 운영 환경이 중단되는 경우가 없는지 확인해야 합니다. * 상태 파일 공유 테라폼의 상태 파일을 깃과 같은 형상관리 시스템에 공유하는 것은 다음과 같은 이유 때문에 부적절 1. 변경 사항을 실행하고 나서 푸시하는 것을 누락 할 수 있음 2. 여러 명의 팀 구성원이 동시에 하나의 상태 파일에 apply 명령을 실행하지 못하게 하는 잠금 기능을 제공하지 않음 3. 테라폼의 상태 파일은 모든 데이터를 평문으로 저장하기에 보안적으로 적절하지 않음 위의 문제와 같은 상황을 해결하기 위해서는 테라폼에 내장된 원격 백엔드 기능을 사용하여 클라우드 스토리지에 저장하는 것이 좋음 1. 원격 백엔드 구성 시 테라폼은 plan이나 apply 명령을 실행할 때마다 해당 백엔드에서 상태 파일을 자동으로 로드 apply 명령을 실행한 후에는 상태 파일을 백엔드에 자동 저장 2. apply 명령을 실행 시 자동으로 잠금을 활성화 -lock-timeout=<TIME>을 사용하면 apply 명령을 실행할 때 잠금이 해제되기까지 테라폼이 얼마 동안 대기하도록 할지 설정 3. 데이터를 보내거나 상태 파일을 저장할 때 암호화하는 기능을 지원 테라폼 백엔드에는 몇 가지 단점 존재 1. 2단계로 백엔드를 구성 - 백엔드 구성 시 테라폼 코드를 작성하여 S3 버킷 및 다이나모DB 테이블을 생성하고 해당 코드를 로컬 백엔드와 함께 배포 테라폼 코드로 돌아가서 원격 백엔드 구성을 추가 새로 생성된 S3 버킷과 다이나모 DB 테이블을 사용하고, init 명령을 실행하여 로컬 상태를 S3에 복사 - 백엔드 삭제 시 백엔드 구성을 제거한 다음 init 명령을 실행하여 테라폼 상태를 로컬 디스크에 다시 복사 destroy 명령을 실행하여 S3 버킷 및 다이나모DB 테이블을 삭제 2. 테라폼의 백엔드 블록에서는 변수나 참조를 사용할 수 없음 해당 단점의 유일한 해결책은 init 명령어 실행 시 -backend-config 옵션을 통해 변수를 전달하는 것 GCP 스토리지 백엔드 참조 https://www.terraform.io/language/settings/backends/gcs AWS S3 백엔드 참조 https://www.terraform.io/language/settings/backends/s3 백엔드 구성 참조 https://www.terraform.io/language/settings/backends/configuration * 상태 파일 격리 하나의 환경에서 문제가 발생하더라도 다른 환경에 영향을 주지 않도록 상태 파일을 격리 1.작업 공간을 통한 격리 테라폼은 별도의 이름을 가진 여러 개의 작업 공간에 저장할 수 있음 테라폼은 default라는 기본 작업 공간에서 시작하며 작업 공간을 지정하지 않으면 기본 작업 공간을 사용 # 테라폼 워크 스페이스를 생성합니다. $ terraform workspace new example1 # 현재 사용중인 워크스페이스를 보여줍니다. $ terraform workspace show # 생성된 워크스페이스 목록을 보여줍니다. $ terraform workspace list # 워크스페이스를 전환할 수 있습니다. $ terraform workspace select <workspace name> 워크스페이스 생성 후 적용 시 S3에는 ‘evn:’ 라는 폴더가 생성 되고, 각 워크스페이스 마다 각자의 상태 파일을 따로 보유 GCP에서는 백엔드에 지정한 경로에 워크스페이스 명.tfstate 파일이 생성 위의 워크스페이스에는 몇 가지 단점이 있음 모든 작업 공간의 상태 파일이 동일한 스토리지에 저장 terraform workspace 명령어를 실행하지 않으면 워크스페이스에 대한 정보를 알기가 어려움 어떠한 워크스페이스에서 작업하는지 잊어버릴 수가 있어 실수를 할 수가 있음 2. 파일 레이아웃을 이용한 격리 환경을 완전히 격리하려면 각 테라폼 구성 파일을 분리된 폴더에 위치 예를 들어 스테이징 환경의 대한 모든 구성은 stage 폴더에, 프로덕션 환경의 모든 구성은 prod 폴더에 위치 서로 다른 권한을 사용하여 다른 백엔드를 구성 예를 들어 각 환경은 각각 분리된 S3 버킷을 백엔드로 사용 각 환경별 VPC, 서비스, 데이터 베이스 같은 각 구성 요소를 별도의 테라폼 폴더 혹은 별도의 상태 파일에서 사용하는 것을 권장 위와 같은 구성은 면에서 단점 실수로 인한 인프라를 망가뜨리진 않겠지만 한 번의 명령으로 전체 인프라를 만들수 없음 위와 같은 구성에서는 각 구성 요소 각각에 apply 명령을 실행해야 함 테라그런트를 사용하는 경우 apply-all 명령을 사용하여 이 프로세스를 자동화 할 수는 있음 다른 폴더에 있는 리소스를 직접 액세스 할 수 없어 리소스 종속성을 사용할 수 없음 # Terraform 모듈 테라폼으로 인프라의 규모가 커질경우 하나의 파일에 모든것을 정의할 경우 의도치않게 다른 부분에 영향을 끼칠 수 있고 환경별 같은 리소스의 코드가 중복되어 쌓일수가 있음 이러한 단점을 해결하기 위해 테라폼은 모듈이란 요소를 제공 모듈은 관련있는 요소끼리 모아 하나의 패키지를 만든다 예를 들어 VPC 모듈의 경우 서브넷, netmask 등의 리소스를 하나의 패키징을 한다. - 모듈의 장점 1. 캡슐화 : 서로 관련있는 요소들 끼리만 캡슐화를 하여 의도치 않은 문제 발생을 예방 2. 재사용성 : 모듈을 사용하여 리소스를 정의하면 다른 환경에서도 해당 리소스를 쉽게 재사용 3. 일관성 : 매번 새로 작성하게 되면 사람에 따라 리소스의 옵션이 빠지는 부분이 생길수도 있고 매번 같을 수 없기에 모듈을 재 사용시 일관성을 유지 * 모듈의 기본 구문 예시 module "<NAME>" { source = "<SOURCE>" [CONFIG...] } 위와 같은 구문을 사용하여 모듈의 경로를 지정하여 참조 provider "google" { credentials = file("key.json") project = "terraform-348208" region = "asia-northeast3" } module "webserver_cluster" { source = "../../../modules/services/webserver-cluster" } * 모듈 입력 테라폼의 모듈에서도 입력 매개 변수를 만들어 사용할 수 있음 - 모듈 코드 예시 variable "cluster_name" { description = "The name to use for all the cluster resources" type = string } resource "aws_security_group" "instance" { name = "${var.cluster_name}-instance" } - 모듈을 사용하는 코드 예시 module "webserver_cluster" { source = "../modules/webserver-cluster" cluster_name = "webserver-prod" } * 모듈 지역 변수 변수를 입력받는 대신 공통적으로 사용하고 있는 값들은 지역변수로 정의하여 사용 - 지역 변수 문법 local.<NAME> - 모듈에서 사용 예시 locals { http_port = 80 any_port = 0 any_protocol = "-1" tcp_protocol = "tcp" all_ips = ["0.0.0.0/0"] } resource "aws_security_group_rule" "allow_http_inbound" { type = "ingress" security_group_id = aws_security_group.alb.id from_port = local.http_port to_port = local.http_port protocol = local.tcp_protocol cidr_blocks = local.all_ips } * 모듈 출력 변수 - 모듈에서 output 선언 output "asg_name" { value = aws_autoscaling_group.example.name description = "The name of the Auto Scaling Group" } - 모듈 output 사용 문법 예시 module.<MODULE_NAME>.<OUTPUT_NAME> - 모듈 output 사용 예시 module "webserver_cluster" { source = "../../../modules/services/webserver-cluster" } resource "aws_autoscaling_schedule" "scale_out_during_business_hours" { scheduled_action_name = "scale-out-during-business-hours" min_size = 2 max_size = 10 desired_capacity = 10 recurrence = "0 9 * * *" autoscaling_group_name = module.webserver_cluster.asg_name } * 모듈 주의 사항 1. 파일 경로 테라폼은 내장 함수 file을 사용하여 디스크에서 파일을 읽을 수 있음 file 함수를 사용할 때 파일 경로가 상대 경로여야 함 즉, terraform apply 명령어를 실행하는 경로에서는 file 함수를 사용할 수 있지만 참조되는 별도의 모듈에서는 file 함수를 사용할 수는 없음 이 문제를 해결하기 위해서는 경로 참조 표현식을 사용 path.<TYPE> path.module : 해당 표현식이 있는 모듈의 파일 시스템 경로 path.root : terraform apply를 실행하는 루트 모듈의 파일 시스템 경로 path.cwd : 현재 작업 중인 파일 시스템의 경로를 반환 일반적으로는 path.root와 동일하지만 테라폼 일부 기능은 다른 디렉토리에서 작동하므로 경로가 달라질 수 있음 - 사용 예시 data "template_file" "user_data" { template = file("${path.module}/user-data.sh") } 2. 인라인 블록 일부 테라폼 리소스의 구성은 인라인 블록 또는 별도의 리소스로 정의 모듈을 작성할때는 인라인 블록 대신 별도의 리소스를 사용해야 유연성이 높음 - 인라인 블록 예시 resource "aws_security_group" "alb" { .... ingress { from_port = local.http_port to_port = local.http_port protocol = local.tcp_protocol cidr_blocks = local.all_ips } } - 리소스 예시 resource "aws_security_group_rule" "allow_http_inbound" { type = "ingress" security_group_id = aws_security_group.alb.id from_port = local.http_port to_port = local.http_port protocol = local.tcp_protocol cidr_blocks = local.all_ips } # Example * AWS example.tf --------------------------------------------------------------------------- provider "aws" { region = "ap-northeast-2" access_key = "your_access_key" secret_key = "your_secret_key" } resource "aws_security_group" "node" { name = "allow_node_js_and_ssh" description = "Allow SSH and nodejs port from all" ingress { cidr_blocks = ["0.0.0.0/0"] protocol = "tcp" from_port = 22 to_port = 22 } ingress { cidr_blocks = ["0.0.0.0/0"] protocol = "tcp" from_port = 3000 to_port = 3000 } } data "aws_security_group" "default" { name = "default" } resource "aws_instance" "example" { ami = "ami-0e17ad9abf7e5c818" instance_type = "t2.micro" key_name = "example" vpc_security_group_ids = [ aws_security_group.node.id, data.aws_security_group.default.id ] provisioner "remote-exec" { connection { user = "ec2-user" private_key = file("your/pem/key") host = aws_instance.example.public_ip } inline = [ "sudo amazon-linux-extras install epel -y", "sudo yum install --enablerepo=epel -y nodejs", "sudo wget https://your.helloworld.js.link -O /home/ec2-user/helloworld.js", "sudo wget https://your.helloworld.service.link -O /etc/systemd/system/helloworld.service", "sudo systemctl enable helloworld", "sudo systemctl start helloworld", ] } } * GCP example.tf (Web server) --------------------------------------------------------------------------- provider "google" { credentials = file("key.json") project = "terraform-348208" region = "asia-northeast3" } resource "google_compute_instance" "example" { name = "webserver" machine_type = "f1-micro" zone = "asia-northeast3-a" boot_disk { initialize_params { image = "gcr.io/google-containers/busybox" } } network_interface { network = "default" access_config {} } tags = ["web"] metadata_startup_script = <<-EOF #!/bin/bash echo "Hello, World" > index.html nohup busybox httpd -f -p 8080 & EOF } resource "google_compute_firewall" "default" { name = "webserver-firewall" network = "default" allow { protocol = "tcp" ports = ["8080"] } source_tags = ["web"] target_tags = ["web"] } * AWS example.tf (Web server) --------------------------------------------------------------------------- provider "aws" { region = "us-east-2" } resource "aws_instance" "example" { ami = "ami-xxxxxxxxxxx" instance_type = "t2.micro" vpc_security_group_ids = [aws_security_group.instance.id] user_data = <<-EOF #!/bin/bash echo "Hello, World" > index.html nohup busybox httpd -f -p 8080 & EOF tags = { Name = "terraform-example" } } resource "aws_security_group" "instance" { name = var.security_group_name ingress { from_port = 8080 to_port = 8080 protocol = "tcp" 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" } * AWS example.tf (변수 선언 예시) --------------------------------------------------------------------------- variable "number_example" { description = "An example of a number variable in Terraform" type = number default = 42 } variable "list_example" { description = "An example of a list in Terraform" type = list default = ["a", "b", "c"] } variable "list_numeric_example" { description = "An example of a numeric list in Terraform" type = list(number) default = [1, 2, 3] } variable "map_example" { description = "An example of a map in Terraform" type = map(string) default = { key1 = "value1" key2 = "value2" key3 = "value3" } } 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 } } * GCP example.tf (스토리지 생성 리소스) --------------------------------------------------------------------------- provider "google" { credentials = file("key2.json") project = "terraform-348208" region = "asia-northeast3" } resource "random_id" "instance_id" { byte_length = 8 } resource "google_storage_bucket" "terraform_state" { # 버킷 이름 name = "terraform-up-and-running-state-${random_id.instance_id.hex}" # 버킷을 삭제할 때 해당 버킷에 포함된 모든 객체를 삭제합니다. force_destroy = true location = "ASIA" storage_class = "Standard" # 실수로 S3 버킷을 삭제하는 것을 방지합니다.(생명주기 설정) lifecycle { prevent_destroy = true } # 코드 이력을 관리하기 위해 상태 파일의 버전 관리를 활성화합니다. versioning { enabled = false } // GCP는 스토리지 디폴트가 암호화 } terraform { backend "gcs" { bucket = "terraform-up-and-running-state-0d9027ef04cb8b9b" credentials = "key2.json" } } * AWS example.tf (S3 생성 리소스) --------------------------------------------------------------------------- provider "aws" { region = "us-east-2" } resource "aws_s3_bucket" "terraform_state" { bucket = var.bucket_name // This is only here so we can destroy the bucket as part of automated tests. You should not copy this for production // usage force_destroy = true # 상태 파일의 이력관리를 위해 # 버전관리 기능을 활성화 합니다. versioning { enabled = true } # Enable server-side encryption by default server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } } } } resource "aws_dynamodb_table" "terraform_locks" { name = var.table_name billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" attribute { name = "LockID" type = "S" } } * Docker example.tf (도커에 nginx 설치) --------------------------------------------------------------------------- terraform { required_providers { docker = { source = "kreuzwerker/docker" } } } provider "docker" {} resource "docker_image" "nginx" { name = "nginx:latest" keep_locally = false } resource "docker_container" "nginx" { image = docker_image.nginx.latest name = "tutorial" ports { internal = 80 external = 8000 } } * AWS example.tf (ELB 생성 리소스) --------------------------------------------------------------------------- provider "aws" { region = "us-east-1" } variable "server_port" { description = "The port the server will use HTTP requests" default = 8080 } data "aws_availability_zones" "all" {} resource "aws_security_group" "instance" { name = "terraform-example-instance" ingress { from_port = var.server_port to_port = var.server_port protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } lifecycle { create_before_destroy = true } } resource "aws_security_group" "elb" { name = "terraform-example-elb" 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"] } } resource "aws_launch_configuration" "example" { image_id = "ami-0d5eff06f840b45e9" instance_type = "t2.micro" security_groups = [aws_security_group.instance.id] user_data = <<-EOF #!/bin/bash echo "Hello, World" > index.html nohup python3 -m http.server ${var.server_port} & EOF lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "example" { launch_configuration = aws_launch_configuration.example.id availability_zones = data.aws_availability_zones.all.names load_balancers = [aws_elb.example.name] health_check_type = "ELB" min_size = 2 max_size = 10 tag { key = "Name" value = "terraform-asg-example" propagate_at_launch = true } } resource "aws_elb" "example" { name = "terraform-asg-example" availability_zones = data.aws_availability_zones.all.names security_groups = [aws_security_group.elb.id] listener { lb_port = 80 lb_protocol = "http" instance_port = var.server_port instance_protocol = "http" } health_check { healthy_threshold = 2 unhealthy_threshold = 2 timeout = 3 interval = 30 target = "HTTP:${var.server_port}/" } } output "elb_dns_name" { value = aws_elb.example.dns_name } * AWS example.tf (To-Do) --------------------------------------------------------------------------- Terraform을 위한 AWS 환경 준비하기 Terraform으로 IAM 구성하기 Terraform으로 S3 구성하기 Terraform으로 DynamoDB 구성하기 Terraform으로 VPC 구성하기 Terraform으로 EC2 구성하기 Terraform으로 RDS 구성하기 Terraform으로 AutoScaling Group 구성하기 Terraform으로 Network Load Balancer 구성하기 Terraform으로 Application Load Balancer 구성하기