2013年12月7日土曜日

Play Frameworkでサブプロジェクトを使い倒す

Play framework 2.x Java and 1.x Advent Calendar 2013の7日目です。

さて今日はサブプロジェクトをうまいこと使おうよ、というお話です。
1つのプロジェクト内のcontrollerにadminディレクトリを掘っていた時期もありました。。。
運用の話はどちらかと言うと、他の人から色々聞いてみたい。。。多分自分のスクリプトはかなりしょっぱい。。。。(メモリエラー対策のため1時間に1回Jenkinsでサービスの再起動をさせている・・・)


で、今回の話はPlayのドキュメントだとWorking with sub-projectsに当たる部分になります。
Playはsbtのmulti-buildをよろしく使っているようです。
間違っている部分やもっといい方法などがあれば指摘していただけると嬉しいです。

Playは個人プロジェクトとか小規模なプロジェクトでこそ、その開発スピードなどの威力を発揮していると個人的には感じているのですが、チームで大規模な開発をしていくケースもあるかと思います。そういった場合にこのサブプロジェクトが多いに役立つのではないかと思います。



対象者によってディレクトリを区切るケース


例えばPlayで求人のマッチング系のサービスを作っていくというケースを考えてみます。
  • 求職者側は http://hoge.com/
  • 採用者側は http://hoge.com/staff
  • サービス運用者側は http://hoge.com/manager
のURLで使うケースを想定してステップごとにサブプロジェクト対応をしていく方法を考えてみます。



STEP1.ライブラリのサブプロジェクト化


Javaの開発をやっていく中でそのチームで共通的に使用しているライブラリ的な物があるかと思います。
例えば、日付のフォーマッターやUser-Agentの切り分けルール、テキストにwbrを埋め込む処理などのPlayにかかわらず使用できるライブラリです。
これらの資産をPlayでも使っていく場合に、なるべくパッケージ名は固定にしたいとか、プロジェクトに依存せずに使いたい、という要望が出てくるかと思います。

そんな時に使うのがサブプロジェクトになります。
もちろんプラグインとして利用する、jarファイルとして読み込む、などの方法も可能かと思います。ただプロジェクトを進める中で共通ライブラリも合わせて強化してフィードバックをしていく場合は、サププロジェクト化してすぐに編集できる状態にしておくのも有用かと思います。

自前ライブラリはproject/Build.scala に以下のように追記することで、Playの他のプロジェクトに依存せずに使えるようになります。
import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

  val appName = "hoge.com"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    javaCore,
    javaJdbc,
    javaEbean)

  val common = Project(appName + "-common", appVersion, appDependencies, path = file("modules/common"))

  lazy val main = play.Project(appName, appVersion, appDependencies).settings( // Add your own project settings here
  ).dependsOn(common)

}


やっている内容としては、

  • val common の行でサブプロジェクトの追加
  • lazy val main にdependsOn(common)で依存関係の追加

になります。
この場合のフォルダ構成は以下のようになります。

app
conf
public
modules
 └ common
    └ src
       └ main
           └ java
project
 └ Build.scala

Adding a simple library sub-projectを見てみると、サブプロジェクトにすることで、コンパイルする単位をサブプロジェクトごとに出来るようです。Java,Scalaそれぞれ100ファイルを超えてくるとコンパイルに時間がかかるので、これは地味に嬉しい・・・。まあデバッグモードだと依存関係のあるプロジェクトもビルドされ直すようですが。


STEP2.Playの拡張機能のサブプロジェクト化


Playで開発をしていると、Controllerに共通の処理を埋め込みたくなる場合があると思います。セッション系の処理をゴニョゴニョしたりサイトのtitleを出力させたりFacebook用のタグを生成したりなどなど全ページ共通処理を行う場合ですね。
そんな時にはControllerをextendsしたBaseControllerを作って、プロジェクトではそれをさらにextendして各種Controllerを呼ぶ、なんてことがあると思います(この基底になるプロジェクトをbaseプロジェクトとします)。
この場合だとsbtプロジェクトではなくなり、Playプロジェクトになるのでproject/Build.scala に以下のように記載することになります。

import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

  val appName = "hoge.com"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    javaCore,
    javaJdbc,
    javaEbean)

  val common = Project(appName + "-common", appVersion, appDependencies, path = file("modules/common"))
  lazy val base = play.Project(appName + "-base", appVersion, appDependencies, path = file("modules/base"))

  lazy val main = play.Project(appName, appVersion, appDependencies).settings( // Add your own project settings here
  ).dependsOn(common,base).aggregate(base)

}

lazy val baseの行にありますが、プロジェクトが Projectからplay.Projectになりました。Playに依存するプロジェクトの場合、play.Projectにする必要があります。

Playは必要最低限の準備しかしてないので、みんな独自の拡張をしていると思います。
これで自分用のPlayプロジェクトのベースが準備出来ました。
このサブプロジェクトに直接アクセスされちゃうんじゃないの?という心配ですが、routesファイルが記載されていなければ、大丈夫かと思います。

STEP3.ディレクトリ区切りのサブプロジェクト化

さて、チームで開発していく中で問題になるのが、routesファイルのコンフリクト問題です。
モジュール(controller)別で開発担当者が違う場合にroutesファイルがコンフリクトを起こしまくって色々な感情と問題を引き起します。

そんな時にサブプロジェクトにします。そうすることで多少なりともroutesファイルのコンフリクトを避ける事が出来るようになります。

先述の
  • 求職者側は http://hoge.com/
  • 採用者側は http://hoge.com/staff
  • サービス運用者側は http://hoge.com/manager
の場合、staff,managerをそれぞれサブプロジェクトにするイメージです。
この場合もサブプロジェクトはsbtのプロジェクトではなくPlayのプロジェクトになります。
・・・とここで気がつくわけです。Ebeanの処理は共通ではないだろうか・・・?と。
Modelは求職者側だろうと採用者側だろうとだろうと同じになります。プロジェクト内で共通になるModelをさらに別出ししてサブプロジェクト(この場合、coreプロジェクトとします)にします。

project/Build.scala に以下のように記載することになるかと思います。
import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

  val appName = "hoge.com"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    javaCore,
    javaJdbc,
    javaEbean)

  val common = Project(appName + "-common", appVersion, appDependencies, path = file("modules/common"))
  lazy val base = play.Project(appName + "-base", appVersion, appDependencies, path = file("modules/base"))
  lazy val core = play.Project(appName + "-core", appVersion, appDependencies, path = file("modules/core"))

  lazy val staff = play.Project(appName + "-staff", appVersion, appDependencies, path = file("modules/staff")).dependsOn(common,base,core)
  lazy val manager = play.Project(appName + "-manager", appVersion, appDependencies, path = file("modules/manager")).dependsOn(common,base,core)

  lazy val main = play.Project(appName, appVersion, appDependencies).settings( // Add your own project settings here
  ).dependsOn(common,base,core,staff, manager).aggregate(base, core,staff, manager)

}

これでroutesファイルをサブプロジェクトごとに分割して管理できるようになりました。routesファイルもこのようにして分けることが出来るようになります。

conf/routesはこうなります。
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index()

->  /staff staff.Routes
->  /manager manager.Routes

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

上記の/staff staff.Routesで指定されていた、modules/staff/conf/staff.routesはこうなります。
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.staff.Application.index()
GET     /detail/:id     controllers.staff.Detail.index(id:Long, page: Integer ?= 1)


# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

staff.routesはあらかじめprefexとして /staff がつくことがconf/routesで指定されているので、modules/staff/conf/staff.routesに記載されている
GET     /detail/:id     controllers.staff.Detail.index(id:Long, page: Integer ?= 1)
は実質的に
http://hoge.com/staff/detail/1
とかのURLに該当するようになります。

ちなみにhttp://www.playframework.com/documentation/2.1.x/SBTSubProjectsにあるように、
GET     /assets/*file               controllers.admin.Assets.at(path="/public", file)
のような書き方は試してみたもののうまく行かず。。サブプロジェクごとにAssetsを指定することで、読み込むディレクトリを切り替えられる(?)みたいです。
jQueryとかbootstrapは共通の呼び出してるからいいか・・と
GET     /assets/*file               controllers.Assets.at(path="/public", file)
のように共通の場所を呼び出す形で放置をしています。

このようにサブプロジェクトに分けることで、プロジェクトごとに開発担当者を分けて開発をしやすくなります。

最終的にディレクトリ構成はこのようになります。

app
  └ controllers
  └ models
  └ views
conf
  └ application.conf
  └ routes
modules
  └ base
    └ app/controllers
 └ common
    └ src
       └ main
           └ java
  └ core
    └ app/models
  └ manager
    └ conf/manager.routes
    └ app/controllers
    └ app/views  
  └ staff
    └ conf/staff.routes
    └ app/controllers
    └ app/views  
project
 └ build.properties
 └ Build.scala
 └ plugins.sbt


このようにサブプロジェクトにすることで、大規模開発もしやすくなるのではないかと思います。

明日は


@s_kozakeさんが 中堅SIerがPlay1系を導入した感想を とのことです。
Play1系は触ったことがないのですが、Play2系とはまた違って業務向けにはいいという話を聞くので、楽しみです。

2013年10月2日水曜日

Play FrameworkでRawSQLの実行例

Play Framework for JavaでORMにEbeanを使った際の直SQLの書き方の一例です。
ネットで探しても日本語による情報がなかなか無いので書いておきます。

public static List<DateItemBean> getTweetResultListGroupByDate(Item item) {
String sql = " SELECT created_at,"
+ " COUNT( CASE WHEN POINT = 0 THEN 1 ELSE NULL END ) AS neutral,"
+ " COUNT( CASE WHEN POINT > 0 THEN 1 ELSE NULL END ) AS positive,"
+ " COUNT( CASE WHEN POINT < 0 THEN 1 ELSE NULL END ) AS negative"
+ " FROM tweet" + " WHERE item_id = :item_id"
+ " GROUP BY DATE_FORMAT( created_at,  '%Y%m%d' )"
+ " ORDER BY created_at ASC";
List<SqlRow> sqlRows = Ebean.createSqlQuery(sql)
.setParameter("item_id", item.itemId).findList();
List<DateItemBean> results = new ArrayList<DateItemBean>();
for (SqlRow row : sqlRows) {
Date date = row.getDate("created_at");
Integer countNeutral = row.getInteger("neutral");
Integer countPositive = row.getInteger("positive");
Integer countNegative = row.getInteger("negative");
DateItemBean bean = DateItemBeanService.setDateItemBean(item,
countNeutral, countNegative, countPositive, date);
results.add(bean);
}
return results;
}



のようにしてあげることで、実行することが出来ます。 詳しいソースは以下のGithubから確認して下さい。
https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/blob/master/app/services/TweetService.java

SQL 内で
:item_id
のように書いてあげることでsetParameterでbindしてあげることが出来ます。

セットされた値は
row.getInteger("neutral")
などのようにして取得することが出来ます。

なおDate型で取得する場合ですが、
DATE_FORMAT( created_at,  '%Y-%m-%d 00:00:00.000' )
みたいな感じにしてもString型にしかならなかったので、オブジェクトの方を見てるのかもしれません。よくわかりませんが・・・。



2013年9月27日金曜日

Play Frameworkでのルーティング設定

Play Frameworkでページャーを実装したい場合ですが、このようにルーティングを設定します。



GET     /detail/:categoryId/:id                           controllers.ItemDetail.index(categoryId:Long, id:Long, page: Integer ?= 1)
routesにルーティングの設定を書きますが、その際に変数のデフォルト値を設定することが出来ます。例えば引数のpageが未指定だった場合にはページ番号1として内部の処理が行われます。

例えばテンプレート内で生成されたURLが

https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/blob/master/app/views/category.scala.html

<a class="btn btn-default" href="detail/@{category.categoryId}/@{bean.getItem.itemId}">
で生成されているような
/detail/1/2
のようになった場合にはPlay Frameworkの内部では以下のように変換がされます。
/detail/1/2?page=1

Controller側ではどのように受け取られるかといいますと、
https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/blob/master/app/controllers/ItemDetail.java

public static Result index(Long categoryId, Long itemId, Integer page) {
のような引数にそれぞれの値がセットされるようになります。
2ページ目以降は
https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/blob/master/app/views/detail.scala.html
テンプレート内で以下の様なリンクを生成することで

<a href="@routes.ItemDetail.index(bean.getItem.categoryId, bean.getItem.itemId, currentPage - 1)">

/detail/1/2?page=2
のURLが生成されるようになります。

/detail/1/2/3のようにページ番号もスラッシュ区切りにするようなURLのパターンを変える方法はよくわかりませんでした。



Play Framework本番サーバへのデプロイあれこれ

アプリケーションのデプロイ作業って悩ましいですよね。 例えばDBサーバの接続情報は本番サーバと開発サーバで違っていたり、
メールサーバの接続情報もまたしかり。

Play Frameworkはこういった設定ファイルは

conf/application.conf

に設定されていて、
本番環境で設定ファイルを振り分ける方法としては、
Play起動時に設定ファイルを指定して起動する方法が紹介されています。
http://www.playframework-ja.org/documentation/2.1.3/Production

次のようにして、本番環境用の別の設定ファイルを指定することもできます:
$ target/start -Dconfig.file=/full/path/to/conf/application-prod.conf

ちなみにみなさんって本番環境とのこういった設定の振り分けってどうしてます?
デプロイスクリプトに組み込む際に、呼び出す設定ファイルを切り替える?
Jenkinsでビルドする前にsedコマンドとかで上書きしてます?

とにかくいずれの方法にしても、アプリケーション固有の設定(例えば一覧に表示する件数とかサイト名とか)なんかをapplication.conf
に書くと、開発環境用の設定ファイルと本番環境用の設定ファイル両方に書く必要が出て、まあ手間が増えるわけです。

なので私は提唱したい。
とりあえずapplication.confには共通で呼び出す値を書くようにしよう。
環境によって変わる値は外部ファイル化して環境によってそのファイルだけ切り替えるようにしよう、と。

つまりはこんなかんじにしてみてはいかがでしょうか。
https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/tree/master/conf

https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/blob/master/conf/application.conf
には

include "databases.conf"

のような形で呼び出す処理を書く。環境によって切り替える必要がある時にはこのファイルを上書きする。そうすることで切り替えたいところだけ切り替えることができるようになりますね。





2013年9月26日木曜日

Play Frameworkがチーム開発に向かない理由

個人的にPlay Frameworkで作っているプロジェクトではこんなようなクラス構造にしています。

https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa/tree/master/app

beans
components
controllers
models
services
tasks
utils
views

ちなみにこの中で基本的な構造として用意されているのは
controllers
models
views
の3つだけです。

つまり他の構造は個人の主観によって作られてしまうため、チームで開発を行う場合にはある程度のルール作りが必要になってしまいます。

Play Frameworkは個人で作るには爆速で作れると思いますが、チームで大規模な開発をするのには向いてないような印象があります。

他の人はどんなクラス構造にしているのか興味があります。








Play FrameworkでAkka schedulerがうまく動作しなかった件

Playで定期実行する処理を書くときには
にあるように簡単に書くことができます。

Akka.system().scheduler().schedule(
  Duration.create(0, TimeUnit.MILLISECONDS), //Initial delay 0 milliseconds
  Duration.create(30, TimeUnit.MINUTES),     //Frequency 30 minutes
  testActor,
  "tick",
  Akka.system().dispatcher()
);

ここにある例を参考に自分でも定期実行する処理を書いてみたのですが、どうやら上手く動いていなかった様子。

上手く動かなかったケース
Akka.system()
.scheduler()
.schedule(Duration.create(0, TimeUnit.SECONDS),
Duration.create(15, TimeUnit.MINUTES), getAnalyze,
Akka.system().dispatcher());

動くように修正したケース

Akka.system()
.scheduler()
.schedule(Duration.create(1, TimeUnit.SECONDS),
Duration.create(300, TimeUnit.SECONDS), getAnalyze,
Akka.system().dispatcher());

もともと下のパターンは動いていたので、バグなのか分単位だと動作しない仕様なのかよくわかりませんが、とりあえず動くようになりました。
Akka.system()
.scheduler()
.schedule(Duration.create(0, TimeUnit.SECONDS),
Duration.create(1, TimeUnit.HOURS), getFeed,
Akka.system().dispatcher());

2013年9月23日月曜日

さくらのVPSでPlay Frameworkを動かす:サブドメインの割り当て

それぞれ以下の様なドメインでアクセスをさせたいので、サブドメインの設定を行います。

ムームーDNSで
さくらのVPSにサブドメインを設定するを参考にサブドメインの設定を行います。
次にApacheの設定を行います。

vim /etc/httpd/conf/httpd.conf
NameVirtualHost *:80
サブドメインなしの場合にアクセスした際の表示調整

<VirtualHost *:80>
    ServerAdmin webmaster@ドメイン
    DocumentRoot /var/www/html
    ServerName ドメイン
    ServerAlias www.ドメイン
    ErrorLog logs/ドメイン-error_log
    CustomLog logs/ドメイン-access_log common
</VirtualHost>


サブドメインの設定
vim /etc/httpd/conf.d/play.conf
<VirtualHost *:80>
  ServerAdmin webmaster@ドメイン
  DocumentRoot /var/www/html
  ServerName サブドメイン
  ErrorLog logs/サブドメイン-error_log
  CustomLog logs/サブドメイン-access_log common
  ProxyPreserveHost On
  ProxyPass  /excluded !
  ProxyPass / http://127.0.0.1:9000/
  ProxyPassReverse / http://127.0.0.1:9000/
</VirtualHost>
ちなみに最初の時点では
/var/www/html内にファイルがなくてデフォルトの画面が出てしまうので、適当なページを作っておきます。

vim /var/www/html/index.html
適当なhtmlファイル

そんなこんなで出来上がったサイトがこちらになります。
http://api.negaposia.com/



さくらのVPSでPlay Frameworkを動かす:ドメインの割り当て

さて、そろそろIPアドレスベースでのアクセスに飽きてきたので、ドメインを割り振りましょう。
ムームードメインでドメインを取得することが多いのですが、ムームーDNSを使うケースとさくらのネームサーバを使うケースといろいろ有るようです。

今はお試し期間中でさくらのネームサーバを使えないので、ムームーDNSを使って設定を行ってみます。

ムームーDNSの画面で以下のように設定をしました。

サブドメイン:空欄
種別:A
内容:さくらのVPSから割り当てられたIPアドレス

これでIPアドレスではなく、ドメインでアクセスが出来るようになります。


さくらのVPSでPlay Frameworkを動かす:Jenkinsにジョブの設定を行う

さあ、JenkinsにJobの設定を行いましょう。
今回はこのPlay Frameworkのプロジェクトをビルド・デプロイしていきます。
https://github.com/YoshiteruIwasaki/NegativePositieAnalyzerForJa
適当な名前をつけてJobを作成します。

ビルドではシェルの実行を選択してもろもろの設定を行います。
このジョブでは

  1. Playサーバの停止
  2. ビルド
  3. Playサーバの起動

を行います。

export PATH=$PATH:/usr/local/play
service play stop
sleep 10
cat << EOT > ${WORKSPACE}/conf/databases.conf
db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://localhost:3306/negaposi?useUnicode=yes&characterEncoding=utf8&connectionCollation=utf8_general_ci"
db.default.user=***
db.default.password="***"
db.default.connectionTestStatement="select 1;"
db.default.connectionTimeout=20 seconds
EOT
cat << EOF > ${WORKSPACE}/conf/secret.conf
application.secret="***"
EOF

cat << EOFF > ${WORKSPACE}/conf/evolutions.conf
evolutionplugin=disabled
EOFF

play clean compile stage
service play start

こんなかんじに設定をしました。
Githubからプルをしたらいくつか本番環境用に設定ファイルを書き換えてビルドをします。

実際にビルドして停止・起動やビルドが上手く行っているか確認してみます。


2013年9月22日日曜日

さくらのVPSでPlay Frameworkを動かす:Playをサービス化する

Play Frameworkの起動・停止をサービス化します。
によると、どうやら
target/start
で起動がされるようです。

起動するとRUNNING_PIDなるファイルが生成されるので、このファイルに記載されているプロセスをkillすれば停止できるようです。

vim /etc/init.d/play
#!/bin/bash 
# source function library
. /etc/rc.d/init.d/functions
# Path to the JVM
JAVA_HOME=/usr/java/default
export JAVA_HOME
export PATH=$PATH:$JAVA_HOME/bin
export PATH=$PATH:/usr/local/play
APPLICATION_PATH=アプリケーションのパス
RETVAL=0
SERVICE_NAME=`basename $0`
start() {
        echo -n $"Starting : $SERVICE_NAME"
        $APPLICATION_PATH/target/start &
        RETVAL=$?
        if [ $RETVAL == 0 ]; then
          success
        else
          failure
        fi
        echo
}
stop() {
        echo -n $"Stopping $SERVICE_NAME: "
        if [ -e $APPLICATION_PATH/RUNNING_PID ]; then
          kill `cat $APPLICATION_PATH/RUNNING_PID`
        fi
        RETVAL=$?
        if [ $RETVAL == 0 ]; then
          success
        else
          failure
        fi
        echo
}
restart() {
        stop
        start
}
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        restart
        ;;
  status)
        status
        RETVAL=$?
        ;;
  *)
        echo $"Usage: $0 {start|stop|status|restart}"
        exit 1
esac
exit $RETVAL


Play Frameworkを自動起動に設定します。

chmod +x /etc/init.d/play
chkconfig --add /etc/init.d/play
chkconfig play on

参考


playの自動起動スクリプト
アプリケーションを本番モードで起動する

さくらのVPSでPlay Frameworkを動かす:PlayとApacheの連携設定

さて、事前にApacheとPlayの連携を設定しましょう。
設定方法は
を参考にして以下のように設定します。
vim /etc/httpd/conf.d/play.conf
<VirtualHost *:80>
  ProxyPreserveHost On
  ProxyPass  /excluded !
  ProxyPass /play http://127.0.0.1:9000/
  ProxyPassReverse /play http://127.0.0.1:9000/
</VirtualHost>


Apacheを再起動します。

service httpd restart



さくらのVPSでPlay Frameworkを動かす:Jenkinsの設定

さて、Jenkinsの設定を行っていきます。

基本方針

管理者ユーザーを1人作って、そのユーザーのみビルドの実行などが出来るようにします。
一般ユーザーはビルド状態の参照だけ出来るようにします。

まずはセキュリティの設定をします。

次に管理者ユーザーのアカウントを作成します。

諸々のプラグインを導入します。
メトリック関係のプラグインも入れたいところですが、まずはGithubからプルしてPlayのビルド・デプロイをするために必要なプラグインを導入します。

インストールするプラグイン
  • GitHub Plugin
  • Git Plugin
をとりあえずインストールしました。

このJenkinsでは

  • Play Frameworkによるアプリケーションのビルド・デプロイ
  • Play FrameworkのUnitテスト
  • jarライブラリのビルド

を予定していますが、ログインしていない状態でもアクセスできるのはjarライブラリのビルドだけにしたいので、
権限管理から「 行列による権限設定(プロジェクト単位)」を選択して、作成した管理者ユーザーにのみ全権限を付与して、匿名ユーザーには閲覧権限も与えないように設定を行いました。

参考

さくらVPSでJenkinsをセットアップし、認証を行うまで



さくらのVPSでPlay Frameworkを動かす:Jenkinsのインストール

さてさくらのVPS上でPlay Frameworkが動く環境が準備出来ました。
しかしながら現在開発はMac上で行っています。

Macで開発したソースコードをGithubにPUSHしてPost-Receive HooksでVPS上にデプロイといった流れも取れるかと思いますが、今の時点でRubyを触りたくないので、JenkinsとGitをVPSにインストールして思ったタイミングで最新のソースコードをPULL,DEPLOYするようにしてみたいと思います。(さくらのVPSにgit(バージョン1.7.1)があらかじめインストールされていました。)



Jenkinsを入れておけばPlayのアプリケーションだけでなく、Playのアプリケーションで使っているライブラリのjarファイル生成も将来的に行うことを見越してです。
あとはPlayでテストコードを書いてみるかな・・・。といったことも考えています。

とりあえずまずはJenkinsのインストールです。

Jenkinsインストール


に従ってコマンドを実行してみます。

sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

yum install jenkins

Jenkinsはインストールまではとっても簡単。

Jenkinsを起動・自動起動の設定をしておく

service jenkins start
chkconfig jenkins on

JenkinsとApache連携


Jenkinsはデフォルトで8080ポートで起動します。
このVPS上では基本的にApacheをプロキシサーバとして利用して、他のポートは塞ぐ運用で考えています。

Jenkinsは

http://IPアドレス/jenkins

でアクセスできるように設定をしていきたいと思います。(将来的にドメインを取った時にはhttp://jenkins.ドメインでアクセスできるようにしたい)

vim /etc/sysconfig/jenkins
JENKINS_ARGS="--prefix=/jenkins"
Jenkinsを再起動します。
service jenkins restart
Apacheの設定を行います。

 vim /etc/httpd/conf.d/jenkins.conf
ProxyPass         /jenkins  http://localhost:8080/jenkins
ProxyPassReverse  /jenkins  http://localhost:8080/jenkins
ProxyRequests     Off
# Local reverse proxy authorization override
# Most unix distribution deny proxy by default (ie /etc/apache2/mods-enabled/proxy.conf in Ubuntu)
<Proxy http://localhost:8080/jenkins*>
  Order deny,allow
  Allow from all
</Proxy>

Apacheを再起動します。

service httpd restart

http://IPアドレス/jenkins
にアクセスをして確認します。

参考

Running Jenkins behind Apache
jenkinsとapacheの連携







さくらのVPSでPlay Frameworkを動かす:Playのインストール

ようやくPlayのインストールになります。

Play2.1.5のインストール


今開発中のアプリケーションはPlay2.1.x系で作っているので、Play 2.1.5をVPSにインストールしたいと思います。

ちなみにPlayの場合は2.1.x系と2.2.x系だと全く別物になっている可能性があるので、注意が必要です。

cd /usr/local/src/
wget http://downloads.typesafe.com/play/2.1.5/play-2.1.5.zip
unzip play-2.1.5.zip
mv play-2.1.5 /var/lib/
don’t install to /opt, /usr/local
http://www.playframework.com/documentation/2.1.3/Installing
とのことで、インストール先には/usr/localは避けたほうがいいようです。

バージョンアップの際にさくっと切り替えられるようにしたいのでシンボリックリンクを貼っておきます。

cd /usr/local/
ln -s /var/lib/play-2.1.5 play
ls -lat
Jenkinsからplayインストールフォルダを操作できるようにパーミッションの変更をします。

Play! 2.0 / 2.1 ぐらいで play コマンドを実行したときに Permission denied な例外が発生する問題への対処

chmod -R go+wx /var/lib/play-2.1.5 play/framework/sbt/boot
chmod -R go+wx /var/lib/play-2.1.5 play/repository

Playにパスを通す


どこからでもplayコマンドが実行できるようにするため、パスを通します。

export PATH=$PATH:/usr/local/play

パスが通っているか確認をします。

cd /root/
play help

ヘルプが表示されればOKです。


さくらのVPSでPlay Frameworkを動かす:JDKのインストール

今作っているPlay Frameworkのアプリケーションは2.1.x系で作っています。

2.1.x系のバージョンのJavaは

http://www.playframework-ja.org/documentation/2.1.3/Installingによると

Play framework の実行には、JDK 6以降 が必要です。

とあるので、JDK7系で行ってみようかと思います。

JDKインストール


さくらのVPSは64ビットなので
から

 jdk-7u40-linux-x64.rpm

をダウンロードして、VPS上に転送をします。

サーバー上のターミナルからJDKのインストールを行います。

rpm -ivh jdk-7u40-linux-x64.rpm 
インストールが上手く行ったかバージョン確認コマンドを実行してみます。

java -version
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)
javac -version
javac 1.7.0_40

不要になった jdk-7u40-linux-x64.rpm は削除しておきました。

/etc/profileの編集


/etc/profileを編集しておきます。

vim /etc/profile
export JAVA_HOME=/usr/java/default
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar

編集した内容を反映させます。

source /etc/profile


2013年9月21日土曜日

さくらのVPSでPlay Frameworkを動かす:MySQLの設定をする

Apacheに比べて設定する箇所の多いMySQL。
vim /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
default-character-set = utf8
skip-character-set-client-handshake
innodb_file_per_table
query-cache-size=16M
#Change Buffer Pool Size
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M
tmp_table_size = 64M
table_cache = 256
key_buffer = 128M
default-storage-engine=INNODB
# Slow Query
log-slow-queries = /var/log/mysql-slow.log
long_query_time=1
log-queries-not-using-indexes
[mysql]
default-character-set = utf8
[client]
default-character-set=utf8
socket=/var/lib/mysql/mysql.sock
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid 

こんなかんじに設定をしました。さくらのVPS 1Gプランなので、半分ほどMySQLにメモリを割り当ててみました。

service mysqld start
MySQLを起動したら初期設定を行います。

mysql_secure_installation
パスワード等のもろもろの設定を行います。

ログローテーションの設定をします。

vim /etc/logrotate.d/mysql-log-rotate
/var/log/mysql/error.log {
        # create 600 mysql mysql
        notifempty
    daily
        rotate 3
        missingok
        compress
    postrotate
    # just if mysqld is really running
    if test -x /usr/bin/mysqladmin && \
       /usr/bin/mysqladmin ping &>/dev/null
    then
       /usr/bin/mysqladmin flush-logs
    fi
    endscript
}

/var/log/mysql/query.log {
        # create 600 mysql mysql
        notifempty
    daily
        rotate 3
        missingok
        compress
    postrotate
    # just if mysqld is really running
    if test -x /usr/bin/mysqladmin && \
       /usr/bin/mysqladmin ping &>/dev/null
    then
       /usr/bin/mysqladmin flush-logs
    fi
    endscript
}

/var/log/mysql-slow.log {
        # create 600 mysql mysql
        notifempty
    daily
        rotate 3
        missingok
        compress
    postrotate
    # just if mysqld is really running
    if test -x /usr/bin/mysqladmin && \
       /usr/bin/mysqladmin ping &>/dev/null
    then
       /usr/bin/mysqladmin flush-logs
    fi
    endscript
}
ログローテーションに伴う設定を追加します。

vim /etc/my.cnf
[mysqladmin]
password = "パスワード"
user = root

再起動します。

service mysqld restart





さくらのVPSでPlay Frameworkを動かす:Apacheの設定をする

Apacheの設定を行います。

vim /etc/httpd/conf/httpd.conf

phpもindexに設定しておきます。

DirectoryIndex index.html index.html.var index.php

セキュリティ対策として以下を設定しておきます。

ServerTokens Prod
ServerSignature Off

Apacheを再起動します。

service httpd restart



さくらのVPSでPlay Frameworkを動かす:Apache,MySQL,PHPのインストール

以下の設定をします。

文字コードの設定

vim/etc/sysconfig/i18n
#LANG="C"
LANG="ja_JP.UTF-8"
SYSFONT="latarcyrheb-sun16"

http://support.sakura.ad.jp/manual/vps/ossetup.html

yum パッケージの更新

yum update

chkrootkit インストール

yum install chkrootkit

Apache,MySQL,PHPのインストール

yum install httpd httpd-devel mod_ssl mysql mysql-server mysql-devel php php-pdo php-mysql php-mbstring php-mcrypt php-xml gd php-gd php-devel php-pear php-openssl
自動起動の設定をしておく

chkconfig mysqld on
chkconfig httpd on



さくらのVPSでPlay Frameworkを動かす:iptablesの設定をする

さあVPSコントロールパネルからVPSを起動したらまずはiptablesの設定をします。

sshでのセキュリティを高めるために以下の設定を行います。

  • rootによるログインの禁止
  • パスワードによるログインの禁止
  • ポートの変更

まずはrootでログインします。

ssh root@IPアドレス -p 22

ログイン用のユーザーを作成します。

useradd USER_NAME -G wheel
passwd USER_NAME
あとで root になれるユーザを制限するために wheel に所属させておきます。

セキュリティ対策のため、もろもろ設定をしていきます。
vim /etc/ssh/sshd_config
ログインするためのポート番号を変更します。
Port SSH_PORT
rootによるログインを禁止します。
PermitRootLogin no
パスワード入力によるログインを禁止します。
PasswordAuthentication no
作成したユーザーによるsudoコマンド実行の設定を行います。
vim /etc/pam.d/su
auth required pam_wheel.so use_uid
の行のコメントアウトを外します。
vim /etc/login.defs
SU_WHEEL_ONLY yes
sudo visudo
# %wheel ALL=(ALL) ALL
の行のコメントアウトを外します。

公開鍵の設定を行っていきます。

新たに作成したユーザーのディレクトリに公開鍵を保存するためのsshフォルダを作成します。

su - USER_NAME
mkdir ~/.ssh/
chmod 700 ~/.ssh/

Macから接続するので、Terminalで次のコマンドを実行します。

ssh-keygen -f /Users/tyongeri/.ssh/id_rsa.IPアドレス

作成された公開鍵を転送します。
cd .ssh
scp id_rsa.IPアドレス.pub root@IPアドレス:/home/USER_NAME/.ssh/authorized_keys
サーバ側で再び作業をします。

su - USER_NAME
cd .ssh
sudo chown USER_NAME:USER_NAME authorized_keys
chmod 600 authorized_keys
さて、新しく作ったアカウントでsshログイン出来るか確認してみます。

ssh USER_NAME@IPアドレス -i id_rsa.IPアドレス -p 22
ついでにsudoが出来るか確認しておきます。
sudo su -
sshの設定変更を反映させます。
/etc/init.d/sshd restart
改めて変更したポートでアクセスできるか確認します。

ssh USER_NAME@IPアドレス -i id_rsa.IPアドレス -p SSH_PORT

ついでにrootでのログインが出来なくなっているか確認します。

ssh root@IPアドレス -p SSH_PORT
Macから接続する際に毎回鍵ファイルやポートの指定は手間なので、設定に記載します。

vim ~/.ssh/config
Host IPアドレス
    HostName IPアドレス
    Port SSH_PORT
    User USER_NAME
    identityfile ~/.ssh/id_rsa.IPアドレス
設定が出来ました。
ssh IPアドレス
で接続できることを確認します。


さくらのVPSでPlay Frameworkを動かす:VPSサーバを契約する

さくらのVPSでPlay Framework for Java 2.1.3を動かしてみたいと思います。

今回契約したのはさくらのVPS 1Gのプランです。

メモリ:1GB
ディスク容量:100GB
CPU:仮想 2コア

というスペックになっています。
果たしてPlay Frameworkはストレスなく動作してくれるのでしょうか。

最終的に構築したいのはこういった構成です。


フロントをApacheにして、プロキシ経由でPlay Frameworkにアクセスします。
バックエンド処理をPlay Frameworkに任せて、フロントのアプリケーションはCakePHPとかで作ることも想定しています。

あと、今回作るシステムでコアになっている部分は自作ライブラリとしてjarファイルにしていますが、jarファイルを作る環境がないので、Jenkinsも立ち上げてGithubからソースコードを取得してjarファイルの生成もしてもらうように考えています。

さくらのVPSを契約すると30分ほどでVPSが生成されます。
以前はサーバが起動した状態でホイッと渡されて環境を構築していましたが、最近はサーバを停止した状態で渡されるようになっています。

おそらくサーバを渡されてから構築するまでの間に起動したまま放置しておいてrootアカウントを乗っ取られるとか結構起きてサーバを停止した状態で渡されるようになったんじゃないかと思います。

さ、サーバの準備ができたらコントロールパネルからサーバを起動します。


2013年9月4日水曜日

Java Play framework 2.0のebeanで、Expr.containsを使ってのOR検索

一言多いプログラマーの独り言: Java Play framework 2.0のebeanで、OR検索


に触発されたので、書いてみる

public static Page page(int page, int rows, String sortBy, String order, String filter) {
    return 
        find.where("name like :keyword OR detail like :keyword")
            .setParameter("keyword", "%" + filter.trim() + "%")
            .orderBy(sortBy + " " + order)
            .findPagingList(rows)
            .getPage(page);
}

のように記載されていますが、Expr.containsを使ってこういう方法も使えるかと思います。

public static Page page(int page, int rows, String sortBy, String order, String filter) {
    return
        find.where().or(Expr.contains("name", filter.trim()), Expr.contains("detail",filter.trim()))
            .orderBy(sortBy + " " + order)
            .findPagingList(rows)
            .getPage(page);
}
http://www.avaje.org/static/javadoc/pub/com/avaje/ebean/Expr.html#contains(java.lang.String, java.lang.String)

2013年8月6日火曜日

最近コミュニケーションについて考えている

最近コミュニケーションについて考えています。

色々と考えるところがあったのでちょっとブログにまとめておきます。

コミュニケーションとはなにか

コミュニケーションに必要となる力は大きく2つ。

話す相手の意見・考えを汲み取る力、想像力。
相手の話そうとしていることを汲み取ること、話す相手がどう考えているかを想像すること。

自分の考えを要約して伝える力、表現力。
相手のことを考え、相手の求めに応じたアウトプットができることが必要です。
相手に自分がどう思われてるかではなく、どうすれば伝わるかを考えます。

この2つの力がコミュニケーションに必要な力となります。

コミュニケーションとはつまりリーダー論だった


コミュニケーション能力とは自分の中で意識レベルまで落とし込まれていないと仕組みを作っても結局何も変わらないのかもしれません。

次第に組織におけるコミュニケーションを良くするためには、まずリーダーから意識を変えていく必要があると考えるようになりました。

もちろんすぐに意識をして変えられるものではありませんので、訓練をする必要がありそうです。

じゃあ実際にどうすればいいのか


具体的にはどうすればいいのでしょうか。
まずはコミュニケーションで必要となるものを考えてみます。

には、
 「部下とのコミュニケーションがうまくいかない」という管理職には共通点があります。コミュニケーションの「質」ばかりを追求していることです。少しでも仕事につながる話をしよう、部下の成長を促す言葉をかけよう、等々。それはもちろん大切なことですが、 それ「だけ」ではいけません。部下とのコミュニケーションを円滑にするためには、質もさることながら「回数」がなにより重要です。
と書かれています。つい仕事に関係あることだけしか喋らないようになりがちですが、風通しの良い職場を作るためには仕事に関係あることだけじゃなく、仕事と関係のない話も必要なようです。量を重ねていくことで次第に質も伴ってくるという考えです。

またその際にはコミュニケーションの回数といった数値として可視化出来るものを判断基準にすることで、コミュニケーションの活性度を図ることが大事と書かれています。
会社が伸びるのも落ち込むのも内部のコミュニケーション次第です。管理職であれば「部下とコミュニケーションを取るのは大切な仕事」と肝に銘じてあたらなくてはなりません。
と書かれています。具体的な方法としては1日に何回話しかけたかをメモしておくといいそうです。これはすぐにでも取り組めそうです。1日の最後にまとめて見返すことで翌日は○回話しかけると言った目標を立てることができます。

機械的にやることは問題がありますが、抜本的な意識改革を行うためにはそういう環境や仕組みを作って無理にでもやって見ることが必要ではないかと考えています。

でも「コミュニケーションは質より量」であると述べられています。
まずは量、回数・頻度といったものを高めることが大切であると書かれています。
回数を多くすることで次第に質を上げていく事が大事なようです。

ちなみにこのブログの次のエントリーが気になったのですが、残念ながら次のエントリーはないようでした。。。

でも「高い頻度を持ってコミュニケーションをとること」が大事だと書かれています。

女性エンジニアのコミュニケーションの取り方が書かれています。仕事の話だけではなくいろいろな話をすることでいざというときに頼み事がしやすくなったりするメリットが有ると思います。

では「2.3分間のコーチングをする」が紹介されています。
今日のアクションプランについて3分毎に話をすることが紹介されています。今うちの会社では朝会をやっているので、その時に取り入れてみるといいかもしれません。

では「日頃から信用してもらえるような行動をあなたがとる」と書かれていました。コミュニケーションの礎となるのはお互いの信頼です。具体的に信頼をどう得るかは難しいところですが、自分の信念を持って相手に接するなど自分で考えてみる必要がありそうです。

では六花亭の取り組みが紹介されていました。
何かの提出や報告も、反応がなければ形骸化してしまいます。日報に目を通して反応するといった事を行うことでお互いの信頼関係が築いていけるのではないでしょうか。

思えば小学生の頃、毎日の日記に対しての先生の赤ペンでの返事が嬉しかったものです。
たまに、せっかく長文で書いたのに「よくできました」のハンコだけで済まされていて悲しい思いをしたこともありました。

大切なのは相手を大事にする気持ちですね。

には次のようなことが書かれています。

組織の協働(コラボレーション)の力を高めるには、1人ひとりのメンバーが自発的に行動を起こすことが必要です。メンバーの気付きを積極的に発言させ、皆で耳を傾け、応援しましょう。
例えば、上からの指示を待って仕事をするのではなく、自ら進んで職場の問題に気付き、自分が何をすべきなのかを考えて皆に改善の提案をしたり、他の社員の行動をサポートしたりすることです。こうした積極的な想いやりの行動が職場のメンバーの信頼関係につながります。
コミュニケーションとはつまり一人ひとりが自分で考え・行動する力をつけることなのかもしれません。

コミュニケーションに必要なのはノウハウやスキルの前に、まずは周りに主体的・自律的に働きかけていく姿勢。待っていてはだめなのです。コミュニケーションは会社という組織のいわば血液のような存在であり、滞れば必ずどこかで支障が出てきます。一人ひとりがまわりに積極的にコミュニケーションを取っていくこと、ちょっとした気づかいが職場の空気を良くし、コミュニケーションをしやすい風土につながっていくはずです。
と書かれています。コミュニケーションを良くするには、やはり自分の意識改革を行なっていくしか無いかもしれません。

また、エンジニアとして心がけることとしてはどのような点があるでしょうか。

ではモバイルファクトリーでの取り組みが紹介されています。

毎日1時間「帰りの会」、毎週1回「週次レビュー」を行っていることを紹介した。
 ここでコードレビューなども行うが、ただプリントをして持っていけばいいので、すぐに話が終わる。大事なのはそうやって会議の時間を余らせることで、そうしたタバコ部屋に似たダラダラした時間やグダグダした話し合いの中からこそ重要な話が出てくるのだという。
とあるように、これも仕事の話以外から仕事に話につなげるなどの例ですね。


転職前からコミュニケーション能力を鍛える方法があります。受託開発のITエンジニアであっても、ただ開発業務をこなすだけではなく、より効果的なシステムを考えて顧客企業に提案するなど工夫をしてみることです。周囲の協力を得ながら積極的にプロジェクトを動かす経験を積むことで、コミュニケーション能力は高められるはずです
と書かれています。言われたことをやるだけではなく、自分から提案を行うなど創造的な分野で頭をつかうことがコミュニケーション能力の向上にもつながると書かれています。

ではコミュニケーションをする際にエンジニアに見られがちな注意点が書かれています。
「あいづちが私の話にかぶり気味なる」のは自分でも注意しなければと思います。

では次のような内容が紹介されていました。
あなたにしかできない業務ではなく、あなたにしか思いつけない新しいアイデアや提案、そしてそれを実現させる行動力があなたの価値になっていく
アイデアや提案・行動力が価値になる、お金を生み出すということです。


考察

これらのことから考えてまず取り組むべきはコミュニケーションの量を増やすことです。
そのためには積極的に自分の意識を変えていく必要があります。

僕がまだ28歳の頃、意識的に自分の性格を変えようとしたことがありました。
本を読んでそれを1日1つ実践する。それを何度も繰り返すことで自分の意識を変えてみる、ということを行ったことがあります。
はっきり言って単純です。今になってこんなことを?みたいに思っていましたが、
冒頭に書かれていた文を見て、実行に移すことを決心したのです。

ずっとその態度を続けていれば、いつかそれが自分自身のキャラクターになる。
気配りをするクセが、だんだん自分の体になじみ自然になっていく。
そして新しく知り合った人は、新しく生まれ変わった僕を「僕そのもの」と思ってくれる。

ちなみにこの本です。


いきなり変わると、周りから「え、どうしたの?」と思われるかもしれません。それでもやることで、自分が、そして自分の周りが変わっていくかもしれません。
僕が見たいのは別の景色なのかもしれません。
変わるには、多分、「いきなり変わる」、くらいの心意気が必要なんだと思います。
そして、それを素直に受け取るには、自分はもう最後の年齢かもしれません。

やりたいこと、やったほうがいいと思って出来ていないこと、いろいろあります。
明日から少しずつですが、取り組んでみたいと思います。

2013年8月4日日曜日

Googleの人口音声APIを使ってTwitterの自動つぶやきアプリを作ろうとして挫折した話

Googleは非公開の人口音声APIであるGoogle TTSがあります。

Google TTSについては、

あたりを見てみて下さい。
GETパラメータに読み上げさせたい文字列のクエリを送ることで読み上げてくれるというとてもシンプルな作りになっています。

読み上げといえば、Twitterのタイムラインですよね。
早速Twitterタイムラインを読み上げるプログラムをGAE for PHPで組んでみました。
いざ、読み上げる時に幾つか問題が発生しました。

1点目:日本語で英語を読み上げさせると1文字ずつアルファベットで読み上げてしまう。

2点目:文字列によって読みあげられないパターンが有る。(http://www.ffffine.net/blog/2011/05/google-tts%E3%81%A7twitter%E3%81%AEtl%E3%82%92%E8%AA%AD%E3%81%BF%E4%B8%8A%E3%81%92%E3%82%8B/でも触れられていますね。)

3点目:APIを短い時間にコールし過ぎると止められる。

まず1点目を解消するために、Yahooの日本語形態素解析API(http://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html)をつかって、文章を区切って、全て英字だった場合には英語で読み上げさせるようにしました。

2点目はどうやら名詞で終わる場合は「埼玉。」のように句点をつけなければ読み上げが出来ないみたいだったり、逆に句点があると読みあげられなかったりと色々なパターンが有るようだ、ということがわかりました。
1週間ほど試行錯誤を繰り返してみて、通常の文章でエラーだったら最後に「。」を追加して再度読み上げにチャレンジ、それでもだめだったら「,」を最後に追加して読み上げにチャレンジ、ととりあえず自動的になんパターンかで読み上げるようにしてみました。
それでもまだ読み上げに失敗するケースが多く、結局これ以上やっても時間の無駄ということで諦めることにしました。
正式なAPIでないため、ドキュメントもなくその辺りを探るのはしんどいので。。。

3点目はSleepを埋め込むことで対応しました。本来であればSleepの秒数を(例えば)2〜10秒の間でランダムにするとかといったことでより人間的な動きに近づき、また読み上げ間隔も変わって面白いと思いますが、すでに2点目でこのプロジェクトに挫折したため、単純なSleepだけになっています。


もし読み上げさせに興味が有る方はGithubからforkなりしてみて下さい。

2013年7月25日木曜日

Vagrantについて調べてみた

Chefとは違いVagrantについては前に1回エントリーを読んだことがあります。
どのエントリーかは忘れてしまったのであれですが、なかなか良くまとまっていてほぼ理解できたと思います。

一言で言えば開発環境構築を簡単にするツールです。
もうXAMPPなんていりません。

http://d.hatena.ne.jp/naoya/20130315/1363340698

http://www.ryuzee.com/contents/blog/4292

あたりを参考に。

開発環境だけにとどまらず、同じ設定をしたサーバを作ることが出来るみたいです。

まあ確かに開発環境と本番環境でミドルウェアのバージョンが違うとか
開発環境ごとにphp.iniやらhttpd.confやらの設定が違うとかありがちな話です。

なるほどなあと思いながら、今のところ必要無さそうなのでそっとページを閉じました。

2013年7月24日水曜日

Chefについて調べてみた

久しぶりにこのブログを書いています。
こっちのブログじゃなくて、会社の方のブログを書いたほうがいいんだけど・・・。

とりあえずこっちのブログはあくまでも自分の実験的な感じで書いて行きたいと思います。

さて、最近よく耳にするChef。
ちょっと調べてみました。

どうやらサーバの運用を自動化するスクリプト郡のようです。

http://itpro.nikkeibp.co.jp/article/Active/20130307/461541/
http://ameblo.jp/principia-ca/entry-10775775095.html

あたりを見るに、どうやら冗長化しているサーバの設定を変更する際に1台1台設定するんじゃなく、自動的に全部のサーバ(もしくは特定のサーバ)に対して設定を反映するために使えるツールということだそうです。

大体100台くらいのオーダーくらいのサーバを管理している会社で使われることが多いようです。

あれこれAWS使えば一発じゃ。と思ったのでとりあえず今日はここで調査終了。
デプロイツールのcapistrano的な立ち位置かと思ったらどうやら違うみたいでPuppet(自分はよく知らない)と同じ感じみたいです。

2013年3月14日木曜日

Play Framework2.0のプロジェクトに対してJenkins上でFindbugsを実行する方法

Play Frameworkのプロジェクトに対して、継続的にFindbugsを実行して安定してバグのない状態でリリースをしたい。

Play Framework1系の頃はwarにするプラグインとかもあったので、mavenやらantやらが使えたんじゃないかと推測していますが、とりあえずPlay Framework2.0で mvn installとか試しても駄目ぽかったので、FindbugsはEclipseで実行するしか無い!?なんて思っておりました。

でも今時のフレームワークでそんなわけない、と思い直し、調べてみたら、JenkinsさんでFindbugs実行できました。

今回動作を確認したのは

Play Framework2.0.4 Java版

で作られたプロジェクトです。

主に参考にしたのは下記サイトです。
http://stackoverflow.com/questions/13930791/using-findbugs-with-play-framework-2
http://www.inoio.de/blog/2013/01/08/integrating-findbugs-into-a-playframework-2-project-java/

プロジェクト側の準備

Playのアプリケーション側で変更を行うファイルは2つだけ。

  • project/plugins.sbt
  • project/Build.scala

になります。

project/plugins.sbt
// Comment to get more information during initialization
logLevel := Level.Warn

// The Typesafe repository
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"

// Use the Play sbt plugin for Play projects
addSbtPlugin("play" % "sbt-plugin" % "2.0.4")
に対して、次の1行を追加します。
addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.1.7")
ちなみになぜか
addSbtPlugin("play" % "sbt-plugin" % "2.0.4")
 の次の行に書いたらビルドの時に怒られたので、1行あけた上で追加しました。

最終的にはこうなりました。


// Comment to get more information during initialization
logLevel := Level.Warn

// The Typesafe repository
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"

// Use the Play sbt plugin for Play projects
addSbtPlugin("play" % "sbt-plugin" % "2.0.4")

addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.1.7")

project/Build.scala
import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

  val appName = "hoge"
  val appVersion = "0.1"

  val appDependencies = Seq(
    // Add your project dependencies here,
    "org.apache.commons" % "commons-email" % "1.2",
    "commons-io" % "commons-io" % "2.0.1",
    "commons-daemon" % "commons-daemon" % "1.0.10",
    "commons-lang" % "commons-lang" % "2.6",
    "commons-dbutils" % "commons-dbutils" % "1.5",
    "commons-codec" % "commons-codec" % "1.6",
    "mysql" % "mysql-connector-java" % "5.1.20",
    "commons-beanutils" % "commons-beanutils" % "1.8.3"
    )

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings( // Add your own project settings here
    resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/")
}
みたいなところに1行import文を追加します。

import de.johoop.findbugs4sbt.FindBugs._

あとはval mainの箇所をこんなかんじで書き換えます。
val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA,settings = Defaults.defaultSettings ++ findbugsSettings).settings( // Add your own project settings here
    resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/")
}
以下を追加するような感じですね。引数でsettingsを追加して、さらに.settingsがあるのがなんか解せない感じですが、とりあえずスルーです。

settings = Defaults.defaultSettings ++ findbugsSettings 
とりあえずこれで動きはしますが、Findbugsの除外ルールを書いておきましょう
val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA,
settings = Defaults.defaultSettings ++ findbugsSettings).settings(
// Add your own project settings here
    resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/",
  findbugsExcludeFilters := Some(
    <FindBugsFilter>
      <Match>
        <Class name="~views\.html\..*"/>
      </Match>
      <Match>
        <Class name="~Routes.*"/>
      </Match>
      <Match>
        <Class name="~controllers\.routes.*"/>
      </Match>
    </FindBugsFilter>
  )
) 
ここでは findbugsExcludeFilters を指定しましたが、他にもいくつか指定できる項目があるようです。

https://bitbucket.org/jmhofer/findbugs4sbt/wiki/Home
に載っている項目が指定できるようなのですが、

  • findbugsReportType
  • findbugsEffort

などを指定しようと試みたところ、エラーになりました。

最終的にはこうなりました。


import sbt._
import Keys._
import PlayProject._
import de.johoop.findbugs4sbt.FindBugs._

object ApplicationBuild extends Build {

  val appName = "hoge"
  val appVersion = "0.1"

  val appDependencies = Seq(
    // Add your project dependencies here,
    "org.apache.commons" % "commons-email" % "1.2",
    "commons-io" % "commons-io" % "2.0.1",
    "commons-daemon" % "commons-daemon" % "1.0.10",
    "commons-lang" % "commons-lang" % "2.6",
    "commons-dbutils" % "commons-dbutils" % "1.5",
    "commons-codec" % "commons-codec" % "1.6",
    "mysql" % "mysql-connector-java" % "5.1.20",
    "commons-beanutils" % "commons-beanutils" % "1.8.3")

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA,
    settings = Defaults.defaultSettings ++ findbugsSettings).settings( // Add your own project settings here
      resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/",
      findbugsExcludeFilters := Some(
        <FindBugsFilter>
          <Match>
            <Bug category="MALICIOUS_CODE"/>
          </Match>
          <Match>
            <Bug category="NOISE"/>
          </Match>
          <Match>
            <Bug category="I18N"/>
          </Match>
          <Match>
            <Bug category="SECURITY"/>
          </Match>
          <Match>
            <Bug category="EXPERIMENTAL"/>
          </Match>
          <Match>
            <Bug pattern="BAC_BAD_APPLET_CONSTRUCTOR"/>
          </Match>
          <Match>
            <Bug pattern="DMI_UNSUPPORTED_METHOD"/>
          </Match>
          <Match>
            <Bug pattern="CD_CIRCULAR_DEPENDENCY"/>
          </Match>
          <Match>
            <Bug pattern="IMA_INEFFICIENT_MEMBER_ACCESS"/>
          </Match>
          <Match>
            <Bug pattern="IL_INFINITE_RECURSIVE_LOOP"/>
          </Match>
          <Match>
            <Bug pattern="PS_PUBLIC_SEMAPHORES"/>
          </Match>
          <Match>
            <Bug pattern="UOE_USE_OBJECT_EQUALS"/>
          </Match>
          <Match>
            <Bug pattern="USM_USELESS_SUBCLASS_METHOD"/>
          </Match>
          <Match>
            <Class name="‾views¥.html¥..*"/>
          </Match>
          <Match>
            <Class name="‾Routes.*"/>
          </Match>
          <Match>
            <Class name="‾controllers¥.routes.*"/>
          </Match>
        </FindBugsFilter>))
}


ではまずここまででfindbugsを実行してみましょう。

http://www.inoio.de/blog/2013/01/08/integrating-findbugs-into-a-playframework-2-project-java/

には

sbt findbugs

のコマンドが書いてありましたが、playコマンドで実行すれば問題ありません。
とりあえずplayのコンソールを開きます。(下の画面のバージョンが2.1.0になってるのは一旦無視して下さい)

       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.6.0_37 and Scala 2.10.0), http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hoge] $ 
ここで次のコマンドを実行します。

       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.6.0_37 and Scala 2.10.0), http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hoge] $ findbugs

これで実行されてtarget/scala-2.9.1/findbugs/findbugs.xmlが生成されます。

ちなみに、project/Build.scalaに除外ルールを記述したと思いますが、除外ルールを変更しても全然更新されませんでした。
cleanしても駄目だなあと思いましたが、次のコマンドを打てば除外ルールがクリアされます。




       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.6.0_37 and Scala 2.10.0), http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hoge] $ update


どうやら一つ上のレイヤーで更新をかけて上げる場合にはcleanコマンドではなく、updateコマンドを使うようです。


Jenkins側の準備

Jenkinsはすでに稼働しているものとします。
そして、とりあえずPlay Frameworkのプロジェクトに対してJenkins側でジョブを作って、distコマンドとか実行して、playのstart,stopが出来ている状態を前提としています。

プラグインのインストール

必要になるのは次のプラグインです。

  • sbtプラグイン(多分これなくても動かせます)
  • findbugsプラグイン
まあ、サクッと入れてあげましょう。
ちなみにsbtコマンドが必要、つまりCentOSだと
yum install sbt
が必要?とか思いましたが、不要です。

以下、多分不要です。
################ここから#######################
さてプラグインを入れたら1つだけ設定を行います。

「Jenkinsの管理」→「システムの設定」からsbtのjarファイルのパスを指定してあげます。

今回は次のようにしました。

Name:play2.0.4
Path:/usr/local/play-2.0.4/framework/sbt/sbt-launch.jar

Playの中に含まれているsbt-launch.jarを指定してあげればOKです。
################ここまで#######################

ジョブの作成

ビルドでまずはシェルの実行をします。
PLAY_HOME=/usr/local/play-2.0.4

cd ${WORKSPACE}
${PLAY_HOME}/play clean
${PLAY_HOME}/play compile
とりあえず上のように設定しましたがどちらかと言うとupdateコマンドを呼んであげたほうがいいかもしれません。

こうすれば多分sbtプラグインがなくてもfindbugs実行出来ます。

PLAY_HOME=/usr/local/play-2.0.4

cd ${WORKSPACE}
${PLAY_HOME}/play clean
${PLAY_HOME}/play update
${PLAY_HOME}/play compile
${PLAY_HOME}/play findbugs

以下、多分不要です。
################ここから#######################
次にBuild using sbtを選択します。
変更する場所としては、Actionsに以下を指定します。

Actions:findbugs

################ここまで#######################


ビルド後の処理として、
Findbugs警告の集計を追加します。
もともとfindbugs.xmlを生成するようにしてあるので、特に設定を変更せずに行けました。

これでビルドを実行することでFindbugs警告をJenkins上で確認できるようになります。

xmlによる出力だと人間の目で見ても見づらいですが、Jenkins上だと非常に見やすくなりますね。

2013年3月12日火曜日

「(CakePHPとか)PHPのテストについての勉強会」を横で聞きながらの感想

先日、Co-Edoに遊びに行きました。
その日はCakePHPとか、PHPのテスト勉強会をやっていました。

部屋の片隅でコソッとJavaをいじりながら聞いていた感想を勝手に書きたいと思います。

以前はSymfonyやCakePHPなどをやっていましたが、最近はJavaしか触っていないので、そこまで深くは聞いていなかったのですが、どの言語でもやることは一緒だなあと思ったので、PHPのテストの話を聞いて、Javaでどこまで実践できているかを考えてみたいと思います。

テストの基本からCakePHPでTDD/BDDへ by @sizuhiko


テストを書きましょう、という話です。
テストを書くことによるメリットの説明などが行われていました。CGI時代は単純なプログラムだったけれど、現在は複数人での開発、プログラムの複雑化などにより、テストの必要性が高まっているということでした。
スローテストは不健康の始まり、ということで、テストを走らせてから30分もテストしっぱなしだとだめだよね、という話でした。
テスト自体もCIしなきゃ駄目ですね。
さらにはCakePHPのテストについての説明でした。
CakePHPのテストでSelenium2が使えます。Jenkinsで継続的にテストしましょうという話でした。

【ふりかえり】テストはその昔、Slim3のチュートリアルでテストを書いたことがあるだけです。。。


「CakePHP2+Jenkinsで継続的インテグレーション」について話してきました


Jenkinsを使ってCakePHP2をテストする方法の説明でした。

CakePHP2用のBuildファイル サンプル(Ant)も用意されているようでございます。
【ふりかえり】PHPでもCheckstyleやPMD使えるんですね。Jenkinsはビルド以外にはFindbugsなどで使ってますが、テストを実行させたことはありませんでした。

Play FrameworkのBuildファイルサンプル、どこかにないかなあ。。。


xdebugリモートデバッグ


xdebugを使ってEclipseでデバッグする方法の説明でした。
PHPだと、print_r使ってデバックすることが多かったです。
EclipseでSSHトンネル掘ってデバックする方法も紹介されていました。

【ふりかえり】JavaだとEclipseの強力なデバックツールに助けられますね。Play Frameworkだとデバックモードが重いので、気が重いですが。。。
SSHトンネル掘ってのデバックということは、例えばJenkinsサーバ上とか検証環境で動かしているのもデバック出来るということだろうか?今度やってみよう。

PHPUnit と Selenium2 を使ってブラウザベースの自動テストを実行するための最初の一歩的な何かを発表してきた


PHPでSelenium2を使う方法の紹介でした。
やはりシナリオ作成はFirefoxのプラグインでボタンポチポチして、ソースコードに変換する方法ですね。
IEだとちょっと設定が色々必要なようです。

【ふりかえり】とりあえずやったことは、ある。サーバの不可などでWaitの制御が結構手間かかる。

IntelliJ IDEA からCakePHPのテストを 実行する方法


 IntelliJ IDEAでテストを動かす方法の紹介でした。

 【ふりかえり】IntelliJ IDEA =PhpStormなんでしょうか?最近すごいよく聞きます。
NetBeansやらAptanaやらに心変わりしたこともありますが、結局Eclipseに戻ってきたので、もう他のエディタに移る気はあんまりありません。WebStormを使ったときはJavascriptの強力なエディタだなあとは思いましたが。。。


ちなみに、今回新しいサイトを立ち上げようとしていて、言語・フレームワーク選定から入りました。近々リリースします。

JavaかPHPを使うとしたら今の時代、個人でなるべくお金をかけずにやるなら以下の選択肢かと思います。

(なお、Ruby,Pythonは1から勉強しないといけないため、今回は選定から外しました。
諸般の事情で早いところサイトを立ち上げて、というか、復旧させて、ドメインに良い感じのコンテンツを置いてあげないといけない状況になっているので、とにかくスピード重視です。この辺りの諸般の事情については、また別のエントリーで触れたいと思います。)

PHPのフレームワーク(CakePHP,Symfony)×ロリポサーバ

まあ、Symfonyは2系になってから大規模過ぎて挫折した経験があるので、CakePHPが手っ取り早いと思います。

Play Framework×Heroku

今の時代、PaasといったらHerokuでしょうか。ただ、無料で使える範囲だとDBサーバーの容量がすぐにあふれてしまいそうなので、今回は却下です。
Heroku以外でサクッとPlay Frameworkが動かせるPaasがあれば教えて欲しいです。

Slim3×GAE

GAEはオワコンでしょうか?正直そう思います。
GAEのDBサーバはダンプを取ってMySQLに移行とかがサクッと出来ません。一生添い遂げる覚悟がないと厳しいかもしれません。
GAEでもRDS系のDBが使えるようですが、本気で有料のようです。
今回たちあげたいサービスはYoutubeからデータを引っ張ってくるので、同じGoogleのサービスであれば、親和性が高いかも、と雰囲気で選びました。
親和性云々実際は関係無さそうでした。
jarファイルもXXX-appengine.jarみたいなのと、どっち使えばいいの?という感じです。

結局慣れたところでSlim3を使うことにしました。
今からCakePHPを思い出すのはちょっと時間かかりそうだった、というのとGAEの限界とHerokuの限界だったら、Herokuの限界の方が先にきそうだと思ったので、GAEとしました。