OKEクラスターでコンテナを動かしてみる

このエントリーはOracle Cloudアドベントカレンダー その2の2日目です。

2日目も引き続き、Oracle Contianer Engine for Kubernetes(以下「OKE」。Oracleが提供するKubernetesのマネージドサービス)の話題です。このエントリーでは、1日目に作成したOKEでクラスターにkubectlでアクセスし、サンプルのコンテナをデプロイしてみます。

0 . 前提条件

  • 1日目の手順を終えるなどして、OKEクラスターが利用できること
  • Unix/Linux互換のターミナル環境が利用できること

手順の流れ

大雑把に言って、以下のような手順になります。

  1. kubectl等のコマンドラインツールをインストールする
  2. OKEにアクセスするためのkubectlの設定ファイルを取得する
  3. コンテナをデプロイする

それではやってみましょう。

1 . kubectl等のコマンドラインツールをインストールする

OKEクラスターの利用のためには、以下のコマンドラインツールが必要です。

  • Python 2.7.5+ or 3.5+
  • OCI CLIOracle Cloud Infrastructure、以下OCIのCLI
  • kubectl

kubectlでOKEクラスターにアクセスするためには、接続情報などが書かれたkubectlの設定ファイルを入手する必要があります。これを行うために、OCI CLIが必要です。

また、OCI CLIの利用のためにPythonを必要とします。

1.1. Pythonのインストール

現在のPythonのバージョンを確認するには、ターミナルで以下のコマンドを実行します。

Python 2.x 系の場合

python --version

Python 3.x 系の場合

python3 --version

もし上述の条件を満たしていない場合は、ここでPythonのインストール/アップデートを行ってください。

1.2. OCI CLIをインストールをする

OCI CLIのインストールを行っていきます。

1.2.1. OCIDの確認

OCIネイティブのアカウントでコンソールにログインし、画面右上の人型のアイコンをクリックします。さらに、展開されたメニューの"User Settings"をクリックします。

ユーザーの詳細情報の画面で”User Information"タブ内にユーザーのOCIDが表示されている箇所があります。OCIDの値の右にある"Copy"をクリックすると、クリップボードにOCIDがコピーされるので、これを手元のテキストエディタなどにペーストしておきます。

次に、同じく画面右上の人型のアイコンをクリックし、展開されたメニューにあるTenant名をクリックします。

Tenantの詳細情報の画面で”Tenancy Information"タブ内にOCIDが表示されている箇所があります。OCIDの値の右にある"Copy"をクリックすると、クリップボードにOCIDがコピーされるので、これを手元のテキストエディタなどにペーストしておきます。

1.2.2. OCI CLIのインストール

OCI CLIをインストールするには、以下のコマンドを実行します。

bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"

CLIのバイナリの配置先などを設定するインタラクションがあります。自身の環境に合わせて、好みの設定を行ってください(全てデフォルトのままで進めても問題ありません)。

インストールが完了したら、OCI CLIの初期セットアップを行います。以下のコマンドを実行してください。

oci setup config

ここでも設定をおこなうためのインタラクションがあります。ここでは、下表のように入力してください。

質問 応答操作
Enter a location for your config [/home/<ユーザー名>/.oci/config] なにも入力せず[Return]
Enter a user OCID 先の手順で確認したユーザーのOCIDを入力
Enter a tenancy OCID 先の手順で確認したテナントのOCIDを入力
Enter a region (e.g. eu-frankfurt-1, us-ashburn-1, us-phoenix-1) アクセスしたいリージョンを例に従って入力
Do you want to generate a new RSA key pair? (If you decline you will be asked to supply the path to an existing key.) [Y/n] Y + [Return]
Enter a directory for your keys to be created [/home/<ユーザー名>/.oci] なにも入力せず[Return]
Enter a name for your key [oci_api_key] なにも入力せず[Return]
Enter a passphrase for your private key (empty for no passphrase) なにも入力せず[Return]

CLIからOCIに対してアクセスを行う際には、OCIのAPIの認証が行われます。このため予め認証をパスするのに必要なAPIキーを、ユーザー毎にOCIにアップロードしておく必要があります。

OCI CLIの初期セットアップの際に作成した鍵ペアのうち、公開鍵の方を、管理コンソールからアップロードします。

まず、以下のターミナルで以下のコマンドを実行し、公開鍵を表示しておきます。

cat ~/.oci/oci_api_key_public.pem

続いてOCIのコンソールに移り、画面右上の現在ログイン中のユーザー名が表示されている箇所をクリックします。さらに、展開されたメニューの"User Settings"をクリックします。

ユーザーの詳細画面の左側のメニューで、"API Keys"をクリックし、さらに"Add Public Key"ボタンをクリックします。

”Add Public Key"ダイアログの入力欄に、先ほとターミナルに表示した公開鍵をペーストし、"Add"ボタンをクリックします("-----BEGIN PUBLIC KEY-----"と"-----END PUBLIC KEY-----"の行も含めてペーストします)。

以上でOCI CLIのインストールは完了です。

1.3. kubectlのインストール

Kubernetesの公式ドキュメントの手順に従って、kubectlをインストールしておきます。

2 . OKEにアクセスするためのkubectlの設定ファイルを取得する

最後にkubectlの設定ファイルを取得し、kubectlで実際にクラスターにアクセスしてみます。 OCIのコンソールで、画面右上のタブから"Developer Services"をクリックします。

あらかじめ作成しておいたOKEクラスターの名前をクリックします。

クラスターの詳細画面で、"Access Kubeconfig"をクリックします。

”How to Access Kubeconfig"ダイアログに、kubetctlの設定ファイルを取得するためのコマンドが表示されますので、これらを順次実行していきます。

1つ目のコマンドは、設定ファイルを格納するためのディレクトリの作成です。

mkdir -p $HOME/.kube

2つ目のコマンドは、OCI CLIの設定ファイル取得用のコマンドを実行しています(実際のコマンドはダイアログからコピーしてください)。

oci ce cluster create-kubeconfig --cluster-id ocid1.cluster.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --file $HOME/.kube/config

最後に、以下のコマンドを実行して、kubectlの動作確認をしてみます。

kubectl get nodes

以下のような実行結果になれば、正常にクラスターにアクセスできています。

NAME              STATUS    ROLES     AGE       VERSION
129.213.100.157   Ready     node      3d        v1.11.1
129.213.118.118   Ready     node      3d        v1.11.1
129.213.95.69     Ready     node      3d        v1.11.1

3 . コンテナをデプロイする

それではいよいよ、OKEのクラスターにコンテナをデプロイしていきます。

今回は、公式のドキュメントにあるサンプルを利用して、Nginxを動かしてみます。以下のコマンドを実行してください。

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

コマンドで指定されているymalファイルには、クラスターでコンテナを動かすための構成情報が記述されています(URLにアクセスすると、内容を確認できます)。上記のコマンドでは、このファイルの構成に従って、OKEクラスター上にコンテナをデプロイするよう指示しています。

以下のコマンドを実行すると、コンテナ(KubernetesではPodという単位に内包されています)の状態を確認できます。

kubectl get pods -l app=nginx
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
nginx-deployment-1771418926-r18az   1/1       Running   0          16h

2つのPodに冗長化してデプロイされてるようでうす。どちらも"Running"のステータスになっていればOKです。

このままでは、クラスターの外からコンテナにトラフィックを届けることができないので、以下のコマンドでクラスター外に公開します。

kubectl expose deployment nginx-deployment --type="LoadBalancer" --port 80

これによって、上の手順で作成したPodのセット(nginx-deployment)に対して、ロードバランサーを通してトラフィックを届けることができます。ここでのロードバランサーは、このコマンドが実行されたときにOCIのロードバランサーを自動プロビジョニングして利用しています。

数十秒程度時間をおいてから以下のコマンドを実行すると、コンテナが公開されているIPアドレスロードバランサー)のIPアドレスを確認できます。

kubectl get services
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE
kubernetes         ClusterIP      10.96.0.1       <none>            443/TCP        4d
nginx-deployment   LoadBalancer   10.96.219.208   129.213.187.123   80:32053/TCP   33s

nginx-deploymentというエントリーのEXTERNAL-IPに表示されているものがそれです。

ブラウザでこのIPアドレスにアクセスすると、おなじみにNginxの画面が表示されます。

http://129.213.187.123/

f:id:charlier_shoe:20181203024728p:plain

コンテナのデプロイは以上で完了です!

お掃除

デプロイしたものを削除するには、以下のコマンドを実行してください。

kubectl delete deployment nginx-deployment

kubectl delete service nginx-deployment

OKEでKubernetesクラスターをプロビジョニングする

このエントリーはOracle Cloudアドベントカレンダー その2の1日目です。

Oracle Contianer Engine for Kubernetes(以下、OKE)は、Oracleが提供するKubernetesのマネージドサービスです。このエントリーでは、OKEでクラスターを作る手順をご紹介したいと思います。

0 . 前提条件

  • Oracle Cloud Infrastructure(Oracleの第2世代IaaS。以下OCI)が利用可能であること
  • OCIネイティブのアカウントが作成済みで、必要な権限を割り当て済みであること。試すけだならAdministratorグループに入れておけばOK1

登録したばかりのOracle Cloud環境では、これらの前提を満たすような準備が必要です。ゼロからOKEを使い始めるまでのStep by Stepのガイドは、paasdocsのチュートリアルにまとめてありますので、そちらを参照ください。

1 . 手順

OCIのコンソールに、OCIネイティブのアカウントでログインします。ネイティブアカウントの場合はログイン画面の右側のフォームです。

OCIコンソールへのログイン

コンソール左上のメニューを展開し、"Developer Services"をクリックします。

ここです。

画面左のメニューで"Clusters"をクリックし、List Scpesのメニューでクラスターを作成したいCompartmentを選択します。続けて、"Create Cluster"ボタンをクリックします。

Create Clusterでクラスターの作成を開始

"Create Cluster"ダイアログで、全てデフォルトの設定のまま"Create"ボタンをクリックします。

クラスター作成ダイアログ

デフォルトではクラスターが必要とするネットワークリソース等も自動的に構成されます。ダイアログの前半にある"CUSTOM CREATE"ラジオボタンを選択すると、クラスターの下回りのネットワークを細かく設定することもできます。

途中経過がダイアログに表示されますので、"Cluster and associated network resources created."というメッセージが表示されるまで待った後、"Close"ボタンをクリックします。

概ね数分程度で作成完了します。クラスターの一覧画面に、cluster1が表示されます。StatusがActiveになったらOKです。

クラスターのステータス

以上です。簡単!


明日も引き続きOracle Cloudアドベントカレンダーを担当します。明日はkubectlでクラスターにつないで、何かコンテナを動かして見たいと思います。


  1. AWSのIAMアカウントみたいなもの、と説明することにしていますが、細かい話をすると結構違います。まあ、そういうのが必要なんです。

gRPCを使ってみた

gRPC for Javaをいじってみたので、使い方のまとめ

gRPCとは

Googleが開発した、RPC(Remote Procedure Call)を実装するためのフレームワークです。

Protocol Bufferを利用した高速通信、.protoによるインターフェース定義、JavaやNode.jsなどを含む、多数のプログラミング言語に対応していることなどが特徴。

仕組み

大雑把な流れは、以下の通りです。

  1. .proto形式でインターフェース定義を記述
  2. .protoから、利用したいプログラミング言語用の、サーバーのスケルトン実装とクライアントスタブを生成
    1. で生成されたものをベースにサーバー、クライアントを実装

クライアントでスタブのメソッドを呼び出すと、通信はフレームワークがよしなにやってくれます。

f:id:charlier_shoe:20160908135226p:plain

公式ドキュメント「What is gRPC?」http://www.grpc.io/docs/guides/ より抜粋

Javaで実際にやってみた

Javaで実際にやってみました。 (コードはこちら

.protoの作成

以下の様な感じで、.protoを作成します。

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.oracle.jdt2016.hackathon.hr";
option java_outer_classname = "HrProto";
option objc_class_prefix = "HR";

import "google/protobuf/empty.proto";

package hr;

service Hr {
    rpc Employees(google.protobuf.Empty) returns (EmployeesReply) {}
}

message EmployeesReply {
    repeated Employee employee = 1;
}

message Employee {
    float commissionPct = 1;
    int64 departmentId = 2;
    string email = 3;
    int64 employeeId = 4;
    string firstName = 5;
    int64 hireDate = 6;
    string jobId = 7;
    string lastName = 8;
    int64 managerId = 9;
    string phoneNumber = 10;
    float salary = 11;
}

今回は、サーバーにあるダミーの従業員データを取得するインターフェースを作りました。引数は無し、返り値は従業員データの配列です。

"service"で始まる部分がメソッドの定義です。引数なしを定義するときは、"google/protobuf/empty.proto"をインポートして”google.protobuf.Empty”を引数に指定します。
返り値の型は”message”で始まる部分で定義しています。要素に配列を含みたい場合、”repeated"と記述すると、後続する型の配列だという宣言になります。

今回は、クライアント、サーバーとも同期的に処理を行なうインターフェースをとして定義していますが、非同期のインターフェースも使えます。
詳細は公式のドキュメントを参照して下さい。

サーバースケルトン、クライアントスタブの生成

今回はGradleを使います(Mavenでもいけるようです)。

まず、IDEを使うなりして適当なGradleプロジェクトを作成しておきます。

次に、src/main/protoフォルダを作成し、.protoファイルを配置します。

続いて、build.gradleを編集します。 今回使ったbuild.gradle(関連する部分を抜粋)は、以下の通りです。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'com.google.protobuf'def grpcVersion = '1.0.0'
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    …
    compile "io.grpc:grpc-netty:${grpcVersion}"
    compile "io.grpc:grpc-protobuf:${grpcVersion}"
    compile "io.grpc:grpc-stub:${grpcVersion}"
}

protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.0.0'
    }
    plugins {
        grpc {
            artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
        }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {
                // To generate deprecated interfaces and static bindService method,
                // turn the enable_deprecated option to true below:
                option 'enable_deprecated=false'
            }
        }
    }
}

以下のコマンドを実行すると、build/generated配下に目的のコードが生成されます。

 > gradle generateProto

生成されるコードのパッケージ名には、.protoの"option java_package"で宣言したものになります。

サーバーの実装

サーバーの実装は以下の通りです(ほとんど公式のHelloWorldサンプルから持ってきています)。

//importは省略

public class HrServer {
    /* The port on which the server should run */
    private int port = 50051;
    private Server server;

    private void start() throws IOException {
        server = ServerBuilder.forPort(port).addService(new HrImpl())
          .build().start();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                HrServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
          server.shutdown();
        }
    }

    /**
     * Await termination on the main thread since the grpc library uses daemon threads.
     */
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    /**
     * Main launches the server from the command line.
     */
    public static void main(String[] args)
            throws IOException, InterruptedException {
        final HrServer server = new HrServer();
        server.start();
        server.blockUntilShutdown();
    }

    private class HrImpl extends HrGrpc.HrImplBase {
        @Override
        public void employees(
                Empty request, StreamObserver<EmployeesReply> responseObserver) {
            // DBから従業員データを取得する
            EntityManager em = EntityManagerUtils.getEntityManager();
            @SuppressWarnings("unchecked")
            List<Employee> entities =
                    em.createNamedQuery("Employee.findAll").getResultList();
            em.close();
            // 返り値を作成
            EmployeesReply.Builder replyBuilder = EmployeesReply.newBuilder();
            for (Employee entity : entities) {
                com.oracle.jdt2016.hackathon.hr.Employee employee =
                        com.oracle.jdt2016.hackathon.hr.Employee.newBuilder()
                        .setCommissionPct(entity.getCommissionPct())
                        .setDepartmentId(entity.getDepartmentId())
                        .setEmail(entity.getEmail())
                        .setEmployeeId(entity.getEmployeeId())
                        .setFirstName(entity.getFirstName())
                        .setHireDate(entity.getHireDate().getTime())
                        .setJobId(entity.getJobId())
                        .setLastName(entity.getLastName())
                        .setManagerId(entity.getManagerId())
                        .setPhoneNumber(entity.getPhoneNumber())
                        .setSalary(entity.getSalary())
                        .build();
                replyBuilder.addEmployee(employee);
            }
            responseObserver.onNext(replyBuilder.build());
            responseObserver.onCompleted();
        }
    }
}

サーバーの起動はServerBuilderからServerを作成→startという流れ。

ロジックは、"HrGrpc.HrImplBase"をextendsして、Employeesメソッドをオーバーライドして記述します。

クライアントに返却するデータも.protoファイルを元に生成されたクラスを使います。今回はEmployeesReply、Employeeというクラスが作られています。 オーバーライドしたメソッドの中でこれらのオブジェクトを作って、responseObsererに渡して上げればよいようです。

クライアントの実装

クライアントの実装は以下の通りです(こちらも公式のHelloWorldサンプルをベースにしています)。

//importは省略

public class HrClient {
    private final ManagedChannel channel;
    private final HrGrpc.HrBlockingStub blockingStub;

    public HrClient(String host, int port) {
        channel = ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext(true).build();
        blockingStub = HrGrpc.newBlockingStub(channel);
    }

    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    public void getEmployees() {
        EmployeesReply response;
        long begin = System.currentTimeMillis();
        try {
            response = blockingStub.employees(Empty.newBuilder().build());
        } catch (StatusRuntimeException e) {
            System.out.println("RPC failed");
            return;
        }
        System.out.println("Employees: " + response.getEmployeeCount());
    }

    public static void main(String[] args) throws Exception {
        HrClient client = new HrClient("localhost", 50051);
        try {
            client.getEmployees();
        } finally {
            client.shutdown();
        }
    }
}

スタブオブジェクトから、ローカルのメソッドを呼び出す感覚でemployeeを実行できます。

gRPCは、Protocol Bufferを使って高速な通信を実現しているわけですが、アプリケーションを実装する分には、プロトコル依存の部分を意識する必要はほとんどありません。

Spring Boot で組み込みDerbyデータベースを使う

【2016/06/14 追記】
JPAの部分は、Spring Data JPAの機能を利用するとpersistence.xmlを書かなくても動かせるはずです。コードを直したら、本ブログも修正する予定です。 コメント頂いた@makingさん。ありがとうございます。


先日のJava Day Tokyo 2016では、ショートハッカソンのセッションがあったのですが、そちらの準備と一部講師を担当させていただいてました。
3時間という、一般的なハッカソンに比べるとかなり短い枠でしたので、さすがに一からモノを作ってもらうわけには行かず、予めサンプル・アプリケーションを作っておいてそれをカスタマイズしてもらうという形態をとりました。

サンプルアプリを作っていく中で、Spring bootのアプリ内で組み込みDerby(読み取り専用)を動かすという技を習得したので、やり方を書きたいと思います。

ポイント

今回のアプリでは、DBのバイナリ/データを含む全ての依存成果物を、1つのjarに収める必要がありました。
Application Container Cloud(ACC)というOracleのコンテナ型のPaaSを利用するハッカソンでしたので、マイクロサービスらしくアプリをひとつにまとめたかったためです(本当は外部にDBを設けることもできます)。

これを実現するため、以下の2点を抑える必要がありました。

  1. Derbyのバイナリ/データファイルをjarに組み込む
  2. サンプルアプリから同じJVM上のDerbyに接続する

これらを踏まえて、以下、手順にいきます。

手順

1. データファイルの作成

はじめに、通常の手順でDerbyのデータベースを作成し、データを投入します。

以下の例では、ijを使ってC:\temp\dbフォルダ配下にhrというデータベースを作成し、同時に接続しています。ijは、DerbyのCLIクライアント(Oracle Databaseのsqlplusのようなもの)です。

> cd ${JAVA_HOME}\db\bin
ijバージョン10.11
> ij.bat
ij> connect 'jdbc:derby:C:\temp\db\hr;create=true';

続いてデータを投入していきます。例えば、create_tables.sql、populate_tables.sqlというスクリプトを使う場合、以下のコマンドを実行します。

ij> run 'C:\temp\db\scripts\create_tables.sql';
…
(実行結果の出力)
…
ij> run 'C:\temp\db\scripts\populate_tables.sql';
…
(実行結果の出力)
…

データの投入が終わったら接続を解除します。

ij> exit;

データファイルは接続時に指定したパスに作成されています。ディレクトリ構成は以下のようになっています。

C:\temp\db
└── hr
    ├── log
    │   ├── log.ctrl
    │   ├── log1.dat
    │   ├── logmirror.ctrl
    │   └── README_DO_NOT_TOUCH_FILES.txt
    ├── README_DO_NOT_TOUCH_FILES.txt
    ├── seg0
    │   ├── c10.dat
    │   ├── cf0.dat
    │   ├── …(.datがたくさん)
    │   └── README_DO_NOT_TOUCH_FILES.txt
    └── service.properties

次に、上記のファイル群の中にあるservice.propertiesを編集します。これはDerbyデータベースの設定を記述するファイルです。

derby.storage.tempDirectoryという設定を追記して、一時ファイルの出力先を変更します。
デフォルトでは、データファイル群のトップのフォルダ(上記のhrフォルダ)に一時ファイルが出力されますが、今回はそのフォルダはjarの中に格納されてしまいます。そのため、書き込み可能な領域(jarファイルの外)を一時ファイルの出力先にしておきます。

以下の例は、プログラムの実行カレント以下、temp/db/hrフォルダに設定しています。

SysschemasIndex2Identifier=225
SyscolumnsIdentifier=144
…
derby.storage.tempDirectory=temp/db/hr (←追記)

最後に、データファイル群をMavenプロジェクトの配下にコピーします。 今回の場合、src/main/resources/の下にdataという階層を切ってファイルを配置しました。こうしておくと、ビルドしたときに、jarファイル内のトップにdata/hrというフォルダ構成が作られてデータファイルが格納されます。

Mavenプロジェクト内のデータファイルの階層

[プロジェクトトップ]
├─src
│  ├─main
│  │  ├─java
│  │  │  └─…
│  │  └─resources
│  │      ├─data
│  │      │  └─hr
│  │      │      ├─log
│  │      │      └─seg0
│  ├─…  ├─…

jarファイル内のデータファイルの階層

xxx.jar
├─data
│  └─hr
│      ├─log
│      └─seg0
├─lib
├─…

2. JPAの設定

データベースにアクセスするには、クラスパスを使ったDBアクセス*1を利用します。
これは、データファイルをパッケージしたjarをクラスパスに入れておいて、クラスパス中のDBにアクセスする方法です。ただし、今回はアプリケーション本体のjarにデータファイルが入っていますので、新たにクラスパスを追加する必要はありません*2

注意すべきは、JDBC URLが、この方法特有の書き方になることと、JDBCドライバーにEmbeddedDriverを指定することです。

今回の場合、ビルド成果物のjar直下にdata/hrというパスでデータファイルが格納されています。それに合わせて、persistence.xmlに以下のように記述します。

    <persistence-unit name="HR" transaction-type="RESOURCE_LOCAL"><properties>
            <property
                name="javax.persistence.jdbc.url"
                value="jdbc:derby:classpath:data/hr"/>
            <property
                name="javax.persistence.jdbc.driver"
                value="org.apache.derby.jdbc.EmbeddedDriver"/>
        </properties></persistence-unit>

ユーザー名/パスワードは、データベースを作成した時に設定していれば記述する必要があると思われますが、とくに何もしていなければ不要です。

3. ビルド

pom.xmlには、依存モジュールにderbyのjarを追加しておきます。

    <dependencies>
        …
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>

あとはビルドして、アプリを実行します。

> cd [pom.xmlのあるフォルダ]
> maven package
> java -jar target/[jarファイル名]

まとめ

実際にハッカソンで使ったアプリケーションは、GitHubにコードがあります。今回の手法の実装例として、ご参考にどうぞ。

Derbyのマニュアルのトップはこちら

*1:Accessing databases within a jar file using the classpath

*2:この機能は、データファイルだけのjarを作っておいて、それをクラスパスに入れるという使い方が想定されているようです。しかし、データファイルのjarを作っておいてSpring Bootアプリのjarに入れる構造にすると、クラスパスからデータファイルのjarが見えなくなってしまい、DBにアクセスできません。

ローカル環境のWebLogic Serverに、warを自動デプロイする

WebLogic Maven プラグインには、ビルドした成果物をテスト環境に自動デプロイする機能があります。 今回は、この機能を使って、ローカルPCで稼働するDockerコンテナ上のWebLogic Serverに、Mavenから自動デプロイしてみます。

この機能の本来の用途は、インテグレーションテスト等のテストフェーズを自動化することだと思いますが、これにとどまらず、実装フェーズでも活用できます。
例えば、UI画面の開発時など、コード修正→画面確認を繰り返して試行錯誤するような場面がありますが、こんなとき自動でデプロイができるととても作業が捗ります。

手順

0. WebLogic ServerがインストールされたDockerコンテナを用意する

本記事では、ローカル環境のVirtualBox上でLinux仮想マシンを動かし、その仮想マシンをDockerのホストとして利用します。
WebLogic ServerのDockerコンテナの作成手順は、過去の記事を参照ください。

また、DockerそのものとDocker on WebLogicの詳しい解説は、先日のWebLogic Server勉強会の資料が参考になりますので、こちらもぜひご参照ください(←宣伝)。

1. WebLogic Maven プラグインをインストールする

WebLogic Maven プラグインを、ローカル環境にインストールします。
こちらの手順も、過去の記事を参照ください。「1-2. WebLogic Mavenプラグインのインストール」までの作業をやれば良いはずです。

2. pom.xmlを編集する

以下の例のように、<build> > <plugins>タグの配下に、WebLogic Mavenプラグインの設定を記述します。

<build>
    <plugins>
        <plugin>
            <groupId>com.oracle.weblogic</groupId>
            <artifactId>weblogic-maven-plugin</artifactId>
            <version>12.2.1-0-0</version>
            <executions>
                <execution>
                    <id>wls-redeploy</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>redeploy</goal>
                    </goals>
                    <configuration>
                        <adminurl>t3://localhost:8001</adminurl>
                        <user>weblogic</user>
                        <password>welcome1</password>
                        <targets>AdminServer</targets>
                        <upload>true</upload>
                        <remote>false</remote>
                        <source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
                        <name>${project.build.finalName}</name>
                        <verbose>true</verbose>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

goalに"redeploy"を指定しているので、既にデプロイされている物があった場合、自動的に更新されます。

configration配下の記述は、環境に合わせて変更する必要があります。
各設定の意味は、以下のとおりです。

  • adminurl
  • user
    • WebLogic Serverの管理者ユーザーのユーザー名
  • password
    • WebLogic Serverの管理者ユーザーのパスワード
  • targets
    • ビルドした成果物のデプロイ先
  • upload
    • 成果物をアップロードするかどうか(管理コンソールからデプロイするときの、アップロード操作に相当すると思われる)
  • remote
    • サーバーがリモートに設置されているかどうか
  • source
    • 成果物(warなど)のパス
  • name
    • 成果物の名前
  • verbose
    • デプロイ時に詳細メッセージを出力するかどうか

今回のように、VirtualBox上の仮想マシン上のDockerコンテナでWebLogic Serverを動かしている場合、uploadは"true"、remoteは"false"が正解のようです。

3. ビルドを実行する

実際にビルドを実行してみます。以下のコマンドを実行します。

> mvn clean pre-integration-test

warのビルドが終わると、デプロイが開始されます。
以下のように、最終的に"redeploy 完了"のメッセージが表示されれば、デプロイ完了です。

[INFO] --- weblogic-maven-plugin:12.2.1-0-0:redeploy (wls-redeploy) @ linguistic
s ---
[INFO] Command flags are: -noexit -redeploy -username weblogic -password *******
-name linguistics-1.0 -source C:\Users\hhayakaw\Documents\GitRepository\linguis
tics\app\target\linguistics-1.0.war -targets AdminServer -upload -verbose -admin
url t3://localhost:8001
…
ターゲットの状態: サーバー AdminServerでredeploy 完了

実際に所定のURLにアクセスすると、アプリケーションが稼働していることがわかると思います。

せっかくなので、デプロイが完了したらブラウザで開くところまで一気にやるようにすると、さらにいい感じです。

WebLogic Maven Pluginを使った自動デプロイの手順は、以上です。
WebLogic Maven Pluginは、使いこなせるとまだまだ色々できそうなので、探求を続けていきたいところです。

東京Node学園 20時限目で発表してきました

約1週間ほど立ってしまいましたが、4/5に開催された東京Node学園 20時限目に、発表者として参加してきました。

東京Node学園 20時限目の全体のまとめについては、すでに受付の方が書いてくださったブログと、Togetterがありますので、この記事では、各発表資料へのリンクをポイントしておきます。

資料リンク一覧(見つけられたもののみ)

(@saneyukiさん、@niccolliさん、見つけられてないだけだったらごめんなさい。)

僕の発表では、「Oracle が Node.js を始めたというのだが!」と題しまして、最近Oracleがリリースした、Oracle Node CloudとOracle JETの紹介をさせていただきました。左記2製品については、発表資料を参照下さい。

お礼など

Oracleの人間が自社製品の紹介をするという、勉強会の空気を全く読んでいないテーマにも関わらず、参加者の皆さんには暖かく受け入れていただきました。Node学園の皆さんの懐の深さに感謝です。
登壇したら冒頭いきなり盛大な拍手をもらったのには、正直感動したなー。

僕自身、運営にOracleが絡んでいない勉強会に参加するのはかなり久しぶりでしたが、やっぱりこういう勉強会に出るとモチベーション上がりますね。
今更ではありますが、Node.jsやフロントエンドのJavaScriptについて、僕自身もしっかりキャッチアップしていきたいと思います。

最後に、Node学園代表の @yosuke_furukawa 氏にお礼を。
今回の発表について相談を持ちかけたとき、快く承知してもらいました。彼の存在無くしては、今回の機会が実現することは難しかったことでしょう。
前職の新卒入社以来の縁ですが、今後ともよろしくお願いします。

なんだか若干退職エントリーみたいだな。そういった予定は今現在まったく無いので、この記事を見てい頂いている皆様も、今後ともよろしくお願いします。

WebLogic Mavenプラグインを使って依存モジュールの解決を楽にする

jBatch×WebLogicのネタも考え中ですが、閑話休題ということで。

以下のような開発をしているエンジニアの方は、WebLogic Mavenプラグインを利用するのがおすすめです。

  • WebLogic Serverで動かすアプリケーションを開発している
  • アプリケーションのビルドにMavenを利用している

例えば、Servlet-APIJDBCドライバのjarファイルを、pom.xmlにひとつひとつ書いて取得してたりしないでしょうか。しかも、WebLogicに入っているモジュールのバージョンを確認して、自力でローカルリポジトリにインストールしたりとか…。

WebLogic Mavenプラグインを使うと、個別にpom.xmlに記述せずとも、WebLogicに入っているモジュールを自動取得することができます。
手間が減るのはもちろん、コンパイルとランタイム間での、バージョン不整合を防止することができます。

というわけで、WebLogic Mavenプラグインを使って、WebLogicに入っているモジュール解決する手順を紹介します。

1. 手順

1-1. 前提条件

この手順は、以下の条件を満たしていることが前提です。

  • ビルドを実行するマシンにmvnがインストールされている
  • WebLogic Serverの、ORACLE_HOMEが参照できる

また、これ移行の手順は、WebLogic Server 12.2.1を使う想定で書きます。

1-2. WebLogic Mavenプラグインのインストール

以下のコマンドを実行します(WebLogic Server 12.2.1 の場合)

> cd ${ORACLE_HOME}\oracle_common\plugins\maven\com\oracle\maven\oracle-maven-sync\12.2.1
> mvn install:install-file -DpomFile=oracle-maven-sync-12.2.1.pom -Dfile=oracle-maven-sync-12.2.1.jar

※${ORACLE_HOME}は環境に合わせて変更してください。

以下のような内容がコンソールに出力されればOKです。

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ standalone-pom---
[INFO] Installing C:\WLSHandsOn\mw\oracle_common\plugins\maven\com\oracle\maven\oracle-maven-sync\12.2.1\oracle-maven-sync-12.2.1.jar to C:\Users\hhayakaw\.m2\repository\com\oracle\maven\oracle-maven-sync\12.2.1-0-0\oracle-maven-sync-12.2.1-0-0.jar
[INFO] Installing C:\WLSHandsOn\mw\oracle_common\plugins\maven\com\oracle\maven\oracle-maven-sync\12.2.1\oracle-maven-sync-12.2.1.pom to C:\Users\hhayakaw\.m2\repository\com\oracle\maven\oracle-maven-sync\12.2.1-0-0\oracle-maven-sync-12.2.1-0-0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.076 s
[INFO] Finished at: 2016-03-07T09:32:09+09:00
[INFO] Final Memory: 9M/245M
[INFO] ------------------------------------------------------------------------

1-3. ローカルリポジトリにモジュールを取り込む

MavenのローカルリポジトリOracle_Home配下からモジュールを取り込みます。

以下のコマンドを実行します(12.2.1の場合)

>mvn com.oracle.maven:oracle-maven-sync:push -DoracleHome=${ORACLE_HOME}

※${ORACLE_HOME}は環境に合わせて変更してください。

取り込むモジュールがたくさんあるので、コンソールに大量のメッセージが出力されますが、最終的に以下の内容が出力されれば成功のようです。

[INFO] SUMMARY
[INFO] ------------------------------------------------------------------------
[INFO] PUSH SUMMARY - ARTIFACTS PROCESSED SUCCESSFULLY
[INFO] ------------------------------------------------------------------------
[INFO] Number of artifacts pushed: 1013
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] PUSH SUMMARY - ERRORS ENCOUNTERED
[INFO] ------------------------------------------------------------------------
[INFO] No issues encountered.
[INFO]
[INFO] IMPORTANT NOTE
[INFO] This operation may have added/updated archetypes in your repository.
[INFO] To update your archetype catalog, you should run:
[INFO] 'mvn archetype:crawl -Dcatalog=$HOME/.m2/archetype-catalog.xml'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:51 min
[INFO] Finished at: 2016-03-07T09:39:33+09:00
[INFO] Final Memory: 11M/162M
[INFO] ------------------------------------------------------------------------

1-4. pom.xmlを編集する

ビルドするプロジェクトのpom.xml<dependency>と、<plugin>タグを追加します。

以下の様に、両タグを記述します。また、既存の記述の不要な<dependency>は削除しておきます。

<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"><dependencies><dependency>
            <groupId>com.oracle.weblogic</groupId>
            <artifactId>weblogic-server-pom</artifactId>
            <version>12.2.1-0-0</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>
    </dependencies><build>
        <plugins><plugin>
                <groupId>com.oracle.weblogic</groupId>
                <artifactId>weblogic-maven-plugin</artifactId>
                <version>12.2.1-0-0</version>
            </plugin>
        </plugins>
    </build></project>

1-5. ビルドする

普段どおりビルドを実行します。例えば、

> mvn clean package

初回は結構時間がかかります。WebLogicから取り込んだモジュールの、メタデータのダウンロードらしき処理がおこなわれます。

2. 適用例

例えば、以前紹介したjBatchのサンプルアプリケーションですと、以下のモジュールの記述を削減できます。

  • javax.batch-api
  • cdi-api
  • javax.inject
  • javax.transaction-api
  • javax.ws.rs-api
  • javax.persistence

実際のコードはこちらJava EE系が軒並み減らせました。


WebLogic Mavneプラグインの機能は、今回紹介したものだけではありません。ビルドしたものをテスト環境に自動デプロイしたり、WLSTを実行したりなど、もっと高度な機能があります。

詳細は公式のドキュメントを参照ください。