表参道.rb #14 でDigdagについてLTしてきました

8/4に開催された表参道.rb #14でLTさせていただきました。

omotesandorb.connpass.com

ビアガーデン風ということで、会場提供していただいたSansan株式会社様のGardenというイベントスペースで開催され、飲み物、食べ物も無料提供していただきました。ごちそうさまでした。写真とっておけばよかった。

発表資料

Rubyを触る機会が以前にもまして減っているのですが、最近使ったDigdagにRuby APIがあったので、Ruby APIについてLTしました。

懇親会でもいろいろお話を伺いましたが、みなさんバッチ処理ではそれなりに困りごとがあるようだったので、興味を持って聞いていただけたのかなと思っています。

ひさしぶりのLT登壇で緊張しましたが、いくつか笑いも起こっていたので、自分も楽しんでLTできました。

資料はこちらです。

speakerdeck.com

追記

資料中に「Ruby APIから設定した配列をYAML側でfor_eachを使って処理するのはできなさそう」というようなことを書きましたが、loopでできるのではというアドバイスをいただきました。

参考として教えていただいたGistの通りにやってみたところ、lYAML内で配列を扱うことができました。

gist.github.com

@hiroysato さん @smdmts さんありがとうございました!

このようにいろいろな方から反応をいただけるのも、ちょっと勇気をだしてコミュニティで発表することの利点ですよね。

mackerel-agent-pluginsにmackerel-plugin-php-fpmが含まれてないんだけど?と言われて調べた

まえがき

mackerel-plugin-php-fpm使いたいんだけど、CentOS使ってるんだけどまだyumで入ってこないんだよねー?と言われたので、ちょっと気になったので調べてみました。

このプラグインは私が作ったものなのですが、私の場合はgo buildしたものを/usr/local/binに置くような雑運用かつUbuntu環境なのですが、実際のところどうなんでしょうか。

確認方法について

CentOSの環境が無いのでDockerで確認することにしました。Docker for Macがリリースされてから、よりDockerが気軽に使えるようになりましたね。

gist.github.com

今回はmackerel-plugin-php-fpmのインストールだけを確認したいので、mackerel-agentはインストールしていません。

確認のための準備

準備と言ってもdocker buildだけです。イメージ名は何でもいいです。

$ docker build -t centos7-mackerel-agent-plugins .

確認してみる

無事にビルドできたら、さっそくdocker runしてみます。

docker run -it --rm centos7-mackerel
Usage of mackerel-plugin-php-fpm:
  -metric-key-prefix string
        Metric key prefix (default "php-fpm")
  -tempfile string
        Temp file name
  -timeout uint
        Timeout (default 5)
  -url string
        PHP-FPM status page URL (default "http://localhost/status?json")

あら?インストールされているようですね。

もうちょっと確認してみる

インストールディレクトリを見てみましょう。

ここで察しがついているかたもいるかもしれませんが、もう少々お付き合いください。

docker run -it --rm centos7-mackerel ls -la /usr/local/bin
---snip---
lrwxrwxrwx  1 root root   33 Jul 21 14:55 mackerel-plugin-php-apc -> ../../bin/mackerel-plugin-php-apc
lrwxrwxrwx  1 root root   37 Jul 21 14:55 mackerel-plugin-php-opcache -> ../../bin/mackerel-plugin-php-opcache
---snip---

おや。リンクになっていますね。/usr/binに実体がありそうな雰囲気です。

docker run -it --rm centos7-mackerel ls -la /usr/bin
---snip---
-rwxr-xr-x  1 root root 5131476 Jul 14 07:30 mackerel-plugin-php-apc
-rwxr-xr-x  1 root root 4787124 Jul 14 07:30 mackerel-plugin-php-fpm
-rwxr-xr-x  1 root root 5131476 Jul 14 07:30 mackerel-plugin-php-opcache
---snip---

実体はここにあって、ここにはmackerel-plugin-php-fpmもありますね。

なぜ/usr/local/binにインストールされないのか

0.19.4で変わったみたいです。

mackerel.io

今までのものは互換性を保つために/usr/local/binにもおいてくれるようですね。

まとめ

インストールされてないと言っていたのが、インストールディレクトリが変わったこと以外の問題だったらちょっとわかりませんが、

Mackerelのように定期的にアップデートがあるプロダクトは、リリースノートもかかさずチェックしていくのがいいのかもしれませんね。

Raspberry PIとUSBマイクとMackerelを組み合わせて、室内の騒音レベルを可視化する

手近なものでできそうだったので、連休の合間にやってみました。

Raspberry Piの準備

Raspberry PIはすでに持っていたRaspberry Pi 2 Model Bを使います。

Raspberry Pi 2 Model B (1)

Raspberry Pi 2 Model B (1)

USBマイクはこれを使います。Raspberry Piでマイクを使うときはよく使われるものみたいですね。

SANWA SUPPLY MM-MCUSB16 USBマイクロホン

SANWA SUPPLY MM-MCUSB16 USBマイクロホン

Raspberry PiでUSBマイクを使う例は、それなりに検索するとでてきますが、以下の記事を参考にしました。

qiita.com

soundmeter

音声信号処理についての知見もないので、あまりそこで時間をかけたくないのでsoundmeterというのをつかってみました。

github.com

インストールはREADMEに従えばよいでしょう。インストールするとsoundmeterコマンドが使用できるようになります。

いくつかオプションがあり、RMSを取得するサンプリング時間や、結果の出力方法などを指定できます。

ちなみにRMSはRoot Mean Squareの略のようです。ざっくりイメージを掴むなら以下の記事を参考にしてみてください。

sleepfreaks-dtm.com

USBマイクのGAIN調整

soundmeterが使えるようになったら、USBマイクのGAINを調整します。

計測したい場所にRaspberry PIをおいて、soundmeterを実行しておきます。

オプション無しで実行すると、0.5秒ごとのRMSが表示されます。

もう一つターミナルを開いて、alsamixerコマンドを実行し、マイクのGAINを調整します。

f:id:ariarijp:20160717230306p:plain

Mackerelで可視化

ここまででRaspberry PiとUSBマイクを使って騒音レベルが取得できるようになったので、あとはMackerelにメトリックを送信するだけです。

Raspberry PiへのMackerel導入については割愛しますが、以前Qiitaに記事を書いたので、多少は参考になるかもしれません。

qiita.com

カスタムメトリックを使う

Mackerelにメトリックを送信するには、カスタムメトリックの仕様にあわせてスクリプトを書く必要があります。

mackerel.io

グラフ定義とメトリックを出力するスクリプトであれば、監視対象のホストで動けばどんな言語で書いてもいいと思いますが、

今回はRaspberry Piをつかっていることもあるので、素直にPythonで書いてみました。

gist.github.com

これを適当なディレクトリーに置きます。今回は/opt/mackerel-check-scripts/soundmeter.pyに置くものとしておきましょう。

mackerel-agent.confに設定を追加する

[plugin.metrics.soundmeter]
command = "/opt/mackerel-check-scripts/soundmeter.py"

上記の設定を追加し、mackerel-agentを再起動すると、数分後には以下のようなグラフが描画できていると思います。

f:id:ariarijp:20160717231903p:plain

実際のグラフを見ると途切れている箇所がありますが、時折soundmeterが以下の様なエラーを出してしまうので、おそらくこれが原因だと思いますが、厳密に計測したいわけでもないので、これは無視しています。

pi@raspberrypi:~ $ soundmeter
         8  Traceback (most recent call last):
  File "/usr/local/bin/soundmeter", line 9, in <module>
    load_entry_point('soundmeter==0.1.3', 'console_scripts', 'soundmeter')()
  File "/usr/local/lib/python2.7/dist-packages/soundmeter/meter.py", line 311, in main
    m.start()
  File "/usr/local/lib/python2.7/dist-packages/soundmeter/meter.py", line 116, in start
    self.record()  # Record stream in `AUDIO_SEGMENT_LENGTH' long
  File "/usr/local/lib/python2.7/dist-packages/soundmeter/meter.py", line 90, in record
    data = self.stream.read(FRAMES_PER_BUFFER)
  File "/usr/local/lib/python2.7/dist-packages/pyaudio.py", line 608, in read
    return pa.read_stream(self._stream, num_frames, exception_on_overflow)
IOError: [Errno -9981] Input overflowed

まとめ

すでにRaspberry PiやUSBマイクなどの機器が手元にあり参考にできる記事も多く、Mackerelの扱いにもある程度慣れているので、連休の合間に2時間ほど作業しただけでできました。

最近はre:dashやAnsible、AWS Lambdaなど、Pythonで書かれたもの/書くもののお世話になる機会が増えてきたので、もうすこしPythonを書けるようになりたいなと思ったりもします。

re:dashがインストールできなくなったので調査した

7/3 22:30追記

re:dash開発者の @arikfr さんがこの記事を読んでくれたらしく。setuptoolsが修正されるまではこの記事で紹介したワークアラウンドを入れることになったようです。

github.com

github.com

なので、これからインストールする方にはこの記事は不要なのですが、記事は自分の活動記録としてそのまま残しておきます。

まえがき

Ubuntu用のセットアップスクリプトでre:dashをインストールしようとしたらインストールできなくなってたので調べました。

使用した環境はVagrantで起動したUbuntu14.04で、Vagrant Boxはubuntu/trusty64 (virtualbox, 20160627.0.0), re:dashのバージョンは0.10.1.b1834です。

検証に使用したVagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.network "forwarded_port", guest: 80, host: 9001

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end

  config.vm.provision "shell", inline: <<-SHELL
    sudo locale-gen ja_JP.UTF-8
  SHELL
end

セットアップスクリプト実行中のエラー

$ curl https://raw.githubusercontent.com/getredash/redash/master/setup/ubuntu/bootstrap.sh
$ sudo bash bootstrap.sh

VMにログインし上記の様にスクリプトを実行したところ、途中は割愛しますがre:dashが依存しているモジュールを pip でインストールするあたりでこんなエラーが出てしまいました。

Downloading/unpacking httplib2==0.9.2 (from -r requirements.txt (line 1))
  Downloading httplib2-0.9.2.zip (210kB): 210kB downloaded
  Running setup.py (path:/tmp/pip_build_root/httplib2/setup.py) egg_info for package httplib2
    Traceback (most recent call last):
      File "<string>", line 3, in <module>
      File "/usr/local/lib/python2.7/dist-packages/setuptools/__init__.py", line 14, in <module>
        from setuptools.extension import Extension
      File "/usr/local/lib/python2.7/dist-packages/setuptools/extension.py", line 11, in <module>
        from . import msvc
      File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 244, in <module>
        class PlatformInfo:
      File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 253, in PlatformInfo
        current_cpu = safe_env['processor_architecture'].lower()
      File "/usr/lib/python2.7/UserDict.py", line 23, in __getitem__
        raise KeyError(key)
    KeyError: 'processor_architecture'
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 3, in <module>

  File "/usr/local/lib/python2.7/dist-packages/setuptools/__init__.py", line 14, in <module>

    from setuptools.extension import Extension

  File "/usr/local/lib/python2.7/dist-packages/setuptools/extension.py", line 11, in <module>

    from . import msvc

  File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 244, in <module>

    class PlatformInfo:

  File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 253, in PlatformInfo

    current_cpu = safe_env['processor_architecture'].lower()

  File "/usr/lib/python2.7/UserDict.py", line 23, in __getitem__

    raise KeyError(key)

KeyError: 'processor_architecture'

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /tmp/pip_build_root/httplib2
Storing debug log for failure in /root/.pip/pip.log

re:dashのセットアップスクリプトでいうと以下の辺りです。

原因を考える

ちょっと前に使った時は問題なかったのですが、なぜエラーが発生するようになってしまったのでしょうか。

いろいろ調べて回ったのですが、同様の事例や解決策を見つけられなかったのでsetuptoolsリポジトリを見てみました。

github.com

リリースを見てみると、最近リリースがあったようです(2016/7/03 10時現在では8時間前)

先ほどのエラーメッセージに/usr/local/lib/python2.7/dist-packages/setuptools/msvc.pyとあったので、該当のファイルが追加されたPull Requestをみてみます。

github.com

たぶんこのあたりでなんかあるんだろうなと思ったので、セットアップスクリプトを以下のように変えて再度実行します。

@@ -29,7 +29,7 @@
 apt-get -y update
 apt-get -y dist-upgrade
 apt-get install -y python-pip python-dev nginx curl build-essential pwgen
-pip install -U setuptools
+pip install -U setuptools==23.1.0

 # redash user
 # TODO: check user doesn't exist yet?

再実行

再実行時は念のためVMを破棄し、再度作成しなおしたVM上でスクリプトを実行したところインストールが問題なく完了し、 http://localhost:9001/ にアクセスすることが出来ました。

まとめ

re:dashは頻繁にアップデートされるため、たまにインストールがうまくいかないことがある印象があるのですが、今回はsetuptoolsの問題でした。

おそらくsetuptoolsもそのうち修正されるんじゃないかと思いますが、re:dashを使おうとして同じような問題にぶつかった方がこの記事を参考にしていただけたらうれしいです。

iOS9のWebView内でTwitterのシェアボタンをクリックしたらiOS標準のダイアログで投稿したい

まえがき

同僚さんがタイトルに書いてあるようなことをやりたいけどできないと言っていたのでちょっと調べてみました。

やってみた

正しいやり方かどうかは別として、こんなやり方になりました。

import UIKit
import Social

class ViewController: UIViewController, UIWebViewDelegate {
    @IBOutlet weak var webView: UIWebView!
    
    let twitterIntentUrl = "https://twitter.com/intent/tweet"
    let initialUrlString = "http://ariarijp.hatenablog.com"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = NSURL(string : initialUrlString)
        let urlRequest = NSURLRequest(URL: url!)
        
        webView.delegate = self;
        
        // WebViewの初期表示
        webView.loadRequest(urlRequest)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        let absUrl = request.URL?.absoluteString
        
        // リクエストされたURLがTwitterのシェアボタンっぽいものだったら、Social.frameworkでダイアログを表示する
        if absUrl!.containsString(twitterIntentUrl) {
            // アカウントの設定が済んでいれば投稿ダイアログ、設定が済んでいなかったらアラートを表示する
            // 参考: http://www.brianjcoleman.com/tutorial-share-facebook-twitter-swift/
            if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
                // URLからシェアに使うテキスト、URLを取り出す
                let tweetUrl = getQueryStringParameter(request.URL?.absoluteString, param: "url")
                let tweetText = getQueryStringParameter(request.URL?.absoluteString, param: "text")
                
                let twitterSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
                twitterSheet.setInitialText("\(tweetText!) \(tweetUrl!)")
                self.presentViewController(twitterSheet, animated: true, completion: nil)
            } else {
                let alert = UIAlertController(title: "アカウントエラー", message: "Twitterにログインしてください", preferredStyle: UIAlertControllerStyle.Alert)
                alert.addAction(UIAlertAction(title: "閉じる", style: UIAlertActionStyle.Default, handler: nil))
                self.presentViewController(alert, animated: true, completion: nil)
            }
            
            return false
        }
        
        return true
    }
    
    // URLから指定したパラメータを文字列として取り出す
    // 参考: https://gist.github.com/gillesdemey/509bb8a1a8c576ea215a#gistcomment-1483938
    func getQueryStringParameter(url: String?, param: String) -> String? {
        if let url = url, urlComponents = NSURLComponents(string: url), queryItems = (urlComponents.queryItems) {
            return queryItems.filter({ (item) in item.name == param }).first?.value!
        }
        return nil
    }
}

StoryboardでwebViewを配置して、このViewControllerにOutletを追加してあるのが前提です。

また、はてなブログのURLで動作確認してますが、HTTPSでは無いのでATSの設定をいじったりしてます。

動作確認

動かしてみるとこんな感じです。

Twitterにログインしていない状態でアプリ内WebViewでシェアボタンをタップ

f:id:ariarijp:20160621233518p:plain

Twitterにログインした状態でアプリ内WebViewでシェアボタンをタップ

f:id:ariarijp:20160621233543p:plain

今回のような実装を入れなかった場合(デフォルトの挙動

f:id:ariarijp:20160621233636p:plain

まとめ

雑な感じで「できないことはないでしょう」と言った手前ムキになってやった感がありますが、思ったようなことはできたのでよかったです。

もっと効率が良いやりかたがあれば、Twitterなんかでぜひ教えて下さい。