-
Notifications
You must be signed in to change notification settings - Fork 0
CreateInstanceOnHeroku
Herokuにクレジットカードを登録してあるアカウントを持っている場合の手順をまとめてみます。
Herokuアプリを作成し、必要なビルドパックを設定し、assets:precompile
に必要なconfig varを設定します。
export HEROKU_APP=<アプリ名>
しておくことで、Heroku CLIに-a
オプションを渡す必要がなくなります。
export HEROKU_APP=<希望のアプリ名>
heroku create $HEROKU_APP
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-activestorage-preview
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-apt
heroku buildpacks:add heroku/nodejs
heroku buildpacks:add heroku/ruby
heroku addons:create heroku-postgresql
heroku addons:create heroku-redis
https://github.com/heroku/heroku-buildpack-activestorage-preview はffmpeg関連のバイナリを提供してくれます。圧縮後のSlugの大きさが、heroku-24でAptfileでffmpeg関連のパッケージを入れた場合の630MB前後から270MBへと改善されます。このビルドパックを利用する場合は、Aptfile
から関連のパッケージを除き、.profile
から関連のパスを除くことができます。
アセットのプリコンパイルの時にOTP_SECRET
が設定されている必要があるようです。
heroku config:set OTP_SECRET=`ruby -r securerandom -e 'puts SecureRandom.hex(64)'` \
RAILS_SERVE_STATIC_FILES=true
データベースの初期設定の時にドメイン名でインスタンスアカウントが作られるのでLOCAL_DOMAIN
も先に設定しておきます。カスタムドメインを利用する予定ならそちらを設定しましょう。Herokuアプリケーションのデフォルトドメインは推測不能なものになりました。設定されたデフォルトドメインをheroku domain
コマンドで参照してLOCAL_DOMAIN
を設定します。
heroku domains
=== example-app Heroku Domain
example-app-1234567890ab.herokuapp.com
jq
コマンドが利用可能なら下記のようなコマンドで設定できます。
heroku config:set LOCAL_DOMAIN=`heroku domains --json | jq -r '.[] | select(.kind=="heroku").hostname'`
2024年4月17日にActiveRecord Encryptionを利用するようになってから、下記のように追加の環境変数の設定が必要になりました。
The ActiveRecord encryption feature requires that these variables are set:
- ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
- ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
- ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
Run `bin/rails db:encryption:init` to generate values and then assign the environment variables.
手元にMastodonの走る環境を用意してキーやソルトを生成し、Herokuアプリのconfig varに設定してみました。
$ bundle install
$ heroku config:set `RAILS_ENV=production bin/rails db:encryption:init | grep ^ACTIVE_RECORD_ENCRYPTION_'`
アセットのプリコンパイルがNode.jsからのERR_OSSL_EVP_UNSUPPORTED
のようなエラーで失敗する場合には上記のようにNODE_OPTIONS
を--openssl-legacy-provider
に設定しておく必要があるようです。
Railsが7に更新された頃から、heroku/nodejs
ビルドパックを追加しておかないと、assets:precompile
がerror Command "webpack" not found.
といったエラーで失敗するようになりました。
RAILS_SERVE_STATIC_FILES
は、Add cache headers to static files served through Railsのマージされていない環境ではenabled
とします。
アプリケーションログの保存にPapertrailなどのログ収集add-onも付けておくとよいでしょう。
mastodon-3.5.1のリリース後、PumaからRedisへの接続がプールされるようになりました。これに伴って、Heroku Redisへの接続数が制限を越えてしまう可能性が高くなったようです。最初から、キャッシュ用のRedisとSidekiq用のRdisを最初から作成しておくのがよいでしょう。
heroku addons:create heroku-redis --as CACHE_REDIS
heroku addons:create heroku-redis --as SIDEKIQ_REDIS
稼働中のサーバにHeroku Redisを追加した記録もあります。
コードをHerokuにデプロイします。
例えばv2.8.0タグを使い、ワーキングコピーではheroku-branch
ブランチで管理するなら、
VERSION=v2.8.0
git clone https://github.com/tootsuite/mastodon.git
cd mastodon
heroku git:remote
git checkout -b heroku-branch refs/tags/$VERSION
git push heroku heroku-branch:master
git push
コマンドでHerokuへのコードのデプロイとHerokuでのアプリケーションのビルドが行われます。数分かかるのでしばらく待ちます。うまくいかなかった場合には端末にエラーメッセージが表示されているかもしれません。
Slugの大きさが制限を越えてデプロイできなかった場合には、Heroku Helpよりサポートチケットで関連するアプリのSlugの大きさの制限の緩和を依頼する必要があります。
Redisへの接続が自己署名証明書によるTLSの場合には、デプロイ前に、PumaとSidekiqへの変更やストリーミングへの変更が必要でしょう。
データベースの初期設定をします。
heroku run rails db:migrate
アプリケーションの設定をします。SECRET_KEY_BASE
はRubyビルドパックが設定してくれます。
heroku config:set \
HEROKU=true \
SMTP_FROM_ADDRESS=notifications@localhost \
`heroku run rake mastodon:webpush:generate_vapid_key | grep ^VAPID_`
最初のユーザーをadminとして登録します。初期パスワードは端末に表示されます。
heroku run tootctl accounts create \
<表示名> \
--email <メールアドレス> \
--confirmed \
--role Owner
必要な場合には、念のためアカウントの登録はできないようにしておきます:
heroku run tootctl settings registrations close
Dynoをスケールする
heroku ps:scale web=1 worker=1
ここまででMastodonを使ってみることができるはずです。
heroku open
でMastodonを開き、Log inのリンクからログインしてみてください。アップロードされたファイルはDynoの再起動で消えちゃうので注意してください。
アプリケーションのログを見ながらアクセスしてみるとエラーを確認できるかもしれません。
heroku logs -t -a $APP_NAME
Mastodonはデフォルトではdynoのファイルシステムにアイコンやメディアなどのファイルを格納します。これらのファイルは1日に1度のdynoの再起動の際に消えてしまいます。
Amazon S3のバケットを作成し、そこにファイルを格納するようにします。著者はAWSについて詳しくないので下記の記述には誤りや危険な設定が含まれる可能性があります。気づいたことがあればお知らせください。
https://aws.amazon.com/ からログインし、AWS Management Consoleに進み、AWS servicesからS3を検索し、クリックして、
- Create bucketをクリック、
- bucket nameを設定 (ファイルのダウンロード元のホスト名の一部として使われます)、
- regionを設定、
- versioning、logging、tagsを適宜設定、
- permissionはデフォルト (Ownerのみ読み書き)、
して作成する。
(2023-06-03追記) 現在は、Access Control List (ACL)を有効にする必要があるようです。このサーバでは、バケットのダッシュボードからPermissionsタブを開きAccess control list (ACL)節を確認すると下記のようになっていました。
Grantee | Objects | Bucket ACL |
---|---|---|
Bucket owner (your AWS account) | List, Write | Read, Write |
Everyone (public access) | - | - |
Authenticated users group (anyone with an AWS account) | - | - |
S3 log delivery group | - | - |
まずIAMユーザーに付与する権限を定義します。
https://aws.amazon.com/ からログインし、AWS Management Consoleに進み、AWS servicesからIAMを検索し、クリックして、
- Policiesをクリック、
- Create policyをクリック、
- Create Your Own Policyをクリック、
- Policy NameとDescriptionを適宜設定、Policy Documentに下記をペースト、
- Create Policyをクリック
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::<バケット名>",
"arn:aws:s3:::<バケット名>/*"
]
}
]
}
次にIAMユーザーを作成します。
https://aws.amazon.com/ からログインし、AWS Management Consoleに進み、AWS servicesからIAMを検索し、クリックして、
- Usersをクリック、
- Add userをクリック、
- User nameを設定、
- Programmatic accessを選択、
- Attach existing policies directlyを選択、
- 上記で作成したポリシーを検索して選択、
- Reviewをクリック、
- Create userをクリック
Access key IDとSecret access keyが表示されている状態で次に進みます。
Config varを設定します。S3_HOSTNAME
はリージョンによって異なるかもしれません。下記の例はus-east-1
のものです。
$ heroku config:set S3_ENABLED=true
$ heroku config:set S3_BUCKET=<バケット名>
$ heroku config:set AWS_ACCESS_KEY_ID=<作成したIAMユーザーのものをペースト>
$ heroku config:set AWS_SECRET_ACCESS_KEY=<作成したIAMユーザーのものをペースト>
$ heroku config:set S3_REGION=<us-east-1などのリージョン名>
$ heroku config:set S3_PROTOCOL=https
$ heroku config:set S3_HOSTNAME=s3.amazonaws.com
ここまででアイコンやメディアファイルがS3に保存されるようになったはずです。Mastodonから何かアップロードし、AWSのS3のコンソールから確認してみてください。
Pipelinesを使う場合には、slugをビルドするアプリケーションではS3_ENABLED
をfalse
にしておくと、precompileされたassetはアプリケーションから、稼働中にアップロードされたファイルはS3から利用するようになります。
Bucketterアドオンが利用できるかもしれません。$5/月から。
MastodonのWebUIからJavaScriptによってリソースを読み込むことができるよう、Allow-Access-Control-Allow-Methods
とAccess-Control-Allow-Origin
レスポンスヘッダを追加します。AWSにログインし、S3のダッシュボードからPermissions-Cross-origin resource sharing (CORS)と進んで、下記のようなJSONを入力してSaveボタンをクリックしてください。
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
MastodonはAGPLでライセンスされているので、利用者の求めがあればソースコードを提供する必要があります。コードにインスタンス独自の改修をしている場合には、公開レポジトリへのリンクを公開しておくと良いかもしれません。下記のようなイニシャライザを例えばconfig/initializer/source.rb
として追加しておくことで、/about
や/about/more
で閲覧できるページ下部の「ソースコード」へのリンクが指定したものになります。
# frozen_string_literal: true
module Mastodon
module Version
module_function
def source_base_url
'https://github.com/<ユーザー名>/mastodon'
end
end
end
デプロイ時のコミットハッシュも記録しておく場合には、下記のようなrakeタスクを例えばlib/tasks/version.rake
として置いておくことで、デプロイ中にイニシャライザを追加できます。
namespace :source do
desc 'Record source version'
task :version do
hash = ENV['SOURCE_VERSION'] # available on Heroku while build
if hash.blank?
begin
hash = `git show --pretty=%H 2>/dev/null`.strip
# ignore the error: fatal: Not a git repository
rescue Errno::ENOENT # git command is not available
end
end
unless hash.blank?
hash_abb = hash[0..7]
mastodon_version_to_s = Mastodon::Version.to_s
File.open('config/initializers/version.rb', 'w') do |f|
f.write <<~_TEMPLATE
# frozen_string_literal: true
module Mastodon
module Version
module_function
def to_s
"#{mastodon_version_to_s} at #{hash_abb}"
end
def source_base_url
'https://github.com/ユーザー名/mastodon'
end
def source_tag
"#{hash}"
end
end
end
_TEMPLATE
end
end
end
end
task 'assets:precompile' => ['source:version']
ここではMailgunを使ってみます。
heroku addons:create mailgun
heroku addons:open mailgun
電話番号を入力してアカウントを認証してもらい、一番下のAuthorized RecipientsにMastodonのユーザー登録に利用するメールアドレスを追加し、ベリファイしておく。
Config varを設定する
$ heroku config:set SMTP_FROM_ADDRESS=<メールアドレス>
$ for v in SMTP_SERVER SMTP_PORT SMTP_LOGIN SMTP_PASSWORD; do
heroku config:set $v=`heroku config:get MAILGUN_$v`
done
下記のコマンドで、カスタムドメインを使ってアプリケーションにアクセスできるようになります。
heroku domains:add <インスタンスのFQDN>
インスタンスのFQDNが、CNAME
で、上記のコマンドで表示されたターゲット<インスタンスのFQDN>.herokudns.com
を指すように設定する。
トップレベルドメインを使う場合には、ANAME
あるいはALIAS
レコードにしてください (業者によっては利用できません)。
Automated Certificate Managementを利用する場合には、Hobby以上のdynoを利用する必要があります (dynoの稼働時間に比例した料金がかかります)
heroku ps:type hobby
heroku certs:auto:enable
しばらくして、下記のコマンドがOK
を表示するようになれば、HTTPSでのアクセスが可能です。
heroku certs:auto
下記でMastodonがHTTP経由のアクセスをHTTPSにリダイレクトします。自ドメインの設定も直しておく。
heroku config:set LOCAL_HTTPS=true
heroku config:set LOCAL_DOMAIN=<インスタンスのFQDN>
Mastodonではブラウザのリアルタイムに近い更新にNode.jsによって実装されているstreaming APIを利用します。上記で作成したアプリケーションではリクエストはRailsが処理しますので、streaming APIが利用できません。下記のように2つめのアプリケーションを作成してリクエストをそちらに送ることで対応できます。(ActionCableを使うようにコードを書き換えられればいいのかな?)
APP_NAME=<上記で作成したアプリ名>
STREAMING_APP_NAME=<上記とは違う、希望のアプリ名>
heroku create $STREAMING_APP_NAME
heroku buildpacks:add heroku/nodejs -a $STREAMING_APP_NAME
heroku addons:attach $APP_NAME::DATABASE --as DATABASE -a $STREAMING_APP_NAME
heroku addons:attach $APP_NAME::REDIS --as REDIS -a $STREAMING_APP_NAME
streaming/index.js
で検出されたCPU数に基づくデフォルトの並列度 7ではFree dynoの512 MBのメモリでは若干足りないようです。ここでは5まで減らしてみます:
heroku config:set STREAMING_CLUSTER_NUM=5 -a $STREAMING_APP_NAME
最近のnodeでは自己署名証明書を提示するPostgreSQLサーバへはERR! Error: self signed certificate
というエラーを記録して接続を拒否します。これを避けるには、このようにして環境変数で自己証明書を受け入れるようにコードを変更してそのようにconfig varを設定しておくこともできます。
heroku config:set DB_SSLMODE=ssl_noverify -a $STREAMING_APP_NAME
Mastodonのコードのうち、Procfileを下記のように書き換えて、
web: node ./streaming
レポジトリに登録する。
git add Procfile
git commit -m 'Start streaming service'
このレポジトリを新しく作ったアプリケーションにデプロイする。手元では、ブランチを作成してそこにpushし、GitHub syncでそのブランチからコードをデプロイしています。
Streaming APIに新しく作ったアプリケーションを使うように設定する。
heroku config:set STREAMING_API_BASE_URL=wss://$STREAMING_APP_NAME.herokuapp.com -a $APP_NAME
Puma、Sidekiq、Nodeのプロセス数やスレッド数によっては、PostgreSQLやRedisへの接続数がプランの制限を超える可能性があります。その場合は適宜調整が必要です。
app/javascript/images/logo.svg
として透明な背景のSVGを、app/javascript/images/app-icon.svg
として不透明な背景で余白のあるSVGを用意しておいて、rakeタスクにpngファイルとicoファイルを生成してもらいます。
$ sudo apt install librsvg2-bin
$ RAILS_ENV=development bundle exec rake branding:generate_app_icons
ファイルがごっそり変更されます。
さらに、Safariの非アクティブなタブのアイコンには、黒1色で背景が透明なSVGをapp/javascript/images/logo-symbol-icon.svg
として用意する必要があるようです。不透明部分の色は、app/views/layouts/application.html.haml
下記の行で変更します。
%link{ rel: 'mask-icon', href: asset_pack_path('media/images/logo-symbol-icon.svg'), color: '#6364FF' }/
$ git status
:
modified: app/javascript/icons/android-chrome-144x144.png
modified: app/javascript/icons/android-chrome-192x192.png
modified: app/javascript/icons/android-chrome-256x256.png
modified: app/javascript/icons/android-chrome-36x36.png
modified: app/javascript/icons/android-chrome-384x384.png
modified: app/javascript/icons/android-chrome-48x48.png
modified: app/javascript/icons/android-chrome-512x512.png
modified: app/javascript/icons/android-chrome-72x72.png
modified: app/javascript/icons/android-chrome-96x96.png
modified: app/javascript/icons/apple-touch-icon-1024x1024.png
modified: app/javascript/icons/apple-touch-icon-114x114.png
modified: app/javascript/icons/apple-touch-icon-120x120.png
modified: app/javascript/icons/apple-touch-icon-144x144.png
modified: app/javascript/icons/apple-touch-icon-152x152.png
modified: app/javascript/icons/apple-touch-icon-167x167.png
modified: app/javascript/icons/apple-touch-icon-180x180.png
modified: app/javascript/icons/apple-touch-icon-57x57.png
modified: app/javascript/icons/apple-touch-icon-60x60.png
modified: app/javascript/icons/apple-touch-icon-72x72.png
modified: app/javascript/icons/apple-touch-icon-76x76.png
modified: app/javascript/icons/favicon-16x16.png
modified: app/javascript/icons/favicon-32x32.png
modified: app/javascript/icons/favicon-48x48.png
modified: app/javascript/images/app-icon.svg
modified: app/javascript/images/logo.svg
modified: app/javascript/images/logo-symbol-icon.svg
modified: app/views/layouts/application.html.haml
modified: public/favicon.ico
変更点をcommitしてデプロイします。
$ git add public/favicon.ico app/javascript/{icons,images} app/views/layouts/application.html.haml
$ git commit
副作用として、設定画面の左上のアイコンが、app/javascript/images/logo.svg
のものになります。
Contents in this wiki is copyright 2017 by zunda and licensed under a Creative Commons Attribution 4.0 International License.