[Fly.io] Fly.io로 node.js 서버 배포하기

박기영·2023년 1월 22일
5

배포

목록 보기
3/3

프로젝트의 끝. 배포.
Heroku의 프리티어 지원이 끝나면서 많은 분들이 대체할 것을 찾고 있고,
그렇게 찾아낸 것이 Fly.io이다.
물론, 완전 무료는 아니다. 어느 정도까지는 무료지만, 오버하면 요금이 발생하며,
그 것을 막아주는 장치가 없기에 조심해야한다.
아무튼, Fly.io로 배포하면서 하루 종일 삽질했던 사람으로서,
다른 분들이 필자같이 시간 낭비를 하지 않으셨으면 하는 바람을 담아 이 글을 작성해본다.

필자는 맥북 유저입니다. 따라서 homebrew를 통해 설치를 진행했습니다.

배포 전 주의 사항

package.json에서 scripts를 잘 살펴보자.

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "backend": "nodemon app.js", // 보통 서버를 nodemon으로 계속 돌리기 때문에
    "start": "node app.js" // 가끔 이 스크립트를 까먹으시는 분들이 계신다
  },

Fly.ionpm run start를 기본으로 하고 있는 것으로 보인다.
굳이 공식 문서에서 설명을 찾지는 않았는데,
경험상 start 부분을 작성하지 않고 배포하시면 에러가 계속 뜰겁니다.
배포 시작 전에 scripts를 미리 확인해주세요!

Fly.io 설치하기

$ brew install flyctl

당연하게도, Fly.io를 사용하기 위해 설치를 해줘야한다.

Fly.io 회원 가입 및 카드 입력

회원가입(로그인) 부분은 커맨드 입력으로 할 수 있기는 한데..필자는 그냥 홈페이지에 직접 들어가서 했다.
홈페이지에서 깃허브로 로그인을 할 수 있기 때문에 이 부분은 설명이 필요없다고 생각한다...!
로그인을 하시면, Dashboard로 가실 수 있는데, 여기서 카드 등록을 해줘야한다!

왜냐, 위에서도 말했지만 Fly.io는 어느 정도는 무료이지만, 오버하는 순간 과금이 되기 때문이다.

프로젝트에 Fly.io 설치하기

회원 가입과 카드 등록을 마쳤으니, 이제 프로젝트 배포를 진행해보자.

우선, 가장 먼저 해야하는 것은 본인의 프로젝트 폴더에서 Fly.io를 실행하는 것이다.

$ cd 본인_프로젝트_폴더_이름

프로젝트 폴더에 접근했으면, 아래 코드를 입력하자!

$ flyctl launch

아래와 같이 ?가 차례대로 나오면서 입력을 요구할 것이다.

app name

참고로, app name을 입력할 때,
숫자와 소문자 알파벳, 그리고 대쉬(-)만 사용 가능하니 참고해주세요!
다른게 포함되면 아래와 같은 에러가 발생하면서 종료됩니다.

region

대략 배포 지역을 선택하라는 것 같은데, 대한민국은 보이지 않는다.
필자는 가장 가까운 지역인 일본-도쿄를 선택했다.
이 선택으로 인해 발생하는 문제는 없는 것 같다!(아직까지는 전혀 문제가 없다)

Postgresql, Redis

DB 설정을 위한 단계인듯한데, 필자는 mongoDB를 사용하므로 전부 다 No!

deploy now?

바로 배포를 시작하겠냐고 물어보는데, 당연히 Yes!

이제 주르르륵 터미널에 뭔가가 작성되면서 배포가 된다.

launch 이후 상황

위 단계까지 끝나면, 배포한 프로젝트 폴더에 3개의 파일이 생성된다.

.dockerignore, Dockerfile, fly.toml 파일이 생성된다.

필자의 프로젝트가 작고 하찮은 것이라 그런건지 모르겠지만,
fly.toml을 제외하고는 신경쓰지 않아도 문제가 없다..!

그런데...저 파일들이 생성되는 것까지는 좋은데,
필자는 에러가 발생했다.

error - unhealthy allocations

역시 배포다. 한번에 되는 일이 없다.
배포하며 참고했던 다른 분들의 글을 보면 다 잘 되는데, 나만 또 안된다.

이 문제가 무엇이냐?
두 번째 줄에 보면 이유를 설명해준다.
unhealthy allocations. 뭔가 할당이 잘못된 모양이다.

무슨 에러이며, 어떻게 해결하는걸까?
친절하게도 트러블슈팅 가이드라고 사이트까지 알려준다. 들어가보자.

음..아무래도 사용자들이 보편적으로 겪는 문제에 대한 해결책을 적어놓은 것 같다.
문서에서 말하는 것들을 하나씩 체크해보자.

첫 번째 방법(실패)

internal_port라는 key가 보인다. 기본값으로 8080이 들어가 있는데,
Fly.io가 이 포트를 기준으로 실행을 한다는 것으로 보인다.

공식 문서에는 Dockerfile에 무엇인가를 적으라고 하는데,
필자는 그렇게 했더니 적어 준 것에 대한 에러가 발생했다.
즉, 해결책이 아니었다.

두 번째 방법(성공)

음..공식 문서를 더 아래로 내려보니 Host Checking이라는 글이 있다.

보통 nodejs를 사용하면, 어떤 포트를 사용하는지 알려주기 위해 다음과 같은 코드를 적을 것이다.

const port = process.env.PORT || 8080;

app.listen(port);

여기에, 인자를 추가해주라고 한다.

const port = process.env.PORT || 8080;

app.listen(port, "0.0.0.0");

이렇게 바꿔주자. 필자는 이걸 통해서 문제가 해결되었다.

결국, unhealthy allocations 에러는

IP 주소를 명시하지 않은 것 때문에 발생한 것 같다.
또한 8080번 포트의 사용을 꽤나 강조한다.

주의 사항

fly.toml 파일에서 환경 변수 설정을 위해 [env]를 건드린 분이 계실 것이다.
그 중에서는 port를 본인이 원하는 것으로 설정하신 분도 계실 것이라 생각한다.
그런데, 첫 번째 방법에서 봤다싶이 Fly.io8080번 포트를 기본으로 한다.
물론, 본인이 internal_port를 직접 변경해도 되겠지만(아마도),
굳이 그럴 필요가 없으시다면, 8080 포트를 기준으로 본인의 포트를 바꾸는게 더 나아보인다.

필자는 위 이미지 처럼 [env]에서 PORT를 설정했다가 에러가 난 적도 있기 때문에, 이 말을 남긴다.
현재는 PORT 설정을 지웠고, 초기 fly.toml 파일 그대로 [env]를 돌려놨다.
초기 상태에서는 [env] 아래에 아무것도 입력되어있지 않았다.
물론, 에러 파악하느라 이것저것 고쳐서 이게 원인인지는 확실하지 않지만,
정 포트를 변경하고 싶다면, 우선 배포를 완전히 해놓고 수정하는게 정신건강에 좋을 것 같다.

환경 변수(혹은 secrets) 설정

백엔드를 구현했으면 DB 관련, 토큰 관련 정보를 숨겨야한다.
이런 상황은 환경 변수 설정으로 해결을 하는데,
Fly.io에서는 위에서 이미 봐서 알 수 있듯,
fly.toml 파일의 [env] 아래에 입력하는 것으로 쉽게 설정 가능하다.

그런데! 공식 문서의 환경 변수 설정 방법에서 민감한 정보는 secrets를 활용하라고 한다..!

[env]에 직접 입력하는 것은 위험하기 때문인 것으로 생각된다.
그래서 필자는 전~부 다 secrets로 설정을 해줬다.

secrets를 설정하는 방법은 다음과 같다.
참고로, 터미널은 여전히 프로젝트 폴더를 가리키고 있다.

$ flyctl secrets set 환경_변수_이름="환경_변수"

여기서 중요한 것은 환경 변수 값을 ""로 꼭 감싸줘야한다는 것이다.

공식 문서에서는 Env values can only be strings라고 적혀있는데,
아무래도 이게 secrets에도 적용되는 것 같다.

물론, secrets 설정 관련 문서에서는 ""가 없는데,
필자는 "" 없이 사용했다가 에러가 났다.

또한, 필자를 아~주 헷갈리게 만든 말이 있다.
지금 와서 살펴보니 해석을 잘못했던 것 같다..ㅎㅎ...

These will be set as $DATABASE_URL environment variable within your application processes

환경 변수는 process.env로 접근하는 건 알겠는데,
secrets는 또 다른 접근법이 있는건가?? $를 꼭 붙여주라는건가??
secrets를 설정하면 [env] 내에 $가 붙은채로 저장이 된다는 건가??
라고 생각해버렸다... 진짜 삽질이었다.

결국 사용법은 똑같았다. 이게 무슨 말이냐?
에를 들어보자. 아무렇게나 작성해봤다.

// 환경 변수와 secrets 사용 시

const DUMMY = `${process.env.ENVIRONMENT_VAR}/?fak#2212%${process.env.SECRETS_VAR}`;
// fly.toml

[env]
  ENVIRONMENT_VAR = "hahaha"
// 터미널

$ flyctl secrets set SECRETS_VAR="hohoho"

앞쪽에 있는 것은 [env] 아래에 작성한 것을 사용,
뒤쪽에 있는 것은 secrets set으로 설정했던 것을 사용한 것이다.

사용법에 전~혀 다른게 없다.
필자는 사용법이 다른 줄 알고 계속 삽질했다.

주의 사항

방금 봤듯이 process.env로 환경 변수와 secrets에 동시에 접근이 가능하다.
그래서 필자는 만약 두 개를 동시에 사용해야하는 경우라면
이름이 중복되는 것이 없도록 관리할 것 같다.
혹시 모를 충돌이 걱정되기 때문이다..
정확한 작동 원리를 알 수가 없어서 일단은 그렇게 하는 것으로..!

환경 변수 및 secrets 확인하기

본인이 설정한 환경 변수는 fly.toml에 적혀있을 것이고,
secrets는...???? 어디있지?
이는 본인 프로젝트의 Dashboard에서 확인 가능하다.

왼쪽의 메뉴바에서 Secrets 탭에 들어가면 CLI로 설정했던 secrets들이 전부 나온다.

물론, 터미널로 확인하는 방법도 있다.

$ flyctl config env

이렇게 커맨드를 입력해주면, 아래와 같이 환경 변수가 출력된다.
언제 생성했는지도 보이는듯 하다!

앞서 필자는 secrets에 모든 것을 다 넣었기 때문에,
Environment variables, 즉, [env]에는 아무 것도 없는 것을 확인할 수 있다.

자, 이렇게 환경 변수까지 잘 처리를 해주면 배포가 잘 될 것이다.(아마도)
Fly.io는 신기하게도 secrets를 하나씩 설정할 때마다 deploy가 자동적으로 실행됐다.

따라서, 아래 이미지 처럼 보이면 정상적인 배포가 된 것이다.

혹시나, 본인이 입력한 secrets가 아니라 이상한 문자열이 등장하더라도 놀라지말자.

보안상 이유로, 입력한 문자열을 그대로 보여주지 않게끔 되어있기 때문이다.

launch 커맨드는 일회용

위에서 배포의 시작점이 되는 launch 커맨드에 대해서 알아봤었다.
그런데, 이 커맨드는 처음 한 번 배포시에만 사용 가능하고, 그 뒤에 변경 사항을 다시 배포하기 위해서는

$ fly deploy

이렇게 커맨드를 사용해야한다.
이 때도 마찬가지로,

이렇게 보이면 배포가 성공적으로 잘 된 것이다.

DB에 IP 연결하기

여기까지 왔으면, 서버가 켜지긴 할 것이다.(보통 Hello world를 출력되지않으시나요?ㅎㅎ)

그런데 문제가 있다. 결국, API 통신을 위해서는 DB와의 연계가 필요한데,
모두들 잘 알고 계시겠지만 DB는 IP 주소 등록을 해줘야지만 해당 IP에서 접근할 수 있다.
물론, 필자는 MongoDB만 사용해왔기 때문에 다른 DB는 다를 수 있습니다.

아무튼, 내가 배포한 이 백엔드 서버가 어떤 IP 주소를 가지고 있는지 확인하고,
그 것을 DB 쪽에 등록해줄 필요가 있다.
IP 주소 확인은 간단하다.

배포한 프로젝트의 Dashboard에 들어가면 Overview라는 페이지가 맨 처음 보인다.
거기에 IP 주소가 적혀있다. 필요한 것으로 사용해 주시면 될 것 같다.
필자는 붉은 테두리로 감싸진 IP 주소를 사용했다.

shared v4

혹시나 IP가 v4가 아니라 shared v4인 것이 이상하게 느껴지시는 분이 계실 수도 있다.
shared v4에 관련하여 공식 문서에는 다음과 같은 공지 글이 올라와있다.

대략, 이 세상에서 접속할 수 있는 IPv4 주소가 점점 부족해지고 있기 때문에,
기본적으로는 하나의 앱에만 종속되는 v4 주소가 아니라, 공유되는 v4 주소를 할당해준다는 것 같다.

글에 적혀 있듯이, 본인의 프로젝트 폴더에 접근하여 터미널에서 아래와 같이 입력하면

$ fly ips allocate-v4

다음과 같이 dedicated IPv4 주소를 얻을 수 있다.

에러 발생!!!

MongooseServerSelectionError: Could not connect to any servers in your MongoDB Atlas cluster. One common reason is that you're trying to access the database from an IP that isn't whitelisted. Make sure your current IP address is on your Atlas cluster's IP whitelist: https://docs.atlas.mongodb.com/security-whitelist/

IP 주소를 DB에 추가하고 다시 배포했더니 위와 같은 에러가 발생했다..
그런데 문제는...저 에러가 IP 주소를 추가하지 않았기 때문에 발생하는 에러라는 것이다.

??????

그래서 우선은 어디서나 접근할 수 있도록 0.0.0.0/0를 사용하여 에러를 해결했다.

이 부분에 대해서, 도무지 이해가 되지 않아 여러 방법을 찾아봤다.
Fly.io 커뮤니티에도 필자와 같은 상황인 사람이 많았고,
그 많은 질문글에는 이렇다할 해결책이 없었다..

혹시나 원인을 알고 계신 분이라면 꼭 알려주셨으면 좋겠습니다 ㅠㅠ

DB에 접속하기 위해서는 id, password를 입력해줘야하기도 하고,
이 값들은 secrets를 활용해서 숨겨놨기 때문에,
IP 접근 허용을 0.0.0.0/0으로 해도 엄청난 문제는 없을 것 같긴하지만..여전히 찝찝하다...

아무튼 이렇게, DB에 IP 연결까지 완료되었다!(완료가 아닌 것 같지만)

API 경로 변경해주기

항상 배포 후에 해줘야하는 것이 있다.
개발할 때 사용한 http://localhost:5000 등등 로컬 주소를 배포 주소로 변경해줘야한다.

방금 위에서 본 이미지에서 Hostname에 본인이 배포한 서버의 주소가 있을 것이다.
이를 프론트쪽에 적용해주자!
물론, 백쪽에도 CORS 에러 방지를 위해 프론트엔드 배포 주소를 입력해주자!

// 프론트엔드

fetch("백엔드_배포_주소/api_통신_라우트/api_통신_라우트/..")
// 백엔드

// CORS 해결
app.use((req, res, next) => {
  res.setHeader(
    "Access-Control-Allow-Origin",
    "프론트엔드_배포_주소"
  );

  // ... //
});

이제 끝이다.
DB 연결에서 찝찝한 점이 여전히 남아있지만, 배포는 완료되었고,
환경 변수 설정을 통해 서버와 DB의 접근을 가능한 범위 내에서는 안전하게 지키기 위한 노력도 했다.

필자가 겪은 일들을 나열하다보니, 정리가 안된 느낌이 많지만
혹시나 헤매는 분들이 계시다면 이 글이 어떤 방식으로든 도움이 되었으면 좋겠다.

참고 자료

mamonde456님 블로그
summereuna님 블로그
zeddios님 블로그
Fly.io 공식 docs

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글