廃墟

本ブログは更新を終了しました。 技術的な記事のみ、有用性を鑑みて残しておきます。

rails + sunspot なアプリケーションの本番環境をdocker-composeでつくる

Dockerやdocker-composeの話って結構「いいよ」と目にするんですが、いざ始めようと思うとハードルが高いですよね。

僕も今日は丸一日Docker(-compose)に吸われてしまいましたが、その成果をちょっとメモしておきます。

やりたいこと

やりたいのは以下のような構成。

  • Railsが動くコンテナをDockerで作る
  • Sunspotによる検索をしたいのでsolrをDockerで動かす
  • DBはRDSを想定するのでコンテナでは作らず外部に繋ぐ

想定環境

  • Mac OSX Sierra (10.12.4)
  • Docker 17.06.1-ce
  • docker-compose 1.14.0

DBをローカル宛にする方法

まず簡単な方から。

これは、単に docker.for.mac.localhost というホスト名でホストOSが見えるようになっているので、以下のようにすればOK。

# .env.production (抜粋)
DATABASE_URL=mysql2://root:@docker.for.mac.localhost:3306/my_app
# database.yml (抜粋)
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000
  username: root
  password: ''
  socket: /tmp/mysql.sock
production:
  <<: *default
  database: my_app
  # ↓ ここが重要。他はデフォルト。
  url: <%= ENV['DATABASE_URL'] %>

この場合のDockerfile/docker-composeは後述のsolrを使う場合のから読み取って欲しい。

SolrをDockerベースで動かしつつsunspotと連携させる方法

こっちが難しかった。

先にコードを示す。

#Dockerfile
FROM ruby:2.4.1
RUN apt-get update -qq && \
  apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /app
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle config build.nokogiri --use-system-libraries && \
  bundle install -j3 --without test development --no-cache
COPY . /app

こっちは割と普通の例だと思う。 bundle install--deployment を付けるとrakeがないって怒られるようになってしまったので、つけるのやめた。

# docker-compose.yml
version: '2'
services:
  web:
    restart: always
    build: .
    env_file: .env.production
    command: "bundle exec rails s -p 3000 -b '0.0.0.0' -e production"
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    depends_on:
      - solr
  solr:
    image: solr:6.6.0
    volumes:
      - ./solr/configsets/sunspot/conf:/opt/solr/server/solr/production
      - solr:/opt/solr/server/solr/production/data
    entrypoint:
      - docker-entrypoint.sh
      - solr-precreate
      - production
      - /opt/solr/server/solr/configsets/sunspot

volumes:
  solr:

何のことはなく、volumesでホストOSのディレクトリをゲストに使わせてるだけなんだけど、これが重要だった。

この状態で docker-compose build && docker-compose up -d で立ち上げた後、solrの Core というのを作るために次のコマンドを打つ

# このコマンドは打たなくてよくなった
# docker-compose exec solr bin/solr create_core -c production

↑entrypointのセクションで不要に出来ました。

solrのwebコンソールとかにらめっこしながら、とにかく開発時にsunspotが生成する schema.xmlsolrconfig.yml をdockerのsolrに認識してもらおうとしてたらこのようになった。

現状の課題

現状だと solr/configsets/sunspot/conf/core.propertiessolr/configsets/sunspot/conf/data/ にsolrのデータが生成されてしまう。 開発環境で邪魔になるという問題もあるし、本来これらはvolumeを定義してそこに保存されて欲しい。 しかし、solrのconfig類を渡す必要もあるので、いまひとつどうすればいいか分からなかった。 ……solrについては元々あまり詳しくないので、誰か補足してくれたら凄く嬉しいです。是非コメントください。

参考

以上、調べるのに割と苦戦したのでメモでした。誰かの助けになれば幸い。

科学×中学生がテーマなノベルゲーム「EVERETT EFFECT」 開発途上版 "interpretation" 公開予定

f:id:akn_ep:20160729005618j:plain

everett-effect.com

僕がシナリオとか演出とか企画全体の概ね半分くらいをやっている「EVERETT EFFECT」というゲームの開発途上版を5月6日に東京ビッグサイトで行われるコミティア120にあわせて公開します。

ノベルゲーム部公式ページ

ノベルゲーム部、という名前のコミティア部活動の一員としての出展です。

ブースは K04b Sapience です。よろしくおねがいします。

Mac OSXでRPGツクール2000製のゲームを遊ぶ(EasyWine+IPAモナーフォント)

RPGツクール2000や2003の時代、名作フリーゲーム多いですよね。

久しぶりにやりたい作品があったので、手元のMacで動くようにしてみました。

ちなみに、クリアまでやった訳じゃないので、もしかしたらセーブデータの扱いとかでおかしくなることがあるかもしれないです!

2003とかでも似たような感じで動く気がします。まだやってないのですが、上手く行った/行かなかった人がいたらコメントください。

1. EasyWineのインストール

以下のページを参照。 NAVERまとめが公式配布先っていうちょっと変わり種ですね。

🍎 EasyWine.app 🍷 - 😃 mattintosh note 📝

2. RPGツクール2000RTPのインストール

2000時代はRTP同梱は稀ですよねー…ってことで。

RTPをダウンロードして、インストーラーのexeを開くだけです。

もしexeが他のソフトに関連付いてしまってる場合は、副ボタンクリック(二本指とかCtrl+クリックとか)からeasywineを選んで開く。

3. IPAモナーフォントのインストール

以下のサイトからIPAモナーフォントを持ってきてインストール

各ファイルを開くとFontBookが開くので、流れに従えばインストールできるかと。

IPAモナーフォント

4. user.cfg の編集

レジストリ編集。しないと文字が汚くて遊べたもんじゃない。

参考サイトによると、IPAモナー明朝は下が途切れちゃうから明朝もゴシックにした方がいいとあり、 やってみたら確かに途切れちゃったので僕もゴシックにしてみました。

ここのやり方は、以下の通り。

  1. Finderで適当なウィンドウを開いて Cmd+Shift+G でフォルダ移動ダイアログを開く
  2. ~/Library/Caches/Wine/prefixes/default/ と入れて開く
  3. user.cfg というファイルがあるので、CotEditorなどで開く
  4. [Software\\Wine\\Fonts\\Replacements] と書かれたエリアに以下の内容を書き足す
"MS Gothic"="IPA \x30e2\x30ca\x30fc \x30b4\x30b7\x30c3\x30af"
"MS Mincho"="IPA \x30e2\x30ca\x30fc \x660e\x671d"
"MS PGothic"="IPA \x30e2\x30ca\x30fc P\x30b4\x30b7\x30c3\x30af"
"MS PMincho"="IPA \x30e2\x30ca\x30fc P\x660e\x671d"
"MS UI Gothic"="\x30d2\x30e9\x30ae\x30ce\x4e38\x30b4 Pro W4"
"\xff2d\xff33 \x30b4\x30b7\x30c3\x30af"="IPA \x30e2\x30ca\x30fc \x30b4\x30b7\x30c3\x30af"
"\xff2d\xff33 \x660e\x671d"="IPA \x30e2\x30ca\x30fc \x30b4\x30b7\x30c3\x30af"
"\xff2d\xff33 \xff30\x30b4\x30b7\x30c3\x30af"="IPA \x30e2\x30ca\x30fc P\x30b4\x30b7\x30c3\x30af"
"\xff2d\xff33 \xff30\x660e\x671d"="IPA \x30e2\x30ca\x30fc P\x660e\x671d"

尚、念のためuser.cfgのバックアップを取っておいた方が安心です。

参考:

参°ぼっくす: Mac上のWineでRPGツクールのゲームを動作させてみる

🎮 Mac で『Ib(イヴ)』をプレイする 🍎 - 😃 mattintosh note 📝

GAE/Goの基本的な設定の覚え書き

Go言語は昔(1.4まで?)はパッケージ管理の標準的な仕組みがなくて、1.7あたりでvendorディレクトリを必ず読むようになったらしい。 デファクトスタンダードは、今の所glideというものらしい。 godepsも良いみたいだけど、個人的に何となくglideの方が扱いやすく思えたので。

GOPATHを指定する必要があり、僕はautoenvを使う事にしたけど、何にせよこの後のプロセスを踏む前に必ず、作業ディレクトリがGOPATHに含まれてるようにする。

echo $GOPATH
/Users/akn/go:/Users/akn/Documents/TimeCard/time-card-gae

普通の $PATH と同様 : で区切っていいらしい。

で、とにかくディレクトリ構造が肝心。僕はこんな風にした。(srcなしとかも試したけど、srcがないと上手く動作しなかった)

your-app-dir
└── src
    ├── app.yaml
    ├── glide.lock
    ├── glide.yaml
    ├── main.go
    ├── server
    │   └── server.go
    └── vendor

主要なファイルの中身を晒しておく。

app.yaml

application: my-app
version: 0
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app

nobuild_files:
- vendor

skip_files:
- vendor/.*$

main.go

package main

import (
    "server"
)

func init() {
    server.Start()
}

良く分かんないんだけど、main.goから直接vendor以下のを読みに行くと上手く行かなかったので、server packageを定義して、そっちから読むようにしてる。

あとは以下で開発

goapp serve

もしくはデプロイ

goapp deploy

これだと覚え書きすぎて分かりにくいので、丁寧めの記事へのリンクを貼っておきます。 ただし、僕は goenv を使って app/srcでディレクトリを分ける方法はCan’t findとか言われてライブラリが読まれず、上手く行かなかった。

www.freegufo.com

qiita.com

qiita.com

Refile+S3のpresignにSwiftからダイレクトにアップロードする

画像等のファイルをS3(とかのクラウドストレージ)に置くのが当たり前になってきた昨今、クライアントから画像をアップロードするときにアプリケーションサーバーを経由するのは、そこからさらにS3に挙げるという点ではムダ。

この改善方法として、クライアントサイド(ブラウザやネイティブアプリ)からS3に直接アップロードして、アプリケーションサーバーには「ここに置いといたよ」とだけ伝える方法がある。 このアプローチは、特にherokuのようなアプリケーションサーバーに負荷をかけたくない環境では高い効果を発揮する。

で、このエントリの対象読者は、上の文で何が言いたいか分かってくれる人。

そうじゃない人は、諦めてアプリケーションサーバーに頼ってCarrierWaveかPaperclipの安定版を使う事を強くオススメする。

さて本題。

CarrierWaveの後継と噂される(同じ人が開発している)Rails向けの画像取り扱いgemのRefileというのがある。まだ安定バージョンではないし、Rails5系に組み込もうとするとSinatraとrakeのバージョンがぶつかってしまったり面倒が多いが、設計はCarrierWaveよりもさらに洗練されているっぽい。

GitHub - refile/refile: Ruby file uploads, take 3

Refileにはdirect uploadとPresigned uploadsという機能がある。(大文字小文字はREADMEに倣った)

direct uploadはフォームをsubmitしようとしたときにファイルを1個ずつRails側に送って、それが完了してからフォームをsubmitする方式。

Presigned uploadsは、上記で言ったS3に直接上げる方式。

Refileでは、Javascriptからのアップロードのみ実装されているので、これをSwift側で実装する。

ざっくり言えば refile/refile.js at master · refile/refile · GitHub の移植。

// まずはpresign情報を取得する
request("https://example.com/attachments/cache/presign").validate().responseSwiftyJSON { (presignResponse) in
    // ※エラー処理は自分で書いて下さいね。 switchで .Success / .Failure 分岐するのが個人的には好き。
    let url = presignResponse.value!["url"].stringValue
    let fields = presignResponse.value!["fields"].dictionaryValue
    let asName = presignResponse.value!["as"].stringValue
    
    Alamofire.upload(multipartFormData: { (multipartFormData) in
        
        for (name, value) in fields {
            multipartFormData.append(value.stringValue.data(using: .utf8)!, withName: name)
        }
        
        // 以下のNSDataを用意する部分もお好みで。 UIImagePNGRepresentation を使っても良いし、拡張子などで分岐しても良い。
        let image = UIImage(named: "image1.jpg")!
        let imageData = UIImageJPEGRepresentation(image, 1.0)!
        
        multipartFormData.append(imageData, withName: asName)
        
    }, to: url, encodingCompletion: { (encodingResult) in
        // ここにもAlamofireのドキュメントを参照してエラー分岐等を書く必要があると思います。
        print("uploaded")
    })
}

ここで得たデータをPOSTやPUTしてruby側でActiveRecordに渡してやる必要があるけれど、そこはまだやってないので追記するつもり。

ちなみに現状のGemfileの中身はこんな感じ。

tagすら切られてない中途半端なバージョンなので、不用意にバージョンが上がってバグらないように一応refを固定。

gem 'rails', '~> 5.0.1'

# Refile (最新版, unstableなので依存解決のために無理して入れてます)
gem "refile", require: "refile/rails", github: 'refile/refile', ref: 'd7a42dcd7c'
gem "refile-mini_magick"
gem "refile-s3"
# Refileのために以下が必要っぽい
gem "sinatra", github: "sinatra/sinatra", tag: "v2.0.0.beta2"

そういえば、全然関係ないんですが、最近Skyrimにハマってます。めっちゃ面白いですね。

JWTで認証するGem「Knock」でRSA認証する

Knockのデフォルトは secret_key_base を使ったHMAC using SHA-256らしい。

つまり、公開鍵暗号じゃないので、クライアントサイドで自由に中身を見れるというJWTの利点を半分失ってる(安全性については問題ないと思うけど…)

(追記)…と適当な事を書いてたら補足を頂きました。その通りですね! @koniyan ありがとう。

※この辺とても難しいし、僕は暗号の専門家じゃないので、重要なプロジェクトで使う場合は、鵜呑みにしないで裏を取ってくださいね。

で、このメモで書いておきたいのは、KnockでRSA認証する方法。ただそれだけ。

まず、g knock:install で生成される knock.rb の各項目に以下のような感じで書く。 File.readにしてるけど、ENVに設定すればENVから引っ張り出しても良いと思う。(ENVに改行入れる手段とかは知らない…)

# knock.rb
config.token_signature_algorithm = 'RS256'
config.token_secret_signature_key = OpenSSL::PKey.read(File.read(Rails.root.join('keys','jwt-private.pem')))
config.token_public_key = OpenSSL::PKey.read(File.read(Rails.root.join('keys','jwt-public.pem')))

で、以下のコマンドで鍵ペアを生成する。

openssl genrsa 2048 > jwt-private.pem
openssl rsa -in jwt-private.pem -pubout -out jwt-public.pem

あとは所定の位置にpemファイルを置いておくだけ。 クライアントサイドで複合したい時は、jwt-public.pem を誰でも見れるところに置いておくなりして、複合すればOK。お手軽。

特に分かりやすい利点としては、複合したトークンのexpをチェックするだけで期限がチェックできるので、トークンの延長をすべきか否かの判断とかが手軽にできる。とか。そのへん。

ClipMenu, Clippy がハングアップするので Pastebot に乗り換えたら神だった

クリップボード管理アプリで ClipMenu や、その思想を受け継ぐClippyがありますが、僕の環境だと度々CPUが100%に張り付きます。

何かないかなーと思っていたら、Tweetbotなどで有名なチームTapbotsが、Pastebotというアプリを出してました。 かなり使いやすいのでオススメ。

Pastebot Beta