도커 컨테이너 5분 만에 무료로 배포하기(feat. fly.io)

도커 배포라는 또다른 장애물

도커(Docker)와 컨테이너를 사용하면 어디서든 실행 가능한 컨테이너 이미지를 만드들 수 있습니다. 하지만 도커 이미지를 배포하는 과정에는 전혀 다른 차원의 어려움이 존재합니다. 바로 관리형 도커 배포 시스템이 매우 복잡하다는 점인데요. 운영 환경에 사용하는 AWS의 EKS나 ECS, GCP의 GKE나 Cloud Run, Azure의 AKS, Container App 등을 사용해보려고 하면, 도커 지식보다도 알아야 할 게 더 많게 느껴지곤 합니다. 이렇게 복잡했던 도커 배포 환경에 fly.io가 도전장을 내밀었습니다. 도커 이미지만 존재한다면 채 5분도 걸리지 않아 배포를 할 수 있다고 말이죠.

노트
도커와 컨테이너

이 글에서는 도커 기초에 대해서는 다루지 않습니다. 도커나 컨테이너가 아직 익숙하지 않다면 다음 페이지들을 참고해주세요.

fly.io 홈페이지에서도 간단한 배포 과정을 앞세웁니다

이 글에서는 fly.io가 제공하는 환경에 도커 컨테이너를 배포해봅니다. 제 경우 첫 배포까지 5분 정도가 걸렸고, 이후 업데이트에는 10초가 채 걸리지 않더군요. 게다가 무료 티어 성능도 꽤 좋아요! 도커를 배우는 분께 도움이 되리라 생각합니다.

준비물은 다음과 같습니다.

이 글에서 다루는 fly.io 관련 기능은 다음과 같습니다.

* 이 글에서는 ghost 블로그를 설정하고 사용하는 방법은 설명하지 않습니다.

44BITS 소식과 클라우드 뉴스를 전해드립니다. 지금 5,000명 이상의 구독자와 함께 하고 있습니다 📮

fly.io 가입하기

먼저 브라우저에서 https://fly.io/에 접속하고 오른쪽 위의 Sign In 버튼을 누릅니다.

홈페이지 오른쪽 위의 Sign In 버튼을 누릅니다

이후 로그인 화면에서는 아래쪽의 Need an Account? 링크를 누릅니다.

로그인 페이지에서 Need an Account? 링크를 누릅니다

계정 정보를 입력하는 화면에서는 이름과 이메일, 비밀번호를 입력하고 Create My Account 버튼을 누릅니다.

계정 정보를 입력한 후 Create My Account 버튼을 누릅니다

카드 정보를 입력하는 화면에서는 해외 결제가 가능한 카드를 입력합니다. 카드를 등록하더라도 무료 티어를 넘지 않으면 비용이 발생하지 않습니다. 이후 Subscribe 버튼을 누릅니다.

해외 결제 가능한 카드 정보를 입력한 후 Subscribe 버튼을 누릅니다

모든 정보에 이상이 없다면 계정이 생성되고, 간단한 fly.io 사용 방법이 나타납니다.

가입 완료 화면

여기까지 진행했다면 가입했던 이메일 주소로 인증용 메일(제목: Welcome to Fly)이 도착했을 겁니다. 이메일 내용 중 Verify Email 버튼을 눌러 인증을 마치세요.

인증용 이메일에서 Verify Email 버튼을 누릅니다

여기까지 이상이 없다면 끝났습니다. 이제 fly.io를 사용해봅시다.

로컬에 fly.io 설정하기

운영체제별 설치 명령어는 다음과 같습니다.

macOS

brew install flyctl

리눅스

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

Windows

파워셸에서 실행하세요.

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

설치가 끝났다면 flyctl auth login 명령어로 셸에 인증 정보를 불러옵니다.

❯ flyctl auth login
Opening https://fly.io/app/auth/cli/e8c69fb0c9d8b46c3de99df79f80c448 ...

Waiting for session...

자동으로 웹 브라우저에서 fly.io 로그인 화면이 나타납니다. 계정 정보를 입력하고 Sign In 버튼을 누릅니다.

로그인 화면에 계정 정보를 입력하고 Sign In 버튼을 누릅니다
Opening https://fly.io/app/auth/cli/e8c69fb0c9d8b46c3de99df79f80c448 ...

Waiting for session... Done
successfully logged in as raccoonyy@gmail.com

배포한 앱 목록을 확인해봅시다. (아무것도 없어야 정상입니다.)

❯ flyctl apps list
NAME    OWNER   STATUS  LATEST DEPLOY

모든 준비가 끝났습니다. 이제부터 실제로 도커 이미지를 배포해봅시다!

fly.io로 nginx 배포하기

먼저, Nginx 이미지를 배포해보겠습니다. 이제 flyctl launch 명령어를 실행하면 몇 가지 질문을 거치면서 설정 파일(fly.toml)을 만들어줍니다.

❯ flyctl launch
Creating app in /introduce-flyio
Scanning source code
Could not find a Dockerfile, nor detect a runtime or framework from source code. Continuing with a blank app.
? App Name (leave blank to use an auto-generated name): temp-fly-app
Automatically selected personal organization: ...
? Select region:  [Use arrows to move, type to filter]
...

Created app crank-fly-app in organization personal
Wrote config file fly.toml

두 가지 질문이 나타날 텐데요.

두 질문에 답하고 나면 fly.toml 파일이 생성됩니다. 아직은 파일 내용을 다 이해할 필요가 없습니다. 다음 내용을 processes[env] 사이에 추가해주세요. nginx라는 도커 이미지를 배포하겠다는 의미입니다.

[build]
  image = "nginx"

그리고 [[services]] 항목 아래의 internal_port 값을 80으로 바꿔줍니다. 서비스가 컨테이너 내부에서 80번 포트를 사용한다는 뜻입니다.

[[services]]
  ...
  internal_port = 80

이제 flyctl launch 명령어를 사용하여 앱을 배포해봅시다.

❯ flyctl launch
An existing fly.toml file was found for app temp-fly-app
App is not running, deploy...
Deploying temp-fly-app
==> Validating app configuration
--> Validating app configuration done
Services
TCP 80/443 ⇢ 80
Searching for image 'nginx' remotely...
image found: img_n37qpooo2qnpmz69
Image: registry-1.docker.io/library/nginx:latest
Image size: 57 MB
==> Creating release
Release v2 created

You can detach the terminal anytime without stopping the deployment
Monitoring Deployment

1 desired, 1 placed, 0 healthy, 0 unhealthy [health checks: 1 total]
--> v0 deployed successfully

마지막 줄에 successfully 문구가 보인다면 배포가 잘 끝난 겁니다. 이제 웹 브라우저에서 fly.io에 접속하면 배포한 앱이 나타납니다.

fly.io 대시보드에 추가된 앱

* 우리가 배포하지 않은 fly-builder-...라는 앱도 보일 텐데요. 이 앱은 fly 시스템이 이미지 빌드나 배포에 사용하는 앱입니다. 따라서 삭제하면 안 됩니다.

앱 이름을 클릭하면 앱의 상태와 설정 내용을 확인할 수 있습니다. Hostname 부분에 접속할 수 있는 주소(앱이름.fly.dev)가 보입니다. 웹 브라우저에서 접속해봅시다. 여기서는 temp-fly-app.fly.dev입니다. (터미널에서 flyctl open 명령어를 사용해도 브라우저에 앱을 띄워줍니다.)

앱의 상태와 정보

다음과 같은 익숙한 nginx 초기 화면이 나타난다면 성공입니다.

fly.io로 배포한 nginx 화면

이렇게 해서 첫 도커 이미지를 배포했습니다. 앞으로는 좀더 쉬워질 겁니다. 필요에 따라 fly.toml 파일을 수정한 후 flyctl deploy 명령어만 실행하면 새 배포가 진행되거든요. 😀

직접 만든 HTML 파일 서비스하기

이번에는 nginx 초기 화면 대신 우리가 만든 HTML 페이지를 나타내봅시다. 다음과 같은 내용으로 Dockerfile 파일을 만듭니다.

FROM nginx

COPY index.html /usr/share/nginx/html/

보여주고 싶은 간단한 HTML 파일도 만들어야겠죠. 다음 내용으로 index.html 파일을 만듭시다.

<html>
    <body>
      <h1>Fly.io로 배포한 도커 이미지!</h1>
    </body>
</html>

이제 fly.toml 파일의 [build] 부분을 다음과 같이 수정합니다.

[build]
  dockerfile = "Dockerfile"
# image = "nginx"  

마지막으로 터미널에서 flyctl deploy 명령어를 입력하면 수정한 내용을 배포할 수 있습니다.

❯ flyctl deploy
==> Verifying app config
--> Verified app config
==> Building image
Remote builder fly-builder-snowy-meadow-9167 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
Sending build context to Docker daemon     698B
[+] Building 3.4s (6/6) FINISHED                                                                          
 => [internal] load remote build context                                                             0.0s
 => copy /context /                                                                                  0.0s
 => [internal] load metadata for docker.io/library/nginx:latest                                      1.2s
 => [1/2] FROM docker.io/library/nginx@sha256:1761fb5661e4d77e107427d8012ad3a5955007d997e0f4a3d41ac  1.9s
... (중략) ...
 => [2/2] COPY index.html /usr/share/nginx/html/                                                     0.1s
 => exporting to image                                                                               0.0s
 => => exporting layers                                                                              0.0s
 => => writing image sha256:dd5286ef8575d776588afad00459fc1d014aa621b2c7a1ae584ce6d047fe259e         0.0s
 => => naming to registry.fly.io/temp-fly-app:deployment-1658961061                                  0.0s
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/temp-fly-app]
cccfadc8e16c: Pushed 
abc66ad258e9: Pushed 
243243243ee2: Pushed 
f931b78377da: Pushed 
d7783033d823: Pushed 
4553dc754574: Pushed 
43b3c4e3001c: Pushed 
deployment-1658961061: digest: sha256:0821facf7d469c6028908dceaf7ab0b2f3d6b602005142cdd79686e7992a5eb7 size: 1777
--> Pushing image done
image: registry.fly.io/temp-fly-app:deployment-1658961061
image size: 142 MB
==> Creating release
--> release v1 created

--> You can detach the terminal anytime without stopping the deployment
==> Monitoring deployment

v1 is being deployed

긴 메시지가 나타나도 놀라지 마세요. 대다수는 도커 이미지를 빌드하는 과정을 보여줄 뿐입니다. 이 과정은 fly 시스템이 사용하는 빌드용 앱 내에서 진행됩니다. 즉, 로컬의 개발 환경에 도커를 설치하지 않았어도 된다는 말이지요.

앱 배포가 끝났다면 브라우저에서 확인해볼까요?

직접 만든 Dockerfile로 배포한 앱

fly.io의 무료 정책

여기서 잠깐 fly.io의 과금 정책을 살펴보겠습니다. fly.io가 무조건 무료일 수는 없는 노릇이니까요. (기억해보세요. 가입 도중 카드 등록이라는 무시무시한 단계를 거쳤잖아요?)

fly.io의 무료 제공량은 다음과 같습니다. 즉, 이를 넘는 만큼 과금됩니다.

항목 무료 제공량
가상 머신: 공유 cpu 2,340시간/월
볼륨 3GB
대역폭 160GB/월
IP 앱당 IPv6 무제한, 앱 당 IPv4 하나
인증서 10개

복잡해보이지만 이 정도면 개인 블로그를 운영하기에는 충분해 보입니다. (인기 있는 블로그라면 좀 다르겠지만요.)

fly.io에 ghost 블로그 배포하기

그럼 이제 멋 없는 HTML 대신 ghost 블로그를 배포해봅시다. fly.toml 파일의 [build] 부분과 [[services]]internal_port를 다음과 같이 바꿉시다. 글을 쓰는 시점에 최신 ghost 이미지(ghost:5)를 지정했고, ghost 블로그가 서비스에 사용하는 2368 포트를 지정했습니다.

[build]
  image = "ghost:5"

[[services]]
  ...
  internal_port = 2368
  ...

그리고 [env] 부분에 몇 가지 환경 변수도 추가합니다.

[env]
  NODE_ENV = "production"
  database__client = "sqlite3"

끝났습니다. 읭? 벌써?라는 생각이 드나요? 물론 실제 사용하기 위해서는 몇 가지 작업을 추가해야겠지만, 이대로도 블로그가 작동하기는 합니다. flyctl deploy 명령어를 사용하여 배포를 하고, flyctl open 명령어로 배포 결과를 확인해봅시다.

❯ flyctl deploy
...
❯ flyctl open
opening http://temp-fly-app.fly.dev ...

다음과 같이 ghost 블로그를 만났다면 성공입니다.

fly.io로 배포한 ghost 블로그

지금까지 복잡한 과정을 잘 따라오셨으니, 마지막 몇 단계도 힘을 내서 진행해봅시다.

컨테이너를 새로 배포해도 데이터는 보존하기

현재 상태로도 ghost 블로그가 잘 작동하지만 문제가 하나 있습니다. 앱을 새로 배포하면 컨테이너 안에 저장됐던 데이터가 모두 사라진다는 점인데요. 블로그 버전을 업데이트할 때마다 블로그 글이 모두 사라져서는 안 되겠죠?

이럴 때 도커에서는 도커 볼륨을 사용했을 텐데요. fly.io에도 볼륨이 있습니다. flyctl volume create 명령어로 블로그 데이터를 저장할 볼륨을 하나 만들어 보죠.

❯ flyctl volumes create temp_fly_blog_volume
? Select region: Tokyo, Japan (nrt)
        ID: vol_xxxxxxxx
      Name: temp_fly_blog_volume
       App: temp-fly-app
    Region: nrt
      Zone: 337f
   Size GB: 3
 Encrypted: true
Created at: 28 Jul 22 13:15 UTC

이 예시에서는 볼륨의 이름을 temp_fly_blog_volume로 지정했습니다. 명령어를 실행하면 볼륨이 생성될 리전을 선택하라고 하는데, 앱을 배포한 위치와 똑같게 도쿄를 선택했습니다.

볼륨의 기본 크기는 3GB입니다. (이제 볼륨에 대해서는 무료 티어를 꽉 채운 셈이죠.)

* 볼륨 이름에는 영문대소문자, 숫자, 밑줄 문자만 사용할 수 있습니다.

지금 생성한 볼륨과 앱을 마운트하기 위해 fly.toml 파일을 열고 [mounts] 설정을 추가합시다. source에는 방금 생성했던 볼륨의 이름을 넣고, destination에는 ghost 블로그가 데이터를 저장하는 데 사용하는 디렉터리의 위치를 적었습니다.

[mounts]
source="temp_fly_blog_volume"
destination="/var/lib/ghost/content"

다시 배포를 해봅시다.

❯ flyctl deploy
...

이제부터는 블로그 데이터가 볼륨에 차곡차곡 저장될 테니 사용자를 등록하고 블로그 환경을 설정해줍시다. 웹 브라우저를 열고 fly 앱 주소 뒤에 /ghost를 붙여주면 블로그 설정 화면을 만날 수 있습니다.

ghost 블로그의 설정 화면

앞으로 ghost 블로그 버전이 업데이트된다면 flyctl deploy 명령어만 실행하더라도, 새 ghost 블로그 이미지로 배포가 될 겁니다.

요약

fly.io를 사용해보니 도커 이미지를 웹에 배포하는 일도 더이상 어렵지만은 않겠다는 생각이 들었습니다. 도커 이미지가 업데이트됐을 때 새 버전을 배포하는 일도 정말 쉬웠고요. 무료 티어도 넉넉한 편이어서, 지금까지 적절한 연습 환경이 없었던 분이나 소규모 웹 서비스를 운영해보려던 분께 정말 좋은 서비스가 되리라 생각합니다.

여기에 소개한 기능 외에도 인증서, 스케일링, 비밀키 관리 등 다양한 기능이 존재하니 천천히 둘러보시면 재미를 느끼실 수 있을 겁니다.

'좋은 기술 블로그를 만들어 나가기 위한 8가지 제언' 대표 이미지

좋은 기술 블로그를 만들어 나가기 위한 8가지 제언

🗒 기사, 2019-01-17 - 좋은 블로그를 정의하는 것은 간단하지 않습니다. 그럼에도 불구하고 분명히 좋은 블로그가 존재하고 다른 블로그와는 다른 무언가가 있습니다. 이 글에서는 기술 블로그를 중심으로, 좋은 블로그를 만들어나가기 위한 8가지 방법을 제언합니다.

아마존 웹 서비스, 패키지 저장소 서비스 코드 아티팩트(CodeArtifact) 출시

🗞 새소식, 2020-06-15 - 아마존 웹 서비스에서 소프트웨 패키지 저장소 서비스 AWS 코드아티팩트(AWS CodeArtifact) 서비스가 출시되었습니다. 코드아티팩트에서는 현재 자바의 메이븐과 그래들, 자바스크리브의 NPM과 Yarn, 파이썬의 pip를 지원합니다.

AWS 라우트53(Route53), 다른 계정으로 호스팅 영역 DNS 레코드 이전하기

🗒 기사, 2020-07-01 - AWS 라우트53(Route53)에서는 도메인에 연결된 호스팅 영역(Hosted Zone)에서 DNS 레코드를 관리합니다. 다른 계정으로 도메인을 이전하더라도 호스팅 영역은 자동적으로 이전되지 않기 때문에 별도의 마이그레이션 작업을 진행해야합니다. 이 글에에서는 AWS53의 호스팅 영역을 이전하는 간단한 예제를 소개합니다.