2018-08-21 / @syui

heroku

herokuでsnsやらchatやらを立ててみた時の話

導入

この前、chat立てたい、みたいな話を耳にしたので、1,2時間ほどでherokuのほうに立ててみました。ちょっと調べたりした。

そういえば、この場合、立てた、建てた、どっちだろう。まあ、どっちでもいいか。

phx-chat

さて、chatというと、elixirのphx(phoenix)が便利です。これは、作る場合という話ですが、exampleでも機能はないけど、シンプルなものができます。

herokuの場合、なかなか面倒ですけど、dwyl/phoenix-chat-exampleを使えば、割と簡単にできます。

$ git clone https://github.com/dwyl/phoenix-chat-example

$ cd !$:t

$ heroku create $APP_NAME

$ rm -rf .git

$ heroku git:remote -a $APP_NAME 

$ echo "erlang_version=20.1
elixir_version=1.6.0
always_rebuild=false
runtime_path=/app
" >> elixir_buildpack.config

$ echo "web: MIX_ENV=prod POOL_SIZE=2 mix ecto.migrate && mix phx.server" >> Procfile

$ vim config/prod.exs
config :chat, ChatWeb.Endpoint,
  load_from_system_env: true,
  url: [scheme: "https", host: "syui-chat.herokuapp.com", port: 443],
  force_ssl: [rewrite_on: [:x_forwarded_proto]],
  cache_static_manifest: "priv/static/cache_manifest.json",
  secret_key_base: Map.fetch!(System.get_env(), "SECRET_KEY_BASE")

$ mix deps.get
$ mix phx.gen.secret
XXX
$ heroku config:set SECRET_KEY_BASE="XXX" -a $APP_NAME
$ heroku addons:create heroku-postgresql:hobby-dev -a $APP_NAME
$ heroku buildpacks:set https://github.com/HashNuke/heroku-buildpack-elixir.git -a $APP_NAME
$ heroku buildpacks:add --index 2 https://github.com/gjaldon/heroku-buildpack-phoenix-static.git -a $APP_NAME

$ echo mix.lock >> .gitignore
$ git add .
$ git commit -m "first"
$ git push heroku master

多分、こんな感じですね。

https://github.com/dwyl/learn-heroku/blob/master/elixir-phoenix-app-deployment.md

https://hexdocs.pm/phoenix/heroku.html#making-our-project-ready-for-heroku

pleroma

pleromaもphxで書かれています。ただし、phx exampleであるようなディレクトリ構造とは少し違うので、heroku buildpacksも違ってきます。

具体的には、https://github.com/gjaldon/heroku-buildpack-phoenix-static.gitは、assets/package.jsonnpm iするのですが、pleromaは、heroku deployする際に、npm(node)が必要ありません。package.jsonないですし。ということで、buildpacksはelixirのやつだけでOKです。

大抵、srcを読めばわかりますが、config/prod.exsはこんな感じになってます。

config/prod.exs

use Mix.Config

config :pleroma, Pleroma.Web.Endpoint,
 http: [port: {:system, "PORT"}],
  url: [scheme: "https", host: "pleroma.syui.cf", port: 443],
  force_ssl: [rewrite_on: [:x_forwarded_proto]],
  #cache_static_manifest: "priv/static/manifest.json",
  secret_key_base: System.get_env("SECRET_KEY_BASE")

config :logger, level: :info
config :logger, :console, format: "[$level] $message\n"

config :phoenix, :stacktrace_depth, 20

config :pleroma, Pleroma.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: System.get_env("DATABASE_URL"),
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
  ssl: true

config :pleroma, :media_proxy,
  enabled: true,
  redirect_on_failure: true,
  base_url: "https://syui.gitlab.io/img/pleroma"

pleromaもそうですが、imgをuploadするserverが必要で、通常はhostingしているserverにuploadするんですけど、herokuは再起動と同時に、upload(write)されたものを消します。なので、この際にuploadしたavatarも消えます。

ということで、media_proxyを設定してみてどうにかならないかなってことでやってみましたが、どうにもなりませんでした。

これに関しては、srcのlib/pleroma/upload.exをいじって対応しています。けど、やりたいこと事自体は単純なので上記の設定でなんとなく分かると思います(それ自体は有効ではないけど)。

heroku webは、アクセスがないとM30ごとに落ちます。多分そんな感じ。立ち上がるまで結構時間がかかるので、schedulerを設定してみたわけですが、heroku logsみてみると、Stopping all processes with SIGTERMしたことがありました。なのでやめといたほうがいいですね。特にprocess-schedulerです。herokuに負担がかかりすぎるみたい。

$ heroku addons:add process-scheduler -a $APP_NAME
$ heroku addons:create scheduler:standard -a $APP_NAME
$ heroku addons:open process-scheduler -a $APP_NAME
$ heroku addons:open scheduler -a $APP_NAME

こちらは、pleromaの初期コマンド等です。

# sing up
# example : $ heroku run "mix register_user syui syui [email protected] . password123" -a pleroma
$ heroku run "mix register_user <name> <username> <email> <bio> <password>" -a $APP_NAME

# admin user
$ heroku run "mix set_moderator $USER" -a $APP_NAME

# single user mode
$ vim config/config.exs
+ config :pleroma, :instance, registrations_open: false,
+ config :pleroma, :chat, enabled: false

https://git.pleroma.social/pleroma/pleroma/wikis/Pleroma%E3%81%AE%E5%85%A5%E3%82%8C%E6%96%B9

https://git.pleroma.social/pleroma/pleroma/wikis/Admin%20tasks

pleromaにもchatありますね。chatは、web, mobileが必要ないならirc server立てればいいだけなんですけどね。

gnu-social

gnu-socialはphpで書かれていて、しかもcomposerすらないので、つらい感じでした。

ということで、composerを用意してるrepo(branch)から持ってくることにしました。それをカスタマイズしてheroku deployという感じですね。

なお、composer.lockもcommitする必要があります。

$ git clone https://git.gnu.io/dansup/gnu-social
$ cd !$:t
$ git checkout composer-autoloading

$ php -v
7.2

$ composer update
error

# これでいけました
$ vim composer.json
{
  "require": {
    "ext-pgsql": "*",
    "ext-gd": "*",
    "ext-intl": "*",
    "php": "^7.2.0",
    "illuminate/contracts": "*",
    "illuminate/support": "*",
    "ramsey/uuid": "^3.0",
    "ezyang/htmlpurifier": "^4.10",
    "psy/psysh": "^0.8.17"
  },
    "scripts": {
      "post-install-cmd": [
      	"chmod 644 config.php"
      ],
      "post-update-cmd": [
      	"chmod 644 config.php"
      ]
    }
}

$ composer update

$ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-php -a $APP_NAME
$ heroku addons:add cleardb:ignite -a  $APP_NAME

# trueとかの部分はいらない
$ heroku config -a $APP_NAME | grep CLEARDB_DATABASE_URL
mysql://username:password@hostname/database

$ rm -rf .git
$ heroku git:remote -a $APP_NAME 
$ git add .
$ git commit -m "first"
$ git push heroku master

$ open -a Google\ Chrome $APP_NAME.herokuapp.com/install.php

gnu-socialの仕組みは単純で、基本的にinstall.phpconfig.phpを書き込む処理を行います。ただし、deploy後は、heroku resetの関係でそこで作ったconfig.phpは、一時的にしか有効ではありません。もちろん、DBに保存されるユーザー情報は別ですが。

ということで、srcにconfig.phpを用意して、deployする必要があるわけなんですけど(.gitignoreに注意)、config.phpの内容がわからないんですよね。そもそもheroku run bash -a $APP_NAMEしてもconfig.phpが見つからないし、権限を考慮してもやっぱり見当たらないので、herokuの仕組み的なものなんだと思いますが、私は、dockerでpreviewして、そのときに作成したconfig.phpをdocker cpして持ってくることを考えました。

install.phpのINSTALLDIRで設定されているはずなんですが、lib/installer.phpchmod 644を書き直したり、INSTALLDIRを/appにしたりしたのですが、heroku runでは見つけられなかった。

ということで、dockerを使って簡潔に行きましょー。こんな感じで持ってきます。一応、CONFIGUREを読んでもいいんですけど、これ読んでやってるほうが時間かかると思う。

$ sudo docker run -p 8000:80 rudism/gnu-social /root/start
$ open -a Google\ Chrome http://localhost:8000/install.php

$ sudo docker ps -q

$ sudo docker cp xxx:/var/www/gnu-social/config.php .
or
$ sudo docker exec xxx /bin/bash
	cat /var/www/gnu-social/config.php
$ sudo docker cp xxx:/var/www/gnu-social/config.php .
or
$ sudo docker commit xxx rudism/gnu-social
$ sudo docker run -it rudism/gnu-social /bin/bash
	cat /var/www/gnu-social/config.php
$ sudo docker ps -q
$ sudo docker cp xxx:/var/www/gnu-social/config.php .

上記コマンドを見れば、やりたいことはだいたい分かると思います。いろいろな方法があります。この他にも色々ありますね。

しかし、要点は、heroku dbの情報を入力して作成されたconfig.phpを持ってくることが重要だと思います。下記はexampleです。

config.php

<?php
if (!defined('GNUSOCIAL')) { exit(1); }
$config['site']['name'] = 'gnusocial';
$config['site']['server'] = 'app.herokuapp.com';
$config['site']['path'] = false; 
$config['site']['ssl'] = 'never'; 
$config['db']['database'] = 'mysql://username:password@hostname/database';
$config['db']['type'] = 'mysql';
// Uncomment below for better performance. Just remember you must run
// php scripts/checkschema.php whenever your enabled plugins change!
//$config['db']['schemacheck'] = 'script';

gnu-socialをdocker等で試行錯誤した結果、sslpgsqlはどうやっても機能しませんでした。特に、pgsqlはバグってると思います。lib/installer.phpみたいなファイルから書き直したんですが、docker上でもheroku上でもまともに動きませんでした。正直、よくわからない。

https://suriyadeepan.gitbooks.io/mesh-guide/content/gnu_social.html

あと、config.phpを置いたのはいいんですが、本来は、644でchmodされるファイルですから、そのパーミッションで行きたいんですけど、なぜかheroku run bash, ls -l config.phpで確かめてみると、意図した権限じゃないんですよね。危険ですよ。composerでscript実行してるはずなんですけどね。(これはherokuの問題だと思う)

その他、gnu-socialはqvitterというpluginがあるのですが、qvitter+pleroma-feでdual bootできるっぽいので、今度試してみようかなーと思っています。(herokuでどこまでできるかわからないけど。directory構造とかnginx設定とかが来ると、ちょっと面倒なことになりそうですね…)

https://git.pleroma.social/pleroma/pleroma-fe/wikis/dual-boot-with-qvitter

追記

qvitter+pleroma-feできた。

ちょっとハマったところとかを解説。

qvitterは、urlに/index.phpを付けずにapiを呼び出すため、fancy urlsをenableにしていない場合、url間違いでapiを呼び出せない。そのためエラーを出すのだけど、herokuなどのPaaSは、nginxなどを直接操作できないため、なかなか面倒でした。

具体的には、nginx_app.confを作って、Procfileのオプションで指定してやればOKでした。

nginx_app.conf

location @rewriteapp {
    # rewrite all to index.php
    rewrite ^(.*)$ /index.php/$1 last;
}

Procfile

web: vendor/bin/heroku-php-nginx -C nginx_app.conf 

メモ

fancy urls = index.php 
ex : /index.php/api/user -> /api/user 
https://github.com/hannesmannerheim/qvitter/issues/256

> nginx_app.conf
location @rewriteapp {
# rewrite all to index.php
rewrite ^(.*)$ /index.php/$1 last;
} 

> Procfile 
-C nginx_app.conf 
https://devcenter.heroku.com/articles/custom-php-settings

あと、ちょっと変な現象に遭遇して、config.phpのsite nameにqvitterを入れてると、plugins/Qvitterを呼び出せなかった気がする。多分、plugins/Qvitterのcodeか何かバッティングしたのかも。なので、サイト名変えました。

site

そういえば、今回、適当にsnsとか立ててみたわけですが、ついでに自分のメインサイトの方にも追記しておきました。

ですが、そんな立てても使いませんよね。ということで、cssのほうもちょっと作ってみました。使わないやつは赤で表示します。

https://mstdn.syui.cf

https://pleroma.syui.cf

http://qvitter.herokuapp.com

http://syui-chat.herokuapp.com

<span class="blue"></span>
span.blue {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #61bdcc;
    margin: 0px 5px;
    box-shadow: 0px 0px 10px #61bdcc;
}

<span class="red"></span>
span.red {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #f41b16b0;
    margin: 0px 5px;
    box-shadow: 0px 0px 10px #f41b16;
}

これ、他にも使えそうだなー。