티스토리 뷰

Cloud Functions

 

2019년 11월 GA 된 Cloud Run은 퍼블릭 클라우드에서 서버리스 애플리케이션 분야의 게임 체인저로 단숨에 자리 잡았습니다. Cloud Functions의 경우 multiple language runtime 개수도 부족해서 타사의 FaaS(Functions-as-a-Service)를 쫓아가기 바쁜 상황처럼 느껴졌는데요. 그러던 와중에 Cloud Run이 등장하게 됩니다. Cloud Run은 Cloud Functions 대비해서 인스턴스 크기가 커졌습니다. 즉, CPU, Memory를 더 풍성하게 사용할 수 있게 된 거죠. 그뿐만이 아니라 Cloud Run은 클릭 몇 번으로 트래픽을 버전별로 분할해서 처리할 수 있고 function instance에서 처리 가능한 요청 개수(maximum requests per container)를 조절할 수 있습니다. 여러 가지 특성을 비교해보면 Cloud Run은 Cloud Functions의 상위 호환입니다. 이런 Cloud Run의 인프라를 Cloud Functions 사용자들이 누릴 수 있도록 Cloud Functions 2nd generation 이 나왔습니다. 그리고 2022년 8월 3일 GA가 됐습니다.

Cloud Functions(2nd gen)은 구글의 차세대 FaaS입니다. Cloud Run과 Eventarc 기반으로 동작합니다! 그렇기 때문에 Cloud Functions 2nd-gen으로 생성하게 되면 Cloud Run 메뉴에도 서비스가 생성됩니다. 사실상 사용자는 Cloud Functions 메뉴로 Cloud Run 서비스를 이용하는 겁니다. 기존에 Cloud Functions으로 운영하던 애플리케이션도 손쉽게 넘길 수도 있겠습니다. 이번 글에서는 Cloud Functions 1st gen과 2nd gen의 차이점을 알아보도록 하겠습니다. ( * Cloud Run의 환경을 이야기할 때는 container instance라고 표현하고 Cloud Functions의 경우 function instance라고 합니다. 이 글에서는 이야기하는 Cloud Functions 2nd-gen은 Cloud Run 환경에서 동작하기 때문에 “container instance”라고 표현하는 게 정확하지만, 서비스의 이름을 따라 1st, 2nd 구분 없이 모두 function instance라고 표기하고 있으니 참고하세요 )


 

# 인스턴스 크기

1st gen의 경우 2 vCPU, 8GB까지 가능합니다.

 

2nd gen은 8 vCPU 32 GiB(preview)까지 가능합니다. 

 

preview를 제외하면 4 vCPU 16 GiB까지 지원됩니다. 재밌는 건 2nd는 Cloud Run 환경에서 동작하기 때문에 Cloud Functions 1st gen과 메모리 계산 방식에 차이가 있다는 겁니다. GB와 GiB의 차이가 되겠죠.

 

# Timeout

1st gen은 최대 9분 동안 실행됩니다. 2nd의 경우 Cloud Run과 마찬가지로 60분 동안 실행됩니다.

 

# cold start

2nd도  콜드 스타트를 피해 갈 수 없습니다. 1st gen와 비교해도 (이론상으로는) 큰 차이가 없습니다. 또한 둘 다 아래처럼 대기 인스턴스를 지정해둘 수 있습니다. 이때는 유휴 인스턴스에 대해 비용이 청구되기 때문에 응답 속도가 중요한 비즈니스의 경우에만 설정해주시면 됩니다.

 

Cloud Functions 2nd-gen으로 생성되면 “CPU is only allocated during request processing” 상태로 CPU를 할당하고 사용합니다. 즉, 요청이 없을 때는 CPU가 할당되지 않기 때문에 비용이 청구되지 않습니다. 

https://cloud.google.com/run/docs/configuring/cpu-allocation

 

2nd-gen는 Cloud Run 기반으로 동작하기 때문에 최소 인스턴스 개수가 지정되어 있지 않다면 유휴 상태가 15분 이상 지속되지 않습니다. 즉, 그 안에 function instance가 종료되고 이후 요청은 콜드 스타트가 됩니다. 1st-gen은 유휴 상태가 유지되는 시간이 공식적으로는 안내되어 있지는 않지만 2nd-gen과 유의미한 차이는 없습니다.

 

# concurrency

1st gen은 function instance 하나 당 하나의 요청만을 처리합니다. 즉, 요청이 하나 들어오면 한 개의 function instance가 구동되어 요청을 처리하죠. 순차적으로 두 개의 요청이 들어온다면 한 개의 function instance에서 두 개 모두 처리될 겁니다. 이때 첫 번째 요청은 cold start 되겠지만 두 번째 요청은 즉시 처리됩니다. 만약 두 개의 요청이 순차적으로 들어오는 게 아니라 동시에 들어온다면 이를 처리하기 위해 function instance 두 개가 실행됩니다. 다시 말해 concurrency가 1입니다. 아래는 Cloud Run에서 사용되는 이미지지만 Cloud Function에도 적용시킬 수 있습니다. 

 

한편 2st gen은 Cloud Run 기반이기 때문에 function instance 하나가 최대 1,000개의 요청을 처리합니다. 이 값은 CPU에 따라 달라집니다. 2nd gen로 생성된 경우 concurrency는 1로 설정됩니다. Cloud Functions의 기본 값을 따라가는 거죠. 이 설정을 변경하려면 Cloud Run 메뉴로 이동해서 수정하시면 됩니다. 서론에서 이야기한 것처럼 2nd gen으로 생성된 함수는 Cloud Functions와 Cloud Run 메뉴 양쪽에서 모두 보입니다. 다만, Cloud Functions 쪽에 없는 세부적인 설정을 위해서는 Cloud Run 메뉴로 이동해야 합니다.

Cloud Run 메뉴에서 생성하는 서비스의 "maximum requests per container” 기본 값은 80입니다. 이 값이  concurrency입니다.

 

아무튼, 한 개의 function instance가 여러 개의 처리를 할 수 있다는 건 상황에 따라서 굉장히 큰 장점입니다. 물론, 요청 한 개가 function instance의 리소스 대부분을 사용하는 경우라면 2nd gen이라도 “maximum requests per container”를 1 이상으로 설정해서는 안됩니다. 애플리케이션의 성격에 따라 유연하게 조절해서 사용해야 합니다.

 

# concurrency 테스트

Apache JMeter를 통해 트래픽을 생성하고 1st, 2nd gen의 concurrency를 확인해봅니다. 공식문서에도 테스트가 있지만 결과는 다를 겁니다. 트래픽을 발생시키는 컴퓨팅 파워에도 차이가 있고 function instance 크기도 다를 테니까요. 테스트에 사용된 스펙은 아래와 같습니다.

- 인스턴스 크기 : 2GB / 2 GiB(concurrency: 10)
- 스레드(유저) 개수 : 400
- 초당 호출 개수 : 3개
- Ramp-Up 시간 : 1초
- 트래픽 발송 위치 : Republic of Korea

즉, 400명의 사용자가 초당 3개의 요청을 보내는 겁니다. 먼저 테스트를 위한 함수를 준비했습니다. 소스코드는 “CREATE FUNCTION”을 눌렀을 때 나오는 기본 예제 코드입니다. Runtime만 기본 Node.js에서 python으로 변경했습니다 (Runtime을 변경한 특별한 이유는 없습니다)

 

Apache JMeter도 준비했습니다. 

 

자, 모든 준비가 끝났으니 HTTP 트래픽을 흘리면 되겠습니다. 여기서는 약 15분간 트래픽을 생성하고 결과를 비교했습니다. 아래는 Active instances의 변화입니다.

 

요청이 들어오면 “hello world”를 응답하는 간단한 애플리케이션이기 때문에 콜드 스타트 구간만 인스턴스가 최대치(유저수)만큼 올라가는 걸 볼 수 있습니다. 이후부터는 function instances가 들어오는 모든 요청을 처리 가능한 수준으로만 유지되는 것을 확인할 수 있습니다. 대략 4~15ms 이면 처리가 끝나기 때문에 1st-gen의 경우 40~50개 정도의 function instances면 초당 1,200개의 요청을 처리하는데 충분합니다. 반면 2nd-gen은 10개 수준으로 가능합니다. 이건 확실히 비용 절감으로 연결되는 부분이겠죠!

1초 만에 400개의 클라이언트가 동시에 접속했던 초반에 콜드 스타트 상황은 어땠을까요? JMeter의 Summary Report를 통해 확인해보겠습니다. 첫 번째 이미지가 1st-gen이고 두 번째 이미지가 2st-gen입니다. ( * Error % 는 신경 쓰지 말아 주세요. JMeter를 중간에 종료하면서 소켓이 비정상 종료돼서 발생한 오류입니다 )

몇 가지 의미를 찾아보면 우선 Max 값을 통해 콜드 스타트가 어떤 영향을 줬는지 살펴볼 수 있습니다. 1st-gen의 경우 10초까지 걸렸던 경우가 있는 반면 2st-gen의 경우 3.5초까지 걸렸습니다. 평균 처리 시간은 200ms로 두 버전에서 차이가 없었습니다. 

 

# 마무리

Cloud Function 2nd-gen이 처음 나온 2022년 초에는 콜드 스타트가 최적화되지 않아서 운영 레벨에서 사용하기 어려웠습니다. 하지만 지금의 테스트 결과를 보면 이미 1st-gen을 뛰어넘고 있기 때문에 굳이 1st-gen을 사용할 이유를 찾기 어렵습니다. 물론 Container Registry를 사용 중이라면 2nd-gen을 사용하기 위해 Artifact Registry로 넘어가야 한다는 등의 사소한 수고는 필요하지만요. Cloud Run이 처음 나왔을 때 게임 체인저라고 환호했던 게 기억나는데 이제 Cloud Functions은 거인의 어깨 위로 올라섰습니다. 구글의 서버리스 애플리케이션의 미래가 더욱 궁금해집니다.

 

# Ref.

 

 

 

댓글
최근에 올라온 글
최근에 달린 댓글
글 보관함
Total
Today
Yesterday