さよならスティッキーセッション!PayaraでJavaEEでもセッションをKVSに。
この記事は Java EE Advent Calendar 2015 の 9 日目です。 昨日は btnrouge さんの「Payara Microのからくり」でした。
意図せずしてPayaraネタ連続です。
はじめに
Payara MicroはSpringBootやWildfly Swarmあるいは一日目に紹介されていたKumuluzEEと同様にJavaEE向けマイクロサービス環境です。 javaコマンドで単純に実行できるDockerフレンドリーな奴ですね。
先に上げたMWと一番の違いはPayara MicroはEJBを組み直してfat-jarを作るわけではなく、最小構成のコンテナでwarをデプロイして動かす、ということです。 具体的には下記のようなコマンドで実行します。
$ java -jar payara-micro-4.1.1.154.jar --deploy example-payara_micro.war
payara-micoro.jarがシンプルに起動だけする感じですね。単一jarでは動かないとい点はありますが、ギャップが小さい分、不具合が圧倒的に少ないですね。 Wildfly Swarmなんかは色々アグレッシブな分、まだ安定してない感じがしますが...
通常のwarと開発方法が同じなので、開発時はNetBeans + Payaraで作って実行時はPayara Microみたいな開発が楽な気がします。
さて、そんなPayara Microですがもう1点重要な点として、JavaEEの特徴の一つであるスティッキーセッションを要求されません! GlassFishを始め、JavaEEではインメモリセッションレプリケーションが基本になっており、原則同じサーバへのアクセスが要求されます。 しかし、PayaraではKVSであるHazelcastを組み込んであるため、どこのサーバへのアクセスも透過になるのでスケールアウト構成がシンプルに構築できます。
LL系だとKVSにセッションを格納するのは珍しく無いですが、JavaEEだとOracle Coherenceを担ぎ出す必要があったので大きな一歩ですね。JCacheはHTTP Sessionをカバーする仕様ではなさそうですし。
準備
下記からbootstrapになるpayara-microのjarファイルを落とします。現時点での最新版は下記からダウンロードしてください。
実践
それでは実際に試していきたいと思います。まずは、mavenプロジェクトで今回は作成したpom.xmlは下記の通り。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.orz.pascal</groupId> <artifactId>example-payara_micro</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>example-payara_micro</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>fish.payara.extras</groupId> <artifactId>payara-micro</artifactId> <version>4.1.152.1</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>example-payara_micro</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>7.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
基本、JavaEEベースのものですね? 続いてコードになります。せっかくなので、CDIのSessionScopeを使います。
@Data @Named @SessionScoped public class User implements Serializable{ String name; int age; }
リクエストを受け取り、CDIをバインドするJAX-RSサービスは、StatelessBeansで作ります。
/** * REST Web Service * * @author koduki */ @Path("session") @Stateless public class SessionResource { @Inject private User user; /** * Creates a new instance of SessionResource */ public SessionResource() { } @GET @Produces(MediaType.APPLICATION_JSON) public String get() { String version = "2.0"; String msg = "Application version " + version + System.lineSeparator(); msg += String.format("I am %s. I am %d years old.", user.getName(), user.getAge()); return msg; } @GET @Path("update/{name}") @Produces(MediaType.APPLICATION_JSON) public User update(@PathParam("name") String name, @QueryParam("age") int age, @Context HttpServletRequest request) { user.setName(name); user.setAge(age); return user; } }
では、こちらを動かして見ましょう。
$ mvn package $ java -jar java -jar payara-micro-4.1.1.154.jar --deploy target/example-payara_micro.war [2015-12-09T07:19:25.856+0900] [Payara 4.1] [INFO] [NCLS-CORE-00087] [javax.enterprise.system.core] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1449613165856] [levelValue: 800] Grizzly Framework 2.3.23 started in: 33ms - bound to [/0.0.0.0:8080] . . Members [1] { Member [192.168.99.1]:5901 this } ]] . . [2015-12-09T07:19:39.340+0900] [Payara 4.1] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1449613179340] [levelValue: 800] Deployed 1 wars
Deployed 1 warsになれば完了です。
途中、Membersと出ているところがHazelcastのクラスタです。 これは、マルチキャストグループで範囲が指定されていて自動的にひっかけてきます。 登録作業がいらないのでDokcerとかでダイナミックにインスタンスが増減しても、問題にならないのはいいですね。
では、検証コマンドを。ブラウザだと確認が面倒なのでcurlで実施しました。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/example-payara_micro/webresources/session HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONID=3ccb47b039bb17f872d2216c9cde; Path=/example-payara_micro; HttpOnly Set-Cookie: JSESSIONIDVERSION=2f6578616d706c652d7061796172615f6d6963726f:0; Path=/example-payara_micro; HttpOnly Content-Type: application/json Date: Tue, 08 Dec 2015 22:53:29 GMT Content-Length: 52 Application version 2.0 I am null. I am 0 years old.
最初は、セッショに何も値が入ってないのでnullが返ります。続いて値の登録して、結果を確認して見ます。
$ curl -c cookie.txt -b cookie.txt -i "http://localhost:8080/example-payara_micro/webresources/session/update/Nanoha?age=9" HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f6578616d706c652d7061796172615f6d6963726f:1; Path=/example-payara_micro; HttpOnly Content-Type: application/json Date: Tue, 08 Dec 2015 22:55:24 GMT Content-Length: 25 {"age":9,"name":"Nanoha"}% $ curl -c cookie.txt -b cookie.txt -i http://localhost:8080/example-payara_micro/webresources/session HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f6578616d706c652d7061796172615f6d6963726f:2; Path=/example-payara_micro; HttpOnly Content-Type: application/json Date: Tue, 08 Dec 2015 22:55:57 GMT Content-Length: 54 Application version 2.0 I am Nanoha. I am 9 years old.
まずは、セッションが想定どおりに動いているのが確認できました。続いて、もう一つ立ち上げます。
$ java -jar ~/Downloads/payara-micro-4.1.1.154.jar --deploy /tmp/example-payara_micro.war --port 8180 Dec 09, 2015 7:57:46 AM org.glassfish.security.services.impl.authorization.AuthorizationServiceImpl initialize . . Members [2] { Member [192.168.99.1]:5901 Member [192.168.99.1]:5903 this } ]] . . [2015-12-09T07:58:06.839+0900] [Payara 4.1] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1449615486839] [levelValue: 800] Deployed 1 wars
今回は同一サーバに複数個立ち上げるのでポートを変更してあります。 また、Membersに新しく追加されたのが確認できるかと思います。
では、新規に立ち上げた方のサーバにアクセスしてみます。
$ curl -c cookie.txt -b cookie.txt -i http://localhost:8180/example-payara_micro/webresources/session HTTP/1.1 200 OK Server: Payara Micro #badassfish Set-Cookie: JSESSIONIDVERSION=2f6578616d706c652d7061796172615f6d6963726f:3; Path=/example-payara_micro; HttpOnly Content-Type: application/json Date: Tue, 08 Dec 2015 23:01:07 GMT Content-Length: 54 Application version 2.0 I am Nanoha. I am 9 years old.%
nullではなく、適切にレプリケーションされた値が入ってることが確認できました! これでラウンドロビンでアクセスができますね!
Docker対応
やはり、この手のものはDocker経由で使いたいので、Dockerでの動きを検証してみました。
上記のDockerファイルを元に作成して、問題なく動作しました。
少なくとも同一ホストであれば、特になんの設定もしなくても Docker間でのマルチキャストによるディスカバリも含めてセッションレプリケーションも問題なく動作しました。
複数グループ
複数のセッション/クラスタグループを作るには、マルチキャストグループを変更することで可能です。
payara-microの場合は、IPが224.2.2.4がデフォルトのようなので
$ java -jar payara-micro-4.1.1.154.jar --deploy example-payara_micro.war -mcAddress 224.2.2.4
こんな感じで指定すればいいかと思いきや、現状では下記のようなエラーが出ます。
Dec 09, 2015 8:32:16 AM org.hibernate.validator.internal.util.Version <clinit> INFO: HV000001: Hibernate Validator 5.1.2.Final Exception in thread "main" fish.payara.micro.BootstrapException: PlainTextActionReporterFAILURENo configuration found for server.hazelcast-runtime-configuration at fish.payara.micro.PayaraMicro.bootStrap(PayaraMicro.java:714) at fish.payara.micro.PayaraMicro.main(PayaraMicro.java:105) Caused by: org.glassfish.embeddable.GlassFishException: PlainTextActionReporterFAILURENo configuration found for server.hazelcast-runtime-configuration at com.sun.enterprise.glassfish.bootstrap.ConfiguratorImpl.configure(ConfiguratorImpl.java:75) at com.sun.enterprise.glassfish.bootstrap.GlassFishImpl.configure(GlassFishImpl.java:71) at com.sun.enterprise.glassfish.bootstrap.GlassFishImpl.<init>(GlassFishImpl.java:65) at com.sun.enterprise.glassfish.bootstrap.StaticGlassFishRuntime$1.<init>(StaticGlassFishRuntime.java:116) at com.sun.enterprise.glassfish.bootstrap.StaticGlassFishRuntime.newGlassFish(StaticGlassFishRuntime.java:116) at fish.payara.micro.PayaraMicro.bootStrap(PayaraMicro.java:694) ... 1 more
masterでは修正されたようなので、しばらく更新を待つしかないですね。 ちなみに、マイクロじゃない方のPayaraでは問題なく動作しましたので、すぐ使いたいならそちらで。
バージョニング
今回の構成ではラウンドロビンでバランシングができるので、シンプルなリリース要件ならブルーグリーンデプロイメントで問題ないと思います。 ただし、リリース中に一切ログインさせない、不整合も起こさせないといった完全なZero-downtime Deploymentを実施するならバージョン毎に別のセッションが必要です。
しかし、現行のGlassFish及びそれをベースとしたPayaraにはアプリバージョン毎のセッション等は作成できないので、 そちらに関しては別途の仕組みの検討が要りますし、いずれにしてもスティッキー性が必要になってきます。
まとめ
今回はPayara Microでのセッションレプリケーションを確認しました。実にDocker時代と相性の良さそうな機能ですね。 将来的には、mod_mrubyと組み合わせてZero-downtime Deploymentも作ってみたいと思います。
では、明日はsusumuisさんの「Javaプログラマー12年の僕がSpring童貞卒業的なこと書きます!」です!
参考
WildFly Swarmではじめる「パーツとしてのJavaEE」
天神LT勉強会 on Zusaar でWildFly Swarmに関して話してきましたので、ブログにまとめ直してみます。
JavaEEコンテナの世界観
昔ながらのJavaEEコンテナはすべてがJavaEEで完結することを目指して作られていました。 なので、WeblogicやGlassFish, WildFly等には下記のような機能があります。
あと、リソースを効率的に使うために、一つのマシンに複数のアプリを乗せる事を前提とした機能も多いです。
「同一のミドルなのでシステムはうまく結合していて、管理も簡単!」
「JavaEEコンテナさえあればインフラは他には不要!」
というやさしい世界を目指してる感じなのですが、実際は
「そんなのないよ、ありえない」
という感じです。
従来のJavaEEコンテナの限界
どうありえないかですが、だいたい下記のような問題があります。
- ブルーグリーンデプロイメントとかを考えるとクラスタリング機能が弱い(URLが同じアプリを複数持てない)
- OSを含めてJavaEE以外のシステムの監視もあるので、アラートはZabbixとか別でやりたい
- 無停止デプロイ系は困ったちゃん(信頼するには精度が微妙で、理解するにはブラックボックス過ぎる)
- そもそも、JavaEEだけでは完結しない(Apacheが必要だったり、ローカルファイルが必要だったり)
すべてがJavaEEで完結するやさしい世界なら他のミドルも要らないし、幸せなのですが、現実は過酷です。色々なツールを組み合わせて使ってることが多いでしょう。
当然、そうなると組合せを最適化するための支援ツールが必要になってくるのですが、それがDockerであったり、Consulであったり、あるいはOpenShiftやCloudFoundryのようなPaaSというのが現代の流れです。
個々のシステムを上記に挙げたような基盤でくっつけていく感じですね。この流れにJavaEEコンテナも統合したい。
別に、現状でも統合できるのですが、単なる実行以外の機能もたくさん付いていので、無駄に重かったり、デプロイしづらかったりと、扱いづらかったりするのが現状の課題です。
WildFly Swarm
そこでWildFly Swarmです。
これは乱暴に言えば、SpringBootのJavaEE版です。WildFlyのコンポーネントと組合せて、実行可能なFat-JARを作成します。
クラスタ機能とかそういったものは一切なくなるので、Docker等とかと組み合わせて、クラスタリングやスケジューリング、デプロイなんかを考えるのが前提です。
内部的には同じくJBossプロジェクトで出しているJavaEEのテストツールであるArquillianでも使われているShrinkwrapを使ってFat-JARを作成しているようです。
WildFly 9 betaをベースとしていて、Swarm自身もAlpha3ということもあって、JSFなんかはまだ正常動作しませんが、CDIやJAX-RS、JPAなんかは動きます。
パーツとしてのJavaEE
WildFly Swarmを使うことで、DockerやConsulなどの複合システムでアプリケーション実行という機能を提供する「パーツ」として使いやすくなります。
結果として、JavaEEコンテナで完結するよりは複雑な構成になりますが、JavaEEとそれ以外で2重に色々なものが存在する状態よりはずっと楽になります。
似たようなアプローチとしては、GlassFishベースのPayara MicroとWebSphareのLiberty Profileがあります。
逆に、対極のアプローチが次期Weblogicに搭載される予定のマルチテナントです。同一のWeblogicで複数アプリをより互いの影響を小さくした状態にして相乗りさせ、効率的に集約をする機能です。
従来のJavaEE的な考え方を追及する方向なので、そもそもJavaEEのベースになっているWeblogicとしては自然な進化の気もします。OS仮想化やDockerによるコンテナ化よりもずっと効率的でしょうし。
使い方
前置きが長くなってしまいましたが、使い方です。JAX-RS, CDI, JPA(h2database利用)を使ったシンプルなプログラムをGitHubにおいてあります。
WildFlyのインストールやその他準備は不要で、Mavenがあれば動きます。NetBeansとかMavenと連携できるIDE使えば開発も楽ちんです。
まずは、特に工夫をせず、シンプルにJAX-RS, CDI, JPAを使ったコードをそれぞれ書きます。
リソースはEmployeeServiceのfindAllお読んで結果を返す感じ。
@ApplicationScoped @Path("/employees") public class EmployeeResource { @Inject private EmployeeService employeeService; @GET @Produces(MediaType.APPLICATION_JSON) public List<Employee> findAll() { List<Employee> results = employeeService.findAll(); System.err.println(results); return results; } }
サービスは単純にJPAを呼ぶだけ。
@ApplicationScoped public class EmployeeService { @Inject EntityManager em; public List<Employee> findAll() { return em.createQuery("SELECT e FROM Employee e", Employee.class).getResultList(); } }
EntityはLombok使いながらシンプルなJPA。
@Entity @Data @AllArgsConstructor public class Employee implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; public Employee() { } }
ここまでは普通のJavaEEな書き方。
ここからが、Swarmの書き方。
まずはBootStrapになるクラスを作成。Arquillianと雰囲気にてますね。
public class Main { public static void main(String[] args) throws Exception { Container container = new Container(); registDataSource(container); JAXRSDeployment deployment = new JAXRSDeployment(container); // set config deployment.getArchive().addClass(JpaResources.class); deployment.addResource(JaxRsApplication.class); // load resouces deployment.getArchive().addAsWebInfResource(new ClassLoaderAsset("META-INF/beans.xml", Main.class.getClassLoader()), "beans.xml"); deployment.getArchive().addAsWebInfResource(new ClassLoaderAsset("META-INF/persistence.xml", Main.class.getClassLoader()), "classes/META-INF/persistence.xml"); deployment.getArchive().addAsWebInfResource(new ClassLoaderAsset("META-INF/load.sql", Main.class.getClassLoader()), "classes/META-INF/load.sql"); // load classes deployment.addResource(EmployeeResource.class); deployment.getArchive().addPackage("cn.orz.pascal.example.wilfly.swarm.domain"); container.start().deploy(deployment); }
データベースの登録はこんな感じ。
private static void registDataSource(Container container) { container.subsystem(new DatasourcesFraction() .driver(new Driver("h2") .datasourceClassName("org.h2.Driver") .xaDatasourceClassName("org.h2.jdbcx.JdbcDataSource") .module("com.h2database.h2")) .datasource(new Datasource("InMemoryPersistenceUnit") .driver("h2") .connectionURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE") .authentication("sa", "sa")) ); // Prevent JPA Fraction from installing it's default datasource fraction container.fraction(new JPAFraction() .inhibitDefaultDatasource() .defaultDatasourceName("InMemoryPersistenceUnit") ); }
つづいて、pom.xml。wildfly-swarm-pluginでMainクラスを指定するのがポイント。
<groupId>cn.orz.pascal</groupId> <artifactId>example-wilfly-swarm</artifactId> <version>0.1</version> <packaging>jar</packaging> <name>example-wildfly-swarm</name> <properties> <version.wildfly-swarm>1.0.0.Alpha3</version.wildfly-swarm> <maven.min.version>3.2.1</maven.min.version> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.source>1.8</maven.compiler.source> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${version.wildfly-swarm}</version> <configuration> <mainClass>cn.orz.pascal.example.wilfly.swarm.Main</mainClass> </configuration> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
ちなみに、NetBeans 8の組み込みMavenのバージョンは3.0.5なので実行すると下記のようなエラーがでます。 3.2以上のMavenを入れて指定しましょう。というか今時なんでこんなバージョンを...
WARNING: Error injecting: org.wildfly.swarm.plugin.PackageMojo java.lang.NoClassDefFoundError: org/eclipse/aether/resolution/ArtifactResolutionException at java.lang.Class.getDeclaredConstructors0(Native Method) at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
アプリケーションの実行は
$ mvn package
をすると${app-name}-swarm.jarができるので
$ java -jar ${app-name}=swarm.jar
で実行するだけです。IDEからなら単純にMainクラスを実行するだけですね。実行したら
http://localhost:8080/employees
にアクセス。中々いい感じに動きます。
まとめ
SpringBoot開発も運用も軽量で良いなー、とJavaEE派としては羨ましがってたんですが、これはなかなか良さそうです。
Dockerとの組み合わせも簡単になりますしね。とはいえ、アルファ版なので完成度とかは今後に期待。Payara Microの方が現時点の完成度は高そうだし、KVSとの連携もできるみたいだから、そっちも確認して見ようかな?
しかし、JavaEE8ではマルチテナントなんかより、こっちの機能を標準化して欲しい...
参考
Dockerってなんですか? それ、JavaEEで分かるよ。
はじめに
最近、Dockerが大人気ですね。GoogleやMicrosoftも参入してることはもちろんですが、Oracle Weblogicのサポート対象に入ってたりとミドルウェア側、それもエンタープライズ系製品が対応してきたのは面白い動きです。
とはいえDockerって何?っていう人もまだまだ結構多いと思います。
Dockerとはvmwareなどの技術の先にある軽量な仮想マシンである、と捉えてもあまり間違ってないと思いますが、個人的にはJavaEEコンテナの類似品というか、それと同じ文脈で考えたほうがしっくりきました。
なので、自分の理解を深めるためにも、JavaEEエンジニアはどのようにDocker及びその周辺技術を理解すればいいのか、という視点でまとめてみました。
そもそもDockerって?
Dockerとvmwareのような従来の仮想化で何が違うかですが、vmwareがOSの仮想化をしているのに対し、 DockerはOSの上でアプリケーションとミドルウェアを仮想化するコンテナ技術です。 まあ、chrootやjailの偉いやつです。Solarisでいうところのゾーンですね。
OSレイヤーを仮想化しないので、オーバーヘッドが非常に少なく高速に動作します。 それでいて、アプリケーションの動作環境をパッケージングしてるので、 ライブラリのバージョンや依存関係を含めて競合しない形で配布できるので 開発環境と本番環境が違う、みたいな問題も従来の仮想環境同様に解決できます。
むしろ、従来の仮想環境ではそうは言っても複数台立ち上げるのはコストが高いので、 似たようなロールは同じサーバに置く運用でしたが、Dockerの場合は軽量なのでロール毎に厳密に分けるのに向いていますね。その辺が人気の秘密です。
Dockerとwarとアプリケーションのパッケージング
さて、アプリケーションを依存ライブラリも含めてパッケージングと言われると、どこかで聞いたことがある気がしますね?
そう、「war(Web Application Archive)」です。 JavaEEの場合、依存するライブラリ(jar)を含めてパッケージングし、異なるwar間での依存を切り離します。 なので、同じサーバにデプロイしていても、あるアプリケーションはバージョン1系を使い、 別のアプリケーションはバージョン2系を使うというようなケースでも、問題が起こることはありません。
JavaEEはJavaとして閉じることで、これを実現していましたが、DockerはLinuxアーキテクチャで動作するものであればパッケージングが可能です。 なので、例えば「Apache + ImageMagic + Rails + アプリコード」をまとめてパッケージングする、という形になります。 デプロイもDockerコンテナに配置するだけなので、ImageMagicが無いとか、Apacheの設定が違うとかに悩まされることもなく、すぐに動作します。 この辺も、GlassFishにwarを配置するときとイメージは同じです。
JavaEEのwarと一番の違いはミドルウェアもパッケージングしちゃうところですね。 なので、JavaEEアーキテクチャで作成した、アプリをDocker化する場合はTomcatやJetty, あるいはGlassFishやWeblogicごとパッケージングする形になります
DockerfileとMavenと構成管理
warを作るためには通常はMavenかGradleを使いますね? この辺を使えば、手順をコード化出来るのはもちろんのこと、依存関係も自動解決してくれて便利ですよね。
Dockerの場合はDockerfileが概ねこれに当たります。 また、依存関係の解決事態はDockerの中で使うyumだとかaptだとかのパッケージマネージャに任せるのが基本となります。
より高度なビルドをしたいときにはPackerやChef/Ansibleなんかを組み合わせるケースもあります。
MavenにMavenリポジトリがあり各ライブラリを自動で落とせるように、 DockerにもDockerHubがあり必要なイメージを自動でダウンロードして使うことが出来ます。
オーケストレーションとクラスタリング
JavaEEの仕様では無いですが、GlassFishやWildFly、Weblogicといった一般的なJavaEEコンテナはクラスタリング機能を持っています。
これは複数のサーバを束ね、グループとして扱うことで、設定の同期やデプロイの一元化あるいは負荷分散や高可用性を提供します。
Dockerは基本的にはパッケージングに特化した技術なので、同等の機能はありませんが、SerfやConsulのようなオーケストレーションツールを使うことで実現できます。
オーケストレーションツールが何を提供するかといえば、基本的にはグルーピング(クラスタリング)とサービスディスカバリ、トリガの管理です。
詳細は後述しますが、簡単に言えばGlassFishやWeblogicのクラスタ機能/管理サーバを「自作するための機能」です。
残念ながら現時点でGlasFishのクラスタ機能と同じものを完成品として提供するツールは無いと思います。(使ったことがないけどGoogleのk8sはそれに当たるかも?) まあ、もうちょっと標準化されたものは今後出てくるでしょうが、根本的にインフラ構成に依存するので、 Consulをベースとした「Java + Dockerのソリューション」とか「Rails + Dockerのソリューション」みたいな感じで出てくるんじゃないかと思います。
これだけ書くと微妙ですが、クラウド、特にDockerを使うケースだとインスタンスの追加と削除を非常に頻繁に行うことになるので、 既存のGlassFishなどのクラスタ機能では不向きな部分がありますが、この点によくマッチします。
個人的にはGlassFishもWeblogicもクラスタ機能が微妙なので、この辺りは気になるところです。
では、クラスタリング、サービスディスカバリ、トリガ管理に関して、Consulを例に記載します。
クラスタリング
Consulでのクラスタリングは単なる名前付けです。主に後述するトリガ管理で有用な機能ですが、 Consulの場合は各クラスタに関してDNS登録をするので、単純な負荷分散はこの時点で対応できます。
サービスディスカバリ
サービスディスカバリはサービス、今回の話で言えばDockerコンテナを見つけるてクラスタに登録する機能です。
と言っても、難しいことをしているわけではなく、各DockerコンテナにConsul Agentをインストールし、 Consul Serverに起動時にリクエストを投げることで発見をします。
発見だけではなく、ヘルスチェックも提供していて、落ちれば自動的にクラスタから外されます。
トリガ管理
トリガ管理はイベントを起点に何らかの処理をクラスタに対して実行する機能です。
イベントはサービスディスカバリによりクラスタに追加されたり、削除されることはもちろん、外部から任意に実行することも出来ます。
トリガ機能を使うことで、クラスタに対するデプロイやZabbixやSensuといった監視システムに対して自動で登録削除をすることが出来ます。
上記の機能を組み合わせてGlassFishのクラスタ機能や管理ツールと同等の機能を作成していくことになります。JavaEEコンテナの提供するクラスタ機能だと、当然JavaEEで完結するものにしか提供されません。
結果として、同じシステムに対してGlassFishのクラスタとZabbixのクラスタが二重管理されるような形になります。Consul等を使うことでこれを集約して管理できるのは大きな利点です。
リソース管理
各コンテナが使うリソースが適切に配分されるように多くのGlassFishやWeblogicでは同時にさばけるリクエスト数はDBコネクション数などのリソース制御を行います。
これと同様の仕組みはDocker単独では提供していませんが、Mesosやk8s(Kubernetes)を組み合わせて使います。このあたりは正直まだ成熟してないので、今後に期待。
フェイルオーバー
Dockerにはフェイルオーバーに類する機能はありません。こちらはDockerコンテナ内の各ミドルウェアの機能に頼ることになります。
例えばWebシステムであればセッションさえフェイルオーバーできれば基本は問題ないので、 KVSで外部に出して、各APPサーバ、各KVSサーバ自体は状態を持たないかレプリを持つのが基本となります。
JavaEEであればGlassFish, WeblogicであればCohrerence、WildflyであればInfinispanを使うことになります。 もちろんクラスタを作ってインメモリレプリケーションをすることも出来ます。
個人的にはインメモリレプリケーションをJavaEEコンテナ側でするよりも、外部に取りたいのでSpring-Sessin + RedisがJSF等でも使えないか検討中です。
Immutable InfrastructureとJavaEE
Dockerそのものとは関係ありませんが、Dockerを取り巻く概念の一つにImmutable Infrastructureがあります。
これは、その名の通り、サーバなどのインフラに対して一切設定変更を行わず、修正やアプリケーションのデプロイをしたい場合はサーバごと作りなおす、という手法です。
これにより、Chefなどのように冪等性を気にせずスクリプトが作れますし、ローカルにファイルを出力するなどの状態が無いのでサーバの増減が非情に簡単になります。
もちろん、手動オペレーション + 物理サーバでこんなことが出来るはずがなく、自動構成管理と仮想化が基本になるのですが、Dockerはこの運用に非常にマッチします。
一見新しい概念なのですが、実はJavaEEは元々これを強く意識したアーキテクチャーです。
最近は出来ますが、JavaEEではトランザクションとポータビリティを意識してEJBへのローカルファイルへのアクセスは禁止でした。今は利便性のため可能ですが、やはりDBなどを使うのが基本となります。
EJBがJNDI Lookupで引いた場合、クラスタ内のどのサーバで動くか分からないため、そのような作ります。warまたはearをデプロイすればどこのサーバでも全く同様に動くのがJavaEEの基本思想です。 これって、Immutable Infrastructureと左程変わらないですよね? 当時と違いDockerはJava以外のものもパッケージングできるため適用対象が増えただけで、概念としては同様です。
なので、JavaEEエンジニアであればImmutable Infrastructureは考え方としてしっくりくるので、さほど警戒する必要は無いと思います。
まとめ
今回はJavaEEエンジニアはどのようにDocker及びその周辺技術を理解すればいいのか、という視点でまとめてみました。
Dockerやオーケストレーション、Immutable Infrastructureは非情にホットな技術で新しい用語も多いですが、 基本的には、JavaEEでも考慮されていたユースケースをより汎用的に、現代的に解決するための手段なので、 今回みたいに対応づけて考えると分かりやすいんじゃないかなー、と思います。
逆に言えば被ってる部分も多いので、機能の使い分けが重要になってくると思います。特にクラスタ周り。 Docker時代のJavaEEインフラをどう構成するのが良いのかは、なかなか面白そうなので継続して考えていきたいです。
この辺を考えると、未来のJavaEEコンテナはクラスタとかデプロイとかの運用要件を外部システムに任せて軽量なアプリケーションコンテナとして生きる道もあると思います。
また、現時点では要素技術が良くも悪くも独立していますが、Google Compute EngineのようなPublic PaaSやOpenShift 3.0のようなPrivate PaaSがPaaSという形で統合する統合する形になるかと思います。
それではHappy Hacking!
萌える瞬間英作文バージョン 0.401をリリースしました!
毎年、エイプリルフールはなんか作ろう! と画策しているのですが、今年は「萌える瞬間英作文」というAndroidアプリを作りました。
萌える瞬間英作文 - Google Play の Android アプリ
さて、瞬間英作文ってご存知でしょうか?
こちらの英語上達完全マップ●瞬間英作文で触れられている英語学習方法で、簡単な英文を大量に作成することで英語を瞬時に理解・作成できるように訓練する方法です。
基本的な方法は
- 日本語の文章を読む
- 1で状況をイメージしながら、英文を作成する
となります。慣れないうちは中学1年生レベルの英語ですら、結構疲れるのですが私もこの学習方法をするようになってからTOEICが100点くらい上がったのでそれなりに効果あるんじゃないかと。 ※ 他の勉強もしてたので瞬間英作文だけの効果じゃないとは思いますけど。
この学習を進めるためにこういう本も出てますし
どんどん話すための瞬間英作文トレーニング(CDなしバージョン) |
こんな感じのiOSアプリもあります。
なので、どんどん瞬間英作文を進めていくことができそうな気がするんですが、一つこの手の教材には致命的な欠点が...
というのも、英文が単調すぎたり、不自然すぎてイマイチ状況がイメージし難いんですよね。
例えば例文として出てくるのは
「私は学生です。/I am a student.」
とかなんですが、そんな事を言うシーンがあまりないので、ぱっとイメージが思いつきませんよね? イメージしながら英文を作ることが、この学習方法の最大のポイントなので、これは結構微妙です。
では、これならどうでしょう?
「ジャッジメントですの!/I am a "Judgement".」
ばっちり、ドヤ顔の少女の顔が即座に浮かびましたよね?
掲載してある英文の例としては他にも
「もう、何も怖くない/I'm not afraid of anything anymore.」 「俺の妹がこんなに可愛いわけがない/My little sister can't be this cute!」 「そうそう!も~っと私に頼っていいのよ!/Yes, you can count on me more and more!」 「あぁー心がぴょんぴょんするんじゃーっ/Ah^ My Mind is hopping.」 「煩わしい太陽ね。/Good morning.」 「くおえうえーーーるえうおおお/I am Chloe Lemaire.」
こういったイメージしやすく、日常的に使える英文をとりあえず90個くらい作ってみました。
文法はせいぜい中学2年生くらいで習うものなので、どんどん声に出していきましょう! ただし、英語がわかる人が近くにいるときはちょっと気を付けたほうがいいかも?
なお、英文の追加および間違いの指摘は随時募集中なので、ブログコメントかTwitter、あるいは
に直接PRいただければと思います。
それでは、Happy Hacking! And let's study English!
Meteorが見せるIsomorphicなDBとリアクティブな開発モデル
最近、Meteorを試して見てます。これはちょっとスゴイ。正直、当時Railsに受けたのと同じ興奮がある。
Meteorの説明は「リアルタイムWebアプリケーションフレームワークMeteorについて」あたりが分かりやすいので、こちら参照。
実は、2012年の公開時から存在は知ってたんだけど、チュートリアル見たくらいで特に興味はありませんでした。 しかし、今は違います。それは主にスマホアプリの開発にかなりの威力を発揮しそうだと気づいたからです。
まず、なんで公開時にあまり興味がなかったかですが、そもそもRailsなど同レイヤーのWebアプリケーションの開発FWとして考えていました。
その場合
- SPA
- リアクティブな開発モデル
- クライアントとサーバを同一コード(JS)で書ける
- サーバサイドのDBをクライアントから透過的に呼び出せる(IsomorphicなDBモデル)
という特徴はせいぜいリアクティブな開発モデルがちょっと気になるくらいで、他はさほど気になりませんでした。 むしろ、クライアントコードとサーバサイドを同一でってのは初期のASPやGWTはじめ多くのFWが目指したものの、そこまで流行らなかった印象だけがありました。 リアクティブプログラミングも気にはなるものの、私が当時書いていたWebアプリはさほどUIがリッチではなかったので、修正したコードが更新ボタン押さなくても反映されるのはスゴイな、という印象止まり。
SPAにはSEOの問題もついて回りますし、ざっくりフルスタック過ぎて密結合なシステムになりそうだなーと、むしろ悪い印象。
というわけで、さほど興味を引くものではなかったのですが、Meteor + PhoneGap/Cordovaと連携して使うことで、クライアントとサーバ(クラウド)のデータ同期というかアクセスモデルを超シンプルにしてくれると気づきました。
そもそも何が問題?
スマホ開発の課題をあげよ、と言われると人によって多くの課題が上がると思いますが、その一つにサーバサイドと連携したデータアクセスがあると考えています。
いろんなケースがあるかとは思いますが、サーバサイドと連携したアプリを書くときは全体として以下の様な構成にするとことが多いかと思います。
図:モデル1
このモデル1のケースでは以下の様な問題があります。
- モデルを一致させるためにサーバサイドとクライアントサイドに類似コードが大量のボイラーコードが発生する(+ REST周りのコードが必要)
- Server DBとClient DBの同期が困難(安定したネットワークではないため不整合の吸収が必須)
クライアントサイドにデータを一切持たず、サーバサイドに直接アクセスすることで、同期の複雑さ等は解決できます。しかし、反面ネットワークに繋がってない時は利用できず、繋がってる時もレスポンスに影響が出るので使い勝手に影響がでます。
実際のシステムモデルはともかくとして、プログラム上はこんな感じでシンプルにアクセスしたい。
図:モデル2
この場合、クライアントのDBは基本キャッシュになっていて、クライアントDBへの操作を行えば、サーバサイドにも自動的に反映。モデルのメタ情報も何らかの方法でサーバサイドと同期を取っておけばボイルコードが一気に減り、やる気も上がって生産性アップ! となります。
そう、Meteorはこのモデルを実現してくれてるんです。
MeteorにおけるIsomorphicなDBモデル
Meteorがサーバとクライアントを意識しないIsomorphicなDBモデルをどのように実現しているかですが、minimongoというmongodbのJS実装を使っていて、これをクライアントのキャッシュDBとして使っています。
ユーザへのレスポンスは基本的にminimongoの結果をまず返され、それと同時に投げているサーバサイドのリクエストをバックエンドで待つ。
この時、もしサーバサイドのmongodbとクライアントサイドのminimongoの結果が違った場合は、非同期でこっそり書き換えるという仕様。
厳密なデータ一貫性が必要なケースには使えないけれど、リアルタイムなデータを表示するレポート系ツールやチャットやSNSのような場合なら基本問題無いでしょう。
また、Meteorの場合、サーバサイドがnode.jsなので当然のようにモデルが共有されます。
Meteorがサーバサイドもクライアントサイドも同一のコードで書ける、という点は単に同じコードでvalidationロジックが実行出来る、というよりもモデルの共有ができるので、サーバサイドとクライアントサイドで違うビジネスロジック部分だけを意識すれば良いという点が大きいです。
IsomorphicなDBモデルとリアクティブプログラミング
Meteorの特徴はIsomorphicなDBモデルということに加えて、リアクティブプログラミングを採用している点も大きいです。
リアクティブプログラミングはExcelのように宣言的に関係を記述することでデータの変更による再計算を自動化する開発モデル。
GUIだとデータバインディングを利用したMVVMとかがその具体的な方法の認識。
この2つを組み合わせることでMeteorはリアルタイムアプリケーション--- 言い方を変えるとユーザ関連系などサーバサイドの頻繁な更新が伴うアプリケーションを非常にシンプルに書ける。
というのも、モデル1のような通常のシステム構成でchatのようなユーザ間連携があるシステムを作る場合
- クライアント側の書き込みのUIからモデルに値を渡す
- クライアントのモデルがサーバサイドのREST/IFをコールする
- REST/IFから受け取った値をサーバのモデルに渡す
- サーバのモデルはDBに値を書き込む
- サーバはDBの変更を別のクライアントに呼び出すモデルを実行(WebSocketではなくクライアントがポーリングしてるならクライアントにその実装がいる)
- 5で呼び出されたモデルはDBから値を取得してREST/IFに渡す
- クライアントはREST/IFから受け取った値をクライアントのモデルに値を渡す
- クライアントのモデルはUIに値を渡して変更する
という感じになります。FWによって記述量が変わるところはあっても、1から8の工程を明示的に何かしら記述をしないといけません。これは単純にサーバサイドもクライアントサイドもJSで書いたからといって変わるものではありません。
対してMeteorの場合は
- UIからクライアントのDBに書き込むモデルを作る
- モデルとクライアントのDBを紐付ける(※ モデルをJSONハードコーディングじゃなくてDBの戻り値にする)
- UIとモデルを紐付ける
これだけです。それぞれのステップでの記述量もかなり短くなるのですが、それ以前にステップが8個から3個に減っています。
これが実現できるのはIsomorphicなDBモデルなので、各クライアントは自分たちのローカルDBへの操作だけ意識すれば良く、かつリアクティブなので、データに変更があれば、UIにも自動的に反映されるからです。
この点が開発のシンプルさを激的に改善してくれる部分です。各工程のコストを小さくするのと、工程自体が無くなるのではたとえ1秒で終わることでも人間の意識としては結構違いますしね。
まとめ
では、Meteorは万能で同様の開発モデルを採用していくべきでしょうか? 必ずしもそうではないと思います。 まず、Meteorに限って言えば未成熟な部分もあります。特にモバイル周りはUI周りが弱かったり、Cordovaとの連携にまだ不備があったりとめんどくさい部分もあります。ただ、コレは時間が解決してくれる気もします。
そうではなく、そもそもあまり向いてないケースとしては、トランザクションが厳密なケースと、既存の連携システムが多い場合です。
トランザクションが厳密であれば信頼性の低いクライアントのDBは使えず、結局常にサーバにアクセスする以外ありません。
また、このアプローチは基本的にクライアントに合わせてサーバサイドを作る場合に適したモデルなので、すでに他システムなどでサーバサイドが存在する場合にはかえって面倒になるかもしれません。
ただ、逆に言えば厳密性が重要ではなく、プロトタイプや新規アプリを開発するには非常に向いた方法だと思うので、MBaaSとの連携含めてもうちょっと研究してきたいと思います。
そもそも、AndroidやiOSを前提にしたネイティブなMeteorっぽい仕組みのが個人的には嬉しいですし。
それではHappy Hacking!
参考
JavaEEだけどDockerがしたい!- GlassFish on Docker
Java EE Advent Calendar 2014 14日目です。
今年はDockerが大躍進した年だな、と思います。
そこで、このビックウェーブに乗り遅れないように、GlassFishを使って、Dockerベースの環境を作ってみました。
今回利用するコンテナはこちら
Appコンテナ
GlassFishが乗っているサーバです。下記のプロセスが動いています。このコンテナの数を増やす事でスケールアウトが可能。
- GlassFish
- Fluentd
- Consul client agent
本体はGlassFishですが、周辺システムとしてFluentdとConsulをインストールしてあります。
Dockerテクニック的に言えば、FuluentdやConsulを入れた状態でいったんイメージを構築して、それを継承する形で、glassfishイメージを作ってるので、wildflyやtomcatにすげ替えることも、比較的簡単に出来る構成にしてあります。
また、通常、Dockerでは1プロセスしか起動できないので、"run.sh"を作って、Fuluentd, Consul, GlassFishの3つのプロセスを起動させています。この辺りは、もしかたしたらKubernetesとかコンテナを管理するシステムを用いることでもっとシンプルに出来るかもしれないです。
他にも、Dockerらしく作るために気をつけた点としてApplicationを最初からデプロイしています。最初は起動前からデプロイしてある構成を想定してたのですが、アプリケーションのデプロイ時にJDBC Sourceを参照するためエラーになるので、auto-deployにして、起動時にデプロイする方式にしました。
DBコンテナ
App Serverが使うDBコンテナです。GlassFish上のアプリがJDBCを利用しているので、App コンテナより先に起動する必要があります。
Consulコンテナ
サービスディスカバリやオーケストレーションを提供するConsulの中央サーバです。今回の構成ではDNSも兼ねています。
- Consul server agent
Log集計コンテナ
Fuluentdのログ集計サーバです。今回はファイルに吐いてますが、本番だとmongodbとかasticsearchに連携する形になるかと思われます。
ConsulやFuluentdを入れてる理由は、Dockerを使うユースケースでは古き良きオンプレの静的な構成ではなく、クラウド的な動的な構成が想定されるためです。
起動
コンテナは構築済み&docker-hubに上げてあるので、動作確認はシンプルに下記で実施できます。
git clone https://github.com/koduki/docker-javaee.git cd docker-javaee ./docker-mng.sh up
これで動作します。"http://localhost:8080/javaee-simple-tester/"にアクセスして頂ければ、アクセスが可能です。
まとめ
こんかいはGlassFishのクラスタも組んでませんし、リバースプロクシも入れてないので、高可用構成にはなっていません。
ConsulのDNSを経由してラウンドロビンにアクセスすることは可能ですが、HA ProxyとしてConsulを使っても潰れないのかは調査不足。
一方、Consulのイベント通知機能を使えば、Consulクラスタになった時点でGlassFishのクラスタに入れるのは簡単そうです。
ただ、その構成にすべきかは、いまいち思案中。設定もデプロイもDockerレベルで完了しちゃうので、GlassFishクラスタを組むメリットは小さいです。
それよりもLBを入れて、負荷分散するほうがよっぽど重要。ただ、GlassFishは原則スティッキーなアクセスを求めるので、この辺をどう解決するのかが課題。クラスタ組まないとセッションレプリケーションもできないですしね。
単純にセッションを無理やりKVSに詰める仕組みを自作してもJavaEEとの相性が悪いので、なんとかHttpSessionレベルをフックする必要があるので、こと辺りは課題として要調査。
来年はこの構成をベースに、もっとスケールアウト出来る仕組みを考えないとなー。
明日はemagさんの「Arquillian Cubeについて」です。
参考
Excelを使わない技術 - 正しい神の殺し方
ドキュメント作成技術 Advent Calendar 2014 13日目です。
※ なお、本題とは一切関係ありませんが中2力全開気味なのでご注意ください。
Excel、というツールをご存知でしょうか? まあ、知らない人はいないと思います。
それは表計算ツールであり、グラフ描画ツールであり、データーベースであり、ワープロであり、DTPであり、時にはゲームエンジンとなる万能の釜です。
はじめは計算をシンプルにしたいという素朴な願いから生まれたそれは、やがてアレもしたい、コレもしたいという人の願いを叶え続け、万能と成り果てました。 今では、こんなことも出来ます。
Excelすげーですね!
ここまでスゴイ使い方はあまり見ませんが、方眼紙Excelで作った入力フォームとか、方眼紙Excelで作ったUMLとか、あまつさえ方眼紙Excelで作った表とかを見たりメンテナンスして、職人の凄さにむせび泣いた方なら多いかと思います。人はそれらを神Excelといいい、おそれ、あるいは尊びました。
「ネ申 Excel」問題 という論文もありますし、職人ならざる我々もドキュメントを書き、育てることを来年の抱負とするためにも、Excelを使わない技術に関して紹介していきます。
はじめに言葉があった
Advent Calendarを読むような方の期待をいきなり裏切って申し訳ないですが、Wordを使いましょう。文章書きたいなら、ね。
Wordだけではありません。UMLを描きたいならastahを使いましょう、WBSを描きたいならProjectLibreを使いましょう、画像を表示したいならそもそもjpegで良いのです。なんのために貼り付けてあるんですか?
もちろんツールは何でも良いのですが、餅は餅屋です。無理して全部Excelでやるから無駄に保守性が下がるので、適切なツールを使って、それをチーム/社内標準にすれば良いですし、印刷や他社配布ならPDFにプリントしてやれば良いのです。最近のOS使ってるならそのくらい造作も無いことなので、その作業は本当にExcelでするべきか考えてみましょう
どうでも良いですがWordを「言葉 -> コトノハ」と読むと素敵ですね。
されど我は愚者の夢を見る
神Excelには適切なツールを使うことでさっくりと消滅してもらえば良いのですが、まだ不満はあります。
それはgitなどのバージョン管理システムとの相性です。
「履歴」という謎のシートやページに編集内容や編集者を記載し、さらに「変更箇所の色を赤で変えておきました」的なコメントを見かける事があります。これはExcelを表として適切に使ってても起こる悲しい問題です。
バイナリの差分管理が適切にできれば良いのですが、あいにく私は良いツールを知らないので、コミットログなんかだけだと更新内容が判断できません。
ソースコードみたいにdiffが出ればいいんですけど。
そう、逆に言えばソースコードみたいにテキストで管理することでdiffを含めて適切に管理できます。テキストファイルで管理することで、Pull Requestで差分管理/レビューできますし、良いことづく目ですね。
テキストで書いた場合は専用ツールで書いた場合に比べて表現力が劣ることが多いですが、後述するツールでかなり補えますし、意外に気にならないものです。
巨人の肩
かのニュートンは言いました。「私がより遠くまで見渡せたとすれば、それは巨人の肩の上に乗ることによってです。」と。先人に倣って、まずは既存のツールを紹介します。
最初の一つは一日目のsky_yさんも紹介されているPandocとMarkdown. Markdownはプレーンテキストで論理構造をいい感じに表現出来る単なる「記法」ですが、githubを初め多くのツールやWebサービスが対応してるため、標準化されたWiki記法として人気を博しています。
私も普段書くメモもそうですし、このブログもMarkdown形式で書いています。pandocはMarkdownをHTMLやPDFあるいはWord形式で出力してくれるツールです。 これもかなり便利。以前書いた記事ですが、書きのようにプレゼン資料を作ることも出来ます。
Keynote風のプレゼンテーションをMarkdownで作ってみた - ブログなんだよもん
他にも図形を書くblockdiagとか、サーバの設定を記述するserverspecとか、テスト仕様書を記述するCucumberやturnipも便利です。
そして13番目の奴は訪れる
既存ツールも紹介したことなので、個人的にJSONを使った表なんかも作ってるのでちょっと紹介。 Excelを使った表でデータを管理ってのは個人的にというか仕事でよくやります。サーバ一覧とかACL一覧とかパラメータシートとかそういう奴。Excelで作るのが簡単だし、見栄えも悪く無いですしね。
ただ、前述したとおり、まともにdiffが取れないので、更新履歴シートを作ったり、変更箇所を赤色にしたり、ゴミみたいな運用をせざるえません。
更新が噛み合ったらマージも出来ない阿鼻叫喚。マスタドキュメント系なのでこれはかなり不便で、下手をすると変更担当の人に依頼するという謎の運用になります。
そんな時はこんな感じでJSONで記述して、それを埋め込んだHTMLのJavaScriptを使って表として表示することが出来ます。
"Description":"GlassFish Application Server", "Environment":["production"], "Network":{ "Host":["localhost.localdomain"], "IP":["enp0s3", "10.0.2.15"] }, "OSName":["Linux dev-ubuntu 3.13.0-32-generic"],
わりと見栄えも悪くないので、個人的には結構お気に入り。
JSONだけ外出しにした方が、そのJSONを解析するツールとかが作りやすくて便利(事実、私もserverspec風のツールを作ってます)ですが、IE以外はセキュリティの問題でローカルファイルは参照禁止なので ローカルで管理が中心の時はgistに書いたみたいにHTMLの中に埋め込む方が良いかと思います。
ちなみに、XMLの方が好きって人はXSLTを使うことも出来ます。なにげにIE, Chrome, Firefox, Sfari, Operaとほぼすべてのブラウザが対応していますしね。 こいつが普及してたらまた違ったのかなー。
幼年期の終わり
Excelは良いツールですが、なんでもExcelでするのは微妙ですし、そもそも現代のソフトウェア技術は残念ながらテキストの方が管理に向いています。
私達は、Excelという揺りかごを飛び立ち、新しい世界へと行く必要があります。ドキュメントを作って印刷するだけの時代は終わりました。現在は、作って、メンテナンスし育てる必要があるので、それに見合った方法を色々模索していかないとなー、と思います。
以上、酔っ払った勢いで、主に見出し的に中2力を出した文章を最後まで読んでいただきありがとうございました。
それでは、Happy Hacking!