これまで作成したMavenのwebappを、今度はMinishift上のOpenShiftコンテナオーケストレーション環境で動かしてみる、という内容です。
- アプリのソース: zaki-lknr/javaee-memoapp2: JavaEE + MySQL習作
- Ubuntu 18.04 (非コンテナ環境)でのデプロイ例
Minishiftのインストール・構築についてはこちら
Minishiftを使ってVirtualBox上にOpenShift環境をつくる - Qiita
※「Minishiftってなに?」「OpenShiftってなに??」「コンテナオーケストレーションっておいしいの?」「コンテナってなんや?」あたりの説明はすっ飛ばしています。
プロジェクトの作成
まず作業用のプロジェクトを作る。
デフォルトでmyproject
が割り当てられているが、せっかくなので別途プロジェクト(ネームスペース)を作る。
OpenShiftでは基本的にシステム単位でネームスペースを分ける感じにするとよさそう。(ネームスペース横断してPod間通信を行うには工夫が必要)
oc new-app
でプロジェクトを作成すると、自動でそのプロジェクトが選択された状態になる。
zaki@mascarpone% oc new-project samplepj
Now using project "samplepj" on server "https://192.168.99.100:8443".
You can add applications to this project with the 'new-app' command. For example, try:
oc new-app centos/ruby-25-centos7~https://github.com/sclorg/ruby-ex.git
to build a new example application in Ruby.
zaki@mascarpone%
ネーミングセンスの無さよ…
MySQL
MinishiftではMySQLは標準で使用できる状態で構築される。
標準で使用できるイメージについては、openshift
というネームスペースに配置されている
zaki@mascarpone% oc get is -n openshift mysql
NAME DOCKER REPO TAGS UPDATED
mysql 172.30.1.1:5000/openshift/mysql latest,5.5,5.6 + 1 more... 3 weeks ago
また、アプリケーションによってはパラメタなどの指定やPVの設定などが簡単にできるようにTemplateも含まれている。MinishiftのMySQLではmysql-persistent
というPV設定を含むTemplateが最初から準備されている。
zaki@mascarpone% oc get template -n openshift mysql-persistent
NAME DESCRIPTION PARAMETERS OBJECTS
mysql-persistent MySQL database service, with persistent storage. For more information about u... 9 (3 generated) 4
zaki@mascarpone%
パラメタ(と省略時のデフォルト値)については、describe
コマンドで確認できる。
zaki@mascarpone% oc describe template -n openshift mysql-persistent
...snip...
Parameters:
Name: MEMORY_LIMIT
Display Name: Memory Limit
Description: Maximum amount of memory the container can use.
Required: true
Value: 512Mi
Name: NAMESPACE
Display Name: Namespace
Description: The OpenShift Namespace where the ImageStream resides.
Required: false
Value: openshift
Name: DATABASE_SERVICE_NAME
Display Name: Database Service Name
Description: The name of the OpenShift Service exposed for the database.
Required: true
Value: mysql
Name: MYSQL_USER
Display Name: MySQL Connection Username
Description: Username for MySQL user that will be used for accessing the database.
Required: true
Generated: expression
From: user[A-Z0-9]{3}
Name: MYSQL_PASSWORD
Display Name: MySQL Connection Password
Description: Password for the MySQL connection user.
Required: true
Generated: expression
From: [a-zA-Z0-9]{16}
Name: MYSQL_ROOT_PASSWORD
Display Name: MySQL root user Password
Description: Password for the MySQL root user.
Required: true
Generated: expression
From: [a-zA-Z0-9]{16}
Name: MYSQL_DATABASE
Display Name: MySQL Database Name
Description: Name of the MySQL database accessed.
Required: true
Value: sampledb
Name: VOLUME_CAPACITY
Display Name: Volume Capacity
Description: Volume space available for data, e.g. 512Mi, 2Gi.
Required: true
Value: 1Gi
Name: MYSQL_VERSION
Display Name: Version of MySQL Image
Description: Version of MySQL image to be used (5.7, or latest).
Required: true
Value: 5.7
とにかく動かすのであれば、
項目 | 内容 |
---|---|
DATABASE_SERVICE_NAME | クラスタ環境上でのサービス名 |
MYSQL_USER | デプロイ時に作成するDBユーザ |
MYSQL_PASSWORD | 作成するユーザのパスワード |
MYSQL_DATABASE | デプロイ時に作成するDB |
を指定しておけば最低限動作する。
サービス名は、ほかのPodからの名前解決にも使用される。
デプロイ
ユーザ名・パスワード・DB名を、作成済みアプリケーションに合わせて、以下の設定でMySQLをデプロイする。
項目 | 値 |
---|---|
DATABASE_SERVICE_NAME | memoapp-db |
MYSQL_USER | memoapp |
MYSQL_PASSWORD | memoapp |
MYSQL_DATABASE | memoapp_db |
zaki@mascarpone% oc new-app mysql-persistent -p DATABASE_SERVICE_NAME=memoapp-db -p MYSQL_USER=memoapp -p MYSQL_PASSWORD=memoapp -p MYSQL_DATABASE=memoapp_db
--> Deploying template "openshift/mysql-persistent" to project samplepj
MySQL
---------
MySQL database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/5.7/root/usr/share/container-scripts/mysql/README.md.
NOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.
The following service(s) have been created in your project: memoapp-db.
Username: memoapp
Password: memoapp
Database Name: memoapp_db
Connection URL: mysql://memoapp-db:3306/
For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/5.7/root/usr/share/container-scripts/mysql/README.md.
* With parameters:
* Memory Limit=512Mi
* Namespace=openshift
* Database Service Name=memoapp-db
* MySQL Connection Username=memoapp
* MySQL Connection Password=memoapp
* MySQL root user Password=2f044g2TyeVyxt3o # generated
* MySQL Database Name=memoapp_db
* Volume Capacity=1Gi
* Version of MySQL Image=5.7
--> Creating resources ...
secret "memoapp-db" created
service "memoapp-db" created
persistentvolumeclaim "memoapp-db" created
deploymentconfig.apps.openshift.io "memoapp-db" created
--> Success
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose svc/memoapp-db'
Run 'oc status' to view your app.
これでDBは動作する。簡単ですね。
Connection URL: mysql://memoapp-db:3306/
と表示されている通り、 このネームスペースのほかのPodからであれば このURLでDBアクセスができる。
MySQLのPodが起動できたかどうかはoc get pod
でREADY
の状態が1/1
でSTATUS
がRunning
になっていればOK
zaki@mascarpone% oc get pod
NAME READY STATUS RESTARTS AGE
memoapp-db-1-2lsjj 1/1 Running 0 27s
まぁできれば、ほかにはoc logs
コマンドでPod内のログを確認もするとより良い。
webapp
minishiftってTomcatはデフォルトで入ってないのかーっていろいろチェックしたら、WildFlyというAPサーバが標準で使用できる状態になっていた。
わいるどふらい(WildFly)って何?から学ぶEJB - ponsuke_tarou’s blog
WildFlyという存在を知らなかった…(JavaEE方面は疎いので)
というわけで、このAPサーバを使用してみます。
(Tomcatを使えるようにしたり、DockerHubでなくRed HatのコンテナレジストリからAPサーバのイメージを取得する、という方法もある。が、今回はWildFlyを使用)
Red Hat Container Catalog (RHCC) を使ってみよう! | RED HAT OPENEYE -レッドハットの情報ポータル
Tomcat→WildFly対応
APサーバをTomcatからWildFly v13.0に変更するにあたり、そのままだと動かないので修正を行う。
※ これはコンテナ化による修正・変更でなく、APサーバの変更に伴う修正。
14:12:17,627 ERROR [stderr] (default task-2) javax.naming.NameNotFoundException: comp/env -- service jboss.naming.context.java.comp.env
14:12:17,632 ERROR [stderr] (default task-2) at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:106)
14:12:17,632 ERROR [stderr] (default task-2) at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:207)
14:12:17,632 ERROR [stderr] (default task-2) at org.jboss.as.naming.InitialContext$DefaultInitialContext.lookup(InitialContext.java:237)
変更点はJNDIデータソース周りの設定
-
(XML)JDBCによるDBサーバの定義の仕方の変更
-
Tomcatでは
META-INF/context.xml
に記述していたデータソースの定義は、WildFlyだとMETA-INF
かWEB-INF
に*-ds.xml
というファイル名で作成する- Developer Guide / 3. Deployment Descriptors used In WildFly
- ただし、実行すると
WFLYJCA0091: -ds.xml file deployments are deprecated. Support may be removed in a future version.
とログが出力されるので、そのうち変更される模様…
-
設定例についてはいい感じのサンプルが見つからなかったので寄せ集めで書いてみた
-
memoapp2-ds.xml
<?xml version="1.0" encoding="UTF-8"?> <datasources> <datasource jndi-name="jdbc/memoapp_db" pool-name="memoapp_db" > <connection-url>jdbc:mysql://memoapp-db:3306/memoapp_db?useUnicode=yes&characterEncoding=utf8</connection-url> <driver>mysql</driver> <security> <user-name>memoapp</user-name> <password>memoapp</password> </security> </datasource> <drivers> <driver name="mysql" module="com.mysql" > <driver-class>com.mysql.cj.jdbc.Driver</driver-class> </driver> </drivers> </datasources>
-
-
(Javaコード)名前空間の参照方法の変更
-
Tomcatだと例え<GlobalNamingResources>でグローバルな設定を行っても、グローバルな名前空間でJNDIアクセスするわけではなかった。
-
Apache Tomcat 8 (8.5.37) - JNDI Resources HOW-TO / Using resources
All configured entries and resources are placed in the java:comp/env portion of the JNDI namespace
Tomcatでは全てのリソースは
java:com/env
でアクセスするようになっているが、ほかのAPサーバはそでもない。 -
src/main/java/jp/example/www/dao/MemoappDaoImpl.java
--- a/src/main/java/jp/example/www/dao/MemoappDaoImpl.java +++ b/src/main/java/jp/example/www/dao/MemoappDaoImpl.java @@ -27,6 +27,5 @@ public class MemoappDaoImpl implements MemoappDao { //con = DriverManager.getConnection(url, user, pass); Context initContext = new InitialContext(); - Context envContext = (Context)initContext.lookup("java:/comp/env"); - DataSource ds = (DataSource)envContext.lookup("jdbc/memoapp_db"); + DataSource ds = (DataSource)initContext.lookup("jdbc/memoapp_db"); con = ds.getConnection(); System.out.println("con: " + con); @@ -71,6 +70,5 @@ public class MemoappDaoImpl implements MemoappDao { //con = DriverManager.getConnection(url, user, pass); Context initContext = new InitialContext(); - Context envContext = (Context)initContext.lookup("java:/comp/env"); - DataSource ds = (DataSource)envContext.lookup("jdbc/memoapp_db"); + DataSource ds = (DataSource)initContext.lookup("jdbc/memoapp_db"); con = ds.getConnection(); smt = con.createStatement();
-
DBアクセスの日本語設定
前述のJNDI周りの変更を行うことで、APからDBへアクセス可能になるが、デフォルトのMySQL設定だとエンコード設定が日本語対応していないため、Java側から日本語アクセス可能な設定を行う。
※ これもコンテナ化による修正・変更でなく、DBサーバのエンコード設定をサーバ側で設定するのではなくクライアント側で対処する、という内容。
- blog.k11i.biz: UTF-8 エンコードされた絵文字を MySQL / JDBC で取り扱うには?
- 自分用備忘録: OpenShiftのMySQLにUTF-8設定を追加する
- https://bugzilla.redhat.com/show_bug.cgi?id=1010582
<connection-url>
に指定するJDBCのURIに、エンコーディング設定を付加する。
こんな感じ
<connection-url>jdbc:mysql://memoapp-db:3306/memoapp_db?useUnicode=yes&characterEncoding=utf8</connection-url>
また、テーブル作成時にエンコード情報を付与してcreate table
する。
--- a/src/main/java/jp/example/www/dao/MemoappDaoImpl.java
+++ b/src/main/java/jp/example/www/dao/MemoappDaoImpl.java
@@ -99,5 +99,6 @@ public class MemoappDaoImpl implements MemoappDao {
"create_date DATETIME comment '作成日'," +
"modified_date DATETIME comment '更新日'," +
- "primary key (memo_id)" + ")";
+ "primary key (memo_id)" + ") " +
+ "engine=InnoDB default charset=utf8";
// create table
smt.executeUpdate(create_table);
今回はMySQLは標準のものをそのまま使用しDBサーバ側の設定で日本語対応していないので、アプリケーション側で可能な日本語設定を行っている。
DBサーバ側で対応するには、日本語設定を含んだDBのイメージを作成すればよいはず。(未確認)
対応の内容は基本的にVMの場合と同じはず。(未確認)
また、DockerHubにあるMySQL公式イメージであれば、--character-set-server
という起動パラメタがあるみたいなので、これでエンコード設定ができると思われ。(Minishiftで用意してあるイメージはこのパラメタがなさそう)
S2Iを使ったビルドとデプロイ
コンテナ環境でアプリケーションをデプロイするには通常は
- アプリケーションのビルド(コンパイルが必要な言語であれば)
- コンテナイメージのビルド
- VM環境であればこの手順がない
- が、この手順があることで「構築済みの初期状態」のイメージを作ることでいつでも初期状態の環境を作り直すことができる
- コンテナのデプロイ
- 前段で作ったコンテナイメージを「実行」するだけ
- VM環境であればwarを配置したりの手順に相当かな
- コンテナ環境でも「(Tomcatとかの)ミドルウェアのコンテナをデプロイ」して、その中にあとからアプリを手動デプロイとかできなくもないけど、オーケストレーション環境でそれをするメリットはあまりないかも…(Podのスケールができないとか)
という手順になるけど、OpenShiftの機能であるS2I(source-to-image)を使うことで、コマンド一発で全部完了することができる。(ついでに言うと、GitのリポジトリURLを指定することで、ソースコードのcloneから全自動)
- OpenShiftのsource-to-image (s2i)をすごく簡単に説明するよ - nekop's blog
- OpenShift v3 と source-to-image (s2i) - Qiita
JavaEEであれば、pom.xml
が含まれていれば認識する模様 (つまりMavenプロジェクトとして作っていればOK)
Creating New Applications - Application Life Cycle Management | Developer Guide | OKD Latest
前置きが長くなったけど、以下のコマンドを実行
zaki@mascarpone% oc new-app openshift/wildfly~https://github.com/zaki-lknr/javaee-memoapp2.git
--> Found image 4dea579 (3 weeks old) in image stream "openshift/wildfly" under tag "13.0" for "openshift/wildfly"
WildFly 13.0.0.Final
--------------------
Platform for building and running JEE applications on WildFly 13.0.0.Final
Tags: builder, wildfly, wildfly13
* A source build using source code from https://github.com/zaki-lknr/javaee-memoapp2.git will be created
* The resulting image will be pushed to image stream tag "javaee-memoapp2:latest"
* Use 'start-build' to trigger a new build
* This image will be deployed in deployment config "javaee-memoapp2"
* Port 8080/tcp will be load balanced by service "javaee-memoapp2"
* Other containers can access this service through the hostname "javaee-memoapp2"
--> Creating resources ...
imagestream.image.openshift.io "javaee-memoapp2" created
buildconfig.build.openshift.io "javaee-memoapp2" created
deploymentconfig.apps.openshift.io "javaee-memoapp2" created
service "javaee-memoapp2" created
--> Success
Build scheduled, use 'oc logs -f bc/javaee-memoapp2' to track its progress.
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose svc/javaee-memoapp2'
Run 'oc status' to view your app.
oc new-app
のコマンド自体はすぐに完了し、裏でbuilder podというリポジトリからソースをcloneしてソースのビルドからイメージのビルドまでを実行するPodが起動する
zaki@mascarpone% oc get all [~]
NAME READY STATUS RESTARTS AGE
pod/javaee-memoapp2-1-build 1/1 Running 0 17s
pod/memoapp-db-1-2lsjj 1/1 Running 0 10m
NAME DESIRED CURRENT READY AGE
replicationcontroller/memoapp-db-1 1 1 1 10m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/javaee-memoapp2 ClusterIP 172.30.210.59 <none> 8080/TCP 18s
service/memoapp-db ClusterIP 172.30.11.241 <none> 3306/TCP 10m
NAME REVISION DESIRED CURRENT TRIGGERED BY
deploymentconfig.apps.openshift.io/javaee-memoapp2 0 1 0 config,image(javaee-memoapp2:latest)
deploymentconfig.apps.openshift.io/memoapp-db 1 1 1 config,image(mysql:5.7)
NAME TYPE FROM LATEST
buildconfig.build.openshift.io/javaee-memoapp2 Source Git 1
NAME TYPE FROM STATUS STARTED DURATION
build.build.openshift.io/javaee-memoapp2-1 Source Git@188442f Running 17 seconds ago
NAME DOCKER REPO TAGS UPDATED
imagestream.image.openshift.io/javaee-memoapp2 172.30.1.1:5000/samplepj/javaee-memoapp2
zaki@mascarpone% oc get pod [~]
ocNAME READY STATUS RESTARTS AGE
javaee-memoapp2-1-build 1/1 Running 0 25s
memoapp-db-1-2lsjj 1/1 Running 0 10m
この場合はoc logs -f javaee-memoapp2-1-build
でビルドログ(Mavenの実行状態)を確認できる。
zaki@mascarpone% oc logs -f javaee-memoapp2-1-build
...
Pushing image 172.30.1.1:5000/samplepj/javaee-memoapp2:latest ...
Pushed 0/13 layers, 0% complete
Pushed 1/13 layers, 10% complete
Pushed 2/13 layers, 18% complete
Pushed 3/13 layers, 25% complete
Pushed 4/13 layers, 32% complete
Pushed 5/13 layers, 40% complete
Pushed 6/13 layers, 47% complete
Pushed 7/13 layers, 55% complete
Pushed 8/13 layers, 63% complete
Pushed 9/13 layers, 75% complete
Pushed 9/13 layers, 82% complete
Pushed 10/13 layers, 89% complete
Pushed 10/13 layers, 94% complete
Pushed 11/13 layers, 97% complete
Pushed 12/13 layers, 97% complete
Pushed 13/13 layers, 100% complete
Push successful
zaki@mascarpone%
ビルドが完了されると、内部で動作しているイメージレジストリへpushされ実行可能状態になり、
そのままPodが起動される。
zaki@mascarpone% oc get pod
NAME READY STATUS RESTARTS AGE
javaee-memoapp2-1-build 0/1 Completed 0 4m
javaee-memoapp2-1-rgjqq 1/1 Running 0 11s
memoapp-db-1-2lsjj 1/1 Running 0 15m
zaki@mascarpone
APサーバ関連のPodは、oc logs
で確認してエラーがなくlistening
とかstarted
とか起動してる感じのログが出ていればOK
07:21:38,706 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://0.0.0.0:9990/management
07:21:38,707 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://0.0.0.0:9990
07:21:38,716 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 13.0.0.Final (WildFly Core 5.0.0.Final) started in 46464ms - Started 698 of 890 services (315 services are lazy, passive or on-demand)
これでDBとwebアプリケーションが動作している状態
zaki@mascarpone% oc get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
javaee-memoapp2 ClusterIP 172.30.210.59 <none> 8080/TCP 9m
memoapp-db ClusterIP 172.30.11.241 <none> 3306/TCP 19m
routerの作成
アプリをデプロイしただけだと、動作しているServiceに対してクラスターの外部からアクセスすることができないため、外部からのアクセス用のRouter Podを作成する。
zaki@mascarpone% oc expose service javaee-memoapp2
route.route.openshift.io/javaee-memoapp2 exposed
zaki@mascarpone%
zaki@mascarpone% oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
javaee-memoapp2 javaee-memoapp2-samplepj.192.168.99.100.nip.io javaee-memoapp2 8080-tcp None
これで、http://javaee-memoapp2-samplepj.192.168.99.100.nip.io
に対してアクセスすると、javaee-memoapp2のPodの8080/TCPへルーティングしてくれる、という状態になる。
ちなみに、oc expose
で作成されるRouter Podのドメイン名は、デフォルトでは<サービス名>-<ネームスペース名>.サブドメイン
となる。なのでワイルドカードDNSが使用できる環境が基本的には必要。
(--hostname
オプションで任意のホスト名/FQDNを指定することも可能)
ブラウザでwebアクセス
Minishift環境だとrouteで作成したドメイン名はpublicなワイルドカードDNSサービスを使って作られるので特に意識せずに利用できる。
ワイルドカードDNSは nip.io が良い - 約束の地
routeのドメインであるjavaee-memoapp2-samplepj.192.168.99.100.nip.io
にブラウザでアクセスすると…
そういえばアプリの構成的にコンテキストルートでの動作ではなかった。
/memoapp2
のパスを付け加えてアクセス。。
うごいたああ!!
日本語入力も大丈夫でした。