nodeJS 서버 AWS에 배포하기 - (4) route53, SSL, 로드밸런서, Nginx

🪐 C:on·2022년 1월 22일
4

AWS

목록 보기
4/6

🔨 도메인 구매

이제 도메인을 구매해서 인스턴스의 ip와 연결해줄 것이다. AWS는 Route53을 통해 도메인과 ip를 연결해주는 서비스를 제공한다.

일단 도메인을 구매해야하는데 나는 가비아에서 구매를 했다.
도메인 하나를 구입해놓고 서브도메인으로 프로젝트를 연결해주고 있는데 원래는 문어발처럼 관련없는 서비스끼리 한 도메인에 묶이면 안되겠지만 난 가난한 백수기에 그냥 서브도메인으로 프로젝트 서버 ip를 매핑해주고 있다.

원하는 도메인을 검색해준다.

이렇게 이벤트하는 도메인이 촤라락 나온다.
보통 탑레벨 도메인이 com이나 co.kr같은 것은 비싸고 사진처럼 shop 같은 것들은 500원 주고 1년 사용할 수 있다. 1년 이후에 연장을 해본적은 없어서 이벤트값이 적용되는 지 잘 모르겠다. 여튼 1년까지는 500원만으로 사용이 가능하다는 것!

참고로 도메인 구조는 위와 같고 우리가 구입하는 것은 1단계+3단계 도메인을 구입하는 것이다. 서브도메인은
구매한 도메인 판매 사이트에서 차상위 도메인을 추가하는 기능을 통해 무료로 생성할 수 있다.

🔨 route53

route53은 AWS가 제공하는 DNS이다. route53에 도메인을 하나 등록하면 이와 연결된 ip들을 설정해 줄 수 있다.

route53 서비스는 도메인 하나당 $0.5이다. 한화로 650원 정도여서 1년이면 8천원정도 나온다.

참고 : 생활코딩-aws2-route53 2.원리

top level domain마다 관리하는 등록소가 있다. 등록소에게 top level domain을 구매하기 위해 등록대행자를 거친다. 내가 이용한 등록대행자는 가비아인 셈이다.

도메인네임서버를 장만하면 도메인의 ip정보를 세팅해놓는다. 이때 도메인네임서버는 컴퓨터하나 사서 세팅해줄 수 있지만 AWS에서 route53서비스를 통해 임대를 할 수도 있다.

세팅이끝나면 내 탑레벨도메인을 관리하는 등록소에게 DNS가 누구인지 등록대행자(가비아)를 거쳐 전달해준다.

이제 클라이언트는 로컬 DNS서버에게 url을 전달하면 이 DNS는 루트 네임서버에게 탑레벨의 등록소 주소를 전달받고 다시 등록소에게 url의 DNS를 물어본다. 마지막으로 url의 DNS에게 ip를 전달받아 요청을 전달할 수 있게 된다.

route53 콘솔로 이동하고 왼쪽의 호스팅영역을 클릭한다.

호스팅영역 생성을 클릭

도메인이름에 구매한 도메인네임과 탑레벨 도메인까지 입력해주고 호스팅 영역 생성을 클릭

생성된 호스팅의 도메인 이름을 클릭해서 접속한다.

오른쪽의 레코드생성을 클릭

레코드이름은 자신이 이름을 딱 봤을 때 어떤 ip와 연결되는 레코드인지 구별할 수 있는 이름을 적어준다.
레코드유형은 A로 하고 값에 인스턴스의 탄력적IP주소를 입력한 뒤 레코드 생성을 누른다.]

그리고 호스팅영역을 생성했을 때 NS유형과 SOA유형의 레코드가 자동으로 생성되어있는데 이는 도메인을 구입한 사이트에서 네임서버를 변경해주는데 사용한다.

가비아는 도메인관리 페이지로 접속하면 왼쪽의 도메인 정보변경 메뉴에서 네임서버를 클릭하면 DNS를 변경해줄 수 있다.

여기에 NS유형의 값 4개를 네임서버 설정에서 호스트명으로 입력해주고 적용한다.

이제 시간이 조금 지나면 도메인네임으로 인스턴스에 접속할 수 있다.

도메인만으로 접속하면 기본 80포트로 접속되고 뒤에 :와 함께 포트번호를 적어서 다른 포토에 접근할 수 있다.

이 문제는 도메인과 인스턴스에 SSL을 적용한 뒤에 해결하도록 하자

🔨 SSL

크롬에서는 SSL적용이 안된 사이트는 클라이언트에게 계속해서 알림을 준다. 위험할 거 같은 사이트는 접속하기 싫어하니 SSL을 적용을 해주자.

AWS의 Certification Manager를 이용하면 간편하게 인증서를 발급받아 route53에 등록되어 있는 도메인에 적용할 수 있다.

Certification Manager 콘솔로 이동해서 우측 상단의 요청을 클릭하고 인증서요청이 뜨면 다음을 클릭


SSL을 적용해 줄 도메인을 입력해준다.
와일드카드 SSL을 생성해주면 하위(3차이상) 도메인(shop.naver.com, news.naver.com 등등) 모두에 SSL이 적용된다. 와일드카드 SSL을 요청하려면 *를 사용한다.(*.naver.com)

단, 2차도메인(naver.com)은 적용이 안되므로 이 인증서에 다른 이름 추가를 눌러서 2차 도메인도 적어준다.

파란색 알림창의 인증서보기를 눌러준다. 알림창 안뜨면 그냥 생성된 인증서 ID를 눌러줘도 된다.

Route54에서 레코드 생성을 눌러주면 자동으로 DNS레코드 생성 단계를 설정해준다. 그러면 레코드생성버튼을 눌러준다.

이렇게 검증 대기중으로 뜬다. 검증까지는 시간이 좀 걸린다. 배고픈거 참고 여기까지 했으니 발급되는 동안 저녁을 먹고온다.

밥먹고 오니 발급 완료! 이제 연결을 해주자.

🔨 ALB 생성

ALB는 Application load balancer이다.
클라이언트가 수백명이면 서버가 응답하다 지쳐서 멈출 수도 있다.

이러한 문제는 다음 2가지 방법으로 해결해준다.

  • Scale-up : 하드웨어의 성능을 업그레이드
  • Scale-out : 여러대의 서버가 나눠서 일을 분담

신기하게도 서버를 추가하는 것이 하드웨어 향상보다 비용이 적다. 그리고 무중단 서비스가 가능해진다.

Scale-out시에 여러 서버에게 트래픽을 균등하게 분산시켜 주는 것이 바로 로드밸런서이다.

EC2 콘솔에서 왼쪽 아래에 로드밸런서에 접속한 뒤 로드밸런서 생성버튼을 눌러준다.

세가지 로드밸런서가 있는데 적혀있는 설명에 따르면 첫번째 애플리케이션 로드밸런서가 http및 https 트래픽을 사용한 웹 애플리케이션을 위한 유연한 기능을 위해 사용된다고 한다.
따라서 애플리케이션 로드밸런서의 create를 눌러준다.

보안그룹은 이전에 만든 basic을 적용해주고 Listener and routing에 http와 https를 설정해준다.
각 리스너의 default action을 target-group(대상그룹)으로 선택한다.

대상그룹이란 리스너가 전달한 요청을 처리하기 위한 부하분산 대상들의 모임이다.
대상 그룹에는 EC2의 인스턴스 ID, post 등이 적혀있고 요청이 잘 전달되는지에 관한 모니터링을 하는 헬스체크를 확인할 수 있다.

로드밸런서를 선택하고 리스너에서 규칙 보기/편집을 클릭한다.

연필모양을 클릭하면 규칙을 편집할 수 있다. 위의 연필을 클릭하고 마.지.막 옆에 나타난 연필을 또 클릭한다.

then에 있는 규칙을 삭제하고 작업추가를 눌러서 리디렉션대상을 선택한다.

https, 443을 입력해서 설정해준 뒤 체크를 클릭하고 오른쪽 상단의 업데이트를 누른다.

route53으로 이동해서 레코드생성을 누른다. 별칭을 on으로 바꿔주면 생성한 로드밸런서를 선택할 수 있다. 선택후 생성을 눌러준다.

새로운 레코드를 생성했으므로 기존에 domain.comwww.domain.com으로 생성했던 레코드는 삭제해주어야 로드밸런서로 거쳐갈 수 있다.

하지만 아직 포트없이 도메인만 호출하면 에러가 뜰것이다. nginx를 설치해서 해결해보자.

🔨 Nginx

proxy서버는 서버와 클라이언트 사이 중계기능을 하는 서버를 의미한다. 클라이언트는 서버에게 직접요청하지 않고 프록시 서버에게 요청을 날린다. 요청을 받은 프록시서버는 웹서버에게 실제 데이터를 요청하고 그 응답을 클라이언트에게 전달해준다.

프록시서버는 크게 두 가지로 나눠진다.

  • 포워드 프록시
  • 리버스 프록시

포워드 프록시는 클라이언트는 캐싱기능을 사용한다. 클라이언트의 요청이 인터넷에 연결되기 전 캐시에 데이터가 남아있다면 캐시에서 컨텐츠를 제공한다.

리버스 프록시는 실제 데이터를 작업하는 서버보다 앞에서 대리로 요청을 받아 인트라넷에 직접 접근하는 요청을 차단할 수 있다. 또한 SSL암호화 등을 수행할 수 있으며 API gateway역할을하여 외부의 요청에 대한 응답을 엔드포인트로 대리 전송해준다.

따라서 우리는 nginx를 리버스프록시 용도로 사용할 것이다.

아파치와의 차이점
아파치는 쓰레드/프로세스 기반으로 요청 하나당 쓰레드 하나가 처리하는 구조이다. 따라서 사용자가 많으면 많은 쓰레드를 생성해야 하기 때문에 메모리 및 cpu낭비가 심하다.
엔진엑스는 비동기 이벤트 드리븐 구조로 nodeJS와 유사하다. nodejs 창시자는 nginx를 nodejs의 앞단에 두어서 버퍼 오버플로우 취약점에 의한 공격을 방지하는 것을 추천한다.
버퍼오버플로우란 버퍼가 메모리공간을 벗어나는 경우로 사용하지 않는 영역에 데이터가 덮어씌워져 주소,값을 바꾸는 공격이다.

nginx 설치

인스턴스에 접속하여 sudo apt-get install nginx를 입력하여 nginx --v를 입력해서 설치가 성공적으로 완료됐는지 확인한다.

그리고 domain.conf를 수정해야 하므로 다음을 차례대로 입력한다.

cd /etc/nginx
sudo vi nginx.conf

vi 편집기에서 파일을 수정해줄 것이다.
vi의 기본적인 명령어는 어렵지 않으므로 구글에 검색한다.
http안에 다음코드를 삽입해준다.

server {     
  listen 80;
  server_name  ~.;   
  location / {         
    proxy_set_header X-Real-IP $remote_addr;             
    proxy_set_header HOST $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # port setting , 서버의 port와 동일한 port로 pass 시켜야 합니다.
    proxy_pass http://127.0.0.1:4200;
    proxy_redirect off;
  }
}

이제 이렇게 http요청을 보내면 자동으로 https로 리다이렉팅하여 nginx가 nodejs에게 대신 요청을 전달하고 응답을 받아 우리에게 보여주게 된다.

❗오류

http post요청 오류

get요청은 정상적으로 nodejs서버에게 전달되지만 post요청은 전달이 되지 않았다.
http로 요청을 보내면 https로 redirecting이 되면서 post요청이 GET요청으로 바뀌어있었다.

리다이렉트시 동일한 메서드로 요청하려면 http상태코드를 307을 사용해야 한다. 하지만 ALB는 301과 302만을 지원한다.

따라서 리다이렉트를 로드밸런서에서 하지않고 nginx에서 수행하기로 했다.

로드밸런서의 리스너 중 80포트에 관련된 것의 규칙을 다시 리다이렉트가 아닌 프로젝트의 인스턴스로 연결해주었다.

nginx.conf는 리다이렉팅 코드를 추가하여 상태코드는 307로 설정해주었다.

이제 리다이렉팅이 되어도 메서드는 유지되어 정상적으로 응답을 받을 수 있게 되었다.

프론트엔드와 백엔드의 도메인이 다르기 때문에 쿠키 옵션을 설정할 때 sameSite="none"으로 설정해줬고 따라서 secure=true로 강제로 해줘야 했다.

문제는 프론트엔드에 쿠키가 저장이 안되는 것이었다.
구글링을 해보니 express-session 옵션에서 proxy=true를 추가해줘야함을 발견했다.

secret: config.session.secreatKey,
  resave: false,
  saveUninitialized: false,
  store: sessionStore,
  proxy: true,
  cookie: {
    httpOnly: true,
    maxAge: 3600000,
    sameSite: "none",
    secure: true,
  },

이렇게 설정해주니 정상적으로 동작했다.

개발단계에서는 secure을 사용하기 힘드니 다음과같이 변경했다.

secret: config.session.secreatKey,
  resave: false,
  saveUninitialized: false,
  store: sessionStore,
  proxy: true,
  cookie: {
    httpOnly: true,
    maxAge: 3600000,
    sameSite: config.node === "dev" ? false : "none",
    secure: config.node === "dev" ? false : true,
  },

2개의 댓글

comment-user-thumbnail
2022년 5월 16일

진짜 너무 감사드립니다. 선생님 덕분에 사흘동안 뺑이치던거 해결됐습니다 ㅠㅠㅠ

1개의 답글