2022-08-27 / @syui

heroku , fly

herokuからfly.ioへの移行

herokuのplan:free(hobby)が廃止され、mo/$31になるので、herokuからfly.ioへ移行を考えています。

Heroku Dynos starts at $7/month, Heroku Data for Redis® starts at $15/month, Heroku Postgres starts at $9/month.

https://blog.heroku.com/next-chapter

herokuのこれまでの評価は、素晴らしかったです。感謝しかありません。ありがとう!

fly.io plan:free(hobby)

クレジットカードを登録するとplan:hobbyが使えるようになります。クレジットカードはできればVプリカ(visaプリペイドカード)などを使用するようにしてください。登録できない場合に通常のクレジットカードで登録するしかありません。このへんは未検証です。

Add a payment method to get even more free allowances.

VM: shared-cpu 2,340 hours per month Run 3 shared-cpu-1x VMs with 256MB RAM full time. Volumes 3GB Provision 3GB of persistent volumes for permanent storage Bandwidth 160GB per month See outbound data transfer for regional breakdown Anycast IPs Unlimited IPv6, 1 IPv4 per active app Additional IPv4 addresses are $2 per month Certificates 10 active certificates Add 10 certificates to your apps

  • dyno : 2,340

  • ram : 256M

  • strage(volume) : 3G

https://fly.io/docs/about/pricing/

fly.ioはかなりherokuを意識しているようで、cliもありますので、herokuと同じように使いやすいと思いました。

herokuからの移行はこちらから自動で移行するツールがあります。が、これでうまくいくとは思っていません。

注意点としては以下になります。

  • herokuとfly.ioで使うメールアドレスを同一のものにすること

これを行わないとfly.ioでherokuのメールアドレスからアカウントが作成された上で、appをdeployします。この際、アカウントにはクレジットカードの登録も必要です。あらかじめfly.ioで作成している場合、そのアカウントは使われません。

fly.ioのlaunch/herokuを使う際はherokuとfly.ioで使うメールアドレスを同一のものにしておきましょう。

さて、ではherokuからの移行ツールが動作するかというと、当然ですが移行ツールでは正常にdeployが完了しませんでした。

したがって、docsを読んで最初からfly.io用にdeployできる構成を作らなければなりません。

mastodon

ここではfly.ioでmastodonを正常に動作させるまでをやります

fly.ioでmastodonを動かす場合、(1)memory:512Mにすること、(2)redis-serverを独自に立ち上げることが重要になります。

公式のdockerfileでもdeployは成功しますが、logsを見てみると、redisの処理がうまく行かず、webにアクセスできませんでした。

さらに、独自のアドレスを使う場合は、fly.ioのapps/xxx/certificatesで証明書を発行した上でCNAMEを追加します。ここまでやって初めて正常に動作しました。

ref : https://github.com/tmm1/flyapp-mastodon

$ fly apps create xxx
$ fly scale memory 512
app = "xxx"

kill_signal = "SIGINT"
kill_timeout = 5

[env]
  # WEB_DOMAIN="mstdn.syui.cf"
  # LOCAL_DOMAIN="syui.cf"
  LOCAL_DOMAIN="xxx.fly.dev"
  LANG="en_US.UTF-8"
  RAILS_ENV = "production"
  RAILS_LOG_TO_STDOUT = "enabled"
  WEB_CONCURRENCY = "1"
  REDIS_HOST = "xxx-redis.internal"
  REDIS_PORT = "6379"
  S3_ENABLED=false
  SINGLE_USER_MODE=true
  OTP_SECRET=""
  SECRET_KEY_BASE=""
  # LOCAL_HTTPS=false

[deploy]
  release_command = "bundle exec rails db:migrate"

[mounts]
  processes = ["rails"]
  source = "mastodon_uploads"
  destination = "/opt/mastodon/public/system"

[processes]
  rails = "bundle exec rails s -p 8080"
  sidekiq = "bundle exec sidekiq"

[[statics]]
  guest_path = "/opt/mastodon/public"
  url_prefix = "/"

[[services]]
  internal_port = 8080
  processes = ["rails"]
  protocol = "tcp"

  [[services.ports]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

  [[services.http_checks]]
    path = "/health"
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

OTP_SECRET, SECRET_KEY_BASEfly secrets setしたほうがいいです。ですが、secretsは不安定なので、envに書いたほうがいいです。理由は後述します。

私の場合、下記のようにDBを引き継いだのですが、反映されませんでした。

$ heroku config
$ fly pg create xxx
$ fly proxy 5432 -a xxx
---
# host=localhost
$ export DATABASE_URL=postgres://xxx:xxx@localhost:5432
$ pg_dump --no-owner -C -d $HEROKU_DATABASE_URL | psql -d $DATABASE_URL
---
$ fly pg attach -a $app $app_db
# DATABASE_URLにsecrets setしている場合はattachは動きません。unsetしてください
$ fly secrets unset DATABASE_URL

以前のsecret-keyなどもenvに入れて、rails db:setupを実行していません。引き継ぎなのでmigrateの処理を入れています。

しかし、新しくアカウントを作り直すことになったので、最初から初期化してdeployするのがおすすめかもしれません。

$ fly pg create --name xxx-pg
$ fly pg attach -a xxx xxx-pg
# fly deploy -c fly.setup.toml

次は、redis-serverを作ります。

app = "xxx-redis"

[[mounts]]
  source = "xxx_redis"
  destination = "/data"

volume sizeが3G以上になる場合、課金が発生します。

$ fly apps create xxx-redis
$ fly vol create -c fly.redis.toml xxx_redis --size 1
$ fly deploy --config fly.redis.toml --build-target redis-server

dockerからなのか、ちょっとしたことですぐに動かなくなります。例えば、mediaのurlがうまく取得できない場合でもそういった事が起こるので、注意が必要です。問題が発生した場合、pgをresetしたほうが早いです。

$ fly vol create mastodon_uploads --size 1
# こういったやり方で画像をuploadしている場合は動かなくなる
PAPERCLIP_SECRET="xxx"
PAPERCLIP_ROOT_URL="https://github.com/syui/xxx/raw"

最終的には、appをdeployします。

$ fly deploy
$ fly open
$ fly ssh console
# https://zenn.dev/kumasun/articles/12dcc7b3e91722945228
# 新しくアカウントを作る
$ cd mastodon
$ RAILS_ENV=production bundle exec bin/tootctl accounts create $USER --email=$EMAIL --confirmed --role admin 
# 既存のアカウントを上書き
$ RAILS_ENV=production bundle exec bin/tootctl accounts modify $USER --email=$EMAIL --confirm --role admin

sidekiqが遅い場合があり、redisが原因だと思われます。しかし、これ以上の方法があるとは思えません。

あと、fly.io(cli)は、バグっているので、まあまあの割合で有効に動作しないことがあります。なにかおかしいと思ったときは、そういうこともあるということで。私の場合、secretsに入れたはずのやつが消えたり、消したはずのやつが残ってたり、DATABASE_URLを消したあとにもattachで追加できなくなったりといったことが頻発しました。

mastodon/dockerfile

下記からは主にうまく動作しない手順となります。

通常はfly launchにてy/NでNにします。こうすることでdockerfileが上書きされず、mastodonのdockerfileを使用します。

[processes]
  web = "bundle exec puma -C config/puma.rb"
		worker = "bundle exec sidekiq"

[build]
  [build.args]
    BUNDLER_VERSION = "2.3.9"
    NODE_VERSION = "14"
    RUBY_VERSION = "3.0.4"

[deploy]
  release_command = "bundle exec rails db:migrate"

[env]
  PORT = "8080"

[[services]]
  processes = ["web"] # this service only applies to the web process
  http_checks = []
  internal_port = 8080
  protocol = "tcp"
  script_checks = []

多少、dockerfileを手直ししてdeployすればいいです。postgres, redisのurlをenvに入れておきましょう。これはdockerfileに入れてもfly.ioに入れてもいいです。herokuからの移行はこちらを参考にしてください。

$ ruby -v
$ rbenv install 3.0.4
$ bunlde

$ fly secrets set DATABASE_URL=xxx
$ fly secrets set REDIS_URL=xxx
$ fly deploy
$ fly open

fly.io/dockerfile

下記からは主にうまく動作しない手順となります。

fly.ioが生成するdockerfileを使ってdeployする大まかなヒントです。

ruby ‘~> 3.1.0’

$ rbenv install 3.1.0
$ ruby -v
$ bundle

$ fly auth login
$ fly launch
$ fly deploy
> An error occurred while installing idn-ruby (0.1.4), and Bundler cannot
> An error occurred while installing charlock_holmes (0.7.7), and Bundler cannot
...

Dockerfile

ARG DEV_PACKAGES="git build-essential libpq-dev wget vim curl gzip xz-utils libsqlite3-dev ffmpeg libicu[0-9][0-9] libicu-dev libidn11 libidn11-dev libpq-dev libxdamage1 libxfixes3 zlib1g-dev libcairo2 libdatrie1 libgdk-pixbuf2.0-0 libgraphite2-3 libharfbuzz0b libpango-1.0-0 libpangocairo-1.0-0 libpangoft2-1.0-0 libpixman-1-0 librsvg2-2 libthai-data libthai0 libvpx[5-9] libxcb-render0 libxcb-shm0 libxrender1 libglib2.0-0"

これでやっと通りました。

しかし、bundle exec rails assets:precompilelibicudata.so.67: cannot open shared object file: No such file or directory - /app/vendor/bundle/ruby/3.1.0/gems/charlock_holmesが出ます。

Dockerfile

ENV OTP_SECRET=xxx
RUN gem pristine --all

ARG PROD_PACKAGES=xxx libglib2.0-0

postgres

fly-postgres(pg)はpublicではなくprivateです。container内からしかアクセスできません。ただし、localからアクセスする手段があります。それを使いheroku-pgのdumpをfly-pgに追加します。

$ heroku config
$ fly pg create
$ fly proxy 5432 -a xxx
---
# host=localhost
$ export DATABASE_URL=postgres://xxx:xxx@localhost:5432
$ pg_dump --no-owner -C -d $HEROKU_DATABASE_URL | psql -d $DATABASE_URL
---
$ fly pg attach --app $app $app_db
# DATABASE_URLにsecrets setしている場合はattachは動きません。unsetしてください
$ fly secrets unset DATABASE_URL

redis

fly.ioはredisがつらい。もしかしたらherokuのrediscloud使えばいいかも。でもREDIS_URLに入れてもうまく動作しなかった。

$ heroku addons:create rediscloud

総評として、herokuがよすぎた。

追記 : 調整することで、fly.ioでもかなり快適に動作するようになりました。ただし、総合的に見てherokuのほうが便利です。fly.ioはherokuより安く運用できるという点で評価できます。