山口小夜子の映画「氷の花火」を見た個人的な記憶
めずらしく、技術的ではないことを書きました。
「山口小夜子」覚えてますか? 知ってますか? ファッションとは全く無縁な私がこんなことを書くのは恥ずかしいのですが、若いころ山口小夜子は憧れの人でした。
先週の土曜日に、映画 「氷の花火」 を見て気まました。映画は山口小夜子の遺品の整理風景から始まり、彼女と一緒に仕事をした人たちへのインタビューから彼女の一生を描き、最後に現在のモデルを使い山口小夜子を再現するというプロジェクトという内容でファンとしてはとても見応えのある内容でした。
幾つかのメモ:
- 山口小夜子といえば黒髪のおかっぱに切れ長の目ですが、実はくっくりとした目で切れ長はメークで作っていた!
- 昔、デーモニッシュな資生堂の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 の利用イメージは
- Chefを開発環境にインストール
- 利用開始時にChefのRubyインタプリターを含む実行環境をターゲットにインストール (knife solo prepare コマンド)
- レシピの作成・修正
- 実行 (knife solo cooke コマンド)
- レシピの文法チェック等
- レシピを含むcookbookをターゲットへ転送
- レシピをターゲットで実行
- 問題があれば 3.に戻る
ですが、Itamaeは
- Itamaeを開発環境にインストール
- レシピの作成・修正
- 実行 (itamaeコマンド)
- レシピに対応する操作をsshを使いターゲット上で実行
- 問題があれば 2.に戻る
ChefのレシピのRubyコードはターゲット上で実行されますが、Itamaeはレシピは開発環境で実行されます。 したがって、ターゲットの状況によって処理を変更する部分、たとえば not_if や only_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_file
はremote_file
- Chef の
remote_file
に相当するものは無い? - ファイル等のモード指定に8進数が使えない
user
のaction: :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) を使い表示するようにしました。
2. 新規リポジトリーの作成
Web上かから新規にGitリポジトリーが作成作成出来るようにしました。Ginatraで使っているRugged (libgit2のRubyインターフェース)に git init のAPIがあったので簡単にできました。
3. その他
- なぜかUTF-8を含むテキストファイルをbinaryとして表示してしまう問題をアドホックに対処
- gitレポジトリーへのアクセスは ssh を使っているので URL表示等を ssh に変更
- Unicornで使う為のコンフィグ等の追加
などです。まだ RSpec を修正していません ^^;
RedmineのコンテンツをMarkdownに変換しました
Textile vs Markdown
軽量マークアップ言語にはたくさんの種類があります。私も教育で使うテキストの作成やRedmineのWikiでは長年 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が選択できるのでこれを使えば良いかもしれません。
しかし、私はRedmineのWikiを教育でよく使っています。お客様毎に教育で使うコード、ヒント、参考情報・・・などを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に付いて
- 良いところ、悪いところ
- MacへのRuby, Ruby on Railsのインストール方法
Ruby on Rails を使い簡単なバックエンドを作る
- クライアントになるiOS アプリの紹介 ーー Swiftで作りました
- Ruby on RailsのScaffoldでバックエンドを作る実演
バックエンドを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 に書かれているようにいくつかの構成で動かせます
- Rack: Rack上で動かす
- ActiveRecord without Rails: Rackの上で動かすけど ActiveRecord を使う
- Rails: Ruby on Railsの中で動かす
- ...
今回のシステムでは管理者用の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)) }
json.array!(@todos) do |todo| json.extract! todo, :id, :due, :task, :created_at, :updated_at end
Grape
- 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.json で JSONを返せるます。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の中で動かした方が開発しやすいかも知れませんね。