4 minute read

잘 쓰고 있던 Heroku 무료 dyno가 중지됐다. 그동안 무료로 잘 쓴 의리로 Heroku를 좀 쓰다가 클라우드 무료 VM으로 옮겼다. 주기적으로 트윗하는 게 전부인 트위터 인용봇은 클라우드 무료 인스턴스에서 돌려도 충분하다.

오라클 클라우드 무료 VM

오라클 클라우드를 선택했다. 처음엔 구글 클라우드를 사용할 계획이었다. 오라클 클라우드 무료 티어가 더 사양이 좋다는 얘기를 듣고 찾아보니 훨씬 더 빠방하게 제공한다. 역시 경쟁은 아름답다. 후발주자 화이팅이다.

  1. Arm 기반 Ampere A1 코어 및 24GB 메모리(매월 OCPU 3,000시간 및 18,000GB-시간을 제공하며 VM 1개 또는 4개로 사용 가능)
  2. AMD 기반 컴퓨트 VM 2개(각각 OCPU 1/8개 및 1GB 메모리) 사용

트위터 인용봇은 2번으로 충분하다. 내가 끌렸던 건 1번이다. 오라클 클라우드에서는 무료로 ARM 아키텍처 VM을 제공한다. ARM 아키텍처에서 Elixir 프로젝트가 잘 돌아가는지 확인도 하고 싶었다. 잘 돌아가는 걸 확인하고 원래 계획했던 저사양의 AMD 기반 VM으로 옮겼다. 초반에 ARM 아키텍처 VM 생성이 안 돼서 몇 번을 시도했는지 모른다. 결제 카드를 등록하면 잘 생성된다는 팁을 보고 등록하고 생성했더니 한 번에 성공했다. 카드 등록 여부로 생성할 수 있는 풀을 다르게 선택하는 것 같았다.

SSH 접속 편의를 위한 alias 설정

SSH 키는 VM을 만들 때 다운로드했다. 매번 접속 IP 주소와 개인키 경로를 인자로 넘길 필요가 없게 별칭을 만든다.

~/.ssh/config 파일을 수정하면 된다.

Host app
  HostName 123.123.123.123
  User ubuntu
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile ~/.ssh/private-key.key
  IdentitiesOnly yes
  LogLevel FATAL

별칭을 만들어놔서 ssh app 을 입력하면 접속한다.

Swap space 할당

무료 인스턴스 메모리가 1GB로 작아서 필수적으로 세팅해야 한다.

$ sudo fallocate -l 2G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

실제 메모리를 다 사용하면 사용할 Swap space를 세팅한다.

Systemd로 프로세스 관리하기

크래시로 프로그램이 종료되면 다른 조치하지 않는 한 다시 실행되게 한다. 아예 실행조차 안 되는 상황이 아니고 특정 조건에만 크래시가 난다면 Sentry로 보고된 정보를 보고 고칠 때까지 시간을 벌 수 있다.

service unit 정의를 /etc/systemd/system/myapp.service 파일에 한 후

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service

파일을 리로드하고 자동실행을 활성화하고 바로 시작한다.

Systemd-journald로 로그 파일 설정

Systemd로 실행한 프로세스 로그를 journalctl 프로그램으로 볼 수 있다.

journalctl -u -f myapp

좁은 집 셋방살이에 맞게 /etc/systemd/journald.conf 파일을 수정해 남길 로그 양을 설정한다.

[Journal]
Storage=persistent
SystemMaxUse=500M
SystemKeepFree=1G
MaxRetentionSec=7day

sudo systemctl daemon-reload
sudo systemctl restart systemd-journald

로그를 저장하다가 디스크 공간이 부족하지 않게 적당히 설정한다.

배포 전용 user 설정

GitHub Actions을 사용해서 배포할 계획이다. 모든 권한이 있는 root 유저를 배포용으로 사용하지 말고 배포할 디렉터리에만 권한이 있는 유저를 만든다.

deployuser 계정을 만든다.

sudo useradd -m -s /bin/bash deployuser

appgroup 그룹을 만들고 배포 계정뿐만 아니라 주로 사용하는 계정도 포함시킨다.

sudo groupadd appgroup

sudo usermod -aG appgroup ubuntu
sudo usermod -aG appgroup deployuser

외부에서 빌드한 파일을 복사할 디렉터리를 만들고 권한을 설정한다. 아래 예제에서는 /app 디렉터리를 사용했다.

sudo mkdir /app
sudo chown deployuser:appgroup /app

sudo chmod g+s /app

/app 디렉터리에서 새로 생성되는 파일이나 하위 디렉터리가 그룹 소유권을 그대로 상속하게 한다.

VM 설정은 끝났다. 이제 VM으로 편하게 배포하는 방법을 궁리해야 한다.

GitHub actions가 사용할 ssh key 생성

GitHub actions에서 VM으로 파일을 복사해야 한다. 즉 ssh key를 생성해서 public key를 VM에 인증된 키로 등록하고 private key는 GitHub actions가 소유해야 한다.

ssh-keygen -t ed25519 -C "github-actions-deploy"

deployuser 권한을 사용하게 인증된 키로 등록한다.

sudo su deployuser
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys

public key를 인증된 키에 추가하고 비밀키를 GitHub이 제공하는 저장소 환경 secrets에 저장하고 쓴다.

scp-action GitHub actions으로 배포

Systemd로 프로세스를 관리하기 때문에 scp로 파일을 복사하기 전에 종료하고 복사 후에 다시 시작한다.

- name: Stop service
  uses: appleboy/ssh-action@v1.2.2
  env:
    APP_NAME: ${{ secrets.APP_NAME }}
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USER }}
      key: ${{ secrets.KEY }}
      envs: APP_NAME
      script: |
        sudo systemctl stop $APP_NAME

- name: Deploy via scp
  uses: appleboy/scp-action@v1.0.0
  with:
    username: ${{ secrets.USER }}
    host: ${{ secrets.HOST }}
    key: ${{ secrets.KEY }}
    source: _build/prod/rel/tbot800/*
    target: ${{ secrets.APP_DIR }}
    strip_components: 4

- name: Start service
  uses: appleboy/ssh-action@v1.2.2
  env:
    APP_NAME: ${{ secrets.APP_NAME }}
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USER }}
      key: ${{ secrets.KEY }}
      envs: APP_NAME
      script: |
        sudo systemctl start $APP_NAME

Tbot-800.ex에서 사용 중인 스크립트를 가져왔다. 빌드 결과물은 _build/prod/rel/tbot800/* 디렉터리에 저장된다.

GitHub이 제공하는 저장소 환경 secretssecrets 환경 변수로 접근한다. 위에서 배포용으로 만든 ssh key의 private 키를 KEY 키의 값으로 저장했다. secrets.KEY 코드로 접근한다.

전체 GitHub actions workflows는 ohyecloudy/tbot-800.ex/blob/main/.github/workflows/elixir.yaml - github.com 에서 볼 수 있다.

마치며

Heroku에서 돌리던 Tbot-800.ex를 오라클 무료 VM으로 옮겼다. 그동안 무료로 오래 써서 의리로 Heroku를 사용했다고 하지만 항상 우선순위에서 밀려 질질 끌었던 것 같다. 자동으로 재시작할 수 있게 System로 프로세스를 관리하고 배포 전용 user를 만들어서 GitHub actions로 배포하는 개발 파이프라인을 구성했다. DB를 사용하지 않는 프로젝트라서 DB를 따로 세팅하지는 않았다.

알림이 아쉬운데, 이건 Telegram 같은 메신저를 사용해 보면 어떨까 생각하고 있다. 지금은 가끔 Twitter(X)에 들어가 인용봇이 잘 동작하고 있는지 확인하고 있다.

링크