山口小夜子の映画「氷の花火」を見た個人的な記憶

めずらしく、技術的ではないことを書きました。

山口小夜子」覚えてますか? 知ってますか? ファッションとは全く無縁な私がこんなことを書くのは恥ずかしいのですが、若いころ山口小夜子は憧れの人でした。

f:id:yuum3:20151103094057j:plain

先週の土曜日に、映画 「氷の花火」 を見て気まました。映画は山口小夜子の遺品の整理風景から始まり、彼女と一緒に仕事をした人たちへのインタビューから彼女の一生を描き、最後に現在のモデルを使い山口小夜子を再現するというプロジェクトという内容でファンとしてはとても見応えのある内容でした。

幾つかのメモ:

  • 山口小夜子といえば黒髪のおかっぱに切れ長の目ですが、実はくっくりとした目で切れ長はメークで作っていた!
  • 昔、デーモニッシュな資生堂のCMで知った セルジュルタンス のモロッコ・マラケシュの邸宅でのインタビューや小夜子と映った昔の画とは妄想が膨らむ収穫だった
  • 資生堂で小夜子の出たCMを作った方々へのインタビューを見ると、かなりアーティスティックなCMを作れる、それを認めてくれる経営者のいた経済成長中の日本があったんだなぁ〜 というノスタルジー
  • 遺品の整理シーンに出てきた蔵書。コクトー寺山修司タルコフスキー世阿弥・・・私も持っていたものが多い、彼女の書いた本を読んだりして影響を受けていたんだろうな

彼女は軽薄な女よ:

私が若い頃、不思議な画廊のマダムと友達になり、画廊にも出入りしたり、マダムと食事に行ったり、お芝居に行ったり・・・そんな中で私が山口小夜子のファンだと言うと、「彼女は軽薄な女よ・・・・」と答えたことはずっと記憶に残っていました。マダムは芸術家の芸術にせっする態度のには厳しい哲学を持った人でした、当然ながらたくさんの美術、お芝居、それらに関係するパーティーの中で暮らしている方でした。

山口小夜子はファッションモデルとして世界一の座を射止めた人です、高田賢三が「かぐや姫が降りてきたという感じ・・・」とインタビューの中で語っていましたが、たしかにランウェイの彼女には神がった美しさがありました。

しかし、トップモデルをずっと続けることはできません。モデルを辞めた後の彼女は、舞踏、お芝居、若い芸術家とのコラボなど、いろいろなものに取り組んでいる画像、共演者たちのインタビューが流れながら映画は進んでいきました。 そこに映る彼女には心ときめく山口小夜子を発見できませんでした。 彼女自身の頑張りはわかります、しかし表現者(芸術家)としてまだまだだったのでしょうか。画廊のマダムの言っていた事がわかった気がしました。

一度頂点を極めてしまった人のその後は辛い・・・・ しかし、彼女が山口小夜子という素晴らしい世界を見せてくれた事は事実です。

ありがとう、山口小夜子さん。

プロビジョニングツールをChef-soloからItamaeに替えてみました

Chef から Itamae ?

EY-Officeではサーバーソフトの構築を行うプロビジョニングツールとして現在は Chef-solo を使っています。しかし Chef-solo は終了するらしいのですが、その移行先が良いように思えず悩みながら使い続けてきました。

そんなとき登場してきた Itamae はシンプルでとても魅力を感じました。詳しくは クックパッド開発者ブログのItamae記事 をご覧下さい。

今回時間があったので、EY-Officeの開発支援サーバー (Redmine, Git/Gnatara, CI ...)の全プロビジョニングをChefからItamaeに置き換えてみました。書き換えたレシピは約700行です、そこで感じた事を書きます。

Itamaeの良いところ、良くないところ

1. 情報が貧弱

まず良くない点ですが、Chefのドキュメント に比べると情報の量、質ともに低いです。どうしても思ったように動作しない場合は Itamae の実装を読むことが度々ありました。

公式なドキュメントは

今回、参考になった情報は

2. Chefとは動作モデルが違うぞ!

最初に戸惑ったのでは、Chefとの動作モデルの違いです。

Chef-solo の利用イメージは

  1. Chefを開発環境にインストール
  2. 利用開始時にChefのRubyインタプリターを含む実行環境をターゲットにインストール (knife solo prepare コマンド)
  3. レシピの作成・修正
  4. 実行 (knife solo cooke コマンド)
    1. レシピの文法チェック等
    2. レシピを含むcookbookをターゲットへ転送
    3. レシピをターゲットで実行
  5. 問題があれば 3.に戻る

ですが、Itamaeは

  1. Itamaeを開発環境にインストール
  2. レシピの作成・修正
  3. 実行 (itamaeコマンド)
    1. レシピに対応する操作をsshを使いターゲット上で実行
  4. 問題があれば 2.に戻る

ChefのレシピのRubyコードはターゲット上で実行されますが、Itamaeはレシピは開発環境で実行されます。 したがって、ターゲットの状況によって処理を変更する部分、たとえば not_ifonly_if にはRubyのコードは書けず、shellのコードを書く必要があります。

Chef

execute "apt-get-update" do
  command "apt-get update"
  only_if { File.mtime('/var/lib/apt/lists') < Time.now - 86400 }
end

Itamae

execute "apt-get-update" do
  command "apt-get update"
  only_if "exit $(( `stat -c %Y /var/lib/apt/lists` > (`date +%s` - 86400) ))"
end

実行時にターゲット上の情報を動的に取得できないので、今回はレシピ(コード)で自動的に取得するのをあきらめnode で指定するようにした部分もありました。

3. Chefに似ているけど・・・

Itamae のResource (DSL, 命令?)はChefと似た作りになっていて、そのまま動くものもありますが、少し違う部分もあります。また無いものもあります。

  • Chef の cooke_fileremote_file
  • Chef の remote_file に相当するものは無い?
  • ファイル等のモード指定に8進数が使えない
  • useraction: :modify が出来ない
  • cron Resource が無い
  • ・・・

4. defineを使おう

無いResourceを簡単に作れる Definitions があり、これを使って足りないResourceを補いました。

# URL指定でネット上のファイルをダウンロードするResource
define :external_file, url: nil, owner: nil, group: nil, mode: nil do
    p = params

    execute "download #{p[:name]} from #{p[:url]}" do
        cmd =  "curl -s -f -L -o #{p[:name]} #{p[:url]}"
        cmd += " && chown #{p[:owner]} #{p[:name]}"  if p[:owner]
        cmd += " && chgrp #{p[:group]} #{p[:name]}"  if p[:group]
        cmd += " && chmod #{p[:mode]} #{p[:name]}"   if p[:mode]

      command cmd
      not_if "test -e #{p[:name]}"
    end
end

# crontab 設定用 Resource
define :cron_for, crontab: nil do
  user = params[:name]
  crontab = params[:crontab]
  crontab_path = "/tmp/crontab.#{rand(10000)}"

  file crontab_path do
    content crontab
    not_if "crontab -u #{user} -l"
  end

  execute "crontab -u #{user}" do
    command "crontab -u #{user} #{crontab_path} && rm #{crontab_path}"
    only_if "test -e #{crontab_path}"
  end
end

利用レシピ

ruby_package = "ruby-2.2.2"
external_file "/usr/local/src/#{ruby_package}.tar.bz2" do
  url "http://cache.ruby-lang.org/pub/ruby/2.2/#{ruby_package}.tar.bz2"
end

cron_for "root"  do
  crontab <<-EOH
#
PATH=/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
#
30 5 * * * /usr/local/bin/backup.sh >> /var/tmp/bakup.log  2>&1
  EOH
end

Chef の cron Resource は crontab形式を知らなくてもDSLで設定出来るようになっていましたが、define であまり高度な機能を作る事は考えない方が良いと思いました。やってみると直ぐに限界が見えます。

どうしても高度な Resourceが必要な場合は Quirra: itamae resourceの書き方 を参考に Resource を作った方が良いと思います。

5. シンプルで早い

動作モデルの違いやResourceの充実度の違いはありますが、Itamaeはレシピが実行されるまでの時間が早くとても快適です!

Chef-soloを利用していて、複雑化するChefの推奨移行環境に疑問を持つ方は、ぜひ一度 Itamaeを試してみると良いと思います。

今回作ったレシピ

今回作ったレシピの一部です(ubuntu 14.04用です)

execute "apt-get-update" do
  command "apt-get update"
  only_if "exit $(( `stat -c %Y /var/lib/apt/lists` > (`date +%s` - 86400) ))"
end


execute "ensure ja_JP.UTF-8" do
  command "locale-gen ja_JP.UTF-8"
  not_if "grep -c 'ja_JP.UTF-8' /var/lib/locales/supported.d/local"
end

execute "ensure JST" do
  command <<-EOH
echo "Asia/Tokyo" | sudo tee /etc/timezone
sudo dpkg-reconfigure --frontend noninteractive tzdata
  EOH
  not_if "date | grep -c 'JST'"
end

# shell を zsh に変更 (ユーザーは node[:user]で指定)
file "/etc/passwd" do
  action :edit
  block do |content|
    content.gsub!("/home/#{node[:user]}:/bin/bash", "/home/#{node[:user]}:/bin/zsh")
  end
end

postgreSQLをインストールし DBのencoding, localeを変更する

%w{postgresql-9.3 libpq-dev postgresql-client-9.3 postgresql-contrib-9.3}.each do |pkg|
  package pkg do
    action :install
  end
end

service "postgresql" do
  action [:start, :enable]
end

link "/usr/local/bin/psql" do
  to "/usr/local/pgsql/bin/psql"
end

execute "initdb" do
  user "postgres"
  cwd "/var/lib/postgresql/9.3/"
  command <<-EOH
rm -rf main
/usr/lib/postgresql/9.3/bin/initdb -D main --encoding=UTF-8 --locale=ja_JP.UTF-8
rm main/pg_hba.conf main/pg_ident.conf main/postgresql.conf
ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem main/server.crt
ln -s /etc/ssl/private/ssl-cert-snakeoil.key main/server.key
EOH
  action :nothing
end

execute "re-initdb with ja_JP.UTF-8" do
  command "echo re-initdb"
  notifies :stop, 'service[postgresql]', :immediately
  notifies :run, 'execute[initdb]', :immediately
  notifies :start, 'service[postgresql]', :immediately
  not_if "/usr/lib/postgresql/9.3/bin/psql -U postgres -c \"select * from pg_database where datname ='template0'\" " +
    "| grep -c 'ja_JP.UTF-8'"
end

注) specinfra 2.41.1 で Ubuntu14.04でPostgreSQL起動中判定の問題が対処されたので暫定パッチは不要になりました :-)

GitLabを使ったプライベートGitリポジトリーの管理を止め改造版 Ginatra にしました。

プライベートGitリポジトリーの管理

私の会社 EY-Ofiice零細企業なので プライベートGitリポジトリーGitHub等の有料サービスではなく、さくらクラウド上のサーバーで GitLab を動かしてきました。 しかし、

  • 通常の開発は一人でしか行わないのでpull request等の機能はいらない
  • GitLabは処理が重くサーバーのメモリーを増やさないと遅すぎる
  • 複数人で開発するような仕事ではお客様側でGitHub等を用意してくれる
  • 教育でGitLabを使う事があるが、その時だけGitLab用サーバーを用意すれば良い気がする

ということで、あまりメリットを感じていませんでした。

Gitを使い始めた頃は GitWeb でリポジトリーや履歴が表示と。自作した新規リポジトリー作成CGIで運用していました。

Ginatra

Git用のWebツール を見ていたらSinatraベースの Ginatra ( GitHub ) を知り、インストールしてみました。

Ginatraは Gitリポジトリーのファイル(コード)や履歴の表示ツールです。Sinatraベースなので動作も軽いし、メンテも楽で、今風の画面デザインも気に入りました。

Ginatraの改造

ただし、不満もあり Fork し機能を追加。変更しました。

1. リポジトリーの階層化

Ginatraは複数のGitリポジトリーディレクトリーをサポートしていますが、フラットに管理しています。EY-Officeでは docs(ドキュメント), edu(教育用), products(製品)・・・などのディレクトリーの下にGitリポジトリーを置いています。GitLabには Groupという機能がありこれを使っていました。

やはり、グループが無いと不便なので Repo クラスに group属性を追加し group:nameレポジトリーの識別子として使うように改造しました。 また、一覧表示は Bootstrap の Collapse(Accordion) を使い表示するようにしました。

f:id:yuum3:20150811153016p:plain

2. 新規リポジトリーの作成

Web上かから新規にGitリポジトリーが作成作成出来るようにしました。Ginatraで使っているRugged (libgit2のRubyインターフェース)に git init のAPIがあったので簡単にできました。

f:id:yuum3:20150811153029p:plain

3. その他

などです。まだ RSpec を修正していません ^^;

RedmineのコンテンツをMarkdownに変換しました

Textile vs Markdown

軽量マークアップ言語にはたくさんの種類があります。私も教育で使うテキストの作成やRedmineWikiでは長年 Textile を使って来ました。 しかし、最近はGithubの標準のマークアップ言語がMarkdownだったり、Atom(エディター)をはじめたくさんのMarkdownをサポートするツールが現れ、私も MacDownというツールが気に入り開発ドキュメント等のMarkdown化がどんんどん進んでいます。このブログもはてな記法ではなくMarkdownを使っています。

TextileとMarkdownを比べると 標準のMarkdown は記述能力が低くテキストを作るには能力不足ですが、 GitHubの拡張php Markdown Extra などの拡張機能を使えば、ほぼ同等です。

Redmine

Redmineマークアップ言語のデフォルトはTextileですが、Version 2.5からMarkdownをサポートするようになりました。ただし、Redmineサーバー全体の設定なので新規にRedmineを始める人以外には使いにくいものでした。 redmine_persist_wfmtのようなプラグインを使えばコンテンツ(Wiki, Ticket...)単位でTextitle/Markdownが選択できるのでこれを使えば良いかもしれません。

しかし、私はRedmineWikiを教育でよく使っています。お客様毎に教育で使うコード、ヒント、参考情報・・・などをWikiを使い提供しています。しかも以前に作ったWikiを元に作成する事が多いので、そのTextileとMarkdwonが混在するのは望ましくありません。

そこで、Textileコンテンツを全てMarkdownに変換してしま事にしました。

Textile to Markdown

"Convert Textile to Markdown" で検索すると Pandoc というドキュメント変換ツールが良く出来てきます。このツールは色々なドキュメント形式を相互変換できる素晴らしいツールですが、やはり完璧ではありませんでした。 Markdown→TextileではGitHub拡張をサポートしていますが、Textile→MarkdownではテーブルをGitHub拡張に変換してくれたりしません・・・

そこで、自作を検討しました。MarkdownとTexitleは似ています、正規表現を使えばほとんど変換できます。

def textile_to_markdown(textile)
  d = []
  pre = false
  table_header = false
  text_line = false

  textile.each_line do |s|
     s.chomp!

    if pre
      if s =~ /<\/pre>/
        d << "~~~"
        pre = false
      else
        d << s
      end
      next
    end

    s.gsub!(/(^|\s)\*([^\s\*].*?)\*(\s|$)/, " **\\2** ")
    s.gsub!(/(^|\s)@([^\s].*?)@(\s|$)/, " `\\2` ")
    s.gsub!(/(^|\s)-([^\s].*?)-(\s|$)/, " ~~\\2~~ ")
    s.gsub!(/"(.*?)":(.*?)\.html/, " [\\1](\\2.html) ")

    d << ""  if text_line
    text_line = false

    case s
    when /^<pre>/
      d << "~~~"
      pre = true
    when /^\*\*\* (.*)$/
      d << "      * " + $1
    when /^\*\* (.*)$/
      d << "   * " + $1
    when /^\* (.*)$/
      d << "* " + $1
    when /^\#\#\# (.*)$/
      d << "      1. " + $1
    when /^\#\# (.*)$/
      d << "   1. " + $1
    when /^\# (.*)$/
      d << "1. " + $1
    when /^h(\d)\. (.*)$/
      d << "#" * $1.to_i + " " + $2
    when /^!(.*?)!/
      d << "![](#{$1})"
    when /^\|_\./
      d << s.gsub("|_.", "| ")
      table_header = true
    when /^\|/
      d << s.gsub(/\=\..+?\|/, ":---:|").gsub(/\s+.+?\s+\|/, "---|") if table_header
      table_header = false
      d << s.gsub("|=.", "| ")
    when /^\s*$/
      d << s
    else
      d << s
      text_line = true
    end
  end

  d.join("\n") + "\n"
end

このコードは全てのTextileフォーマットをサポートしているわけではありませんが、私が使ってる機能はほぼ網羅しています。 変換されたMarkdownはGitHub拡張やphp Markdown Extraの機能を使っています。

さてRedmineでは色々なところにTextileが書けますが、今回は Wiki, Ticket, Ticketの履歴のコンテンツに対応しました。必要があれば追加して下さい。

def update_content(model, attrbute)
  total = model.count
  step = total / 10
  puts "  #{model}.#{attrbute} : #{total}"
  model.all.each_with_index do |rec, ix|
    n = ix + 1
    puts sprintf("%8d", n)   if n % step == 0
    rec[attrbute] = textile_to_markdown(rec[attrbute])  if rec[attrbute]
    rec.save!
  end
end

update_content(WikiContent, :text)
update_content(Issue, :description)
update_content(Journal, :notes)

コードは Gistにも置きました。

このコードを以下のように runnder で実行するとTextileがMarkdownに変換されます。

$  rails runner -e production tools/textile2md.rb  

注意: このプログラムは全てのTextile形式を変換できるものではありません、またバグ等で正しく変換できない場合もありますので、変換前に必ずRDBのバックアップを取って下さい。

iOS 開発者のためのバックエンド入門 (2)

iOS開発者の勉強会 yidev 第20回勉強会iOS 開発者のためのバックエンド入門 (2) という発表をしました。 iOSだけでなく、Androidの開発者の方にも参考になる話だと思います。

今回の内容は、Ruby on Railsを使い簡単なバックエンドをその場で作ってみるという実演付きの話をしました

Ruby, Ruby on Railsに付いて

Ruby on Rails を使い簡単なバックエンドを作る

  • クライアントになるiOS アプリの紹介 ーー Swiftで作りました
  • Ruby on RailsのScaffoldでバックエンドを作る実演

f:id:yuum3:20150712112536p:plain

バックエンドをHerokuにデプロイ

  • 出来たバックエンドを代表的なPaaS (Platform as a Service) である、Heroku にデプロイする
    • ライブラリー構成を少し変え、Herokuにgit pushするだけでデプロイが完了
    • Herokuへのデプロイ手順はHerokuのページにもありますが、RailsGirlのHeroku の Rails アプリをアップページ が判りやすいです

コード、資料

使ったコードは GitHub におきました

iOS 開発者のためのバックエンド入門 (1)

iOS開発者の勉強会 yidev 第18回勉強会iOS 開発者のためのバックエンド入門 (1) という発表をしました。 iOSだけでなく、Androidの開発者の方にも参考になる話だと思います。

今回の発表で伝えたかった事は

バックエンドを作るにはいろいろな選択肢がある

  • Parse.comに代表される BaaS (Backend as a Service)を使うとプログラミングやサーバーの構築なしにバックエンドが持てます。ただしBaaS側が提供している機能(データ共有、プッシュ通知...)出来ることには限界があります。
  • BaaSの中には StrongLoopのように、プログラミング出来る自由度の高いものもあります。
  • たいていのBaaSは、お試し的な利用は無料ですが負荷が高くなると課金されます。ビジネスの場合にはコスト的にも独自のバックエンドを作るという選択もあります。
  • 独自のバックエンドを作る場合には、プログラミング言語フレームワーク... とたくさんの選択肢があります。これはどの用途でもベストという解答はないので、目的に応じて選んで下さい。

独自のバックエンドを作る場合、最初はPaaSがお勧め

インフラは知識だけでなく、安定したインフラを運用して行くには経験も必要になります。

しかし、Heroku に代表される PaaS (Platform as a Service) を使うとインフラの知識(エンジニア) 無しに始められのでお勧めです。

APIフレームワークGrapeをRuby on Railsの中で動かすと遅いぞ

ある仕事でスマフォ用のAPIサーバーを作る事になり、REST-like APIが簡単に作れるフレームワーク grape を調査してみました。grapeの良さは、DSLで簡単にAPIサーバーが書ける点とRackで動く軽いフレームワークなのでRuby on Railsに比べ高いパフォーマンスが期待できる点です。

システム構成

Grapeは Mounting に書かれているようにいくつかの構成で動かせます

今回のシステムでは管理者用のWebアプリは Ruby on Railsで作るので、モデルを共有できるRuby on Railsに組み込み使うのが魅力的です。

評価用コードを作ってみた

準備

  • まずはRailsのプロジェクトを作り、scaffoldでいつものアプリを作成
$ rails new api_test
$ cd apt_test
$ rails g scaffold todo due:date task:string
  • テストデータ作成 db/seed.rb も作成
Todo.delete_all
100.times { |i| Todo.create!(due: Time.now + i.day, task: sprintf("task%02d", i)) }
  • 性能比較のために app/views/todos/index.json.jbuilder を変更
json.array!(@todos) do |todo|
  json.extract! todo, :id, :due, :task, :created_at, :updated_at
end

Grape

grape ページの情報を基に API を作成

  • Gemfile
source 'https://rubygems.org'
  ....
gem 'grape'
  • API のコード
class SimpleApi < Grape::API
  version 'v1'
  format :json

  resource :todos do
    desc "Return all todos."
    get  do
      Todo.all
    end
  end
end
  • config/routes.rb に APIをマウント
Rails.application.routes.draw do
  resources :todos
  mount SimpleApi => '/api'
end
  • config/application.rb にAPIのコードを読み込むように設定
module ApiTest
  class Application < Rails::Application
    ....
    config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
    config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
  end
end

これで http://localhost:3000/api/v1/todos をアクセスするとtodosテーブルの全内容がJSONで取得できます

性能が気になる

Ruby on Railsのコードも http://localhost:3000/todos.jsonJSONを返せるます。grapeがどれくらい性能が良いのかを比較してみました。

比較する環境は実際の環境に近くなるように

  • RAILS_ENV は production
  • サーバーは unicorn、worker_processes は 4
  • アプリは EC2 t2.micro で実行
  • ただし、RDBはsqlite3のまま、Nginx等のフロントエンドは無し

ab -c 8 -n 1000 URL で性能を計測

Framework Requests per second
Grape within Rails 33.66
Ruby on Rails 34.49

Grape の性能は Ruby on Rails と同じくらい!? もちろん、テストデータ、abのパラメータにより多少状況は変わりますが・・・

ActiveRecord without Railsを試した

Rackの上で直接動くシンプルな grape の性能がこんなに低いのは納得出来なかったので、ActiveRecord without Rails で試してみました

  • Rackの上で動かす grape.ru を作成
require 'grape'
require 'active_record'
require 'sqlite3'
require_relative 'app/api/simple_api'
require_relative 'app/models/todo'

use ActiveRecord::ConnectionAdapters::ConnectionManagement
ActiveRecord::Base.configurations = YAML.load_file('config/database.yml')
ActiveRecord::Base.establish_connection(:production)

run SimpleApi

この grape.ru を指定し unicorn 起動

Framework Requests per second
Grape 56.65

Ruby on Rails に比べ 1.6倍の性能が出ました!

結論

テスト中のサーバーのメモリー使用量も調べてみました。実メモリ使用量(RES)は Ruby on Railsに比べると半分以下です。

Framework Requests per second VIRT(memory) RES(memory)
Grape within Rails 33.66 345308 91828
Ruby on Rails 34.49 345308 91828
Grape 56.65 253472 38852

結論としては、grape を使って API サーバーを作るなら Rackベースで起動し、 Ruby on Railsとは別に動かした方が良い。

ただし、開発時は Ruby on Railsの中で動かした方が開発しやすいかも知れませんね。