AWS CDK에서 Terraform으로
안녕하세요. 인프랩 DevOps Engineer 선비입니다!
인프랩에 합류한 후 매일 다양하고 즐거운 경험을 하고 있습니다. 오늘은 그 중에서도 코드형 인프라(Infrastructure as Code, IaC)와 관련된 이야기를 해보려고 합니다.
작년 11월까지만 해도 IaC 도구로 AWS CDK를 사용하고 있었는데요, 그 당시 저를 힘들게 했던 고민들과 지금은 어째서 Terraform을 사용하게 되었는지 소개해드리겠습니다.
코드형 인프라
저희는 사용자에게 좋은 품질의 서비스를 안정적으로 제공하기 위해서 인프라를 구성합니다. 빠른 변화에 대응하기 쉽도록 클라우드 환경에서 인프라를 구성하고 있습니다.
많은 클라우드 서비스 공급자는 고객이 인프라 구성을 빠르고 간편하게 할 수 있도록 웹 콘솔을 지원하고 있는데요, 아마도 소규모 조직이나 클라우드 서비스를 이제 갓 도입한 조직의 경우 대부분의 인프라를 웹 콘솔을 통해서 구성했을 것입니다.
하지만 어느 정도 조직과 서비스의 규모가 커지면, 더 이상 웹 콘솔만으로는 정교하게 인프라를 구성하기 어려워집니다. 이러한 경우 도입을 고려해볼 수 있는 것이 코드로 인프라를 구성할 수 있는 IaC 입니다.
IaC를 사용하면 인프라 구성 단계에서 조건이나 반복을 처리하는 것이 쉬워지고 버전 관리, 테스트, CI/CD 등 기존에 서비스 개발에 사용했던 기술을 유사하게 사용할 수 있기 때문에 휴먼 에러의 방지와 동시다발적 협업에 유리해집니다.
저희는 클라우드 서비스로는 AWS만을 사용하고 있었고, 서비스 개발에 TypeScript를 사용 중이었기 때문에 IaC 도구로 AWS CDK를 채택하여 사용하는 중이었습니다.
AWS CDK와 멀어진 이유
업무를 진행하면서 사용할 도구를 선택할 때에는 다양한 조건을 검토하게 됩니다.
그 중에는 “기술적으로 얼마나 진보한 도구인가?” 또는 “누가 그 도구를 만들었는가?” 등의 조건도 있겠지만 결국 선택될 것은 ”지금, 이 상황에 가장 도움이 되는 도구“일 것입니다.
저희가 IaC 도구에게 바랐던 점은 다음과 같습니다.
- 이미 많은 사람들이 사용 중이며 따라서 많은 정보가 인터넷에 공개되어 있을 것.
- 직관적이고 간단하여 응용 및 확장에 용이할 것.
- 예외 상황이 발생하더라도 유연하게 안정성을 복구할 수 있을 것.
AWS CDK는 이러한 조건을 충분히 만족시켜주지 못했습니다.
문서와 커뮤니티
AWS CDK Reference Documentation
AWS CDK의 공식 문서는 아직 많이 부족한 상태입니다.
코드를 작성하면서 필요로 하는 정보를 찾는 것이 어려웠습니다. 간혹 AWS DevOps 블로그와 같은 곳에서 좋은 글을 찾기도 했지만 고민과 검색에 시간을 많이 쓰고도 정확한 문서나 사례를 발견하지 못해 결론을 내리지 못하는 경우가 많았습니다.
특히 이를 Terraform의 공식 문서와 비교하면 품질이 다소 차이가 나는 것을 알 수 있습니다. Terraform의 경우 Intro 문서에 Terraform을 처음 접하는 사람이 알고 싶어할 정보를 잘 적어두었으며, Get Started에는 따라하기만 하면 되는 사용자 친화적인 튜토리얼이 정리되어 있었습니다.
서적 역시 Terraform의 경우 Terraform Up & Running 이라는 서적이 2판 한국어 번역본까지 나온 것에 비해서 AWS CDK를 소개하는 서적은 아직까지 찾을 수 없었습니다.
AWS CDK는 사용하는 사람이 많지 않아서 커뮤니티로부터도 원하는 정보를 얻는 것이 쉽지 않았습니다. 두 도구에 대한 사람들의 관심과 사용 여부를 정확하게 측정하는 것은 불가능하지만 2021년 11월 기준으로 공개된 정보를 대략 세어보자면 다음과 같습니다.
- Terraform의 GitHub Repository 스타 수는 30k
- Terraform AWS Provider의 GitHub Repository 스타 수는 6.5k
- StackOverFlow 검색 결과 수는 26,929개
- 2016년부터 현재까지 Google 검색 지수에서 AWS CDK와 Pulumi를 지속적으로 크게 앞서고 있음
그에 비하면 AWS CDK의 수치는 비교적 낮은 편입니다.
- GitHub Repository 스타 수는 7.8k
- StackOverFlow 검색 결과 수는 8,558개
Terraform이 2014년에 출시했고 AWS CDK가 2018년에 출시한 것을 고려하면 AWS CDK가 폭발적으로 성장하고 있는 것은 분명하지만, 적어도 현재 커뮤니티 크기는 Terraform이 훨씬 크다고 할 수 있습니다.
현재 인프랩에는 DevOps 엔지니어의 수가 적기 때문에 문서가 잘 정리되어 있고 커뮤니티가 크다는 Terraform의 장점은 AWS CDK를 Terraform으로 대체하기로 결정하기에 충분한 이유였습니다.
진입 장벽
Terraform은 HCL(Hashicorp Configuration Language) 이라는 언어로 코드를 작성해야 합니다.
AWS CDK는 TypeScript, Python 등 잘 알려진 프로그래밍 언어를 이용하여 코드를 작성할 수 있습니다.
다만 AWS CDK를 Python으로 사용해보고자 했던 적이 있는데, Javascript 및 TypeScript를 제외한 다른 언어의 AWS CDK 라이브러리는 각 언어의 특성을 온전히 반영하여 포팅된 라이브러리는 아니고 AWS가 자체 개발한 JSII 기반의 호환 라이브러리여서 클래스 상속을 어노테이션으로 해야 하는 등 어색한 부분이 존재하는 상태였습니다. 따라서 AWS CDK를 사용한다면 TypeScript를 언어로 사용하는 것을 추천하고 싶습니다.
도메인 특화 언어인 HCL은 따로 학습을 해야 하는데 반해 TypeScript는 유사한 언어를 이미 알고 있다면 빠르게 사용할 수 있으므로 AWS CDK가 Terraform에 비해 진입 장벽이 낮다고 생각할 수 있습니다.
그러나 이는 정말로 출발만을 고려하였을 때 그런 것이고, 4단계 운영 성숙도에 도달하기까지를 생각하면 오히려 Terraform이 AWS CDK보다 진입 장벽이 낮다고 볼 수 있습니다.
HCL은 문법이나 규칙 등이 적고 응용하기에 매우 간단한 언어입니다. Hashicorp가 공식적으로 공개한 튜토리얼 및 예제 뿐만 아니라 많은 사용자들이 진행하고 있는 Terraform 기반의 프로젝트를 인터넷에서 참고할 수 있으므로 좋은 Terraform IaC 구조는 따라하기 쉽습니다.
AWS CDK의 경우 TypeScript로 작성할 수 있다는 점이 초기에는 익숙하게 작업이 가능하다는 장점이 되지만 온전한 구조를 만들고 협업하는 단계까지 도달하는 데에는 단점이 됩니다.
HCL은 정의형이며 복잡한 로직을 받아들이지 않으므로 온전히 인프라 설계와 구조 정립에 몰입할 수 있으나 TypeScript AWS CDK로 인프라를 구성하는 것은 인프라 구조와 TypeScript 프로젝트 구조를 모두 고려해야 하므로 많은 고민과 시행착오를 필요로 합니다.
또한 Terraform은 많은 경우에 코드와 현실의 리소스가 1대1 대응 관계가 되어 코드만 보더라도 현실의 인프라가 어떻게 구성되어 있는지 투명하게 파악할 수 있습니다.
AWS CDK는 편의를 위해 IAM 정책 등의 일부 리소스는 따로 정의해주지 않더라도 알아서 구성해줍니다. 이것이 빠르게 인프라를 구성해야 하는 경우에는 장점으로 작용할 수가 있지만, 결국 코드만 보아서는 현실의 인프라가 정확히 어떻게 구성되어 있는지 파악할 수 없으므로 시간이 갈수록 단점이 됩니다.
따라서 AWS CDK는 Terraform과 달리 사용 사례가 적고 복잡한 프로젝트 관리를 필요로 하며, 자동으로 생성해주는 리소스가 불명확하므로 계속 사용하기는 힘들 것으로 판단했습니다.
IaC와 현실 사이의 간극
Terraform의 경우 아래와 같이 직접 현실의 인프라 상태를 조회하고, IaC 코드와 차이를 파악하여 현실의 인프라 구성을 변경하는 방식으로 인프라를 배포합니다.
...
aws_route53_zone.this: Refreshing state... [id=SECRET]
aws_vpc.this: Refreshing state... [id=SECRET]
aws_internet_gateway.this[0]: Refreshing state... [id=SECRET]
aws_subnet.private["0"]: Refreshing state... [id=SECRET]
aws_subnet.private["1"]: Refreshing state... [id=SECRET]
aws_subnet.public["1"]: Refreshing state... [id=SECRET]
aws_subnet.public["0"]: Refreshing state... [id=SECRET]
...
# aws_vpc.this has changed
~ resource "aws_vpc" "this" {
...
AWS CDK는 방식이 Terraform과 전반적으로 유사하지만 중요한 차이점이 하나 있습니다. 인프라 상태를 조회하고 구성을 변경하는 일을 CDK가 직접 하는 것이 아니라 CloudFormation이 한다는 점입니다.
즉, AWS CDK는 인프라 배포가 필요한 경우 CDK 코드를 CloudFormation Template으로 변환하여 CloudFormation Stack으로 배포를 하게 되며, 현실의 인프라는 이 CloudFormation Stack에 의해 변화하게 되는 것입니다.
이러한 구조는 얼핏 보기에는 별 문제가 없어 보이지만, 사실은 중요한 두 가지 문제를 야기합니다.
CDK에서 현실 추적 불가능
첫 번째는 CDK를 거치지 않은 현실의 인프라 변경이 CDK에 반영되기가 어렵다는 점입니다.
우선 CDK에서 인프라 변화를 조회하는 diff 명령을 수행하면 CDK 코드와 현재 배포되어 있는 CloudFormation Template과의 차이점을 사용자에게 표시합니다.
중요한 점은, 이 과정에서 CDK는 현실 인프라의 상태를 조회하지 않는다는 것입니다.
현실의 인프라 변경을 알아차리는 방법은 CloudFormation Stack에서 Draft Detection을 수행하는 방법 뿐입니다. 매번 CDK 인프라 배포를 수행할 때마다 Draft Detection을 수행하는 것은 어려울 것입니다.
또한 이마저도 찾아낸 Drift를 CloudFormation Template이나 CDK 코드로 반영하는 방법은 지원하지 않으며 현실의 인프라를 변경 전으로 복원하는 방식으로 Drift를 제거하여 해결하는 방법만 지원합니다.
모든 인프라 변경을 CDK를 통해서 하도록 강제하는 것도 생각해볼 수 있지만 이러한 제약은 예외 상황 발생 시 지켜지지 않을 가능성이 높으며 Drift에는 사람이 변경한 것뿐만 아니라 AMI 최신 이미지 ID 등 AWS가 정책상 일으킨 변화나 RDS DB 엔진 마이너 업데이트 등 자동으로 일어난 변화도 존재하므로 Drift를 완벽히 차단하는 것은 사실상 불가능합니다.
이러한 CDK의 특징은 인프라 관리를 정말 어렵게 합니다.
CDK의 diff 결과를 신뢰할 수 없음
두 번째 문제는 CDK가 알지 못하는 CloudFormation 동작이 발생할 수 있다는 점입니다.
이는 첫 번째 문제와 연결되는데, 다음과 같은 상황을 가정해봅시다.
- CDK를 통해 리소스를 하나 만들었는데, 어떤 인자 값이 1 이었습니다. (이 인자 값이 커지는 인프라 변경은 리소스 재생성 없이 in-place로 동작하지만, 인자 값이 작아지려면 리소스 재생성을 해야 합니다. RDS의 Engine Version 처럼요.)
- 시간이 지나는 동안 CDK를 통하지 않은 인프라 변경이 발생하여 해당 리소스의 인자 값이 3이 되었습니다.
- 2번의 변화를 알지 못하는 어떤 사람이 CDK를 통해 해당 리소스의 인자 값을 2로 변경하려고 합니다.
- CDK에서는 CloudFormation Template과 코드의 차이를 보고하므로 1에서 2가 되는 정상적인 in-place update로 표시합니다. 인프라 배포를 진행합니다.
- CloudFormation은 변경된 Template을 처리하기 위해 리소스 업데이트를 하려고 합니다.
- 현실을 보니 인자 값이 3이었고 변경할 값은 2이므로 리소스를 삭제하고 다시 생성합니다.
위와 같이 CDK에서 의도하지 않은 변경이 CloudFormation 단에서 벌어질 수가 있습니다. 물론 항상 CloudFormation이 무조건 Replace를 진행하는 것도 아니지만, CDK가 경고하지 않은 변경사항이 외부 요인과 CloudFormation의 판단에 따라 실행될 수 있음은 분명합니다.
인프라 관리자에게 위와 같은 일이 일어날 수 있다는 가능성은 엄청난 불안감을 안겨주게 됩니다.
더욱이 이러한 상황에 대한 리소스 또는 인자 별 정의 문서와 같은 것이 없고 어떤 때에는 CloudFormation이 Replace를 막아주는데 또 어떤 때에는 묻지도 따지지도 않고 그냥 리소스를 지우고 다시 만드는 등… 일관성도 찾기 어려워서 더 곤란하곤 했습니다.
다양한 경우에 따른 CloudFormation의 행동을 일일히 기록하는 방법도 있었겠지만 그보다 이런 문제가 처음부터 없는 도구를 사용하기로 하는 것이 훨씬 합리적이라고 생각했습니다.
따라서 IaC와 현실 사이에 CloudFormation이 끼어 있다는 사실로 인해 AWS CDK를 계속 사용하는 것이 어렵겠다는 판단에 도달했습니다.
마무리
지금까지 인프랩이 AWS CDK에서 Terraform으로 오기까지 어떤 과정을 거쳤는지 살펴보았습니다.
현재는 위에서 기술한 많은 고민이 사라진 상태로 행복한 인프라 코딩을 하고 있습니다.
또한 이 글을 처음 작성했던 때보다 더 깊이 Terraform에 대해 알아보고 지금은 Terragrunt 및 Terratest 기반의 모듈화와 자동 테스팅까지 도입하였는데 이는 다음에 별도의 글로 소개해드리도록 하겠습니다.
감사합니다.