블로그 개발중에서 aws에 배포를 했던 작업이 가장 어려웠고 배포를 하면서 잊지못할 놀랍고도 환상적인 경험을 많이 하였다. 하지만 그 만큼 더 많은걸 배웠고 경험해봤으니 꽤나 즐겁기도 했다.
프론트와 백엔드가 완성되고 이제 배포하는 일만 남았을때 였다. 몇번 연습삼아서 ec2에 한두번 올려본적은 있지만 제대로된 서비스 라고 부를만한 프로젝트를 각잡고 배포를 해본적은 없었다. 이번에 좀 제대로된 인프라를 직접 구축해보고 경험해보자는 뜻에서 아래의 목표를 세웠다.
배포에 필요한 권한만 가진 배포 전용 IAM 계정을 따로하나 만들었다.
정확히는 최소 권한
이 아니라 배포 도중 실수했을때 정신건강을 위해 삭제 권한과 모든 리소스에 대한 허용을 줬다.
(모범 사례는 정말 생성하는데 필요한 최소한의 액세스 레벨과 배포할 리소스에대한 권한만 있어야함)
serverless 프레임워크에서 nextjs 배포에 필요한 최소 권한은 여기에 나와있다.
추가적으로 백엔드 배포에 필요한 몇가지 권한을 넣었고 아래 이미지는 내가 적용한 권한 목록이다.
사실 프론트엔드 배포에 관해선 딱히 한게 없다. 왜냐면 저 serverless 프레임워크가 모든걸 알아서 해주기때문이다. 물론 직접 손으로 aws에 배포를 할수도있지만 nextjs를 배포하려면 빌드된 파일 업로드부터 SSR 과 SSG 등 동적인 빌드를 위한 람다 함수 생성,연결 까지 직접 해줘야하기 때문에 상당히 복잡하고 귀찮은 작업이기 때문이다. 한가지 문제가 있었다면 serverless 패키지 자체의 문제때문에 강제로 낮은 버전으로 배포를 했다. 공식 문서에서도 아직 베타 버전이며 지원하지 않는 기능과 버그가 있다라고 설명하고 있다.
vpc를 구성하면서 겪었던 문제들이다. 실제 구성했던 방법을 쓰기엔 글이 너무 길어져서 구축 방법은 생략하고 나중에 따로 글을 작성할 예정이다.
현재 이 블로그는 EC2에 mongoDB로 데이터베이스를 사용하고있다.
db를 쓰기전에 먼저 vpc를 구축했는데 사실 그동안 vpc가 가상 네트워크를 구축하는것 정도로만 알고있었는데 실제 vpc를 생성하고 그안에 EC2 인스턴스를 실행시키기 까지 서브넷
,라우팅 테이블
,게이트웨이
등의 추가 개념을 익히는데 꽤나 애를먹었다.
다행이 예전에 네트워크 관련 공부를 했던적이 있어서 크게 고통받지는 않았지만 아직 vpc에 대한 이해가 부족한상태에서 구축하려니 몇가지 문제에 부딪혔다.
첫번째 문제는 private 네트워크에 있는 인스턴스는 외부에서 접속이 불가능한데 ec2 인스턴스에 db를 구축하려면 결국은 접속해서 설정을 해줘야하는데 이걸 어떻게 해결하는가
였다.
이 문제를 해결하는 방법으로 bastion host
(바스티온 아님 ㅎ) 라는 방법이 있긴 하지만 이렇게 까지 구축을 하는건 너무 과하다고 생각하여 건너뛰었다. bastion host 란 쉽게 말하면 vpc 안에서 방화벽의 역할을 하는 인스턴스 라고 설명할수있다.
자세한 내용은 이 글을 한번 읽어보자 설명이 친절하게 잘 되어있다.
아무튼 이 문제를 어떻게 해결할까 고민을하다가 한가지 기막힌 아이디어가 떠올랐는데 바로 db 세팅을 할때만 private subnet에 인터넷 게이트웨이를 연결하고 세팅을 한다음 다시 게이트웨이 연결을 끊는거였다. 직접 인스턴스에 접속해서 db를 건드릴 일은 웬만하면 없기 때문에 이렇게 vpc에 db 구축을 해냈다.
백엔드도 serverless 프레임워크로 람다에 배포를 하였다. 배포하는거 까진 문제가 없었지만 이걸 어떻게 vpc안에 있는 인스턴스와 연결하는지는 몰랐다.
먼저 내가 (잘못)이해한 백엔드의 구조는 이렇게 되어있다 라고 생각했었다.
람다함수(백엔드 코드)에서 mongoDB가 실행중인 ec2의 주소에 접속하는걸 확인하려고 대략 4~5일동안 헤맨걸로 기억한다.
정말 온갖 삽질을 다하다가 반쯤 포기했을때 우연히 유튜브에서 vpc에 람다를 배포하는 강의가 있었고 그 영상에서 설명하는 내용을 보게되었는데
이 람다 함수에 vpc를 적용하면 해당 람다 함수는 vpc 내부에서 실행된다
라고 설명하더라.
(해당 영상은 여기)
그러니까 람다함수가 vpc 안에서 동작한다면 현재 배포한 백엔드의 아키텍처는 아래와 같은 모습이라는거였다.
근데 여기서도 문제가 생겼는데 람다랑 ec2가 같은 네트워크 안에 있으면 바로 접근 가능해야 하는데
계속 연결이 안돼는거였다.
정말 vpc 때문에 일주일 가량 고생을 했는데 이제 해결했다 싶었더니 아직도 해결을 못한것이다.
그렇게 한숨쉬며 aws 콘솔만 보고 있다가 정말 기가 막힌 실수를 발견을 하게되었다.
바로 ec2의 공인 ip
와 사설 ip
부분이었는데
놀랍게도 백엔드 코드에서는 mongoDB를 연결할때 공인 주소로 접속을 하려고 하니 접속이 안 되는 거였고 그걸 vpc 내부 ip로 바꿔주니 바로 연결이 되는거였다. 이렇게 간단한걸 간과하고 고생하다니 기가막힐 노릇이지만 은근 자주 있는것 같다.
블로그의 포스트 카드에 썸네일 이미지 최적화를 적용하고 싶어서 이미지 크기를 조정하는 리사이저를 적용하기로 했다.
사실 이 이미지 최적화는 nextjs 에서 next/image
컴포넌트가 이미지 파일 변환,크기조정,캐싱 까지 모든걸 자동으로 처리한다.
아예 공식적으로 img 태그 대신 next/image
컴포넌트를 사용하라고 강요한다. 하지만 이번에도 개인욕심으로 직접 구현해 보고싶어 lambda@edge를 이용한 이미지 리사이저를 구현해보기로 했다.
이미지 리사이저의 기본적인 개념은 cloudfront에서 이미지 요청이 들어왔을때 해당 이미지를 응답하기 전에 실행되는 lambda@edge 함수에서 해당 이미지를 최적화 한다음(포맷 변경 및 크기 조정등) 클라이언트로 보내주는 역할을 하며 한번 응답한 이미지는 이후 캐시로 저장하고있다가 같은 주소에 대한 요청이면 캐싱된 이미지를 응답한다.
이 이미지 리사이저를 구현하는 대에도 상당한 시간이 걸렸다. 이미지 변환 코드를 짜는것 부터 배포까지 온갖 버그에 시달리며 람다 함수를 배포하고 삭제하는걸 100번 가까이 한것같다. 하지만 이렇게 몸으로 부딪히며 실수와 오류를 경험한다는것 자체가 내가 배움을 얻고 성장하고 있다 라고 생각한다. 그리고 결국 문제를 해결했을때의 기분이 참 좋음.
참고로 이미지 리사이징 함수를 구현하는 과정은 꽤나 길고 복잡하다. 내가 참고했던 구현하는 방법은 여기에 있다.
ec2 인스턴스에 db를 올리기전 몇가지 테스트를 해본다고 mongoDB를 실행하고 간단한 로그인 계정이랑 포스트 1개를 가진 db를 생성했다.
그리고 로그인과 포스트가 뜨는지 확인하고 접속을 종료했다. 그런데 바로 다음날, 블로그에 접속해보니 이상하게 포스트가 안뜨고 로그인도 안돼는것이다. 뭔가 잘못된거 같아서 ec2 인스턴스에 직접 접속해보니 db의 모든 테이블은 삭제되고 README 라는 테이블 하나에 메세지가 남겨져있었다.
내용을 요약하지만 이렇다.
당신의 모든 데이터는 백업되었으니 48시간안에 0.05(대략 115만원) 비트코인을 보내십시오. 그러지 않으면 우리는 모든 데이터를 공개할것이며 GDPR에 일러바쳐서 데이터를 제대로 보관하지 못한 법적인 책임을 지게 될것입니다.
해킹당한 db내용은 어차피 중요한것도 아니고 복구랑 gdpr도 개소리 라는걸 알고있었기에 크게 걱정은 안했지만 실제로 이런 해킹을 당해보니 정말 심장이 내려앉는 느낌이었다. 혹시나 ec2 나 aws 계정에 문제가 있을까봐 인스턴스는 삭제하고 pem 키도 지워버렸다.
나중에 알게된건데 mongoDB를 처음 실행시켰을때 반드시 admin 계정을 생성해줘야한다 그러지 않으면 별도의 로그인없이 누구나 접속이 가능하게된다.
그래서 아예 이런 보안설정이 안된 db만 노리는 봇한테 당한거 같다. 사실 테스트한 저 db에 문제가 없다면 그냥 바로 쓸생각이었는데 지금 생각하니 소름돋는다.