아, 당신이 ALB시군요... (feat. 퍼블릭 IP 한도 초과)

퍼블릭 IP 부족 사태로 인한 ALB 도입과 최적화 기록.

Jun Noh

직전 포스팅에서 SSL 인증서를 꼼수로 쉽게 셋팅하는 init-letsencrypt.sh 스크립트를 다뤘었다.

사실 로드밸런서(ALB)를 앞단에 딱 하나 두고 HTTPS 통신을 전담하게 한 뒤, 뒤에 숨은 내 서비스들은 편안하게 HTTP로 통신하게 하는 아키텍처가 정석이긴 하다. 특히 요즘처럼 한 달에 2개 이상의 서비스를 말 그대로 “공장처럼 찍어내고” 있는 내 상황에는 더더욱 그렇다.

하지만 알면서도 지금까지 그렇게 하지 않았던 이유는 단 하나였다. 고정 비용을 단 한 푼도 내기 싫었기 때문이다.

그런데 오늘 사건이 터졌다. 새로운 서비스를 하나 더 올리려고 EC2에 퍼블릭 IP를 추가하려는데… 엥? 할당이 안 된다. 알고 보니 AWS에서 기본적으로 계정당 퍼블릭 IP를 5개까지만 쓸 수 있게 하드 리밋을 걸어둔 것이다.

물론 AWS 고객센터에 제한을 풀어달라고 요청하면 금방 늘려주긴 한다. 하지만 막상 계산기를 두드려보니, 퍼블릭 IP를 6개, 7개씩 주렁주렁 매달고 요금을 내는 것보다 차라리 맘 편하게 로드밸런서를 하나 두고 나머지 서버들의 퍼블릭 IP를 싹 다 지워버리는 게 장기적으로 볼 때 훨씬 저렴하고 관리하기도 편했다. (ALB 기본 비용이 아무리 비싸다 한들, 그만큼의 정신적 평화와 값어치를 하니까…)

그래서 이참에 맘먹고 제대로 된 순서로 ‘가성비 AWS 인프라’를 처음부터 다시 구성했다. 나중에 또 기억 안 나서 하루 종일 삽질하기 싫으니 여기에 확실히 남겨둔다.

🛠 AWS 가성비 인프라 구축 완벽 가이드

Step 1. ALB 생성 및 ACM 인증서 준비

가장 먼저 트래픽을 정면에서 받아줄 로드밸런서와 보안을 위한 인증서를 세팅했다. 이제 더 이상 Certbot이랑 싸우지 않아도 된다.

  1. ACM 인증서 신청: AWS Certificate Manager에서 slowflowsoft.com*.slowflowsoft.com에 대해 와일드카드 인증서를 신청했다. 클릭 몇 번 하니 발급되는 걸 보고 그동안 왜 도커 스크립트를 파고 있었나 현타가 살짝 왔다.
  2. ALB 생성: EC2 콘솔에서 Application Load Balancer를 만들었다. 외부에서 들어오는 HTTP(80)와 HTTPS(443) 요청을 모두 받을 수 있게 리스너를 시원하게 열어두었다.
  3. HTTPS 리스너 설정: 443 리스너에 방금 신청한 ACM 인증서를 찰떡같이 적용해서, 로드밸런서가 앞으로 모든 SSL 여정을 전담하게 만들었다.

Step 2. 대상 그룹(Target Group) 구성

ALB가 트래픽을 받았으면 실제 앱이 돌아가는 서버의 어느 포트로 꽂아줄지 정의해야 한다.

  1. 대상 그룹 생성: ‘Instances’ 유형으로 대상 그룹을 파고, 포트는 Nginx가 대기 중인 80번으로 설정했다. 이제 서버 내부에선 HTTPS 인증서 신경 쓸 일이 아예 사라졌다.
  2. 상태 검사(Health Check): ALB가 이 서버가 일하고 있는지 뻗었는지 감시할 수 있게 경로를 /health로 열고, 성공 코드는 200으로 세팅했다.
  3. 인스턴스 연결: babple, ZuZoo 등 열심히 돌아가고 있는 인스턴스들을 각각의 대상 그룹 안에 80번 포트로 예쁘게 등록해줬다.

Step 3. Route 53 도입 및 가비아 네임서버 변경

여기서 예상치 못하게 덜미를 잡혔다. 가비아에는 ALB 주소를 다이렉트로 연결해줄 ‘별칭(Alias)’ 기능이 없었다. 어쩔 수 없이 DNS 관리 권한을 AWS로 통째로 옮기기로 결단했다.

  1. Route 53 호스팅 영역 생성: AWS에서 내 도메인 이름으로 호스팅 영역을 파버렸다.
  2. 가비아에서 권한 위임: Route 53이 던져준 4개의 네임서버 주소를 가비아 도메인 설정에 덮어씌웠다. 이제 도메인 관리는 가비아가 아니라 철저히 AWS 시스템 안에서 돌아간다.
  3. 별칭(Alias) 레코드 생성: 권한을 넘기고 나니 모든 게 편하다. 이제 Route 53에서 A 레코드를 만들 때 ‘별칭’ 스위치만 탁 켜주면 방금 만든 ALB를 아주 쉽게 연결할 수 있게 되었다.

Step 4. Nginx 및 Docker 설정 최적화

트래픽 대문 공사가 끝났으니, 이제 서버 내부에 덕지덕지 붙어있던 불필요한 SSL 잔재들을 걷어내고 최적화할 차례다.

  1. Nginx 다이어트: 기존 nginx.conf에 있던 443 포트 설정과 Certbot 경로를 싹 지워버렸다. 어차피 모든 요청은 ALB가 80번으로 던져주니까. 속이 다 후련하다.
  2. 리다이렉트 오류 해결: Nginx가 지멋대로 포트를 떼어먹고 브라우저를 localhost로 튕겨버리는 킹받는 에러가 발생해서, absolute_redirect off;를 추가해 참교육 시켰다.
  3. Docker 가속: docker-compose.yml에서도 더 이상 쓸모없는 443 포트 바인딩과 매번 꼬이던 SSL 볼륨 마운트를 미련 없이 날렸다. 컨테이너가 훨씬 가벼워졌다.

Step 5. 단일 퍼블릭 IP 돌려막기

퍼블릭 IP를 싹 다 지워버렸으니, 이제 각 서버에 접속해서 작업할 때는 어떻게 할까?

EIC 엔드포인트나 NAT 인스턴스 같은 멋진 방법들도 알아봤지만, 역시 내 상황에선 제일 무식하고 확실한 방법이 최고다.

그냥 퍼블릭 IP 딱 하나만 남겨두고, 평소에는 연결해제 해뒀다가 서버에 수동으로 붙어서 작업할 일이 생길 때만 그 IP를 해당 인스턴스에 잠깐 할당(연결)해서 사용하는 거다.

어차피 하루에 서버를 2~3개씩 왔다 갔다 하며 동시다발적으로 작업할 일도 없고, 세팅이 한 번 끝나면 터미널로 들어갈 일도 거의 없으니 이 방식이 제일 싸고 직관적이다.

결론

퍼블릭 IP 5개 제한 때문에 울며 겨자먹기로 뜯어고친 구조지만, 결과적으로 아키텍처는 놀랍도록 깔끔해졌고 고정 비용 방어에도 완벽하게 성공했다.

무엇보다 이제 새 서비스를 공장처럼 찍어낼 때마다 SSL 세팅한다고 터미널 붙잡고 닭과 달걀 싸움을 하며 삽질할 일이 영원히 사라졌다는 게 최고의 수확이다.

마침.

다른 글 보기