[번역] Terraform을 활용한 AWS 람다 배포

Sean Soowan Lee
16 min readMar 17, 2018

--

새로운 시스템을 설계하면서 메인 인프라스트럭쳐로 AWS Lambda를 사용하고 Terraform을 이용해 provision하고 있습니다. 이에 관해 블로그를 작성하려다가 좋은 글을 발견해서 번역을 해보았습니다.

원문: https://medium.com/build-acl/aws-lambda-deployment-with-terraform-24d36cc86533

최근에 Hacker News에서 나온 흥미로운 질문이 있었습니다.

저는 다른 사람들의 경험은 어땠는지 알고 싶었습니다. ACL에는 우리가 많이 의존하는 다수의 Lambda 함수가 있으며, 우리는 이를 Terraform을 사용하여 배포합니다. 우리에겐 이러한 배포에 문제가 없었고, 그래서 저는 다른 사람들이 Lambda에 어떻게 접근했는지 궁금했습니다. 그런데 다른 사람들은 어려움을 겪고 있는 것처럼 보였습니다.

그러나 Terraform 모듈은 그것을 추상화할 수 있습니다.

그러나 Terraform은 필요한 변경 사항만 업데이트합니다.

그러나 Terraform을 사용한다면 다른 인프라스트럭쳐를 개발하는 것과 같을 것입니다.

그러나 이미 Terraform을 활용하여 여러 가지 환경을 지원할 수 있습니다.

간략하게 말하자면, 저는 왜 사람들이 그저 Terraform을 사용하지 않는지 궁금합니다. 이것이 이 글을 쓰는 이유입니다. 저는 Terraform의 능력을 최대한 활용하여 Hacker News에서 제기된 모든 우려를 극복할 수 있는 방법을 공유하고 싶습니다.

자, 시작합시다.

속지 마세요

Terraform을 사용하여 Lambda를 배포하는 데 필요한 모든 것이 아래와 같다고 생각할 수 있습니다.

  1. JavaScript 파일을 생성합니다.
  2. 해당 JavaScript 파일을 참조하는 Terraform 구성 파일을 만듭니다.
  3. Terraform을 적용합니다.
  4. 성공했습니다!

틀렸습니다.

이 네 단계는 기술적으로 람다를 배포합니다. 그러나 이러한 표준에 따라 서버에 SSH로 접속하고 여기에 소스 코드를 복사하여 붙여넣으면 “배포를 지속”할 수도 있습니다.

AWS Lambda 자원을 생성하는 것만으로는 충분하지 않습니다. 여기 더 많은 것들이 있습니다.

AWS Lambda에 관한 오해

당신이 이전에 AWS Lambda 관련 발표를 본 적이 있다면, “단순히 함수”이기 때문에 발표자가 Lambda 중심에서 필요한 세부 작업을 빠르게 얼버무렸다는 점을 알게 되었을 것입니다. 그것은 불행히도 람다와 그 근본을 이루는 아키텍처를 총체적으로 간략화합니다. 단순히 코드 베이스가 작기 때문에 그 코드 베이스가 다른 코드 베이스와 같은 노력을 해야 하지 않는다는 것을 의미하지는 않습니다.

작은 폭탄 → 그러나 큰 폭발력

AWS Lambda 함수의 필수 구성 요소

저는 Lambda 함수를 사용하더라도 여전히 다음과 같은 것들을 필요로 한다고 주장합니다.

  1. 테스트 — 논리를 검증하고 회귀를 방지하려면 단위 테스트 및 통합 테스트가 필요합니다.
  2. 3rd-party 패키지 — 아주 기본적인 것을 구축하는 것이 아닌 한, 도움이 되는 3rd-party 라이브러리가 필요할 수 있습니다. 예를 들어 내부적으로 CIDR 블록을 파싱하는 Lambda 함수가 있습니다. CIDR 블록을 파싱하는 로직을 직접 작성해야 합니까? 아니면 ipaddress.js를 사용해야 합니까?
  3. 모듈 번들러 — 이제 테스트 및 런타임을 위한 3rd-party 패키지가 있으므로 프로덕션을 위해 코드베이스를 패키지화하기 위한 도움이 필요합니다. Node.js에서는 Gulp 또는 Webpack과 같은 모듈 번들러가 필요합니다.
  4. AWS 리소스 지원 — Lambda 함수가 독립적으로 작동하는 일은 드뭅니다. 최소한 IAM 역할 및 IAM 정책이 필요합니다. 종종 KMS, CloudWatch Logs 등과 같은 지원 리소스가 필요할 것입니다.
  5. 다중 환경 — 프로덕션에 배포하기 전에 통합 및 스테이징에서 코드를 검증할 수 있습니다. 또한, 신속한 개발 및 테스트를 수행할 수 있는 충분한 로컬 개발 환경이 필요합니다.
  6. 모니터링 — Lambda 함수가 실패하거나 실행 시간을 초과하였는지 상태를 모니터링할 수 있습니다.

Lambda를 프로덕션에 적용하기 전에 이러한 모든 문제에 대한 메커니즘을 가지고 있어야 합니다. 다행히도 이 부분에서 Terraform이 강점이 있습니다.

이제 각 지점을 분해하고 Terraform을 사용하여 어떻게 그들을 다룰 수 있는지 보겠습니다.

개발 및 테스트

당신의 람다 함수가 테스트와 개발이 쉬운지 확인하십시오.

개발 환경과 테스트 환경을 갖추는 것이 Terraform과 엄격하게 관련되어 있지는 않지만 어쨌든 이점을 언급할 필요가 있습니다. 왜일까요? 이들 두 환경이 개발자들이 배포하기 전에 그들의 코드를 신뢰하는 중심이기 때문입니다. 코드가 제대로 작동하는지 확신할 수 없다면 코드를 아직 배포하지 말아야 합니다! 신속하고 익숙한 개발 경험을 위해서는 이 두 가지 환경이 필요합니다.

내부적으로 우리는 이벤트와 관련된 보안을 위해 CloudTrail을 모니터링하는 Node.js 람다 함수를 사용합니다. 작은 코드 베이스에도 불구하고, 이 람다 함수는 39개의 테스트가 있고 JS linter를 통해 신속하고 고품질의 개발을 보장합니다. 이 람다 함수는 테스트 (Gulp, Mocha, Chai, Sinon) 및 런타임 (Lodash, ipaddrs.js) 에 널리 사용되는 3rd-party Node.js 패키지를 사용합니다. 이것이 람다의 친숙하지 않은 프로덕션 환경에도 불구하고 이미 친숙한 Node.js 개발 및 테스트의 경험을 유지할 방법입니다.

폴더 구조는 (주로) 전통적인 Node.js 응용 프로그램과 유사합니다.

이러한 결정들의 기반이 되는 핵심 철학은 우리의 애플리케이션 아키텍처를 AWS Lambda로 제한하지 않는 것입니다.

AWS Lambda는 당신의 애플리케이션에 유일한 시작점으로 보여야 합니다. 애플리케이션 로직은 AWS Lambda와는 구별되어야 하고 별도로 유지되어야 합니다.

이러한 원칙은 필요할 경우에 AWS Lambda를 떠날 수도 있는 유연성을 제공합니다. 또한, 우리가 개발한 어떤 다른 응용 프로그램과 마찬가지로 Lambda 함수를 고려할 수 있게 합니다.

람다는 우리 애플리케이션의 시작점일 뿐입니다.

위의 예시에서, 우리는 Lambda를 우리가 만든 객체를 즉시 호출하게 하고 우리 도메인을 대표하게 하는 것만으로 애플리케이션에서 추출하였습니다. 이 추출을 통해, 우리는 이제 다른 Node.js 응용 프로그램과 마찬가지로 Monitor 클래스에서 시작하여 응용 프로그램을 테스트할 수 있습니다.

이것이 우리가 개발 경험을 쾌적하게 유지하는 방법과 AWS Lambda에 고착되지 않도록 하는 방법입니다. 필요하다면 Express.js를 로직 앞에 두고 AWS의 EC2 Container Service (ECS) 와 같은 것을 사용하여 Docker container에서 실행할 수 있습니다.

AWS 리소스

람다 함수가 필요로 하는 모든 것을 쉽게 생성할 수 있는지 확인하십시오.

람다 함수를 배포하기 위해 장려되는 AWS의 메커니즘은 CloudFormation과 함께 그들의 SAM (Serverless Application Model) 사양을 사용하기 위한 것입니다. AWS SAM의 핵심은 다음과 같은 사양입니다.

… 서버리스 응용 프로그램에 대한 함수, API, 매핑 및 DynamoDB 테이블을 표현하는 방법을 단순화하는 AWS 자원 유형을 지원합니다.

그런데, 서버리스 애플리케이션은 종종 그러한 AWS 리소스 유형보다 훨씬 더 많은 것을 요구하므로, API 게이트웨이, DynamoDB 테이블 또는 람다 함수 자체만이 아닌 람다 함수가 필요로 하는 모든 것을 관리하고 배포하는 방법을 고려하는 것이 중요합니다.

SAM에서 지원

AWS Lambda, DynamoDB, API Gateway

SAM에서 비지원

IAM Role, IAM Policy, ACM Certificate for HTTPS API Gateway, Cloudwatch Events Scheduler, KMS, Route 53 records for CNAME 등.

실제로, 우리의 CloudTrail 보안 서비스 람다 함수에는 CloudTrail 트레일, CloudTrail 암호화 용 KMS 키, CloudTrail 용 S3 버킷, CloudWatch 로그 그룹 및 CloudWatch 로그와의 Lambda 통합이 필요합니다.

이러한 시나리오에서는 람다 함수 자체보다는 인프라스트럭처 전체를 관리하는 방법을 고려하는 것이 더 중요합니다. 다행히 Terraform은 이 부분에서 뛰어나고 필요한 모든 것을 즉시 제공합니다. 당신이 CloudFormation을 이미 사용하고 있다면 SAM이 잘 맞는 조합일 수 있습니다. 그러나 이미 Terraform을 사용 중이거나 Terraform쪽에 기울었다면 SAM에 흔들리지 않을 것입니다. 사용 초기부터 Terraform을 간단히 사용할 수 있으며 그것이 모든 Lambda의 요구사항을 충족시킬 만큼 유연하다는 것을 알 수 있습니다.

Terraform 자체에서 내가 직접 람다 함수에 필요한 IAM 역할을 쉽게 참조하는 방법을 보여주는 샘플 Terraform 코드.

다중 환경

여전히 당신의 람다 함수에 대해 분리된 환경이 필요합니다.

하나의 계정 내에서, 여러 환경에 대해 람다 함수를 분리하라는 AWS의 권장 사항은 ACL의 권장 사항이 아님을 알려드립니다.

왜 아마존의 나쁜 사례를 추천하려고 합니까?

실제로, AWS 계정으로 “논리적인 계정 분리”을 하는 것이 가장 좋은 방법입니다. 달리 말하자면, 당신의 환경에 맞는 별도의 AWS 계정이 있어야 합니다. 자세한 내용은 이곳을 참조하십시오. 따라서 그것은 람다 함수를 단일 계정이 아닌 그러한 해당 계정들에 배포해야 함을 의미합니다. 여기 다시 Terraform이 해결사로 나섭니다.

Terraform은 “workspaces” (이전에는 “environments”라고 했음) 의 개념을 지원합니다. 이를 통해 당신의 Terraform이 작동할 AWS 계정을 쉽게 변경할 수 있도록 합니다.

이 기능을 사용하면, 프로덕션에 적용하기 전에 람다 함수(필요한 모든 AWS 리소스 포함)을 통합 및 스테이징 계정에 쉽게 배포할 수 있습니다. 이로써 당신의 코드를 배포하는 데 훨씬 안전한 방법을 제공합니다. 또한, 인프라스트럭처가 가져야 할 작업 흐름에 훨씬 가깝습니다.

암호화

간단한 슬랙봇조차도 보안을 유지해야 하는 민감한 토큰을 가집니다.

Lambda는 암호화된 환경 변수를 지원합니다. 하지만 … 그것은 별로 의미가 없습니다. 다시 말해 보겠습니다. Lambda는 암호화 휴식 상태의 환경 변수를 즉시 사용할 수 있도록 지원합니다. 그것은 런타임에 환경 변수를 복호화하지 않습니다. 스스로 해야 합니다. 우리의 람다 함수에 암호가 있다면 무엇을 할 수 있을까요? 음, 여기 두 가지 옵션이 있습니다.

  1. KMS 암호화/복호화 — KMS를 사용하여 CipherBlobs로 암호화하고 이를 환경 변수로 전달하거나 코드 베이스에 파일로 저장할 수 있습니다. 그리고 런타임에 AWS SDK를 사용하여 이들을 복호화할 수 있습니다.
  2. 매개 변수 저장소 — EC2 SSM의 매개 변수 저장소에 암호를 저장하고 AWS SDK를 사용하여 런타임에 이들을 불러올 수 있습니다.

요즘에 저는, 이 둘 중에 더 단순한 옵션인 매개 변수 저장소를 살펴보는 것을 추천하게 됩니다. AWS의 콘솔 UI를 사용하여 쉽게 암호를 추가할 수 있습니다.

그런 다음 AWS SDK를 사용하여 쉽게 암호를 불러올 수 있습니다.

여기서, Terraform은 필수적인 KMS 구성이 요구될 경우 도움을 줄 수 있습니다. 예를 들어 사용자 정의 키를 만들려면 Terraform 안에서 정의할 수 있습니다. 만약에 매개 변수를 미리 가져와서 환경 변수를 통해 전달하려고 하면, 이 역시 쉽습니다. Terraform은 당신에게 유연성을 제공합니다.

Terraform은 매개 변수 저장소의 구성을 쉽게 가져올 수 있습니다.

안타깝게도, 암호들은 람다에 대한 자체적인 문제들을 가져옵니다. 복호화 호출에는 150–200ms의 오버헤드가 있습니다. (적어도 저의 벤치마크에서는 그렇습니다)

그렇다면 각각의 호출에 암호가 필요한 경우, 어떻게 우리의 람다 함수를 빠르게 유지할 수 있을까요? 이를 위해서는 AWS Lambda의 이면을 들여다봐야 합니다.

AWS Lambda의 이면

놀랍게도, 그것은 컨테이너의 풀입니다!

당신이 Lambda의 사용을 고려했다면, AWS Lambda 이 어떻게 작동하는지 알고 싶었을 것입니다. Lambda는 컨테이너 내에서 함수를 실행함으로써 작동합니다. 이것이 람다가 빠르게 돌고 보다 넓게 확장할 수 있는 비결입니다.

첫 번째 호출에서 Lambda는 애플리케이션 코드가 포함된 컨테이너를 돌리고 핸들러 함수(일명 콜드 호출)를 호출합니다. 그러나 두 번째 호출 (짧은 시간 내에 발생하는 경우)에서는 같은 컨테이너가 다시 사용됩니다. (예 : 호출) 그것이 재사용될 때, 코드 베이스와 전역 변수 또한 재사용됩니다. 이러한 재사용성은 설정 로직을 단지 한 번만 수행하여 코드 실행을 최적화할 수 있는 옵션을 제공합니다.

우리 Lambda 함수의 첫 번째 호출은 ~1800ms, 두 번째 호출은 ~9ms, 세 번째 및 그 이후의 호출은 1ms도 안 걸립니다.

호출간에 전역 변수를 재사용 할 수 있다는 것을 알면, 우리는 우리의 암호를 한번 해독할 수 있고 이후의 호출에서 재사용 할 수 있도록 전역 변수에 저장할 수 있음을 의미합니다. 이를 통해 최대 150ms의 복호화 시간을 절약할 수 있습니다. 또한 패키지를 로드하는 데 필요한 “부팅 시간”과 런타임을 극복하는 데 도움이 됩니다.

이제, 예시의 맥락에서 중요한 세부 사항을 설명해 보겠습니다. AWS Lambda를 사용하여 Slack 명령을 작성한 경우 3초 이내에 응답해야 함을 알게 됩니다. 그렇지 않으면 Slack이 이를 시간 초과 이벤트로 취급합니다. 우리의 경우, 드물게 회사 전반에 걸친 Slack 명령을 사용하여 처음으로 호출하는 데 2–3초가 걸리면 많은 직원이 시간 초과 이벤트를 받을 위험이 있습니다. 그렇다면첫번째 호출을 빠르게 하기 위해서는 무엇을 할 수 있을까요?

다시 한번, Terraform이 구조대로 다가옵니다.

람다 “워머”

람다를 따뜻한 상태로 유지하려면 지속적으로 호출해야 합니다. AWS에서 사용할 수 있는 간단한 트릭은 CloudWatch 의 이벤트 스케줄러를 사용하여 매분마다 람다 함수를 호출하여 따뜻한 상태로 유지하는 것입니다.

이 스니펫은 Terraform이 우리의 람다 함수를 타겟으로 (`aws_cloudwatch_event_target`) CloudWatch 이벤트 스케줄러에 의해 람다 함수가 호출되도록 허용하는 (`aws_lambda_permission`) CloudWatch 이벤트 스케줄러 (`aws_cloudwatch_event_rule`) 를 생성하는 것을 보여줍니다.

Terraform (또는 CloudFormation)이 없이는 이러한 옵션을 가질 수 없습니다.

모니터링

잘 동작합니까?

마지막으로, 어떤 방법으로 람다 함수의 상태를 모니터링하고 유지할 수 있을까요? 람다만의 고유한 옵션은 없지만 몇 가지 옵션을 사용할 수 있습니다. 다행히도 이러한 옵션 중 일부는 Terraform을 사용하여 구현하기 쉽습니다.

CloudWatch 메트릭스

Terraform을 사용하면, 람다 실패 호출 또는 시간 초과를 모니터링하고 모니터링 할 SNS 이벤트를 트리거하도록 하는 CloudWatch 메트릭스를 만들 수 있습니다. 이 전략은 람다 배포와 함께 단순히 더 많은 Terraform 코드를 생성하는 이점이 있습니다.

로깅

좀 더 인간 친화적인 방법으로 람다 로그를 읽으려면, awslogs라는 툴을 사용할 수 있습니다. 이것으로 CloudWatch Logs를 사용하는 UX를 확연하게 개선하고, 커맨드 라인에서 직접 사용하게 하는 부가적인 이점도 있습니다.

그러나 awslogs 에는 한계가 있습니다. 당신이 더욱 사용자 친화적인 툴을 원한다면, AWS ElasticSearch 클러스터를 만들고 CloudWatch Logs를 연결하여 처리할 수 ​​있습니다. 그런 다음 Kibana 인터페이스를 사용하여 로그를 빠르게 확인하고 기본적인 로그 분석을 수행할 수도 있습니다.

에러 처리

당신은 예외 관리를 위해 New Relic, Airbrake, Rollbar 등과 같은 써드 파티 툴을 계속 사용할 수 있습니다. 이 경우 다른 응용 프로그램과 마찬가지로 각 타사 패키지를 포함하고 이를 사용하도록 람다 함수를 구성하면 됩니다. 대안으로, 단순히 로그와 메트릭스 모니터링을 활용할 수도 있습니다.

성능 모니터링

Datadog 또는 New Relic 사용자인 경우 람다 함수의 성능을 모니터링하는 것이 아주 다르지는 않을 것입니다. 단지 한 번만 설정하면 됩니다. 그리고는? Terraform 자체를 통해 DatadogNew Relic 모니터링을 설정할 수도 있습니다.

결론

당신은 이제 Terraform을 사용하는 다른 모든 코드 베이스에 적용할 때와 같이, 람다 함수를 프로덕션에 배포하는 데 필요한 전체적인 그림을 가지고 있습니다.

제가 Terraform이 이미 람다 함수를 배포하는 데 필요한 모든 것을 갖추고 있으며, 다른 AWS 리소스와 고도로 통합된 기능을 구현할 수 있는 훌륭한 선택임을 보여 드렸기를 바랍니다. Terraform을 이미 사용하고있는 경우, 람다 기능을 배포하기 위해 별도의 프레임워크를 찾을 필요가 없습니다. 우리는 ACL에서 전체 인프라스트럭쳐를 Terraform에서 표준화 했습니다. 그래서 이것은 우리에게 자연스러운 단계였습니다.

이 자세한 포스팅을 통해 Lambda의 효과적인 배포를 위한 완벽하고 실용적인 접근법을 보여드렸기를 바랍니다. 개인적으로 AWS Lambda의 전체 개발 경험에서 사고방식의 리더십을 찾기가 쉽지 않음을 알았습니다. 사람들은 많은 세부 사항을 개별적으로 알아내고 있습니다. 바라건대 이 포스팅이 당신이 그 하위 작업의 일부를 스스로 하는 것을 방지하고, 그로 인해 생산성에 도움이 되었으면 합니다.

당신 자신의 경험이나 대안적인 기술도 저와 공유해 주시기 바랍니다. 진정 그것들을 알고 싶습니다!

--

--