고정된 IP를 통과해 Elastic Beanstalk 접근하기

updated : 2019.09.07

최종안이 아래와 같은데,

Client -> NLB -> Proxy 서버군 -> EB

AWS에 계신 분과 이야기를 하면서 Proxy 서버군 대신 ALB를 붙이는 것이 더 좋을 것이라는 이야기를 들었고, 이게 가능할지 테스트를 해보려 했다.

그러나 ALB로 직접 연결할 방법이 딱히 보이지 않아서 검색을 해봤다.

https://stackoverflow.com/questions/50981864/aws-pass-traffic-from-nlb-to-an-alb

The problem is that Application Load Balancers can scale up, out, in, and/or down

여기 나온 것처럼, NLB와 ALB를 직접 연결할 수는 있으나 IP로 연결 해야 한다.
그러나 ALB의 특성 상 IP는 계속 변할 여지가 많기 때문에, 람다를 통해 계속 NLB가 바라보는 ALB의 IP를 바꿔줘야 한다.

그 방법은 여기 잘 나와있다.
https://aws.amazon.com/ko/blogs/networking-and-content-delivery/using-static-ip-addresses-for-application-load-balancers/

AWS 측에서 공식적으로 제안하는 방법이라고 보면 되겠다.

지금 구성을 고쳐서 이 방식으로 갈아치울 정도로 관리가 편해 보이진 않는다. 다른 곳에서 비슷한 요구를 해 온다면 그때는 이 방식이 훨씬 좋을 수 있다. 다만 IP가 변하는 상황에서 서비스가 안정적으로 유지될 지는 문서를 더 읽어보고 판단해야할 듯 하다.


이미 정답(혹은 더 좋은 방안)을 알고 계신 분들이라면 답답스럽겠지만 온통 혼란스러웠던 3일을 회고해본다. 그 동안 서버란 서버팀에 맡기는 게 당연한 회사를 다녔으므로 서버를 관리할 일도 거의 없었거니와 올 해 백엔드 개발자로 전향한 뒤에도 경험 많은 동료들만 믿고 인프라 관리는 좀처럼 내 일처럼 달려들지 못했다는 점을 고백하고 시작해야겠다.

Show me the IP

모 대기업과 신규 연동을 진행하는데, 상용 오픈을 일주일 남겨두고 방화벽 이야기가 나왔다. 그쪽에서 우리쪽으로 찌르는 request 밖에 없는데, 우리 서버 IP(혹은 대역)을 문의하는 것이었다. 계열사 모두가 같은 정책을 유지하는데, 외부 API로 요청을 보낼 때도 특정 IP나 대역을 지정해야만 한다는 것이다.

이전에도 몇몇 기업이 IP 대역을 문의하긴 했다. 그땐 우리가 그쪽 서버로 쏘는 상황이었고, 우리는 자동으로 스케일링 되는 환경이라 IP를 정해놓을 수 없다고 알려주곤 했었다 - 이것도 뒤에 얘기할텐데, 이 역시 가능하긴 하다.

AWS에선 고정 IP를 제공하거나 낭비하는 것에 매우 깐깐하기도 하고, Elastic Beanstalk같은 완전관리형 솔루션은 도메인이나 리소스 이름으로 관리되고 있었다. 그도 그럴 것이, 블루/그린 배포를 한다거나 EB에서 내부적으로 관리하는 로드발란서 같은 놈이 죽으면 새로 띄워 갈아끼워야 하니까 말이다 - EC2 DNS Resolution 문제가 발생하면 망하는 거지만.

어쨌든 계열사 모두가 같은 정책을 유지한다고 하니 양사 간 협의가 필요한 상황까지 왔다. PO를 통해 서로의 상황을 알아보고 각자 나름의 방법을 찾아보기로 했다. 난 순수한 뇌의 철없는 개발자로서, 비지니스 관점에서 우리가 얼마나 양보를 해야할 지도 몰랐고, 온-프레미스가 지배하던 시대의 방식 그대로 고정 IP로 인프라를 관리하는 건 너무 옛스럽고, AWS에 인프라를 구축한 이상 가능하면 아마존의 방식으로, 아마존이 유도하는 대로 구성하는 것이 더 나은 방향이라고 생각도 했었다. 보통의 경우 괜찮은 관점이라고 생각한다.

CTO님과 이야길 해보면서 이런 정책 차이로 사업을 엎어버릴 수도 없는 노릇이고, 기술적인 문제는 우리가 풀어야 한다는 이야기를 했다. 당연한 말인데, 아차 싶은 지점이 있었다. 나는 ‘개발자가 못한다는 소리를 한다는 건 쪽팔린 일이다’라는 생각을 한번도 해본 적은 없기 때문에(하지만 겉으로 꼰대짓을 했을 수는 있다), 해결하지 못한 것에 부끄러웠던 것은 아니었다. 다만 지금 벌어진 일이 ‘우리의 문제’라는 인식을 못했다는 반성을 했다. 그러니까, ‘정책 차이로 발생한 문제는 다른 사람들이 풀어낼 수 있고 난 죄가 없다’라고 속 편하게 앉아 있었던 것은 아니었을까. 물론 여전히 저쪽이 정책을 좀 수정했으면 하는 마음은 굴뚝같지만, 우리 입장에서의 다양한 해결책을 마련해놓긴 해야한다는 생각이 들었다. 그 비용이 산정되어야 그 ‘다른 사람들’이 외부에서 협의를 해오실 테니까.

무엇보다, 가능한 해결책이 당연히 있었다. 고정 IP를 사용하는 EC2를 올려서 서비스하면 된다. 다만 지금까지 우리가 만들어온 배포/관리 프로세스나 노하우가 분산되고 안정성을 확보할 수 없다는 점 때문에 죽어도 하기 싫은(죽으면 못하긴 하지) 방식이긴 했다.

방안

논의된 이후 남은 시간은 열흘. 위에 언급한 최악의 방식이든 새로 찾을 방식이든 다음 주 월요일에는 인프라 구성을 시작해야 한다는 것에 합의하고, 이틀정도 좋은 해결안을 찾아보기로 했다. 인프라에는 잼병인 내가 풀어야 할 숙제가 꽤나 무겁게 느껴졌는데, 서버 좀 만져보신 분들께 계속 물어보면서 몇가지 방안으로 압축되었다.

1안. Load Balancer 설정으로 해결

Auto Scaling Group에 Load Balancer를 추가로 붙일 수 있는지 검토

  • 어쨌든 AWS의 서비스를 정상적으로 이용하는 방법

이 방법은 대충 키워드만 줏어 들은 방법인데 정확히 어떤 아이디어인지는 잘 모르겠고 조금 찾아보다 NLB쪽으로 방향을 선회하며 더이상 깊이 들어가지 않았다.

2안 Proxy 서버 운영

Client -> Proxy 서버 -> EB의 단계로 구성

  • Proxy 서버에서 EB의 domain으로 접근
  • 프록시 서버의 안정성을 확보할 방안 필요

결론적으로 이 방식을 택하긴 했는데, 뒤에 나올 내용과 조합해서 다시 정리해보겠다.

3안 별도의 환경 구성

Elastic Beanstalk을 사용하는 방법 대신에 EIP를 붙인 인스턴스를 띄우고 서비스하는 방식. 특정 client를 위한 별도의 서버 구성/배포 프로세스 만드는 것도 부담이고, 아래 이유로 최대한 방어해야 하지만, 마땅한 해결책이 없을 경우 바로 구성 시작하기로 합의했다. 하기 싫지만 확실히 가능한 최후의 방법이었다.

  • 배포 프로세스 분산에 따른 업무 부담
  • 환경 변수 등 관리 부담
  • 검증 안된 서비스 구성이지만 바로 운영에 들어가야 함

그런데 또 다른 분이 팁을 주시기를 single-instance scaling group 생성해서 EIP 배정하는 방법도 있다고 하셨다 - 꼭 인스턴스 하나만 넣어야 하는 건 아니고, 제한된 인스턴스로 고정 시켜놓는 것. 이미 현재도 동일한 소스가 올라가는 scaling group이 셋이 있다. API 서버용(scaling 범위가 2대에서 수십대까지), cron용(1대), queue work용(2~4대).

여기에 고정 아이피 접근 용으로 scaling group을 하나 만들어서 내부 EC2 인스턴스에 EIP를 할당하는 방법이다. 이렇게 되면 배포 과정은 동일하게 유지하면서 몇몇 client를 위한 별도 구성을 제공할 수 있다. 다만 이런 요구를 하는 클라이언트가 늘어나면 EIP를 붙인 인스턴스가 늘어나게 될 것이고, scale-out 해야할 상황에는 인스턴스를 확보하고 IP를 추가로 알려줘야 한다. 즉, 최대 사용량을 기준으로 인스턴스를 유지해야 한다는 말인데, 아무래도 (특별한 관리가 없다면) 유휴 자원이 늘어나고 비용이 증가할 것이다.

또한 EIP에 연결된 인스턴스 상태가 안 좋아서 종료하는 경우, 새 인스턴스를 띄운 후 EIP에 다시 연결하는 과정이 필요하다.

4안 NLB를 붙이고 non-TLS(http로만) 접근하기

검색을 하다가 1년 전 NLB(Network Load Balancer)가 출시되었다는 사실도 알게 되었다. Elastic Beanstalk에도 붙일 수 있었는데, 현재 우리 서비스에는 Classic Load Balancer를 사용하고 있다. 이를 적용하자면 Blue/Green 배포를 통해 Load Balancer를 교체해야만 하고, 이에 따른 사이드 이펙트는 어떤 게 있을지 찾아보기도 해야 한다.

AWS 한국 유저그룹 등 검색을 통해 얼마나 쓸모 있을지, 우리 상황에 맞을지 확인해봤다. 몇가지 기억해 둘 만한 내용은 이렇다.

NLB는 subnet 별로 1개이고 HA구성은 내부적으로 되어 있는 것으로 알고 있습니다. 하지만 subnet 장애로 인한 문제를 해결하려면 Route53에서 Health check를 통해서 해결해야 하는것으로 알고 있습니다.

작년 AWS re:invent 세션 중에 자세하게 설명된 부분이 있네요. AZ 마다 Static IP가 나오니깐 그걸 Route53 health check로 Failover 하는 식인 듯 해요.
-https://www.youtube.com/watch?v=z0FBGIT1Ub4&t=1684s

옙. 그리고 DSR처럼 작동하니 기존 Proxy모드였던 CLB, ALB와 차이점을 인지하셔야 합니다.

  • 고정 IP를 지원하는 Network Load Balancer(ELB) 발표“ 참고
  • Client 요청이 백엔드에 전달될 때 Client IP 그대로 들어오므로 X-Forwarded-for 등의 부가 헤더 불필요
  • NLB 앞단에는 Security Group을 배치할 수 없으며, 따라서 관련 방화벽 정책은 백엔드의 Security Group단에서 제어해야 함

NLB의 Target Group에 매핑된 EC2가 인터넷 구간에서 요청하는 Client에 반드시 도달할 수 있는 구조여야 합니다.

위에 언급한 “고정 IP를 지원하는 Network Load Balancer(ELB) 발표“에서 깔끔하게 정리를 해주셔서 무척 도움이 많이 됐다. Elastic Beanstalk에서 사용할 수 있는 세가지 Load Balancer를 비교해주기도 하신다.

문제는 기존 환경에서 TLS를 구성할 때 환경 로드 밸런서로 서버 인증서를 할당하는 방식을 사용한다는 점인데, TLS 레이어를 Load Balancer가 제공하고 이 뒤에 있는 인스턴스와는 http 통신을 하게 된다. 이를 NLB로 바꿀 경우, NLB 뒤에 인증을 담당할 레이어를 더 붙여야 한다. 혹은 http 통신만 가능하도록 구성해야 한다.

그러나 이미 https로 연동된 업체도 있거니와 내년이면 모두 https 접근만 허용하도록 정책이 바뀌어 갈 예정이므로, 당장 오픈 해야하는 상황에 따른 미봉책이 필요할 경우에만 고려해야 한다. 그것도 기존 환경을 바꾸기 보다는 고정 IP로 들어와야 하는 업체를 위한 별도의 환경을 구성할 경우나 가능하다.

오픈이 얼마 남지 않았고 기존 서비스에 최대한 영향을 주지 않기를 바랐기 때문에 이 방식도 패스.

최종안

static-ip-eb-stack images

Client -> NLB -> Proxy 서버군 -> EB

NLB는 Load Balancer가 살아있는 동안(교체되지 않는 이상) static IP를 지원한다. NLB는 multi AZ에 걸쳐 고정된 IP를 만들어주는데, Route53에서 고정IP용 도메인을 생성하고 이 두 IP를 A record값에 모두 입력한다.

NLB 뒤에는 Proxy로 사용할 인스턴스 4대(모니터링 하면서 적절하게 조정할 예정)를 띄우고, NLB에서 바라볼 대상 그룹으로 묶어준다. 클라이언트와의 TLS 통신은 이 Nginx 서버에서 진행하게 된다. 때문에 인증서를 급하게 하나 구입하게 됐다 - 기존에는 AWS에서 제공하는 것만 썼기에.

NginX에서는 클라이언트에서 온 요청을 그대로 전달하는데, Elastic Beanstalk에 도메인 기반의 HTTP로 통신하고 클라이언트에 결과를 돌려준다. 클라이언트로 response가 돌아갈 때는 NLB를 거치지 않고 NginX에서 바로 돌아나간다.

참고로 이 과정에서의 Network Load Balancer가 어떻게 동작 하는지는 최신 네트워크 로드 밸런싱 및 프록시 소개와 같은 글에서 좀 더 깊이 있게 확인할 수 있다.

무엇보다도 이 방식이 마음에 들었던 것은, 프록시 레이어만 하나 추가함으로써 기존에 구성된 인프라를 전혀 건드릴 필요가 없다는 점이었다.

앞으로

이번에는 없었지만, 우리 서버에서 외부로 request하는 상황도 발생할 수 있다. 그들의 서버에 접근할 때 고정 IP/대역을 원할 것이다. 이미 그걸 원했던 업체가 종종 있기도 했다. 이번에 공부하면서 NAT Gateway를 쓰면 쉽게 된다는 걸 알았지만, 이걸 붙이면 어떤 영향이 있을지는 아직 연구가 부족하다. inbound든 outbound든 static IP로 통신해야 하는 서비스를 별도의 하나의 그룹으로 묶을지는 나중에 더 고민해보기로.

이번에 추가한 프록시 서버들을 어떻게 운용할지는 여전히 남은 숙제다. 현재의 프록시 구성이 적당한 지 판달할 만큼 충분한 트래픽을 갖기까지는 한참이나 기다려봐야 할 것이다.

아직 상용 서비스를 시작하진 않았는데, 이 글이 사라졌다면 뭔가 문제가 생겨서 실패했기 때문일 것이다.

마무리

내가 다 고민한 것처럼 써놨지만, 초기부터 중간 중간 열심히 같이 고민해준 동료가 많았다. 나는 처음에만 이런 저런 검토를 한 것 이외에는 사실상 모든 세팅은 사내 인프라 전문가께서 맡아 처리해주셨다. 한 게 없으니 블로그 정리라도 한다. 문제를 해결해야 한다는 부담도 분명 있었지만, 종일 하나의 문제에만 집중할 수 있어서 꽤나 즐거웠던 기억으로 남았다 - 해결이 됐으니까 즐거운 기억으로 남았지.