Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.

Terraform의 plan 결과를 저장해서 사용하기

Terraform으로 리소스를 관리하다 보면 자연히 관리하는 리소스가 많아지게 된다. 하나의 state 파일에서 너무 많은 리소스를 관리하지 않기를 권장하고 있지만, 너무 나누면 관리가 어려운 부분도 있고 성격상 어쩔 수 없이 많은 리소스가 생기게 되는 때도 있다. 이렇게 관리 리소스가 많았지만 plan이나 apply를 할 때 Terraform이 tfstate에 기록해 둔 내용과 AWS 같은 클라우드에 실제 구성을 비교해봐야 하므로 꽤 많은 시간이 걸리게 된다.

기본적으로 planapply를 하면 tfstate 파일과 실제 상태를 비교하는 작업을 항상 진행하므로 아무리 간단한 수정을 하더라도 시간이 꽤 많이 걸리게 되고 항상 terraform plan을 한 뒤 terraform apply를 하게 되는데 양쪽 모두에서 이 작업을 하게 되므로 시간은 더욱 많이 걸리게 된다.

terraform plan -out=path

다음은 AWS Route53에 CNAME을 하나 추가하고 plan을 실행한 결과이다.

$ terraform plan
Acquiring state lock. This may take a few moments...
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.terraform_remote_state.vpc: Refreshing state...
data.terraform_remote_state.us_east_1: Refreshing state...
data.terraform_remote_state.ecs_services: Refreshing state...
aws_iam_user.apex-basic: Refreshing state... (ID: apex-basic)
aws_dynamodb_table.terraform-lock: Refreshing state... (ID: SideEffectTerraformStateLock)
aws_cloudfront_origin_access_identity.labs_sideeffect_kr: Refreshing state... (ID: E2WUVSG763JX3V)
data.aws_elb_service_account.main: Refreshing state...
data.aws_acm_certificate.sideeffect_kr: Refreshing state...
data.aws_iam_policy_document.ecs_service_role: Refreshing state...
data.aws_iam_policy_document.config_service: Refreshing state...
aws_s3_bucket.nodejs-ko: Refreshing state... (ID: nodejs-ko)
aws_route53_zone.sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA)
aws_iam_group.apex: Refreshing state... (ID: apex)
data.aws_iam_policy_document.ecs_instance_role: Refreshing state...
aws_cloudwatch_log_group.ap_northeast_1_ecs: Refreshing state... (ID: ap-northeast-1-ecs)
data.aws_iam_policy_document.nodejs-ko-twitter_lambda_logs: Refreshing state...
data.aws_iam_policy_document.apex-default: Refreshing state...
aws_iam_user.outsider: Refreshing state... (ID: outsider)
aws_cloudfront_origin_access_identity.nodejs_sideeffect_kr: Refreshing state... (ID: E25WJ3C3LDATWV)
data.aws_iam_policy_document.nodejs-ko-twitter_lambda_function: Refreshing state...
data.aws_iam_policy_document.vault_ecs_task: Refreshing state...
data.aws_iam_policy_document.aws_s3_bucket_logs: Refreshing state...
aws_iam_role.config_service: Refreshing state... (ID: config-service)
aws_iam_role.ecs_service_role: Refreshing state... (ID: ecsServiceRole)
aws_iam_policy.nodejs-ko-twitter_lambda_logs: Refreshing state... (ID: arn:aws:iam::410655858509:policy/nodejs-ko-twitter_lambda_logs)
aws_iam_role.ecs_instance_role: Refreshing state... (ID: ecsInstanceRole)
data.aws_iam_policy_document.labs_sideeffect_kr: Refreshing state...
aws_iam_policy.apex-default: Refreshing state... (ID: arn:aws:iam::410655858509:policy/apex-default)
aws_iam_group_membership.apex: Refreshing state... (ID: apex-group-membership)
aws_iam_role.nodejs-ko-twitter_lambda_function: Refreshing state... (ID: nodejs-ko-twitter_lambda_function)
aws_iam_role.vault_ecs_task: Refreshing state... (ID: vault-ecs-task)
aws_s3_bucket.logs: Refreshing state... (ID: kr.sideeffect.logs)
aws_config_configuration_recorder.ap_northeast_1: Refreshing state... (ID: aws-config-ap-northeast-1)
aws_iam_policy_attachment.AWSConfigRole-policy-attachment: Refreshing state... (ID: AWSConfigRole-policy-attachment)
aws_iam_policy_attachment.AdministratorAccess-policy-attachment: Refreshing state... (ID: AdministratorAccess-policy-attachment)
aws_iam_policy_attachment.IAMFullAccess-policy-attachment: Refreshing state... (ID: IAMFullAccess-policy-attachment)
aws_iam_policy_attachment.AWSLambdaFullAccess-policy-attachment: Refreshing state... (ID: AWSLambdaFullAccess-policy-attachment)
data.aws_iam_policy_document.nodejs_sideeffect_kr: Refreshing state...
aws_s3_bucket.labs_sideeffect_kr: Refreshing state... (ID: kr.sideeffect.labs)
aws_route53_record.vault_sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_vault.sideeffect.kr_A)
aws_route53_record.www_sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_www.sideeffect.kr_A)
aws_iam_policy_attachment.ecs_service_role: Refreshing state... (ID: AmazonEC2ContainerServiceforEC2Role)
aws_iam_policy_attachment.ecs_instance_role: Refreshing state... (ID: AmazonEC2ContainerServiceforEC2Role)
aws_iam_instance_profile.ecs_instance_role: Refreshing state... (ID: ecsInstanceRole)
aws_iam_policy_attachment.AmazonS3FullAccess-policy-attachment: Refreshing state... (ID: AmazonS3FullAccess-policy-attachment)
aws_config_config_rule.ap_northeast_1_cloudtrail_enabled: Refreshing state... (ID: cloudtrail-enabled)
aws_config_config_rule.ap_northeast_1_ec2_instances_in_vpc: Refreshing state... (ID: ec2-instances-in-vpc)
aws_s3_bucket.nodejs_sideeffect_kr: Refreshing state... (ID: kr.sideeffect.nodejs)
aws_iam_policy_attachment.apex-default-policy-attachment: Refreshing state... (ID: apex-default-policy-attachment)
aws_iam_policy_attachment.nodejs-ko-twitter_lambda_logs-policy-attachment: Refreshing state... (ID: nodejs-ko-twitter_lambda_logs-policy-attachment)
aws_route53_record.sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_sideeffect.kr_A)
aws_s3_bucket.terraform-state: Refreshing state... (ID: kr.sideeffect.terraform.state)
aws_cloudtrail.ap_northeast_1: Refreshing state... (ID: ap-northeast-1)
aws_config_delivery_channel.ap_northeast_1: Refreshing state... (ID: aws-config-ap-northeast-1)
aws_s3_bucket.vault_sideeffect_kr: Refreshing state... (ID: kr.sideeffect.vault)
data.aws_iam_policy_document.config_service_delivery_permission: Refreshing state...
aws_iam_policy.config_service_delivery_permission: Refreshing state... (ID: arn:aws:iam::410655858509:policy/config-service-delivery-permission)
aws_config_configuration_recorder_status.ap_northeast_1: Refreshing state... (ID: aws-config-ap-northeast-1)
aws_cloudfront_distribution.labs_sideeffect_kr: Refreshing state... (ID: E3EP48YZYH5MDL)
aws_iam_policy_attachment.config_service_delivery_permission_attachment: Refreshing state... (ID: config-service-delivery-permission-attachment)
aws_cloudfront_distribution.nodejs_sideeffect_kr: Refreshing state... (ID: E3AG1R3KSY57S4)
aws_route53_record.labs_sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_labs.sideeffect.kr_A)
aws_route53_record.nodejs_sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_nodejs.sideeffect.kr_A)
data.aws_iam_policy_document.vault: Refreshing state...
aws_iam_policy.vault: Refreshing state... (ID: arn:aws:iam::410655858509:policy/vault-to-write-s3)
aws_iam_policy_attachment.vault-policy-attachment: Refreshing state... (ID: vault-policy-attachment)
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ aws_route53_record.demo_sideeffect_kr
    fqdn:               "<computed>"
    name:               "demo.sideeffect.kr"
    records.#:          "1"
    records.3313064457: "blog.outsider.ne.kr"
    type:               "CNAME"
    zone_id:            "Z12CBQ5AHVFFYA"


Plan: 1 to add, 0 to change, 0 to destroy.
Releasing state lock. This may take a few moments...

좀 길지만, 전체 출력을 모두 적었다. Route53에 CNAME을 생성한 것이지만 tfstate에 있는 모든 리소스를 다 검사한다. 상단에 나오는 출력 부분은 모두 이 부분의 상태를 검사하는 부분이고 최종적으로 CNAME을 1개 추가한다고 알려준다.

$ terraform apply
... 중략
aws_cloudfront_distribution.labs_sideeffect_kr: Refreshing state... (ID: E3EP48YZYH5MDL)
aws_config_configuration_recorder_status.ap_northeast_1: Refreshing state... (ID: aws-config-ap-northeast-1)
aws_cloudfront_distribution.nodejs_sideeffect_kr: Refreshing state... (ID: E3AG1R3KSY57S4)
aws_iam_policy_attachment.config_service_delivery_permission_attachment: Refreshing state... (ID: config-service-delivery-permission-attachment)
aws_route53_record.labs_sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_labs.sideeffect.kr_A)
aws_route53_record.nodejs_sideeffect_kr: Refreshing state... (ID: Z12CBQ5AHVFFYA_nodejs.sideeffect.kr_A)
data.aws_iam_policy_document.vault: Refreshing state...
aws_iam_policy.vault: Refreshing state... (ID: arn:aws:iam::410655858509:policy/vault-to-write-s3)
aws_iam_policy_attachment.vault-policy-attachment: Refreshing state... (ID: vault-policy-attachment)
aws_route53_record.demo_sideeffect_kr: Creating...
  fqdn:               "" => "<computed>"
  name:               "" => "demo.sideeffect.kr"
  records.#:          "" => "1"
  records.3313064457: "" => "blog.outsider.ne.kr"
  ttl:                "" => "5"
  type:               "" => "CNAME"
  zone_id:            "" => "Z12CBQ5AHVFFYA"
aws_route53_record.demo_sideeffect_kr: Still creating... (10s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (20s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (30s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (40s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (40s elapsed)
aws_route53_record.demo_sideeffect_kr: Creation complete (ID: Z12CBQ5AHVFFYA_demo.sideeffect.kr_CNAME)

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

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path:
Releasing state lock. This may take a few moments...

이를 적용하기 위해서 terraform apply를 실행하면 plan과 마찬가지로 다시 리소스를 모두 검사한다. 이는 planapply를 실행하는 사이에 변경된 부분이 있을 수 있으므로 다시 검사를 다 실행하는 것인데 이 리소스가 많아질수록 점점 고통스러운 시간이 되고 그 결과 실패한다면 수정하고 다시 해야 하므로 귀찮아지게 된다.

앞에서 plan을 할 때 출력 로그를 보면 다음과 같은 문구가 있다.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

그냥 넘어가기 쉬운데 plan할 때 -out 파리미터로 플랜을 저장하지 않아서 apply 할 때 똑같이 실행된다고 보장할 수 없다는 부분이다. 이는 앞에서 apply 할 때 재검사하는 것과 같은 이유인데 plan 한 뒤에 다른 변경사항이 생긴다면 적용할 때 다른 변경사항(혹은 다른 사람이 만든 결과를 삭제하는..)이 적용될 수도 있다.

terraform plan -out=planfile처럼 -out 파라미터로 플랜파일을 지정하면(이름은 원하는 대로 지정할 수 있다.)

$ terraform plan -out=planfile
// 중략
Path: planfile

+ aws_route53_record.demo_sideeffect_kr
    fqdn:               "<computed>"
    name:               "demo.sideeffect.kr"
    records.#:          "1"
    records.3313064457: "blog.outsider.ne.kr"
    ttl:                "5"
    type:               "CNAME"
    zone_id:            "Z12CBQ5AHVFFYA"


Plan: 1 to add, 0 to change, 0 to destroy.
Releasing state lock. This may take a few moments...

Path: planfile에서 보듯이 저장한 플랜파일을 알려주고 이 결과가 이 파일에 저장이 된다. 이를 적용할 때는 terraform apply planfile로 앞에서 만든 파일을 지정해 주면 된다.

$ terraform apply planfile
Acquiring state lock. This may take a few moments...
Releasing state lock. This may take a few moments...
Acquiring state lock. This may take a few moments...
aws_route53_record.demo_sideeffect_kr: Creating...
  fqdn:               "" => "<computed>"
  name:               "" => "demo.sideeffect.kr"
  records.#:          "" => "1"
  records.3313064457: "" => "blog.outsider.ne.kr"
  ttl:                "" => "5"
  type:               "" => "CNAME"
  zone_id:            "" => "Z12CBQ5AHVFFYA"
aws_route53_record.demo_sideeffect_kr: Still creating... (10s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (20s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (30s elapsed)
aws_route53_record.demo_sideeffect_kr: Still creating... (40s elapsed)
aws_route53_record.demo_sideeffect_kr: Creation complete (ID: Z12CBQ5AHVFFYA_demo.sideeffect.kr_CNAME)

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

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path:
Releasing state lock. This may take a few moments...

plan에서 검사한 결과를 저장했으므로 위처럼 planfile을 지정해서 apply 하면 다른 리소스를 재검사하지 않고 바로 plan 한 내용만 적용하게 되고 plan 한 부분을 적용함을 보장할 수 있다.

2017/09/09 21:36 2017/09/09 21:36

기술 뉴스 #85 : 17-09-01

웹개발 관련

  • Introduction to Preact — a smaller, faster React alternative : React와 API 호환성이 있으면서 용량이 3KB밖에 안되는 Preact를 소개하는 글이다. Preact를 사용할 때 React와 어떻게 다른지를 소개하고 preact-compat으로 완전히 React와 호환되게 할 방법을 소개한다. 이 글에서는 간단한 앱에서는 Preact가 괜찮고 복잡한 앱이라면 React를 권하고 있다.(영어)
  • Understanding V8’s Bytecode : V8이 자바스크립트 코드를 최적화할 때 생성하는 Bytecode에 대해서 설명하고 Chrome에서 Bytecode를 출력하는 방법과 각 코드가 어떤 의미인지를 간단히 설명한다. Bytecode를 분석해 볼 일은 없지만 참고삼아 알아두면 좋을 정보다.(영어)
  • Increase your web development skill-set: 150 animated tips on Chrome DevTools : 크롬 개발자 도구에 대해 Dev Tips를 안내하는 글이다. 오랫동안 구독해서 보고 있던 뉴스레터인데 이 사이트에 가면 이미 올려진 150개 이상의 개발자도구 팁에 대한 간단한 GIF 애니메이션을 볼 수 있다.(영어)
  • Inside a super fast CSS engine: Quantum CSS (aka Stylo) : Firefox의 차세대 엔진인 Quantum 프로젝트의 Quantum CSS(구 Stylo)가 CSS 처리를 어떻게 빠르게 하는지를 설명한 글이다. 앞부분은 웹브라우저가 CSS를 웹페이지에 어떻게 적용하는지를 설명하고 있어서 브라우저가 동작하는 방식을 이해할 수 있고 이 부분에서 Quantum CSS가 어떻게 속도를 최적화하는지를 설명하고 있다.(영어)
  • Headless mode : Firefox도 56 버전부터 headless 모드를 지원한다.(영어)

그 밖의 프로그래밍 관련

  • Kubernetes at GitHub : GitHub에서 수년간 운영하던 인프라를 Kubernetes로 교체하는 과정을 정리한 글이다. 개발팀이 더 유연하게 운영할 수 있도록 오케스트레이션 도구에서 Kubernetes를 도입하고 테스트를 위해 브랜치를 AWS에 배포해서 리뷰용도로 사용할 수 있도록 AWS 상에서 테스트를 진행하고 이를 github 서비스에 일부분씩 적용해서 전체를 무 상태 서버 전체를 Kubernetes 기반으로 동작하도록 적용했다.(영어)
  • APIs as infrastructure: future-proofing Stripe with versioning : 온라인 결제서비스를 제공하는 Stripe에서 API 버전을 어떻게 관리하고 있는지를 설명한 글이다. 보통 URL에 v1, v2처럼 버전을 붙이고 Stripe도 이를 사용하고 있지만, 하위호환성이 깨지는 문제를 막기 위해 변경이 있는 릴리스 날짜를 버전으로 사용하고 사용자가 처음 API를 사용하면 해당 날짜 버전에 고정되어 계속 같은 버전을 사용하고 변경사항을 자동 생성한 버전 변경 모듈을 이용해서 현재 API에서 사용자가 원하는 버전의 API의 호환성을 자동으로 맞춰주는 방식을 사용한다.(영어)
  • Android – 8.0 Oreo : 안드로이드 8.0 Oreo가 나왔다.(영어)

볼만한 링크

  • [카카오AI리포트]머신러닝 적용의 실제 : 머신러닝을 적용할 때 빠지기 쉬운 함정이나 실제로 고려해야 할 부분을 정리한 글이다. 머신러닝이나 딥러닝을 자세히 알지는 못하지만, 실제 업무에서 데이터를 분석할 때 왜 한 번만 분석하지 말고 계속 지켜봐야 하는지, 신선한 데이터가 왜 중요한지, 어떤 상황에서 데이터 편향이 일어날 수 있는지 잘 정리되어 있다. 실제 적용할 때 이 글을 보고 찬찬히 고민해 볼 여지가 많이 있어 보인다.(한국어)
  • 대기업 Aaron과 실리콘밸리 Bryan : 두 명의 가상 개발자가 대기업과 스타트업으로 서로 이직을 하면서 Role-driven 조직과 Rank-driven 조직에서 최고의 인재가 어떻게 최악의 인재가 되는지를 비교해 준 글이다. 예시가 재미있고 조직 문화와 개개인의 특성이 맞아야 업무 효율도 난다고 생각하는 편이라 공감하면서 읽었다.(한국어)
  • 코드로 100명 이상의 네임택 한 번에 디자인하기 : Design Spectrum 행사를 준비하면서 100명 참가자의 네임택을 만들기 위해서 Sketch의 JavaScript API로 디자인한 네임택에 이름을 넣어 100개를 만들고 A4마다 나눠서 출력한 과정을 담고 있다. Skectch에 JavaScript API가 생긴 줄도 모르고 있었던지라 다음에 필요한 일이 생기면 비슷하게 해보고 싶다.(한국어)

IT 업계 뉴스

  • 우버 새 CEO에 '다라 코스로샤히' 익스피디아 CEO : Uber의 새 CEO로 익스피디아의 CEO인 다라 코스로샤히로 결정되었다.(한국어)
  • Node.js has forked into Ayo : Node.js가 Ayo란 프로젝트로 포크되었다. 국내 기사는 제대로 정리가 안 되어 있는 거 같아서 영문을 링크했는데 이 사건의 내용을 완전히 파악하기가 좀 쉽지는 않은데 현재 TSC를 이끄는 Rod Vagg가 Node.js의 Code of Conduct를 어겼다고 불만이 제기되어 Rod를 TSC에서 제외하는 안건이 CTC에 올라갔지만 6:4로 부결되었다. 이에 반대한 4명은 Node.js에서 빠지기로 하고(참고 글 1, 2) 아마 이들을 중신으로 Ayo가 진행되는 것으로 예상한다. 이 이슈에 대한 Rod의 입장이 장문의 글로 올라왔고 아직도 진행 중인 상황이다. 개인적으로는 이전 io.js 사건 만큼 심각하게 보고 있지는 않고 현재 CTC나 TSC에서 문제를 잘 해결할 거라고 기대하고 있다.(영어)

프로젝트

  • Puppeteer : Headless Chrome을 제어할 수 있는 Node.js 라이브러리.
  • OpenFaaS : Docker와 Kubernetes로 Functions as a Service를 사용할 수 있는 서버리스 프레임워크.
  • Wekan : Node.js로 작성된 Trello같은 오픈소스 칸반.
  • rendertron : Headless Chrome으로 웹사이트를 렌더링하거나 스크린샷을 찍을 수 있는 솔루션.
  • Feather : 오픈소스 아이콘 패키지.
  • Workbox : PWA의 오프라인 캐싱을 도와주는 JavaScript 라이브러리.
  • Saucs : 사용하는 제품이나 벤더를 구독하면 취약점을 알려주는 서비스.

버전 업데이트

2017/09/01 04:55 2017/09/01 04:55