AWS EC2 구축
EC2의 인스턴스 시작을 누르면 다음과 같이
EC2를 쉽게 구축할 수 있도록 AWS가 일종의 EC2 구축 가이드라인?을 해놓았다.
사용자가 쉽고 빠르게 EC2 서버를 구매하기 위한 AWS 경영 전략이 아닐까? ㅋㅋㅎㅎ
다음으로는 AMI를 지정해준다.
AWS EC2 서버의 운영체제를 택하는 절차이다.
나는 인스타 클론코딩에서 알려준 바와 같이 Ubuntu OS를 선택하려고함 -> 확정은 팀원들에게 여쭤보고 정함.
운영체제를 선택한 다음에
인스턴스 유형을 선택한다.
인스턴스 유형은 EC2 서버 컴퓨터의 사양을 선택하는? 느낌인 듯하다.
솔직히 추측이긴하지만, 대략적인 내 생각이다 ㅎㅎ..
AWS에선 인스턴스 유형을 자신이 설정한 인스턴스 스토리지, 메모리 등의 요건에 맞는 걸 선택하라고 참고를 써놨다.
아마.. 비싼 걸 쓰려면 비싼 거 끼리만 쓸 수 있게 만드는 AWS 전략이 아닐까? 라는 생각을 해본다 ㅎㅎㅋㅋㅋ
인스턴스 유형의 토글 버튼을 클릭하면 나오는 팝업 토클 창이다.
프리 티어가 아닌 것들은
일정 기간동안 적혀진 비용을 지불해야 한다.
역시 자본주의 사회답게, 비용이 비쌀수록 EC2 서버 성능은 좋아진다..
다음으로 키 페어를 생성한다.
키 페어는 암호 파일 (.pem) 로 제공되며, EC2 운영진에게만 제공되는 걸로 알고 있다.
그래서 키 페어 파일은 운영진 권한으로 접근할 수 있게 확인해주는 운영진 인증 파일이다.
그렇기에, EC2 구축 후 키 페어 파일의 위치를 까먹으면 혼동이 올 수도 있다 ㅎㅎㅋㅋ...
다음으로는 네트워크 설정으로 보안그룹을 설정해준다.
EC2 서버를 구축하고 구축한 EC2 서버에 들어올 수 있는 경로를 지정해준다고 생각하면 된다.
방화벽 설정과 같다고 보면된다.
우리는 임시 배포이므로 모든 IP를 허용한 채로 보안 그룹 규칙을 설정해두었다.
현업에서 이렇게 하면, 보안에 문제가 많이 생긴다 ㅎㅎ..ㅋㅋ
EC2 인스턴스의 마지막 설정 단계인 스토리지 구성까지 해준다.
기본 스토리지 값은 8 GIB로 설정되어 있고,
우리는 30GIB로 설정해줬다.
프리티어 단계에서 30GIB가 최대 용량이기 때문 ㅎㅎ
탄력적 IP 설정
EC2 서버는 기본적으로 유동적 IP 성질을 지니고 있다.
그렇기에, 서버를 중단하고 재실행하면 서버의 IP 주소가 변경된다.
이러면, 서버를 재실행 할 때마다 클라이언트에게 새로운 IP를 알려줘야하는 불편함이 생긴다.
탄력적 IP는 유동적 IP의 이러한 불편한 점을 해결해준다.
탄력적 IP가 머야?
탄력적 IP는 유동적 IP와 다르게,
서버가 중단되고 재실행 되더라도 중단하기 전의 IP가 그대로 보존된다.
우리는 클라이언트에게 같은 IP 주소로 응답을 받고 보내는 것이 편하기에 탄력적 IP를 설정하겠다.
상황에 따라 탄력적 IP, 유동적 IP 설정하는 듯 ~
탄력적 IP 주소를 할당해본다.
AWS EC2 구축뿐 만아니라, 관리에서도 너무 편하잖아... - 이게 대기업의 힘?
위에서 탄력적 IP 주소 할당 버튼을 클릭하고 아무런 설정 변경없이 할당을 해준다.
유데미 인스타 클론코딩 강의에서 이렇게 함.. 이유는 모르겠지만, 그렇게 중요하지 않은 듯 하다.
짜잔, 탄력적 IP 주소가 생성된 모습을 볼 수 있다.
그럼 여기까진, 탄력적 IP만 생성해둔 상태고, 생성한 탄력적 IP를 우리의 EC2 서버와 연결해준다.
지금 같은 상황을 번호판은 만들어 졌지만, 차에 붙인 상황은 아닌 것과 같다. - 번호판 나홀로,,, 탄력적 IP 나홀로,,,
위에서 연결 버튼을 클릭하면, 할당한 탄력적 IP를 우리가 이전에 구축한 EC2 서버와 연결지어 지는 것이다.
결과적으로 EC2의 탄력적 IP가 할당되었다.
RDS 구축
RDS는 빙글님께서 만들어 주신 RDS를 이용하신다고 하심.
그래도 언젠가는 도움이 될 RDS 구축하는 방법에 대한 기록을 남겨두도록 하겠다.
스토리지 자동 조정 활성화 버튼 체크박스 제거
체크하면, 용량초과하면 유료로 전환됨.
초기 데이터베이스를 AWS에서 자동으로 생성해주는 옵션이 있음.
편하니깐, 이용한다 ~~
배포 셋팅 끝, 배포 시작
우선 위의 과정을 거쳐
배포할 준비는 끝났다.
이제 구축한 AWS EC2 서버에 배포만 하면된다.
그 전에 우리가 만들고 있는 서버 API는 nodemon을 기반으로 실행되고 있었다.
nodemon은 가동 중인 서버를 중단시키지 않고, 서버의 수정 내역을 추가할 수있다.
API 개발할 때 혁신아이템 같았다 ㅋㅋ.. 원래는 중단시키고 저장하고 실행시켜야 했음..
하지만, nodemon을 내리고 AWS EC2에 올려야 된다고 빙글님께서 말씀해주셨다.
이유는 아직 잘 모르겠지만, AWS EC2의 pm2 무중단 서버 가동시스템을 이용하기 때문이 아닐까? 추측해본다.. ㅎㅎ
어쨋든, 내게 주어진 임무는 nodemon을 내리고 배포를 시작하는 것이다.
nodemon 내리기
우선 nodemon을 내리기 위해 node 서버를 실행시킬 때 사용하는 코드를 보면서, 구글링을 했다.
원래는 아래 명령으로 서버를 가동했다.
$ npm run dev
nodemon을 이용해 서버를 가동하면 다음과 같이 실행된다.
nodemon을 이용하면, 중간 중간에 서버 수정내역을 업데이트하면 중단시키지 않고 가동된 채 수정내역이 반영하게 된다.
그래서 이렇게 꿀 같은 nodemon을 내리고 일회성 서버 가동 서비스로 바꿔야 했다.
서버 실행과 관련된 설정은 package.json 에서 볼 수 있었다.
package.json 파일
package.json 파일에서 "dev"에 서버 실행 코드가 할당되어 있었던 것이다.
난 이때까지 $npm run dev 명령이 빙글님께서 따로 설정해두신 서버 실행 모듈인 줄 앎ㅋㅋㅋㅋㅋ..
그럼 "dev"가 서버 실행 요약어 이니, 이 부분을 nodemon으로 실행하는 게 아니라,
babel-node로 실행하도록 바꾼다. - babel-node는 ES6 구문, commandJS 구문 동시에 사용가능
이제 모두 끝났다. nodemon으로 실행하는 것을
bable-node로 실행시켜 nodemon의 무중단 서비스를 제거해냈다.
터미널에서 다음 명령을 실행하자
$ npm run dev
짜잔~, nodemon으로 서버가 가동되지 않고
bable-node로 서버가 가동하고 있는 모습을 볼 수 있다.
nodemon 내려서 일회성 서버 가동하기 성공이다. - EC2 서버에 올려 pm2를 사용하기 위함
nodemon을 내리면서 겪은 시행착오들
원래는 nodemon 대신, node로 서버를 가동시키려고 했다.
왜냐, 내가 알고 있는 node.js 서버 가동하는 방법은 node 밖에 모르기 때문이다..ㅎㅎ
어쨋든 node로 서버 가동을 시도했지만,
겪은 시행착오를 요약하자면 다음과 같다.
어려웠지만, 좋은 경험인 듯 ㅎㅎ..ㅋㅋ
node로 가동을 하면, import 문이 할당되지 않아 import is not defined 에러가 발생..
그리고, import문을 할당하기 위해 package.json에서
"type": "module" 코드를 추가해서
module에 있는 import문을 참조할 수 있도록 했다.
그러더니..ㅋㅋㅋ 이번엔 require 문이 할당되지 않아 require is not defined 에러가 발생했다.. ㅋㅋㅋ
하나 해결하면 다른게 에러가 생기네 ? ㅋㅋ
어쨋든 여러 번의 구글링으로 원인은
node.js 내장 함수인 require문과 외부 모듈인 import를 동시에 사용하려 해서 생긴에러고,
import 문을 사용하면 ES 구문으로 대체되고 따라서, 기존 node.js 문은 무시되는 형태인 듯하다.
따라서, node로 서버를 가동시키기 위해선 node.js 구문, ES 구문 둘 중하나만 사용해야 되는 듯..
여기서 동시에 사용하는 방법을 고민하다가 빙글님께 여쭤보아 해결했다.
해결은 간단했다.
node로 실행하는 게 아니라, babel-node로 실행하면 되었다.
babel-node가 정체가 먼데?
아직까지 나도 잘 모르겠다 ㅋㅋㅋㅎㅎ..
그래도 대략적으론,
옛날구문과 현재 구문을 동시에 사용해서? ES구문 (import)와 commandJS 구문 (require)을 동시에 사용할 수 있게 해주는 것 같다.
babel에 대해선 빙글님, 오리님께 기회가 될 때 여쭤보기로 하고 다음 작업으로 넘어간다.
개발 데드라인이 하루남은 건 안비밀.. ㅋㅋ..
init.js (서버 실행 파일) 에서 포트 리스닝 설정
우선 포트 리스닝 설정은 많은 비하인드 스토리가 존재한다.
여기선 포트 리스닝 설정하는 방법에 대해서만 요약해서 블로깅하겠다.
포트 리스닝 설정을 모르고 있어서 3시간 정도 혼자 끙끙앓았음 ㅋㅋ...
어쨌든, 포트 리스닝 설정을 init.js 에 설정해준다. - 보충 설명 (little TMI ^ . ^)
init.js 가 서버 실행 파일이기에 해당 파일에서 포트 리스닝을 수행해
EC2 서버에 init.js에서 지정한 포트를 리스닝 상태로 만들어 주는 것이다.
- LISTENING(접속 대기) :사용자 PC가 해당 포트정보를 통해 외부에서 접속할 수 있도록 열려 있다는 의미이다.
정리하면, 포트가 리스닝 상태가 되도록 init.js에서 포트 리스닝을 수행했다.
그럼 포트 리스닝을 해보자
아래와 같이 서버 실행 파일인 init.js에서 express 모듈을 가지고 온다.
express는 node.js 웹 프레임워크인 듯하다. -> 아직은 정확히는 잘 모르겠다.
# init.js
const app = require("./config/express");
import "dotenv/config";
const { logger } = require("./config/winston");
다음으로 포트 리스닝할 포트 번호를 변수로 선언한다.
const hostname = '3.38.55.57';
const PORTNUM = 3000;
const PORTNUM_HTTP = 80;
그런 다음, 인스턴스로 받아진 app로 앞에서 선언한 포트 번호에 대한 포트리스닝을 수행한다.
app는 express 모듈로 인스턴스가 정의되어 있다. - 대충 express 모듈 이용해서 포트 리스닝 수행하는 걸로 보인다.
const app = require("./config/express");
import "dotenv/config";
const { logger } = require("./config/winston");
// AWS EC2 포트리스닝
const hostname = '3.38.55.57';
const PORTNUM = 3000;
const PORTNUM_HTTP = 80;
app().listen(PORTNUM, () => {
});
app().listen(PORTNUM_HTTP, () => {
logger.info(`✅Start Express Server on port ${PORTNUM} `);
console.log(`✅ Check it out! at here --> http://localhost:${PORTNUM}/`);
});
이제 여기까지 하면, 로컬환경 서버 API -> 배포용 서버 API로 만들어졌다고 볼 수 있다.
그리고, 배포용 서버 API를 orgin's main 브랜치로 push를 수행해 EC2 서버에 clone할 수 있게 만들면 끝이다!
포트 리스닝하면서 겪은 비하인드 스토리
우선 여기서 할 얘기가 진짜 많은 느낌이 든다..
왜냐,, 포트 리스닝 설정이 있는지도 몰라서 3~5시간 헤맸기 때문이다 ㅎㅎ..ㅋㅋ
포트 리스닝 설정을 해야되는 지 몰라서 AWS EC2 인바운드 규칙에서
특정한 포트번호만 설정하면 클라이언트가 접근할 수 있다고 생각했다.
그래서 아래 웹사이트에서 EC2 IP와 인바운드 규칙에 추가한 포트번호를 입력해서
해당 포트로 들어가는지 확인을 했지만, 전혀 접속이 안되었다.
나는 도저히 이유를 알 수가 없었다.
AWS EC2에서 인바운드 규칙으로 특정 포트에 대한 모든 IP를 할당해줬는데
접근이 안되었기 때문이다.
그렇게..ㅋㅋㅋ 나는 슬랙에 물어보기도 하고 구글링을 3시간동안 했다....
솔직히 내가 생각한 대로 했는데도, 안되니깐 짜증이 나고 하기가 싫었다.. - 그치만 꼭 배포 성공하고 싶었기에...
해답은 nest.js 오픈채팅방을 통해서 알게되었다.
내가 겪고 있는 상황을 설명한 뒤 원인이 무엇인지를 질문을 했다.
AWS EC2의 인바운드 규칙을 80포트 3000포트를 설정해줬는데 3000포트, 80포트로 왜 들어가지지 않나요?
라고 질문을 했다.
A. 서버에서 80번 포트와 3000번 포트를 열어둔 상태인가요?
라는 답변을 얻었다.
첨에는 이해가 안되었다.
왜냐, AWS EC2에서 인바운드 규칙을 설정하면 모든게 다 설정되는 줄 알았다.
하지만, 난 잘못생각하고 있다는 걸 깨달았다..
AWS EC2에서 인바운드 규칙은 단지 클라이언트가 들어올 통로를 개방해준 것에만 불과하다는 것을...
그렇기에, 나는 EC2 서버에 클라이언트가 들어올 통로는 개방해줬지만, 개방된 곳에는 아무것도 연결하지 않았다는 사실을
위의 답을 얻고 혼자생각하며 내가 잘못 생각하고 있는 걸 고칠 수 있었다..
nest.js 오픈 채팅방은 많은 도움을 주는 듯,,
즉, 오개념을 올바르게 다시 정리하면
AWS EC2에서 인바운드 규칙으로 클라이언트가 들어올 입구인 포트를 개방해준다.
EC2 서버에서 클라이언트가 들어올 입구인 포트에 따른 리스닝을 수행한다. -> EC2 서버 내부의 개념인 것 같다.
(아마 하나의 포트에는 하나의 프로세스가 배정받는다. - 리스닝 원리)
그래서 포트 리스닝을 수행해서 클라이언트가 해당 포트로 들어와서 서버가 해당 포트에 리스닝된 프로세스를 이용할 수 있게 한다.
인바운드 규칙은 해줬지만, 포트 리스닝 안된 서버에 대해 비유를 해보면,
놀이공원 지도에 회전목마와 바이킹이 표시되어 있지만, 실제로 지도 위치로 가보면
회전목마와 바이킹이 없어서 이용하지 못하는 상황과 같다.
그래서, 아무것도 못하니깐
클라이언트가 포트(회전목마, 바이킹)에 요청을 보내도 응답없음(회전목마, 바이킹 못탐) 결과가 나오는 것이다.
비하인트 스토리 정리 & 느낀점
정리하면, 이번 사태가 발생한 이유는 오개념에 의해 발생했다고 볼 수 있다.
AWS EC2가 모든 걸 해준다고 믿고 있었기에 난 이에따라 배포를 진행하고 있어서 더 헷갈렸던 것 같다.
결국, 오개념에 들어서도 탈출할 수 있는 방법은 여러 사람과 대화하면서 내가 생각하고 있는 바가 맞는지를 점검하는게
지금으로써 최고의 대안인 듯하다..
따라서, 원인을 도저히 모를 때는 구글링보다 여러 사람에게 질문하면서 해결하자. - 오개념인 상태로 구글링은 그 안에서만 돌고 돈다..
EC2 서버에 배포 시작
우선 키페어 파일(.pem)이 있는 위치인 Desktop으로 가서 ssh 권한으로 서버에 접속하자!
일종의 관리자로서 서버에 접근한다고 보면된다.
$ sudo ssh -i "dongne-server.pem" ubuntu@ec2-3-38-55-57.ap-northeast-2.compute.amazonaws.com
그리고, 위에서 origin's main 브랜치에 배포용 서버 API를 push 해두었으니,
EC2 홈 디렉토리에 git clone을 진행한다.
$ git clone https://github.com/UnivDonguk22/dongne-server.git
git clone 후 $ ls 명령을 수행하면 main 브랜치가 복제된 모습을 볼 수 있다.
그리고 이제, 서버 API를 가동시키기 위해 npm 을 설치한다. - 서버 API가 node.js 기반이기 때문이다.
$ sudo apt install npm 명령이 바로 실행되지 않아서
apt-get을 update, upgrade를 해준다. - 컴퓨터 개론에서 배웠던 것들 ㅎㅎ..
apt-get update, upgrade를 수행을 완료한 뒤
$ sudo apt install npm 명령을 사용해 npm을 설치한다.
npm을 설치 완료한 뒤
git clone 된 디렉토리에서 $ npm i 명령을 수행한다.
서버 API에서 사용된 node_module을 package.json 파일을 참조해 다운로드를 하고 있다. - 정확히 맞는 지는 모르지만, 대충 그런느낌
다음으로
.gitignore 에 속한 파일 중 서버 구동에 필요한 파일인 .env 추가한다 - vim을 이용했다 컴퓨터 빛개론..
:wq! 로 저장하고
.env 파일이 생성되었는지 한번 확인해보자!
.env 는 숨김파일이므로, ls -a 로 확인해야함.
컴퓨터 빛개론..
다음 작업은 무중단 서비스인 pm2를 사용하기 위해 pm2를 설치한다.
pm2를 설치할 때 주의할 점은 sudo 관리자 권한으로 설치를 진행해야 한다. - 터미널에선 권한에 예민하다ㅎㅎ...
첨에 sudo 안쓰고해서 에러가 나옴.. 그래서 당황했지만, 요즘 성장한 나로썬 에러 메시지보고, sudo가 필요함을 깨닫.. ㅋㅋ
$ sudo npm install -g pm2
그럼 이제 진짜 배포만 하면 된다.
pm2를 이용해 무중단 서비스를 사용할거라,
아래 명령으로 서버를 실행한다.
init.js는 동네 서버 실행 파일입니다 ㅎㅎ..
$ pm2 start init.js
이렇게 하면 일반적인? 서버 API를 pm2 무중단 서비스로 배포를 성공했다고 볼 수 있다.
하지만,
나는 아니었다...ㅋㅋ
위의 명령을 수행하면 몇초 지나지 않아 서버 API가 다운 된다는 것을 볼 수 있었다.
그래서 $ pm2 log 명령으로 로그 메시지를 보니,
import 문을 불러올 수 없다는 에러 로그가 찍혀있었다..
위에서 블로깅 했지만, import 문은 ES6 구문으로 commandJS 구문과 원초적으론 동시 사용이 불가능 했다.
결국 위에서 겪은 에러를 EC2 서버에서 API를 실행할 때 생긴 것이다.
위에서의 에러는 node로 실행해서 ES6 구문과 commandJS 구문이 동시 사용이 불가해 ES6 구문을 읽지 못하는 에러인 것을 토대로
ES6 구문과 commandJS 구문을 동시에 실행시킬 수 있는 건 babel-node 라는 것을 알 수 있었다.
그래서 pm2는 기본적으로 node로 실행한다고 추측했다. - ES6 구문을 읽어들이지 못하기 때문 ㅎㅎ..ㅋㅋ
그럼, ES6 구문과 commandJS 구문을 동시에 읽어 들일 수 있는 babel-node로 실행권한을 바꾸면 되겠다는 생각을 했다.
그래서 난 여러 번의 구글링을 했다..
나랑 똑같은 문제를 직면한 사람도 발견할 수 있었고,,,ㅋㅋㅋ - 아래의 stack over flow는 해결책으로는 사용하지 않았다.
난 아래의 github를 통해 문제를 해결할 수 있었다.
아래 명령으로 pm2의 옵션 인자를 추가하면 pm2가 babel-node로 실행하고 있다는 것을 실행결과를 통해 알 수 있었다.
$ pm2 start init.js --interpreter ./node_modules/.bin/babel-node
pm2 옵션인자인 --interpreter에 대한 설명은 다음과 같다.
즉, babel-node 모듈을 실행 경로로 지정하여 babel-node로 서버 API를 실행한다는 사실을 알 수 있다.
모든지 안되면, 되도록,, 기회는 만들어 가는 법..
따라서 나는 pm2로 서버를 가동시키는 것에 성공하여
최종적으로 배포를 성공하게 되었다. 🎉🎉🎉
배포 끝, 배포하며 느낀점
솔직하게 느낀점을 말하자면, 너무 힘들었다.
왜냐, 답이 추상적이고 유추하며 코드 명령들을 사용해야 되기에 힘들었다.
하지만, 그만큼 혼자 유추하며 코드 명령을 사용하는 방법에 대해 많이 터득한 것 같다.
배포를 하면서 네트워크에서 오개념 (AWS EC2 인바운드 규칙과 포트 리스닝 등) 들을 제대로 된 방향으로 다시 공부할 수 있어서 좋았다.
그리고, 배포를 하면서 가장 많이 배우고 느낀점은 터미널 환경에서 사용하는 명령구조와 터미널에서 제공되는 에러 메시지 의도를 어떻게
파악하는 지를 많이 배우게 되어 나중에 똑같은 상황을 접하더라도 더 쉽고 빠르게 해결할 수 있겠다라는 생각을 하게 되었다 ㅎㅎ
마지막으로 배포를 성공적으로 끝맞친 후 나도 충분히 혼자의 힘으로 무언가를 충분히 만들어 낼 수 있고 불가능은 없겠구나 라는 자신감을 얻게 되었다 ㅎㅎ.. 추상적일지라도 요즘 나는 계속 성장하는 느낌이 들어서 너무 행복하다.
협력하면서, 성과를 계속해서 쌓아나간다는 자체만으로도 행복하고 좋은 것 같다.
그리고, 이렇게 좋은 배움과 깨달음을 얻도록 도와준 동네 백엔드 팀원분들에게 감사의 말씀을 드린다.
'🌤 프로젝트 > UMC 2기: 동네' 카테고리의 다른 글
[팀 프로젝트] AWS EC2 배포를 수행하다 - 팀원들이 나에게 양보해준 뜻깊은 기회 -2 (0) | 2022.08.20 |
---|---|
[팀 프로젝트] node.js DB의 null값 Validation 처리하기 (Cannot read properties of undefined (reading 'status')) (0) | 2022.08.17 |
[팀 프로젝트] 프로젝트하면서 Git과 친해지기 - 4 (git pull로 원격저장소 브랜치 가져오기) (0) | 2022.08.04 |
[팀 프로젝트] 프로젝트하면서 Git과 친해지기 - 3 (Git의 작동원리) (0) | 2022.08.03 |
[팀 프로젝트] 프로젝트하면서 Git과 친해지기 - 2 (Git은 repository로 소통한다.) (0) | 2022.08.03 |