안녕하세요 브로코딩입니다.
오늘은 프로젝트 '오디'에서 EC2가 private subnet안으로 들어감에 따라 외부 API 호출이 되지 않았던 문제를 극복했던 과정에 대해 기록해볼까 합니다.
🤔 Situation : 무슨 상황이었나?
: private 서브넷 인스턴스에서 외부 API를 호출하지 못함
상황은 이렇습니다.
우아한 테크코스 레벨4 운영환경에서 미션 요구사항은 public subnet에 있는 prod 서버를 private subnet 내로 옮기는 작업이었습니다.
AS- IS
TO-BE
여러가지 변화가 있지만 이슈를 겪게 했던 가장 큰 변화는 바로 public subnet 내에 있었던 prod 서버 ec2가 private subnet으로 들어가면서부터였습니다.
현재 오디 팀에서는 실시간 대중교통 정보를 기반으로 출발지와 도착지 간의 소요시간을 반환해주는 오디세이 API를 사용하고 있습니다.
그리고 이 API에서는 호출이 되는 서버의 ip 주소를 직접 등록해주어야 했는데요. 등록된 ip에서 호출된 요청만 정상응답을 반환했습니다. 또한, API key가 옳은 값이더라도 등록되지 않은 ip에서 호출한 요청에 대해서는 인증 오류를 반환하였습니다.
문제는 바로 이 포인트에서 발생했습니다.
public subnet 내에 있는 인스턴스의 경우 자동으로 public ip 주소를 할당받습니다
그러나, private subnet 내에 있는 인스턴스의 경우 public ip를 할당받지 않습니다
그럼 private subnet 내에 있는 EC2의 경우, 어떤 ip를 통해 외부 API가 호출이 될까요? 저희 팀은 이 동작 원리에 무지했고 예상대로 Odsay API는 인증 에러를 뱉어내기 시작했습니다.
2024-10-21 00:34:03.324 [INFO] [http-nio-8080-exec-3] [380e1a3c-02a6-4875-a9e1-d727fd08a9eb] [c.o.r.c.RouteClientLoggingInterceptor] - [RouteClient Request] Method: GET, URI: https://api.odsay.com/v1/api/searchPubTransPathT?SX=127.1581790&SY=37.4967860&EX=127.1031130&EY=37.5152980&apiKey=***,
2024-10-21 00:34:03.431 [INFO] [http-nio-8080-exec-3] [380e1a3c-02a6-4875-a9e1-d727fd08a9eb] [c.o.r.c.RouteClientLoggingInterceptor] - [RouteClient Response] Status: 200 OK, Body: {"error":[{"code":"500","message":"[ApiKeyAuthFailed] ApiKey authentication failed."}]}
Task : 무엇을 해야 하는가?
문제 상황으로 부터 우리가 해결해야 하는 것을 특정하면 다음과 같습니다
- 1. 외부 API를 호출하는 고정된 public ip를 찾아낸다, 없다면 라우팅되도록 한다
- 2. 오디세이 server ip에 그 public ip를 등록하여 통신한다
💪 Action : 어떻게 해결했나?
✋ Action1 : 직접 물어보기 > 실패
가장 직접적인 방법은 역시 물어보는 것이겠지요. 현재 상황을 이야기하며 오디세이 사이트에 질문을 남겼습니다.
그러나, 역시나 인프라와 얽혀있는 문제이다 보니 정확한 해결방법을 받지 못했고 페어인 카키와 함께 인프라 상에서의 해결방안을 찾고자 하였습니다.
📝 핵심 문제 상황 정의하기
먼저 이번 문제의 태스크를 한 문장으로 줄여보았습니다.
Odsay 사이트에 등록해주어야 하는 Server IP가 무엇인가?
이는 곧 private subnet 내에 있는 인스턴스가 어떤 라우팅 경로를 거쳐 인터넷과 통신하나? 로 이어집니다.
따라서, 고정적인 ip로 라우팅을 할 수 있게 만들거나, 이 ip를 찾기만 하면 되는 문제로 단순화할 수 있습니다.
📶 Action2 : NAT 개념 이해하고 적용하기
이러한 질문은 곧 구체적인 해결방안으로 이어졌습니다.
처음에는 public subnet 내에 탄력적 ip(고정 public ip)를 사용하는 프리티어 EC2를 만들고,
이 EC2에게 외부 API 호출에 대한 책임을 분담해주자는 의견에 이르렀습니다.
즉 고정적인 ip를 가지고 있고 + 인터넷과 소통 가능한 인스턴스에게 api 호출 책임을 분리하여 문제를 해결하고자 했습니다.
그러나, 비슷한 개념으로 구글링을 하던 중 NAT instance와 NAT gateway라는 개념을 접하게 되었습니다.
그렇다면 NAT 인스턴스와 NAT gateway란 무엇일까요? 깊게 들어가기보다 문제 해결에 초점을 맞추어 개념적인 설명만을 따라가보면 다음과 같습니다.
NAT(Network Address Transalation) 인스턴스란 프라이빗 서브넷의 리소스가 외부 대상과 통신하기 위해 고정된 ip를 가진 public subnet 내의 EC2를 일컫습니다. AWS 공식 문서에서는 다음과 같은 도식을 통해 NAT instance의 개념을 설명하고 있습니다. 저희 페어가 상상했던 구조와 상당히 비슷하죠?
NAT instance가 제 역할을 하기 위해서는 3가지 조건이 필요한데요
- 조건1. NAT instance는 인터넷 엑세스를 위해 public ip를 지녀야 함
- 조건2. NAT instance는 public subnet 내에 있어야 함(인터넷 게이트 웨이 접근이 가능해야 함)
- 조건3. private subnet 의 요청을 NAT instance로 라우팅 설정을 해주어야 함.
이와 비슷한 역할로 NAT gateway도 private subnet 내의 요청에 대해 인터넷 액세스를 도와주는 역할을 하고 있었습니다.
NAT gateway와 NAT instance는 사용자가 관리하는 EC2를 기반으로 하는가 AWS가 관리하는 gateway냐의 차이만 있을 뿐 역할적으로는 동일한 역할을 수행합니다. 구체적인 차이점에 대해서는 AWS 공식 사이트의 설명을 첨부토록 하겠습니다.
다행히도 우아한 테크코스에서 지원하는 인프라 안에서는 NAT gateway가 포함되어 있었습니다.
개선된 인프라 구조에서는 application ec2가 2개의 AZ private subnet에 각각 위치해있었는데요.
여기서 각 AZ의 public subnet 안에서 보라색 아이콘으로 internet gateway와 통신 가능하다고 표시된 아이콘이 NAT gateway였습니다. 즉, 각각의 AZ별로 private subnet에서 호출하는 외부에 대한 요청은 NAT gateway로 라우팅되어 인터넷과 통신하도록 되어있었습니다. NAT-project-a는 AZ-a에서의 private subnet-a 과 인터넷 통신을, Nat-project-b의 경우는 AZ-b에서 private subnet-b와 통신을 주고 받고 있었습니다.
그럼 이에 대한 설정은 어디서 확인할 수 있을까요? 라우팅 테이블을 통해 확인할 수 있었습니다.
실제로 AZ-a에 있는 private subnet인 project-private-a에서 요청하는 10.0으로 시작하는 요청을 제외한 모든 외부 요청을 NAT-project-a로 라우팅해주고 있었습니다.
비슷하게 AZ-b에 있는 private sub인 project- private-b의 경우도 private subnet에서 발생하는 외부 통신 요청을 NAT-proejct-b로 라우팅해주고 있었습니다
그럼 이제 다시 문제를 정의했던 TASK 단계로 돌아가 질문에 대한 답을 해봅시다.
1️⃣ 외부 API를 호출하는 고정된 public ip를 찾아낸다, 없다면 라우팅되도록 한다
: private subnet -a는 고정된 ip를 가진 NAT gateway(NAT-project-a)로 라우팅되어 인터넷과 통신한다
: private subnet -b는 고정된 ip를 가진 NAT gateway(NAT-project-b)로 라우팅되어 인터넷과 통신한다
즉, 인프라 구조에 따라 NAT gateway의 ip가 외부 API입장에서는 요청을 보낸 고정된 public ip가 된다
2️⃣ 오디세이 server ip에 그 public ip를 등록하여 통신한다
이제 오디세이에 각 NAT gateway ip를 등록해줄 차례입니다.
📖 Result : 결과는 어땠는가?
결과적으로 NAT gateway의 ip 주소를 odsay server ip에 등록해주면서 트러블 슈팅에 성공했습니다.
그러나 좋았던 점은 문제를 해결했다는 사실 자체보다, 어떤 방식으로 접근했고 또 그 문제의 원인을 명확히 인지하고 해결방안도 명확히 설명할 수 있을 정도의 문제 해결과정을 겪었다는 것입니다.
문제의 정의와, 어떤 키워드로 구글링을 하며 해결에 접근해야 하는지 배울 수 있었던 값진 기회가 아니었나 싶습니다. 특히 NAT gateway를 통한 설정으로 odsay 호출이 다시 동작하던 순간은 페어였던 카키와 흥분에 찬 하이파이브를 했던 기억이 생생합니다.
그러나, 반대로 현재 프로젝트의 단점인 `외부 API의 높은 의존성`을 명확히 인지하게 된 계기가 되기도 했습니다. 소요시간 반환 API가 stop되니 모든 핵심로직이 무용지물이 되었습니다. SPOF로 API가 작용하고 있다는 것을 인지하고 외부 API 의존도를 낮추기 위한 노력을 꾸준히 이어나아가야 할 것 같다는 생각이 들었습니다.
이상으로 NAT 개념을 처음 공부하고 프로젝트에 적용해본 브로콜리였습니다
'프로젝트 > 오디' 카테고리의 다른 글
💭프로젝트 '오디' 핵심 기능 리팩터링 상상일지 : 웹 소켓 전환 시나리오 (5) | 2024.11.03 |
---|---|
프로젝트 `오디` 핵심 기능 구현 일지 : 실시간 친구 도착 예정정보 공유 기능 (1) | 2024.11.01 |
CD 배포 스크립트 실행 중 오류가 발생한다면? : tag를 활용한 롤백 전략 구축 하기 (1) | 2024.10.28 |
🙉 Test Fixture 생성전략에 대한 고민 feat) FixtureMonkey (2) | 2024.10.12 |
Odsay 대중교통 길찾기 API를 쓰며 느낀 3가지 단점 (2) | 2024.08.26 |