본문으로 건너뛰기

Fly.io를 활용한 도커 컨테이너 배포

· 약 11분
Jake Son

호스팅 서버로 이용중인 Heroku 가 11월 이후로 더 이상 무료플랜을 지원하지 않아 이전부터 눈여겨본 Fly.io 로 이전한 과정을 담고있다.

Fly.io​

Fly.io는 도커 컨테이너를 쉽게 배포할 수 있는 플랫폼이다. 기본적인 도커 지식이 있다면 쉽게 서버를 배포할 수 있다. Trial Plan 이 있어 회원가입만 해도 서버스를 무료로 이용할 수 있으며 결제정보를 추가하면 Free Plan 으로 변경되며 조금 더 많은 사용량을 얻을 수 있다. 두 플랜의 차이점은 아래 사진을 참조한다.

pricing

정보

추후에 달라질 수 있는 내용이라 공식 사이트에서 직접 확인하는 것을 권장한다.

flyctl 설치​

배포 과정은 대부분 flyctl 로 불리는 cli 도구를 활용해 터미널 환경에서 진행된다. Mac 에서는 homebrew 가 설치된 환경이라면 아래 명령어를 통해 설치할 수 있다.

brew install flyctl

없는 경우라면 다음 명령어를 활용한다.

curl -L https://fly.io/install.sh | sh

Windows 라면 PowerShell 에서 다음 명령어를 활용한다.

iwr https://fly.io/install.ps1 -useb | iex

회원가입 & 로그인​

서비스를 이용하기 위해서 회원가입이 필요하며 아래 명령어를 수행하면 자동으로 브라우저가 열리면서 회원가입을 진행 할 수 있다.

만약 이미 회원가입을 했다면 로그인 과정을 진행하도록 한다.

flyctl auth login

Github 계정을 활용하거나 이메일 주소를 입력해 회원가입을 진행하며 다음 과정에서 결제정보를 입력하거나 스킵한다.

login

설정 파일 생성​

배포를 원하는 서버의 코드가 있는 저장소 위치로 이동해 다음 과정을 진행한다. 배포에 대한 설정은 fly.toml 파일을 통해 이루어지며 다음 명령어를 활용해 해당 파일을 쉽게 생성할 수 있다.

flyctl launch

해당 명령어는 현재 디렉토리의 코드를 분석해서 어떤 빌드도구를 활용할지 판단해 설정파일을 생성한다. 따라서 간단한 코드인 경우에는 도커지식이 없어도 바로 서버를 배포할 수 있는 장점이 있다.

정보

이번 포스트에서는 pnpm 을 패키지 매니저로 사용하고 nest.js 를 사용하는 다음 저장소를 기준으로 설명한다. https://github.com/bae-no/bae-no-server

flyctl-launch

명령어를 실행하면 여러 선택지를 물어보게 되는데 상황에 따라 적절할 항목을 선택하도록 한다.

  • App Name: 배포 App 이름, 원하는 이름으로 설정한다.
  • Region: 컨테이너가 올라갈 지역, 서버 이용자에게 가까운 지역을 선택하도록 한다.
  • Set up Postgresql database: postgresql 을 서버를 fly.io 로 만들어 사용할 목적이라면 yes 를 선택한다.
  • Deploy: 바로 배포를 진행할 것인지 여부, 설정파일을 확인하기 위해 No 를 선택한다.

설정파일 확인​

자동 생성한 fly.toml 파일을 살펴보고 상황에 따라 수정해야 한다. 이번 포스트에서 사용한 저장소에서는 pnpm 을 패키지 매니저로 사용하는데 heroku/buildpack 이 지원하지 않기에 builder 라인을 삭제했다 이 경우에는 직접 저장소 root 에 Dockerfile 를 생성해 빌드설정을 진행하게 된다.

fly.toml
app = "bae-no-server"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[build]
# 빌드에 사용할 도커 이미지
# 만약 직접 Dockfile 를 만드는 경우라면 아래 라인을 삭제한다
builder = "heroku/buildpacks:20"

[env]
PORT = "8080" # 환경변수 설정

[[services]]
http_checks = []
internal_port = 8080 # 서버가 사용하는 포트 번호로 변경해준다
processes = ["app"]
protocol = "tcp"
script_checks = []
정보

Nest.js 의 기본 서비스 포트는 3000 이지만 fly.toml 의 internal_port 를 3000 으로 바꾸지 않았다. 그 이유는 main.ts 에서 process.env.PORT 를 사용해 포트번호를 설정하도록 코드를 수정하였고 fly.toml 의 env 설정에서 PORT 를 8080 으로 설정하였기 때문이다.

await app.listen(process.env.PORT || 3000);

Dockerfile 생성​

대부분의 서버 코드는 배포전에 빌드과정이 필요한데 heroku 에서는 이를 buildpack 을 통해 수행한다. fly.io 는 이 개념을 채택해 사용하기에 flyctl launch 를 실행하면 heroku/buildpack 이 builder 옵션에 들어가는 것을 볼 수 있다.

다만 해당 buildpack 은 pnpm 을 지원하지 않기에 커스텀 buildpack 을 사용하거나 직접 Dockerfile 을 만들어야한다. 이번 포스트에서는 Dockerfile 을 만드는 방법을 선택하였다.

FROM node:16-alpine

RUN npm install -g pnpm

COPY .npmrc package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

COPY . .
RUN npm run build

EXPOSE 3000
CMD [ "node", "dist/apps/app/src/main" ]

해당 파일을 보면 직접 패키지 설치 + 빌드 + 실행명령어를 넣어준 것을 확인할 수 있다.

secret 설정​

대부분의 경우 서버를 실행하기 위한 환경변수가 필요한 경우가 있다. fly.toml 의 env 설정을 통해 환경변수를 설정할 수 있으나 데이터베이스 접속정보 같은 민감한 내용을 넣어 공개 github 저장소에 올리는 것은 위험하다. fly.io 에서는 이러한 정보를 저장하기 위해 secret 을 제공한다.

secret 은 내부 값들을 암호화해서 저장하며 컨테이너 내부에서는 복호화한 값으로 참조할 수 있게 만들어준다. flyctl 을 통해 secret 을 관리할 수 있으며 flyctl secret set 을 활용해 여러 key, value 를 설정할 수 있다.

flyctl secret set SECRET=VALUE SECRET2=VALUE2 ...

설정한 값들은 아래 명령어를 통해 확인할 수 있으며 value 부분은 암호화된 것을 확인할 수 있다.

flyctl secrets list

NAME DIGEST CREATED AT
SECRET 5q97fbe12c037e0359f7a2c43827ca27 13h5m ago
SECRET2 34173cb3zf07f89dd2ebc2ac9128303f 12h59m ago

배포​

이제 모든 설정을 마치고 아래 명령어를 수행해 배포를 진행한다.

flyctl deploy

출력되는 메시지들을 살펴보면 도커 이미지를 빌드할 때 자주 보게되는 내용을 확인할 수 있다. 이는 도커가 설치된 외부 서버를 활용하는 경험을 제공해준다. 실제로 fly.io 대시보드 에 들어가면 빌드용도의 새로운 app 이 생성된 것을 확인할 수 있다.

dashboard

위와 같은 나만의 build app 을 가질 수 있는 장점은 docker layer 캐시를 활용할 수 있다는 점이다. 첫 배포시에는 캐시가 없으므로 Dockerfile 에 지정된 모든 과정을 진행해 시간이 걸리지만 이후에는 수정된 스탭만 수행하기에 배포시간이 단축된다. 배포 이후 대시보드의 overview 를 탭에서 서버 url 을 확인할 수 있으며 정상적으로 접속되는지 확인한다.

hostname

Github Action 연동​

이제 Gihub Action 을 사용하여 main 브랜치에 푸시가 발생하면 자동으로 fly.io 로 배포를 진행하도록 설정해보자.

API Token 발급​

다음 명령어를 통해 api token 을 발급한다. (로그인 상태이어야 함)

flyctl auth token

Github Secret 에 TOKEN 추가​

발급한 토큰을 복사한 후 Github 저장소의 Settings -> Secrets -> Actions -> New secret 을 선택해 새로운 secret 을 생성한다. Name 은 FLY_API_TOKEN 이며 Value 는 발급받은 토큰을 입력한다. 마지막으로 Add secret 을 누르면 완료된다.

add-token

Github Action 설정​

이제 다음과 같은 설정파일을 만든 후 푸시하면 앞으로 main 에 커밋이 푸시되면 배포가 진행된다.

.github/workflows/main.yml
name: Fly Deploy

on:
push:
branches:
- main

env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only

deploy