ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

Yesodのquick startが重い

 題に掲げた問題を解決するための記事ではないです。HaskellのwebアプリケーションフレームワークYesodquickstartをDockerfileで行っただけの記事です。

Dockerfile

 Dockerfileを書きます。

FROM haskell:latest
MAINTAINER ushumpei
ARG project_name
RUN stack new $project_name yesod-sqlite \
  && cd $project_name \
  && stack build yesod-bin cabal-install --install-ghc \
  && stack build
CMD ["/bin/sh"]

 ビルドします。任意のプロジェクト名を引数に取れるようにARGで指定しています。引数を渡すには--build-argオプションをつけて、イメージ自体に名前をつけるために-tオプションをつけます。

docker build -t yesod_sample . --build-arg project_name=sample

 ビルドできたらコンテナを立ち上げます。めちゃくちゃ時間がかかります。。。

docker run -i -t --rm -p 3000:3000 yesod_sample /bin/bash

 立ち上がったらコンテナのシェルが起動します、/以下にARGで指定した名前のプロジェクトができているので移動し、起動コマンドを実行します。

cd /sample
stack exec -- yesod devel

 コンパイルマイグレーションが終わればlocalhost:3000で「Welcome To Yesod!」のページにアクセスできるようになります。

感想

 stack build yesod-bin cabal-install --install-ghcがめちゃくちゃ重くて面倒でした。DockerHubに重い部分がいい感じに済んでいるイメージとかありそうですね。dockerstackyesodとわからないことが二つ以上あるともうしっちゃかめっちゃかです。

Leap Motionを手に入れました

 Leap Motionを手に入れました。Leap Motionは手の動きをかなりの精度で取得できるセンサーです。たくさんの言語でプログラミング可能なAPIが提供されていますが、とりあえずjavascriptAPIを使ってみます。また、各クラスの内容もざっくりと整理していこうと思います。

 ここでは、開発PCがmacなのでSDKV2 desktopを使います。Windowsの方は新しいSDKが使えるようなのでそちらを使ったほうがいいかもしれません。(VR関係に強化されているように見えます)

参考

とりあえず

 環境設定やインストールは省略します、公式サイトの指示通りにすれば問題ないはずです。

 仕組みを理解するために、とりあえずブラウザ上に手を表示させてみます。まずはセンサーの値を取れるか確認をするためのコードを記述します。

ブラウザのコンソールに吐き出すだけ

<html>
  <head>
    <!-- <script src="./leap-0.6.4.js"></script> -->
    <script src="https://js.leapmotion.com/leap-0.6.4.js"></script>
    <script>
      Leap.loop(frame => console.log(frame));
    </script>
  </head>
  <body>
  </body>
</html>

 LeapJSを外部リソースとして取得します。LeapはLeapJSがネームスペースとして確保しているオブジェクトのようです。

 値をとるにはLeap.loopにコールバック関数を渡します。この関数にセンサーが取得したframeオブジェクトが引数で渡されるので、それに対する処理を記述するのがアプリケーション作成の基本的な流れになると思います。

 frameオブジェクトの詳細はリファレンスに書いてあります。手、指、ツール(棒状のもの)などのデータをプロパティとして持っていてくれています。

プラグイン Rigged Handを使う

 LeapJSはプラグインという形で機能拡張できるようになっています。プラグインControllerクラスによって管理されます。各プラグインpluginメソッドにより登録しuseメソッドにより有効にしたり、useメソッドに直接関数を渡すことで、loop実行時にコールバックとして呼び出されるようになります。

 プラグインを使うための準備として、まずは先ほどの処理をLeap.loopを使わない記述方法に変えておきます。

<html>
  <head>
    <!-- <script src="./leap-0.6.4.js"></script> -->
    <script src="https://js.leapmotion.com/leap-0.6.4.js"></script>
    <script>
      const controller = new Leap.Controller();
      controller.use(
        () => ({
          frame: frame => console.log(frame),
        })
      );
      controller.connect();
    </script>
  </head>
  <body>
  </body>
</html>

 プラグインを追加する準備が整ったところで、リアルな手を表示することができるRigged Handというプラグインを追加してみます。

 プラグインを使用するにはleap.rigged-hand-0.1.7.jsの他に、いくつかのライブラリを読み込む必要があります。useメソッドでriggedHandを使用するようにします。

<html>
  <head>
    <!--
      以下から必要なライブラリを取得
      https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.js
      https://js.leapmotion.com/leap-0.6.4.js
      https://js.leapmotion.com/leap-plugins-0.1.10.js
      https://github.com/leapmotion/leapjs-rigged-hand/blob/master/build/leap.rigged-hand-0.1.7.js
    -->
    <script type="text/javascript" src="three.js"></script>
    <script type="text/javascript" src="leap-0.6.4.js"></script>
    <script type="text/javascript" src="leap-plugins-0.1.10.js"></script>
    <script type="text/javascript" src="leap.rigged-hand-0.1.7.js"></script>

    <script>
      const controller = new Leap.Controller();
      controller.use('riggedHand');
      controller.connect();
    </script>
  </head>
  <body>
  </body>
</html>

   動作はこんな感じになりました。実際の手も一緒に撮影すればよかったです。。。


Leap Motion - Rigged Hand Demo

LeapJSのクラスに関する考察

 API Referenceによると、LeapJSには次のクラスが存在します。

制御

クラス名 概要
Controller Leap MotionAPIに接続するためのインターフェース。オプションや、フレーム更新時のコールバックの設定など、最もさわりそうなオブジェクト
InteractionBox Leap Motion Controllerに紐づいた表示領域を扱うオブジェクト?領域に応じたベクトルの正規化方法などを提供してくれるみたいです
Frame センサーがフレーム更新ごとに取得した手、指に関するデータを含むオブジェクト

パーツ

クラス名 概要
Pointable 指(Finger)やツールなどの抽象クラスで位置や方向、速度などを保持しているオブジェクトです
Hand 手のオブジェクトで、5本の指の配列や腕などプロパティに持っています。かなり豊富な情報を持っているようです
Finger 指のオブジェクトで、生えている位置や、手の骨の本数文の骨オブジェクトに関する情報を持っています。何指か、などの識別子も持っています
Bone 骨のオブジェクトで、指の骨を表しています。前後の関節を持っています

ジェスチャー

 ジェスチャーは多いので個々の概要は省略します。認識してくれるハンドサインという認識です。独自に追加できるかはちょっとわからないです。

  • Gesture
    • CircleGesture
    • ScreenTapGesture
    • KeyTapGesture
    • SwipeGesture

計算要素

クラス名 概要
Matrix math 行列のオブジェクト。WebGL用外部ライブラリを使っているようです
Vector math ベクトルのオブジェクト。WebGL用外部ライブラリに入っているvec3を使っているようです

感想

 API Referenceを読んで、勝手に各クラスの概要などを書きましたが、このドキュメントを和訳した方が随分人のためになるんだろうな、と思いました。(もしかしたら和訳されている?)

 まだまだわかっていないことが多いので、ご指摘いただければ幸いです。

screenコマンドの小さな話

 ターミナル操作をいい感じにできる仮想端末マネージャコマンドscreenを最近知りました。メモしておきます。

 私の理解では、ターミナルをラップして、通常のシェル操作に加え、セッション、ウィンドウ、領域という概念を取り込み、それらを操作できるようにするものです。

参考: Screen User's Manual

 screenを実行すると仮想端末のセッション(session)とそれに紐づく一つのウィンドウ(window)が起動します。ウィンドウ内ではいつも通りのシェルの操作に加え、セッションに紐づくウィンドウを複数起動できるようになるので、ssh等でサーバに接続した後で起動すると便利です。今まで複数ログインしていた場合、一度ログインしてウィンドウを複数起動することで済ますことができます。(TeraTermのセッションの複製に似た感じです。詳しく見ていないですが内部的に使っているのでしょうか?)

 使うことのメリットとして、

  • 複数ウィンドウを起動できるので、面倒な踏み台経由のssh接続時に結構楽できます。
  • 接続が切れてもセッションが残っていてくれるので、再度つなぎなおした時に作業をすぐに再開できます。一つのウィンドウで長い処理を走らせて寝かせておくこともできます。

 他にも、コマンドによるコピー&ペーストの操作をキーボードのみでできるようになることも、なかなか嬉しいことです。

基本的な使い方

 触っていてよく使っているコマンドを羅列します。

セッション管理系

コマンド 内容
screen 新しいsessionとそれに紐づいた一つのwindowを起動します。
screen -ls 現在起動しているsessionの一覧を表示します
screen -r 最後に繋いでいたsessionを再開します。sessionのidを渡せば指定したセッションにつなぎ直すことができます
Ctrl+a d session起動中に現在のsessionから抜ける(detach)ことができます。detach後もsessionは起動しているので、一覧で確認できます
Ctrl+a \ session起動中、session自体を削除します。一覧からも消えます。起動中の紐づいた全てのwindowも削除されます。私の環境だとCtrl+a Ctrl+¥になっていました

チートシート

 セッション起動後は、Ctrl+a ?でコマンドのチートシートが表示されます。私のメモの存在意義を奪いかねないですが、ただ表示されるコマンドがアルファベット順なのでちょっと見にくいです。そこに漬け込みます。

 以下主にセッション起動後のコマンドになります。

ウィンドウ管理系

コマンド 内容
Ctrl+a c 新しいwindowを開きます
Ctrl+a k windowを削除します、正確には削除しますか?と(y/n)で聞かれます
Ctrl+a " windowの一覧を表示します。j,kでwindowを切り替えることができます
Ctrl+a N 現在開いているwindowの名前(Name?)を表示します
Ctrl+a n, Ctrl+a p 別のwindowに切り替えます。多分切り替え先はprevious,nextに対応していると思います
Ctrl+a Ctrl+a 切り替え前のwindowに切り替えます

領域管理系

コマンド 内容
Ctrl+a S windowを水平に分割します。windowが紐づかない新しい領域(region)を作成します
Ctrl+a | windowを垂直に分割します。windowが紐づかない新しいregionを作成します
Ctrl+a TAB 分割されたregion間を移動します
Ctrl+a X 分割時、現在有効になっているregionを削除します
Ctrl+a Q 分割時、現在有効になっていない他のregionを削除します

メタ操作系(未整理)

コマンド 内容
Ctrl+[ コピーモードに入ります。hjkl等のvimライクな入力で移動することができます。spaceを押すとコピー開始位置を指定、もう一度spaceを押すと選択範囲をコピーします
Ctrl+] バッファのコピーした内容をペーストします

使っていての感想

 色々あります。

  • Ctrl+aがシェルの行頭移動を奪ってしまいます。Ctrl+a aに行頭移動が振り分けられているので頭を切り替えて使っています。セッションの中にいる、と言うことを意識できるのでこれはこれでいいかもしれないです。キーバインドを変えるのは嫌なので私はこのままです。慣れました。
  • vimの画面分割と一緒に使うと結構混乱します。vimCtrl+w系の画面移動とごっちゃになったり、screenでのコピー範囲が行単位なので行番号入っちゃったりしました。これは対応方法がわからないですが、慣れました。
  • スクロールについて最初どうすればいいか不明でした。ログ出力等で流れてしまった時は、コピーモードに入って遡っています。別にコピーしたいわけではないのでちょっと気持ち悪いです。vimの気分で操作しています、less同様いまだに正しい移動操作がわかりません。
  • macにデフォルトで入っているscreenはヴァージョンが古いようで垂直分割ができないです(2016/12/14に確認)。homebrewで入れ直しました。あと、ssh接続時に便利とか書いていましたが意外にコマンド自体が入っていないことがあったりします、注意です。

 他にもウィンドウ操作を記録することができたりするのですが、使い道がわからず今のところ使っていないです。さらに他にも複数人で同じセッションを共有するマルチディスプレーモードという便利なものもあるそうですが、私はぼっちなので使っていません。

感想

 コピー&ペーストがキーボードから手を動かさずに使えるので、嬉しくてローカルでも使っています。早く使いこなせるようになるといいのですが。まだまだ不明点が多いです。

 上記内容に間違いがあれば申し訳ないですが、修正しますので是非教えていただきたいです。

dockerに関するメモ

golangを少しだけ試して見たくなったけれど、環境を作りたくなかったのでdockerを使って見ました。特に目新しい内容ではないですが、自分用にメモします。

構成というほどでもないですが、

  • コーディング: 自分のマシン
  • 実行環境: コンテナ

とするために、まずはディレクトリを作成し、そのディレクトリをコンテナと共有して起動します。

mkdir ~/my_golang_src
cd ~/my_golang_src
docker run -i -t --rm -v `pwd`:/my_golang_src golang /bin/bash

シェルが開くので、/my_golang_srcに移動してgo runなどでソースを実行できるようになります。

redisのレプリケーション

 redisレプリケーションがとても簡単だったのでメモです。勉強も兼ねてdockerを無駄に使っています。環境は以下です。

  • macOS Sierra
  • Docker for Mac Version 1.12.3-beta29.2

 とりあえずredisが入ったAlpine Linuxのイメージを作成します。my-alpineディレクトリを作りその中にDockerfileを以下の内容で作成します;

FROM alpine:latest
MAINTAINER ushumpei
RUN apk --no-cache add redis
CMD ["/bin/sh"]
$ docker build -t my-alpine .
$ docker run -it --rm  my-alpine

 redisがインストールされた状態でAlpine Linuxが起動しました。

単一マシン上で

 先ほどのmy-alpineを使って、同一ホストで複数のredisを起動し、master、slaveのレプリケーション構成を行ってみます;

/ # redis-server --port 3679 &
/ # redis-server --port 3680 --slaveof localhost 3679 &

 本当はちゃんと設定ファイルを書いたほうがいいんだと思います。レプリケーションを確認すると;

/ # redis-cli -p 3679 INFO
...
# Replication
role:master
connected_slaves:1
slave0:ip=::1,port=3680,state=online,offset=519,lag=1
...

/ # redis-cli -p 3680 INFO
...
# Replication
role:slave
master_host:localhost
master_port:3679
...

/ # redis-cli -p 3679 set hoge fuga
OK
/ # redis-cli -p 3680 get hoge
"fuga"

とちゃんとなっているようです。

docker

 docker-composeでmaster: 1台、slave: 2台の構成を作ってみようと思います。新たにディレクトリ(docker-redisにしました)を作成し、その中にdocker-compose.ymlを以下のように作成しました。

version: '2' 

services:
  master:
    image: redis:latest
    restart: always
    ports:
      - 3679:3679
  slave_1:
    image: redis:latest
    restart: always
    ports:
      - 3680:3679
    command: redis-server --slaveof master 6379
  slave_2:
    image: redis:latest
    restart: always
    ports:
      - 3681:3679
    command: redis-server --slaveof master 6379

 slave_1slave_2からはmasterhostmasterで参照できるようになっているようで、分かるまで悩みました(参考: Compose のネットワーク機能)。docker-compose upで起動します。

 動作確認を先ほどのmy-alpineから行います。--netオプションで先ほどのdocker-compose upで起動したコンテナたちのネットワークに参加します。こうすることで、masterなどのホストを参照することができるようになります。

(ネットワーク名はデフォルトではディレクトリ名_defaultになるらしく、docker network lsでも確認できます。ディレクトリのハイフンが無視されています、どういうルールでしょう?)

$ docker run -it --rm --net dockerredis_default my-alpine

/ # redis-cli -h master INFO
...
# Replication
role:master
connected_slaves:2
slave0:ip=172.19.0.4,port=6379,state=online,offset=2367,lag=1
slave1:ip=172.19.0.2,port=6379,state=online,offset=2367,lag=1
...

/ # redis-cli -h slave_1 INFO
...
# Replication
role:slave
master_host:master
master_port:6379
...

/ # redis-cli -h slave_2 INFO
# Replication
role:slave
master_host:master
master_port:6379
...

/ # redis-cli -h master set hoge fuga
OK
/ # redis-cli -h slave_1 get hoge
"fuga"
/ # redis-cli -h slave_2 get hoge
"fuga"

感想

 redisレプリケーションってすごく簡単にできるように思えました。pub/sub使って何か作ってみたいです。