hogepiyoエンジニアリング

トラブルシューティングからノウハウ、作ってみた系まで扱う情報系ブログ

IntelliJ上でtomcat起動時にClassNotFoundExceptionがでるときの対処(Maven管理プロジェクト)

Eclipdeだと大概Assembly Deploymentでmavenを追加すれば直るわけですが、IntelliJだとそれに類する機能が見当たらないのでどうしましょっていう話

ClassNotFoundExceptionが出る原因

ビルドは通るくせにtomcat起動時にExceptionが出るもんだから「は?」となるがtomcatが参照するディレクトリにファイルが生成されていないことが原因

対処法

.javaファイルは $APP_HOME/WEB-INF/classes、ライブラリは $APP_HOME/WEB-INF/lib配下に置くようにする
置く場所を指定するにはpom.xmlに下記のように記述する
ライブラリに関しては結局コピーするくらいしか方法が見つからなかった
実は設定できるんだったら情報ほしいです。。

<project>

<!-- なんかその他設定 -->

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
	<configuration>
	<encoding>UTF-8</encoding>
	<source>1.8</source>
	<target>1.8</target>
	</configuration>
      </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
      <!-- copy-dependenciesで依存関係のあるライブラリをlibディレクトリにコピー -->  
      <execution>
        <id>copy</id>
	<phase>install</phase>
        <goals>
	  <goal>copy-dependencies</goal>
        </goals>
        <configuration>
	  <outputDirectory>web/WEB-INF/lib</outputDirectory>
        </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>

  <!-- .classファイルの出力先  -->
  <outputDirectory>web/WEB-INF/classes</outputDirectory>
</build>

</project>

Docker(とその他ツール)でつくるなんちゃってオートスケーリングシステム

最近dockerが流行ってきてて自分もdocker使ってなんかやりたいなーと思ってなんちゃってオートスケーリングシステムを作ってみた。

全体図

最初にざっと全体図
システム全体の概要はこんな感じ
f:id:marshi:20140706143545j:plain

ツールの役割は以下のとおり。ちなみにOSはCens OS

役割 ツール 今回使用したバージョン
Load Barancer HAProxy 1.4.24
リソース管理 Mesos 0.19.0
仮想マシン Docker 0.11.1
負荷監視 Zabbix 2.2

簡単にいうとZabbixでDocker上で動いているサーバの平均CPU使用率を監視して、負荷が一定以上になったらサーバを増加、一定以下になったらサーバを減少させる。
どの物理サーバに新しいdockerコンテナを作成するかはMesosに任せる。

各部品の説明

HAProxy
webサーバのロードバランサー
設定ファイルにIPを追加することでバックエンドサーバを増やせる。
お手軽なので今回はこれで。

Mesos
物理サーバで構成されたクラスタから空いているリソースにジョブを依頼できるツール
Mesos Masterに依頼することでMesos Slaveサーバの中から空いているリソースを提供してくれる
全体図中のMarathonはMesos経由で永続的なジョブを走らせるためのMesos Frameworkというものらしい

Docker
いわずもがな

Zabbix
リソース監視ツール
監視対象のリソースの使用状況をトリガにして任意のスクリプトを実行させることができる

構築手順

ツールそのものの導入については割愛
Mesosの導入についてはsonots:blogさんの下記エントリが詳しい。実際かなり参考にさせていただきました。
http://blog.livedoor.jp/sonots/archives/35421955.html
http://blog.livedoor.jp/sonots/archives/35451869.html

ZooKeeperの起動(sonots:blogより引用)

# cd /path/to/mesos
cd 3rdparty/zookeeper-3.3.4
cp conf/zoo_sample.cfg  conf/zoo.cfg
sudo ./bin/zkServer.sh start
cd -

Mesosの起動

cd mesos/build/
bin/mesos-master.sh --zk=zk://{zookeeperのサーバIP}:2181/mesos --work_dir=work_dir --quorum=1
bin/mesos-slave.sh --master=zk://{zookeeperのサーバIP}:2181/mesos --isolation=cgroups/cpu --cgroups_hierarchy=/cgroup/cpu --ip=${slaveサーバ自身のIP}

Marathonの起動

cd marathon
bin/start --master zk://${zookeeperのサーバIP}:2181/mesos --zk_hosts ${zookeeperのサーバIP}:2181

Zabbixの設定(これが一番めんどくさかったりする)

監視対象(アイテム)の追加
設定 -> テンプレート -> テンプレートの作成 から新規テンプレートを作成
テンプレートを作成したらテンプレートからアイテムに移動して、各サーバのCPU負荷を監視するためのアイテムの追加を行う
このとき、zabbixトラッパーにしておくことで、zabbix APIを使って送信された値をZabbixで受け取れるようにする
f:id:marshi:20140706160615p:plain

さらに、各サーバのCPU負荷の平均を監視するためのアイテム追加を行う。
これにはZabbixアグリゲートを指定する
f:id:marshi:20140706160937p:plain

トリガの追加
さらに、CPU負荷に反応するためのトリガを設定
CPUが30%以上の場合と15%以下の場合に反応するようなトリガをつくる
f:id:marshi:20140706162741p:plain
f:id:marshi:20140706163007p:plain

アクションの追加
トリガの条件を満たしたときに実行するアクションを設定する
CPU使用率30%を超えたときにはスケールアウト、15%を下回ったときにはスケールインするスクリプトを実行するように設定する
スクリプトについては後述

スクリプト

作ったスクリプトについてざっと説明。

https://github.com/marshi/autoscaling

cpu.sh
dockerコンテナ上で動作する仮想マシンCPU使用率を取得する。
引数としてコンテナIDを指定することで該当コンテナ上の使用率のみ取得する。
http://qiita.com/marshi@github/items/e8db79c43abf2fca8d72
ちなみにDockerのメトリクス収集に関してはつい昨日参加していた"Docker Meetup Tokyo #3"で@stanakaさんがお話されていた。「タイムリーな話きた!これでqiitaのストックも伸びる!」とか思ってたら全然そんなことなかった。世の中甘くない。

send_zabbix_cpu.sh
zabbixサーバからクラスタのコンテナID一覧を取得し、cpu.shを使って各コンテナ上のCPU使用率を取得したうえでzabbixサーバへ送信する。
各ホストサーバ上で定期実行する必要がある。

send_node_num.sh
クラスタ上で動作する仮想マシンの台数をMESOSから取得し、zabbixサーバへ送信する。
いずれかのサーバ上で定期実行する必要がある。

zabbix_host.sh
docker eventによって、dockerの起動の検知とHAProxyへの登録、docker終了の検知とHAProxyからの削除を行う。
各ホストサーバ上で常時起動しておく必要がある。

scaleout.sh
実行するたびにクラスタ上のサーバ台数を1台増やす。
zabbixで負荷が閾値を超えるとこのスクリプトを実行する。

scalein.sh
実行するたびにクラスタ上のサーバ台数を1台減らす。
zabbixで負荷が閾値を下回るとこのスクリプトを実行する。

動作確認

Zabbixの監視対象にDockerで作ったサーバのノード数も追加して、CPU平均使用率とノード数を見てみた
CPU使用率にまぁ追随してそう
f:id:marshi:20140706164455j:plain
ただし、実行条件をチェックするタイミングによっては過剰なノード数の増加・減少が発生しているため、タイミングの調整やノードの下限の設定などが更に必要そう。

まとめ

Docker、Zabbix、Mesos、HAProxyを使用してオートスケーリングシステムの構築をしてみた
実際の動作確認ではCPU使用率の増減に応じてクラスタのノード数も増減していることを確認できた
ただ、調整の必要がある点として、CPU使用率に対する極端なノード数の増減、最低動作台数の保証がありますね
さらに、各スクリプトを手動で実行する必要があるので、これらを包括的に管理・実行するための仕組みも用意できればなおよし

オートスケーリングって監視間隔が短くないとスパイクに対応できないし、かといって監視間隔が短すぎるとロードバランシングされて負荷が減少する前に一気にノード数が増えそうだし、どういう監視の仕方がいいんでしょうね。
リソースが潤沢にあるなら別に短い間隔で監視して増え過ぎたら後で減らすッて感じでもいいんでしょうけども。

あと最近知ったけどflynnっていうのがあってそれで今回つかったMesosとか置き換えられるんじゃないのかーと思ったり。むしろオートスケーリングがもっと簡単にできるんじゃ・・と思ったり。
ちょっとした絶望感を覚えつつも触ったことないものにいっぱい触れる機会を持てたからいいかと前向きに捉えることにする。

EclipseのDeployment Assemblyの項目で"The given project is not a virtual component project"とでるときの対処

なかなか原因がわからずはまってたんですが、一応わかったのでメモ

方法は単純で↓の画面で動的Webモジュールのチェックをいれればいい。
f:id:marshi:20131122000545p:plain

GitHubからプロジェクトをインポートしてきたときにこういう現象が起こったが、根本的な原因が何かはまだよくわかってない。

SpringでFTL(freemarker)を使うときの設定

STS(Spring Tool Suite)を使ってEclipseでSpring MVCプロジェクトを新規作成すると、初期状態ではテンプレートエンジンとしてJSPを使うように設定されている。
これをFTL(Freemarker Template Language)を使うように変更しようとしたら少しやらないといけないことがあったのでメモ。

やらないといけないことは大きく2つ。

  1. ライブラリの追加
  2. servlet-context.xmlの編集

ライブラリの追加

Mavenプロジェクトならpom.xmlに以下を追加

<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework-version}</version>
</dependency>

servlet-context.xmlの編集

編集前

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/views/" />
	<property name="suffix" value=".jsp" />
</bean>

編集後

<bean id="freemarkerConfig"
	class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
	<property name="templateLoaderPath" value="/WEB-INF/views" />
</bean>

<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
	<property name="prefix" value="" />
	<property name="suffix" value=".ftl" />
</bean>

この状態で WEB-INF/views/配下にftlファイルを置いて呼び出せばftlファイルの内容が表示される。

MySQLの外部キー制約エラーの詳細の確認方法

結論から言うと他のサイトで見るような外部キーのややこしい制約にはまったわけではなく、単にテーブル名の不一致だった。
が、基本的にMySQLで外部エラーがでたときは以下のような簡素なメッセージしか出してくれないので原因が判別しづらい。

ERROR 1005 (HY000): Can't create table 'hoge_db_name.huga_table_namer' (errno: 150)

そこで、以下のコマンドで最新の外部キー制約エラーの詳細を確認することができる。

SHOW ENGINE INNODB STATUS\G;

これを実行するといろいろな項目がずらっとでてくるが、外部キー制約エラーの詳細は

LATEST FOREIGN KEY ERROR

に書かれている。昔は以下のコマンドだったらしいが、新しめのMySQLだとSyntax Errorではじかれるので注意

SHOW INNODB STATUS;

なんで最初っから詳細メッセージ出してくれないんだろうなぁ。

Springでアノテーションを使ってプロパティファイルを読み込む

調べてもパッと出て来なかったので忘備録

ただし、結局プロパティの読み込みはアノテーションよりもXMLのほうで読み込んだ方がわかりやすいかも.
とはいいつつ、わからないのが気持ち悪いので一応書く.

@Configuration
@PropertySource("classpath:parameter.properties") //プロパティファイルの読み込み
public class User {

	@Autowired
	private Environment env; //読み込んだプロパティの結果が格納される

	public void speak() {
		System.out.println("こんにちは" + env.getProperty("name") + "です"); //プロパティのキーを指定(この場合は"name"がキー)して値を取得
	}
}

こんな感じ.

knife-soloをUbuntuにインストールする

基本的にドットインストールの動画通りだけど、chef-soloのインストールで詰まったのでメモ.

まず、chefを https://learnchef.opscode.com/quickstart/workstation-setup/#linux を参考にインストールする

$sudo gem install knife-solo
Fetching: mixlib-config-1.1.2.gem (100%)
Fetching: mixlib-cli-1.3.0.gem (100%)
Fetching: mixlib-log-1.6.0.gem (100%)
Fetching: mixlib-authentication-1.3.0.gem (100%)
Fetching: mixlib-shellout-1.2.0.gem (100%)
Fetching: systemu-2.5.2.gem (100%)
Fetching: yajl-ruby-1.1.0.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing knife-solo:
        ERROR: Failed to build gem native extension.

        /usr/bin/ruby1.9.1 extconf.rb
/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load suc                      h file -- mkmf (LoadError)
        from /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from extconf.rb:1:in `<main>'


Gem files will remain installed in /var/lib/gems/1.9.1/gems/yajl-ruby-1.1.0 for                       inspection.
Results logged to /var/lib/gems/1.9.1/gems/yajl-ruby-1.1.0/ext/yajl/gem_make.ou                      t

あ?
requireされているmkmfがないらしい。

$ sudo apt-get install make
$ sudo gem install knife-solo
Building native extensions.  This could take a while...

省略

Installing RDoc documentation for knife-solo-0.2.0...
$ knife -v
Chef: 11.6.0

入ったぽい。めでたし。