오라클 무료 VM을 알차게 사용하려면? - 트위터(X) 봇을 Heroku에서 OCI로 이사
잘 쓰고 있던 Heroku 무료 dyno가 중지됐다. 그동안 무료로 잘 쓴 의리로 Heroku를 좀 쓰다가 클라우드 무료 VM으로 옮겼다. 주기적으로 트윗하는 게 전부인 트위터 인용봇은 클라우드 무료 인스턴스에서 돌려도 충분하다.
오라클 클라우드 무료 VM
오라클 클라우드를 선택했다. 처음엔 구글 클라우드를 사용할 계획이었다. 오라클 클라우드 무료 티어가 더 사양이 좋다는 얘기를 듣고 찾아보니 훨씬 더 빠방하게 제공한다. 역시 경쟁은 아름답다. 후발주자 화이팅이다.
- Arm 기반 Ampere A1 코어 및 24GB 메모리(매월 OCPU 3,000시간 및 18,000GB-시간을 제공하며 VM 1개 또는 4개로 사용 가능)
- 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이 제공하는 저장소 환경 secrets을 secrets 환경 변수로 접근한다. 위에서 배포용으로 만든 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)에 들어가 인용봇이 잘 동작하고 있는지 확인하고 있다.
링크
- Cloud Computing Services - Google Cloud - cloud.google.com
- GitHub Actions에서 비밀 사용 - GitHub Docs - docs.github.com
- GitHub Actions 설명서 - GitHub Docs - docs.github.com
- ohyecloudy/tbot-800.ex/blob/main/.github/workflows/elixir.yaml - github.com
- #TIL systemd로 프로세스를 관리하고 systemd-journald로 로그를 본다 - dev diary, TIL - ohyeclou…
- #TIL Linux Swap space(스왑 공간) 설정 - dev diary, TIL - ohyecloudy.com
- Elixir 프로젝트를 Heroku에 배포하기 - ohyecloudy’s pnotes - ohyecloudy.com
- GitHub Actions로 ARM64 플랫폼 빌드 및 배포 - ohyecloudy’s pnotes - ohyecloudy.com
- GitHub Actions에서 사용할 배포용 Linux 유저 생성 - ohyecloudy’s pnotes - ohyecloudy.com
- 크래시 리포트, 에러 트래킹이 필요하면 sentry - ohyecloudy’s pnotes - ohyecloudy.com
- 10년 전에 Clojure로 짠 트위터 인용봇을 Elixir로 재작성한 후기 - ohyecloudy’s pnotes - ohyecloudy…
- 클라우드 인프라 - Oracle 대한민국 - oracle.com
- Optimizing for Free Hosting — Elixir Deployments - by Damon Janis - Medium - …