Rails+MySQL(ClearDB)でHeroku Review Appsを構築する

heroku/ruby ビルドパックを使うと、DATABASE_URL 環境変数から config/database.yml が作成される。データベースにMySQL(ClearDB)を使う場合、CLEARDB_DATABASE_URL にデータベース接続用のURLが設定されるため、config/database.yml が作成されない。

heroku-buildpack-run などを使ってビルドフェーズで database.yml を作成し、CLEARDB_DATABASE_URL のスキームを mysql:// から mysql2:// に変更して database.yml の url に設定することでRailsからデータベースに接続できる。

app.json

{
  "environments": {
    "review": {
      "addons": [
        "cleardb:ignite"
      ],
      "buildpacks": [
        { "url": "heroku/ruby" },
        { "url": "https://github.com/weibeld/heroku-buildpack-run.git" }
      ],
      "scripts": {
        "postdeploy": "rails db:migrate db:seed"
      }
    }
  }
}

config/database.review.yml

production:
  adapter: mysql2
  encoding: utf8
  url: <%= ENV["CLEARDB_DATABASE_URL"].sub("mysql://", "mysql2://") %>

buildpack-run.sh

#!/bin/bash
cp -v config/database.review.yml config/database.yml

NewRelicのトランザクションの”by percent of wall clock time”

NewRelicのTop 5 web transactionsのグラフの見方がよくわからないという話。by percent of wall clock ってなんだよと。

ドキュメントには以下のように書いてある。

ウォールクロックタイム

ウォールクロックタイムとは、時計によって記録された時間です。New Relicはすべてのトランザクションにウォールクロックタイムを使用し、すべてのトランザクションにわたってその値を合計します。

ホストは並行してリクエストを実行できるため、100%を超える割合が表示されることがあります。例えば、100%の値は、選択したすべてのトランザクションの実行時間が、ウォールクロックタイムの記録に費やされた時間と等しいことを示します。

https://docs.newrelic.com/jp/docs/apm/apm-ui-pages/monitoring/transactions-page-find-specific-performance-problems/#wall_clock_time

「時計によって記録された時間」という表現がわかりにくいですが、要は「物理の時計」で計測された時間、現実で経過した時間(Real time)のことです。グラフの縦軸が100%だった場合、それは例えば10分間に実行されたトランザクション時間の合計が10分だったという意味です。サーバーは複数のリクエストを並行して実行するため、トラヒックが多い時にトランザクション時間(別のトランザクションの処理待ちで動いてない時間も含まれる)をすべて合計すると100%を超えることがあるという話。

Railsでコントローラーのアクションを強制終了させる

Railsでコントローラーのアクションを強制終了させてリダイレクトしたり何らか正常なレスポンスを返したりしたい時のやり方。

カスタムエラークラスを作り、rescue_from でキャッチしてあげれば良い。

class HogeController < ApplicationController
  class FugaError < ActionController::ActionControllerError
  end

  rescue_from FugaError do |error|
    redirect_to piyo_url, alert: error.message
  end

  def index
    do_somithing!
  end

  private

  def do_something!
    raise FugaError.new("Error!")
  end
end

AWS SDK for RubyでSNSショートメッセージを送る

AWS SDK for Ruby Version 3 でのやり方
https://github.com/aws/aws-sdk-ruby

IAMの設定

sns:Publish のActionを許可しておく。Resourceは指定できないのでワイルドカード。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "*"
        }
    ]
}

コード

Gemfile で aws-sdk-sns を読み込んでおく。

gem "aws-sdk-sns"

クレデンシャルは環境変数に設定するなどしておいて、以下のコードでSMSを送信できる。

require 'aws-sdk-sns'
client = Aws::SNS::Client.new
result = client.publish(message: "test message", phone_number: "+819000000000")
result.message_id
#=> "af277fa2-5e77-517a-aebf-27f9c06114fd"

MessageAttributesを指定する場合は以下のようにする。

result = client.publish(
  message: "test message",
  phone_number: "+819000000000",
  message_attributes: {
    "AWS.SNS.SMS.SenderID" => {
      data_type: "String",
      string_value: "hoge"
    }
  }
)

UniFi セキュリティゲートウェイを Adopt した後、デバイスにログインできなくなる

UniFiセキュリティゲートウェイを Adopt し、ステータスがCONNECTEDになった後にセキュリティゲートウェイ(192.168.1.1)へデフォルトアカウント(ubnt / ubnt)でHTTPS、SSHのログインができなくなった。Adopt 前はログインできていた。

フォーラムなどを検索すると Settings > Site の Advanced Features を有効にして Device Authentication を確認しろとの情報が出てくるが、手元のコントローラー(バージョン 6.0.36)ではDevice Authenticationが見つけられなかった。

結局 Settings > System Settings > Controller Configuration > Device SSH Authentication というところにSSHのユーザー名とパスワードを見つけた。HTTPSも同じユーザー名とパスワードでログインできた。

ちなみにDevice Authenticationが見つけられないのは「New Settings」がONになっていてベータ版の管理画面が有効になっていたからだった。

SESのSMTPパスワードの署名を作るRubyワンライナー

Amazon SES で SMTP を使う場合、ユーザー名は IAM のアクセスキーID、パスワードはシークレットアクセスキーを使った署名を設定する必要があります。

https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/smtp-credentials.html

RubyでSESのSMTPパスワードを作るワンライナー。AWS_SECRET_ACCESS_KEY、AWS_REGIONを適宜変更してください。

AWS_SECRET_ACCESS_KEY=xxxxx AWS_REGION=xxxxx ruby -rbase64 -ropenssl -e 'puts  Base64.encode64([0x04].pack("c*") + ["AWS4" + ENV["AWS_SECRET_ACCESS_KEY"], "11111111", ENV["AWS_REGION"], "ses", "aws4_request", "SendRawEmail"].inject { |k, v| OpenSSL::HMAC.digest("sha256", k, v) })'

HerokuでRails+MySQLを使うときのdatabase.ymlの設定

Herokuで ruby の buildpack を使うと、以下の条件の場合に config/database.yml が自動的に作成される。

  • config ディレクトリがある
  • activerecord がインストールされている
  • 環境変数に DATABASE_URL が設定されている

データベースに ClearDB などを使う場合、DATABASE_URL を以下のフォーマットで設定する。

mysql2://<username>:<password>@<host>/<database>?<key>=<value>(&<key>=<value>)

例:

mysql2://username:password@us-xxx-xxx-xxxxxxxxxxxx.xx.cleardb.net/rails_production?encoding=utf8&pool=5&timeout=5000

database.yml の内容は rails console から ActiveRecord::Base.connection_config で確認できる。

irb(main):001:0>  ActiveRecord::Base.connection_config
=> {:encoding=>"utf8", :pool=>"5", :timeout=>"5000", :adapter=>"mysql2", :username=>"xxxxx", :password=>"xxxxx", :database=>"heroku_xxxxx", :host=>"us-xxxx-xxxx-xxxx-xx.cleardb.net"}

注意点

ClrearDBで Ignite (Free) や Punch ($9.99/mo) などのプランを使うとCLEARDB_DATABASE_URLという環境変数(例: mysql://xxxxxxxxxxxxxx:xxxxxxxx@us-xxxx-xxxx-xxxx-xx.cleardb.net/heroku_xxxxxxxxxxxxxx?reconnect=true)が作成されるので、これをDATABASE_URLにコピーしてスキームのmysqlをmysql2に修正するだけで簡単に設定できる。

これをこのまま使うと、パラメータのreconnect=trueもdatabase.ymlに設定されてしまうので注意が必要。

Amazon LightsailのWordPressのphpMyAdminに接続する

LightsailのwordpressにはphpMyAdminがインストールされていますが、デフォルトではローカルホスト以外からの接続は拒否されてしまいます。SSHポートフォワーディングを使うことで安全にphpMyAdminに接続することができます。

ssh [HOST] -L 8080:localhost:80 -N

あとはブラウザで http://localhost:8080/phpmyadmin でphpMyAdminにアクセスできます。

ログインアカウント名は root、パスワードはホームディレクトリの bitnami_application_password です。

Heroku Postgres のデータベースをローカルの開発環境にリストアする

Herokuの本番環境のデータベースをローカル環境にコピーして利用したい場合の手順。

データベースのバックアップを作成

heroku pg:backups capture --app app_name

コマンドを実行するとバックアップが実行される。しばらくしてバックアップが終了すると以下のように表示されるので、バックアップ名(b001の部分)を控えておく。

Backing up DATABASE to b001... done

バックアップをローカルにダウンロード

heroku pg:backups:download b001 --app app_name

先ほど控えておいたバックアップ名を指定してダウンロードする。カレントディレクトリに latest.dump という名前でダンプファイルが作成される。

データベースのリストア

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U user_name -d database_name latest.dump